@aglyn/plugins-mui 1.0.0-beta.231 → 1.0.0-beta.233
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/breadcrumbs.js +7 -0
- package/src/lib/components/breadcrumbs.js.map +1 -1
- package/src/lib/components/card.js +13 -6
- package/src/lib/components/card.js.map +1 -1
- package/src/lib/components/collection-entries.js +8 -0
- package/src/lib/components/collection-entries.js.map +1 -1
- package/src/lib/components/inline-text.d.ts +5 -5
- package/src/lib/components/inline-text.js +5 -5
- package/src/lib/components/inline-text.js.map +1 -1
- package/src/lib/components/stack.js +7 -2
- package/src/lib/components/stack.js.map +1 -1
- package/src/lib/components/typography.js +79 -3
- package/src/lib/components/typography.js.map +1 -1
- package/src/lib/plugin.js +3 -3
- package/src/lib/plugin.js.map +1 -1
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.233",
|
|
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.233",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.233",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.233",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.233",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.233",
|
|
33
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.233",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"mobx": "^7.0.5",
|
|
36
36
|
"mobx-react-lite": "^5.1.0"
|
|
@@ -77,6 +77,13 @@ export const schema = {
|
|
|
77
77
|
color: '#2196f3'
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
|
+
// MUI wraps each of `Children.toArray(children)` in its own `<li>` and puts
|
|
81
|
+
// a separator between them, so the renderer has to hand it one React child
|
|
82
|
+
// per crumb (AGL-3660). Wrapped in the single `<Branch>`, a trail of any
|
|
83
|
+
// length was ONE item: no separators, and `maxItems` never collapsed it.
|
|
84
|
+
flags: {
|
|
85
|
+
positionalChildren: Aglyn.FEATURE_FLAG.ENABLED
|
|
86
|
+
},
|
|
80
87
|
attributes: [
|
|
81
88
|
{
|
|
82
89
|
name: 'separator',
|
|
@@ -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 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":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,eAAe,QAAQ,yBAAwB;AACxD,OAAOC,oBAAoB,4BAA2B;AACtD,SAASC,UAAU,QAAwB,QAAO;AAClD,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,iBAAgB;AAgBrD,SAASC,QAAQC,KAAc;IAC7B,IAAIA,SAAS,QAAQA,UAAU,IAAI,OAAOC;IAC1C,MAAMC,SAASC,OAAOH;IACtB,IAAI,CAACG,OAAOC,QAAQ,CAACF,WAAWA,SAAS,GAAG,OAAOD;IACnD,OAAOI,KAAKC,KAAK,CAACJ;AACpB;AAEA;;;;;;;CAOC,GACD,MAAMK,mCAAqBZ,WAGzB,CAACa,OAAOC;IACR,MAAM,EACJC,SAAS,EACTC,QAAQ,EACRC,mBAAmB,EACnBC,kBAAkB,EAClBC,UAAU,EACVC,SAAS,EACTC,QAAQ,EAET,GAAGR,OADCS,wCACDT;;;;;;;;;IACJ,qBACE,KAACd;QACCe,KAAKA;QACL,+DAA+D;QAC/DC,WAAWA,aAAa;QACxBC,UAAUZ,QAAQY;QAClBC,qBAAqBb,QAAQa;QAC7BC,oBAAoBd,QAAQc;QAC5BC,YAAYA,cAAcb;QAC1BiB,cAAYH,aAAa;OACrBE;kBAEHD;;AAGP;AACAT,mBAAmBY,WAAW,GAAG;AAEjC,wDAAwD,GACxD,MAAMC,aAAa;IAAEC,MAAM;IAAYC,YAAY;AAAK;AAExD,OAAO,MAAMC,SAAyD;IACpEC,KAAK1B;IACL2B,UAAU7B;IACVuB,aAAa;IACbO,aACE,oEACA;IACFC,UAAUnC,MAAMoC,iBAAiB,CAACC,UAAU;IAC5CC,MAAM;QAAEC,MAAMtC,gBAAgBsC,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;
|
|
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 // MUI wraps each of `Children.toArray(children)` in its own `<li>` and puts\n // a separator between them, so the renderer has to hand it one React child\n // per crumb (AGL-3660). Wrapped in the single `<Branch>`, a trail of any\n // length was ONE item: no separators, and `maxItems` never collapsed it.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\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","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","label","component","FieldComponentType","TEXT_FIELD","type","condition","crumb","componentId","renderAs","presets","NodeType","PRESET","data","nodes","variant"],"mappings":"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"}
|
|
@@ -49,13 +49,20 @@ export const CARD_ACTIONS_ID = 'muiCardActions';
|
|
|
49
49
|
"children"
|
|
50
50
|
]);
|
|
51
51
|
const outlined = variant === 'outlined';
|
|
52
|
+
// A variant or an elevation the author did not set is left to the site's
|
|
53
|
+
// theme (AGL-3660): a Card dropped from the drawer takes the card style
|
|
54
|
+
// the theme's MuiCard defaults give it, as every Card the site has does.
|
|
55
|
+
const chosen = outlined ? 'outlined' : variant === 'elevation' ? 'elevation' : undefined;
|
|
56
|
+
// Same trap as Paper: a string elevation indexes MUI's shadow array and
|
|
57
|
+
// yields a completely flat card.
|
|
58
|
+
const depth = outlined ? undefined : toElevation(elevation);
|
|
52
59
|
return /*#__PURE__*/ _jsx(MuiCard, _extends({
|
|
53
|
-
ref: ref
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
elevation:
|
|
58
|
-
}, applySemanticElement(rest), {
|
|
60
|
+
ref: ref
|
|
61
|
+
}, chosen ? {
|
|
62
|
+
variant: chosen
|
|
63
|
+
} : {}, depth !== undefined ? {
|
|
64
|
+
elevation: depth
|
|
65
|
+
} : {}, applySemanticElement(rest), {
|
|
59
66
|
children: children
|
|
60
67
|
}));
|
|
61
68
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/card.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 { mdiCardOutline, mdiCardText, mdiGestureTap } from '@aglyn/shared-data-mdi'\nimport MuiCard from '@mui/material/Card'\nimport MuiCardActions from '@mui/material/CardActions'\nimport MuiCardContent from '@mui/material/CardContent'\nimport MuiCardHeader from '@mui/material/CardHeader'\nimport { forwardRef, type ReactNode } from 'react'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport { toElevation } from './paper'\n\n// Component ids are persisted in screen documents; never rename.\nexport const CARD_ID: Aglyn.ComponentId = 'muiCard'\nexport const CARD_HEADER_ID: Aglyn.ComponentId = 'muiCardHeader'\nexport const CARD_CONTENT_ID: Aglyn.ComponentId = 'muiCardContent'\nexport const CARD_ACTIONS_ID: Aglyn.ComponentId = 'muiCardActions'\n\nexport interface CardElementProps {\n variant?: 'elevation' | 'outlined'\n /** Shadow depth, 0–24; ignored on the outlined variant. */\n elevation?: number | string\n children?: ReactNode\n}\n\nexport interface CardHeaderElementProps {\n title?: string\n subheader?: string\n}\n\nexport interface CardActionsElementProps {\n /** If true, the 8px gap between action buttons is removed. */\n disableSpacing?: boolean\n children?: ReactNode\n}\n\n/**\n * Card (https://mui.com/material-ui/react-card/) — a Paper with card\n * padding conventions. Composed from separate Header/Content/Actions\n * elements rather than a single element with a dozen text props, so the\n * pieces are individually selectable, stylable and reorderable on the\n * canvas like everything else.\n */\nconst CardElement = forwardRef<HTMLDivElement, CardElementProps>(\n (rawProps, ref) => {\n // See PaperElement: cleared props are dropped before anything reads\n // them, so `rest` cannot carry a `null` into MUI (AGL-1451).\n const props = dropClearedProps(rawProps)\n const { variant, elevation, children, ...rest } = props\n const outlined = variant === 'outlined'\n return (\n <MuiCard\n ref={ref}\n variant={outlined ? 'outlined' : 'elevation'}\n // Same trap as Paper: a string elevation indexes MUI's shadow\n // array and yields a completely flat card.\n elevation={outlined ? undefined : toElevation(elevation)}\n {...applySemanticElement(rest as Record<string, unknown>)}\n >\n {children}\n </MuiCard>\n )\n },\n)\nCardElement.displayName = 'AglynCard'\n\n/**\n * Card header. `title`/`subheader` are plain strings: MUI wraps each in\n * its own Typography, and passing children instead would drop the\n * heading/subheading type scale a card header exists to provide.\n */\nexport const CardHeaderElement = forwardRef<\n HTMLDivElement,\n CardHeaderElementProps\n>((props, ref) => {\n const { title, subheader, ...rest } = props as CardHeaderElementProps & {\n children?: unknown\n }\n // A header with neither string is invisible and unselectable on the\n // canvas; a placeholder keeps the node reachable.\n delete (rest as { children?: unknown }).children\n return (\n <MuiCardHeader\n ref={ref}\n title={title || (subheader ? undefined : 'Card title')}\n subheader={subheader || undefined}\n {...rest}\n />\n )\n})\nCardHeaderElement.displayName = 'AglynCardHeader'\n\nexport const CardContentElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiCardContent ref={ref} {...props} />)\nCardContentElement.displayName = 'AglynCardContent'\n\nexport const CardActionsElement = forwardRef<\n HTMLDivElement,\n CardActionsElementProps\n>((props, ref) => <MuiCardActions ref={ref} {...props} />)\nCardActionsElement.displayName = 'AglynCardActions'\n\n/** Elevation only means anything on the (default) `elevation` variant. */\nconst ELEVATION_ONLY = { when: 'variant', is: 'outlined', notMatch: true }\n\nexport const cardSchema: Aglyn.ComponentSchema<CardElementProps> = {\n $id: CARD_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card',\n description:\n 'Surface that groups a header, content and actions about one subject.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardOutline.path, sx: { color: '#2196f3' } },\n attributes: [\n semanticElementAttribute('this card'),\n semanticElementLabelAttribute(),\n {\n name: 'variant',\n label: 'Variant',\n description:\n 'Elevation raises the card with a shadow; outlined draws a border ' +\n 'instead and has no shadow.',\n component: Aglyn.FieldComponentType.SELECT,\n // Same call as Paper (AGL-1451): a real sentinel, because `elevation`\n // is MUI's own value for the other half of this two-way choice and is\n // what the Elevation control is conditioned on. `''` could not\n // persist (AGL-1191).\n options: [\n { value: 'elevation', label: 'Elevation (default)' },\n { value: 'outlined', label: 'Outlined' },\n ],\n },\n {\n name: 'elevation',\n label: 'Elevation',\n description: 'Shadow depth from 0 (flat) to 24.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n condition: ELEVATION_ONLY,\n },\n ],\n}\n\nexport const cardHeaderSchema: Aglyn.ComponentSchema<CardHeaderElementProps> = {\n $id: CARD_HEADER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Header',\n description: 'The title and subheader at the top of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardText.path, sx: { color: '#2196f3' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'title',\n label: 'Title',\n description: 'The card heading.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'subheader',\n label: 'Subheader',\n description: 'Secondary line under the title.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nexport const cardContentSchema: Aglyn.ComponentSchema = {\n $id: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Content',\n description: 'The padded body region of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardText.path, sx: { color: '#2196f3' } },\n}\n\nexport const cardActionsSchema: Aglyn.ComponentSchema<CardActionsElementProps> =\n {\n $id: CARD_ACTIONS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Actions',\n description: 'The button row at the bottom of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiGestureTap.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'disableSpacing',\n label: 'Disable spacing?',\n description: 'If true, the gap between action buttons is removed.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n }\n\nexport const cardPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(CARD_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Card',\n pluginId: BUNDLE_ID,\n description: 'Image, heading, body text and an action button',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: cardSchema.icon,\n data: {\n $id: null,\n componentId: CARD_ID,\n pluginId: BUNDLE_ID,\n props: {},\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { maxWidth: 360 },\n nodes: [\n // The existing Image element rather than MUI's CardMedia: it\n // already carries the media-CDN srcSet, the lazy loading and the\n // empty-source placeholder (AGL-74/175), none of which is worth\n // re-implementing for a card.\n {\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: '', height: '160px', objectFit: 'cover' },\n },\n {\n $id: null,\n componentId: CARD_HEADER_ID,\n pluginId: BUNDLE_ID,\n props: { title: 'Card title', subheader: 'Supporting line' },\n },\n {\n $id: null,\n componentId: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body2',\n children:\n 'A short description of whatever this card is about.',\n },\n },\n ],\n },\n {\n $id: null,\n componentId: CARD_ACTIONS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n // `'button'`, not `''` (AGL-1453). This preset was MINTING the\n // unpersistable value: every card dropped on a canvas planted a\n // `renderAs: ''` that no longer matches any option. Renders\n // identically — both take the non-link branch.\n props: {\n children: 'Learn more',\n renderAs: 'button',\n size: 'small',\n },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId(CARD_ID, 'outlined'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Card Outlined',\n pluginId: BUNDLE_ID,\n description: 'Text-only card with a border instead of a shadow',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: cardSchema.icon,\n data: {\n $id: null,\n componentId: CARD_ID,\n pluginId: BUNDLE_ID,\n props: { variant: 'outlined' },\n sx: { maxWidth: 360 },\n nodes: [\n {\n $id: null,\n componentId: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'h6', children: 'Card title' },\n },\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body2',\n children: 'A short description of this card.',\n },\n },\n ],\n },\n ],\n },\n },\n]\n\nexport default CardElement\n"],"names":["Aglyn","mdiCardOutline","mdiCardText","mdiGestureTap","MuiCard","MuiCardActions","MuiCardContent","MuiCardHeader","forwardRef","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","BUNDLE_ID","dropClearedProps","generatePresetId","toElevation","CARD_ID","CARD_HEADER_ID","CARD_CONTENT_ID","CARD_ACTIONS_ID","CardElement","rawProps","ref","props","variant","elevation","children","rest","outlined","undefined","displayName","CardHeaderElement","title","subheader","CardContentElement","CardActionsElement","ELEVATION_ONLY","when","is","notMatch","cardSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","path","sx","color","attributes","name","label","component","FieldComponentType","SELECT","options","value","TEXT_FIELD","type","condition","cardHeaderSchema","flags","selfClosing","FEATURE_FLAG","ENABLED","cardContentSchema","cardActionsSchema","SWITCH","cardPresets","NodeType","PRESET","data","componentId","maxWidth","nodes","alt","height","objectFit","renderAs","size"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/card.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 { mdiCardOutline, mdiCardText, mdiGestureTap } from '@aglyn/shared-data-mdi'\nimport MuiCard from '@mui/material/Card'\nimport MuiCardActions from '@mui/material/CardActions'\nimport MuiCardContent from '@mui/material/CardContent'\nimport MuiCardHeader from '@mui/material/CardHeader'\nimport { forwardRef, type ReactNode } from 'react'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport { toElevation } from './paper'\n\n// Component ids are persisted in screen documents; never rename.\nexport const CARD_ID: Aglyn.ComponentId = 'muiCard'\nexport const CARD_HEADER_ID: Aglyn.ComponentId = 'muiCardHeader'\nexport const CARD_CONTENT_ID: Aglyn.ComponentId = 'muiCardContent'\nexport const CARD_ACTIONS_ID: Aglyn.ComponentId = 'muiCardActions'\n\nexport interface CardElementProps {\n variant?: 'elevation' | 'outlined'\n /** Shadow depth, 0–24; ignored on the outlined variant. */\n elevation?: number | string\n children?: ReactNode\n}\n\nexport interface CardHeaderElementProps {\n title?: string\n subheader?: string\n}\n\nexport interface CardActionsElementProps {\n /** If true, the 8px gap between action buttons is removed. */\n disableSpacing?: boolean\n children?: ReactNode\n}\n\n/**\n * Card (https://mui.com/material-ui/react-card/) — a Paper with card\n * padding conventions. Composed from separate Header/Content/Actions\n * elements rather than a single element with a dozen text props, so the\n * pieces are individually selectable, stylable and reorderable on the\n * canvas like everything else.\n */\nconst CardElement = forwardRef<HTMLDivElement, CardElementProps>(\n (rawProps, ref) => {\n // See PaperElement: cleared props are dropped before anything reads\n // them, so `rest` cannot carry a `null` into MUI (AGL-1451).\n const props = dropClearedProps(rawProps)\n const { variant, elevation, children, ...rest } = props\n const outlined = variant === 'outlined'\n // A variant or an elevation the author did not set is left to the site's\n // theme (AGL-3660): a Card dropped from the drawer takes the card style\n // the theme's MuiCard defaults give it, as every Card the site has does.\n const chosen = outlined ? 'outlined' : variant === 'elevation' ? 'elevation' : undefined\n // Same trap as Paper: a string elevation indexes MUI's shadow array and\n // yields a completely flat card.\n const depth = outlined ? undefined : toElevation(elevation)\n return (\n <MuiCard\n ref={ref}\n {...(chosen ? { variant: chosen } : {})}\n {...(depth !== undefined ? { elevation: depth } : {})}\n {...applySemanticElement(rest as Record<string, unknown>)}\n >\n {children}\n </MuiCard>\n )\n },\n)\nCardElement.displayName = 'AglynCard'\n\n/**\n * Card header. `title`/`subheader` are plain strings: MUI wraps each in\n * its own Typography, and passing children instead would drop the\n * heading/subheading type scale a card header exists to provide.\n */\nexport const CardHeaderElement = forwardRef<\n HTMLDivElement,\n CardHeaderElementProps\n>((props, ref) => {\n const { title, subheader, ...rest } = props as CardHeaderElementProps & {\n children?: unknown\n }\n // A header with neither string is invisible and unselectable on the\n // canvas; a placeholder keeps the node reachable.\n delete (rest as { children?: unknown }).children\n return (\n <MuiCardHeader\n ref={ref}\n title={title || (subheader ? undefined : 'Card title')}\n subheader={subheader || undefined}\n {...rest}\n />\n )\n})\nCardHeaderElement.displayName = 'AglynCardHeader'\n\nexport const CardContentElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiCardContent ref={ref} {...props} />)\nCardContentElement.displayName = 'AglynCardContent'\n\nexport const CardActionsElement = forwardRef<\n HTMLDivElement,\n CardActionsElementProps\n>((props, ref) => <MuiCardActions ref={ref} {...props} />)\nCardActionsElement.displayName = 'AglynCardActions'\n\n/** Elevation only means anything on the (default) `elevation` variant. */\nconst ELEVATION_ONLY = { when: 'variant', is: 'outlined', notMatch: true }\n\nexport const cardSchema: Aglyn.ComponentSchema<CardElementProps> = {\n $id: CARD_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card',\n description:\n 'Surface that groups a header, content and actions about one subject.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardOutline.path, sx: { color: '#2196f3' } },\n attributes: [\n semanticElementAttribute('this card'),\n semanticElementLabelAttribute(),\n {\n name: 'variant',\n label: 'Variant',\n description:\n 'Elevation raises the card with a shadow; outlined draws a border ' +\n 'instead and has no shadow.',\n component: Aglyn.FieldComponentType.SELECT,\n // Same call as Paper (AGL-1451): a real sentinel, because `elevation`\n // is MUI's own value for the other half of this two-way choice and is\n // what the Elevation control is conditioned on. `''` could not\n // persist (AGL-1191).\n options: [\n { value: 'elevation', label: 'Elevation (default)' },\n { value: 'outlined', label: 'Outlined' },\n ],\n },\n {\n name: 'elevation',\n label: 'Elevation',\n description: 'Shadow depth from 0 (flat) to 24.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n condition: ELEVATION_ONLY,\n },\n ],\n}\n\nexport const cardHeaderSchema: Aglyn.ComponentSchema<CardHeaderElementProps> = {\n $id: CARD_HEADER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Header',\n description: 'The title and subheader at the top of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardText.path, sx: { color: '#2196f3' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'title',\n label: 'Title',\n description: 'The card heading.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'subheader',\n label: 'Subheader',\n description: 'Secondary line under the title.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\nexport const cardContentSchema: Aglyn.ComponentSchema = {\n $id: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Content',\n description: 'The padded body region of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiCardText.path, sx: { color: '#2196f3' } },\n}\n\nexport const cardActionsSchema: Aglyn.ComponentSchema<CardActionsElementProps> =\n {\n $id: CARD_ACTIONS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Card Actions',\n description: 'The button row at the bottom of a card.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiGestureTap.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'disableSpacing',\n label: 'Disable spacing?',\n description: 'If true, the gap between action buttons is removed.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n }\n\nexport const cardPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(CARD_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Card',\n pluginId: BUNDLE_ID,\n description: 'Image, heading, body text and an action button',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: cardSchema.icon,\n data: {\n $id: null,\n componentId: CARD_ID,\n pluginId: BUNDLE_ID,\n props: {},\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { maxWidth: 360 },\n nodes: [\n // The existing Image element rather than MUI's CardMedia: it\n // already carries the media-CDN srcSet, the lazy loading and the\n // empty-source placeholder (AGL-74/175), none of which is worth\n // re-implementing for a card.\n {\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: '', height: '160px', objectFit: 'cover' },\n },\n {\n $id: null,\n componentId: CARD_HEADER_ID,\n pluginId: BUNDLE_ID,\n props: { title: 'Card title', subheader: 'Supporting line' },\n },\n {\n $id: null,\n componentId: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body2',\n children:\n 'A short description of whatever this card is about.',\n },\n },\n ],\n },\n {\n $id: null,\n componentId: CARD_ACTIONS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n // `'button'`, not `''` (AGL-1453). This preset was MINTING the\n // unpersistable value: every card dropped on a canvas planted a\n // `renderAs: ''` that no longer matches any option. Renders\n // identically — both take the non-link branch.\n props: {\n children: 'Learn more',\n renderAs: 'button',\n size: 'small',\n },\n },\n ],\n },\n ],\n },\n },\n {\n $id: generatePresetId(CARD_ID, 'outlined'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Card Outlined',\n pluginId: BUNDLE_ID,\n description: 'Text-only card with a border instead of a shadow',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: cardSchema.icon,\n data: {\n $id: null,\n componentId: CARD_ID,\n pluginId: BUNDLE_ID,\n props: { variant: 'outlined' },\n sx: { maxWidth: 360 },\n nodes: [\n {\n $id: null,\n componentId: CARD_CONTENT_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'h6', children: 'Card title' },\n },\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body2',\n children: 'A short description of this card.',\n },\n },\n ],\n },\n ],\n },\n },\n]\n\nexport default CardElement\n"],"names":["Aglyn","mdiCardOutline","mdiCardText","mdiGestureTap","MuiCard","MuiCardActions","MuiCardContent","MuiCardHeader","forwardRef","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","BUNDLE_ID","dropClearedProps","generatePresetId","toElevation","CARD_ID","CARD_HEADER_ID","CARD_CONTENT_ID","CARD_ACTIONS_ID","CardElement","rawProps","ref","props","variant","elevation","children","rest","outlined","chosen","undefined","depth","displayName","CardHeaderElement","title","subheader","CardContentElement","CardActionsElement","ELEVATION_ONLY","when","is","notMatch","cardSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","path","sx","color","attributes","name","label","component","FieldComponentType","SELECT","options","value","TEXT_FIELD","type","condition","cardHeaderSchema","flags","selfClosing","FEATURE_FLAG","ENABLED","cardContentSchema","cardActionsSchema","SWITCH","cardPresets","NodeType","PRESET","data","componentId","maxWidth","nodes","alt","height","objectFit","renderAs","size"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,cAAc,EAAEC,WAAW,EAAEC,aAAa,QAAQ,yBAAwB;AACnF,OAAOC,aAAa,qBAAoB;AACxC,OAAOC,oBAAoB,4BAA2B;AACtD,OAAOC,oBAAoB,4BAA2B;AACtD,OAAOC,mBAAmB,2BAA0B;AACpD,SAASC,UAAU,QAAwB,QAAO;AAClD,SACEC,oBAAoB,EACpBC,wBAAwB,EACxBC,6BAA6B,QACxB,6BAAyB;AAChC,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,gBAAgB,QAAQ,iCAA6B;AAC9D,SAASC,gBAAgB,QAAQ,iCAA6B;AAC9D,SAASC,WAAW,QAAQ,aAAS;AAErC,iEAAiE;AACjE,OAAO,MAAMC,UAA6B,UAAS;AACnD,OAAO,MAAMC,iBAAoC,gBAAe;AAChE,OAAO,MAAMC,kBAAqC,iBAAgB;AAClE,OAAO,MAAMC,kBAAqC,iBAAgB;AAoBlE;;;;;;CAMC,GACD,MAAMC,4BAAcZ,WAClB,CAACa,UAAUC;IACT,oEAAoE;IACpE,6DAA6D;IAC7D,MAAMC,QAAQV,iBAAiBQ;IAC/B,MAAM,EAAEG,OAAO,EAAEC,SAAS,EAAEC,QAAQ,EAAW,GAAGH,OAATI,wCAASJ;;;;;IAClD,MAAMK,WAAWJ,YAAY;IAC7B,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,MAAMK,SAASD,WAAW,aAAaJ,YAAY,cAAc,cAAcM;IAC/E,wEAAwE;IACxE,iCAAiC;IACjC,MAAMC,QAAQH,WAAWE,YAAYf,YAAYU;IACjD,qBACE,KAACrB;QACCkB,KAAKA;OACAO,SAAS;QAAEL,SAASK;IAAO,IAAI,CAAC,GAChCE,UAAUD,YAAY;QAAEL,WAAWM;IAAM,IAAI,CAAC,GAC/CtB,qBAAqBkB;kBAExBD;;AAGP;AAEFN,YAAYY,WAAW,GAAG;AAE1B;;;;CAIC,GACD,OAAO,MAAMC,kCAAoBzB,WAG/B,CAACe,OAAOD;IACR,MAAM,EAAEY,KAAK,EAAEC,SAAS,EAAW,GAAGZ,OAATI,wCAASJ;;;;IAGtC,oEAAoE;IACpE,kDAAkD;IAClD,OAAO,AAACI,KAAgCD,QAAQ;IAChD,qBACE,KAACnB;QACCe,KAAKA;QACLY,OAAOA,SAAUC,CAAAA,YAAYL,YAAY,YAAW;QACpDK,WAAWA,aAAaL;OACpBH;AAGV,GAAE;AACFM,kBAAkBD,WAAW,GAAG;AAEhC,OAAO,MAAMI,mCAAqB5B,WAGhC,CAACe,OAAOD,oBAAQ,KAAChB;QAAegB,KAAKA;OAASC,SAAU;AAC1Da,mBAAmBJ,WAAW,GAAG;AAEjC,OAAO,MAAMK,mCAAqB7B,WAGhC,CAACe,OAAOD,oBAAQ,KAACjB;QAAeiB,KAAKA;OAASC,SAAU;AAC1Dc,mBAAmBL,WAAW,GAAG;AAEjC,wEAAwE,GACxE,MAAMM,iBAAiB;IAAEC,MAAM;IAAWC,IAAI;IAAYC,UAAU;AAAK;AAEzE,OAAO,MAAMC,aAAsD;IACjEC,KAAK3B;IACL4B,UAAUhC;IACVoB,aAAa;IACba,aACE;IACFC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QAAEC,MAAMjD,eAAeiD,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;IAC5DC,YAAY;QACV3C,yBAAyB;QACzBC;QACA;YACE2C,MAAM;YACNC,OAAO;YACPV,aACE,sEACA;YACFW,WAAWxD,MAAMyD,kBAAkB,CAACC,MAAM;YAC1C,sEAAsE;YACtE,sEAAsE;YACtE,+DAA+D;YAC/D,sBAAsB;YACtBC,SAAS;gBACP;oBAAEC,OAAO;oBAAaL,OAAO;gBAAsB;gBACnD;oBAAEK,OAAO;oBAAYL,OAAO;gBAAW;aACxC;QACH;QACA;YACED,MAAM;YACNC,OAAO;YACPV,aAAa;YACbW,WAAWxD,MAAMyD,kBAAkB,CAACI,UAAU;YAC9CC,MAAM;YACNC,WAAWzB;QACb;KACD;AACH,EAAC;AAED,OAAO,MAAM0B,mBAAkE;IAC7ErB,KAAK1B;IACL2B,UAAUhC;IACVoB,aAAa;IACba,aAAa;IACbC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QAAEC,MAAMhD,YAAYgD,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;IACzDa,OAAO;QAAEC,aAAalE,MAAMmE,YAAY,CAACC,OAAO;IAAC;IACjDf,YAAY;QACV;YACEC,MAAM;YACNC,OAAO;YACPV,aAAa;YACbW,WAAWxD,MAAMyD,kBAAkB,CAACI,UAAU;QAChD;QACA;YACEP,MAAM;YACNC,OAAO;YACPV,aAAa;YACbW,WAAWxD,MAAMyD,kBAAkB,CAACI,UAAU;QAChD;KACD;AACH,EAAC;AAED,OAAO,MAAMQ,oBAA2C;IACtD1B,KAAKzB;IACL0B,UAAUhC;IACVoB,aAAa;IACba,aAAa;IACbC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QAAEC,MAAMhD,YAAYgD,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;AAC3D,EAAC;AAED,OAAO,MAAMkB,oBACX;IACE3B,KAAKxB;IACLyB,UAAUhC;IACVoB,aAAa;IACba,aAAa;IACbC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QAAEC,MAAM/C,cAAc+C,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;IAC3DC,YAAY;QACV;YACEC,MAAM;YACNC,OAAO;YACPV,aAAa;YACbW,WAAWxD,MAAMyD,kBAAkB,CAACc,MAAM;QAC5C;KACD;AACH,EAAC;AAEH,OAAO,MAAMC,cAAoC;IAC/C;QACE7B,KAAK7B,iBAAiBE;QACtB8C,MAAM9D,MAAMyE,QAAQ,CAACC,MAAM;QAC3B1C,aAAa;QACbY,UAAUhC;QACViC,aAAa;QACbC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;QACzCC,MAAMP,WAAWO,IAAI;QACrB0B,MAAM;YACJhC,KAAK;YACLiC,aAAa5D;YACb4B,UAAUhC;YACVW,OAAO,CAAC;YACR,gEAAgE;YAChE4B,IAAI;gBAAE0B,UAAU;YAAI;YACpBC,OAAO;gBACL,6DAA6D;gBAC7D,iEAAiE;gBACjE,gEAAgE;gBAChE,8BAA8B;gBAC9B;oBACEnC,KAAK;oBACLiC,aAAa;oBACbhC,UAAUhC;oBACVW,OAAO;wBAAEwD,KAAK;wBAAIC,QAAQ;wBAASC,WAAW;oBAAQ;gBACxD;gBACA;oBACEtC,KAAK;oBACLiC,aAAa3D;oBACb2B,UAAUhC;oBACVW,OAAO;wBAAEW,OAAO;wBAAcC,WAAW;oBAAkB;gBAC7D;gBACA;oBACEQ,KAAK;oBACLiC,aAAa1D;oBACb0B,UAAUhC;oBACVkE,OAAO;wBACL;4BACEnC,KAAK;4BACLiC,aAAa;4BACbhC,UAAUhC;4BACVW,OAAO;gCACLC,SAAS;gCACTE,UACE;4BACJ;wBACF;qBACD;gBACH;gBACA;oBACEiB,KAAK;oBACLiC,aAAazD;oBACbyB,UAAUhC;oBACVkE,OAAO;wBACL;4BACEnC,KAAK;4BACLiC,aAAa;4BACbhC,UAAUhC;4BACV,+DAA+D;4BAC/D,gEAAgE;4BAChE,4DAA4D;4BAC5D,+CAA+C;4BAC/CW,OAAO;gCACLG,UAAU;gCACVwD,UAAU;gCACVC,MAAM;4BACR;wBACF;qBACD;gBACH;aACD;QACH;IACF;IACA;QACExC,KAAK7B,iBAAiBE,SAAS;QAC/B8C,MAAM9D,MAAMyE,QAAQ,CAACC,MAAM;QAC3B1C,aAAa;QACbY,UAAUhC;QACViC,aAAa;QACbC,UAAU9C,MAAM+C,iBAAiB,CAACC,OAAO;QACzCC,MAAMP,WAAWO,IAAI;QACrB0B,MAAM;YACJhC,KAAK;YACLiC,aAAa5D;YACb4B,UAAUhC;YACVW,OAAO;gBAAEC,SAAS;YAAW;YAC7B2B,IAAI;gBAAE0B,UAAU;YAAI;YACpBC,OAAO;gBACL;oBACEnC,KAAK;oBACLiC,aAAa1D;oBACb0B,UAAUhC;oBACVkE,OAAO;wBACL;4BACEnC,KAAK;4BACLiC,aAAa;4BACbhC,UAAUhC;4BACVW,OAAO;gCAAEC,SAAS;gCAAME,UAAU;4BAAa;wBACjD;wBACA;4BACEiB,KAAK;4BACLiC,aAAa;4BACbhC,UAAUhC;4BACVW,OAAO;gCACLC,SAAS;gCACTE,UAAU;4BACZ;wBACF;qBACD;gBACH;aACD;QACH;IACF;CACD,CAAA;AAED,eAAeN,YAAW"}
|
|
@@ -146,6 +146,14 @@ export const collectionEntriesSchema = {
|
|
|
146
146
|
color: 'secondary.main'
|
|
147
147
|
}
|
|
148
148
|
},
|
|
149
|
+
// Search divides `Children.toArray(children)` into one group per entry
|
|
150
|
+
// (AGL-1516), so the renderer has to hand it one React child per stamped
|
|
151
|
+
// node (AGL-3660). Wrapped in the single `<Branch>`, a block of N entries
|
|
152
|
+
// read as ONE child, never divided evenly, and failed open: no search
|
|
153
|
+
// field on the published page at all.
|
|
154
|
+
flags: {
|
|
155
|
+
positionalChildren: Aglyn.FEATURE_FLAG.ENABLED
|
|
156
|
+
},
|
|
149
157
|
attributes: [
|
|
150
158
|
{
|
|
151
159
|
name: 'collectionSlug',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/collection-entries.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 { mdiPostOutline } from '@aglyn/shared-data-mdi'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport { forwardRef, use } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n// Persisted component id (AGL-551/582); the compose pipeline references it\n// through the @aglyn/aglyn constant. Never rename.\nexport const ENTRIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRIES_COMPONENT_ID\n\nexport interface CollectionEntriesProps extends StackProps {\n /**\n * Collection to repeat over (compose-time, AGL-551). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Maximum entries rendered (compose-time; blank = all, capped at 100). */\n entriesLimit?: number | string\n /**\n * Only entries in this category repeat (compose-time, AGL-582).\n * Matches the collection's category by stable id or display name.\n */\n filterCategory?: string\n /** Only entries carrying this tag repeat (compose-time, AGL-582). */\n filterTag?: string\n /**\n * Entries per page (compose-time, AGL-620). When set, the block renders one\n * page window instead of the top `entriesLimit`; the built-in collection\n * list uses this with the `page` from `/{collection}/page/{n}`.\n */\n perPage?: number | string\n /** 1-based page for `perPage` (compose-time, AGL-620). */\n page?: number | string\n /**\n * Render this block only on page 1 of the routed listing (compose-time,\n * AGL-1871). For a LEAD card — a block with no `perPage`, showing the top\n * of the set — every `/{collection}/page/{n}` past the first otherwise\n * repeats the identical entry above a page of different ones.\n *\n * Opt-in and default off: a block that legitimately belongs on every page\n * of a listing (a \"popular posts\" rail) has exactly the same shape.\n */\n firstPageOnly?: boolean\n /**\n * Show a search box that filters the RENDERED entries by title/excerpt as\n * the reader types (AGL-1516, Figma 494:1220). Opt-in and default off, so\n * every existing instance renders exactly as before.\n *\n * Client-evaluated over the entries this block already holds — the page is\n * ISR-cached, so a keystroke never costs a Firestore read. That set is\n * whatever the expansion left the block holding, which paging, an\n * `entriesLimit` or the 100-entry cap can each cut down. A truncated block\n * says so under its results either way — on a miss and on a hit\n * (AGL-1516, AGL-2569) — rather than pretending global search.\n */\n search?: boolean\n /**\n * What the search box DOES with a query (AGL-1525, Figma 496:1218).\n *\n * - `filter` (default, and the reading of an absent value) — hides the\n * non-matching cards in place. What AGL-1516 shipped.\n * - `suggest` — the card grid stays exactly as it was and a floating panel\n * of matching entries opens under the field, ending in a link to the\n * site-wide results page. The frame's toolbar behaviour: a reader\n * skimming the list is not made to lose it to a typo.\n */\n searchMode?: CollectionSearchMode\n /** Hint text inside the search box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per rendered entry\n * (`expandCollectionEntries`, AGL-1516); never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /**\n * How many entries `searchIndex` was drawn from — server-stamped by\n * `expandCollectionEntries` alongside it (AGL-1516), never set by hand.\n * `searchIndex.length < searchTotal` is the only honest test of whether\n * this block holds the whole set; `perPage` is not, because\n * `entriesLimit` and the 100-entry cap truncate too.\n */\n searchTotal?: number\n /**\n * Whether the read behind `searchTotal` reached its own bound — server-\n * stamped beside it (AGL-1516), never set by hand.\n *\n * `searchTotal` is a count of what the server SAW, and the collection read\n * is limited. Without this flag a block holding 100 of 400 posts satisfies\n * `searchIndex.length === searchTotal` and renders the one empty state that\n * claims to have looked everywhere.\n */\n searchCapped?: boolean\n}\n\n/** The two things the toolbar search box can do (AGL-1516/AGL-1525). */\nexport type CollectionSearchMode = 'filter' | 'suggest'\n\n/**\n * Author-facing choices for {@link CollectionEntriesProps.searchMode}.\n *\n * Both values are truthy on purpose (AGL-1453): `''` cannot survive a save,\n * so an author who tried the other mode would have no route back. \"Filter\"\n * carries the absent-value reading, which is what every block published\n * before AGL-1525 has.\n */\nexport const COLLECTION_SEARCH_MODE_OPTIONS: ReadonlyArray<{\n value: CollectionSearchMode\n label: string\n}> = [\n { value: 'filter', label: 'Filter the entries in place' },\n { value: 'suggest', label: 'Show a suggestions dropdown' },\n]\n\n/** The searching variant's module (AGL-3438). */\ntype EntriesSearchModule = typeof import('./collection-entries-search')\n\n/**\n * What a searching block stamps on its root. The browser looks for it before\n * the page hydrates (`prepareEntriesSearch`); nothing styles or targets it.\n */\nexport const ENTRIES_SEARCH_ATTRIBUTE = 'data-aglyn-entries-search'\n\n/**\n * React's thenable contract (`status`/`value`), stamped on the load once it\n * settles — the plugin loader's ensure promise carries it for the same reason\n * (AGL-1541): `use()` unwraps a stamped promise synchronously, and suspends on\n * a bare one even after it has resolved.\n */\ntype StampedLoad = Promise<EntriesSearchModule> & {\n status?: 'pending' | 'fulfilled' | 'rejected'\n value?: EntriesSearchModule\n reason?: unknown\n}\n\nlet searchLoad: StampedLoad | undefined\n\n/**\n * The searching variant, fetched once per visit however many blocks ask. A\n * failed fetch (a deploy mid-visit) is forgotten rather than cached, so the\n * next render that asks tries again.\n */\nexport const loadEntriesSearch = (): Promise<EntriesSearchModule> => {\n if (searchLoad) return searchLoad\n const load: StampedLoad = import('./collection-entries-search')\n searchLoad = load\n load.then(\n (module) => {\n load.status = 'fulfilled'\n load.value = module\n },\n (reason: unknown) => {\n load.status = 'rejected'\n load.reason = reason\n if (searchLoad === load) searchLoad = undefined\n },\n )\n return load\n}\n\n/**\n * Loads the searching variant BEFORE the element renders, wherever a render\n * that waited for it would cost something (AGL-3438). The plugin calls it as\n * the element registers (`prepare` in `plugin.ts`), and awaits it.\n *\n * - **On the server, always.** A render that suspended there would hold the\n * whole streamed shell for the fetch — and a first, cold one would do it\n * on a page the ISR cache then keeps.\n * - **Where the whole library is loading** (`everything`): the console and\n * the besigner, where an author can turn search on at any moment.\n * - **In a browser about to hydrate a searching block**, which the server\n * HTML says by carrying {@link ENTRIES_SEARCH_ATTRIBUTE}. The fetch then\n * rides along with the element's own instead of starting when hydration\n * reaches the block.\n *\n * Anywhere else nothing is fetched: a page whose blocks have search off —\n * most of them — never downloads the box. A searching block that renders\n * without it (a client-side navigation onto one) suspends until it lands,\n * which holds the navigation, never a half-drawn list.\n */\nexport const prepareEntriesSearch = (\n everything: boolean,\n): Promise<unknown> | undefined =>\n everything ||\n typeof document === 'undefined' ||\n document.querySelector(`[${ENTRIES_SEARCH_ATTRIBUTE}]`)\n ? loadEntriesSearch()\n : undefined\n\n/**\n * Repeats its children once per published entry of a content collection\n * (AGL-551) — the collections sibling of the dataset repeatable. The tenant\n * expands it at compose time with `{{entry.*}}` tokens; in the besigner the\n * template renders once with literal tokens, matching the repeatable UX.\n *\n * With `search` on (AGL-1516) the block is `SearchingEntries`, from its own\n * module (AGL-3438). Off — the default, and most blocks — it is the plain\n * stack below, and the search box's MUI input never reaches the page.\n *\n * There is NO Suspense boundary around the searching variant, on purpose. A\n * boundary would have to be in the server's tree too, and a client render\n * that suspended inside it would show its fallback: a list without its box,\n * then the box pushing it down. Without one, a render that has to wait holds\n * whatever is above it — the hydration of the page (whose server HTML stays\n * exactly as it is), or a navigation (whose old page stays up) — and\n * `prepareEntriesSearch` makes sure the published page almost never has to.\n */\nconst CollectionEntries = forwardRef<HTMLDivElement, CollectionEntriesProps>(\n (props, ref) => {\n if (props.search) {\n const { SearchingEntries } = use(loadEntriesSearch())\n return <SearchingEntries ref={ref} {...props} />\n }\n // collectionSlug/entriesLimit/filter*/search* are compose-time or\n // search-only attributes: strip so they never hit the DOM.\n const {\n collectionSlug,\n entriesLimit,\n filterCategory,\n filterTag,\n perPage,\n page,\n firstPageOnly,\n search,\n searchMode,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n children,\n ...rest\n } = props\n return (\n <MuiStack ref={ref} spacing={4} {...rest}>\n {children}\n </MuiStack>\n )\n },\n)\nCollectionEntries.displayName = 'AglynCollectionEntries'\n\nexport const collectionEntriesSchema: Aglyn.ComponentSchema<CollectionEntriesProps> =\n {\n $id: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Entries',\n description: 'Repeats its children once per entry in a content collection.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose published entries the children repeat ' +\n 'over (e.g. \"blog\"). Leave blank on a list-template page to use ' +\n 'the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'entriesLimit',\n label: 'Entries limit',\n description: 'Maximum entries rendered (blank = all, capped at 100).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'filterCategory',\n label: 'Filter by category',\n description:\n 'Only entries in this category repeat — the category name or its ' +\n 'stable id both match (e.g. \"Guides\"). Blank = no category filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'filterTag',\n label: 'Filter by tag',\n description:\n 'Only entries carrying this tag repeat (e.g. \"nextjs\"). Blank = ' +\n 'no tag filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'perPage',\n label: 'Entries per page',\n description:\n 'Paginate the list: entries per page (blank = no pagination). ' +\n 'Pairs with the page from /{collection}/page/{n}.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'page',\n label: 'Page',\n description: '1-based page to render when Entries per page is set.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'firstPageOnly',\n label: 'Only on page 1',\n description:\n 'Show this block on the first page of the listing only. Use it ' +\n 'for a featured or lead card: without it the same entry repeats ' +\n 'at the top of every /page/{n}, above a page of different ones.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'search',\n label: 'Search',\n description:\n 'Show a search box that filters the rendered entries by title ' +\n 'and excerpt as the reader types. It searches the entries this ' +\n 'block rendered — whatever paging, an entry limit or the ' +\n '100-entry cap left it holding, and it says so on a miss.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'searchMode',\n label: 'When the reader types',\n description:\n 'Filter the entries in place, or leave the list alone and open a ' +\n 'dropdown of matching entries under the box — each one a link, ' +\n 'ending in \"View all results\" for a search across the whole site.',\n component: Aglyn.FieldComponentType.SELECT,\n options: COLLECTION_SEARCH_MODE_OPTIONS.map((option) => ({\n ...option,\n })),\n // Meaningless while there is no search box to type in.\n condition: { when: 'search', is: true },\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this block’s own search box — the one that ' +\n 'filters the entries below it as a reader types. Blank shows ' +\n '\"Search posts…\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Meaningless while there is no search box to hint in.\n condition: { when: 'search', is: true },\n },\n // `searchIndex` and `searchTotal` are deliberately NOT attributes: both\n // are server-stamped by expandCollectionEntries, like Category Pills'\n // `items` and Related Posts' `entries`.\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between entries.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n }\n\nexport { CollectionEntries }\n// The collection family's palette presets ride this element's registry entry\n// (`presets: 'collectionPresets'` in plugin.ts), as they did when the family\n// was one module.\nexport { collectionPresets } from './collection-presets'\n"],"names":["Aglyn","mdiPostOutline","MuiStack","forwardRef","use","BUNDLE_ID","ENTRIES_ID","COLLECTION_ENTRIES_COMPONENT_ID","COLLECTION_SEARCH_MODE_OPTIONS","value","label","ENTRIES_SEARCH_ATTRIBUTE","searchLoad","loadEntriesSearch","load","then","module","status","reason","undefined","prepareEntriesSearch","everything","document","querySelector","CollectionEntries","props","ref","search","SearchingEntries","collectionSlug","entriesLimit","filterCategory","filterTag","perPage","page","firstPageOnly","searchMode","searchPlaceholder","searchIndex","searchTotal","searchCapped","children","rest","spacing","displayName","collectionEntriesSchema","$id","pluginId","description","category","ComponentCategory","DATA_DISPLAY","icon","path","sx","color","attributes","name","component","FieldComponentType","TEXT_FIELD","type","SWITCH","SELECT","options","map","option","condition","when","is","collectionPresets"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/collection-entries.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 { mdiPostOutline } from '@aglyn/shared-data-mdi'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport { forwardRef, use } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n// Persisted component id (AGL-551/582); the compose pipeline references it\n// through the @aglyn/aglyn constant. Never rename.\nexport const ENTRIES_ID: Aglyn.ComponentId =\n Aglyn.COLLECTION_ENTRIES_COMPONENT_ID\n\nexport interface CollectionEntriesProps extends StackProps {\n /**\n * Collection to repeat over (compose-time, AGL-551). Blank = the\n * collection routed by the current URL on list-template screens.\n */\n collectionSlug?: string\n /** Maximum entries rendered (compose-time; blank = all, capped at 100). */\n entriesLimit?: number | string\n /**\n * Only entries in this category repeat (compose-time, AGL-582).\n * Matches the collection's category by stable id or display name.\n */\n filterCategory?: string\n /** Only entries carrying this tag repeat (compose-time, AGL-582). */\n filterTag?: string\n /**\n * Entries per page (compose-time, AGL-620). When set, the block renders one\n * page window instead of the top `entriesLimit`; the built-in collection\n * list uses this with the `page` from `/{collection}/page/{n}`.\n */\n perPage?: number | string\n /** 1-based page for `perPage` (compose-time, AGL-620). */\n page?: number | string\n /**\n * Render this block only on page 1 of the routed listing (compose-time,\n * AGL-1871). For a LEAD card — a block with no `perPage`, showing the top\n * of the set — every `/{collection}/page/{n}` past the first otherwise\n * repeats the identical entry above a page of different ones.\n *\n * Opt-in and default off: a block that legitimately belongs on every page\n * of a listing (a \"popular posts\" rail) has exactly the same shape.\n */\n firstPageOnly?: boolean\n /**\n * Show a search box that filters the RENDERED entries by title/excerpt as\n * the reader types (AGL-1516, Figma 494:1220). Opt-in and default off, so\n * every existing instance renders exactly as before.\n *\n * Client-evaluated over the entries this block already holds — the page is\n * ISR-cached, so a keystroke never costs a Firestore read. That set is\n * whatever the expansion left the block holding, which paging, an\n * `entriesLimit` or the 100-entry cap can each cut down. A truncated block\n * says so under its results either way — on a miss and on a hit\n * (AGL-1516, AGL-2569) — rather than pretending global search.\n */\n search?: boolean\n /**\n * What the search box DOES with a query (AGL-1525, Figma 496:1218).\n *\n * - `filter` (default, and the reading of an absent value) — hides the\n * non-matching cards in place. What AGL-1516 shipped.\n * - `suggest` — the card grid stays exactly as it was and a floating panel\n * of matching entries opens under the field, ending in a link to the\n * site-wide results page. The frame's toolbar behaviour: a reader\n * skimming the list is not made to lose it to a typo.\n */\n searchMode?: CollectionSearchMode\n /** Hint text inside the search box (blank = \"Search posts…\"). */\n searchPlaceholder?: string\n /**\n * Server-stamped matchable text per rendered entry\n * (`expandCollectionEntries`, AGL-1516); never set by hand.\n */\n searchIndex?: Aglyn.CollectionEntrySearchItem[]\n /**\n * How many entries `searchIndex` was drawn from — server-stamped by\n * `expandCollectionEntries` alongside it (AGL-1516), never set by hand.\n * `searchIndex.length < searchTotal` is the only honest test of whether\n * this block holds the whole set; `perPage` is not, because\n * `entriesLimit` and the 100-entry cap truncate too.\n */\n searchTotal?: number\n /**\n * Whether the read behind `searchTotal` reached its own bound — server-\n * stamped beside it (AGL-1516), never set by hand.\n *\n * `searchTotal` is a count of what the server SAW, and the collection read\n * is limited. Without this flag a block holding 100 of 400 posts satisfies\n * `searchIndex.length === searchTotal` and renders the one empty state that\n * claims to have looked everywhere.\n */\n searchCapped?: boolean\n}\n\n/** The two things the toolbar search box can do (AGL-1516/AGL-1525). */\nexport type CollectionSearchMode = 'filter' | 'suggest'\n\n/**\n * Author-facing choices for {@link CollectionEntriesProps.searchMode}.\n *\n * Both values are truthy on purpose (AGL-1453): `''` cannot survive a save,\n * so an author who tried the other mode would have no route back. \"Filter\"\n * carries the absent-value reading, which is what every block published\n * before AGL-1525 has.\n */\nexport const COLLECTION_SEARCH_MODE_OPTIONS: ReadonlyArray<{\n value: CollectionSearchMode\n label: string\n}> = [\n { value: 'filter', label: 'Filter the entries in place' },\n { value: 'suggest', label: 'Show a suggestions dropdown' },\n]\n\n/** The searching variant's module (AGL-3438). */\ntype EntriesSearchModule = typeof import('./collection-entries-search')\n\n/**\n * What a searching block stamps on its root. The browser looks for it before\n * the page hydrates (`prepareEntriesSearch`); nothing styles or targets it.\n */\nexport const ENTRIES_SEARCH_ATTRIBUTE = 'data-aglyn-entries-search'\n\n/**\n * React's thenable contract (`status`/`value`), stamped on the load once it\n * settles — the plugin loader's ensure promise carries it for the same reason\n * (AGL-1541): `use()` unwraps a stamped promise synchronously, and suspends on\n * a bare one even after it has resolved.\n */\ntype StampedLoad = Promise<EntriesSearchModule> & {\n status?: 'pending' | 'fulfilled' | 'rejected'\n value?: EntriesSearchModule\n reason?: unknown\n}\n\nlet searchLoad: StampedLoad | undefined\n\n/**\n * The searching variant, fetched once per visit however many blocks ask. A\n * failed fetch (a deploy mid-visit) is forgotten rather than cached, so the\n * next render that asks tries again.\n */\nexport const loadEntriesSearch = (): Promise<EntriesSearchModule> => {\n if (searchLoad) return searchLoad\n const load: StampedLoad = import('./collection-entries-search')\n searchLoad = load\n load.then(\n (module) => {\n load.status = 'fulfilled'\n load.value = module\n },\n (reason: unknown) => {\n load.status = 'rejected'\n load.reason = reason\n if (searchLoad === load) searchLoad = undefined\n },\n )\n return load\n}\n\n/**\n * Loads the searching variant BEFORE the element renders, wherever a render\n * that waited for it would cost something (AGL-3438). The plugin calls it as\n * the element registers (`prepare` in `plugin.ts`), and awaits it.\n *\n * - **On the server, always.** A render that suspended there would hold the\n * whole streamed shell for the fetch — and a first, cold one would do it\n * on a page the ISR cache then keeps.\n * - **Where the whole library is loading** (`everything`): the console and\n * the besigner, where an author can turn search on at any moment.\n * - **In a browser about to hydrate a searching block**, which the server\n * HTML says by carrying {@link ENTRIES_SEARCH_ATTRIBUTE}. The fetch then\n * rides along with the element's own instead of starting when hydration\n * reaches the block.\n *\n * Anywhere else nothing is fetched: a page whose blocks have search off —\n * most of them — never downloads the box. A searching block that renders\n * without it (a client-side navigation onto one) suspends until it lands,\n * which holds the navigation, never a half-drawn list.\n */\nexport const prepareEntriesSearch = (\n everything: boolean,\n): Promise<unknown> | undefined =>\n everything ||\n typeof document === 'undefined' ||\n document.querySelector(`[${ENTRIES_SEARCH_ATTRIBUTE}]`)\n ? loadEntriesSearch()\n : undefined\n\n/**\n * Repeats its children once per published entry of a content collection\n * (AGL-551) — the collections sibling of the dataset repeatable. The tenant\n * expands it at compose time with `{{entry.*}}` tokens; in the besigner the\n * template renders once with literal tokens, matching the repeatable UX.\n *\n * With `search` on (AGL-1516) the block is `SearchingEntries`, from its own\n * module (AGL-3438). Off — the default, and most blocks — it is the plain\n * stack below, and the search box's MUI input never reaches the page.\n *\n * There is NO Suspense boundary around the searching variant, on purpose. A\n * boundary would have to be in the server's tree too, and a client render\n * that suspended inside it would show its fallback: a list without its box,\n * then the box pushing it down. Without one, a render that has to wait holds\n * whatever is above it — the hydration of the page (whose server HTML stays\n * exactly as it is), or a navigation (whose old page stays up) — and\n * `prepareEntriesSearch` makes sure the published page almost never has to.\n */\nconst CollectionEntries = forwardRef<HTMLDivElement, CollectionEntriesProps>(\n (props, ref) => {\n if (props.search) {\n const { SearchingEntries } = use(loadEntriesSearch())\n return <SearchingEntries ref={ref} {...props} />\n }\n // collectionSlug/entriesLimit/filter*/search* are compose-time or\n // search-only attributes: strip so they never hit the DOM.\n const {\n collectionSlug,\n entriesLimit,\n filterCategory,\n filterTag,\n perPage,\n page,\n firstPageOnly,\n search,\n searchMode,\n searchPlaceholder,\n searchIndex,\n searchTotal,\n searchCapped,\n children,\n ...rest\n } = props\n return (\n <MuiStack ref={ref} spacing={4} {...rest}>\n {children}\n </MuiStack>\n )\n },\n)\nCollectionEntries.displayName = 'AglynCollectionEntries'\n\nexport const collectionEntriesSchema: Aglyn.ComponentSchema<CollectionEntriesProps> =\n {\n $id: ENTRIES_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Collection Entries',\n description: 'Repeats its children once per entry in a content collection.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPostOutline.path, sx: { color: 'secondary.main' } },\n // Search divides `Children.toArray(children)` into one group per entry\n // (AGL-1516), so the renderer has to hand it one React child per stamped\n // node (AGL-3660). Wrapped in the single `<Branch>`, a block of N entries\n // read as ONE child, never divided evenly, and failed open: no search\n // field on the published page at all.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'collectionSlug',\n label: 'Collection slug',\n description:\n 'Content collection whose published entries the children repeat ' +\n 'over (e.g. \"blog\"). Leave blank on a list-template page to use ' +\n 'the collection from the URL.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'entriesLimit',\n label: 'Entries limit',\n description: 'Maximum entries rendered (blank = all, capped at 100).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'filterCategory',\n label: 'Filter by category',\n description:\n 'Only entries in this category repeat — the category name or its ' +\n 'stable id both match (e.g. \"Guides\"). Blank = no category filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'filterTag',\n label: 'Filter by tag',\n description:\n 'Only entries carrying this tag repeat (e.g. \"nextjs\"). Blank = ' +\n 'no tag filter.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'perPage',\n label: 'Entries per page',\n description:\n 'Paginate the list: entries per page (blank = no pagination). ' +\n 'Pairs with the page from /{collection}/page/{n}.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'page',\n label: 'Page',\n description: '1-based page to render when Entries per page is set.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'firstPageOnly',\n label: 'Only on page 1',\n description:\n 'Show this block on the first page of the listing only. Use it ' +\n 'for a featured or lead card: without it the same entry repeats ' +\n 'at the top of every /page/{n}, above a page of different ones.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'search',\n label: 'Search',\n description:\n 'Show a search box that filters the rendered entries by title ' +\n 'and excerpt as the reader types. It searches the entries this ' +\n 'block rendered — whatever paging, an entry limit or the ' +\n '100-entry cap left it holding, and it says so on a miss.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'searchMode',\n label: 'When the reader types',\n description:\n 'Filter the entries in place, or leave the list alone and open a ' +\n 'dropdown of matching entries under the box — each one a link, ' +\n 'ending in \"View all results\" for a search across the whole site.',\n component: Aglyn.FieldComponentType.SELECT,\n options: COLLECTION_SEARCH_MODE_OPTIONS.map((option) => ({\n ...option,\n })),\n // Meaningless while there is no search box to type in.\n condition: { when: 'search', is: true },\n },\n {\n name: 'searchPlaceholder',\n label: 'Search placeholder',\n description:\n 'Hint text inside this block’s own search box — the one that ' +\n 'filters the entries below it as a reader types. Blank shows ' +\n '\"Search posts…\".',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n // Meaningless while there is no search box to hint in.\n condition: { when: 'search', is: true },\n },\n // `searchIndex` and `searchTotal` are deliberately NOT attributes: both\n // are server-stamped by expandCollectionEntries, like Category Pills'\n // `items` and Related Posts' `entries`.\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between entries.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n }\n\nexport { CollectionEntries }\n// The collection family's palette presets ride this element's registry entry\n// (`presets: 'collectionPresets'` in plugin.ts), as they did when the family\n// was one module.\nexport { collectionPresets } from './collection-presets'\n"],"names":["Aglyn","mdiPostOutline","MuiStack","forwardRef","use","BUNDLE_ID","ENTRIES_ID","COLLECTION_ENTRIES_COMPONENT_ID","COLLECTION_SEARCH_MODE_OPTIONS","value","label","ENTRIES_SEARCH_ATTRIBUTE","searchLoad","loadEntriesSearch","load","then","module","status","reason","undefined","prepareEntriesSearch","everything","document","querySelector","CollectionEntries","props","ref","search","SearchingEntries","collectionSlug","entriesLimit","filterCategory","filterTag","perPage","page","firstPageOnly","searchMode","searchPlaceholder","searchIndex","searchTotal","searchCapped","children","rest","spacing","displayName","collectionEntriesSchema","$id","pluginId","description","category","ComponentCategory","DATA_DISPLAY","icon","path","sx","color","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","TEXT_FIELD","type","SWITCH","SELECT","options","map","option","condition","when","is","collectionPresets"],"mappings":"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"}
|
|
@@ -56,11 +56,11 @@ export interface InlineTextProps {
|
|
|
56
56
|
/**
|
|
57
57
|
* One run of text inside a sentence (AGL-1235).
|
|
58
58
|
*
|
|
59
|
-
* The gap this fills:
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* The gap this fills: a statement whose argument lives in an emphasised
|
|
60
|
+
* phrase ("Aglyn commerce is **part of the platform**") — a run of its own
|
|
61
|
+
* colour or weight inside one line. A Typography can hold elements too
|
|
62
|
+
* (AGL-3672), but only after its own text, so it cannot put the emphasis
|
|
63
|
+
* mid-sentence.
|
|
64
64
|
*
|
|
65
65
|
* This is a leaf too, deliberately: the shape that renders correctly is a
|
|
66
66
|
* block container holding SIBLING inline runs, not a nested span. Sibling
|
|
@@ -96,11 +96,11 @@ export const ID = 'muiInlineText';
|
|
|
96
96
|
/**
|
|
97
97
|
* One run of text inside a sentence (AGL-1235).
|
|
98
98
|
*
|
|
99
|
-
* The gap this fills:
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
99
|
+
* The gap this fills: a statement whose argument lives in an emphasised
|
|
100
|
+
* phrase ("Aglyn commerce is **part of the platform**") — a run of its own
|
|
101
|
+
* colour or weight inside one line. A Typography can hold elements too
|
|
102
|
+
* (AGL-3672), but only after its own text, so it cannot put the emphasis
|
|
103
|
+
* mid-sentence.
|
|
104
104
|
*
|
|
105
105
|
* This is a leaf too, deliberately: the shape that renders correctly is a
|
|
106
106
|
* block container holding SIBLING inline runs, not a nested span. Sibling
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/inline-text.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 { mdiFormatColorHighlight, mdiFormatColorText } from '@aglyn/shared-data-mdi'\nimport MuiBox from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { authorHtmlLabel } 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 ID: Aglyn.ComponentId = 'muiInlineText'\n\n/**\n * Inline elements the run may render as. An allow-list rather than a\n * free-text field, for the same reason Box's is (AGL-1201): `element` is\n * persisted and rendered verbatim into every visitor's page, so a typed\n * value would let an author put `script`/`iframe` there.\n *\n * All five are phrasing content, so a run is always legal inside a\n * paragraph — which is the whole point of the element.\n */\nexport const INLINE_TEXT_ELEMENTS = [\n 'span',\n 'strong',\n 'em',\n 'mark',\n 'small',\n] as const\nexport type InlineTextElement = (typeof INLINE_TEXT_ELEMENTS)[number]\n\n/**\n * Theme palette paths, never raw hexes: the design's `text/muted` and\n * `text/primary` tokens ARE `text.secondary` / `text.primary`, and a\n * hardcoded `#757575` would ignore the site's theme (and dark mode).\n *\n * Every value is a real sentinel — an option value of `''` is stripped\n * before save and the pick silently reverts on reload (AGL-1191), so\n * \"inherit\" has to be spelled out rather than left blank.\n */\nconst TONE_COLOR: Record<string, string> = {\n inherit: undefined,\n primary: 'text.primary',\n secondary: 'text.secondary',\n disabled: 'text.disabled',\n accent: 'primary.main',\n accentAlt: 'secondary.main',\n success: 'success.main',\n warning: 'warning.main',\n error: 'error.main',\n}\n\n/** Named weights so the field is a pick, not a number to remember. */\nconst FONT_WEIGHT: Record<string, number> = {\n inherit: undefined,\n regular: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n}\n\n/**\n * `text-decoration` PROPAGATES — it is not inherited. An ancestor's\n * underline (a Screen Link, a struck-through block) is painted straight\n * across every in-flow inline descendant, and `text-decoration: none` on\n * the descendant CANNOT remove it. Only an element that is not an in-flow\n * non-atomic inline box escapes the propagation, which is why \"None\" also\n * switches the run to `inline-block`: an atomic inline box is not painted\n * through.\n *\n * That costs line-breaking — an inline-block will not break across lines —\n * so it is opt-in rather than the default, and the field says so. The\n * default, `inherit`, is a plain `display: inline` run that flows and wraps\n * with the rest of the sentence.\n */\nconst DECORATION_SX: Record<string, Record<string, string>> = {\n inherit: {},\n none: { display: 'inline-block', textDecoration: 'none' },\n underline: { textDecoration: 'underline' },\n lineThrough: { textDecoration: 'line-through' },\n}\n\nexport interface InlineTextProps {\n /** The DOM element rendered; defaults to `span`. */\n element?: InlineTextElement\n /** Palette token key from {@link TONE_COLOR}. */\n tone?: string\n /** Named weight key from {@link FONT_WEIGHT}. */\n weight?: string\n /** Decoration behaviour key from {@link DECORATION_SX}. */\n decoration?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute. Declared because the merge below reads it —\n * undeclared, no typed caller could style a run (AGL-1323).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the run, 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 /** The text of this run; edited inline on the canvas. */\n children?: ReactNode\n}\n\n/**\n * One run of text inside a sentence (AGL-1235).\n *\n * The gap this fills: `muiTypography` is `textEditable`, therefore a leaf —\n * `nodeAcceptsChildren` reports false, so no span can be nested inside it.\n * A statement whose argument lives in an emphasised phrase (\"Aglyn commerce\n * is **part of the platform**\") could not be authored at all; the whole line\n * was one flat colour.\n *\n * This is a leaf too, deliberately: the shape that renders correctly is a\n * block container holding SIBLING inline runs, not a nested span. Sibling\n * runs flow and wrap as a single paragraph, so the sentence breaks\n * naturally with the emphasis landing in the right place. Drop a Box as\n * `p`, then one of these per style change.\n */\nconst InlineText = forwardRef<HTMLElement, InlineTextProps>((props, ref) => {\n const { element, tone, weight, decoration, children, sx, html, ...rest } =\n props\n const component = INLINE_TEXT_ELEMENTS.includes(element as InlineTextElement)\n ? (element as InlineTextElement)\n : 'span'\n // Node styles ride the renderer-merged sx; recompose rather than spread\n // (the stack.ts/link-box.tsx pattern). Writing an `sx` literal after the\n // props spread would REPLACE the author's styles silently — the defect\n // class `aglyn/no-sx-after-spread` guards (AGL-1240/1284).\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // A cleared attribute persists as null, so every lookup below must miss\n // rather than throw; `Record[null]` is simply undefined.\n const color = TONE_COLOR[tone]\n const fontWeight = FONT_WEIGHT[weight]\n return (\n <MuiBox\n ref={ref}\n component={component}\n {...rest}\n // Order matters: the baseline first, then the declared attributes,\n // then the author's node styles last so the Styles panel always wins.\n sx={[\n { display: 'inline' },\n color ? { color } : {},\n fontWeight ? { fontWeight } : {},\n DECORATION_SX[decoration] ?? {},\n ...nodeSx,\n ]}\n >\n {authorHtmlLabel(html) ?? children}\n </MuiBox>\n )\n})\nInlineText.displayName = 'AglynInlineText'\n\nexport const schema: Aglyn.ComponentSchema<InlineTextProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Inline Text',\n description:\n 'One run of text inside a sentence — put several side by side in a ' +\n 'paragraph to emphasize a phrase without breaking the line.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorText.path, sx: { color: '#057822' } },\n flags: {\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n // The run IS its text: edited inline on the canvas, like Typography.\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557), for a different reason than the controls: this\n // run is not interactive, it is `display: inline`. Every element it may\n // render as is phrasing content — that is the guarantee INLINE_TEXT_ELEMENTS\n // exists to make — so a list inside one is a block in an inline box, which\n // the parser lifts straight back out of the enclosing paragraph. Links stay\n // with Screen Link, whose job is a resolved target rather than a typed href.\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this run renders as. `strong` and `em` carry ' +\n 'meaning for screen readers and search engines; `mark` marks a ' +\n 'highlighted phrase; `span` is purely visual.',\n component: Aglyn.FieldComponentType.SELECT,\n options: INLINE_TEXT_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'tone',\n label: 'Color',\n description:\n \"Palette token for this run. Two-tone text is a muted sentence \" +\n 'with one run set to Primary (or Accent) — theme tokens, so it ' +\n 'still reads correctly in dark mode.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'primary', label: 'Text primary' },\n { value: 'secondary', label: 'Text muted' },\n { value: 'disabled', label: 'Text disabled' },\n { value: 'accent', label: 'Accent' },\n { value: 'accentAlt', label: 'Accent alternate' },\n { value: 'success', label: 'Success' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' },\n ],\n },\n {\n name: 'weight',\n label: 'Weight',\n description: 'Font weight for this run only.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'regular', label: 'Regular' },\n { value: 'medium', label: 'Medium' },\n { value: 'semibold', label: 'Semi bold' },\n { value: 'bold', label: 'Bold' },\n ],\n },\n {\n name: 'decoration',\n label: 'Underline / strike',\n description:\n 'Inherit follows the surrounding text, including a link\\'s ' +\n 'underline — an underline PROPAGATES down and cannot be switched ' +\n 'off from inside. None does switch it off, at the cost of the run ' +\n 'no longer breaking across lines, so keep it to a short phrase.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'none', label: 'None (does not wrap)' },\n { value: 'underline', label: 'Underline' },\n { value: 'lineThrough', label: 'Strikethrough' },\n ],\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Inline Text',\n pluginId: BUNDLE_ID,\n description: 'One run of text inside a sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'span', children: 'Inline text' },\n },\n },\n {\n $id: generatePresetId(ID, 'emphasis'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Emphasized Text',\n pluginId: BUNDLE_ID,\n description: 'A phrase picked out of a muted sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'the emphasized phrase',\n },\n },\n },\n {\n // The whole shape in one drop (AGL-1235): the reason the emphasis was\n // never authored is that assembling it by hand meant a container, three\n // runs and a colour on each. This is that assembly, ready to retype.\n $id: generatePresetId(ID, 'statement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Two-tone Statement',\n pluginId: BUNDLE_ID,\n description: 'A muted sentence with one phrase emphasized',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n // A real `<p>`, not a div: the runs are phrasing content, so the\n // paragraph is the honest container and screen readers get a\n // sentence rather than three unrelated fragments.\n componentId: 'muiBox',\n pluginId: BUNDLE_ID,\n props: { component: 'p' },\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0 },\n nodes: [\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: 'Everything you build is ',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'part of the platform',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: ', not bolted onto it.',\n },\n },\n ],\n },\n },\n]\n\nexport default InlineText\n"],"names":["Aglyn","mdiFormatColorHighlight","mdiFormatColorText","MuiBox","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","generatePresetId","ID","INLINE_TEXT_ELEMENTS","TONE_COLOR","inherit","undefined","primary","secondary","disabled","accent","accentAlt","success","warning","error","FONT_WEIGHT","regular","medium","semibold","bold","DECORATION_SX","none","display","textDecoration","underline","lineThrough","InlineText","props","ref","element","tone","weight","decoration","children","sx","html","rest","component","includes","nodeSx","Array","isArray","color","fontWeight","displayName","schema","$id","pluginId","description","category","ComponentCategory","TEXT","icon","path","flags","lazyHydration","FEATURE_FLAG","ENABLED","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","attributes","name","label","FieldComponentType","SELECT","options","map","value","presets","type","NodeType","PRESET","data","componentId","marginTop","marginRight","marginBottom","marginLeft","nodes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/inline-text.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 { mdiFormatColorHighlight, mdiFormatColorText } from '@aglyn/shared-data-mdi'\nimport MuiBox from '@mui/material/Box'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { authorHtmlLabel } 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 ID: Aglyn.ComponentId = 'muiInlineText'\n\n/**\n * Inline elements the run may render as. An allow-list rather than a\n * free-text field, for the same reason Box's is (AGL-1201): `element` is\n * persisted and rendered verbatim into every visitor's page, so a typed\n * value would let an author put `script`/`iframe` there.\n *\n * All five are phrasing content, so a run is always legal inside a\n * paragraph — which is the whole point of the element.\n */\nexport const INLINE_TEXT_ELEMENTS = [\n 'span',\n 'strong',\n 'em',\n 'mark',\n 'small',\n] as const\nexport type InlineTextElement = (typeof INLINE_TEXT_ELEMENTS)[number]\n\n/**\n * Theme palette paths, never raw hexes: the design's `text/muted` and\n * `text/primary` tokens ARE `text.secondary` / `text.primary`, and a\n * hardcoded `#757575` would ignore the site's theme (and dark mode).\n *\n * Every value is a real sentinel — an option value of `''` is stripped\n * before save and the pick silently reverts on reload (AGL-1191), so\n * \"inherit\" has to be spelled out rather than left blank.\n */\nconst TONE_COLOR: Record<string, string> = {\n inherit: undefined,\n primary: 'text.primary',\n secondary: 'text.secondary',\n disabled: 'text.disabled',\n accent: 'primary.main',\n accentAlt: 'secondary.main',\n success: 'success.main',\n warning: 'warning.main',\n error: 'error.main',\n}\n\n/** Named weights so the field is a pick, not a number to remember. */\nconst FONT_WEIGHT: Record<string, number> = {\n inherit: undefined,\n regular: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n}\n\n/**\n * `text-decoration` PROPAGATES — it is not inherited. An ancestor's\n * underline (a Screen Link, a struck-through block) is painted straight\n * across every in-flow inline descendant, and `text-decoration: none` on\n * the descendant CANNOT remove it. Only an element that is not an in-flow\n * non-atomic inline box escapes the propagation, which is why \"None\" also\n * switches the run to `inline-block`: an atomic inline box is not painted\n * through.\n *\n * That costs line-breaking — an inline-block will not break across lines —\n * so it is opt-in rather than the default, and the field says so. The\n * default, `inherit`, is a plain `display: inline` run that flows and wraps\n * with the rest of the sentence.\n */\nconst DECORATION_SX: Record<string, Record<string, string>> = {\n inherit: {},\n none: { display: 'inline-block', textDecoration: 'none' },\n underline: { textDecoration: 'underline' },\n lineThrough: { textDecoration: 'line-through' },\n}\n\nexport interface InlineTextProps {\n /** The DOM element rendered; defaults to `span`. */\n element?: InlineTextElement\n /** Palette token key from {@link TONE_COLOR}. */\n tone?: string\n /** Named weight key from {@link FONT_WEIGHT}. */\n weight?: string\n /** Decoration behaviour key from {@link DECORATION_SX}. */\n decoration?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute. Declared because the merge below reads it —\n * undeclared, no typed caller could style a run (AGL-1323).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the run, 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 /** The text of this run; edited inline on the canvas. */\n children?: ReactNode\n}\n\n/**\n * One run of text inside a sentence (AGL-1235).\n *\n * The gap this fills: a statement whose argument lives in an emphasised\n * phrase (\"Aglyn commerce is **part of the platform**\") — a run of its own\n * colour or weight inside one line. A Typography can hold elements too\n * (AGL-3672), but only after its own text, so it cannot put the emphasis\n * mid-sentence.\n *\n * This is a leaf too, deliberately: the shape that renders correctly is a\n * block container holding SIBLING inline runs, not a nested span. Sibling\n * runs flow and wrap as a single paragraph, so the sentence breaks\n * naturally with the emphasis landing in the right place. Drop a Box as\n * `p`, then one of these per style change.\n */\nconst InlineText = forwardRef<HTMLElement, InlineTextProps>((props, ref) => {\n const { element, tone, weight, decoration, children, sx, html, ...rest } =\n props\n const component = INLINE_TEXT_ELEMENTS.includes(element as InlineTextElement)\n ? (element as InlineTextElement)\n : 'span'\n // Node styles ride the renderer-merged sx; recompose rather than spread\n // (the stack.ts/link-box.tsx pattern). Writing an `sx` literal after the\n // props spread would REPLACE the author's styles silently — the defect\n // class `aglyn/no-sx-after-spread` guards (AGL-1240/1284).\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // A cleared attribute persists as null, so every lookup below must miss\n // rather than throw; `Record[null]` is simply undefined.\n const color = TONE_COLOR[tone]\n const fontWeight = FONT_WEIGHT[weight]\n return (\n <MuiBox\n ref={ref}\n component={component}\n {...rest}\n // Order matters: the baseline first, then the declared attributes,\n // then the author's node styles last so the Styles panel always wins.\n sx={[\n { display: 'inline' },\n color ? { color } : {},\n fontWeight ? { fontWeight } : {},\n DECORATION_SX[decoration] ?? {},\n ...nodeSx,\n ]}\n >\n {authorHtmlLabel(html) ?? children}\n </MuiBox>\n )\n})\nInlineText.displayName = 'AglynInlineText'\n\nexport const schema: Aglyn.ComponentSchema<InlineTextProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Inline Text',\n description:\n 'One run of text inside a sentence — put several side by side in a ' +\n 'paragraph to emphasize a phrase without breaking the line.',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorText.path, sx: { color: '#057822' } },\n flags: {\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n // The run IS its text: edited inline on the canvas, like Typography.\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557), for a different reason than the controls: this\n // run is not interactive, it is `display: inline`. Every element it may\n // render as is phrasing content — that is the guarantee INLINE_TEXT_ELEMENTS\n // exists to make — so a list inside one is a block in an inline box, which\n // the parser lifts straight back out of the enclosing paragraph. Links stay\n // with Screen Link, whose job is a resolved target rather than a typed href.\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this run renders as. `strong` and `em` carry ' +\n 'meaning for screen readers and search engines; `mark` marks a ' +\n 'highlighted phrase; `span` is purely visual.',\n component: Aglyn.FieldComponentType.SELECT,\n options: INLINE_TEXT_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'tone',\n label: 'Color',\n description:\n \"Palette token for this run. Two-tone text is a muted sentence \" +\n 'with one run set to Primary (or Accent) — theme tokens, so it ' +\n 'still reads correctly in dark mode.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'primary', label: 'Text primary' },\n { value: 'secondary', label: 'Text muted' },\n { value: 'disabled', label: 'Text disabled' },\n { value: 'accent', label: 'Accent' },\n { value: 'accentAlt', label: 'Accent alternate' },\n { value: 'success', label: 'Success' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' },\n ],\n },\n {\n name: 'weight',\n label: 'Weight',\n description: 'Font weight for this run only.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'regular', label: 'Regular' },\n { value: 'medium', label: 'Medium' },\n { value: 'semibold', label: 'Semi bold' },\n { value: 'bold', label: 'Bold' },\n ],\n },\n {\n name: 'decoration',\n label: 'Underline / strike',\n description:\n 'Inherit follows the surrounding text, including a link\\'s ' +\n 'underline — an underline PROPAGATES down and cannot be switched ' +\n 'off from inside. None does switch it off, at the cost of the run ' +\n 'no longer breaking across lines, so keep it to a short phrase.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'none', label: 'None (does not wrap)' },\n { value: 'underline', label: 'Underline' },\n { value: 'lineThrough', label: 'Strikethrough' },\n ],\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Inline Text',\n pluginId: BUNDLE_ID,\n description: 'One run of text inside a sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'span', children: 'Inline text' },\n },\n },\n {\n $id: generatePresetId(ID, 'emphasis'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Emphasized Text',\n pluginId: BUNDLE_ID,\n description: 'A phrase picked out of a muted sentence',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'the emphasized phrase',\n },\n },\n },\n {\n // The whole shape in one drop (AGL-1235): the reason the emphasis was\n // never authored is that assembling it by hand meant a container, three\n // runs and a colour on each. This is that assembly, ready to retype.\n $id: generatePresetId(ID, 'statement'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Two-tone Statement',\n pluginId: BUNDLE_ID,\n description: 'A muted sentence with one phrase emphasized',\n category: Aglyn.ComponentCategory.TEXT,\n icon: { path: mdiFormatColorHighlight.path, sx: { color: '#057822' } },\n data: {\n $id: null,\n // A real `<p>`, not a div: the runs are phrasing content, so the\n // paragraph is the honest container and screen readers get a\n // sentence rather than three unrelated fragments.\n componentId: 'muiBox',\n pluginId: BUNDLE_ID,\n props: { component: 'p' },\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { marginTop: 0, marginRight: 0, marginBottom: 0, marginLeft: 0 },\n nodes: [\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: 'Everything you build is ',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'strong',\n tone: 'primary',\n weight: 'bold',\n children: 'part of the platform',\n },\n },\n {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n element: 'span',\n tone: 'secondary',\n children: ', not bolted onto it.',\n },\n },\n ],\n },\n },\n]\n\nexport default InlineText\n"],"names":["Aglyn","mdiFormatColorHighlight","mdiFormatColorText","MuiBox","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","generatePresetId","ID","INLINE_TEXT_ELEMENTS","TONE_COLOR","inherit","undefined","primary","secondary","disabled","accent","accentAlt","success","warning","error","FONT_WEIGHT","regular","medium","semibold","bold","DECORATION_SX","none","display","textDecoration","underline","lineThrough","InlineText","props","ref","element","tone","weight","decoration","children","sx","html","rest","component","includes","nodeSx","Array","isArray","color","fontWeight","displayName","schema","$id","pluginId","description","category","ComponentCategory","TEXT","icon","path","flags","lazyHydration","FEATURE_FLAG","ENABLED","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","attributes","name","label","FieldComponentType","SELECT","options","map","value","presets","type","NodeType","PRESET","data","componentId","marginTop","marginRight","marginBottom","marginLeft","nodes"],"mappings":"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"}
|
|
@@ -136,9 +136,14 @@ export const schema = {
|
|
|
136
136
|
$id: ID,
|
|
137
137
|
pluginId: BUNDLE_ID,
|
|
138
138
|
displayName: 'Stack',
|
|
139
|
-
// Static: may sit in a subtree that keeps its server HTML (AGL-3581).
|
|
140
139
|
flags: {
|
|
141
|
-
|
|
140
|
+
// Static: may sit in a subtree that keeps its server HTML (AGL-3581).
|
|
141
|
+
lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,
|
|
142
|
+
// MUI draws the divider between `Children.toArray(children)`, so the
|
|
143
|
+
// renderer has to hand it one React child per node child (AGL-3660).
|
|
144
|
+
// Wrapped in the single `<Branch>`, a Stack of any size read as ONE
|
|
145
|
+
// child and no divider was ever drawn, on the canvas or a live page.
|
|
146
|
+
positionalChildren: Aglyn.FEATURE_FLAG.ENABLED
|
|
142
147
|
},
|
|
143
148
|
description: 'Lays children out in a row or column with even spacing — the workhorse for most layouts.',
|
|
144
149
|
category: Aglyn.ComponentCategory.LAYOUT,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/stack.ts"],"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 mdiViewColumn,\n mdiViewSequential,\n} from '@aglyn/shared-data-mdi'\nimport MuiDivider from '@mui/material/Divider'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { createElement, forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { colorSchemeAttribute } from '../utils/color-scheme-attribute'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * The dividers the `divider` attribute can name (AGL-2486).\n *\n * MUI's own `divider` prop takes a NODE, which an attributes panel cannot\n * author — so the persisted value is a style name and this file builds the\n * element. Anything else (an author's stray string, a paste) renders no\n * divider rather than the literal text between every pair of children.\n */\nconst DIVIDER_STYLES = {\n line: {},\n dashed: { borderStyle: 'dashed' },\n} as const\n\nexport type StackDivider = keyof typeof DIVIDER_STYLES\n\n// justifyContent/alignItems/flexWrap are not direct StackProps in MUI v6+;\n// pass them through sx.\ntype StackWithFlexProps = Omit<StackProps, 'divider'> & {\n justifyContent?: CSSProperties['justifyContent']\n alignItems?: CSSProperties['alignItems']\n flexWrap?: CSSProperties['flexWrap']\n /** Divider STYLE name, not a node — see {@link DIVIDER_STYLES}. */\n divider?: StackDivider | string\n /**\n * A repeat's directives (AGL-103, AGL-181). The published page's\n * composition consumes them before anything renders; a surface that renders\n * the stored node as written, like a component editor, still hands them over.\n */\n repeatDataset?: string\n repeatLimit?: number | string\n repeatFilter?: string\n repeatSort?: string\n}\n\n/**\n * A Stack's divider runs ACROSS the flow, so its orientation is the\n * opposite of the stack's own axis. A responsive `direction` (an array or\n * object) is not authorable here, so only the string form is inspected;\n * anything else falls back to MUI's default horizontal rule.\n */\nfunction buildDivider(\n divider: unknown,\n direction: unknown,\n): ReactElement | undefined {\n if (typeof divider !== 'string') return undefined\n // `hasOwnProperty`, not a truthiness test on the lookup: `\"constructor\"`\n // and friends resolve through Object.prototype and would sail past one.\n if (!Object.prototype.hasOwnProperty.call(DIVIDER_STYLES, divider)) {\n return undefined\n }\n const style = DIVIDER_STYLES[divider as StackDivider]\n const horizontal =\n typeof direction === 'string' && direction.startsWith('row')\n return createElement(MuiDivider, {\n orientation: horizontal ? 'vertical' : 'horizontal',\n // Without it a vertical divider collapses to zero height in a flex row.\n flexItem: horizontal,\n sx: style,\n })\n}\n\n/**\n * Displays that make a Stack the flex container MUI's `spacing` assumes.\n */\nconst FLEX_DISPLAYS = new Set(['flex', 'inline-flex'])\n\n/**\n * Whether an author's `sx` takes the Stack out of flex at any breakpoint\n * (AGL-3214).\n *\n * A function `sx` cannot be read without a theme, so it counts as flex and\n * the stack keeps MUI's own behaviour.\n */\nfunction hasNonFlexDisplay(sx: unknown): boolean {\n if (Array.isArray(sx)) return sx.some(hasNonFlexDisplay)\n if (!sx || typeof sx !== 'object') return false\n const display = (sx as Record<string, unknown>).display\n const notFlex = (value: unknown) =>\n typeof value === 'string' && !FLEX_DISPLAYS.has(value)\n if (typeof display === 'string') return notFlex(display)\n // Responsive, in either of the forms the styles panel writes: an array of\n // breakpoint values, or a `{ xs, md }` map.\n if (Array.isArray(display)) return display.some(notFlex)\n if (display && typeof display === 'object')\n return Object.values(display).some(notFlex)\n return false\n}\n\nconst Stack = forwardRef<HTMLDivElement, StackWithFlexProps>(\n // repeatDataset/repeatLimit are compose-time attributes (AGL-103): the\n // tenant expands them before render; strip so they never hit the DOM.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ({ justifyContent, alignItems, flexWrap, divider, useFlexGap, repeatDataset, repeatLimit, repeatFilter, repeatSort, sx, ...props }, ref) =>\n createElement(MuiStack, {\n ref,\n sx: [\n { justifyContent, alignItems, flexWrap },\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ],\n // The stored value is a style NAME; MUI wants a node (AGL-2486).\n divider: buildDivider(divider, props.direction),\n // A cleared `direction` persists as null, and MUI resolves it as a\n // responsive value — `null.xs` throws during SSR and 500s the page.\n // Same class as the AGL-1226 button colour, different sharp edge.\n // Last, so an author's element wins over anything `props` carried\n // (AGL-2525). A stack IS the nav row on most sites; before this it\n // could only be wrapped in one.\n ...applySemanticElement(dropClearedProps(props) as Record<string, unknown>),\n // A spacing stored as text — every AI-built page stores its props as\n // text — is still a step on the spacing scale: MUI reads \"2\" as a CSS\n // length and spaces nothing (AGL-3660).\n ...(typeof props.spacing === 'string' && props.spacing.trim() && Number.isFinite(Number(props.spacing))\n ? { spacing: Number(props.spacing) }\n : {}),\n // After the spread, so the element picker cannot carry a `useFlexGap`\n // past it. `spacing` reaches children as `margin-left` unless this is\n // on, and those margins assume flex: on a grid they land INSIDE the\n // track, so every item but the first sits a spacing-step right of its\n // column and the left gutter goes ragged (AGL-3214). `gap` is correct\n // for grid, multi-column and flex alike, and an author's own `sx` gap\n // still wins over it. A cleared switch persists as null, which is why\n // this reads `??` and not `||` — an author's explicit OFF stands.\n useFlexGap: useFlexGap ?? (hasNonFlexDisplay(sx) || undefined),\n }),\n)\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiStack'\n\nexport const schema: Aglyn.ComponentSchema = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Stack',\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n flags: { lazyHydration: Aglyn.FEATURE_FLAG.ENABLED },\n description:\n 'Lays children out in a row or column with even spacing — the workhorse for most layouts.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: {\n path: mdiViewColumn.path,\n sx: { color: '#2196f3' },\n },\n attributes: [\n semanticElementAttribute('this stack'),\n semanticElementLabelAttribute(),\n {\n name: 'direction',\n label: 'Direction',\n description:\n 'Defines the directional flow using the `flex-direction` style property. It is applied for all screen sizes.',\n component: Aglyn.FieldComponentType.SELECT,\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `column`, MUI Stack's own default, already on the list.\n options: [\n { value: 'column', label: 'Column' },\n { value: 'column-reverse', label: 'Column Reversed' },\n { value: 'row', label: 'Row' },\n { value: 'row-reverse', label: 'Row Reversed' },\n ],\n },\n {\n name: 'justifyContent',\n label: 'Justify Content',\n description:\n 'Defines how the browser distributes space between and around content items along the main-axis of the container.',\n component: Aglyn.FieldComponentType.SELECT,\n // \"Default\" deleted (AGL-1453). Unpersistable, and a second name for\n // `flex-start`: CSS's initial `justify-content` is `normal`, which on a\n // flex container — which a Stack always is — behaves as `flex-start`.\n //\n // Deleted rather than sentinelled for a second reason specific to this\n // prop: `justifyContent` is destructured out ABOVE and pushed into\n // `sx`, so it never passes through `dropClearedProps`. It is the one\n // attribute on this component the guard does not cover, so the right\n // move is to stop minting values it would have to catch.\n options: [\n { value: 'flex-start', label: 'Flex Start' },\n { value: 'center', label: 'Center' },\n { value: 'flex-end', label: 'Flex End' },\n { value: 'space-between', label: 'Space Between' },\n { value: 'space-around', label: 'Space Around' },\n { value: 'space-evenly', label: 'Space Evenly' },\n ],\n },\n {\n name: 'alignItems',\n label: 'Align Items',\n description:\n 'Defines how the browser distributes space between and around ' +\n 'content items along the cross-axis of the container — the axis ' +\n 'across the Direction.',\n component: Aglyn.FieldComponentType.SELECT,\n // No \"Default\" sentinel (AGL-1453): `stretch` is CSS's own initial\n // `align-items` and is already on the list, and like `justifyContent`\n // this prop is pushed into `sx` rather than through\n // `dropClearedProps`, so an unpersistable `''` would not be caught.\n options: [\n { value: 'stretch', label: 'Stretch' },\n { value: 'flex-start', label: 'Flex Start' },\n { value: 'center', label: 'Center' },\n { value: 'flex-end', label: 'Flex End' },\n { value: 'baseline', label: 'Baseline' },\n ],\n },\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between its immediate children.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'useFlexGap',\n label: 'Use flex gap',\n description:\n 'Space children with the CSS `gap` property instead of margins. ' +\n 'Required for Spacing to survive wrapping, and it leaves child ' +\n 'margins alone — but `gap` is unsupported in some older browsers.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'flexWrap',\n label: 'Wrap',\n description:\n 'Whether children that do not fit move onto a new line. Turn on ' +\n 'Use flex gap as well, or Spacing is applied as margins and the ' +\n 'wrapped rows get an uneven gap.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'nowrap', label: 'No Wrap' },\n { value: 'wrap', label: 'Wrap' },\n { value: 'wrap-reverse', label: 'Wrap Reversed' },\n ],\n },\n {\n name: 'divider',\n label: 'Divider',\n description:\n 'Draws a rule between each pair of children, across the ' +\n 'Direction. Clear the field for no divider.',\n component: Aglyn.FieldComponentType.SELECT,\n // MUI's own prop takes a node; the persisted value is a style name and\n // the component builds the element. No `''` option (AGL-1453) — \"no\n // divider\" is the field's ✕, which persists as cleared.\n options: [\n { value: 'line', label: 'Line' },\n { value: 'dashed', label: 'Dashed' },\n ],\n },\n // No Repeat fields: every element's Attributes panel draws the same Repeat\n // section (AGL-3111), writing the props a Stack has always stored.\n colorSchemeAttribute(),\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Stack Horizontal',\n icon: {\n path: mdiViewColumn.path,\n sx: { color: '#2196f3' },\n },\n category: Aglyn.ComponentCategory.LAYOUT,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { direction: 'row' },\n },\n },\n {\n $id: generatePresetId(ID, 'vertical'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Stack Vertical',\n icon: {\n path: mdiViewSequential.path,\n sx: { color: '#2196f3' },\n },\n category: Aglyn.ComponentCategory.LAYOUT,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n },\n },\n]\n\nexport default Stack\n"],"names":["Aglyn","mdiViewColumn","mdiViewSequential","MuiDivider","MuiStack","createElement","forwardRef","BUNDLE_ID","colorSchemeAttribute","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","dropClearedProps","generatePresetId","DIVIDER_STYLES","line","dashed","borderStyle","buildDivider","divider","direction","undefined","Object","prototype","hasOwnProperty","call","style","horizontal","startsWith","orientation","flexItem","sx","FLEX_DISPLAYS","Set","hasNonFlexDisplay","Array","isArray","some","display","notFlex","value","has","values","Stack","justifyContent","alignItems","flexWrap","useFlexGap","repeatDataset","repeatLimit","repeatFilter","repeatSort","props","ref","spacing","trim","Number","isFinite","ID","schema","$id","pluginId","displayName","flags","lazyHydration","FEATURE_FLAG","ENABLED","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","SELECT","options","TEXT_FIELD","type","SWITCH","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/stack.ts"],"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 mdiViewColumn,\n mdiViewSequential,\n} from '@aglyn/shared-data-mdi'\nimport MuiDivider from '@mui/material/Divider'\nimport MuiStack, { type StackProps } from '@mui/material/Stack'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { createElement, forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { colorSchemeAttribute } from '../utils/color-scheme-attribute'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * The dividers the `divider` attribute can name (AGL-2486).\n *\n * MUI's own `divider` prop takes a NODE, which an attributes panel cannot\n * author — so the persisted value is a style name and this file builds the\n * element. Anything else (an author's stray string, a paste) renders no\n * divider rather than the literal text between every pair of children.\n */\nconst DIVIDER_STYLES = {\n line: {},\n dashed: { borderStyle: 'dashed' },\n} as const\n\nexport type StackDivider = keyof typeof DIVIDER_STYLES\n\n// justifyContent/alignItems/flexWrap are not direct StackProps in MUI v6+;\n// pass them through sx.\ntype StackWithFlexProps = Omit<StackProps, 'divider'> & {\n justifyContent?: CSSProperties['justifyContent']\n alignItems?: CSSProperties['alignItems']\n flexWrap?: CSSProperties['flexWrap']\n /** Divider STYLE name, not a node — see {@link DIVIDER_STYLES}. */\n divider?: StackDivider | string\n /**\n * A repeat's directives (AGL-103, AGL-181). The published page's\n * composition consumes them before anything renders; a surface that renders\n * the stored node as written, like a component editor, still hands them over.\n */\n repeatDataset?: string\n repeatLimit?: number | string\n repeatFilter?: string\n repeatSort?: string\n}\n\n/**\n * A Stack's divider runs ACROSS the flow, so its orientation is the\n * opposite of the stack's own axis. A responsive `direction` (an array or\n * object) is not authorable here, so only the string form is inspected;\n * anything else falls back to MUI's default horizontal rule.\n */\nfunction buildDivider(\n divider: unknown,\n direction: unknown,\n): ReactElement | undefined {\n if (typeof divider !== 'string') return undefined\n // `hasOwnProperty`, not a truthiness test on the lookup: `\"constructor\"`\n // and friends resolve through Object.prototype and would sail past one.\n if (!Object.prototype.hasOwnProperty.call(DIVIDER_STYLES, divider)) {\n return undefined\n }\n const style = DIVIDER_STYLES[divider as StackDivider]\n const horizontal =\n typeof direction === 'string' && direction.startsWith('row')\n return createElement(MuiDivider, {\n orientation: horizontal ? 'vertical' : 'horizontal',\n // Without it a vertical divider collapses to zero height in a flex row.\n flexItem: horizontal,\n sx: style,\n })\n}\n\n/**\n * Displays that make a Stack the flex container MUI's `spacing` assumes.\n */\nconst FLEX_DISPLAYS = new Set(['flex', 'inline-flex'])\n\n/**\n * Whether an author's `sx` takes the Stack out of flex at any breakpoint\n * (AGL-3214).\n *\n * A function `sx` cannot be read without a theme, so it counts as flex and\n * the stack keeps MUI's own behaviour.\n */\nfunction hasNonFlexDisplay(sx: unknown): boolean {\n if (Array.isArray(sx)) return sx.some(hasNonFlexDisplay)\n if (!sx || typeof sx !== 'object') return false\n const display = (sx as Record<string, unknown>).display\n const notFlex = (value: unknown) =>\n typeof value === 'string' && !FLEX_DISPLAYS.has(value)\n if (typeof display === 'string') return notFlex(display)\n // Responsive, in either of the forms the styles panel writes: an array of\n // breakpoint values, or a `{ xs, md }` map.\n if (Array.isArray(display)) return display.some(notFlex)\n if (display && typeof display === 'object')\n return Object.values(display).some(notFlex)\n return false\n}\n\nconst Stack = forwardRef<HTMLDivElement, StackWithFlexProps>(\n // repeatDataset/repeatLimit are compose-time attributes (AGL-103): the\n // tenant expands them before render; strip so they never hit the DOM.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ({ justifyContent, alignItems, flexWrap, divider, useFlexGap, repeatDataset, repeatLimit, repeatFilter, repeatSort, sx, ...props }, ref) =>\n createElement(MuiStack, {\n ref,\n sx: [\n { justifyContent, alignItems, flexWrap },\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ],\n // The stored value is a style NAME; MUI wants a node (AGL-2486).\n divider: buildDivider(divider, props.direction),\n // A cleared `direction` persists as null, and MUI resolves it as a\n // responsive value — `null.xs` throws during SSR and 500s the page.\n // Same class as the AGL-1226 button colour, different sharp edge.\n // Last, so an author's element wins over anything `props` carried\n // (AGL-2525). A stack IS the nav row on most sites; before this it\n // could only be wrapped in one.\n ...applySemanticElement(dropClearedProps(props) as Record<string, unknown>),\n // A spacing stored as text — every AI-built page stores its props as\n // text — is still a step on the spacing scale: MUI reads \"2\" as a CSS\n // length and spaces nothing (AGL-3660).\n ...(typeof props.spacing === 'string' && props.spacing.trim() && Number.isFinite(Number(props.spacing))\n ? { spacing: Number(props.spacing) }\n : {}),\n // After the spread, so the element picker cannot carry a `useFlexGap`\n // past it. `spacing` reaches children as `margin-left` unless this is\n // on, and those margins assume flex: on a grid they land INSIDE the\n // track, so every item but the first sits a spacing-step right of its\n // column and the left gutter goes ragged (AGL-3214). `gap` is correct\n // for grid, multi-column and flex alike, and an author's own `sx` gap\n // still wins over it. A cleared switch persists as null, which is why\n // this reads `??` and not `||` — an author's explicit OFF stands.\n useFlexGap: useFlexGap ?? (hasNonFlexDisplay(sx) || undefined),\n }),\n)\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiStack'\n\nexport const schema: Aglyn.ComponentSchema = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Stack',\n flags: {\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n // MUI draws the divider between `Children.toArray(children)`, so the\n // renderer has to hand it one React child per node child (AGL-3660).\n // Wrapped in the single `<Branch>`, a Stack of any size read as ONE\n // child and no divider was ever drawn, on the canvas or a live page.\n positionalChildren: Aglyn.FEATURE_FLAG.ENABLED,\n },\n description:\n 'Lays children out in a row or column with even spacing — the workhorse for most layouts.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: {\n path: mdiViewColumn.path,\n sx: { color: '#2196f3' },\n },\n attributes: [\n semanticElementAttribute('this stack'),\n semanticElementLabelAttribute(),\n {\n name: 'direction',\n label: 'Direction',\n description:\n 'Defines the directional flow using the `flex-direction` style property. It is applied for all screen sizes.',\n component: Aglyn.FieldComponentType.SELECT,\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `column`, MUI Stack's own default, already on the list.\n options: [\n { value: 'column', label: 'Column' },\n { value: 'column-reverse', label: 'Column Reversed' },\n { value: 'row', label: 'Row' },\n { value: 'row-reverse', label: 'Row Reversed' },\n ],\n },\n {\n name: 'justifyContent',\n label: 'Justify Content',\n description:\n 'Defines how the browser distributes space between and around content items along the main-axis of the container.',\n component: Aglyn.FieldComponentType.SELECT,\n // \"Default\" deleted (AGL-1453). Unpersistable, and a second name for\n // `flex-start`: CSS's initial `justify-content` is `normal`, which on a\n // flex container — which a Stack always is — behaves as `flex-start`.\n //\n // Deleted rather than sentinelled for a second reason specific to this\n // prop: `justifyContent` is destructured out ABOVE and pushed into\n // `sx`, so it never passes through `dropClearedProps`. It is the one\n // attribute on this component the guard does not cover, so the right\n // move is to stop minting values it would have to catch.\n options: [\n { value: 'flex-start', label: 'Flex Start' },\n { value: 'center', label: 'Center' },\n { value: 'flex-end', label: 'Flex End' },\n { value: 'space-between', label: 'Space Between' },\n { value: 'space-around', label: 'Space Around' },\n { value: 'space-evenly', label: 'Space Evenly' },\n ],\n },\n {\n name: 'alignItems',\n label: 'Align Items',\n description:\n 'Defines how the browser distributes space between and around ' +\n 'content items along the cross-axis of the container — the axis ' +\n 'across the Direction.',\n component: Aglyn.FieldComponentType.SELECT,\n // No \"Default\" sentinel (AGL-1453): `stretch` is CSS's own initial\n // `align-items` and is already on the list, and like `justifyContent`\n // this prop is pushed into `sx` rather than through\n // `dropClearedProps`, so an unpersistable `''` would not be caught.\n options: [\n { value: 'stretch', label: 'Stretch' },\n { value: 'flex-start', label: 'Flex Start' },\n { value: 'center', label: 'Center' },\n { value: 'flex-end', label: 'Flex End' },\n { value: 'baseline', label: 'Baseline' },\n ],\n },\n {\n name: 'spacing',\n label: 'Spacing',\n description: 'Defines the space/gap between its immediate children.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'useFlexGap',\n label: 'Use flex gap',\n description:\n 'Space children with the CSS `gap` property instead of margins. ' +\n 'Required for Spacing to survive wrapping, and it leaves child ' +\n 'margins alone — but `gap` is unsupported in some older browsers.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'flexWrap',\n label: 'Wrap',\n description:\n 'Whether children that do not fit move onto a new line. Turn on ' +\n 'Use flex gap as well, or Spacing is applied as margins and the ' +\n 'wrapped rows get an uneven gap.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'nowrap', label: 'No Wrap' },\n { value: 'wrap', label: 'Wrap' },\n { value: 'wrap-reverse', label: 'Wrap Reversed' },\n ],\n },\n {\n name: 'divider',\n label: 'Divider',\n description:\n 'Draws a rule between each pair of children, across the ' +\n 'Direction. Clear the field for no divider.',\n component: Aglyn.FieldComponentType.SELECT,\n // MUI's own prop takes a node; the persisted value is a style name and\n // the component builds the element. No `''` option (AGL-1453) — \"no\n // divider\" is the field's ✕, which persists as cleared.\n options: [\n { value: 'line', label: 'Line' },\n { value: 'dashed', label: 'Dashed' },\n ],\n },\n // No Repeat fields: every element's Attributes panel draws the same Repeat\n // section (AGL-3111), writing the props a Stack has always stored.\n colorSchemeAttribute(),\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Stack Horizontal',\n icon: {\n path: mdiViewColumn.path,\n sx: { color: '#2196f3' },\n },\n category: Aglyn.ComponentCategory.LAYOUT,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { direction: 'row' },\n },\n },\n {\n $id: generatePresetId(ID, 'vertical'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Stack Vertical',\n icon: {\n path: mdiViewSequential.path,\n sx: { color: '#2196f3' },\n },\n category: Aglyn.ComponentCategory.LAYOUT,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n },\n },\n]\n\nexport default Stack\n"],"names":["Aglyn","mdiViewColumn","mdiViewSequential","MuiDivider","MuiStack","createElement","forwardRef","BUNDLE_ID","colorSchemeAttribute","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","dropClearedProps","generatePresetId","DIVIDER_STYLES","line","dashed","borderStyle","buildDivider","divider","direction","undefined","Object","prototype","hasOwnProperty","call","style","horizontal","startsWith","orientation","flexItem","sx","FLEX_DISPLAYS","Set","hasNonFlexDisplay","Array","isArray","some","display","notFlex","value","has","values","Stack","justifyContent","alignItems","flexWrap","useFlexGap","repeatDataset","repeatLimit","repeatFilter","repeatSort","props","ref","spacing","trim","Number","isFinite","ID","schema","$id","pluginId","displayName","flags","lazyHydration","FEATURE_FLAG","ENABLED","positionalChildren","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","SELECT","options","TEXT_FIELD","type","SWITCH","presets","NodeType","PRESET","data","componentId"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
2
|
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
4
|
/**
|
|
5
5
|
* @license
|
|
6
6
|
* Copyright 2026 Aglyn LLC
|
|
@@ -18,8 +18,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
18
18
|
* limitations under the License.
|
|
19
19
|
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
20
|
import { mdiAlphabetical, mdiFormatHeader1, mdiFormatHeader2, mdiFormatHeader3, mdiFormatHeader4, mdiFormatHeader5, mdiFormatHeader6, mdiFormatText } from "@aglyn/shared-data-mdi";
|
|
21
|
+
import { AglynText } from "@aglyn/shared-ui-jsx";
|
|
21
22
|
import Typography from "@mui/material/Typography";
|
|
22
|
-
import { forwardRef } from "react";
|
|
23
|
+
import { Children, createContext, forwardRef, isValidElement, useContext } from "react";
|
|
23
24
|
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
24
25
|
import { FIELD_TEXT_CONTENT } from "../constants/field-presets.js";
|
|
25
26
|
import { dropClearedProps } from "../utils/drop-cleared-props.js";
|
|
@@ -165,7 +166,14 @@ export const schema = {
|
|
|
165
166
|
// Static: may sit in a subtree that keeps its server HTML (AGL-3581).
|
|
166
167
|
lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,
|
|
167
168
|
textEditable: Aglyn.FEATURE_FLAG.ENABLED,
|
|
168
|
-
richTextEditable: Aglyn.FEATURE_FLAG.ENABLED
|
|
169
|
+
richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,
|
|
170
|
+
// Holds other elements after its own text (AGL-3672). The text keeps its
|
|
171
|
+
// own `<aglyn-text>`, which is all the in-place editor rewrites, so an
|
|
172
|
+
// edit never touches the elements beside it.
|
|
173
|
+
dropping: Aglyn.FEATURE_FLAG.ENABLED,
|
|
174
|
+
// One React child per child node, so the component can tell its
|
|
175
|
+
// elements from its text — see `AglynTypography`.
|
|
176
|
+
positionalChildren: Aglyn.FEATURE_FLAG.ENABLED
|
|
169
177
|
},
|
|
170
178
|
attributes: [
|
|
171
179
|
FIELD_TEXT_CONTENT,
|
|
@@ -390,6 +398,27 @@ export const presets = [
|
|
|
390
398
|
VARIANT_ELEMENT[(_rest_variant = rest.variant) != null ? _rest_variant : 'body1']) != null ? _ref : 'span';
|
|
391
399
|
return Aglyn.authorHtmlBreaksContainer(sanitized, element) ? 'div' : undefined;
|
|
392
400
|
}
|
|
401
|
+
/** The elements a Typography may not sit inside of another as. */ const BLOCK_TEXT_ELEMENTS = new Set([
|
|
402
|
+
'p',
|
|
403
|
+
'h1',
|
|
404
|
+
'h2',
|
|
405
|
+
'h3',
|
|
406
|
+
'h4',
|
|
407
|
+
'h5',
|
|
408
|
+
'h6'
|
|
409
|
+
]);
|
|
410
|
+
/**
|
|
411
|
+
* True inside the child elements of a Typography. A paragraph or heading
|
|
412
|
+
* nested in another is not one the parser keeps: `<p>` closes on any `<p>`
|
|
413
|
+
* start tag and a heading on any heading, so the inner element lands beside
|
|
414
|
+
* the outer one and React hydrates against a page it did not describe. A
|
|
415
|
+
* nested Typography renders as a `<span>` instead — it continues the
|
|
416
|
+
* sentence it was dropped into, which is what nesting text means.
|
|
417
|
+
*/ const TypographyNestingContext = /*#__PURE__*/ createContext(false);
|
|
418
|
+
/** A child NODE, as the renderer hands it over: an element carrying `node`. */ function isChildNode(child) {
|
|
419
|
+
var _child_props;
|
|
420
|
+
return /*#__PURE__*/ isValidElement(child) && ((_child_props = child.props) == null ? void 0 : _child_props.node) != null;
|
|
421
|
+
}
|
|
393
422
|
const AglynTypography = /*#__PURE__*/ forwardRef(function AglynTypography(props, ref) {
|
|
394
423
|
const { html, children } = props, spread = _object_without_properties_loose(props, [
|
|
395
424
|
"html",
|
|
@@ -398,6 +427,9 @@ const AglynTypography = /*#__PURE__*/ forwardRef(function AglynTypography(props,
|
|
|
398
427
|
// A cleared `align` persists as null and MUI capitalizes it — an SSR throw
|
|
399
428
|
// that 500s the page (AGL-1226, same shape as the button colour).
|
|
400
429
|
const rest = dropClearedProps(spread);
|
|
430
|
+
const nested = useContext(TypographyNestingContext);
|
|
431
|
+
const parts = Children.toArray(children);
|
|
432
|
+
const childNodes = parts.filter(isChildNode);
|
|
401
433
|
const hasHtml = typeof html === 'string' && Boolean(html);
|
|
402
434
|
// Sanitized DURING render, on the server and in the browser alike
|
|
403
435
|
// (AGL-1901). The effect this replaced existed because DOMPurify needs a
|
|
@@ -408,6 +440,16 @@ const AglynTypography = /*#__PURE__*/ forwardRef(function AglynTypography(props,
|
|
|
408
440
|
// DOM dependency, so both sides now compute the same bytes from the same
|
|
409
441
|
// prop and agree on the CONTENT instead of on the absence of it.
|
|
410
442
|
const sanitized = hasHtml ? sanitizeTypographyHtml(html) : null;
|
|
443
|
+
if (childNodes.length > 0 || nested) {
|
|
444
|
+
return /*#__PURE__*/ _jsx(HoldingTypography, {
|
|
445
|
+
ref: ref,
|
|
446
|
+
rest: rest,
|
|
447
|
+
sanitized: sanitized,
|
|
448
|
+
nested: nested,
|
|
449
|
+
text: parts.filter((part)=>!isChildNode(part)),
|
|
450
|
+
childNodes: childNodes
|
|
451
|
+
});
|
|
452
|
+
}
|
|
411
453
|
if (sanitized !== null) {
|
|
412
454
|
// A `<p>` cannot hold the block content a rich-text body may contain —
|
|
413
455
|
// see `blockSafeComponent`. `undefined` leaves MUI's own choice alone.
|
|
@@ -430,6 +472,40 @@ const AglynTypography = /*#__PURE__*/ forwardRef(function AglynTypography(props,
|
|
|
430
472
|
children: children
|
|
431
473
|
}));
|
|
432
474
|
});
|
|
475
|
+
/**
|
|
476
|
+
* A Typography that holds elements, or sits inside one that does
|
|
477
|
+
* (AGL-3672). Its own text comes first, then the elements dropped into it.
|
|
478
|
+
*
|
|
479
|
+
* The element changes only where the parser would otherwise rewrite the
|
|
480
|
+
* page: a holder that would be a `<p>` becomes a `<div>`, since the elements
|
|
481
|
+
* inside may be blocks and a block start tag closes a `<p>`; a nested one
|
|
482
|
+
* that would be a paragraph or heading becomes a `<span>`. A rich-text body
|
|
483
|
+
* goes in its own `<aglyn-text>` rather than over the whole element, so the
|
|
484
|
+
* children survive both the render and an in-place edit.
|
|
485
|
+
*/ const HoldingTypography = /*#__PURE__*/ forwardRef(function HoldingTypography(props, ref) {
|
|
486
|
+
var _ref, _rest_component, _rest_variant, _ref1;
|
|
487
|
+
const { rest, sanitized, nested, text, childNodes } = props;
|
|
488
|
+
const element = (_ref = (_rest_component = rest.component) != null ? _rest_component : VARIANT_ELEMENT[(_rest_variant = rest.variant) != null ? _rest_variant : 'body1']) != null ? _ref : 'span';
|
|
489
|
+
const component = (_ref1 = sanitized !== null ? blockSafeComponent(sanitized, rest) : undefined) != null ? _ref1 : nested && BLOCK_TEXT_ELEMENTS.has(element) ? 'span' : childNodes.length > 0 && element === 'p' ? 'div' : undefined;
|
|
490
|
+
return /*#__PURE__*/ _jsxs(Typography, _extends({
|
|
491
|
+
ref: ref,
|
|
492
|
+
variantMapping: VARIANT_ELEMENT
|
|
493
|
+
}, rest, component ? {
|
|
494
|
+
component
|
|
495
|
+
} : {}, {
|
|
496
|
+
children: [
|
|
497
|
+
sanitized !== null ? /*#__PURE__*/ _jsx(AglynText, {
|
|
498
|
+
dangerouslySetInnerHTML: {
|
|
499
|
+
__html: sanitized
|
|
500
|
+
}
|
|
501
|
+
}) : text,
|
|
502
|
+
childNodes.length > 0 && /*#__PURE__*/ _jsx(TypographyNestingContext.Provider, {
|
|
503
|
+
value: true,
|
|
504
|
+
children: childNodes
|
|
505
|
+
})
|
|
506
|
+
]
|
|
507
|
+
}));
|
|
508
|
+
});
|
|
433
509
|
export default AglynTypography;
|
|
434
510
|
|
|
435
511
|
//# sourceMappingURL=typography.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/typography.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 mdiAlphabetical,\n mdiFormatHeader1,\n mdiFormatHeader2,\n mdiFormatHeader3,\n mdiFormatHeader4,\n mdiFormatHeader5,\n mdiFormatHeader6,\n mdiFormatText,\n} from '@aglyn/shared-data-mdi'\nimport Typography, { type TypographyProps } from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiTypography'\n// The brand's own rungs sit alongside MUI's. They were added to the theme\n// (AGL-1308 for the body scale, for the display rung) but never to\n// this list, so the only way to reach one was to hand-write its pixels into\n// the Styles panel — which is the very thing the tokens exist to stop.\n// Display first: the list reads largest to smallest.\n//\n// Exported because it is the theme's rung list, not this element's: any block\n// that offers a type step has to offer the SAME one, or a rung added to the\n// theme becomes reachable from the Typography element and nowhere else.\nexport const typographyVariants = [\n {\n value: 'displayXl',\n label: 'Display XL',\n icon: { path: mdiFormatHeader1.path },\n },\n { value: 'h1', label: 'Heading 1', icon: { path: mdiFormatHeader1.path } },\n { value: 'h2', label: 'Heading 2', icon: { path: mdiFormatHeader2.path } },\n { value: 'h3', label: 'Heading 3', icon: { path: mdiFormatHeader3.path } },\n { value: 'h4', label: 'Heading 4', icon: { path: mdiFormatHeader4.path } },\n { value: 'h5', label: 'Heading 5', icon: { path: mdiFormatHeader5.path } },\n { value: 'h6', label: 'Heading 6', icon: { path: mdiFormatHeader6.path } },\n {\n value: 'subtitle1',\n label: 'Subtitle 1',\n icon: { path: mdiFormatText.path },\n },\n {\n value: 'subtitle2',\n label: 'Subtitle 2',\n icon: { path: mdiFormatText.path },\n },\n { value: 'body1', label: 'Body 1', icon: { path: mdiFormatText.path } },\n { value: 'body2', label: 'Body 2', icon: { path: mdiFormatText.path } },\n { value: 'lede', label: 'Lede', icon: { path: mdiFormatText.path } },\n { value: 'overline', label: 'Overline', icon: { path: mdiFormatText.path } },\n { value: 'caption', label: 'Caption', icon: { path: mdiFormatText.path } },\n {\n value: 'bodyCompact',\n label: 'Body compact',\n icon: { path: mdiFormatText.path },\n },\n { value: 'micro', label: 'Micro', icon: { path: mdiFormatText.path } },\n]\n\nexport const schema: Aglyn.ComponentSchema = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Typography',\n description: \"A block of text in one of the theme's type styles.\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAlphabetical.path,\n sx: { color: '#057822' },\n },\n flags: {\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n attributes: [\n FIELD_TEXT_CONTENT,\n // FIELD_COLOR,\n {\n name: 'variant',\n description:\n 'Which typography step from the site theme this text uses — h1–h6, ' +\n 'body, subtitle, caption, overline. It sets size, weight and line ' +\n 'height together; it does NOT change the HTML element, which is the ' +\n 'field below.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Variant',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `body1`, MUI's own default, already in `typographyVariants`.\n options: [...typographyVariants],\n },\n {\n name: 'component',\n description: 'The html element to use.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Component',\n // \"Default\" deleted rather than given a sentinel (AGL-1453), and this\n // is the prop that shows why a sentinel is not always available: MUI\n // derives the element from `variantMapping[variant]`, so \"Default\"\n // resolves to a DIFFERENT tag per variant — `h2` for a Heading 2, `p`\n // for Body 1. No single value on this list expresses that, and picking\n // one would pin the tag and change the document outline. Unset is the\n // only thing that means \"let the variant decide\", and the field's ✕ is\n // how an author says it.\n options: [\n { value: 'h1', label: 'Heading 1' },\n { value: 'h2', label: 'Heading 2' },\n { value: 'h3', label: 'Heading 3' },\n { value: 'h4', label: 'Heading 4' },\n { value: 'h5', label: 'Heading 5' },\n { value: 'h6', label: 'Heading 6' },\n { value: 'p', label: 'Paragraph' },\n { value: 'div', label: 'Div' },\n { value: 'span', label: 'Span' },\n ],\n },\n {\n name: 'align',\n description: 'The text alignment',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Alignment',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `inherit`, MUI Typography's own default, already on the list.\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n { value: 'justify', label: 'Justified' },\n ],\n },\n {\n name: 'noWrap',\n description: 'If true, the text will not wrap/fold.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Disable wrapping?',\n },\n {\n name: 'gutterBottom',\n description: 'If true, the text will have a space beneath.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Gutter bottom?',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n ...typographyVariants.map((item): Aglyn.PresetSchema => ({\n $id: generatePresetId(ID, item.value),\n type: Aglyn.NodeType.PRESET,\n displayName: item.label,\n pluginId: BUNDLE_ID,\n description: `Element with ${item.label} styles`,\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n sx: { color: '#057822' },\n ...item.icon,\n },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n variant: item.value,\n children: item.label,\n },\n },\n })),\n]\n\n/**\n * Typography that renders rich text (AGL-54): when the node carries an\n * `html` prop it renders as innerHTML; otherwise plain `children`.\n *\n * The `html` prop is re-sanitized on EVERY render (AGL-497). The inline\n * editor sanitizes at commit, but screen node props are written directly via\n * the Firebase client SDK, so a host editor can plant arbitrary `html`\n * (bypassing the editor) that would execute on the public site AND in the\n * besigner canvas on app.aglyn.com.\n *\n * The sanitizer is `Aglyn.sanitizeAuthorHtml` (AGL-1901), which needs no DOM\n * and so runs on the server too — the DOMPurify call this replaced could\n * not, which is why the content used to be deferred to an effect and left\n * every rich-text body out of the server response. The allowlist it enforces\n * is a subset of the old DOMPurify config's, pinned pair by pair in\n * `rich-text-hydration.spec.tsx`.\n */\nfunction sanitizeTypographyHtml(html: string): string {\n return Aglyn.sanitizeAuthorHtml(html)\n}\n\n/**\n * MUI's own `defaultVariantMapping`, plus its `span` fallback — the element\n * Typography will actually render for a given variant. Copied rather than\n * imported because MUI does not export it; `author-html-round-trip.spec.tsx`\n * asserts this table still matches what MUI actually renders, so a MUI upgrade that\n * changed the mapping fails there rather than silently reopening the\n * reparenting hazard below.\n */\n/**\n * The element each variant renders as, and the ONE place that decides\n *.\n *\n * MUI's own `variantMapping` sends `subtitle1` and `subtitle2` to `<h6>`.\n * That is a typographic choice made in a component library, and on a page\n * builder it is a document-outline decision made by somebody who has never\n * seen the page: a subtitle is a label, not a section, and every one on every\n * Aglyn site was silently inserting an `<h6>` into the heading order. It is\n * what Lighthouse's `heading-order` was reporting — a \"Open source &\n * self-hostable\" card label rendering ahead of the page's own `<h1>`, from a\n * node whose author had selected Subtitle 1 and nothing else.\n *\n * Subtitles render as `<p>` here. An author who wants a heading still says so\n * with the Component field, which is the control for exactly that question\n * and is unchanged.\n *\n * Passed to MUI as `variantMapping` rather than only consulted by\n * `blockSafeComponent`, so the table cannot describe one thing while the\n * element is another.\n */\nconst VARIANT_ELEMENT: Record<string, string> = {\n h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5', h6: 'h6',\n subtitle1: 'p', subtitle2: 'p', body1: 'p', body2: 'p', inherit: 'p',\n}\n\n/**\n * The element the rich-text body is put INSIDE, which may have to be a\n * `<div>` rather than the one the variant implies (AGL-1901).\n *\n * The parser closes a `<p>` on any block start tag, and a heading on any\n * other heading. So a body with an `<h2>` in it, served inside the `<p>` MUI\n * renders for `body1`, arrives at the browser as\n * `<p class=\"MuiTypography-root\"></p><h2>…</h2>` — the container emptied and\n * the body promoted to a sibling. React then hydrates against a DOM that is\n * not the one it described and reports a mismatch: the live React #418 shape\n * on tenant (AGL-1926), which this change must not add to.\n *\n * This never mattered before because the server `<p>` was EMPTY and the body\n * arrived through an `innerHTML` assignment, which parses in the CONTEXT of\n * the `<p>` and so does not reparent. Serving the body is what makes the\n * container's element type load-bearing.\n *\n * `authorHtmlBreaksContainer` holds the rule, next to the table of tags it\n * depends on. Returns `'div'` to override, or `undefined` to leave MUI's\n * choice alone; the decision is a pure function of props, so the server and\n * the client make it identically.\n */\nfunction blockSafeComponent(\n sanitized: string,\n rest: TypographyProps,\n): 'div' | undefined {\n const element =\n (rest.component as string | undefined) ??\n // MUI's own default variant is `body1`, which maps to `p` — the case that\n // matters, since a Typography with no variant set is the shape nearly\n // every rich-text node has. (`paragraph` is gone in MUI v7, so the\n // variant mapping is the only other input.)\n VARIANT_ELEMENT[(rest.variant as string) ?? 'body1'] ??\n 'span'\n return Aglyn.authorHtmlBreaksContainer(sanitized, element) ? 'div' : undefined\n}\n\nconst AglynTypography = forwardRef<\n HTMLElement,\n TypographyProps & { html?: string }\n>(function AglynTypography(props, ref) {\n const { html, children, ...spread } = props\n // A cleared `align` persists as null and MUI capitalizes it — an SSR throw\n // that 500s the page (AGL-1226, same shape as the button colour).\n const rest = dropClearedProps(spread)\n const hasHtml = typeof html === 'string' && Boolean(html)\n // Sanitized DURING render, on the server and in the browser alike\n // (AGL-1901). The effect this replaced existed because DOMPurify needs a\n // DOM: the server rendered nothing, so the first client render had to\n // render nothing too or hydration would mismatch (AGL-1268) — the two\n // sides agreed only by both being empty, and the body never reached a\n // crawler. `sanitizeAuthorHtml` is a pure function of the string with no\n // DOM dependency, so both sides now compute the same bytes from the same\n // prop and agree on the CONTENT instead of on the absence of it.\n const sanitized = hasHtml ? sanitizeTypographyHtml(html as string) : null\n if (sanitized !== null) {\n // A `<p>` cannot hold the block content a rich-text body may contain —\n // see `blockSafeComponent`. `undefined` leaves MUI's own choice alone.\n const component = blockSafeComponent(sanitized, rest as TypographyProps)\n return (\n <Typography\n ref={ref}\n variantMapping={VARIANT_ELEMENT}\n {...rest}\n {...(component ? { component } : {})}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n )\n }\n return (\n <Typography ref={ref} variantMapping={VARIANT_ELEMENT} {...rest}>\n {children}\n </Typography>\n )\n})\n\nexport default AglynTypography\n"],"names":["Aglyn","mdiAlphabetical","mdiFormatHeader1","mdiFormatHeader2","mdiFormatHeader3","mdiFormatHeader4","mdiFormatHeader5","mdiFormatHeader6","mdiFormatText","Typography","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","dropClearedProps","generatePresetId","ID","typographyVariants","value","label","icon","path","schema","$id","pluginId","displayName","description","category","ComponentCategory","TEXT","sx","color","flags","lazyHydration","FEATURE_FLAG","ENABLED","textEditable","richTextEditable","attributes","name","component","FieldComponentType","SELECT","options","SWITCH","presets","map","item","type","NodeType","PRESET","data","componentId","props","variant","children","sanitizeTypographyHtml","html","sanitizeAuthorHtml","VARIANT_ELEMENT","h1","h2","h3","h4","h5","h6","subtitle1","subtitle2","body1","body2","inherit","blockSafeComponent","sanitized","rest","element","authorHtmlBreaksContainer","undefined","AglynTypography","ref","spread","hasHtml","Boolean","variantMapping","dangerouslySetInnerHTML","__html"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/typography.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 mdiAlphabetical,\n mdiFormatHeader1,\n mdiFormatHeader2,\n mdiFormatHeader3,\n mdiFormatHeader4,\n mdiFormatHeader5,\n mdiFormatHeader6,\n mdiFormatText,\n} from '@aglyn/shared-data-mdi'\nimport { AglynText } from '@aglyn/shared-ui-jsx'\nimport Typography, { type TypographyProps } from '@mui/material/Typography'\nimport {\n Children,\n createContext,\n forwardRef,\n isValidElement,\n useContext,\n type ReactNode,\n} from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiTypography'\n// The brand's own rungs sit alongside MUI's. They were added to the theme\n// (AGL-1308 for the body scale, for the display rung) but never to\n// this list, so the only way to reach one was to hand-write its pixels into\n// the Styles panel — which is the very thing the tokens exist to stop.\n// Display first: the list reads largest to smallest.\n//\n// Exported because it is the theme's rung list, not this element's: any block\n// that offers a type step has to offer the SAME one, or a rung added to the\n// theme becomes reachable from the Typography element and nowhere else.\nexport const typographyVariants = [\n {\n value: 'displayXl',\n label: 'Display XL',\n icon: { path: mdiFormatHeader1.path },\n },\n { value: 'h1', label: 'Heading 1', icon: { path: mdiFormatHeader1.path } },\n { value: 'h2', label: 'Heading 2', icon: { path: mdiFormatHeader2.path } },\n { value: 'h3', label: 'Heading 3', icon: { path: mdiFormatHeader3.path } },\n { value: 'h4', label: 'Heading 4', icon: { path: mdiFormatHeader4.path } },\n { value: 'h5', label: 'Heading 5', icon: { path: mdiFormatHeader5.path } },\n { value: 'h6', label: 'Heading 6', icon: { path: mdiFormatHeader6.path } },\n {\n value: 'subtitle1',\n label: 'Subtitle 1',\n icon: { path: mdiFormatText.path },\n },\n {\n value: 'subtitle2',\n label: 'Subtitle 2',\n icon: { path: mdiFormatText.path },\n },\n { value: 'body1', label: 'Body 1', icon: { path: mdiFormatText.path } },\n { value: 'body2', label: 'Body 2', icon: { path: mdiFormatText.path } },\n { value: 'lede', label: 'Lede', icon: { path: mdiFormatText.path } },\n { value: 'overline', label: 'Overline', icon: { path: mdiFormatText.path } },\n { value: 'caption', label: 'Caption', icon: { path: mdiFormatText.path } },\n {\n value: 'bodyCompact',\n label: 'Body compact',\n icon: { path: mdiFormatText.path },\n },\n { value: 'micro', label: 'Micro', icon: { path: mdiFormatText.path } },\n]\n\nexport const schema: Aglyn.ComponentSchema = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Typography',\n description: \"A block of text in one of the theme's type styles.\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAlphabetical.path,\n sx: { color: '#057822' },\n },\n flags: {\n // Static: may sit in a subtree that keeps its server HTML (AGL-3581).\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n // Holds other elements after its own text (AGL-3672). The text keeps its\n // own `<aglyn-text>`, which is all the in-place editor rewrites, so an\n // edit never touches the elements beside it.\n dropping: Aglyn.FEATURE_FLAG.ENABLED,\n // One React child per child node, so the component can tell its\n // elements from its text — see `AglynTypography`.\n positionalChildren: Aglyn.FEATURE_FLAG.ENABLED,\n },\n attributes: [\n FIELD_TEXT_CONTENT,\n // FIELD_COLOR,\n {\n name: 'variant',\n description:\n 'Which typography step from the site theme this text uses — h1–h6, ' +\n 'body, subtitle, caption, overline. It sets size, weight and line ' +\n 'height together; it does NOT change the HTML element, which is the ' +\n 'field below.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Variant',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `body1`, MUI's own default, already in `typographyVariants`.\n options: [...typographyVariants],\n },\n {\n name: 'component',\n description: 'The html element to use.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Component',\n // \"Default\" deleted rather than given a sentinel (AGL-1453), and this\n // is the prop that shows why a sentinel is not always available: MUI\n // derives the element from `variantMapping[variant]`, so \"Default\"\n // resolves to a DIFFERENT tag per variant — `h2` for a Heading 2, `p`\n // for Body 1. No single value on this list expresses that, and picking\n // one would pin the tag and change the document outline. Unset is the\n // only thing that means \"let the variant decide\", and the field's ✕ is\n // how an author says it.\n options: [\n { value: 'h1', label: 'Heading 1' },\n { value: 'h2', label: 'Heading 2' },\n { value: 'h3', label: 'Heading 3' },\n { value: 'h4', label: 'Heading 4' },\n { value: 'h5', label: 'Heading 5' },\n { value: 'h6', label: 'Heading 6' },\n { value: 'p', label: 'Paragraph' },\n { value: 'div', label: 'Div' },\n { value: 'span', label: 'Span' },\n ],\n },\n {\n name: 'align',\n description: 'The text alignment',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Alignment',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `inherit`, MUI Typography's own default, already on the list.\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n { value: 'justify', label: 'Justified' },\n ],\n },\n {\n name: 'noWrap',\n description: 'If true, the text will not wrap/fold.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Disable wrapping?',\n },\n {\n name: 'gutterBottom',\n description: 'If true, the text will have a space beneath.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Gutter bottom?',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n ...typographyVariants.map((item): Aglyn.PresetSchema => ({\n $id: generatePresetId(ID, item.value),\n type: Aglyn.NodeType.PRESET,\n displayName: item.label,\n pluginId: BUNDLE_ID,\n description: `Element with ${item.label} styles`,\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n sx: { color: '#057822' },\n ...item.icon,\n },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n variant: item.value,\n children: item.label,\n },\n },\n })),\n]\n\n/**\n * Typography that renders rich text (AGL-54): when the node carries an\n * `html` prop it renders as innerHTML; otherwise plain `children`.\n *\n * The `html` prop is re-sanitized on EVERY render (AGL-497). The inline\n * editor sanitizes at commit, but screen node props are written directly via\n * the Firebase client SDK, so a host editor can plant arbitrary `html`\n * (bypassing the editor) that would execute on the public site AND in the\n * besigner canvas on app.aglyn.com.\n *\n * The sanitizer is `Aglyn.sanitizeAuthorHtml` (AGL-1901), which needs no DOM\n * and so runs on the server too — the DOMPurify call this replaced could\n * not, which is why the content used to be deferred to an effect and left\n * every rich-text body out of the server response. The allowlist it enforces\n * is a subset of the old DOMPurify config's, pinned pair by pair in\n * `rich-text-hydration.spec.tsx`.\n */\nfunction sanitizeTypographyHtml(html: string): string {\n return Aglyn.sanitizeAuthorHtml(html)\n}\n\n/**\n * MUI's own `defaultVariantMapping`, plus its `span` fallback — the element\n * Typography will actually render for a given variant. Copied rather than\n * imported because MUI does not export it; `author-html-round-trip.spec.tsx`\n * asserts this table still matches what MUI actually renders, so a MUI upgrade that\n * changed the mapping fails there rather than silently reopening the\n * reparenting hazard below.\n */\n/**\n * The element each variant renders as, and the ONE place that decides\n *.\n *\n * MUI's own `variantMapping` sends `subtitle1` and `subtitle2` to `<h6>`.\n * That is a typographic choice made in a component library, and on a page\n * builder it is a document-outline decision made by somebody who has never\n * seen the page: a subtitle is a label, not a section, and every one on every\n * Aglyn site was silently inserting an `<h6>` into the heading order. It is\n * what Lighthouse's `heading-order` was reporting — a \"Open source &\n * self-hostable\" card label rendering ahead of the page's own `<h1>`, from a\n * node whose author had selected Subtitle 1 and nothing else.\n *\n * Subtitles render as `<p>` here. An author who wants a heading still says so\n * with the Component field, which is the control for exactly that question\n * and is unchanged.\n *\n * Passed to MUI as `variantMapping` rather than only consulted by\n * `blockSafeComponent`, so the table cannot describe one thing while the\n * element is another.\n */\nconst VARIANT_ELEMENT: Record<string, string> = {\n h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5', h6: 'h6',\n subtitle1: 'p', subtitle2: 'p', body1: 'p', body2: 'p', inherit: 'p',\n}\n\n/**\n * The element the rich-text body is put INSIDE, which may have to be a\n * `<div>` rather than the one the variant implies (AGL-1901).\n *\n * The parser closes a `<p>` on any block start tag, and a heading on any\n * other heading. So a body with an `<h2>` in it, served inside the `<p>` MUI\n * renders for `body1`, arrives at the browser as\n * `<p class=\"MuiTypography-root\"></p><h2>…</h2>` — the container emptied and\n * the body promoted to a sibling. React then hydrates against a DOM that is\n * not the one it described and reports a mismatch: the live React #418 shape\n * on tenant (AGL-1926), which this change must not add to.\n *\n * This never mattered before because the server `<p>` was EMPTY and the body\n * arrived through an `innerHTML` assignment, which parses in the CONTEXT of\n * the `<p>` and so does not reparent. Serving the body is what makes the\n * container's element type load-bearing.\n *\n * `authorHtmlBreaksContainer` holds the rule, next to the table of tags it\n * depends on. Returns `'div'` to override, or `undefined` to leave MUI's\n * choice alone; the decision is a pure function of props, so the server and\n * the client make it identically.\n */\nfunction blockSafeComponent(\n sanitized: string,\n rest: TypographyProps,\n): 'div' | undefined {\n const element =\n (rest.component as string | undefined) ??\n // MUI's own default variant is `body1`, which maps to `p` — the case that\n // matters, since a Typography with no variant set is the shape nearly\n // every rich-text node has. (`paragraph` is gone in MUI v7, so the\n // variant mapping is the only other input.)\n VARIANT_ELEMENT[(rest.variant as string) ?? 'body1'] ??\n 'span'\n return Aglyn.authorHtmlBreaksContainer(sanitized, element) ? 'div' : undefined\n}\n\n/** The elements a Typography may not sit inside of another as. */\nconst BLOCK_TEXT_ELEMENTS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'])\n\n/**\n * True inside the child elements of a Typography. A paragraph or heading\n * nested in another is not one the parser keeps: `<p>` closes on any `<p>`\n * start tag and a heading on any heading, so the inner element lands beside\n * the outer one and React hydrates against a page it did not describe. A\n * nested Typography renders as a `<span>` instead — it continues the\n * sentence it was dropped into, which is what nesting text means.\n */\nconst TypographyNestingContext = createContext(false)\n\n/** A child NODE, as the renderer hands it over: an element carrying `node`. */\nfunction isChildNode(child: ReactNode): boolean {\n return isValidElement(child) && (child.props as any)?.node != null\n}\n\nconst AglynTypography = forwardRef<\n HTMLElement,\n TypographyProps & { html?: string }\n>(function AglynTypography(props, ref) {\n const { html, children, ...spread } = props\n // A cleared `align` persists as null and MUI capitalizes it — an SSR throw\n // that 500s the page (AGL-1226, same shape as the button colour).\n const rest = dropClearedProps(spread)\n const nested = useContext(TypographyNestingContext)\n const parts = Children.toArray(children)\n const childNodes = parts.filter(isChildNode)\n const hasHtml = typeof html === 'string' && Boolean(html)\n // Sanitized DURING render, on the server and in the browser alike\n // (AGL-1901). The effect this replaced existed because DOMPurify needs a\n // DOM: the server rendered nothing, so the first client render had to\n // render nothing too or hydration would mismatch (AGL-1268) — the two\n // sides agreed only by both being empty, and the body never reached a\n // crawler. `sanitizeAuthorHtml` is a pure function of the string with no\n // DOM dependency, so both sides now compute the same bytes from the same\n // prop and agree on the CONTENT instead of on the absence of it.\n const sanitized = hasHtml ? sanitizeTypographyHtml(html as string) : null\n if (childNodes.length > 0 || nested) {\n return (\n <HoldingTypography\n ref={ref}\n rest={rest as TypographyProps}\n sanitized={sanitized}\n nested={nested}\n text={parts.filter((part) => !isChildNode(part))}\n childNodes={childNodes}\n />\n )\n }\n if (sanitized !== null) {\n // A `<p>` cannot hold the block content a rich-text body may contain —\n // see `blockSafeComponent`. `undefined` leaves MUI's own choice alone.\n const component = blockSafeComponent(sanitized, rest as TypographyProps)\n return (\n <Typography\n ref={ref}\n variantMapping={VARIANT_ELEMENT}\n {...rest}\n {...(component ? { component } : {})}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n )\n }\n return (\n <Typography ref={ref} variantMapping={VARIANT_ELEMENT} {...rest}>\n {children}\n </Typography>\n )\n})\n\n/**\n * A Typography that holds elements, or sits inside one that does\n * (AGL-3672). Its own text comes first, then the elements dropped into it.\n *\n * The element changes only where the parser would otherwise rewrite the\n * page: a holder that would be a `<p>` becomes a `<div>`, since the elements\n * inside may be blocks and a block start tag closes a `<p>`; a nested one\n * that would be a paragraph or heading becomes a `<span>`. A rich-text body\n * goes in its own `<aglyn-text>` rather than over the whole element, so the\n * children survive both the render and an in-place edit.\n */\nconst HoldingTypography = forwardRef<\n HTMLElement,\n {\n rest: TypographyProps\n sanitized: string | null\n nested: boolean\n text: ReactNode[]\n childNodes: ReactNode[]\n }\n>(function HoldingTypography(props, ref) {\n const { rest, sanitized, nested, text, childNodes } = props\n const element =\n (rest.component as string | undefined) ??\n VARIANT_ELEMENT[(rest.variant as string) ?? 'body1'] ??\n 'span'\n const component =\n (sanitized !== null ? blockSafeComponent(sanitized, rest) : undefined) ??\n (nested && BLOCK_TEXT_ELEMENTS.has(element)\n ? 'span'\n : childNodes.length > 0 && element === 'p'\n ? 'div'\n : undefined)\n return (\n <Typography\n ref={ref}\n variantMapping={VARIANT_ELEMENT}\n {...rest}\n {...(component ? { component } : {})}\n >\n {sanitized !== null ? (\n <AglynText dangerouslySetInnerHTML={{ __html: sanitized }} />\n ) : (\n text\n )}\n {childNodes.length > 0 && (\n <TypographyNestingContext.Provider value={true}>\n {childNodes}\n </TypographyNestingContext.Provider>\n )}\n </Typography>\n )\n})\n\nexport default AglynTypography\n"],"names":["Aglyn","mdiAlphabetical","mdiFormatHeader1","mdiFormatHeader2","mdiFormatHeader3","mdiFormatHeader4","mdiFormatHeader5","mdiFormatHeader6","mdiFormatText","AglynText","Typography","Children","createContext","forwardRef","isValidElement","useContext","BUNDLE_ID","FIELD_TEXT_CONTENT","dropClearedProps","generatePresetId","ID","typographyVariants","value","label","icon","path","schema","$id","pluginId","displayName","description","category","ComponentCategory","TEXT","sx","color","flags","lazyHydration","FEATURE_FLAG","ENABLED","textEditable","richTextEditable","dropping","positionalChildren","attributes","name","component","FieldComponentType","SELECT","options","SWITCH","presets","map","item","type","NodeType","PRESET","data","componentId","props","variant","children","sanitizeTypographyHtml","html","sanitizeAuthorHtml","VARIANT_ELEMENT","h1","h2","h3","h4","h5","h6","subtitle1","subtitle2","body1","body2","inherit","blockSafeComponent","sanitized","rest","element","authorHtmlBreaksContainer","undefined","BLOCK_TEXT_ELEMENTS","Set","TypographyNestingContext","isChildNode","child","node","AglynTypography","ref","spread","nested","parts","toArray","childNodes","filter","hasHtml","Boolean","length","HoldingTypography","text","part","variantMapping","dangerouslySetInnerHTML","__html","has","Provider"],"mappings":"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"}
|
package/src/lib/plugin.js
CHANGED
|
@@ -109,9 +109,9 @@ const themeModeSwitcher = viaTier('themeModeSwitcher');
|
|
|
109
109
|
schema: 'schema',
|
|
110
110
|
presets: 'presets'
|
|
111
111
|
},
|
|
112
|
-
// The inline text run (AGL-1235):
|
|
113
|
-
//
|
|
114
|
-
//
|
|
112
|
+
// The inline text run (AGL-1235): one styled phrase inside a sentence.
|
|
113
|
+
// Typography also holds elements now (AGL-3672), but a run is still the
|
|
114
|
+
// element that flows and wraps as part of the line.
|
|
115
115
|
muiInlineText: {
|
|
116
116
|
module: inlineText,
|
|
117
117
|
component: 'default',
|
package/src/lib/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/mui/src/lib/plugin.ts"],"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 {\n registerPluginInstallPresetMapper,\n registerPluginInstallPresetsMapper,\n} from '@aglyn/aglyn'\nimport {\n muiPluginInstallToPreset,\n muiPluginInstallToPresets,\n} from './components/plugin-install-presets'\nimport * as Aglyn from '@aglyn/aglyn'\nimport { runInAction } from 'mobx'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * One thunk per component module, named here because several ids share one:\n * `card.tsx` and `accordion.tsx` each export several elements, and a module is\n * imported once however many of its components a surface asks for.\n *\n * Every thunk goes through the element tier (AGL-3401). The common elements\n * are part of it; the rest are `import()`s written inside it, so they can\n * rely on what it loaded instead of each carrying a copy. `element-tier.ts`\n * has the reasoning, and which elements are which.\n */\ntype ElementTier = typeof import('./element-tier')\ntype TierElement = keyof ElementTier['CORE_ELEMENTS']\ntype LazyElement = Exclude<keyof ElementTier, 'CORE_ELEMENTS'>\n\ntype ElementModule = Promise<Record<string, unknown>>\n\n/**\n * One load of the tier, however many thunks ask at once. The bundler shares\n * a module across concurrent imports anyway; a loader that hands out a\n * module while it is still evaluating does not — jiti, which the AI palette\n * and page-axe generators load this bundle through, resolved the second of\n * sixty concurrent imports to the tier's exports before `CORE_ELEMENTS` was\n * assigned.\n */\nlet tierLoad: Promise<ElementTier> | undefined\nconst elementTier = (): Promise<ElementTier> =>\n (tierLoad ??= import('./element-tier'))\nconst inTier =\n (name: TierElement): MuiComponentSource['module'] =>\n () =>\n elementTier().then(\n (tier): Record<string, unknown> => tier.CORE_ELEMENTS[name],\n )\nconst viaTier =\n (name: LazyElement): MuiComponentSource['module'] =>\n () =>\n elementTier().then((tier): ElementModule => tier[name]())\n\nconst appBar = inTier('appBar')\nconst toolbar = inTier('toolbar')\nconst layoutSlot = inTier('layoutSlot')\nconst documentRoot = inTier('documentRoot')\nconst section = inTier('section')\nconst container = inTier('container')\nconst box = inTier('box')\nconst stack = inTier('stack')\nconst grid = inTier('grid')\nconst paper = inTier('paper')\nconst typography = inTier('typography')\nconst inlineText = inTier('inlineText')\nconst button = inTier('button')\nconst screenLink = inTier('screenLink')\nconst linkBox = inTier('linkBox')\nconst image = inTier('image')\nconst icon = inTier('icon')\n\nconst list = viaTier('list')\nconst listItem = viaTier('listItem')\nconst listItemText = viaTier('listItemText')\nconst blocks = viaTier('blocks')\nconst dataTable = viaTier('dataTable')\nconst collectionEntries = viaTier('collectionEntries')\nconst collectionEntryBody = viaTier('collectionEntryBody')\nconst collectionRelated = viaTier('collectionRelated')\nconst collectionShare = viaTier('collectionShare')\nconst collectionEntryMeta = viaTier('collectionEntryMeta')\nconst collectionEntryAuthor = viaTier('collectionEntryAuthor')\nconst collectionAuthorProfile = viaTier('collectionAuthorProfile')\nconst collectionCategories = viaTier('collectionCategories')\nconst collectionSearch = viaTier('collectionSearch')\nconst video = viaTier('video')\nconst languageSwitcher = viaTier('languageSwitcher')\nconst reusableInstance = viaTier('reusableInstance')\nconst navMenu = viaTier('navMenu')\nconst drawer = viaTier('drawer')\nconst product = viaTier('product')\nconst plugin = viaTier('plugin')\nconst customHtml = viaTier('customHtml')\nconst searchBox = viaTier('searchBox')\nconst markdown = viaTier('markdown')\nconst card = viaTier('card')\nconst accordion = viaTier('accordion')\nconst tabs = viaTier('tabs')\nconst imageList = viaTier('imageList')\nconst pagination = viaTier('pagination')\nconst breadcrumbs = viaTier('breadcrumbs')\nconst themeModeSwitcher = viaTier('themeModeSwitcher')\n\n/** One entry of the bundle: what the registry is handed for a component. */\nexport interface MuiBundleEntry {\n component: any\n schema: Aglyn.ComponentSchema<any>\n presets?: Aglyn.PresetSchema[]\n}\n\n/**\n * Where a component id's code lives, and what it is called there (AGL-3141).\n *\n * An id names a module AND an export on it, never a module alone: several\n * modules export more than one element, and the registry needs the component,\n * its schema and — for the element that carries them — its presets by name.\n */\nexport interface MuiComponentSource {\n /** The module, as a thunk, so the bundler gives it a chunk of its own. */\n module: () => Promise<Record<string, unknown>>\n /** The component's export name on that module. */\n component: string\n /** Its schema's export name. */\n schema: string\n /** The export carrying its presets, for the element that ships them. */\n presets?: string\n /**\n * An export that loads what SOME instances of the element render, before\n * any of them does (AGL-3438) — `(everything: boolean) => Promise | void`.\n *\n * For a module the element fetches only when an instance's props ask for\n * it, the way Collection Entries fetches its search box. The element alone\n * knows when a render must not wait for that fetch; `everything` tells it\n * the whole library is loading, which is what the console and the besigner\n * ask for. The bundle is handed over once the load it returns has settled.\n */\n prepare?: string\n}\n\n/**\n * Every component this bundle can register (AGL-140, AGL-3141).\n *\n * One entry per component keeps register/unregister symmetric — the old\n * hand-maintained lists had drifted, and several components were never\n * unregistered on destroy. It replaces a single array built from sixty static\n * imports, which put every element on every published page while the page\n * rendered about nineteen of them.\n *\n * Exported so the bundle is assertable (AGL-1201): duplicate component or\n * preset ids collapse silently inside the registry's keyed records, so they\n * can only be caught before registration.\n */\nexport const MUI_COMPONENT_SOURCES: Readonly<\n Record<string, MuiComponentSource>\n> = {\n muiAppBar: {\n module: appBar,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiToolbar: {\n module: toolbar,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiTypography: {\n module: typography,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The inline text run (AGL-1235): Typography is `textEditable` and so a\n // leaf, which left no way to emphasize a phrase inside a sentence — every\n // statement rendered as one flat color.\n muiInlineText: {\n module: inlineText,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiButton: {\n module: button,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiContainer: {\n module: container,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n layoutSlot: {\n module: layoutSlot,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiList: {\n module: list,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiListItem: {\n module: listItem,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiListItemText: {\n module: listItemText,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // form, formField moved to @aglyn/plugins-forms (AGL-395).\n videoEmbed: {\n module: blocks,\n component: 'VideoEmbed',\n schema: 'videoEmbedSchema',\n presets: 'blockPresets',\n },\n socialLinks: {\n module: blocks,\n component: 'SocialLinks',\n schema: 'socialLinksSchema',\n },\n // A real data grid (AGL-2543): the feature matrix a comparison page is\n // built around, which the palette had no element for at all.\n dataTable: {\n module: dataTable,\n component: 'default',\n schema: 'dataTableSchema',\n presets: 'dataTablePresets',\n },\n // Content collections (AGL-551/582): entries repeater, markdown entry\n // body, related posts, share bar, entry meta.\n collectionEntries: {\n module: collectionEntries,\n component: 'CollectionEntries',\n schema: 'collectionEntriesSchema',\n presets: 'collectionPresets',\n prepare: 'prepareEntriesSearch',\n },\n collectionEntryBody: {\n module: collectionEntryBody,\n component: 'CollectionEntryBody',\n schema: 'collectionEntryBodySchema',\n },\n collectionRelated: {\n module: collectionRelated,\n component: 'CollectionRelated',\n schema: 'collectionRelatedSchema',\n },\n collectionShare: {\n module: collectionShare,\n component: 'CollectionShare',\n schema: 'collectionShareSchema',\n },\n collectionEntryMeta: {\n module: collectionEntryMeta,\n component: 'CollectionEntryMeta',\n schema: 'collectionEntryMetaSchema',\n },\n // The author card that closes an article (AGL-2486): the byline block\n // prints a name, and the record behind it also has a portrait and a bio.\n collectionEntryAuthor: {\n module: collectionEntryAuthor,\n component: 'CollectionEntryAuthor',\n schema: 'collectionEntryAuthorSchema',\n },\n // The subject of an author's own page (AGL-2518): the same person the\n // card above draws as a footnote, with the role fields it has no room for.\n contentAuthorProfile: {\n module: collectionAuthorProfile,\n component: 'ContentAuthorProfile',\n schema: 'contentAuthorProfileSchema',\n },\n // Category pills (AGL-1321): real anchors to /{collection}/category/{slug}.\n collectionCategories: {\n module: collectionCategories,\n component: 'CollectionCategories',\n schema: 'collectionCategoriesSchema',\n },\n // The toolbar search box (AGL-1516): the entries block's own field cannot\n // leave it, so the frame's pills-left / search-right row needs a block.\n collectionSearch: {\n module: collectionSearch,\n component: 'CollectionSearch',\n schema: 'collectionSearchSchema',\n },\n image: {\n module: image,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n video: {\n module: video,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n icon: {\n module: icon,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // booking moved to @aglyn/plugins-bookings (AGL-395).\n // eventList moved to @aglyn/plugins-events-calendar (AGL-313).\n languageSwitcher: {\n module: languageSwitcher,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n reusableInstance: {\n module: reusableInstance,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiScreenLink: {\n module: screenLink,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The linking container (AGL-1231): every other link element is a leaf,\n // so a tile whose icon and description also belong to the target had no\n // way to be authored.\n muiLinkBox: {\n module: linkBox,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Nav menu system (AGL-562): dropdown, mega menu, drawer + menu button.\n muiNavMenu: {\n module: navMenu,\n component: 'default',\n schema: 'navMenuSchema',\n presets: 'navMenuPresets',\n },\n muiMegaMenu: {\n module: navMenu,\n component: 'MegaMenu',\n schema: 'megaMenuSchema',\n },\n muiDrawer: {\n module: drawer,\n component: 'default',\n schema: 'drawerSchema',\n presets: 'drawerPresets',\n },\n muiDrawerToggle: {\n module: drawer,\n component: 'DrawerToggle',\n schema: 'drawerToggleSchema',\n },\n product: {\n module: product,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n marketplacePlugin: {\n module: plugin,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n 'custom-html': {\n module: customHtml,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n searchBox: {\n module: searchBox,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Long-form documents (AGL-1162): one markdown element carrying the whole\n // page, and an \"On this page\" aside derived from its headings.\n markdown: {\n module: markdown,\n component: 'Markdown',\n schema: 'markdownSchema',\n presets: 'markdownPresets',\n },\n tableOfContents: {\n module: markdown,\n component: 'TableOfContents',\n schema: 'tableOfContentsSchema',\n },\n section: {\n module: section,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiStack: {\n module: stack,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Layout & surface primitives (AGL-1201).\n muiBox: {\n module: box,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The canvas ROOT (AGL-2486): registered so the Document layer has a\n // schema — an element picker — instead of falling through to the\n // renderer's unstyled div. No preset: it is not an element anybody drops.\n div: {\n module: documentRoot,\n component: 'default',\n schema: 'schema',\n },\n muiGrid: {\n module: grid,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiPaper: {\n module: paper,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiCard: {\n module: card,\n component: 'default',\n schema: 'cardSchema',\n presets: 'cardPresets',\n },\n muiCardHeader: {\n module: card,\n component: 'CardHeaderElement',\n schema: 'cardHeaderSchema',\n },\n muiCardContent: {\n module: card,\n component: 'CardContentElement',\n schema: 'cardContentSchema',\n },\n muiCardActions: {\n module: card,\n component: 'CardActionsElement',\n schema: 'cardActionsSchema',\n },\n muiAccordion: {\n module: accordion,\n component: 'default',\n schema: 'accordionSchema',\n presets: 'accordionPresets',\n },\n muiAccordionSummary: {\n module: accordion,\n component: 'AccordionSummaryElement',\n schema: 'accordionSummarySchema',\n },\n muiAccordionDetails: {\n module: accordion,\n component: 'AccordionDetailsElement',\n schema: 'accordionDetailsSchema',\n },\n muiTabs: {\n module: tabs,\n component: 'default',\n schema: 'tabsSchema',\n presets: 'tabsPresets',\n },\n muiTabPanel: {\n module: tabs,\n component: 'TabPanelElement',\n schema: 'tabPanelSchema',\n },\n muiImageList: {\n module: imageList,\n component: 'default',\n schema: 'imageListSchema',\n presets: 'imageListPresets',\n },\n muiImageListItem: {\n module: imageList,\n component: 'ImageListItemElement',\n schema: 'imageListItemSchema',\n },\n muiPagination: {\n module: pagination,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiBreadcrumbs: {\n module: breadcrumbs,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n themeModeSwitcher: {\n module: themeModeSwitcher,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n}\n\n/**\n * The bundle entries for `componentIds` — every component when none are\n * named, which is what the console and the besigner ask for because their\n * palette shows every element.\n *\n * Both paths go through the same map, so there is one statement of where a\n * component comes from. An id the map does not know is dropped rather than\n * resolved to an undefined component: a page whose document carries an\n * element this bundle does not own is a document for another plugin's\n * element, not a reason to register a blank.\n */\nexport async function loadMuiBundle(\n componentIds?: readonly string[],\n): Promise<MuiBundleEntry[]> {\n const ids = (componentIds ?? Object.keys(MUI_COMPONENT_SOURCES)).filter(\n (id) => id in MUI_COMPONENT_SOURCES,\n )\n // Keyed on the THUNK, so a module that exports several elements is\n // imported once for all the ids that name it rather than once per id.\n const pending = new Map<\n MuiComponentSource['module'],\n Promise<Record<string, unknown>>\n >()\n const modules = await Promise.all(\n ids.map((id) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n let load = pending.get(source.module)\n if (!load) {\n load = source.module()\n pending.set(source.module, load)\n }\n return load\n }),\n )\n // Each module's on-demand parts, once per module, before anything renders.\n const everything = componentIds === undefined\n const prepared = new Set<unknown>()\n await Promise.all(\n ids.map((id, index) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n const mod = modules[index] as Record<string, unknown>\n if (!source.prepare || prepared.has(mod)) return undefined\n prepared.add(mod)\n const prepare = mod[source.prepare] as\n | ((everything: boolean) => Promise<unknown> | void)\n | undefined\n // A failed fetch is the render's to report, not the registration's:\n // the element asks again when it renders, and a block that still\n // cannot have it fails alone instead of taking the page with it.\n return Promise.resolve(prepare?.(everything)).catch(() => undefined)\n }),\n )\n return ids.map((id, index) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n const mod = modules[index] as Record<string, unknown>\n return {\n component: mod[source.component],\n schema: mod[source.schema] as Aglyn.ComponentSchema<any>,\n ...(source.presets\n ? { presets: mod[source.presets] as Aglyn.PresetSchema[] }\n : {}),\n }\n })\n}\n\n/**\n * Every component registered so far, by id.\n *\n * A second page places elements the first did not, and reaches this bundle\n * again with them; the map is what tells the two apart, and what `load` and\n * `destroy` read so a reload registers everything asked for so far and\n * unregistration stays symmetric with registration.\n */\nconst registeredById = new Map<string, MuiBundleEntry>()\n\nfunction registerEntries(entries: readonly MuiBundleEntry[]): void {\n // One mobx transaction (AGL-371): a single observer notification\n // for the whole batch instead of one per component/preset.\n runInAction(() => {\n for (const entry of entries) {\n Aglyn.components.registerComponent(entry.component, entry.schema)\n }\n for (const entry of entries) {\n if (entry.presets?.length) {\n Aglyn.components.registerPreset(entry.presets)\n }\n }\n })\n}\n\n/**\n * Registers the core MUI component library with the `@aglyn/aglyn` global\n * plugin registry (`AglynNodeRenderer`, `Aglyn.canvas`, ...). This is the\n * platform's core component bundle; feature bundles (commerce,\n * events-calendar, email) declare a dependency on it.\n *\n * `use.componentIds` narrows it to the elements the surface places\n * (AGL-3116/AGL-3141), so a published page fetches those modules and no\n * others. A caller that names nothing gets the whole library, which is what\n * the console and the besigner need and what any surface that cannot say\n * what it places must be given: an element whose component never registered\n * renders NOTHING, silently (AGL-52).\n */\nexport async function registerMuiPlugin(use?: Aglyn.PluginUse): Promise<void> {\n // Install→preset mapper (AGL-419): the console's drawer registration\n // consumes it through core, never importing this plugin.\n registerPluginInstallPresetMapper(muiPluginInstallToPreset)\n // Declared canvas elements become their own palette entries (AGL-1031).\n registerPluginInstallPresetsMapper(muiPluginInstallToPresets)\n\n const wanted = (use?.componentIds ?? Object.keys(MUI_COMPONENT_SOURCES))\n .filter((id) => id in MUI_COMPONENT_SOURCES)\n .filter((id) => !registeredById.has(id))\n const existing = Aglyn.plugins.getDependency(BUNDLE_ID)\n if (existing && !wanted.length) return\n\n const entries = wanted.length ? await loadMuiBundle(wanted) : []\n for (const entry of entries) {\n registeredById.set(entry.schema.$id as string, entry)\n }\n\n // The dependency is added even when this page places nothing of it: a\n // feature bundle declares mui as a dependency and stays WAITING — its own\n // components never registering — until mui is in the manager.\n //\n // Re-read after the imports rather than trusting the check above: the\n // manager runs `load()` from `addDependency` synchronously, so the first\n // caller to get here creates the dependency and every later one takes the\n // branch that registers what it added into a bundle already LOADED.\n if (Aglyn.plugins.getDependency(BUNDLE_ID)) {\n if (entries.length) registerEntries(entries)\n return\n }\n\n Aglyn.plugins.addDependency({\n $id: BUNDLE_ID,\n displayName: 'Material UI',\n description: 'Material UI elements',\n title: 'Material UI',\n dependencies: {},\n load(): void {\n registerEntries([...registeredById.values()])\n },\n destroy(): void {\n const entries = [...registeredById.values()]\n runInAction(() => {\n for (const entry of entries) {\n if (entry.presets?.length) {\n Aglyn.components.unregisterPreset(\n entry.presets.map((preset) => preset.$id),\n )\n }\n }\n for (const entry of entries) {\n Aglyn.components.unregisterComponent(entry.schema.$id)\n }\n })\n },\n })\n}\n"],"names":["registerPluginInstallPresetMapper","registerPluginInstallPresetsMapper","muiPluginInstallToPreset","muiPluginInstallToPresets","Aglyn","runInAction","BUNDLE_ID","tierLoad","elementTier","inTier","name","then","tier","CORE_ELEMENTS","viaTier","appBar","toolbar","layoutSlot","documentRoot","section","container","box","stack","grid","paper","typography","inlineText","button","screenLink","linkBox","image","icon","list","listItem","listItemText","blocks","dataTable","collectionEntries","collectionEntryBody","collectionRelated","collectionShare","collectionEntryMeta","collectionEntryAuthor","collectionAuthorProfile","collectionCategories","collectionSearch","video","languageSwitcher","reusableInstance","navMenu","drawer","product","plugin","customHtml","searchBox","markdown","card","accordion","tabs","imageList","pagination","breadcrumbs","themeModeSwitcher","MUI_COMPONENT_SOURCES","muiAppBar","module","component","schema","presets","muiToolbar","muiTypography","muiInlineText","muiButton","muiContainer","muiList","muiListItem","muiListItemText","videoEmbed","socialLinks","prepare","contentAuthorProfile","muiScreenLink","muiLinkBox","muiNavMenu","muiMegaMenu","muiDrawer","muiDrawerToggle","marketplacePlugin","tableOfContents","muiStack","muiBox","div","muiGrid","muiPaper","muiCard","muiCardHeader","muiCardContent","muiCardActions","muiAccordion","muiAccordionSummary","muiAccordionDetails","muiTabs","muiTabPanel","muiImageList","muiImageListItem","muiPagination","muiBreadcrumbs","loadMuiBundle","componentIds","ids","Object","keys","filter","id","pending","Map","modules","Promise","all","map","source","load","get","set","everything","undefined","prepared","Set","index","mod","has","add","resolve","catch","registeredById","registerEntries","entries","entry","components","registerComponent","length","registerPreset","registerMuiPlugin","use","wanted","existing","plugins","getDependency","$id","addDependency","displayName","description","title","dependencies","values","destroy","unregisterPreset","preset","unregisterComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/mui/src/lib/plugin.ts"],"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 {\n registerPluginInstallPresetMapper,\n registerPluginInstallPresetsMapper,\n} from '@aglyn/aglyn'\nimport {\n muiPluginInstallToPreset,\n muiPluginInstallToPresets,\n} from './components/plugin-install-presets'\nimport * as Aglyn from '@aglyn/aglyn'\nimport { runInAction } from 'mobx'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * One thunk per component module, named here because several ids share one:\n * `card.tsx` and `accordion.tsx` each export several elements, and a module is\n * imported once however many of its components a surface asks for.\n *\n * Every thunk goes through the element tier (AGL-3401). The common elements\n * are part of it; the rest are `import()`s written inside it, so they can\n * rely on what it loaded instead of each carrying a copy. `element-tier.ts`\n * has the reasoning, and which elements are which.\n */\ntype ElementTier = typeof import('./element-tier')\ntype TierElement = keyof ElementTier['CORE_ELEMENTS']\ntype LazyElement = Exclude<keyof ElementTier, 'CORE_ELEMENTS'>\n\ntype ElementModule = Promise<Record<string, unknown>>\n\n/**\n * One load of the tier, however many thunks ask at once. The bundler shares\n * a module across concurrent imports anyway; a loader that hands out a\n * module while it is still evaluating does not — jiti, which the AI palette\n * and page-axe generators load this bundle through, resolved the second of\n * sixty concurrent imports to the tier's exports before `CORE_ELEMENTS` was\n * assigned.\n */\nlet tierLoad: Promise<ElementTier> | undefined\nconst elementTier = (): Promise<ElementTier> =>\n (tierLoad ??= import('./element-tier'))\nconst inTier =\n (name: TierElement): MuiComponentSource['module'] =>\n () =>\n elementTier().then(\n (tier): Record<string, unknown> => tier.CORE_ELEMENTS[name],\n )\nconst viaTier =\n (name: LazyElement): MuiComponentSource['module'] =>\n () =>\n elementTier().then((tier): ElementModule => tier[name]())\n\nconst appBar = inTier('appBar')\nconst toolbar = inTier('toolbar')\nconst layoutSlot = inTier('layoutSlot')\nconst documentRoot = inTier('documentRoot')\nconst section = inTier('section')\nconst container = inTier('container')\nconst box = inTier('box')\nconst stack = inTier('stack')\nconst grid = inTier('grid')\nconst paper = inTier('paper')\nconst typography = inTier('typography')\nconst inlineText = inTier('inlineText')\nconst button = inTier('button')\nconst screenLink = inTier('screenLink')\nconst linkBox = inTier('linkBox')\nconst image = inTier('image')\nconst icon = inTier('icon')\n\nconst list = viaTier('list')\nconst listItem = viaTier('listItem')\nconst listItemText = viaTier('listItemText')\nconst blocks = viaTier('blocks')\nconst dataTable = viaTier('dataTable')\nconst collectionEntries = viaTier('collectionEntries')\nconst collectionEntryBody = viaTier('collectionEntryBody')\nconst collectionRelated = viaTier('collectionRelated')\nconst collectionShare = viaTier('collectionShare')\nconst collectionEntryMeta = viaTier('collectionEntryMeta')\nconst collectionEntryAuthor = viaTier('collectionEntryAuthor')\nconst collectionAuthorProfile = viaTier('collectionAuthorProfile')\nconst collectionCategories = viaTier('collectionCategories')\nconst collectionSearch = viaTier('collectionSearch')\nconst video = viaTier('video')\nconst languageSwitcher = viaTier('languageSwitcher')\nconst reusableInstance = viaTier('reusableInstance')\nconst navMenu = viaTier('navMenu')\nconst drawer = viaTier('drawer')\nconst product = viaTier('product')\nconst plugin = viaTier('plugin')\nconst customHtml = viaTier('customHtml')\nconst searchBox = viaTier('searchBox')\nconst markdown = viaTier('markdown')\nconst card = viaTier('card')\nconst accordion = viaTier('accordion')\nconst tabs = viaTier('tabs')\nconst imageList = viaTier('imageList')\nconst pagination = viaTier('pagination')\nconst breadcrumbs = viaTier('breadcrumbs')\nconst themeModeSwitcher = viaTier('themeModeSwitcher')\n\n/** One entry of the bundle: what the registry is handed for a component. */\nexport interface MuiBundleEntry {\n component: any\n schema: Aglyn.ComponentSchema<any>\n presets?: Aglyn.PresetSchema[]\n}\n\n/**\n * Where a component id's code lives, and what it is called there (AGL-3141).\n *\n * An id names a module AND an export on it, never a module alone: several\n * modules export more than one element, and the registry needs the component,\n * its schema and — for the element that carries them — its presets by name.\n */\nexport interface MuiComponentSource {\n /** The module, as a thunk, so the bundler gives it a chunk of its own. */\n module: () => Promise<Record<string, unknown>>\n /** The component's export name on that module. */\n component: string\n /** Its schema's export name. */\n schema: string\n /** The export carrying its presets, for the element that ships them. */\n presets?: string\n /**\n * An export that loads what SOME instances of the element render, before\n * any of them does (AGL-3438) — `(everything: boolean) => Promise | void`.\n *\n * For a module the element fetches only when an instance's props ask for\n * it, the way Collection Entries fetches its search box. The element alone\n * knows when a render must not wait for that fetch; `everything` tells it\n * the whole library is loading, which is what the console and the besigner\n * ask for. The bundle is handed over once the load it returns has settled.\n */\n prepare?: string\n}\n\n/**\n * Every component this bundle can register (AGL-140, AGL-3141).\n *\n * One entry per component keeps register/unregister symmetric — the old\n * hand-maintained lists had drifted, and several components were never\n * unregistered on destroy. It replaces a single array built from sixty static\n * imports, which put every element on every published page while the page\n * rendered about nineteen of them.\n *\n * Exported so the bundle is assertable (AGL-1201): duplicate component or\n * preset ids collapse silently inside the registry's keyed records, so they\n * can only be caught before registration.\n */\nexport const MUI_COMPONENT_SOURCES: Readonly<\n Record<string, MuiComponentSource>\n> = {\n muiAppBar: {\n module: appBar,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiToolbar: {\n module: toolbar,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiTypography: {\n module: typography,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The inline text run (AGL-1235): one styled phrase inside a sentence.\n // Typography also holds elements now (AGL-3672), but a run is still the\n // element that flows and wraps as part of the line.\n muiInlineText: {\n module: inlineText,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiButton: {\n module: button,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiContainer: {\n module: container,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n layoutSlot: {\n module: layoutSlot,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiList: {\n module: list,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiListItem: {\n module: listItem,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiListItemText: {\n module: listItemText,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // form, formField moved to @aglyn/plugins-forms (AGL-395).\n videoEmbed: {\n module: blocks,\n component: 'VideoEmbed',\n schema: 'videoEmbedSchema',\n presets: 'blockPresets',\n },\n socialLinks: {\n module: blocks,\n component: 'SocialLinks',\n schema: 'socialLinksSchema',\n },\n // A real data grid (AGL-2543): the feature matrix a comparison page is\n // built around, which the palette had no element for at all.\n dataTable: {\n module: dataTable,\n component: 'default',\n schema: 'dataTableSchema',\n presets: 'dataTablePresets',\n },\n // Content collections (AGL-551/582): entries repeater, markdown entry\n // body, related posts, share bar, entry meta.\n collectionEntries: {\n module: collectionEntries,\n component: 'CollectionEntries',\n schema: 'collectionEntriesSchema',\n presets: 'collectionPresets',\n prepare: 'prepareEntriesSearch',\n },\n collectionEntryBody: {\n module: collectionEntryBody,\n component: 'CollectionEntryBody',\n schema: 'collectionEntryBodySchema',\n },\n collectionRelated: {\n module: collectionRelated,\n component: 'CollectionRelated',\n schema: 'collectionRelatedSchema',\n },\n collectionShare: {\n module: collectionShare,\n component: 'CollectionShare',\n schema: 'collectionShareSchema',\n },\n collectionEntryMeta: {\n module: collectionEntryMeta,\n component: 'CollectionEntryMeta',\n schema: 'collectionEntryMetaSchema',\n },\n // The author card that closes an article (AGL-2486): the byline block\n // prints a name, and the record behind it also has a portrait and a bio.\n collectionEntryAuthor: {\n module: collectionEntryAuthor,\n component: 'CollectionEntryAuthor',\n schema: 'collectionEntryAuthorSchema',\n },\n // The subject of an author's own page (AGL-2518): the same person the\n // card above draws as a footnote, with the role fields it has no room for.\n contentAuthorProfile: {\n module: collectionAuthorProfile,\n component: 'ContentAuthorProfile',\n schema: 'contentAuthorProfileSchema',\n },\n // Category pills (AGL-1321): real anchors to /{collection}/category/{slug}.\n collectionCategories: {\n module: collectionCategories,\n component: 'CollectionCategories',\n schema: 'collectionCategoriesSchema',\n },\n // The toolbar search box (AGL-1516): the entries block's own field cannot\n // leave it, so the frame's pills-left / search-right row needs a block.\n collectionSearch: {\n module: collectionSearch,\n component: 'CollectionSearch',\n schema: 'collectionSearchSchema',\n },\n image: {\n module: image,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n video: {\n module: video,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n icon: {\n module: icon,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // booking moved to @aglyn/plugins-bookings (AGL-395).\n // eventList moved to @aglyn/plugins-events-calendar (AGL-313).\n languageSwitcher: {\n module: languageSwitcher,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n reusableInstance: {\n module: reusableInstance,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiScreenLink: {\n module: screenLink,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The linking container (AGL-1231): every other link element is a leaf,\n // so a tile whose icon and description also belong to the target had no\n // way to be authored.\n muiLinkBox: {\n module: linkBox,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Nav menu system (AGL-562): dropdown, mega menu, drawer + menu button.\n muiNavMenu: {\n module: navMenu,\n component: 'default',\n schema: 'navMenuSchema',\n presets: 'navMenuPresets',\n },\n muiMegaMenu: {\n module: navMenu,\n component: 'MegaMenu',\n schema: 'megaMenuSchema',\n },\n muiDrawer: {\n module: drawer,\n component: 'default',\n schema: 'drawerSchema',\n presets: 'drawerPresets',\n },\n muiDrawerToggle: {\n module: drawer,\n component: 'DrawerToggle',\n schema: 'drawerToggleSchema',\n },\n product: {\n module: product,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n marketplacePlugin: {\n module: plugin,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n 'custom-html': {\n module: customHtml,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n searchBox: {\n module: searchBox,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Long-form documents (AGL-1162): one markdown element carrying the whole\n // page, and an \"On this page\" aside derived from its headings.\n markdown: {\n module: markdown,\n component: 'Markdown',\n schema: 'markdownSchema',\n presets: 'markdownPresets',\n },\n tableOfContents: {\n module: markdown,\n component: 'TableOfContents',\n schema: 'tableOfContentsSchema',\n },\n section: {\n module: section,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiStack: {\n module: stack,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // Layout & surface primitives (AGL-1201).\n muiBox: {\n module: box,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n // The canvas ROOT (AGL-2486): registered so the Document layer has a\n // schema — an element picker — instead of falling through to the\n // renderer's unstyled div. No preset: it is not an element anybody drops.\n div: {\n module: documentRoot,\n component: 'default',\n schema: 'schema',\n },\n muiGrid: {\n module: grid,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiPaper: {\n module: paper,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiCard: {\n module: card,\n component: 'default',\n schema: 'cardSchema',\n presets: 'cardPresets',\n },\n muiCardHeader: {\n module: card,\n component: 'CardHeaderElement',\n schema: 'cardHeaderSchema',\n },\n muiCardContent: {\n module: card,\n component: 'CardContentElement',\n schema: 'cardContentSchema',\n },\n muiCardActions: {\n module: card,\n component: 'CardActionsElement',\n schema: 'cardActionsSchema',\n },\n muiAccordion: {\n module: accordion,\n component: 'default',\n schema: 'accordionSchema',\n presets: 'accordionPresets',\n },\n muiAccordionSummary: {\n module: accordion,\n component: 'AccordionSummaryElement',\n schema: 'accordionSummarySchema',\n },\n muiAccordionDetails: {\n module: accordion,\n component: 'AccordionDetailsElement',\n schema: 'accordionDetailsSchema',\n },\n muiTabs: {\n module: tabs,\n component: 'default',\n schema: 'tabsSchema',\n presets: 'tabsPresets',\n },\n muiTabPanel: {\n module: tabs,\n component: 'TabPanelElement',\n schema: 'tabPanelSchema',\n },\n muiImageList: {\n module: imageList,\n component: 'default',\n schema: 'imageListSchema',\n presets: 'imageListPresets',\n },\n muiImageListItem: {\n module: imageList,\n component: 'ImageListItemElement',\n schema: 'imageListItemSchema',\n },\n muiPagination: {\n module: pagination,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n muiBreadcrumbs: {\n module: breadcrumbs,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n themeModeSwitcher: {\n module: themeModeSwitcher,\n component: 'default',\n schema: 'schema',\n presets: 'presets',\n },\n}\n\n/**\n * The bundle entries for `componentIds` — every component when none are\n * named, which is what the console and the besigner ask for because their\n * palette shows every element.\n *\n * Both paths go through the same map, so there is one statement of where a\n * component comes from. An id the map does not know is dropped rather than\n * resolved to an undefined component: a page whose document carries an\n * element this bundle does not own is a document for another plugin's\n * element, not a reason to register a blank.\n */\nexport async function loadMuiBundle(\n componentIds?: readonly string[],\n): Promise<MuiBundleEntry[]> {\n const ids = (componentIds ?? Object.keys(MUI_COMPONENT_SOURCES)).filter(\n (id) => id in MUI_COMPONENT_SOURCES,\n )\n // Keyed on the THUNK, so a module that exports several elements is\n // imported once for all the ids that name it rather than once per id.\n const pending = new Map<\n MuiComponentSource['module'],\n Promise<Record<string, unknown>>\n >()\n const modules = await Promise.all(\n ids.map((id) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n let load = pending.get(source.module)\n if (!load) {\n load = source.module()\n pending.set(source.module, load)\n }\n return load\n }),\n )\n // Each module's on-demand parts, once per module, before anything renders.\n const everything = componentIds === undefined\n const prepared = new Set<unknown>()\n await Promise.all(\n ids.map((id, index) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n const mod = modules[index] as Record<string, unknown>\n if (!source.prepare || prepared.has(mod)) return undefined\n prepared.add(mod)\n const prepare = mod[source.prepare] as\n | ((everything: boolean) => Promise<unknown> | void)\n | undefined\n // A failed fetch is the render's to report, not the registration's:\n // the element asks again when it renders, and a block that still\n // cannot have it fails alone instead of taking the page with it.\n return Promise.resolve(prepare?.(everything)).catch(() => undefined)\n }),\n )\n return ids.map((id, index) => {\n const source = MUI_COMPONENT_SOURCES[id] as MuiComponentSource\n const mod = modules[index] as Record<string, unknown>\n return {\n component: mod[source.component],\n schema: mod[source.schema] as Aglyn.ComponentSchema<any>,\n ...(source.presets\n ? { presets: mod[source.presets] as Aglyn.PresetSchema[] }\n : {}),\n }\n })\n}\n\n/**\n * Every component registered so far, by id.\n *\n * A second page places elements the first did not, and reaches this bundle\n * again with them; the map is what tells the two apart, and what `load` and\n * `destroy` read so a reload registers everything asked for so far and\n * unregistration stays symmetric with registration.\n */\nconst registeredById = new Map<string, MuiBundleEntry>()\n\nfunction registerEntries(entries: readonly MuiBundleEntry[]): void {\n // One mobx transaction (AGL-371): a single observer notification\n // for the whole batch instead of one per component/preset.\n runInAction(() => {\n for (const entry of entries) {\n Aglyn.components.registerComponent(entry.component, entry.schema)\n }\n for (const entry of entries) {\n if (entry.presets?.length) {\n Aglyn.components.registerPreset(entry.presets)\n }\n }\n })\n}\n\n/**\n * Registers the core MUI component library with the `@aglyn/aglyn` global\n * plugin registry (`AglynNodeRenderer`, `Aglyn.canvas`, ...). This is the\n * platform's core component bundle; feature bundles (commerce,\n * events-calendar, email) declare a dependency on it.\n *\n * `use.componentIds` narrows it to the elements the surface places\n * (AGL-3116/AGL-3141), so a published page fetches those modules and no\n * others. A caller that names nothing gets the whole library, which is what\n * the console and the besigner need and what any surface that cannot say\n * what it places must be given: an element whose component never registered\n * renders NOTHING, silently (AGL-52).\n */\nexport async function registerMuiPlugin(use?: Aglyn.PluginUse): Promise<void> {\n // Install→preset mapper (AGL-419): the console's drawer registration\n // consumes it through core, never importing this plugin.\n registerPluginInstallPresetMapper(muiPluginInstallToPreset)\n // Declared canvas elements become their own palette entries (AGL-1031).\n registerPluginInstallPresetsMapper(muiPluginInstallToPresets)\n\n const wanted = (use?.componentIds ?? Object.keys(MUI_COMPONENT_SOURCES))\n .filter((id) => id in MUI_COMPONENT_SOURCES)\n .filter((id) => !registeredById.has(id))\n const existing = Aglyn.plugins.getDependency(BUNDLE_ID)\n if (existing && !wanted.length) return\n\n const entries = wanted.length ? await loadMuiBundle(wanted) : []\n for (const entry of entries) {\n registeredById.set(entry.schema.$id as string, entry)\n }\n\n // The dependency is added even when this page places nothing of it: a\n // feature bundle declares mui as a dependency and stays WAITING — its own\n // components never registering — until mui is in the manager.\n //\n // Re-read after the imports rather than trusting the check above: the\n // manager runs `load()` from `addDependency` synchronously, so the first\n // caller to get here creates the dependency and every later one takes the\n // branch that registers what it added into a bundle already LOADED.\n if (Aglyn.plugins.getDependency(BUNDLE_ID)) {\n if (entries.length) registerEntries(entries)\n return\n }\n\n Aglyn.plugins.addDependency({\n $id: BUNDLE_ID,\n displayName: 'Material UI',\n description: 'Material UI elements',\n title: 'Material UI',\n dependencies: {},\n load(): void {\n registerEntries([...registeredById.values()])\n },\n destroy(): void {\n const entries = [...registeredById.values()]\n runInAction(() => {\n for (const entry of entries) {\n if (entry.presets?.length) {\n Aglyn.components.unregisterPreset(\n entry.presets.map((preset) => preset.$id),\n )\n }\n }\n for (const entry of entries) {\n Aglyn.components.unregisterComponent(entry.schema.$id)\n }\n })\n },\n })\n}\n"],"names":["registerPluginInstallPresetMapper","registerPluginInstallPresetsMapper","muiPluginInstallToPreset","muiPluginInstallToPresets","Aglyn","runInAction","BUNDLE_ID","tierLoad","elementTier","inTier","name","then","tier","CORE_ELEMENTS","viaTier","appBar","toolbar","layoutSlot","documentRoot","section","container","box","stack","grid","paper","typography","inlineText","button","screenLink","linkBox","image","icon","list","listItem","listItemText","blocks","dataTable","collectionEntries","collectionEntryBody","collectionRelated","collectionShare","collectionEntryMeta","collectionEntryAuthor","collectionAuthorProfile","collectionCategories","collectionSearch","video","languageSwitcher","reusableInstance","navMenu","drawer","product","plugin","customHtml","searchBox","markdown","card","accordion","tabs","imageList","pagination","breadcrumbs","themeModeSwitcher","MUI_COMPONENT_SOURCES","muiAppBar","module","component","schema","presets","muiToolbar","muiTypography","muiInlineText","muiButton","muiContainer","muiList","muiListItem","muiListItemText","videoEmbed","socialLinks","prepare","contentAuthorProfile","muiScreenLink","muiLinkBox","muiNavMenu","muiMegaMenu","muiDrawer","muiDrawerToggle","marketplacePlugin","tableOfContents","muiStack","muiBox","div","muiGrid","muiPaper","muiCard","muiCardHeader","muiCardContent","muiCardActions","muiAccordion","muiAccordionSummary","muiAccordionDetails","muiTabs","muiTabPanel","muiImageList","muiImageListItem","muiPagination","muiBreadcrumbs","loadMuiBundle","componentIds","ids","Object","keys","filter","id","pending","Map","modules","Promise","all","map","source","load","get","set","everything","undefined","prepared","Set","index","mod","has","add","resolve","catch","registeredById","registerEntries","entries","entry","components","registerComponent","length","registerPreset","registerMuiPlugin","use","wanted","existing","plugins","getDependency","$id","addDependency","displayName","description","title","dependencies","values","destroy","unregisterPreset","preset","unregisterComponent"],"mappings":"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"}
|