Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 30x 30x 12x 12x 12x 12x 12x 30x 30x 30x 30x 80x 78x 62x 52x 52x 62x 2x 2x 52x 50x 30x 30x 82x 114x 52x 52x 52x | import type {
ClassNameRule,
ComponentMetadata,
ComposedComponentMetadata,
SlotMetadata,
StructureMetadata,
} from '@fds-uif/generator-base';
/**
* Plan of class getters that need to exist on the generated class for the
* template to bind to. Keyed by structure node identity (the node object
* reference) so that the template walker can look up the same getter by
* walking the same tree.
*
* Names are derived deterministically from a node's `name` field (or
* synthesized from depth/index for unnamed nodes), with disambiguation if
* collisions occur within a single component.
*/
export interface ClassGetterPlan {
/** Component-level rules that apply to the root structure node. */
rootRules: ClassNameRule[];
/** Stable getter name for the root, or `null` when the root has no
* conditional rules (template should render a static class instead). */
rootGetterName: string | null;
/** Per-node rules for non-root nodes that need a dynamic class binding. */
nodes: Map<StructureMetadata | ComposedComponentMetadata, NodeGetter>;
}
export interface NodeGetter {
name: string;
rules: ClassNameRule[];
}
/**
* Build a class-getter plan by walking the metadata structure tree.
*
* - The root node's effective rules come from `metadata.classNames`
* (component-level), which already carries state/variant/modifier
* conditionals.
* - Non-root nodes use their own `node.classNames` array.
* - A node only gets a getter when at least one rule has conditional entries.
*/
export function buildClassGetterPlan(metadata: ComponentMetadata): ClassGetterPlan {
const used = new Set<string>();
const reserve = (preferred: string): string => {
let name = preferred;
let i = 2;
while (used.has(name)) name = `${preferred}${i++}`;
used.add(name);
return name;
};
const rootRules = metadata.classNames ?? [];
const rootGetterName = hasConditional(rootRules) ? reserve('computedClass') : null;
const nodes = new Map<StructureMetadata | ComposedComponentMetadata, NodeGetter>();
const walk = (children: StructureMetadata['children'] | undefined, parentName: string): void => {
if (!children) return;
children.forEach((child, index) => {
if (isSlot(child)) return;
const baseName = nodeBaseName(child, parentName, index);
const rules = child.classNames ?? [];
if (hasConditional(rules)) {
const name = reserve(`${baseName}Class`);
nodes.set(child, { name, rules });
}
if (isStructure(child)) {
walk(child.children, baseName);
}
});
};
walk(metadata.structure.children, 'root');
return { rootRules, rootGetterName, nodes };
}
function hasConditional(rules: ClassNameRule[]): boolean {
return rules.some((r) => r.conditional && r.conditional.length > 0);
}
function isSlot(c: StructureMetadata['children'][number]): c is SlotMetadata {
return (c as SlotMetadata).type === 'slot';
}
function isStructure(c: StructureMetadata['children'][number]): c is StructureMetadata {
return !isSlot(c) && (c as ComposedComponentMetadata).type !== 'component';
}
function nodeBaseName(
node: StructureMetadata | ComposedComponentMetadata,
parentName: string,
index: number,
): string {
const explicit = (node as StructureMetadata).name;
Eif (explicit && explicit !== 'root') return explicit;
return `${parentName}Child${index}`;
}
|