@aglyn/plugins-mui 1.0.0-beta.218 → 1.0.0-beta.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/lib/components/accordion.js +1 -1
- package/src/lib/components/accordion.js.map +1 -1
- package/src/lib/components/blocks.js +1 -1
- package/src/lib/components/blocks.js.map +1 -1
- package/src/lib/components/breadcrumbs.js +1 -1
- package/src/lib/components/breadcrumbs.js.map +1 -1
- package/src/lib/components/button.js +5 -5
- package/src/lib/components/button.js.map +1 -1
- package/src/lib/components/collection-categories.js +1 -1
- package/src/lib/components/collection-categories.js.map +1 -1
- package/src/lib/components/collection-entries.js +1 -1
- package/src/lib/components/collection-entries.js.map +1 -1
- package/src/lib/components/collection-entry-body.js +1 -1
- package/src/lib/components/collection-entry-body.js.map +1 -1
- package/src/lib/components/collection-search.js +1 -1
- package/src/lib/components/collection-search.js.map +1 -1
- package/src/lib/components/document-root.js +1 -1
- package/src/lib/components/document-root.js.map +1 -1
- package/src/lib/components/image.js +4 -4
- package/src/lib/components/image.js.map +1 -1
- package/src/lib/components/language-switcher.js +3 -3
- package/src/lib/components/language-switcher.js.map +1 -1
- package/src/lib/components/layout-slot.d.ts +1 -1
- package/src/lib/components/layout-slot.js +3 -3
- package/src/lib/components/layout-slot.js.map +1 -1
- package/src/lib/components/link-box.js +4 -4
- package/src/lib/components/link-box.js.map +1 -1
- package/src/lib/components/markdown.js +3 -3
- package/src/lib/components/markdown.js.map +1 -1
- package/src/lib/components/pagination.js +2 -2
- package/src/lib/components/pagination.js.map +1 -1
- package/src/lib/components/screen-link.js +7 -7
- package/src/lib/components/screen-link.js.map +1 -1
- package/src/lib/components/tabs.js +2 -2
- package/src/lib/components/tabs.js.map +1 -1
- package/src/lib/components/video.d.ts +5 -4
- package/src/lib/components/video.js +24 -22
- package/src/lib/components/video.js.map +1 -1
- package/src/lib/video-embed-providers.d.ts +30 -0
- package/src/lib/video-embed-providers.js +125 -0
- package/src/lib/video-embed-providers.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-mui",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.220",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-theme": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.220",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.220",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.220",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.220",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.220",
|
|
33
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.220",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"mobx": "^7.0.5",
|
|
36
36
|
"mobx-react-lite": "^5.1.0"
|
|
@@ -298,7 +298,7 @@ export const accordionSummarySchema = {
|
|
|
298
298
|
{
|
|
299
299
|
name: 'screenId',
|
|
300
300
|
label: 'Header links to',
|
|
301
|
-
description: 'Optional. Give the header a destination and the row splits: the ' + 'text becomes a link to that
|
|
301
|
+
description: 'Optional. Give the header a destination and the row splits: the ' + 'text becomes a link to that page and the chevron beside it ' + 'opens the panel. That is the only way a header can do both — a ' + 'link inside the toggle button is invalid and unreachable by ' + 'keyboard. Leave it empty and the whole row toggles, as before.',
|
|
302
302
|
component: Aglyn.FieldComponentType.SCREEN_SELECT
|
|
303
303
|
}
|
|
304
304
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/accordion.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiChevronDown, mdiUnfoldMoreHorizontal } from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport MuiAccordion from '@mui/material/Accordion'\nimport MuiAccordionDetails from '@mui/material/AccordionDetails'\nimport MuiAccordionSummary from '@mui/material/AccordionSummary'\nimport Box from '@mui/material/Box'\nimport Link from '@mui/material/Link'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, isValidElement, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport {\n authorHtmlLabel,\n withAuthorHtmlLabel,\n} from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ACCORDION_ID: Aglyn.ComponentId = 'muiAccordion'\nexport const ACCORDION_SUMMARY_ID: Aglyn.ComponentId = 'muiAccordionSummary'\nexport const ACCORDION_DETAILS_ID: Aglyn.ComponentId = 'muiAccordionDetails'\n\nexport interface AccordionElementProps {\n /** Starts open on the published site. */\n defaultExpanded?: boolean\n disabled?: boolean\n /** Removes the extra margin an expanded accordion normally gains. */\n disableGutters?: boolean\n children?: ReactNode\n}\n\n/**\n * Expand/collapse panel (https://mui.com/material-ui/react-accordion/).\n *\n * On the besigner canvas it renders **expanded regardless of\n * `defaultExpanded`**: collapsed details are unreachable for selection\n * and styling, which is the same reason the Drawer expands its contents\n * while selected (AGL-571). Preview and the published site both honour\n * the author's setting and toggle normally, so what ships is never in\n * doubt — only the editing surface differs.\n */\nconst AccordionElement = forwardRef<HTMLDivElement, AccordionElementProps>(\n (props, ref) => {\n const { defaultExpanded, children, ...rest } = props\n const { editorInert } = Aglyn.useScreenLink(undefined)\n if (editorInert) {\n // Controlled-open with no onChange: the canvas is a static surface,\n // clicks there select rather than toggle.\n // MUI types Accordion's children as NonNullable: an accordion with\n // no summary/details is not a renderable shape, and the renderer\n // always supplies the pair.\n return (\n <MuiAccordion\n ref={ref}\n expanded\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n }\n return (\n <MuiAccordion\n ref={ref}\n defaultExpanded={!!defaultExpanded}\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n },\n)\nAccordionElement.displayName = 'AglynAccordion'\n\nexport interface AccordionSummaryElementProps {\n /**\n * Optional screen the header's label links to (AGL-1232). Setting it\n * SPLITS the row — see {@link AccordionSummaryElement}. A screen id, never\n * a path: the href is resolved from the host routing map at render time,\n * so a slug rename or re-parent cannot break it, and a bare `href` would\n * full-reload the site instead of routing.\n */\n screenId?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute — recomposed below so the Styles panel's values are\n * merged rather than replaced (AGL-1240/1284).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the label, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n children?: ReactNode\n}\n\n/** Label and chevron sit on one line; the label takes the slack. */\nconst SUMMARY_ROW_SX = {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n} as const\n\n/** Matches MuiAccordionSummary's own gutters so the split row lines up. */\nconst SUMMARY_LABEL_SX = {\n flex: 1,\n minWidth: 0,\n px: 2,\n py: 1.5,\n} as const\n\n/**\n * The toggle keeps only its chevron: the label is a sibling now, so the\n * summary's own content slot would just add empty width.\n */\nconst SUMMARY_TOGGLE_SX = {\n flex: '0 0 auto',\n '& .MuiAccordionSummary-content': { display: 'none' },\n} as const\n\n/**\n * The visible text of a React children tree, for naming a control that has no\n * text of its own (AGL-2349).\n *\n * It has to walk, not test: what arrives here is the node renderer's output,\n * which wraps the authored string in an `<AglynText>` element inside an\n * array (`libs/aglyn-node-renderer/src/lib/components/leaf.tsx`), so the string\n * is a grandchild at best and never the value itself. Descends `props.children`\n * rather than reading DOM text, because this runs on the React tree BEFORE\n * anything is in the DOM to read. (It also predates AGL-2011, when\n * `<aglyn-text>` was a shadow host whose `innerText` read back empty — that\n * is no longer true, but walking props is still the right tool here.)\n */\nconst childText = (node: ReactNode): string => {\n if (node == null || typeof node === 'boolean') return ''\n if (typeof node === 'string' || typeof node === 'number') return String(node)\n if (Array.isArray(node)) return node.map(childText).join('')\n if (isValidElement(node)) {\n return childText((node.props as { children?: ReactNode })?.children)\n }\n return ''\n}\n\n/**\n * The always-visible header row. Supplies the expand chevron MUI leaves\n * to the caller — without it the control looks like inert text and gives\n * no hint that there is anything to open.\n *\n * ## Why a linked header is a different shape (AGL-1232)\n *\n * `MuiAccordionSummary` renders a `<button>`, and the mobile drawer needs\n * Product/Solutions to reach their jump pages as well as open. An anchor\n * inside that button is invalid nesting — browsers unnest it, and nested\n * interactive content is unusable with a screen reader or a keyboard — so\n * the row cannot both toggle and navigate while it is one control.\n *\n * With a `screenId` set the row therefore splits in two: the label becomes\n * a real anchor, and a chevron-only `MuiAccordionSummary` beside it becomes\n * the toggle. Two siblings, each independently focusable, no nesting. The\n * summary keeps driving the panel through MUI's accordion context, so\n * `aria-expanded` and the transition are unchanged.\n *\n * With no `screenId` — every accordion authored before this — the output is\n * byte-for-byte what it always was: one `MuiAccordionSummary`, whole row\n * toggles.\n */\nexport const AccordionSummaryElement = forwardRef<\n HTMLDivElement,\n AccordionSummaryElementProps\n>((props, ref) => {\n const { screenId, html, ...rest } = props\n // Resolution is the routing map's job, exactly as Screen Link does it;\n // an unpublished or deleted target yields no href.\n const { href, suppressNavigation } = Aglyn.useScreenLink(screenId)\n\n if (!screenId) {\n return (\n <MuiAccordionSummary\n ref={ref}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n {...withAuthorHtmlLabel(rest, html)}\n />\n )\n }\n\n const { children, sx, ...rowRest } = rest\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // The chevron button's only content is an icon, so it needs a name of its\n // own — \"Product\" the link and \"Product\" the toggle are two controls.\n //\n // This USED to be `typeof children === 'string'`, which is never true in\n // production (AGL-2349). A summary's label is authored through\n // FIELD_TEXT_CONTENT, and the node renderer does not pass that string down as\n // `children` — `Leaf` lifts it out as `textContent` and renders\n // `<Component>{children}<AglynText>{textContent}</…></Component>`,\n // so `children` is always an ARRAY holding an element. The string branch was\n // reachable only from a spec that handed the component a bare string, so\n // every published split summary named its chevron \"Toggle section\".\n //\n // Read from `children` rather than from the formatted label below, because\n // a formatted one is markup handed to `dangerouslySetInnerHTML` and has no\n // React children to walk. `children` carries the same words either way.\n const label = childText(children).trim()\n const content = authorHtmlLabel(html) ?? children\n return (\n <Box ref={ref} {...rowRest} sx={[SUMMARY_ROW_SX, ...nodeSx]}>\n {href && !suppressNavigation ? (\n <AppLink href={href} underline=\"hover\" sx={SUMMARY_LABEL_SX}>\n {content}\n </AppLink>\n ) : (\n // Unresolved target, or a surface that must not navigate (canvas,\n // preview): same two-part shape, so the editor never shows a row\n // that behaves differently from the one that ships.\n //\n // Same ELEMENT too, not just the same box (AGL-1268). `href` comes\n // from the screens map, which differs between the render that\n // produced an ISR page and the render that hydrates it — a screen\n // published or unpublished in between. Switching `<a>` to `<span>`\n // on that would remount the subtree at hydration; an `<a>` with no\n // href is HTML's placeholder link, so the element is stable and the\n // row is still inert. Link Container settled on this shape first.\n <Link underline=\"none\" color=\"inherit\" sx={SUMMARY_LABEL_SX}>\n {content}\n </Link>\n )}\n <MuiAccordionSummary\n aria-label={label ? `Toggle ${label}` : 'Toggle section'}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n sx={SUMMARY_TOGGLE_SX}\n />\n </Box>\n )\n})\nAccordionSummaryElement.displayName = 'AglynAccordionSummary'\n\nexport const AccordionDetailsElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiAccordionDetails ref={ref} {...props} />)\nAccordionDetailsElement.displayName = 'AglynAccordionDetails'\n\nexport const accordionSchema: Aglyn.ComponentSchema<AccordionElementProps> = {\n $id: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion',\n description:\n 'Header that expands to reveal its details — FAQs, long forms, ' +\n 'anything progressive.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n // Structure is fixed by MUI: summary first, details second.\n restrictChildren: [\n Aglyn.LinealDirectiveFlag.LIMIT_TO,\n { components: [ACCORDION_SUMMARY_ID, ACCORDION_DETAILS_ID] },\n ],\n // …and it reads that structure BY INDEX, so the renderer has to hand it one\n // React child per node child (AGL-1237). Without this the summary swallowed\n // the details and every accordion on every published site expanded to reveal\n // an empty panel, with its content rendering unconditionally inside the\n // summary's heading.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'defaultExpanded',\n label: 'Start expanded?',\n description:\n 'If true, the panel is already open when a visitor arrives. The ' +\n 'canvas always shows it open so you can edit inside it.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disableGutters',\n label: 'Disable gutters?',\n description:\n 'If true, the panel keeps its height when expanded instead of ' +\n 'gaining extra margin — the tighter look for a stacked FAQ.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disabled',\n label: 'Disabled?',\n description: 'If true, visitors cannot expand the panel.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const accordionSummarySchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Summary',\n description: 'The clickable header row of an accordion.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n flags: {\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557). The row is a `<button>`, and a list or a link\n // inside one is invalid markup the parser unnests and a control a keyboard\n // cannot reach — the same constraint that made a LINKED header two sibling\n // controls rather than an anchor inside the toggle (AGL-1232).\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'screenId',\n label: 'Header links to',\n description:\n 'Optional. Give the header a destination and the row splits: the ' +\n 'text becomes a link to that screen and the chevron beside it ' +\n 'opens the panel. That is the only way a header can do both — a ' +\n 'link inside the toggle button is invalid and unreachable by ' +\n 'keyboard. Leave it empty and the whole row toggles, as before.',\n component: Aglyn.FieldComponentType.SCREEN_SELECT,\n },\n ],\n}\n\nexport const accordionDetailsSchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Details',\n description: 'The region an accordion reveals when expanded.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n}\n\nconst panel = (summary: string, body: string) => ({\n $id: null,\n componentId: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n {\n $id: null,\n componentId: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n props: { children: summary },\n },\n {\n $id: null,\n componentId: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'body2', children: body },\n },\n ],\n },\n ],\n})\n\nexport const accordionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ACCORDION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Accordion',\n pluginId: BUNDLE_ID,\n description: 'One expandable panel with a header and body text',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: accordionSchema.icon,\n data: panel('Accordion header', 'The details revealed when expanded.'),\n },\n {\n $id: generatePresetId(ACCORDION_ID, 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Three stacked question panels, ready to edit',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n nodes: [\n panel('How do I get started?', 'Answer the first question here.'),\n panel('What does it cost?', 'Answer the second question here.'),\n panel('How do I get support?', 'Answer the third question here.'),\n ],\n },\n },\n]\n\nexport default AccordionElement\n"],"names":["Aglyn","mdiChevronDown","mdiUnfoldMoreHorizontal","AppLink","MdiIcon","MuiAccordion","MuiAccordionDetails","MuiAccordionSummary","Box","Link","forwardRef","isValidElement","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","withAuthorHtmlLabel","generatePresetId","ACCORDION_ID","ACCORDION_SUMMARY_ID","ACCORDION_DETAILS_ID","AccordionElement","props","ref","defaultExpanded","children","rest","editorInert","useScreenLink","undefined","expanded","displayName","SUMMARY_ROW_SX","display","alignItems","width","SUMMARY_LABEL_SX","flex","minWidth","px","py","SUMMARY_TOGGLE_SX","childText","node","String","Array","isArray","map","join","AccordionSummaryElement","screenId","html","href","suppressNavigation","expandIcon","path","sx","rowRest","nodeSx","label","trim","content","underline","color","aria-label","AccordionDetailsElement","accordionSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","restrictChildren","LinealDirectiveFlag","LIMIT_TO","components","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","SWITCH","accordionSummarySchema","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","SCREEN_SELECT","accordionDetailsSchema","panel","summary","body","componentId","nodes","variant","accordionPresets","type","NodeType","PRESET","data","direction"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/accordion.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiChevronDown, mdiUnfoldMoreHorizontal } from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport MuiAccordion from '@mui/material/Accordion'\nimport MuiAccordionDetails from '@mui/material/AccordionDetails'\nimport MuiAccordionSummary from '@mui/material/AccordionSummary'\nimport Box from '@mui/material/Box'\nimport Link from '@mui/material/Link'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, isValidElement, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport {\n authorHtmlLabel,\n withAuthorHtmlLabel,\n} from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ACCORDION_ID: Aglyn.ComponentId = 'muiAccordion'\nexport const ACCORDION_SUMMARY_ID: Aglyn.ComponentId = 'muiAccordionSummary'\nexport const ACCORDION_DETAILS_ID: Aglyn.ComponentId = 'muiAccordionDetails'\n\nexport interface AccordionElementProps {\n /** Starts open on the published site. */\n defaultExpanded?: boolean\n disabled?: boolean\n /** Removes the extra margin an expanded accordion normally gains. */\n disableGutters?: boolean\n children?: ReactNode\n}\n\n/**\n * Expand/collapse panel (https://mui.com/material-ui/react-accordion/).\n *\n * On the besigner canvas it renders **expanded regardless of\n * `defaultExpanded`**: collapsed details are unreachable for selection\n * and styling, which is the same reason the Drawer expands its contents\n * while selected (AGL-571). Preview and the published site both honour\n * the author's setting and toggle normally, so what ships is never in\n * doubt — only the editing surface differs.\n */\nconst AccordionElement = forwardRef<HTMLDivElement, AccordionElementProps>(\n (props, ref) => {\n const { defaultExpanded, children, ...rest } = props\n const { editorInert } = Aglyn.useScreenLink(undefined)\n if (editorInert) {\n // Controlled-open with no onChange: the canvas is a static surface,\n // clicks there select rather than toggle.\n // MUI types Accordion's children as NonNullable: an accordion with\n // no summary/details is not a renderable shape, and the renderer\n // always supplies the pair.\n return (\n <MuiAccordion\n ref={ref}\n expanded\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n }\n return (\n <MuiAccordion\n ref={ref}\n defaultExpanded={!!defaultExpanded}\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n },\n)\nAccordionElement.displayName = 'AglynAccordion'\n\nexport interface AccordionSummaryElementProps {\n /**\n * Optional screen the header's label links to (AGL-1232). Setting it\n * SPLITS the row — see {@link AccordionSummaryElement}. A screen id, never\n * a path: the href is resolved from the host routing map at render time,\n * so a slug rename or re-parent cannot break it, and a bare `href` would\n * full-reload the site instead of routing.\n */\n screenId?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute — recomposed below so the Styles panel's values are\n * merged rather than replaced (AGL-1240/1284).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the label, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n children?: ReactNode\n}\n\n/** Label and chevron sit on one line; the label takes the slack. */\nconst SUMMARY_ROW_SX = {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n} as const\n\n/** Matches MuiAccordionSummary's own gutters so the split row lines up. */\nconst SUMMARY_LABEL_SX = {\n flex: 1,\n minWidth: 0,\n px: 2,\n py: 1.5,\n} as const\n\n/**\n * The toggle keeps only its chevron: the label is a sibling now, so the\n * summary's own content slot would just add empty width.\n */\nconst SUMMARY_TOGGLE_SX = {\n flex: '0 0 auto',\n '& .MuiAccordionSummary-content': { display: 'none' },\n} as const\n\n/**\n * The visible text of a React children tree, for naming a control that has no\n * text of its own (AGL-2349).\n *\n * It has to walk, not test: what arrives here is the node renderer's output,\n * which wraps the authored string in an `<AglynText>` element inside an\n * array (`libs/aglyn-node-renderer/src/lib/components/leaf.tsx`), so the string\n * is a grandchild at best and never the value itself. Descends `props.children`\n * rather than reading DOM text, because this runs on the React tree BEFORE\n * anything is in the DOM to read. (It also predates AGL-2011, when\n * `<aglyn-text>` was a shadow host whose `innerText` read back empty — that\n * is no longer true, but walking props is still the right tool here.)\n */\nconst childText = (node: ReactNode): string => {\n if (node == null || typeof node === 'boolean') return ''\n if (typeof node === 'string' || typeof node === 'number') return String(node)\n if (Array.isArray(node)) return node.map(childText).join('')\n if (isValidElement(node)) {\n return childText((node.props as { children?: ReactNode })?.children)\n }\n return ''\n}\n\n/**\n * The always-visible header row. Supplies the expand chevron MUI leaves\n * to the caller — without it the control looks like inert text and gives\n * no hint that there is anything to open.\n *\n * ## Why a linked header is a different shape (AGL-1232)\n *\n * `MuiAccordionSummary` renders a `<button>`, and the mobile drawer needs\n * Product/Solutions to reach their jump pages as well as open. An anchor\n * inside that button is invalid nesting — browsers unnest it, and nested\n * interactive content is unusable with a screen reader or a keyboard — so\n * the row cannot both toggle and navigate while it is one control.\n *\n * With a `screenId` set the row therefore splits in two: the label becomes\n * a real anchor, and a chevron-only `MuiAccordionSummary` beside it becomes\n * the toggle. Two siblings, each independently focusable, no nesting. The\n * summary keeps driving the panel through MUI's accordion context, so\n * `aria-expanded` and the transition are unchanged.\n *\n * With no `screenId` — every accordion authored before this — the output is\n * byte-for-byte what it always was: one `MuiAccordionSummary`, whole row\n * toggles.\n */\nexport const AccordionSummaryElement = forwardRef<\n HTMLDivElement,\n AccordionSummaryElementProps\n>((props, ref) => {\n const { screenId, html, ...rest } = props\n // Resolution is the routing map's job, exactly as Screen Link does it;\n // an unpublished or deleted target yields no href.\n const { href, suppressNavigation } = Aglyn.useScreenLink(screenId)\n\n if (!screenId) {\n return (\n <MuiAccordionSummary\n ref={ref}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n {...withAuthorHtmlLabel(rest, html)}\n />\n )\n }\n\n const { children, sx, ...rowRest } = rest\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // The chevron button's only content is an icon, so it needs a name of its\n // own — \"Product\" the link and \"Product\" the toggle are two controls.\n //\n // This USED to be `typeof children === 'string'`, which is never true in\n // production (AGL-2349). A summary's label is authored through\n // FIELD_TEXT_CONTENT, and the node renderer does not pass that string down as\n // `children` — `Leaf` lifts it out as `textContent` and renders\n // `<Component>{children}<AglynText>{textContent}</…></Component>`,\n // so `children` is always an ARRAY holding an element. The string branch was\n // reachable only from a spec that handed the component a bare string, so\n // every published split summary named its chevron \"Toggle section\".\n //\n // Read from `children` rather than from the formatted label below, because\n // a formatted one is markup handed to `dangerouslySetInnerHTML` and has no\n // React children to walk. `children` carries the same words either way.\n const label = childText(children).trim()\n const content = authorHtmlLabel(html) ?? children\n return (\n <Box ref={ref} {...rowRest} sx={[SUMMARY_ROW_SX, ...nodeSx]}>\n {href && !suppressNavigation ? (\n <AppLink href={href} underline=\"hover\" sx={SUMMARY_LABEL_SX}>\n {content}\n </AppLink>\n ) : (\n // Unresolved target, or a surface that must not navigate (canvas,\n // preview): same two-part shape, so the editor never shows a row\n // that behaves differently from the one that ships.\n //\n // Same ELEMENT too, not just the same box (AGL-1268). `href` comes\n // from the screens map, which differs between the render that\n // produced an ISR page and the render that hydrates it — a screen\n // published or unpublished in between. Switching `<a>` to `<span>`\n // on that would remount the subtree at hydration; an `<a>` with no\n // href is HTML's placeholder link, so the element is stable and the\n // row is still inert. Link Container settled on this shape first.\n <Link underline=\"none\" color=\"inherit\" sx={SUMMARY_LABEL_SX}>\n {content}\n </Link>\n )}\n <MuiAccordionSummary\n aria-label={label ? `Toggle ${label}` : 'Toggle section'}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n sx={SUMMARY_TOGGLE_SX}\n />\n </Box>\n )\n})\nAccordionSummaryElement.displayName = 'AglynAccordionSummary'\n\nexport const AccordionDetailsElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiAccordionDetails ref={ref} {...props} />)\nAccordionDetailsElement.displayName = 'AglynAccordionDetails'\n\nexport const accordionSchema: Aglyn.ComponentSchema<AccordionElementProps> = {\n $id: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion',\n description:\n 'Header that expands to reveal its details — FAQs, long forms, ' +\n 'anything progressive.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n // Structure is fixed by MUI: summary first, details second.\n restrictChildren: [\n Aglyn.LinealDirectiveFlag.LIMIT_TO,\n { components: [ACCORDION_SUMMARY_ID, ACCORDION_DETAILS_ID] },\n ],\n // …and it reads that structure BY INDEX, so the renderer has to hand it one\n // React child per node child (AGL-1237). Without this the summary swallowed\n // the details and every accordion on every published site expanded to reveal\n // an empty panel, with its content rendering unconditionally inside the\n // summary's heading.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'defaultExpanded',\n label: 'Start expanded?',\n description:\n 'If true, the panel is already open when a visitor arrives. The ' +\n 'canvas always shows it open so you can edit inside it.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disableGutters',\n label: 'Disable gutters?',\n description:\n 'If true, the panel keeps its height when expanded instead of ' +\n 'gaining extra margin — the tighter look for a stacked FAQ.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disabled',\n label: 'Disabled?',\n description: 'If true, visitors cannot expand the panel.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const accordionSummarySchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Summary',\n description: 'The clickable header row of an accordion.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n flags: {\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557). The row is a `<button>`, and a list or a link\n // inside one is invalid markup the parser unnests and a control a keyboard\n // cannot reach — the same constraint that made a LINKED header two sibling\n // controls rather than an anchor inside the toggle (AGL-1232).\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'screenId',\n label: 'Header links to',\n description:\n 'Optional. Give the header a destination and the row splits: the ' +\n 'text becomes a link to that page and the chevron beside it ' +\n 'opens the panel. That is the only way a header can do both — a ' +\n 'link inside the toggle button is invalid and unreachable by ' +\n 'keyboard. Leave it empty and the whole row toggles, as before.',\n component: Aglyn.FieldComponentType.SCREEN_SELECT,\n },\n ],\n}\n\nexport const accordionDetailsSchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Details',\n description: 'The region an accordion reveals when expanded.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n}\n\nconst panel = (summary: string, body: string) => ({\n $id: null,\n componentId: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n {\n $id: null,\n componentId: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n props: { children: summary },\n },\n {\n $id: null,\n componentId: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'body2', children: body },\n },\n ],\n },\n ],\n})\n\nexport const accordionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ACCORDION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Accordion',\n pluginId: BUNDLE_ID,\n description: 'One expandable panel with a header and body text',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: accordionSchema.icon,\n data: panel('Accordion header', 'The details revealed when expanded.'),\n },\n {\n $id: generatePresetId(ACCORDION_ID, 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Three stacked question panels, ready to edit',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n nodes: [\n panel('How do I get started?', 'Answer the first question here.'),\n panel('What does it cost?', 'Answer the second question here.'),\n panel('How do I get support?', 'Answer the third question here.'),\n ],\n },\n },\n]\n\nexport default AccordionElement\n"],"names":["Aglyn","mdiChevronDown","mdiUnfoldMoreHorizontal","AppLink","MdiIcon","MuiAccordion","MuiAccordionDetails","MuiAccordionSummary","Box","Link","forwardRef","isValidElement","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","withAuthorHtmlLabel","generatePresetId","ACCORDION_ID","ACCORDION_SUMMARY_ID","ACCORDION_DETAILS_ID","AccordionElement","props","ref","defaultExpanded","children","rest","editorInert","useScreenLink","undefined","expanded","displayName","SUMMARY_ROW_SX","display","alignItems","width","SUMMARY_LABEL_SX","flex","minWidth","px","py","SUMMARY_TOGGLE_SX","childText","node","String","Array","isArray","map","join","AccordionSummaryElement","screenId","html","href","suppressNavigation","expandIcon","path","sx","rowRest","nodeSx","label","trim","content","underline","color","aria-label","AccordionDetailsElement","accordionSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","restrictChildren","LinealDirectiveFlag","LIMIT_TO","components","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","SWITCH","accordionSummarySchema","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","SCREEN_SELECT","accordionDetailsSchema","panel","summary","body","componentId","nodes","variant","accordionPresets","type","NodeType","PRESET","data","direction"],"mappings":"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"}
|
|
@@ -655,7 +655,7 @@ export const blockPresets = [
|
|
|
655
655
|
type: Aglyn.NodeType.PRESET,
|
|
656
656
|
displayName: 'Nav Bar',
|
|
657
657
|
pluginId: BUNDLE_ID,
|
|
658
|
-
description: 'Header bar with brand text and
|
|
658
|
+
description: 'Header bar with brand text and page links',
|
|
659
659
|
category: Aglyn.ComponentCategory.BLOCKS,
|
|
660
660
|
icon: {
|
|
661
661
|
path: mdiPageLayoutHeader.path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiBullhorn,\n mdiCurrencyUsd,\n mdiFacebook,\n mdiFormatQuoteClose,\n mdiGestureTapButton,\n mdiGithub,\n mdiHelpCircle,\n mdiImage as mdiImageIcon,\n mdiImageText,\n mdiInstagram,\n mdiLinkedin,\n mdiPageLayoutFooter,\n mdiPageLayoutHeader,\n mdiPlayCircle,\n mdiRocketLaunchOutline,\n mdiTwitter,\n mdiViewGridOutline,\n mdiYoutube,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport Box from '@mui/material/Box'\nimport IconButton from '@mui/material/IconButton'\nimport Stack from '@mui/material/Stack'\nimport { forwardRef, useContext } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const VIDEO_EMBED_ID: Aglyn.ComponentId = 'videoEmbed'\nexport const SOCIAL_LINKS_ID: Aglyn.ComponentId = 'socialLinks'\n\n/* ── Video embed ─────────────────────────────────────────────────────────── */\n\nexport interface VideoEmbedProps {\n /** YouTube or Vimeo video URL. */\n url?: string\n /** CSS height (default 315px, 16:9-ish with full width). */\n height?: string\n}\n\n/**\n * Builds the embed src from a PARSED video id — the raw user URL is never\n * placed in the iframe, so only youtube-nocookie/vimeo player origins can\n * ever load (block-library security posture).\n */\nexport function parseVideoEmbedSrc(url: string): string | null {\n try {\n const parsed = new URL(url.trim())\n const host = parsed.hostname.replace(/^www\\./, '')\n if (host === 'youtu.be') {\n const id = parsed.pathname.slice(1)\n return /^[\\w-]{6,20}$/.test(id)\n ? `https://www.youtube-nocookie.com/embed/${id}`\n : null\n }\n if (host === 'youtube.com' || host === 'm.youtube.com') {\n const id =\n parsed.searchParams.get('v') ??\n (parsed.pathname.startsWith('/embed/')\n ? parsed.pathname.split('/')[2]\n : parsed.pathname.startsWith('/shorts/')\n ? parsed.pathname.split('/')[2]\n : '')\n return id && /^[\\w-]{6,20}$/.test(id)\n ? `https://www.youtube-nocookie.com/embed/${id}`\n : null\n }\n if (host === 'vimeo.com' || host === 'player.vimeo.com') {\n const id = parsed.pathname.split('/').find((part) => /^\\d{6,}$/.test(part))\n return id ? `https://player.vimeo.com/video/${id}` : null\n }\n } catch {\n /* invalid URL */\n }\n return null\n}\n\nconst VideoEmbed = forwardRef<HTMLDivElement, VideoEmbedProps>(\n (props, ref) => {\n const { url, height, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const src = url ? parseVideoEmbedSrc(url) : null\n if (!src) {\n // The hint is for the author, so only editing surfaces draw it; a\n // published page renders the bare element.\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n width: '100%',\n height: height || 315,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Video — paste a YouTube or Vimeo URL'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n component=\"iframe\"\n src={src}\n title=\"Embedded video\"\n loading=\"lazy\"\n allow=\"accelerometer; encrypted-media; picture-in-picture\"\n allowFullScreen\n sandbox=\"allow-scripts allow-same-origin allow-presentation allow-popups\"\n {...rest}\n sx={[\n {\n display: 'block',\n width: '100%',\n height: height || 315,\n border: 0,\n },\n ...nodeSx,\n ]}\n />\n )\n },\n)\nVideoEmbed.displayName = 'AglynVideoEmbed'\n\nexport const videoEmbedSchema: Aglyn.ComponentSchema<VideoEmbedProps> = {\n $id: VIDEO_EMBED_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Video',\n description: \"Embeds a YouTube or Vimeo player from the video's URL.\",\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiPlayCircle.path, sx: { color: '#d32f2f' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'url',\n description: 'YouTube or Vimeo video URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Video URL',\n },\n {\n name: 'height',\n description: 'Height of the embed — a number plus a unit, e.g. 315px or 50vh.',\n component: Aglyn.FieldComponentType.CSS_DIMENSION,\n label: 'Height',\n },\n ],\n}\n\n/* ── Social links ────────────────────────────────────────────────────────── */\n\nexport interface SocialLinksProps {\n twitter?: string\n instagram?: string\n facebook?: string\n linkedin?: string\n youtube?: string\n github?: string\n}\n\nconst SOCIAL_NETWORKS: Array<{\n key: keyof SocialLinksProps\n label: string\n path: string\n}> = [\n { key: 'twitter', label: 'Twitter / X', path: mdiTwitter.path },\n { key: 'instagram', label: 'Instagram', path: mdiInstagram.path },\n { key: 'facebook', label: 'Facebook', path: mdiFacebook.path },\n { key: 'linkedin', label: 'LinkedIn', path: mdiLinkedin.path },\n { key: 'youtube', label: 'YouTube', path: mdiYoutube.path },\n { key: 'github', label: 'GitHub', path: mdiGithub.path },\n]\n\nconst SAFE_SOCIAL_HREF = /^https:\\/\\//i\n\nconst SocialLinks = forwardRef<HTMLDivElement, SocialLinksProps>(\n (props, ref) => {\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The profile fields are this element's data, not attributes of its row.\n // Spread onto the Stack, each would be written into the page's HTML as a\n // second copy of the URL, including a URL the filter below refuses.\n const rowProps: Record<string, unknown> = { ...props }\n for (const { key } of SOCIAL_NETWORKS) delete rowProps[key]\n const entries = SOCIAL_NETWORKS.map(({ key, label, path }) => ({\n key,\n label,\n path,\n href: props[key]?.trim(),\n })).filter(\n (entry) => entry.href && SAFE_SOCIAL_HREF.test(entry.href),\n )\n return (\n <Stack ref={ref} direction=\"row\" spacing={0.5} {...rowProps}>\n {entries.length ? (\n entries.map((entry) => (\n <AppLink\n key={entry.key}\n componentVariant=\"icon-button\"\n href={entry.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={entry.label}\n size=\"small\"\n >\n <MdiIcon path={entry.path} />\n </AppLink>\n ))\n ) : suppressNavigation ? (\n // Editing surfaces only: a visitor has no Attributes panel.\n <Box\n sx={{\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n p: 1,\n }}\n >\n {'Social links — add profile URLs in Attributes'}\n </Box>\n ) : null}\n </Stack>\n )\n },\n)\nSocialLinks.displayName = 'AglynSocialLinks'\n\nexport const socialLinksSchema: Aglyn.ComponentSchema<SocialLinksProps> = {\n $id: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Social Links',\n description: 'A row of icon links to whichever social profiles you fill in.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTwitter.path, sx: { color: '#1976d2' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: SOCIAL_NETWORKS.map(({ key, label }) => ({\n name: key,\n description: `${label} profile URL (https).`,\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label,\n })),\n}\n\n/* ── Section presets (composed from existing components) ────────────────── */\n\n/**\n * Preset styling is authored on the node's OWN `sx`, never inside `props`\n * (AGL-1346).\n *\n * Both records render — `Leaf` composes `(sx, props.sx, node.sx)` — but\n * only `node.sx` is the one the Styles panel writes, so a preset that put\n * its styling in `props.sx` authored values the panel could display but no\n * click could ever change or clear. The renderer composes `node.sx` LAST,\n * so moving a preset's styles here changes nothing about how a fresh\n * insert looks; it only makes them editable. Existing documents keep their\n * `props.sx` and still render from it.\n *\n * `plugin.spec.ts` holds every preset in this bundle to that rule.\n */\nconst text = (variant: string, children: string, extra?: object) => {\n const { sx, ...props } = (extra ?? {}) as Record<string, unknown>\n return {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant, children, ...props },\n ...(sx ? { sx } : {}),\n }\n}\n\nconst testimonialCard = (quote: string, name: string) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n paddingTop: 3,\n paddingRight: 3,\n paddingBottom: 3,\n paddingLeft: 3,\n },\n nodes: [\n text('body1', `“${quote}”`),\n text('subtitle2', `— ${name}`),\n ],\n})\n\nconst pricingColumn = (\n tier: string,\n price: string,\n features: string[],\n highlighted?: boolean,\n) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n paddingTop: 3,\n paddingRight: 3,\n paddingBottom: 3,\n paddingLeft: 3,\n alignItems: 'center',\n ...(highlighted && {\n border: '2px solid',\n borderColor: 'primary.main',\n borderRadius: 2,\n }),\n },\n nodes: [\n text('h5', tier),\n text('h3', price),\n ...features.map((feature) => text('body2', feature)),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: highlighted ? 'contained' : 'outlined',\n children: 'Choose plan',\n },\n },\n ],\n})\n\nconst faqItem = (question: string, answer: string) => [\n text('subtitle1', question),\n text('body2', answer, { gutterBottom: true }),\n]\n\n/** Screen link with the visible-on-any-surface nav defaults (AGL-539). */\nconst navLink = (label: string) => ({\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: { children: label, color: 'inherit' },\n})\n\nconst featureColumn = (title: string, body: string) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n minWidth: 220,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n nodes: [text('h5', title), text('body1', body)],\n})\n\n/**\n * A section block's outer band, with its content held to a readable width\n * (AGL-2544).\n *\n * These presets used to be a bare `muiStack` dropped straight into\n * `Document`, so every one of them rendered edge to edge: a Feature Grid ran\n * the full viewport while the page's prose sat at 900px, and the FAQ — which\n * had `maxWidth` but no auto margins — hugged the left edge. That is the\n * result of the documented happy path on a fresh screen, with no misuse, and\n * a subscriber has no reason to suspect the block needs wrapping.\n *\n * The band and the content are separated rather than the whole thing being\n * wrapped, because a CTA or hero genuinely wants a full-bleed background with\n * contained content — wrapping the outer node would take that away to fix the\n * width. So `bandSx` (padding, background) stays outside and the Container\n * goes within it.\n *\n * `layout` moves a row block's own direction onto an inner Stack: dropping a\n * Container between a `direction: 'row'` Stack and its columns would leave\n * the Container as the single flex child and collapse the row.\n */\nconst section = ({\n bandSx,\n layout,\n layoutSx,\n maxWidth = 'lg',\n nodes,\n}: {\n bandSx?: Record<string, unknown>\n layout?: Record<string, unknown>\n layoutSx?: Record<string, unknown>\n maxWidth?: string\n nodes: unknown[]\n}) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: {},\n ...(bandSx ? { sx: bandSx } : {}),\n nodes: [\n {\n $id: null,\n componentId: 'muiContainer',\n pluginId: BUNDLE_ID,\n props: { maxWidth },\n nodes:\n layout || layoutSx\n ? [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: layout ?? {},\n ...(layoutSx ? { sx: layoutSx } : {}),\n nodes,\n },\n ]\n : nodes,\n },\n ],\n})\n\nexport const blockPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(VIDEO_EMBED_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Video',\n pluginId: BUNDLE_ID,\n description: 'Embedded YouTube or Vimeo video',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiPlayCircle.path, sx: { color: '#d32f2f' } },\n data: {\n $id: null,\n componentId: VIDEO_EMBED_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SOCIAL_LINKS_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Social Links',\n pluginId: BUNDLE_ID,\n description: 'Icon links to social profiles',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTwitter.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId('muiStack', 'gallery'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image Gallery',\n pluginId: BUNDLE_ID,\n description: 'Three-across image row',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiImageIcon.path, sx: { color: '#7b1fa2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [1, 2, 3].map((index) => ({\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: `Gallery image ${index}`, height: '240px' },\n })),\n },\n },\n {\n $id: generatePresetId('muiStack', 'testimonials'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Testimonials',\n pluginId: BUNDLE_ID,\n description: 'Three customer quotes',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiFormatQuoteClose.path, sx: { color: '#0288d1' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [\n testimonialCard(\n 'Exactly what our team needed to ship the site fast.',\n 'Customer One',\n ),\n testimonialCard(\n 'The editor is a joy — we update pages in minutes.',\n 'Customer Two',\n ),\n testimonialCard(\n 'Best decision we made for our online presence.',\n 'Customer Three',\n ),\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'pricing'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Pricing Table',\n pluginId: BUNDLE_ID,\n description: 'Three plan columns with features and CTAs',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiCurrencyUsd.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [\n pricingColumn('Basic', '$9', ['One project', 'Email support']),\n pricingColumn(\n 'Pro',\n '$29',\n ['Five projects', 'Priority support', 'Analytics'],\n true,\n ),\n pricingColumn('Team', '$79', [\n 'Unlimited projects',\n 'Dedicated support',\n 'SSO',\n ]),\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Question and answer list',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiHelpCircle.path, sx: { color: '#f57c00' } },\n data: section({\n bandSx: { paddingTop: 4, paddingBottom: 4 },\n // `md`, not the old bare `maxWidth: 720`. The width was never the\n // defect — the missing auto margins were, which is why this block\n // rendered hard against the left viewport edge.\n maxWidth: 'md',\n layout: { spacing: 1 },\n nodes: [\n text('h4', 'Frequently asked questions', { gutterBottom: true }),\n ...faqItem(\n 'How do I get started?',\n 'Answer the question in a sentence or two so visitors keep reading.',\n ),\n ...faqItem(\n 'Can I change plans later?',\n 'Yes — describe how flexible your offering is.',\n ),\n ...faqItem(\n 'Do you offer support?',\n 'Tell visitors how and when they can reach you.',\n ),\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'announcement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Announcement Bar',\n pluginId: BUNDLE_ID,\n description: 'Accent strip with a message and link',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiBullhorn.path, sx: { color: '#c2185b' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: {\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'primary.main',\n color: 'primary.contrastText',\n },\n nodes: [\n text('body2', 'Big news — announce a launch, sale, or update here.'),\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: { children: 'Learn more', size: 'small', color: 'inherit' },\n },\n ],\n },\n },\n // ── Composed page sections (AGL-539) ──────────────────────────────────\n // The pieces every real site needs, insertable without raw-JSON\n // authoring. Node shapes mirror apps/console starter templates; only\n // persisted component ids are referenced.\n {\n $id: generatePresetId('muiAppBar', 'navbar'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Nav Bar',\n pluginId: BUNDLE_ID,\n description: 'Header bar with brand text and screen links',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPageLayoutHeader.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiAppBar',\n pluginId: BUNDLE_ID,\n // Static so the bar sits in the page flow; switch the Position\n // attribute to Sticky/Fixed for a pinned header.\n props: { position: 'static' },\n nodes: [\n {\n $id: null,\n componentId: 'muiToolbar',\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n text('h6', 'Your Brand', { sx: { flexGrow: 1 } }),\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n nodes: [navLink('Home'), navLink('About'), navLink('Contact')],\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'hero'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Hero',\n pluginId: BUNDLE_ID,\n description: 'Centered headline, tagline, and call-to-action button',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiRocketLaunchOutline.path, sx: { color: '#f57c00' } },\n data: section({\n bandSx: {\n paddingTop: 10,\n paddingBottom: 10,\n paddingLeft: 4,\n paddingRight: 4,\n },\n layout: { spacing: 2 },\n layoutSx: { alignItems: 'center' },\n nodes: [\n text('h2', 'A headline that sells your idea', { align: 'center' }),\n text('h6', 'One clear sentence about the value you deliver.', {\n align: 'center',\n }),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'contained',\n size: 'large',\n children: 'Get started',\n },\n },\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'features'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Feature Grid',\n pluginId: BUNDLE_ID,\n description: 'Three selling points in a wrapping row',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: section({\n bandSx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n },\n layout: { direction: 'row', spacing: 2 },\n layoutSx: { flexWrap: 'wrap' },\n nodes: [\n featureColumn('Fast', 'Explain the first reason customers pick you.'),\n featureColumn(\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'imagetext'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image + Text',\n pluginId: BUNDLE_ID,\n description: 'Image beside a heading, copy, and link',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiImageText.path, sx: { color: '#7b1fa2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 4 },\n sx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n alignItems: 'center',\n flexWrap: 'wrap',\n },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: {},\n sx: { flex: 1, minWidth: 280 },\n nodes: [\n {\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: 'Feature image', height: '320px' },\n },\n ],\n },\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 2 },\n sx: { flex: 1, minWidth: 280 },\n nodes: [\n text('h4', 'Show, then tell'),\n text(\n 'body1',\n 'Pair a strong visual with a short paragraph that explains ' +\n 'what visitors are looking at and why it matters.',\n ),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: { variant: 'outlined', children: 'Learn more' },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'cta'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Call to Action',\n pluginId: BUNDLE_ID,\n description: 'Accent band with a heading and button',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#c2185b' } },\n data: section({\n // The accent band stays full-bleed — that is the point of a CTA —\n // while its content is held to the page's width inside it.\n bandSx: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingLeft: 4,\n paddingRight: 4,\n backgroundColor: 'primary.main',\n color: 'primary.contrastText',\n },\n maxWidth: 'md',\n layout: { spacing: 2 },\n layoutSx: { alignItems: 'center' },\n nodes: [\n text('h4', 'Ready to get started?', { align: 'center' }),\n text('body1', 'Tell visitors the one thing to do next.', {\n align: 'center',\n }),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'contained',\n color: 'primary',\n size: 'large',\n children: 'Get started',\n },\n },\n ],\n }),\n },\n // Contact Section moved to @aglyn/plugins-forms (AGL-395): a preset that\n // places `form` nodes has to stamp them with the bundle that registers\n // them, and this one is the only block preset that does.\n {\n $id: generatePresetId('section', 'footer'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Footer',\n pluginId: BUNDLE_ID,\n description: 'Semantic footer with brand, links, social, and copyright',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPageLayoutFooter.path, sx: { color: '#455a64' } },\n data: {\n $id: null,\n componentId: 'section',\n pluginId: BUNDLE_ID,\n props: { element: 'footer', ariaLabel: 'Site footer' },\n sx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n borderTop: 1,\n borderColor: 'divider',\n },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 2 },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: {\n justifyContent: 'space-between',\n alignItems: 'center',\n flexWrap: 'wrap',\n },\n nodes: [\n text('h6', 'Your Brand'),\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n nodes: [\n navLink('Home'),\n navLink('About'),\n navLink('Contact'),\n ],\n },\n ],\n },\n {\n $id: null,\n componentId: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n text(\n 'caption',\n '© Your Company. All rights reserved.',\n { color: 'text.secondary' },\n ),\n ],\n },\n ],\n },\n },\n]\n\nexport { SocialLinks, VideoEmbed }\nexport default VideoEmbed\n"],"names":["Aglyn","mdiBullhorn","mdiCurrencyUsd","mdiFacebook","mdiFormatQuoteClose","mdiGestureTapButton","mdiGithub","mdiHelpCircle","mdiImage","mdiImageIcon","mdiImageText","mdiInstagram","mdiLinkedin","mdiPageLayoutFooter","mdiPageLayoutHeader","mdiPlayCircle","mdiRocketLaunchOutline","mdiTwitter","mdiViewGridOutline","mdiYoutube","AppLink","MdiIcon","Box","Stack","forwardRef","useContext","BUNDLE_ID","generatePresetId","VIDEO_EMBED_ID","SOCIAL_LINKS_ID","parseVideoEmbedSrc","url","parsed","URL","trim","host","hostname","replace","id","pathname","slice","test","searchParams","get","startsWith","split","find","part","VideoEmbed","props","ref","height","rest","nodeSx","Array","isArray","suppressNavigation","ScreenLinkContext","src","sx","width","display","alignItems","justifyContent","border","borderColor","color","fontSize","fontFamily","component","title","loading","allow","allowFullScreen","sandbox","displayName","videoEmbedSchema","$id","pluginId","description","category","ComponentCategory","MEDIA","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","FieldComponentType","TEXT_FIELD","label","CSS_DIMENSION","SOCIAL_NETWORKS","key","SAFE_SOCIAL_HREF","SocialLinks","rowProps","entries","map","href","filter","entry","direction","spacing","length","componentVariant","target","rel","aria-label","size","p","socialLinksSchema","NAVIGATION","text","variant","children","extra","componentId","testimonialCard","quote","flex","paddingTop","paddingRight","paddingBottom","paddingLeft","nodes","pricingColumn","tier","price","features","highlighted","borderRadius","feature","faqItem","question","answer","gutterBottom","navLink","featureColumn","body","minWidth","section","bandSx","layout","layoutSx","maxWidth","blockPresets","type","NodeType","PRESET","data","BLOCKS","index","alt","backgroundColor","position","flexGrow","align","flexWrap","element","ariaLabel","borderTop"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiBullhorn,\n mdiCurrencyUsd,\n mdiFacebook,\n mdiFormatQuoteClose,\n mdiGestureTapButton,\n mdiGithub,\n mdiHelpCircle,\n mdiImage as mdiImageIcon,\n mdiImageText,\n mdiInstagram,\n mdiLinkedin,\n mdiPageLayoutFooter,\n mdiPageLayoutHeader,\n mdiPlayCircle,\n mdiRocketLaunchOutline,\n mdiTwitter,\n mdiViewGridOutline,\n mdiYoutube,\n} from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport Box from '@mui/material/Box'\nimport IconButton from '@mui/material/IconButton'\nimport Stack from '@mui/material/Stack'\nimport { forwardRef, useContext } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const VIDEO_EMBED_ID: Aglyn.ComponentId = 'videoEmbed'\nexport const SOCIAL_LINKS_ID: Aglyn.ComponentId = 'socialLinks'\n\n/* ── Video embed ─────────────────────────────────────────────────────────── */\n\nexport interface VideoEmbedProps {\n /** YouTube or Vimeo video URL. */\n url?: string\n /** CSS height (default 315px, 16:9-ish with full width). */\n height?: string\n}\n\n/**\n * Builds the embed src from a PARSED video id — the raw user URL is never\n * placed in the iframe, so only youtube-nocookie/vimeo player origins can\n * ever load (block-library security posture).\n */\nexport function parseVideoEmbedSrc(url: string): string | null {\n try {\n const parsed = new URL(url.trim())\n const host = parsed.hostname.replace(/^www\\./, '')\n if (host === 'youtu.be') {\n const id = parsed.pathname.slice(1)\n return /^[\\w-]{6,20}$/.test(id)\n ? `https://www.youtube-nocookie.com/embed/${id}`\n : null\n }\n if (host === 'youtube.com' || host === 'm.youtube.com') {\n const id =\n parsed.searchParams.get('v') ??\n (parsed.pathname.startsWith('/embed/')\n ? parsed.pathname.split('/')[2]\n : parsed.pathname.startsWith('/shorts/')\n ? parsed.pathname.split('/')[2]\n : '')\n return id && /^[\\w-]{6,20}$/.test(id)\n ? `https://www.youtube-nocookie.com/embed/${id}`\n : null\n }\n if (host === 'vimeo.com' || host === 'player.vimeo.com') {\n const id = parsed.pathname.split('/').find((part) => /^\\d{6,}$/.test(part))\n return id ? `https://player.vimeo.com/video/${id}` : null\n }\n } catch {\n /* invalid URL */\n }\n return null\n}\n\nconst VideoEmbed = forwardRef<HTMLDivElement, VideoEmbedProps>(\n (props, ref) => {\n const { url, height, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [props['sx']]\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const src = url ? parseVideoEmbedSrc(url) : null\n if (!src) {\n // The hint is for the author, so only editing surfaces draw it; a\n // published page renders the bare element.\n if (!suppressNavigation) return <Box ref={ref} {...rest} />\n return (\n <Box\n ref={ref}\n {...rest}\n sx={[\n {\n width: '100%',\n height: height || 315,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n },\n ...nodeSx,\n ]}\n >\n {'Video — paste a YouTube or Vimeo URL'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n component=\"iframe\"\n src={src}\n title=\"Embedded video\"\n loading=\"lazy\"\n allow=\"accelerometer; encrypted-media; picture-in-picture\"\n allowFullScreen\n sandbox=\"allow-scripts allow-same-origin allow-presentation allow-popups\"\n {...rest}\n sx={[\n {\n display: 'block',\n width: '100%',\n height: height || 315,\n border: 0,\n },\n ...nodeSx,\n ]}\n />\n )\n },\n)\nVideoEmbed.displayName = 'AglynVideoEmbed'\n\nexport const videoEmbedSchema: Aglyn.ComponentSchema<VideoEmbedProps> = {\n $id: VIDEO_EMBED_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Video',\n description: \"Embeds a YouTube or Vimeo player from the video's URL.\",\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiPlayCircle.path, sx: { color: '#d32f2f' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'url',\n description: 'YouTube or Vimeo video URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Video URL',\n },\n {\n name: 'height',\n description: 'Height of the embed — a number plus a unit, e.g. 315px or 50vh.',\n component: Aglyn.FieldComponentType.CSS_DIMENSION,\n label: 'Height',\n },\n ],\n}\n\n/* ── Social links ────────────────────────────────────────────────────────── */\n\nexport interface SocialLinksProps {\n twitter?: string\n instagram?: string\n facebook?: string\n linkedin?: string\n youtube?: string\n github?: string\n}\n\nconst SOCIAL_NETWORKS: Array<{\n key: keyof SocialLinksProps\n label: string\n path: string\n}> = [\n { key: 'twitter', label: 'Twitter / X', path: mdiTwitter.path },\n { key: 'instagram', label: 'Instagram', path: mdiInstagram.path },\n { key: 'facebook', label: 'Facebook', path: mdiFacebook.path },\n { key: 'linkedin', label: 'LinkedIn', path: mdiLinkedin.path },\n { key: 'youtube', label: 'YouTube', path: mdiYoutube.path },\n { key: 'github', label: 'GitHub', path: mdiGithub.path },\n]\n\nconst SAFE_SOCIAL_HREF = /^https:\\/\\//i\n\nconst SocialLinks = forwardRef<HTMLDivElement, SocialLinksProps>(\n (props, ref) => {\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n // The profile fields are this element's data, not attributes of its row.\n // Spread onto the Stack, each would be written into the page's HTML as a\n // second copy of the URL, including a URL the filter below refuses.\n const rowProps: Record<string, unknown> = { ...props }\n for (const { key } of SOCIAL_NETWORKS) delete rowProps[key]\n const entries = SOCIAL_NETWORKS.map(({ key, label, path }) => ({\n key,\n label,\n path,\n href: props[key]?.trim(),\n })).filter(\n (entry) => entry.href && SAFE_SOCIAL_HREF.test(entry.href),\n )\n return (\n <Stack ref={ref} direction=\"row\" spacing={0.5} {...rowProps}>\n {entries.length ? (\n entries.map((entry) => (\n <AppLink\n key={entry.key}\n componentVariant=\"icon-button\"\n href={entry.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={entry.label}\n size=\"small\"\n >\n <MdiIcon path={entry.path} />\n </AppLink>\n ))\n ) : suppressNavigation ? (\n // Editing surfaces only: a visitor has no Attributes panel.\n <Box\n sx={{\n color: 'text.secondary',\n fontSize: 12,\n fontFamily: 'system-ui, sans-serif',\n p: 1,\n }}\n >\n {'Social links — add profile URLs in Attributes'}\n </Box>\n ) : null}\n </Stack>\n )\n },\n)\nSocialLinks.displayName = 'AglynSocialLinks'\n\nexport const socialLinksSchema: Aglyn.ComponentSchema<SocialLinksProps> = {\n $id: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Social Links',\n description: 'A row of icon links to whichever social profiles you fill in.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTwitter.path, sx: { color: '#1976d2' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: SOCIAL_NETWORKS.map(({ key, label }) => ({\n name: key,\n description: `${label} profile URL (https).`,\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label,\n })),\n}\n\n/* ── Section presets (composed from existing components) ────────────────── */\n\n/**\n * Preset styling is authored on the node's OWN `sx`, never inside `props`\n * (AGL-1346).\n *\n * Both records render — `Leaf` composes `(sx, props.sx, node.sx)` — but\n * only `node.sx` is the one the Styles panel writes, so a preset that put\n * its styling in `props.sx` authored values the panel could display but no\n * click could ever change or clear. The renderer composes `node.sx` LAST,\n * so moving a preset's styles here changes nothing about how a fresh\n * insert looks; it only makes them editable. Existing documents keep their\n * `props.sx` and still render from it.\n *\n * `plugin.spec.ts` holds every preset in this bundle to that rule.\n */\nconst text = (variant: string, children: string, extra?: object) => {\n const { sx, ...props } = (extra ?? {}) as Record<string, unknown>\n return {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant, children, ...props },\n ...(sx ? { sx } : {}),\n }\n}\n\nconst testimonialCard = (quote: string, name: string) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n paddingTop: 3,\n paddingRight: 3,\n paddingBottom: 3,\n paddingLeft: 3,\n },\n nodes: [\n text('body1', `“${quote}”`),\n text('subtitle2', `— ${name}`),\n ],\n})\n\nconst pricingColumn = (\n tier: string,\n price: string,\n features: string[],\n highlighted?: boolean,\n) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n paddingTop: 3,\n paddingRight: 3,\n paddingBottom: 3,\n paddingLeft: 3,\n alignItems: 'center',\n ...(highlighted && {\n border: '2px solid',\n borderColor: 'primary.main',\n borderRadius: 2,\n }),\n },\n nodes: [\n text('h5', tier),\n text('h3', price),\n ...features.map((feature) => text('body2', feature)),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: highlighted ? 'contained' : 'outlined',\n children: 'Choose plan',\n },\n },\n ],\n})\n\nconst faqItem = (question: string, answer: string) => [\n text('subtitle1', question),\n text('body2', answer, { gutterBottom: true }),\n]\n\n/** Screen link with the visible-on-any-surface nav defaults (AGL-539). */\nconst navLink = (label: string) => ({\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: { children: label, color: 'inherit' },\n})\n\nconst featureColumn = (title: string, body: string) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n sx: {\n flex: 1,\n minWidth: 220,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n nodes: [text('h5', title), text('body1', body)],\n})\n\n/**\n * A section block's outer band, with its content held to a readable width\n * (AGL-2544).\n *\n * These presets used to be a bare `muiStack` dropped straight into\n * `Document`, so every one of them rendered edge to edge: a Feature Grid ran\n * the full viewport while the page's prose sat at 900px, and the FAQ — which\n * had `maxWidth` but no auto margins — hugged the left edge. That is the\n * result of the documented happy path on a fresh screen, with no misuse, and\n * a subscriber has no reason to suspect the block needs wrapping.\n *\n * The band and the content are separated rather than the whole thing being\n * wrapped, because a CTA or hero genuinely wants a full-bleed background with\n * contained content — wrapping the outer node would take that away to fix the\n * width. So `bandSx` (padding, background) stays outside and the Container\n * goes within it.\n *\n * `layout` moves a row block's own direction onto an inner Stack: dropping a\n * Container between a `direction: 'row'` Stack and its columns would leave\n * the Container as the single flex child and collapse the row.\n */\nconst section = ({\n bandSx,\n layout,\n layoutSx,\n maxWidth = 'lg',\n nodes,\n}: {\n bandSx?: Record<string, unknown>\n layout?: Record<string, unknown>\n layoutSx?: Record<string, unknown>\n maxWidth?: string\n nodes: unknown[]\n}) => ({\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: {},\n ...(bandSx ? { sx: bandSx } : {}),\n nodes: [\n {\n $id: null,\n componentId: 'muiContainer',\n pluginId: BUNDLE_ID,\n props: { maxWidth },\n nodes:\n layout || layoutSx\n ? [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: layout ?? {},\n ...(layoutSx ? { sx: layoutSx } : {}),\n nodes,\n },\n ]\n : nodes,\n },\n ],\n})\n\nexport const blockPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(VIDEO_EMBED_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Video',\n pluginId: BUNDLE_ID,\n description: 'Embedded YouTube or Vimeo video',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiPlayCircle.path, sx: { color: '#d32f2f' } },\n data: {\n $id: null,\n componentId: VIDEO_EMBED_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SOCIAL_LINKS_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Social Links',\n pluginId: BUNDLE_ID,\n description: 'Icon links to social profiles',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiTwitter.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId('muiStack', 'gallery'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image Gallery',\n pluginId: BUNDLE_ID,\n description: 'Three-across image row',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiImageIcon.path, sx: { color: '#7b1fa2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [1, 2, 3].map((index) => ({\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: `Gallery image ${index}`, height: '240px' },\n })),\n },\n },\n {\n $id: generatePresetId('muiStack', 'testimonials'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Testimonials',\n pluginId: BUNDLE_ID,\n description: 'Three customer quotes',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiFormatQuoteClose.path, sx: { color: '#0288d1' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [\n testimonialCard(\n 'Exactly what our team needed to ship the site fast.',\n 'Customer One',\n ),\n testimonialCard(\n 'The editor is a joy — we update pages in minutes.',\n 'Customer Two',\n ),\n testimonialCard(\n 'Best decision we made for our online presence.',\n 'Customer Three',\n ),\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'pricing'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Pricing Table',\n pluginId: BUNDLE_ID,\n description: 'Three plan columns with features and CTAs',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiCurrencyUsd.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: { paddingTop: 4, paddingBottom: 4 },\n nodes: [\n pricingColumn('Basic', '$9', ['One project', 'Email support']),\n pricingColumn(\n 'Pro',\n '$29',\n ['Five projects', 'Priority support', 'Analytics'],\n true,\n ),\n pricingColumn('Team', '$79', [\n 'Unlimited projects',\n 'Dedicated support',\n 'SSO',\n ]),\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Question and answer list',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiHelpCircle.path, sx: { color: '#f57c00' } },\n data: section({\n bandSx: { paddingTop: 4, paddingBottom: 4 },\n // `md`, not the old bare `maxWidth: 720`. The width was never the\n // defect — the missing auto margins were, which is why this block\n // rendered hard against the left viewport edge.\n maxWidth: 'md',\n layout: { spacing: 1 },\n nodes: [\n text('h4', 'Frequently asked questions', { gutterBottom: true }),\n ...faqItem(\n 'How do I get started?',\n 'Answer the question in a sentence or two so visitors keep reading.',\n ),\n ...faqItem(\n 'Can I change plans later?',\n 'Yes — describe how flexible your offering is.',\n ),\n ...faqItem(\n 'Do you offer support?',\n 'Tell visitors how and when they can reach you.',\n ),\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'announcement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Announcement Bar',\n pluginId: BUNDLE_ID,\n description: 'Accent strip with a message and link',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiBullhorn.path, sx: { color: '#c2185b' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: {\n paddingTop: 1,\n paddingBottom: 1,\n paddingLeft: 2,\n paddingRight: 2,\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'primary.main',\n color: 'primary.contrastText',\n },\n nodes: [\n text('body2', 'Big news — announce a launch, sale, or update here.'),\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: { children: 'Learn more', size: 'small', color: 'inherit' },\n },\n ],\n },\n },\n // ── Composed page sections (AGL-539) ──────────────────────────────────\n // The pieces every real site needs, insertable without raw-JSON\n // authoring. Node shapes mirror apps/console starter templates; only\n // persisted component ids are referenced.\n {\n $id: generatePresetId('muiAppBar', 'navbar'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Nav Bar',\n pluginId: BUNDLE_ID,\n description: 'Header bar with brand text and page links',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPageLayoutHeader.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiAppBar',\n pluginId: BUNDLE_ID,\n // Static so the bar sits in the page flow; switch the Position\n // attribute to Sticky/Fixed for a pinned header.\n props: { position: 'static' },\n nodes: [\n {\n $id: null,\n componentId: 'muiToolbar',\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n text('h6', 'Your Brand', { sx: { flexGrow: 1 } }),\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n nodes: [navLink('Home'), navLink('About'), navLink('Contact')],\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'hero'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Hero',\n pluginId: BUNDLE_ID,\n description: 'Centered headline, tagline, and call-to-action button',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiRocketLaunchOutline.path, sx: { color: '#f57c00' } },\n data: section({\n bandSx: {\n paddingTop: 10,\n paddingBottom: 10,\n paddingLeft: 4,\n paddingRight: 4,\n },\n layout: { spacing: 2 },\n layoutSx: { alignItems: 'center' },\n nodes: [\n text('h2', 'A headline that sells your idea', { align: 'center' }),\n text('h6', 'One clear sentence about the value you deliver.', {\n align: 'center',\n }),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'contained',\n size: 'large',\n children: 'Get started',\n },\n },\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'features'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Feature Grid',\n pluginId: BUNDLE_ID,\n description: 'Three selling points in a wrapping row',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiViewGridOutline.path, sx: { color: '#2e7d32' } },\n data: section({\n bandSx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n },\n layout: { direction: 'row', spacing: 2 },\n layoutSx: { flexWrap: 'wrap' },\n nodes: [\n featureColumn('Fast', 'Explain the first reason customers pick you.'),\n featureColumn(\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n }),\n },\n {\n $id: generatePresetId('muiStack', 'imagetext'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image + Text',\n pluginId: BUNDLE_ID,\n description: 'Image beside a heading, copy, and link',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiImageText.path, sx: { color: '#7b1fa2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 4 },\n sx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n alignItems: 'center',\n flexWrap: 'wrap',\n },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: {},\n sx: { flex: 1, minWidth: 280 },\n nodes: [\n {\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: 'Feature image', height: '320px' },\n },\n ],\n },\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 2 },\n sx: { flex: 1, minWidth: 280 },\n nodes: [\n text('h4', 'Show, then tell'),\n text(\n 'body1',\n 'Pair a strong visual with a short paragraph that explains ' +\n 'what visitors are looking at and why it matters.',\n ),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: { variant: 'outlined', children: 'Learn more' },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId('muiStack', 'cta'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Call to Action',\n pluginId: BUNDLE_ID,\n description: 'Accent band with a heading and button',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#c2185b' } },\n data: section({\n // The accent band stays full-bleed — that is the point of a CTA —\n // while its content is held to the page's width inside it.\n bandSx: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingLeft: 4,\n paddingRight: 4,\n backgroundColor: 'primary.main',\n color: 'primary.contrastText',\n },\n maxWidth: 'md',\n layout: { spacing: 2 },\n layoutSx: { alignItems: 'center' },\n nodes: [\n text('h4', 'Ready to get started?', { align: 'center' }),\n text('body1', 'Tell visitors the one thing to do next.', {\n align: 'center',\n }),\n {\n $id: null,\n componentId: 'muiButton',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'contained',\n color: 'primary',\n size: 'large',\n children: 'Get started',\n },\n },\n ],\n }),\n },\n // Contact Section moved to @aglyn/plugins-forms (AGL-395): a preset that\n // places `form` nodes has to stamp them with the bundle that registers\n // them, and this one is the only block preset that does.\n {\n $id: generatePresetId('section', 'footer'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Footer',\n pluginId: BUNDLE_ID,\n description: 'Semantic footer with brand, links, social, and copyright',\n category: Aglyn.ComponentCategory.BLOCKS,\n icon: { path: mdiPageLayoutFooter.path, sx: { color: '#455a64' } },\n data: {\n $id: null,\n componentId: 'section',\n pluginId: BUNDLE_ID,\n props: { element: 'footer', ariaLabel: 'Site footer' },\n sx: {\n paddingLeft: 4,\n paddingRight: 4,\n paddingTop: 6,\n paddingBottom: 6,\n borderTop: 1,\n borderColor: 'divider',\n },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 2 },\n nodes: [\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 2 },\n sx: {\n justifyContent: 'space-between',\n alignItems: 'center',\n flexWrap: 'wrap',\n },\n nodes: [\n text('h6', 'Your Brand'),\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n nodes: [\n navLink('Home'),\n navLink('About'),\n navLink('Contact'),\n ],\n },\n ],\n },\n {\n $id: null,\n componentId: SOCIAL_LINKS_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n text(\n 'caption',\n '© Your Company. All rights reserved.',\n { color: 'text.secondary' },\n ),\n ],\n },\n ],\n },\n },\n]\n\nexport { SocialLinks, VideoEmbed }\nexport default VideoEmbed\n"],"names":["Aglyn","mdiBullhorn","mdiCurrencyUsd","mdiFacebook","mdiFormatQuoteClose","mdiGestureTapButton","mdiGithub","mdiHelpCircle","mdiImage","mdiImageIcon","mdiImageText","mdiInstagram","mdiLinkedin","mdiPageLayoutFooter","mdiPageLayoutHeader","mdiPlayCircle","mdiRocketLaunchOutline","mdiTwitter","mdiViewGridOutline","mdiYoutube","AppLink","MdiIcon","Box","Stack","forwardRef","useContext","BUNDLE_ID","generatePresetId","VIDEO_EMBED_ID","SOCIAL_LINKS_ID","parseVideoEmbedSrc","url","parsed","URL","trim","host","hostname","replace","id","pathname","slice","test","searchParams","get","startsWith","split","find","part","VideoEmbed","props","ref","height","rest","nodeSx","Array","isArray","suppressNavigation","ScreenLinkContext","src","sx","width","display","alignItems","justifyContent","border","borderColor","color","fontSize","fontFamily","component","title","loading","allow","allowFullScreen","sandbox","displayName","videoEmbedSchema","$id","pluginId","description","category","ComponentCategory","MEDIA","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","name","FieldComponentType","TEXT_FIELD","label","CSS_DIMENSION","SOCIAL_NETWORKS","key","SAFE_SOCIAL_HREF","SocialLinks","rowProps","entries","map","href","filter","entry","direction","spacing","length","componentVariant","target","rel","aria-label","size","p","socialLinksSchema","NAVIGATION","text","variant","children","extra","componentId","testimonialCard","quote","flex","paddingTop","paddingRight","paddingBottom","paddingLeft","nodes","pricingColumn","tier","price","features","highlighted","borderRadius","feature","faqItem","question","answer","gutterBottom","navLink","featureColumn","body","minWidth","section","bandSx","layout","layoutSx","maxWidth","blockPresets","type","NodeType","PRESET","data","BLOCKS","index","alt","backgroundColor","position","flexGrow","align","flexWrap","element","ariaLabel","borderTop"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SACEC,WAAW,EACXC,cAAc,EACdC,WAAW,EACXC,mBAAmB,EACnBC,mBAAmB,EACnBC,SAAS,EACTC,aAAa,EACbC,YAAYC,YAAY,EACxBC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,mBAAmB,EACnBC,mBAAmB,EACnBC,aAAa,EACbC,sBAAsB,EACtBC,UAAU,EACVC,kBAAkB,EAClBC,UAAU,QACL,yBAAwB;AAC/B,SAASC,OAAO,EAAEC,OAAO,QAAQ,uBAAsB;AACvD,OAAOC,SAAS,oBAAmB;AAEnC,OAAOC,WAAW,sBAAqB;AACvC,SAASC,UAAU,EAAEC,UAAU,QAAQ,QAAO;AAC9C,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,iBAAoC,aAAY;AAC7D,OAAO,MAAMC,kBAAqC,cAAa;AAW/D;;;;CAIC,GACD,OAAO,SAASC,mBAAmBC,GAAW;IAC5C,IAAI;QACF,MAAMC,SAAS,IAAIC,IAAIF,IAAIG,IAAI;QAC/B,MAAMC,OAAOH,OAAOI,QAAQ,CAACC,OAAO,CAAC,UAAU;QAC/C,IAAIF,SAAS,YAAY;YACvB,MAAMG,KAAKN,OAAOO,QAAQ,CAACC,KAAK,CAAC;YACjC,OAAO,gBAAgBC,IAAI,CAACH,MACxB,CAAC,uCAAuC,EAAEA,IAAI,GAC9C;QACN;QACA,IAAIH,SAAS,iBAAiBA,SAAS,iBAAiB;gBAEpDH;YADF,MAAMM,MACJN,2BAAAA,OAAOU,YAAY,CAACC,GAAG,CAAC,gBAAxBX,2BACCA,OAAOO,QAAQ,CAACK,UAAU,CAAC,aACxBZ,OAAOO,QAAQ,CAACM,KAAK,CAAC,IAAI,CAAC,EAAE,GAC7Bb,OAAOO,QAAQ,CAACK,UAAU,CAAC,cACzBZ,OAAOO,QAAQ,CAACM,KAAK,CAAC,IAAI,CAAC,EAAE,GAC7B;YACR,OAAOP,MAAM,gBAAgBG,IAAI,CAACH,MAC9B,CAAC,uCAAuC,EAAEA,IAAI,GAC9C;QACN;QACA,IAAIH,SAAS,eAAeA,SAAS,oBAAoB;YACvD,MAAMG,KAAKN,OAAOO,QAAQ,CAACM,KAAK,CAAC,KAAKC,IAAI,CAAC,CAACC,OAAS,WAAWN,IAAI,CAACM;YACrE,OAAOT,KAAK,CAAC,+BAA+B,EAAEA,IAAI,GAAG;QACvD;IACF,EAAE,eAAM;IACN,eAAe,GACjB;IACA,OAAO;AACT;AAEA,MAAMU,2BAAaxB,WACjB,CAACyB,OAAOC;IACN,MAAM,EAAEnB,GAAG,EAAEoB,MAAM,EAAW,GAAGF,OAATG,wCAASH;;;;IACjC,yEAAyE;IACzE,MAAMI,SAASC,MAAMC,OAAO,CAACN,KAAK,CAAC,KAAK,IAAIA,KAAK,CAAC,KAAK,GAAG;QAACA,KAAK,CAAC,KAAK;KAAC;IACvE,MAAM,EAAEO,kBAAkB,EAAE,GAAG/B,WAAWzB,MAAMyD,iBAAiB;IACjE,MAAMC,MAAM3B,MAAMD,mBAAmBC,OAAO;IAC5C,IAAI,CAAC2B,KAAK;QACR,kEAAkE;QAClE,2CAA2C;QAC3C,IAAI,CAACF,oBAAoB,qBAAO,KAAClC;YAAI4B,KAAKA;WAASE;QACnD,qBACE,KAAC9B;YACC4B,KAAKA;WACDE;YACJO,IAAI;gBACF;oBACEC,OAAO;oBACPT,QAAQA,UAAU;oBAClBU,SAAS;oBACTC,YAAY;oBACZC,gBAAgB;oBAChBC,QAAQ;oBACRC,aAAa;oBACbC,OAAO;oBACPC,UAAU;oBACVC,YAAY;gBACd;mBACGf;aACJ;sBAEA;;IAGP;IACA,qBACE,KAAC/B;QACC4B,KAAKA;QACLmB,WAAU;QACVX,KAAKA;QACLY,OAAM;QACNC,SAAQ;QACRC,OAAM;QACNC,eAAe;QACfC,SAAQ;OACJtB;QACJO,IAAI;YACF;gBACEE,SAAS;gBACTD,OAAO;gBACPT,QAAQA,UAAU;gBAClBa,QAAQ;YACV;eACGX;SACJ;;AAGP;AAEFL,WAAW2B,WAAW,GAAG;AAEzB,OAAO,MAAMC,mBAA2D;IACtEC,KAAKjD;IACLkD,UAAUpD;IACViD,aAAa;IACbI,aAAa;IACbC,UAAUhF,MAAMiF,iBAAiB,CAACC,KAAK;IACvCC,MAAM;QAAEC,MAAMrE,cAAcqE,IAAI;QAAEzB,IAAI;YAAEO,OAAO;QAAU;IAAE;IAC3DmB,OAAO;QAAEC,aAAatF,MAAMuF,YAAY,CAACC,OAAO;IAAC;IACjDC,YAAY;QACV;YACEC,MAAM;YACNX,aAAa;YACbV,WAAWrE,MAAM2F,kBAAkB,CAACC,UAAU;YAC9CC,OAAO;QACT;QACA;YACEH,MAAM;YACNX,aAAa;YACbV,WAAWrE,MAAM2F,kBAAkB,CAACG,aAAa;YACjDD,OAAO;QACT;KACD;AACH,EAAC;AAaD,MAAME,kBAID;IACH;QAAEC,KAAK;QAAWH,OAAO;QAAeT,MAAMnE,WAAWmE,IAAI;IAAC;IAC9D;QAAEY,KAAK;QAAaH,OAAO;QAAaT,MAAMzE,aAAayE,IAAI;IAAC;IAChE;QAAEY,KAAK;QAAYH,OAAO;QAAYT,MAAMjF,YAAYiF,IAAI;IAAC;IAC7D;QAAEY,KAAK;QAAYH,OAAO;QAAYT,MAAMxE,YAAYwE,IAAI;IAAC;IAC7D;QAAEY,KAAK;QAAWH,OAAO;QAAWT,MAAMjE,WAAWiE,IAAI;IAAC;IAC1D;QAAEY,KAAK;QAAUH,OAAO;QAAUT,MAAM9E,UAAU8E,IAAI;IAAC;CACxD;AAED,MAAMa,mBAAmB;AAEzB,MAAMC,4BAAc1E,WAClB,CAACyB,OAAOC;IACN,MAAM,EAAEM,kBAAkB,EAAE,GAAG/B,WAAWzB,MAAMyD,iBAAiB;IACjE,yEAAyE;IACzE,yEAAyE;IACzE,oEAAoE;IACpE,MAAM0C,WAAoC,aAAKlD;IAC/C,KAAK,MAAM,EAAE+C,GAAG,EAAE,IAAID,gBAAiB,OAAOI,QAAQ,CAACH,IAAI;IAC3D,MAAMI,UAAUL,gBAAgBM,GAAG,CAAC,CAAC,EAAEL,GAAG,EAAEH,KAAK,EAAET,IAAI,EAAE;YAIjDnC;eAJuD;YAC7D+C;YACAH;YACAT;YACAkB,IAAI,GAAErD,aAAAA,KAAK,CAAC+C,IAAI,qBAAV/C,WAAYf,IAAI;QACxB;OAAIqE,MAAM,CACR,CAACC,QAAUA,MAAMF,IAAI,IAAIL,iBAAiBxD,IAAI,CAAC+D,MAAMF,IAAI;IAE3D,qBACE,KAAC/E;QAAM2B,KAAKA;QAAKuD,WAAU;QAAMC,SAAS;OAASP;kBAChDC,QAAQO,MAAM,GACbP,QAAQC,GAAG,CAAC,CAACG,sBACX,KAACpF;gBAECwF,kBAAiB;gBACjBN,MAAME,MAAMF,IAAI;gBAChBO,QAAO;gBACPC,KAAI;gBACJC,cAAYP,MAAMX,KAAK;gBACvBmB,MAAK;0BAEL,cAAA,KAAC3F;oBAAQ+D,MAAMoB,MAAMpB,IAAI;;eARpBoB,MAAMR,GAAG,KAWhBxC,qBACF,4DAA4D;sBAC5D,KAAClC;YACCqC,IAAI;gBACFO,OAAO;gBACPC,UAAU;gBACVC,YAAY;gBACZ6C,GAAG;YACL;sBAEC;aAED;;AAGV;AAEFf,YAAYvB,WAAW,GAAG;AAE1B,OAAO,MAAMuC,oBAA6D;IACxErC,KAAKhD;IACLiD,UAAUpD;IACViD,aAAa;IACbI,aAAa;IACbC,UAAUhF,MAAMiF,iBAAiB,CAACkC,UAAU;IAC5ChC,MAAM;QAAEC,MAAMnE,WAAWmE,IAAI;QAAEzB,IAAI;YAAEO,OAAO;QAAU;IAAE;IACxDmB,OAAO;QAAEC,aAAatF,MAAMuF,YAAY,CAACC,OAAO;IAAC;IACjDC,YAAYM,gBAAgBM,GAAG,CAAC,CAAC,EAAEL,GAAG,EAAEH,KAAK,EAAE,GAAM,CAAA;YACnDH,MAAMM;YACNjB,aAAa,GAAGc,MAAM,qBAAqB,CAAC;YAC5CxB,WAAWrE,MAAM2F,kBAAkB,CAACC,UAAU;YAC9CC;QACF,CAAA;AACF,EAAC;AAED,6EAA6E,GAE7E;;;;;;;;;;;;;CAaC,GACD,MAAMuB,OAAO,CAACC,SAAiBC,UAAkBC;IAC/C,aAA0BA,gBAAAA,QAAS,CAAC,GAA9B,EAAE5D,EAAE,EAAY,SAAPV;;;IACf,OAAO;QACL4B,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO;YAAEoE;YAASC;WAAarE;OAC3BU,KAAK;QAAEA;IAAG,IAAI,CAAC;AAEvB;AAEA,MAAM8D,kBAAkB,CAACC,OAAehC,OAAkB,CAAA;QACxDb,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO;YAAEyD,SAAS;QAAE;QACpB/C,IAAI;YACFgE,MAAM;YACNC,YAAY;YACZC,cAAc;YACdC,eAAe;YACfC,aAAa;QACf;QACAC,OAAO;YACLZ,KAAK,SAAS,CAAC,CAAC,EAAEM,MAAM,CAAC,CAAC;YAC1BN,KAAK,aAAa,CAAC,EAAE,EAAE1B,MAAM;SAC9B;IACH,CAAA;AAEA,MAAMuC,gBAAgB,CACpBC,MACAC,OACAC,UACAC,cACI,CAAA;QACJxD,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO;YAAEyD,SAAS;QAAE;QACpB/C,IAAI;YACFgE,MAAM;YACNC,YAAY;YACZC,cAAc;YACdC,eAAe;YACfC,aAAa;YACbjE,YAAY;WACRuE,eAAe;YACjBrE,QAAQ;YACRC,aAAa;YACbqE,cAAc;QAChB;QAEFN,OAAO;YACLZ,KAAK,MAAMc;YACXd,KAAK,MAAMe;eACRC,SAAS/B,GAAG,CAAC,CAACkC,UAAYnB,KAAK,SAASmB;YAC3C;gBACE1D,KAAK;gBACL2C,aAAa;gBACb1C,UAAUpD;gBACVuB,OAAO;oBACLoE,SAASgB,cAAc,cAAc;oBACrCf,UAAU;gBACZ;YACF;SACD;IACH,CAAA;AAEA,MAAMkB,UAAU,CAACC,UAAkBC,SAAmB;QACpDtB,KAAK,aAAaqB;QAClBrB,KAAK,SAASsB,QAAQ;YAAEC,cAAc;QAAK;KAC5C;AAED,wEAAwE,GACxE,MAAMC,UAAU,CAAC/C,QAAmB,CAAA;QAClChB,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO;YAAEqE,UAAUzB;YAAO3B,OAAO;QAAU;IAC7C,CAAA;AAEA,MAAM2E,gBAAgB,CAACvE,OAAewE,OAAkB,CAAA;QACtDjE,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO;YAAEyD,SAAS;QAAE;QACpB/C,IAAI;YACFgE,MAAM;YACNoB,UAAU;YACVnB,YAAY;YACZC,cAAc;YACdC,eAAe;YACfC,aAAa;QACf;QACAC,OAAO;YAACZ,KAAK,MAAM9C;YAAQ8C,KAAK,SAAS0B;SAAM;IACjD,CAAA;AAEA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,MAAME,UAAU,CAAC,EACfC,MAAM,EACNC,MAAM,EACNC,QAAQ,EACRC,WAAW,IAAI,EACfpB,KAAK,EAON,GAAM;QACLnD,KAAK;QACL2C,aAAa;QACb1C,UAAUpD;QACVuB,OAAO,CAAC;OACJgG,SAAS;QAAEtF,IAAIsF;IAAO,IAAI,CAAC;QAC/BjB,OAAO;YACL;gBACEnD,KAAK;gBACL2C,aAAa;gBACb1C,UAAUpD;gBACVuB,OAAO;oBAAEmG;gBAAS;gBAClBpB,OACEkB,UAAUC,WACN;oBACE;wBACEtE,KAAK;wBACL2C,aAAa;wBACb1C,UAAUpD;wBACVuB,KAAK,EAAEiG,iBAAAA,SAAU,CAAC;uBACdC,WAAW;wBAAExF,IAAIwF;oBAAS,IAAI,CAAC;wBACnCnB;;iBAEH,GACDA;YACR;SACD;;AAGH,OAAO,MAAMqB,eAAqC;IAChD;QACExE,KAAKlD,iBAAiBC;QACtB0H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACC,KAAK;QACvCC,MAAM;YAAEC,MAAMrE,cAAcqE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAC3DuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa5F;YACbkD,UAAUpD;YACVuB,OAAO,CAAC;QACV;IACF;IACA;QACE4B,KAAKlD,iBAAiBE;QACtByH,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACkC,UAAU;QAC5ChC,MAAM;YAAEC,MAAMnE,WAAWmE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACxDuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa3F;YACbiD,UAAUpD;YACVuB,OAAO,CAAC;QACV;IACF;IACA;QACE4B,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAM3E,aAAa2E,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAC1DuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEwD,WAAW;gBAAOC,SAAS;YAAE;YACtC/C,IAAI;gBAAEiE,YAAY;gBAAGE,eAAe;YAAE;YACtCE,OAAO;gBAAC;gBAAG;gBAAG;aAAE,CAAC3B,GAAG,CAAC,CAACsD,QAAW,CAAA;oBAC/B9E,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBAAE2G,KAAK,CAAC,cAAc,EAAED,OAAO;wBAAExG,QAAQ;oBAAQ;gBAC1D,CAAA;QACF;IACF;IACA;QACE0B,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMhF,oBAAoBgF,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACjEuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEwD,WAAW;gBAAOC,SAAS;YAAE;YACtC/C,IAAI;gBAAEiE,YAAY;gBAAGE,eAAe;YAAE;YACtCE,OAAO;gBACLP,gBACE,uDACA;gBAEFA,gBACE,qDACA;gBAEFA,gBACE,kDACA;aAEH;QACH;IACF;IACA;QACE5C,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMlF,eAAekF,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAC5DuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEwD,WAAW;gBAAOC,SAAS;YAAE;YACtC/C,IAAI;gBAAEiE,YAAY;gBAAGE,eAAe;YAAE;YACtCE,OAAO;gBACLC,cAAc,SAAS,MAAM;oBAAC;oBAAe;iBAAgB;gBAC7DA,cACE,OACA,OACA;oBAAC;oBAAiB;oBAAoB;iBAAY,EAClD;gBAEFA,cAAc,QAAQ,OAAO;oBAC3B;oBACA;oBACA;iBACD;aACF;QACH;IACF;IACA;QACEpD,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAM7E,cAAc6E,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAC3DuF,MAAMT,QAAQ;YACZC,QAAQ;gBAAErB,YAAY;gBAAGE,eAAe;YAAE;YAC1C,kEAAkE;YAClE,kEAAkE;YAClE,gDAAgD;YAChDsB,UAAU;YACVF,QAAQ;gBAAExC,SAAS;YAAE;YACrBsB,OAAO;gBACLZ,KAAK,MAAM,8BAA8B;oBAAEuB,cAAc;gBAAK;mBAC3DH,QACD,yBACA;mBAECA,QACD,6BACA;mBAECA,QACD,yBACA;aAEH;QACH;IACF;IACA;QACE3D,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMnF,YAAYmF,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACzDuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEwD,WAAW;gBAAOC,SAAS;YAAE;YACtC/C,IAAI;gBACFiE,YAAY;gBACZE,eAAe;gBACfC,aAAa;gBACbF,cAAc;gBACd/D,YAAY;gBACZC,gBAAgB;gBAChB8F,iBAAiB;gBACjB3F,OAAO;YACT;YACA8D,OAAO;gBACLZ,KAAK,SAAS;gBACd;oBACEvC,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBAAEqE,UAAU;wBAAcN,MAAM;wBAAS9C,OAAO;oBAAU;gBACnE;aACD;QACH;IACF;IACA,yEAAyE;IACzE,gEAAgE;IAChE,qEAAqE;IACrE,0CAA0C;IAC1C;QACEW,KAAKlD,iBAAiB,aAAa;QACnC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMtE,oBAAoBsE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACjEuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACV,+DAA+D;YAC/D,iDAAiD;YACjDuB,OAAO;gBAAE6G,UAAU;YAAS;YAC5B9B,OAAO;gBACL;oBACEnD,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO,CAAC;oBACR+E,OAAO;wBACLZ,KAAK,MAAM,cAAc;4BAAEzD,IAAI;gCAAEoG,UAAU;4BAAE;wBAAE;wBAC/C;4BACElF,KAAK;4BACL2C,aAAa;4BACb1C,UAAUpD;4BACVuB,OAAO;gCAAEwD,WAAW;gCAAOC,SAAS;4BAAE;4BACtCsB,OAAO;gCAACY,QAAQ;gCAASA,QAAQ;gCAAUA,QAAQ;6BAAW;wBAChE;qBACD;gBACH;aACD;QACH;IACF;IACA;QACE/D,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMpE,uBAAuBoE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACpEuF,MAAMT,QAAQ;YACZC,QAAQ;gBACNrB,YAAY;gBACZE,eAAe;gBACfC,aAAa;gBACbF,cAAc;YAChB;YACAqB,QAAQ;gBAAExC,SAAS;YAAE;YACrByC,UAAU;gBAAErF,YAAY;YAAS;YACjCkE,OAAO;gBACLZ,KAAK,MAAM,mCAAmC;oBAAE4C,OAAO;gBAAS;gBAChE5C,KAAK,MAAM,mDAAmD;oBAC5D4C,OAAO;gBACT;gBACA;oBACEnF,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBACLoE,SAAS;wBACTL,MAAM;wBACNM,UAAU;oBACZ;gBACF;aACD;QACH;IACF;IACA;QACEzC,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMlE,mBAAmBkE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAChEuF,MAAMT,QAAQ;YACZC,QAAQ;gBACNlB,aAAa;gBACbF,cAAc;gBACdD,YAAY;gBACZE,eAAe;YACjB;YACAoB,QAAQ;gBAAEzC,WAAW;gBAAOC,SAAS;YAAE;YACvCyC,UAAU;gBAAEc,UAAU;YAAO;YAC7BjC,OAAO;gBACLa,cAAc,QAAQ;gBACtBA,cACE,UACA;gBAEFA,cACE,YACA;aAEH;QACH;IACF;IACA;QACEhE,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAM1E,aAAa0E,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QAC1DuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEwD,WAAW;gBAAOC,SAAS;YAAE;YACtC/C,IAAI;gBACFoE,aAAa;gBACbF,cAAc;gBACdD,YAAY;gBACZE,eAAe;gBACfhE,YAAY;gBACZmG,UAAU;YACZ;YACAjC,OAAO;gBACL;oBACEnD,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO,CAAC;oBACRU,IAAI;wBAAEgE,MAAM;wBAAGoB,UAAU;oBAAI;oBAC7Bf,OAAO;wBACL;4BACEnD,KAAK;4BACL2C,aAAa;4BACb1C,UAAUpD;4BACVuB,OAAO;gCAAE2G,KAAK;gCAAiBzG,QAAQ;4BAAQ;wBACjD;qBACD;gBACH;gBACA;oBACE0B,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBAAEyD,SAAS;oBAAE;oBACpB/C,IAAI;wBAAEgE,MAAM;wBAAGoB,UAAU;oBAAI;oBAC7Bf,OAAO;wBACLZ,KAAK,MAAM;wBACXA,KACE,SACA,+DACE;wBAEJ;4BACEvC,KAAK;4BACL2C,aAAa;4BACb1C,UAAUpD;4BACVuB,OAAO;gCAAEoE,SAAS;gCAAYC,UAAU;4BAAa;wBACvD;qBACD;gBACH;aACD;QACH;IACF;IACA;QACEzC,KAAKlD,iBAAiB,YAAY;QAClC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAM/E,oBAAoB+E,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACjEuF,MAAMT,QAAQ;YACZ,kEAAkE;YAClE,2DAA2D;YAC3DC,QAAQ;gBACNrB,YAAY;gBACZE,eAAe;gBACfC,aAAa;gBACbF,cAAc;gBACdgC,iBAAiB;gBACjB3F,OAAO;YACT;YACAkF,UAAU;YACVF,QAAQ;gBAAExC,SAAS;YAAE;YACrByC,UAAU;gBAAErF,YAAY;YAAS;YACjCkE,OAAO;gBACLZ,KAAK,MAAM,yBAAyB;oBAAE4C,OAAO;gBAAS;gBACtD5C,KAAK,SAAS,2CAA2C;oBACvD4C,OAAO;gBACT;gBACA;oBACEnF,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBACLoE,SAAS;wBACTnD,OAAO;wBACP8C,MAAM;wBACNM,UAAU;oBACZ;gBACF;aACD;QACH;IACF;IACA,yEAAyE;IACzE,uEAAuE;IACvE,yDAAyD;IACzD;QACEzC,KAAKlD,iBAAiB,WAAW;QACjC2H,MAAMtJ,MAAMuJ,QAAQ,CAACC,MAAM;QAC3B7E,aAAa;QACbG,UAAUpD;QACVqD,aAAa;QACbC,UAAUhF,MAAMiF,iBAAiB,CAACyE,MAAM;QACxCvE,MAAM;YAAEC,MAAMvE,oBAAoBuE,IAAI;YAAEzB,IAAI;gBAAEO,OAAO;YAAU;QAAE;QACjEuF,MAAM;YACJ5E,KAAK;YACL2C,aAAa;YACb1C,UAAUpD;YACVuB,OAAO;gBAAEiH,SAAS;gBAAUC,WAAW;YAAc;YACrDxG,IAAI;gBACFoE,aAAa;gBACbF,cAAc;gBACdD,YAAY;gBACZE,eAAe;gBACfsC,WAAW;gBACXnG,aAAa;YACf;YACA+D,OAAO;gBACL;oBACEnD,KAAK;oBACL2C,aAAa;oBACb1C,UAAUpD;oBACVuB,OAAO;wBAAEyD,SAAS;oBAAE;oBACpBsB,OAAO;wBACL;4BACEnD,KAAK;4BACL2C,aAAa;4BACb1C,UAAUpD;4BACVuB,OAAO;gCAAEwD,WAAW;gCAAOC,SAAS;4BAAE;4BACtC/C,IAAI;gCACFI,gBAAgB;gCAChBD,YAAY;gCACZmG,UAAU;4BACZ;4BACAjC,OAAO;gCACLZ,KAAK,MAAM;gCACX;oCACEvC,KAAK;oCACL2C,aAAa;oCACb1C,UAAUpD;oCACVuB,OAAO;wCAAEwD,WAAW;wCAAOC,SAAS;oCAAE;oCACtCsB,OAAO;wCACLY,QAAQ;wCACRA,QAAQ;wCACRA,QAAQ;qCACT;gCACH;6BACD;wBACH;wBACA;4BACE/D,KAAK;4BACL2C,aAAa3F;4BACbiD,UAAUpD;4BACVuB,OAAO,CAAC;wBACV;wBACAmE,KACE,WACA,wCACA;4BAAElD,OAAO;wBAAiB;qBAE7B;gBACH;aACD;QACH;IACF;CACD,CAAA;AAED,SAASgC,WAAW,EAAElD,UAAU,GAAE;AAClC,eAAeA,WAAU"}
|
|
@@ -69,7 +69,7 @@ export const schema = {
|
|
|
69
69
|
$id: ID,
|
|
70
70
|
pluginId: BUNDLE_ID,
|
|
71
71
|
displayName: 'Breadcrumbs',
|
|
72
|
-
description: 'Trail showing where a page sits. Fill it with
|
|
72
|
+
description: 'Trail showing where a page sits. Fill it with Page Links so it ' + 'survives slug changes.',
|
|
73
73
|
category: Aglyn.ComponentCategory.NAVIGATION,
|
|
74
74
|
icon: {
|
|
75
75
|
path: mdiSlashForward.path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/breadcrumbs.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiSlashForward } from '@aglyn/shared-data-mdi'\nimport MuiBreadcrumbs from '@mui/material/Breadcrumbs'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'muiBreadcrumbs'\n\nexport interface BreadcrumbsElementProps {\n /** Text drawn between crumbs; defaults to `/`. */\n separator?: string\n /** Collapse into an ellipsis above this many crumbs. */\n maxItems?: number | string\n itemsBeforeCollapse?: number | string\n itemsAfterCollapse?: number | string\n /** Accessible name of the ellipsis button. */\n expandText?: string\n /** Accessible name of the navigation landmark. */\n ariaLabel?: string\n children?: ReactNode\n}\n\nfunction toCount(value: unknown): number | undefined {\n if (value == null || value === '') return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed < 1) return undefined\n return Math.round(parsed)\n}\n\n/**\n * Breadcrumb trail (https://mui.com/material-ui/react-breadcrumbs/).\n *\n * MUI renders a `nav` and inserts the separators between whatever\n * children it is given, so the crumbs themselves are ordinary Screen\n * Links — the trail stays rename-safe for free, and the last crumb can\n * be plain Typography for the current page.\n */\nconst BreadcrumbsElement = forwardRef<\n HTMLElement,\n BreadcrumbsElementProps\n>((props, ref) => {\n const {\n separator,\n maxItems,\n itemsBeforeCollapse,\n itemsAfterCollapse,\n expandText,\n ariaLabel,\n children,\n ...rest\n } = props\n return (\n <MuiBreadcrumbs\n ref={ref}\n // A blank separator would collapse the trail into run-on text.\n separator={separator || '/'}\n maxItems={toCount(maxItems)}\n itemsBeforeCollapse={toCount(itemsBeforeCollapse)}\n itemsAfterCollapse={toCount(itemsAfterCollapse)}\n expandText={expandText || undefined}\n aria-label={ariaLabel || 'Breadcrumb'}\n {...rest}\n >\n {children}\n </MuiBreadcrumbs>\n )\n})\nBreadcrumbsElement.displayName = 'AglynBreadcrumbs'\n\n/** The collapse tuning only matters once a max is set. */\nconst COLLAPSING = { when: 'maxItems', isNotEmpty: true }\n\nexport const schema: Aglyn.ComponentSchema<BreadcrumbsElementProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Breadcrumbs',\n description:\n 'Trail showing where a page sits. Fill it with
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/breadcrumbs.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiSlashForward } from '@aglyn/shared-data-mdi'\nimport MuiBreadcrumbs from '@mui/material/Breadcrumbs'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'muiBreadcrumbs'\n\nexport interface BreadcrumbsElementProps {\n /** Text drawn between crumbs; defaults to `/`. */\n separator?: string\n /** Collapse into an ellipsis above this many crumbs. */\n maxItems?: number | string\n itemsBeforeCollapse?: number | string\n itemsAfterCollapse?: number | string\n /** Accessible name of the ellipsis button. */\n expandText?: string\n /** Accessible name of the navigation landmark. */\n ariaLabel?: string\n children?: ReactNode\n}\n\nfunction toCount(value: unknown): number | undefined {\n if (value == null || value === '') return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed < 1) return undefined\n return Math.round(parsed)\n}\n\n/**\n * Breadcrumb trail (https://mui.com/material-ui/react-breadcrumbs/).\n *\n * MUI renders a `nav` and inserts the separators between whatever\n * children it is given, so the crumbs themselves are ordinary Screen\n * Links — the trail stays rename-safe for free, and the last crumb can\n * be plain Typography for the current page.\n */\nconst BreadcrumbsElement = forwardRef<\n HTMLElement,\n BreadcrumbsElementProps\n>((props, ref) => {\n const {\n separator,\n maxItems,\n itemsBeforeCollapse,\n itemsAfterCollapse,\n expandText,\n ariaLabel,\n children,\n ...rest\n } = props\n return (\n <MuiBreadcrumbs\n ref={ref}\n // A blank separator would collapse the trail into run-on text.\n separator={separator || '/'}\n maxItems={toCount(maxItems)}\n itemsBeforeCollapse={toCount(itemsBeforeCollapse)}\n itemsAfterCollapse={toCount(itemsAfterCollapse)}\n expandText={expandText || undefined}\n aria-label={ariaLabel || 'Breadcrumb'}\n {...rest}\n >\n {children}\n </MuiBreadcrumbs>\n )\n})\nBreadcrumbsElement.displayName = 'AglynBreadcrumbs'\n\n/** The collapse tuning only matters once a max is set. */\nconst COLLAPSING = { when: 'maxItems', isNotEmpty: true }\n\nexport const schema: Aglyn.ComponentSchema<BreadcrumbsElementProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Breadcrumbs',\n description:\n 'Trail showing where a page sits. Fill it with Page Links so it ' +\n 'survives slug changes.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiSlashForward.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'separator',\n label: 'Separator',\n description: 'Text drawn between crumbs, e.g. / or ›. Default /.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'maxItems',\n label: 'Collapse above',\n description:\n 'Collapse the middle of the trail into an ellipsis once there ' +\n 'are more crumbs than this. Blank never collapses.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'itemsBeforeCollapse',\n label: 'Keep at start',\n description: 'Crumbs kept before the ellipsis. Default 1.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n condition: COLLAPSING,\n },\n {\n name: 'itemsAfterCollapse',\n label: 'Keep at end',\n description: 'Crumbs kept after the ellipsis. Default 1.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n condition: COLLAPSING,\n },\n {\n name: 'expandText',\n label: 'Expand button label',\n description:\n 'Accessible name of the ellipsis button. Default \"Show path\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n condition: COLLAPSING,\n },\n {\n name: 'ariaLabel',\n label: 'Accessible label',\n description:\n 'Names the navigation landmark for screen readers. Default ' +\n '\"Breadcrumb\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nconst crumb = (label: string) => ({\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n // Text links, not buttons: a breadcrumb rendered as a button gets\n // button typography and the wrong role for assistive tech (AGL-1195).\n props: { children: label, renderAs: 'link', color: 'inherit' },\n})\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Breadcrumbs',\n pluginId: BUNDLE_ID,\n description: 'Two links and the current page',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n crumb('Home'),\n crumb('Section'),\n {\n // The current page is not a link — linking a page to itself is\n // the classic breadcrumb accessibility mistake.\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body2',\n color: 'text.primary',\n children: 'Current page',\n },\n },\n ],\n },\n },\n]\n\nexport default BreadcrumbsElement\n"],"names":["Aglyn","mdiSlashForward","MuiBreadcrumbs","forwardRef","BUNDLE_ID","generatePresetId","ID","toCount","value","undefined","parsed","Number","isFinite","Math","round","BreadcrumbsElement","props","ref","separator","maxItems","itemsBeforeCollapse","itemsAfterCollapse","expandText","ariaLabel","children","rest","aria-label","displayName","COLLAPSING","when","isNotEmpty","schema","$id","pluginId","description","category","ComponentCategory","NAVIGATION","icon","path","sx","color","attributes","name","label","component","FieldComponentType","TEXT_FIELD","type","condition","crumb","componentId","renderAs","presets","NodeType","PRESET","data","nodes","variant"],"mappings":"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"}
|
|
@@ -139,7 +139,7 @@ export const schema = {
|
|
|
139
139
|
$id: ID,
|
|
140
140
|
pluginId: BUNDLE_ID,
|
|
141
141
|
displayName: 'Button',
|
|
142
|
-
description: 'A button — or a link styled as one, if you point it at a
|
|
142
|
+
description: 'A button — or a link styled as one, if you point it at a page or URL.',
|
|
143
143
|
category: Aglyn.ComponentCategory.INPUT,
|
|
144
144
|
icon: {
|
|
145
145
|
path: mdiGestureTapButton.path,
|
|
@@ -189,13 +189,13 @@ export const schema = {
|
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
name: 'screenId',
|
|
192
|
-
description: 'Optional: navigate to this
|
|
192
|
+
description: 'Optional: navigate to this page when clicked — the address ' + 'follows the published path like a Page Link (AGL-139).',
|
|
193
193
|
component: Aglyn.FieldComponentType.SCREEN_SELECT,
|
|
194
|
-
label: 'Link to
|
|
194
|
+
label: 'Link to page'
|
|
195
195
|
},
|
|
196
196
|
{
|
|
197
197
|
name: 'href',
|
|
198
|
-
description: 'Where the button goes when it points off this site. Ignored while ' + 'Link to
|
|
198
|
+
description: 'Where the button goes when it points off this site. Ignored while ' + 'Link to page names one — that field wins, so clear it to use a ' + 'URL. Absolute (https://…) for another site, or a path for one of ' + 'your own routes.',
|
|
199
199
|
component: Aglyn.FieldComponentType.TEXT_FIELD,
|
|
200
200
|
label: 'External URL',
|
|
201
201
|
// A bare `#fragment` saves and goes nowhere (AGL-2867).
|
|
@@ -205,7 +205,7 @@ export const schema = {
|
|
|
205
205
|
FIELD_LINK_TARGET_NAME,
|
|
206
206
|
{
|
|
207
207
|
name: 'renderAs',
|
|
208
|
-
description: 'What this announces as once it links somewhere. Button is for ' + 'calls to action; pick "Link (button styling)" for a chip that only ' + 'navigates, so it keeps this look but announces as the link it is. ' + 'Ignored when there is no
|
|
208
|
+
description: 'What this announces as once it links somewhere. Button is for ' + 'calls to action; pick "Link (button styling)" for a chip that only ' + 'navigates, so it keeps this look but announces as the link it is. ' + 'Ignored when there is no page or URL to point at.',
|
|
209
209
|
component: Aglyn.FieldComponentType.SELECT,
|
|
210
210
|
label: 'Render as',
|
|
211
211
|
// `'button'` is a REAL SENTINEL, not a deletion (AGL-1453). Unlike the
|