74 lines
3.4 KiB
TypeScript
74 lines
3.4 KiB
TypeScript
/**
|
|||
|
|
* Virtual Layers rows for components whose content lives in ARRAY PROPS
|
||
|
|
* rather than Craft child nodes.
|
||
|
|
*
|
||
|
|
* FeaturesGrid, Tabs, Accordion, PricingTable, Testimonials, Gallery,
|
||
|
|
* ContentSlider, NumberCounter, Menu, SocialLinks, Navbar and ContactForm all
|
||
|
|
* render their items from a prop array, so Craft sees them as leaf nodes and
|
||
|
|
* the Layers tree showed nothing underneath them -- the "Layers doesn't show
|
||
|
|
* everything" report. These rows are display-only: they are not Craft nodes,
|
||
|
|
* cannot be dragged, and selecting one selects the PARENT node (plus asks the
|
||
|
|
* array editor to scroll that item into view -- see LayerFocusContext).
|
||
|
|
*
|
||
|
|
* ColumnLayout is deliberately absent: it uses real `<Element canvas>`
|
||
|
|
* children, which LayersPanel already nests correctly.
|
||
|
|
*/
|
||
|
|
|
||
|
|
export interface VirtualRow {
|
||
|
|
index: number;
|
||
|
|
label: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface VirtualChildSpec {
|
||
|
|
/** Name of the array prop holding the items. */
|
||
|
|
prop: string;
|
||
|
|
/** Per-item field used as the row label. */
|
||
|
|
label: string;
|
||
|
|
/** Used as "<fallback> <n>" when the label field is missing or blank. */
|
||
|
|
fallback: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Keyed by the component's craft `displayName` -- the same string
|
||
|
|
* `LayerNode` already resolves and shows as the row text.
|
||
|
|
*
|
||
|
|
* Verified against each component's actual item interface and
|
||
|
|
* `craft.props` defaults (see task-9-report.md, Step 1):
|
||
|
|
* - Tabs items are `{ label, content }`, not `{ title }` -- registry
|
||
|
|
* corrected from `title` to `label`.
|
||
|
|
* - ContentSlider items are `{ heading, text, ... }`, not `{ title }` --
|
||
|
|
* registry corrected from `title` to `heading`.
|
||
|
|
* Every other entry (prop name and label field) matched the component as
|
||
|
|
* originally drafted. */
|
||
|
|
export const VIRTUAL_CHILD_PROPS: Record<string, VirtualChildSpec> = {
|
||
|
|
'Features Grid': { prop: 'features', label: 'title', fallback: 'Feature' },
|
||
|
|
Tabs: { prop: 'tabs', label: 'label', fallback: 'Tab' },
|
||
|
|
Accordion: { prop: 'items', label: 'title', fallback: 'Item' },
|
||
|
|
'Pricing Table': { prop: 'plans', label: 'name', fallback: 'Plan' },
|
||
|
|
Testimonials: { prop: 'testimonials', label: 'name', fallback: 'Testimonial' },
|
||
|
|
Gallery: { prop: 'images', label: 'alt', fallback: 'Image' },
|
||
|
|
'Content Slider': { prop: 'slides', label: 'heading', fallback: 'Slide' },
|
||
|
|
'Number Counter': { prop: 'counters', label: 'label', fallback: 'Counter' },
|
||
|
|
Menu: { prop: 'links', label: 'text', fallback: 'Link' },
|
||
|
|
'Social Links': { prop: 'links', label: 'platform', fallback: 'Link' },
|
||
|
|
Navbar: { prop: 'links', label: 'text', fallback: 'Link' },
|
||
|
|
'Contact Form': { prop: 'fields', label: 'label', fallback: 'Field' },
|
||
|
|
};
|
||
|
|
|
||
|
|
const MAX_LABEL = 40;
|
||
|
|
|
||
|
|
export function deriveVirtualRows(displayName: string, props: Record<string, any>): VirtualRow[] {
|
||
|
|
const spec = VIRTUAL_CHILD_PROPS[displayName];
|
||
|
|
if (!spec) return [];
|
||
|
|
|
||
|
|
const items = props?.[spec.prop];
|
||
|
|
if (!Array.isArray(items)) return [];
|
||
|
|
|
||
|
|
return items.map((item, index) => {
|
||
|
|
const raw = item && typeof item === 'object' ? item[spec.label] : undefined;
|
||
|
|
const text = typeof raw === 'string' ? raw.trim() : '';
|
||
|
|
if (!text) return { index, label: `${spec.fallback} ${index + 1}` };
|
||
|
|
const label = text.length > MAX_LABEL ? `${text.slice(0, MAX_LABEL)}…` : text;
|
||
|
|
return { index, label };
|
||
|
|
});
|
||
|
|
}
|