mirror of
https://github.com/zoriya/v10.git
synced 2026-08-07 06:37:45 +00:00
docs(site): add Slider and Tooltip API reference pages (#862)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
487be3ea96
commit
7a5ce94bca
@@ -30,6 +30,12 @@ const NAME_OVERRIDES: Record<string, string> = {
|
||||
'pip-button': 'PiPButton',
|
||||
};
|
||||
|
||||
// Parts whose HTML element file doesn't follow the `{component}-{part}-element.ts` convention.
|
||||
// Key: `{component}/{part-kebab}`, Value: element file basename (without `.ts`).
|
||||
const PART_ELEMENT_OVERRIDES: Record<string, string> = {
|
||||
'tooltip/provider': 'tooltip-group-element',
|
||||
};
|
||||
|
||||
function buildProps(coreData: CoreExtraction): Record<string, PropDef> {
|
||||
const props: Record<string, PropDef> = {};
|
||||
for (const prop of coreData.props) {
|
||||
@@ -298,12 +304,14 @@ function buildSingleComponentReference(source: ComponentSource, program: ts.Prog
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a React source file instantiates a Core class (matches `new \w+Core\(`).
|
||||
* Check if a React source file instantiates the component's own Core class
|
||||
* (matches `new {ComponentName}Core(`). This prevents auxiliary classes like
|
||||
* `TooltipGroupCore` from being mistaken for the primary Core.
|
||||
*/
|
||||
function instantiatesCore(filePath: string): boolean {
|
||||
function instantiatesCore(filePath: string, componentName: string): boolean {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
return /new \w+Core\(/.test(content);
|
||||
return new RegExp(`new ${componentName}Core\\b`).test(content);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
@@ -349,8 +357,10 @@ function discoverParts(source: ComponentSource, program: ts.Program): PartSource
|
||||
for (const partExport of localExports) {
|
||||
const kebab = partKebabFromSource(partExport.source, componentKebab);
|
||||
|
||||
// Look for sub-part element file: {component}-{part}-element.ts
|
||||
const subPartElementFile = path.join(htmlDir, `${componentKebab}-${kebab}-element.ts`);
|
||||
// Look for sub-part element file: {component}-{part}-element.ts (or override)
|
||||
const overrideKey = `${componentKebab}/${kebab}`;
|
||||
const elementBasename = PART_ELEMENT_OVERRIDES[overrideKey] ?? `${componentKebab}-${kebab}-element`;
|
||||
const subPartElementFile = path.join(htmlDir, `${elementBasename}.ts`);
|
||||
const hasSubPartElement = fs.existsSync(subPartElementFile);
|
||||
|
||||
// Resolve React source path for JSDoc description extraction
|
||||
@@ -358,7 +368,7 @@ function discoverParts(source: ComponentSource, program: ts.Program): PartSource
|
||||
const reactPath = fs.existsSync(reactFile) ? reactFile : undefined;
|
||||
|
||||
// Primary detection: the part whose React source instantiates the Core class
|
||||
const isPrimary = !!reactPath && instantiatesCore(reactPath);
|
||||
const isPrimary = !!reactPath && instantiatesCore(reactPath, source.name);
|
||||
|
||||
const subPartUsesDataAttrs = !isPrimary && !!reactPath && usesDataAttrs(reactPath);
|
||||
|
||||
|
||||
@@ -77,8 +77,10 @@ export function extractPartDescription(filePath: string, program: ts.Program, pa
|
||||
/**
|
||||
* Extract custom React-specific props from a sub-part's Props interface.
|
||||
*
|
||||
* Walks syntactic own members of `{localName}Props` (excluding inherited
|
||||
* `UIComponentProps` members and `children`).
|
||||
* Walks syntactic own members of `{localName}Props`, then also includes
|
||||
* members from any extended interface declared within the project (i.e., not
|
||||
* from `node_modules`). This picks up props from project types like
|
||||
* `TooltipGroupProps` while excluding inherited React DOM attributes.
|
||||
*/
|
||||
export function extractSubPartProps(filePath: string, program: ts.Program, localName: string): Record<string, PropDef> {
|
||||
const sourceFile = program.getSourceFile(filePath);
|
||||
@@ -86,27 +88,53 @@ export function extractSubPartProps(filePath: string, program: ts.Program, local
|
||||
const checker = program.getTypeChecker();
|
||||
const props: Record<string, PropDef> = {};
|
||||
|
||||
const SKIP_PROPS = new Set(['children']);
|
||||
|
||||
function collectFromMembers(members: ts.NodeArray<ts.TypeElement>) {
|
||||
for (const member of members) {
|
||||
if (!ts.isPropertySignature(member) || !member.name || !ts.isIdentifier(member.name)) continue;
|
||||
const name = member.name.text;
|
||||
if (SKIP_PROPS.has(name) || !member.type) continue;
|
||||
|
||||
let typeStr = checker.typeToString(checker.getTypeFromTypeNode(member.type));
|
||||
if (member.questionToken) typeStr = typeStr.replace(/ \| undefined$/, '');
|
||||
|
||||
const propDef: PropDef = { type: typeStr };
|
||||
|
||||
const symbol = checker.getSymbolAtLocation(member.name);
|
||||
if (symbol) {
|
||||
const docs = symbol.getDocumentationComment(checker);
|
||||
const desc = docs.map((d) => d.text).join('');
|
||||
if (desc) propDef.description = desc;
|
||||
}
|
||||
|
||||
props[name] = propDef;
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(sourceFile, function visit(node) {
|
||||
if (ts.isInterfaceDeclaration(node) && node.name.text === `${localName}Props`) {
|
||||
for (const member of node.members) {
|
||||
if (!ts.isPropertySignature(member) || !member.name || !ts.isIdentifier(member.name)) continue;
|
||||
const name = member.name.text;
|
||||
if (name === 'children' || !member.type) continue;
|
||||
// Collect own syntactic members.
|
||||
collectFromMembers(node.members);
|
||||
|
||||
let typeStr = checker.typeToString(checker.getTypeFromTypeNode(member.type));
|
||||
// Only strips trailing ` | undefined`; other orderings (e.g., `undefined | string`) pass through.
|
||||
if (member.questionToken) typeStr = typeStr.replace(/ \| undefined$/, '');
|
||||
// Walk extends clause and include members from project-local interfaces.
|
||||
if (node.heritageClauses) {
|
||||
for (const clause of node.heritageClauses) {
|
||||
for (const expr of clause.types) {
|
||||
const type = checker.getTypeAtLocation(expr);
|
||||
const symbol = type.getSymbol();
|
||||
const decl = symbol?.declarations?.[0];
|
||||
if (!decl) continue;
|
||||
|
||||
const propDef: PropDef = { type: typeStr };
|
||||
// Only include if declared in project sources (not node_modules).
|
||||
const declFile = decl.getSourceFile().fileName;
|
||||
if (declFile.includes('node_modules')) continue;
|
||||
|
||||
const symbol = checker.getSymbolAtLocation(member.name);
|
||||
if (symbol) {
|
||||
const docs = symbol.getDocumentationComment(checker);
|
||||
const desc = docs.map((d) => d.text).join('');
|
||||
if (desc) propDef.description = desc;
|
||||
if (ts.isInterfaceDeclaration(decl)) {
|
||||
collectFromMembers(decl.members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props[name] = propDef;
|
||||
}
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
|
||||
Reference in New Issue
Block a user