Files
site-builder/craft/src/panels/left/layers-virtual-rows.ts
T

74 lines
3.4 KiB
TypeScript
Raw Normal View History

/**
* 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 };
});
}