@aglyn/plugins-mui 1.0.0-beta.221 → 1.0.0-beta.223
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/lib/components/accordion.js +21 -1
- package/src/lib/components/accordion.js.map +1 -1
- package/src/lib/components/collection-entries-search.d.ts +29 -0
- package/src/lib/components/collection-entries-search.js +228 -0
- package/src/lib/components/collection-entries-search.js.map +1 -0
- package/src/lib/components/collection-entries.d.ts +45 -10
- package/src/lib/components/collection-entries.js +67 -183
- package/src/lib/components/collection-entries.js.map +1 -1
- package/src/lib/components/collection-entry-body.d.ts +1 -0
- package/src/lib/components/collection-entry-body.js.map +1 -1
- package/src/lib/components/collection-search-box.js +9 -0
- package/src/lib/components/collection-search-box.js.map +1 -1
- package/src/lib/components/image.d.ts +24 -5
- package/src/lib/components/image.js +145 -25
- package/src/lib/components/image.js.map +1 -1
- package/src/lib/components/markdown.d.ts +1 -0
- package/src/lib/components/markdown.js.map +1 -1
- package/src/lib/components/search-box.d.ts +3 -1
- package/src/lib/components/search-box.js +8 -3
- package/src/lib/components/search-box.js.map +1 -1
- package/src/lib/plugin.d.ts +11 -0
- package/src/lib/plugin.js +16 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/utils/image-sizes.d.ts +146 -0
- package/src/lib/utils/image-sizes.js +423 -0
- package/src/lib/utils/image-sizes.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-mui",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.223",
|
|
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.223",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.223",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.223",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.223",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.223",
|
|
33
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.223",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"mobx": "^7.0.5",
|
|
36
36
|
"mobx-react-lite": "^5.1.0"
|
|
@@ -80,6 +80,15 @@ AccordionElement.displayName = 'AglynAccordion';
|
|
|
80
80
|
px: 2,
|
|
81
81
|
py: 1.5
|
|
82
82
|
};
|
|
83
|
+
/**
|
|
84
|
+
* The summary is a `<button>`, and a button takes the browser's own font
|
|
85
|
+
* rather than the page's — so every accordion question on a site rendered in
|
|
86
|
+
* the system face while the answer under it used the theme's (AGL-3497).
|
|
87
|
+
* Only the family is inherited: size and weight stay MUI's, and the node's own
|
|
88
|
+
* styles, applied after this, still win.
|
|
89
|
+
*/ const SUMMARY_FONT_SX = {
|
|
90
|
+
fontFamily: 'inherit'
|
|
91
|
+
};
|
|
83
92
|
/**
|
|
84
93
|
* The toggle keeps only its chevron: the label is a sibling now, so the
|
|
85
94
|
* summary's own content slot would just add empty width.
|
|
@@ -143,12 +152,23 @@ AccordionElement.displayName = 'AglynAccordion';
|
|
|
143
152
|
// an unpublished or deleted target yields no href.
|
|
144
153
|
const { href, suppressNavigation } = Aglyn.useScreenLink(screenId);
|
|
145
154
|
if (!screenId) {
|
|
155
|
+
const { sx: plainSx } = rest, plainRest = _object_without_properties_loose(rest, [
|
|
156
|
+
"sx"
|
|
157
|
+
]);
|
|
158
|
+
const plainNodeSx = Array.isArray(plainSx) ? plainSx : plainSx ? [
|
|
159
|
+
plainSx
|
|
160
|
+
] : [];
|
|
146
161
|
return /*#__PURE__*/ _jsx(MuiAccordionSummary, _extends({
|
|
147
162
|
ref: ref,
|
|
148
163
|
expandIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
149
164
|
path: mdiChevronDown.path
|
|
150
165
|
})
|
|
151
|
-
}, withAuthorHtmlLabel(
|
|
166
|
+
}, withAuthorHtmlLabel(plainRest, html), {
|
|
167
|
+
sx: [
|
|
168
|
+
SUMMARY_FONT_SX,
|
|
169
|
+
...plainNodeSx
|
|
170
|
+
]
|
|
171
|
+
}));
|
|
152
172
|
}
|
|
153
173
|
const { children, sx } = rest, rowRest = _object_without_properties_loose(rest, [
|
|
154
174
|
"children",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/accordion.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiChevronDown, mdiUnfoldMoreHorizontal } from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport MuiAccordion from '@mui/material/Accordion'\nimport MuiAccordionDetails from '@mui/material/AccordionDetails'\nimport MuiAccordionSummary from '@mui/material/AccordionSummary'\nimport Box from '@mui/material/Box'\nimport Link from '@mui/material/Link'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, isValidElement, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport {\n authorHtmlLabel,\n withAuthorHtmlLabel,\n} from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ACCORDION_ID: Aglyn.ComponentId = 'muiAccordion'\nexport const ACCORDION_SUMMARY_ID: Aglyn.ComponentId = 'muiAccordionSummary'\nexport const ACCORDION_DETAILS_ID: Aglyn.ComponentId = 'muiAccordionDetails'\n\nexport interface AccordionElementProps {\n /** Starts open on the published site. */\n defaultExpanded?: boolean\n disabled?: boolean\n /** Removes the extra margin an expanded accordion normally gains. */\n disableGutters?: boolean\n children?: ReactNode\n}\n\n/**\n * Expand/collapse panel (https://mui.com/material-ui/react-accordion/).\n *\n * On the besigner canvas it renders **expanded regardless of\n * `defaultExpanded`**: collapsed details are unreachable for selection\n * and styling, which is the same reason the Drawer expands its contents\n * while selected (AGL-571). Preview and the published site both honour\n * the author's setting and toggle normally, so what ships is never in\n * doubt — only the editing surface differs.\n */\nconst AccordionElement = forwardRef<HTMLDivElement, AccordionElementProps>(\n (props, ref) => {\n const { defaultExpanded, children, ...rest } = props\n const { editorInert } = Aglyn.useScreenLink(undefined)\n if (editorInert) {\n // Controlled-open with no onChange: the canvas is a static surface,\n // clicks there select rather than toggle.\n // MUI types Accordion's children as NonNullable: an accordion with\n // no summary/details is not a renderable shape, and the renderer\n // always supplies the pair.\n return (\n <MuiAccordion\n ref={ref}\n expanded\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n }\n return (\n <MuiAccordion\n ref={ref}\n defaultExpanded={!!defaultExpanded}\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n },\n)\nAccordionElement.displayName = 'AglynAccordion'\n\nexport interface AccordionSummaryElementProps {\n /**\n * Optional screen the header's label links to (AGL-1232). Setting it\n * SPLITS the row — see {@link AccordionSummaryElement}. A screen id, never\n * a path: the href is resolved from the host routing map at render time,\n * so a slug rename or re-parent cannot break it, and a bare `href` would\n * full-reload the site instead of routing.\n */\n screenId?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute — recomposed below so the Styles panel's values are\n * merged rather than replaced (AGL-1240/1284).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the label, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n children?: ReactNode\n}\n\n/** Label and chevron sit on one line; the label takes the slack. */\nconst SUMMARY_ROW_SX = {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n} as const\n\n/** Matches MuiAccordionSummary's own gutters so the split row lines up. */\nconst SUMMARY_LABEL_SX = {\n flex: 1,\n minWidth: 0,\n px: 2,\n py: 1.5,\n} as const\n\n/**\n * The toggle keeps only its chevron: the label is a sibling now, so the\n * summary's own content slot would just add empty width.\n */\nconst SUMMARY_TOGGLE_SX = {\n flex: '0 0 auto',\n '& .MuiAccordionSummary-content': { display: 'none' },\n} as const\n\n/**\n * The visible text of a React children tree, for naming a control that has no\n * text of its own (AGL-2349).\n *\n * It has to walk, not test: what arrives here is the node renderer's output,\n * which wraps the authored string in an `<AglynText>` element inside an\n * array (`libs/aglyn-node-renderer/src/lib/components/leaf.tsx`), so the string\n * is a grandchild at best and never the value itself. Descends `props.children`\n * rather than reading DOM text, because this runs on the React tree BEFORE\n * anything is in the DOM to read. (It also predates AGL-2011, when\n * `<aglyn-text>` was a shadow host whose `innerText` read back empty — that\n * is no longer true, but walking props is still the right tool here.)\n */\nconst childText = (node: ReactNode): string => {\n if (node == null || typeof node === 'boolean') return ''\n if (typeof node === 'string' || typeof node === 'number') return String(node)\n if (Array.isArray(node)) return node.map(childText).join('')\n if (isValidElement(node)) {\n return childText((node.props as { children?: ReactNode })?.children)\n }\n return ''\n}\n\n/**\n * The always-visible header row. Supplies the expand chevron MUI leaves\n * to the caller — without it the control looks like inert text and gives\n * no hint that there is anything to open.\n *\n * ## Why a linked header is a different shape (AGL-1232)\n *\n * `MuiAccordionSummary` renders a `<button>`, and the mobile drawer needs\n * Product/Solutions to reach their jump pages as well as open. An anchor\n * inside that button is invalid nesting — browsers unnest it, and nested\n * interactive content is unusable with a screen reader or a keyboard — so\n * the row cannot both toggle and navigate while it is one control.\n *\n * With a `screenId` set the row therefore splits in two: the label becomes\n * a real anchor, and a chevron-only `MuiAccordionSummary` beside it becomes\n * the toggle. Two siblings, each independently focusable, no nesting. The\n * summary keeps driving the panel through MUI's accordion context, so\n * `aria-expanded` and the transition are unchanged.\n *\n * With no `screenId` — every accordion authored before this — the output is\n * byte-for-byte what it always was: one `MuiAccordionSummary`, whole row\n * toggles.\n */\nexport const AccordionSummaryElement = forwardRef<\n HTMLDivElement,\n AccordionSummaryElementProps\n>((props, ref) => {\n const { screenId, html, ...rest } = props\n // Resolution is the routing map's job, exactly as Screen Link does it;\n // an unpublished or deleted target yields no href.\n const { href, suppressNavigation } = Aglyn.useScreenLink(screenId)\n\n if (!screenId) {\n return (\n <MuiAccordionSummary\n ref={ref}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n {...withAuthorHtmlLabel(rest, html)}\n />\n )\n }\n\n const { children, sx, ...rowRest } = rest\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // The chevron button's only content is an icon, so it needs a name of its\n // own — \"Product\" the link and \"Product\" the toggle are two controls.\n //\n // This USED to be `typeof children === 'string'`, which is never true in\n // production (AGL-2349). A summary's label is authored through\n // FIELD_TEXT_CONTENT, and the node renderer does not pass that string down as\n // `children` — `Leaf` lifts it out as `textContent` and renders\n // `<Component>{children}<AglynText>{textContent}</…></Component>`,\n // so `children` is always an ARRAY holding an element. The string branch was\n // reachable only from a spec that handed the component a bare string, so\n // every published split summary named its chevron \"Toggle section\".\n //\n // Read from `children` rather than from the formatted label below, because\n // a formatted one is markup handed to `dangerouslySetInnerHTML` and has no\n // React children to walk. `children` carries the same words either way.\n const label = childText(children).trim()\n const content = authorHtmlLabel(html) ?? children\n return (\n <Box ref={ref} {...rowRest} sx={[SUMMARY_ROW_SX, ...nodeSx]}>\n {href && !suppressNavigation ? (\n <AppLink href={href} underline=\"hover\" sx={SUMMARY_LABEL_SX}>\n {content}\n </AppLink>\n ) : (\n // Unresolved target, or a surface that must not navigate (canvas,\n // preview): same two-part shape, so the editor never shows a row\n // that behaves differently from the one that ships.\n //\n // Same ELEMENT too, not just the same box (AGL-1268). `href` comes\n // from the screens map, which differs between the render that\n // produced an ISR page and the render that hydrates it — a screen\n // published or unpublished in between. Switching `<a>` to `<span>`\n // on that would remount the subtree at hydration; an `<a>` with no\n // href is HTML's placeholder link, so the element is stable and the\n // row is still inert. Link Container settled on this shape first.\n <Link underline=\"none\" color=\"inherit\" sx={SUMMARY_LABEL_SX}>\n {content}\n </Link>\n )}\n <MuiAccordionSummary\n aria-label={label ? `Toggle ${label}` : 'Toggle section'}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n sx={SUMMARY_TOGGLE_SX}\n />\n </Box>\n )\n})\nAccordionSummaryElement.displayName = 'AglynAccordionSummary'\n\nexport const AccordionDetailsElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiAccordionDetails ref={ref} {...props} />)\nAccordionDetailsElement.displayName = 'AglynAccordionDetails'\n\nexport const accordionSchema: Aglyn.ComponentSchema<AccordionElementProps> = {\n $id: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion',\n description:\n 'Header that expands to reveal its details — FAQs, long forms, ' +\n 'anything progressive.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n // Structure is fixed by MUI: summary first, details second.\n restrictChildren: [\n Aglyn.LinealDirectiveFlag.LIMIT_TO,\n { components: [ACCORDION_SUMMARY_ID, ACCORDION_DETAILS_ID] },\n ],\n // …and it reads that structure BY INDEX, so the renderer has to hand it one\n // React child per node child (AGL-1237). Without this the summary swallowed\n // the details and every accordion on every published site expanded to reveal\n // an empty panel, with its content rendering unconditionally inside the\n // summary's heading.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'defaultExpanded',\n label: 'Start expanded?',\n description:\n 'If true, the panel is already open when a visitor arrives. The ' +\n 'canvas always shows it open so you can edit inside it.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disableGutters',\n label: 'Disable gutters?',\n description:\n 'If true, the panel keeps its height when expanded instead of ' +\n 'gaining extra margin — the tighter look for a stacked FAQ.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disabled',\n label: 'Disabled?',\n description: 'If true, visitors cannot expand the panel.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const accordionSummarySchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Summary',\n description: 'The clickable header row of an accordion.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n flags: {\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557). The row is a `<button>`, and a list or a link\n // inside one is invalid markup the parser unnests and a control a keyboard\n // cannot reach — the same constraint that made a LINKED header two sibling\n // controls rather than an anchor inside the toggle (AGL-1232).\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'screenId',\n label: 'Header links to',\n description:\n 'Optional. Give the header a destination and the row splits: the ' +\n 'text becomes a link to that page and the chevron beside it ' +\n 'opens the panel. That is the only way a header can do both — a ' +\n 'link inside the toggle button is invalid and unreachable by ' +\n 'keyboard. Leave it empty and the whole row toggles, as before.',\n component: Aglyn.FieldComponentType.SCREEN_SELECT,\n },\n ],\n}\n\nexport const accordionDetailsSchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Details',\n description: 'The region an accordion reveals when expanded.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n}\n\nconst panel = (summary: string, body: string) => ({\n $id: null,\n componentId: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n {\n $id: null,\n componentId: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n props: { children: summary },\n },\n {\n $id: null,\n componentId: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'body2', children: body },\n },\n ],\n },\n ],\n})\n\nexport const accordionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ACCORDION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Accordion',\n pluginId: BUNDLE_ID,\n description: 'One expandable panel with a header and body text',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: accordionSchema.icon,\n data: panel('Accordion header', 'The details revealed when expanded.'),\n },\n {\n $id: generatePresetId(ACCORDION_ID, 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Three stacked question panels, ready to edit',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n nodes: [\n panel('How do I get started?', 'Answer the first question here.'),\n panel('What does it cost?', 'Answer the second question here.'),\n panel('How do I get support?', 'Answer the third question here.'),\n ],\n },\n },\n]\n\nexport default AccordionElement\n"],"names":["Aglyn","mdiChevronDown","mdiUnfoldMoreHorizontal","AppLink","MdiIcon","MuiAccordion","MuiAccordionDetails","MuiAccordionSummary","Box","Link","forwardRef","isValidElement","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","withAuthorHtmlLabel","generatePresetId","ACCORDION_ID","ACCORDION_SUMMARY_ID","ACCORDION_DETAILS_ID","AccordionElement","props","ref","defaultExpanded","children","rest","editorInert","useScreenLink","undefined","expanded","displayName","SUMMARY_ROW_SX","display","alignItems","width","SUMMARY_LABEL_SX","flex","minWidth","px","py","SUMMARY_TOGGLE_SX","childText","node","String","Array","isArray","map","join","AccordionSummaryElement","screenId","html","href","suppressNavigation","expandIcon","path","sx","rowRest","nodeSx","label","trim","content","underline","color","aria-label","AccordionDetailsElement","accordionSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","restrictChildren","LinealDirectiveFlag","LIMIT_TO","components","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","SWITCH","accordionSummarySchema","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","SCREEN_SELECT","accordionDetailsSchema","panel","summary","body","componentId","nodes","variant","accordionPresets","type","NodeType","PRESET","data","direction"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/accordion.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiChevronDown, mdiUnfoldMoreHorizontal } from '@aglyn/shared-data-mdi'\nimport { AppLink, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport MuiAccordion from '@mui/material/Accordion'\nimport MuiAccordionDetails from '@mui/material/AccordionDetails'\nimport MuiAccordionSummary from '@mui/material/AccordionSummary'\nimport Box from '@mui/material/Box'\nimport Link from '@mui/material/Link'\nimport type { SxProps } from '@mui/material/styles'\nimport { forwardRef, isValidElement, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport {\n authorHtmlLabel,\n withAuthorHtmlLabel,\n} from '../utils/author-html-label'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ACCORDION_ID: Aglyn.ComponentId = 'muiAccordion'\nexport const ACCORDION_SUMMARY_ID: Aglyn.ComponentId = 'muiAccordionSummary'\nexport const ACCORDION_DETAILS_ID: Aglyn.ComponentId = 'muiAccordionDetails'\n\nexport interface AccordionElementProps {\n /** Starts open on the published site. */\n defaultExpanded?: boolean\n disabled?: boolean\n /** Removes the extra margin an expanded accordion normally gains. */\n disableGutters?: boolean\n children?: ReactNode\n}\n\n/**\n * Expand/collapse panel (https://mui.com/material-ui/react-accordion/).\n *\n * On the besigner canvas it renders **expanded regardless of\n * `defaultExpanded`**: collapsed details are unreachable for selection\n * and styling, which is the same reason the Drawer expands its contents\n * while selected (AGL-571). Preview and the published site both honour\n * the author's setting and toggle normally, so what ships is never in\n * doubt — only the editing surface differs.\n */\nconst AccordionElement = forwardRef<HTMLDivElement, AccordionElementProps>(\n (props, ref) => {\n const { defaultExpanded, children, ...rest } = props\n const { editorInert } = Aglyn.useScreenLink(undefined)\n if (editorInert) {\n // Controlled-open with no onChange: the canvas is a static surface,\n // clicks there select rather than toggle.\n // MUI types Accordion's children as NonNullable: an accordion with\n // no summary/details is not a renderable shape, and the renderer\n // always supplies the pair.\n return (\n <MuiAccordion\n ref={ref}\n expanded\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n }\n return (\n <MuiAccordion\n ref={ref}\n defaultExpanded={!!defaultExpanded}\n {...rest}\n children={children as NonNullable<ReactNode>}\n />\n )\n },\n)\nAccordionElement.displayName = 'AglynAccordion'\n\nexport interface AccordionSummaryElementProps {\n /**\n * Optional screen the header's label links to (AGL-1232). Setting it\n * SPLITS the row — see {@link AccordionSummaryElement}. A screen id, never\n * a path: the href is resolved from the host routing map at render time,\n * so a slug rename or re-parent cannot break it, and a bare `href` would\n * full-reload the site instead of routing.\n */\n screenId?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed\n * into an attribute — recomposed below so the Styles panel's values are\n * merged rather than replaced (AGL-1240/1284).\n */\n sx?: SxProps\n /**\n * Sanitized emphasis markup for the label, written by the inline editor's\n * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and\n * is what every surface that cannot render markup falls back to.\n */\n html?: string\n children?: ReactNode\n}\n\n/** Label and chevron sit on one line; the label takes the slack. */\nconst SUMMARY_ROW_SX = {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n} as const\n\n/** Matches MuiAccordionSummary's own gutters so the split row lines up. */\nconst SUMMARY_LABEL_SX = {\n flex: 1,\n minWidth: 0,\n px: 2,\n py: 1.5,\n} as const\n\n/**\n * The summary is a `<button>`, and a button takes the browser's own font\n * rather than the page's — so every accordion question on a site rendered in\n * the system face while the answer under it used the theme's (AGL-3497).\n * Only the family is inherited: size and weight stay MUI's, and the node's own\n * styles, applied after this, still win.\n */\nconst SUMMARY_FONT_SX = { fontFamily: 'inherit' } as const\n\n/**\n * The toggle keeps only its chevron: the label is a sibling now, so the\n * summary's own content slot would just add empty width.\n */\nconst SUMMARY_TOGGLE_SX = {\n flex: '0 0 auto',\n '& .MuiAccordionSummary-content': { display: 'none' },\n} as const\n\n/**\n * The visible text of a React children tree, for naming a control that has no\n * text of its own (AGL-2349).\n *\n * It has to walk, not test: what arrives here is the node renderer's output,\n * which wraps the authored string in an `<AglynText>` element inside an\n * array (`libs/aglyn-node-renderer/src/lib/components/leaf.tsx`), so the string\n * is a grandchild at best and never the value itself. Descends `props.children`\n * rather than reading DOM text, because this runs on the React tree BEFORE\n * anything is in the DOM to read. (It also predates AGL-2011, when\n * `<aglyn-text>` was a shadow host whose `innerText` read back empty — that\n * is no longer true, but walking props is still the right tool here.)\n */\nconst childText = (node: ReactNode): string => {\n if (node == null || typeof node === 'boolean') return ''\n if (typeof node === 'string' || typeof node === 'number') return String(node)\n if (Array.isArray(node)) return node.map(childText).join('')\n if (isValidElement(node)) {\n return childText((node.props as { children?: ReactNode })?.children)\n }\n return ''\n}\n\n/**\n * The always-visible header row. Supplies the expand chevron MUI leaves\n * to the caller — without it the control looks like inert text and gives\n * no hint that there is anything to open.\n *\n * ## Why a linked header is a different shape (AGL-1232)\n *\n * `MuiAccordionSummary` renders a `<button>`, and the mobile drawer needs\n * Product/Solutions to reach their jump pages as well as open. An anchor\n * inside that button is invalid nesting — browsers unnest it, and nested\n * interactive content is unusable with a screen reader or a keyboard — so\n * the row cannot both toggle and navigate while it is one control.\n *\n * With a `screenId` set the row therefore splits in two: the label becomes\n * a real anchor, and a chevron-only `MuiAccordionSummary` beside it becomes\n * the toggle. Two siblings, each independently focusable, no nesting. The\n * summary keeps driving the panel through MUI's accordion context, so\n * `aria-expanded` and the transition are unchanged.\n *\n * With no `screenId` — every accordion authored before this — the output is\n * byte-for-byte what it always was: one `MuiAccordionSummary`, whole row\n * toggles.\n */\nexport const AccordionSummaryElement = forwardRef<\n HTMLDivElement,\n AccordionSummaryElementProps\n>((props, ref) => {\n const { screenId, html, ...rest } = props\n // Resolution is the routing map's job, exactly as Screen Link does it;\n // an unpublished or deleted target yields no href.\n const { href, suppressNavigation } = Aglyn.useScreenLink(screenId)\n\n if (!screenId) {\n const { sx: plainSx, ...plainRest } = rest\n const plainNodeSx = Array.isArray(plainSx) ? plainSx : plainSx ? [plainSx] : []\n return (\n <MuiAccordionSummary\n ref={ref}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n {...withAuthorHtmlLabel(plainRest, html)}\n sx={[SUMMARY_FONT_SX, ...plainNodeSx]}\n />\n )\n }\n\n const { children, sx, ...rowRest } = rest\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n // The chevron button's only content is an icon, so it needs a name of its\n // own — \"Product\" the link and \"Product\" the toggle are two controls.\n //\n // This USED to be `typeof children === 'string'`, which is never true in\n // production (AGL-2349). A summary's label is authored through\n // FIELD_TEXT_CONTENT, and the node renderer does not pass that string down as\n // `children` — `Leaf` lifts it out as `textContent` and renders\n // `<Component>{children}<AglynText>{textContent}</…></Component>`,\n // so `children` is always an ARRAY holding an element. The string branch was\n // reachable only from a spec that handed the component a bare string, so\n // every published split summary named its chevron \"Toggle section\".\n //\n // Read from `children` rather than from the formatted label below, because\n // a formatted one is markup handed to `dangerouslySetInnerHTML` and has no\n // React children to walk. `children` carries the same words either way.\n const label = childText(children).trim()\n const content = authorHtmlLabel(html) ?? children\n return (\n <Box ref={ref} {...rowRest} sx={[SUMMARY_ROW_SX, ...nodeSx]}>\n {href && !suppressNavigation ? (\n <AppLink href={href} underline=\"hover\" sx={SUMMARY_LABEL_SX}>\n {content}\n </AppLink>\n ) : (\n // Unresolved target, or a surface that must not navigate (canvas,\n // preview): same two-part shape, so the editor never shows a row\n // that behaves differently from the one that ships.\n //\n // Same ELEMENT too, not just the same box (AGL-1268). `href` comes\n // from the screens map, which differs between the render that\n // produced an ISR page and the render that hydrates it — a screen\n // published or unpublished in between. Switching `<a>` to `<span>`\n // on that would remount the subtree at hydration; an `<a>` with no\n // href is HTML's placeholder link, so the element is stable and the\n // row is still inert. Link Container settled on this shape first.\n <Link underline=\"none\" color=\"inherit\" sx={SUMMARY_LABEL_SX}>\n {content}\n </Link>\n )}\n <MuiAccordionSummary\n aria-label={label ? `Toggle ${label}` : 'Toggle section'}\n expandIcon={<MdiIcon path={mdiChevronDown.path} />}\n sx={SUMMARY_TOGGLE_SX}\n />\n </Box>\n )\n})\nAccordionSummaryElement.displayName = 'AglynAccordionSummary'\n\nexport const AccordionDetailsElement = forwardRef<\n HTMLDivElement,\n { children?: ReactNode }\n>((props, ref) => <MuiAccordionDetails ref={ref} {...props} />)\nAccordionDetailsElement.displayName = 'AglynAccordionDetails'\n\nexport const accordionSchema: Aglyn.ComponentSchema<AccordionElementProps> = {\n $id: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion',\n description:\n 'Header that expands to reveal its details — FAQs, long forms, ' +\n 'anything progressive.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n // Structure is fixed by MUI: summary first, details second.\n restrictChildren: [\n Aglyn.LinealDirectiveFlag.LIMIT_TO,\n { components: [ACCORDION_SUMMARY_ID, ACCORDION_DETAILS_ID] },\n ],\n // …and it reads that structure BY INDEX, so the renderer has to hand it one\n // React child per node child (AGL-1237). Without this the summary swallowed\n // the details and every accordion on every published site expanded to reveal\n // an empty panel, with its content rendering unconditionally inside the\n // summary's heading.\n flags: { positionalChildren: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'defaultExpanded',\n label: 'Start expanded?',\n description:\n 'If true, the panel is already open when a visitor arrives. The ' +\n 'canvas always shows it open so you can edit inside it.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disableGutters',\n label: 'Disable gutters?',\n description:\n 'If true, the panel keeps its height when expanded instead of ' +\n 'gaining extra margin — the tighter look for a stacked FAQ.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disabled',\n label: 'Disabled?',\n description: 'If true, visitors cannot expand the panel.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const accordionSummarySchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Summary',\n description: 'The clickable header row of an accordion.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n flags: {\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n // Emphasis only (AGL-2557). The row is a `<button>`, and a list or a link\n // inside one is invalid markup the parser unnests and a control a keyboard\n // cannot reach — the same constraint that made a LINKED header two sibling\n // controls rather than an anchor inside the toggle (AGL-1232).\n richTextCommands: [Aglyn.RICH_TEXT_COMMANDS.EMPHASIS],\n attributes: [\n FIELD_TEXT_CONTENT,\n {\n name: 'screenId',\n label: 'Header links to',\n description:\n 'Optional. Give the header a destination and the row splits: the ' +\n 'text becomes a link to that page and the chevron beside it ' +\n 'opens the panel. That is the only way a header can do both — a ' +\n 'link inside the toggle button is invalid and unreachable by ' +\n 'keyboard. Leave it empty and the whole row toggles, as before.',\n component: Aglyn.FieldComponentType.SCREEN_SELECT,\n },\n ],\n}\n\nexport const accordionDetailsSchema: Aglyn.ComponentSchema = {\n $id: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Accordion Details',\n description: 'The region an accordion reveals when expanded.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#2196f3' } },\n}\n\nconst panel = (summary: string, body: string) => ({\n $id: null,\n componentId: ACCORDION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [\n {\n $id: null,\n componentId: ACCORDION_SUMMARY_ID,\n pluginId: BUNDLE_ID,\n props: { children: summary },\n },\n {\n $id: null,\n componentId: ACCORDION_DETAILS_ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: { variant: 'body2', children: body },\n },\n ],\n },\n ],\n})\n\nexport const accordionPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ACCORDION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Accordion',\n pluginId: BUNDLE_ID,\n description: 'One expandable panel with a header and body text',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: accordionSchema.icon,\n data: panel('Accordion header', 'The details revealed when expanded.'),\n },\n {\n $id: generatePresetId(ACCORDION_ID, 'faq'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'FAQ',\n pluginId: BUNDLE_ID,\n description: 'Three stacked question panels, ready to edit',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiUnfoldMoreHorizontal.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'column' },\n nodes: [\n panel('How do I get started?', 'Answer the first question here.'),\n panel('What does it cost?', 'Answer the second question here.'),\n panel('How do I get support?', 'Answer the third question here.'),\n ],\n },\n },\n]\n\nexport default AccordionElement\n"],"names":["Aglyn","mdiChevronDown","mdiUnfoldMoreHorizontal","AppLink","MdiIcon","MuiAccordion","MuiAccordionDetails","MuiAccordionSummary","Box","Link","forwardRef","isValidElement","BUNDLE_ID","FIELD_TEXT_CONTENT","authorHtmlLabel","withAuthorHtmlLabel","generatePresetId","ACCORDION_ID","ACCORDION_SUMMARY_ID","ACCORDION_DETAILS_ID","AccordionElement","props","ref","defaultExpanded","children","rest","editorInert","useScreenLink","undefined","expanded","displayName","SUMMARY_ROW_SX","display","alignItems","width","SUMMARY_LABEL_SX","flex","minWidth","px","py","SUMMARY_FONT_SX","fontFamily","SUMMARY_TOGGLE_SX","childText","node","String","Array","isArray","map","join","AccordionSummaryElement","screenId","html","href","suppressNavigation","sx","plainSx","plainRest","plainNodeSx","expandIcon","path","rowRest","nodeSx","label","trim","content","underline","color","aria-label","AccordionDetailsElement","accordionSchema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","restrictChildren","LinealDirectiveFlag","LIMIT_TO","components","flags","positionalChildren","FEATURE_FLAG","ENABLED","attributes","name","component","FieldComponentType","SWITCH","accordionSummarySchema","textEditable","richTextEditable","richTextCommands","RICH_TEXT_COMMANDS","EMPHASIS","SCREEN_SELECT","accordionDetailsSchema","panel","summary","body","componentId","nodes","variant","accordionPresets","type","NodeType","PRESET","data","direction"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type CollectionEntriesProps } from './collection-entries';
|
|
18
|
+
/**
|
|
19
|
+
* Search (AGL-1516): a toolbar search box filters the rendered entry clones
|
|
20
|
+
* client-side against the server-stamped `searchIndex` — fuzzy, via the same
|
|
21
|
+
* Fuse the icon picker uses. The clones arrive as `children` in stamp order,
|
|
22
|
+
* one group of template roots per entry, so group N of the children IS entry
|
|
23
|
+
* N of the index. Matches keep the list's own order rather than Fuse's score
|
|
24
|
+
* order: a blog list is chronological, and search narrows it, it does not
|
|
25
|
+
* reshuffle it. In the besigner the field renders as an inert affordance (the
|
|
26
|
+
* template renders once with literal tokens and there is no index to search),
|
|
27
|
+
* matching how Category Pills go inert on editing surfaces.
|
|
28
|
+
*/
|
|
29
|
+
export declare const SearchingEntries: import("react").ForwardRefExoticComponent<Omit<CollectionEntriesProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
* Copyright 2026 Aglyn LLC
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
|
+
import MuiStack from "@mui/material/Stack";
|
|
21
|
+
import Typography from "@mui/material/Typography";
|
|
22
|
+
import { Children, forwardRef, useContext, useState } from "react";
|
|
23
|
+
// One coercion for every authored count in this bundle (AGL-1457) — number
|
|
24
|
+
// fields round-trip as strings, and a second parser here would drift.
|
|
25
|
+
import { toCount } from "../utils/to-count.js";
|
|
26
|
+
import { ENTRIES_SEARCH_ATTRIBUTE } from "./collection-entries.js";
|
|
27
|
+
import { SearchField, SuggestSearchBox, useEntryFuse } from "./collection-search-box.js";
|
|
28
|
+
/**
|
|
29
|
+
* The Collection Entries block with its search box on (AGL-1516, AGL-1525).
|
|
30
|
+
*
|
|
31
|
+
* Its own module, loaded only by a block whose `search` is on (AGL-3438): the
|
|
32
|
+
* box draws MUI's InputBase, TextareaAutosize and Chip, about 22 KB the
|
|
33
|
+
* entries block otherwise has no use for, and most blocks — aglyn.com/press's
|
|
34
|
+
* among them — never turn search on. `collection-entries.tsx` renders the
|
|
35
|
+
* plain block itself and reaches for this one through `loadEntriesSearch`.
|
|
36
|
+
*/ /**
|
|
37
|
+
* How much of the collection an entries block's search could actually see
|
|
38
|
+
* (AGL-1516, AGL-2569) — `truncated` says the rendered window is smaller
|
|
39
|
+
* than the set, `paginated` says the missing part lives on other pages of
|
|
40
|
+
* this same list rather than merely outside the block.
|
|
41
|
+
*
|
|
42
|
+
* The test is whether the window is SMALLER THAN THE SET, which only the
|
|
43
|
+
* server knows — `perPage` is the wrong proxy for it in both directions. A
|
|
44
|
+
* block truncated by `entriesLimit` or by the 100-entry cap has no `perPage`
|
|
45
|
+
* at all and would claim a global miss over 6 of 40 posts; a block whose
|
|
46
|
+
* `perPage` exceeds its entry count is a single complete page and would
|
|
47
|
+
* blame pages that do not exist. `searchTotal` is stamped with `searchIndex`,
|
|
48
|
+
* so the two are always in step.
|
|
49
|
+
*
|
|
50
|
+
* Absent — a page cached before that stamp shipped — falls back to the old
|
|
51
|
+
* `perPage` reading rather than guessing "complete": understating the scope
|
|
52
|
+
* of a search is the safe direction to be wrong in.
|
|
53
|
+
*
|
|
54
|
+
* `searchCapped` is the second way a block can fail to hold the set, and the
|
|
55
|
+
* invisible one: `searchTotal` counts the entries the server READ, and that
|
|
56
|
+
* read is bounded. Past the bound `shown === searchTotal` is satisfied by a
|
|
57
|
+
* block holding 100 of 400 posts, and the wording that claims to have looked
|
|
58
|
+
* everywhere is exactly the branch it would take.
|
|
59
|
+
*
|
|
60
|
+
* Both the miss and the hit disclose off this one answer, so the two can
|
|
61
|
+
* never disagree about the same block.
|
|
62
|
+
*/ const searchScope = ({ perPage, shown, searchTotal, searchCapped })=>{
|
|
63
|
+
var _toCount;
|
|
64
|
+
const paginated = ((_toCount = toCount(perPage, 0)) != null ? _toCount : 0) > 0;
|
|
65
|
+
return {
|
|
66
|
+
paginated,
|
|
67
|
+
truncated: typeof searchTotal === 'number' ? shown < searchTotal || Boolean(searchCapped) : paginated || Boolean(searchCapped)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Search (AGL-1516): a toolbar search box filters the rendered entry clones
|
|
72
|
+
* client-side against the server-stamped `searchIndex` — fuzzy, via the same
|
|
73
|
+
* Fuse the icon picker uses. The clones arrive as `children` in stamp order,
|
|
74
|
+
* one group of template roots per entry, so group N of the children IS entry
|
|
75
|
+
* N of the index. Matches keep the list's own order rather than Fuse's score
|
|
76
|
+
* order: a blog list is chronological, and search narrows it, it does not
|
|
77
|
+
* reshuffle it. In the besigner the field renders as an inert affordance (the
|
|
78
|
+
* template renders once with literal tokens and there is no index to search),
|
|
79
|
+
* matching how Category Pills go inert on editing surfaces.
|
|
80
|
+
*/ export const SearchingEntries = /*#__PURE__*/ forwardRef(// collectionSlug/entriesLimit/filter*/search* are compose-time or
|
|
81
|
+
// search-only attributes: strip so they never hit the DOM.
|
|
82
|
+
(_0, _1)=>{
|
|
83
|
+
let _ref = [
|
|
84
|
+
_0,
|
|
85
|
+
_1
|
|
86
|
+
], [_ref1, ..._rest] = _ref, { collectionSlug, entriesLimit, filterCategory, filterTag, perPage, page, firstPageOnly, search, searchMode, searchPlaceholder, searchIndex, searchTotal, searchCapped, children } = _ref1, props = _object_without_properties_loose(_ref1, [
|
|
87
|
+
"collectionSlug",
|
|
88
|
+
"entriesLimit",
|
|
89
|
+
"filterCategory",
|
|
90
|
+
"filterTag",
|
|
91
|
+
"perPage",
|
|
92
|
+
"page",
|
|
93
|
+
"firstPageOnly",
|
|
94
|
+
"search",
|
|
95
|
+
"searchMode",
|
|
96
|
+
"searchPlaceholder",
|
|
97
|
+
"searchIndex",
|
|
98
|
+
"searchTotal",
|
|
99
|
+
"searchCapped",
|
|
100
|
+
"children"
|
|
101
|
+
]), [ref] = _rest;
|
|
102
|
+
const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext);
|
|
103
|
+
const items = searchIndex;
|
|
104
|
+
/** `filter` mode only — `suggest` owns its own query (AGL-1516). */ const [query, setQuery] = useState('');
|
|
105
|
+
const { fuzzy, requestFuzzy } = useEntryFuse(items);
|
|
106
|
+
const childArray = Children.toArray(children);
|
|
107
|
+
// Template roots per entry. Every entry clones the same template, so the
|
|
108
|
+
// children divide evenly; anything else means the children are not the
|
|
109
|
+
// stamped clones, and filtering blind would hide the wrong cards —
|
|
110
|
+
// fail open and render everything.
|
|
111
|
+
const groupSize = (items == null ? void 0 : items.length) && childArray.length % items.length === 0 ? childArray.length / items.length : 0;
|
|
112
|
+
// `suggest` draws its rows from the INDEX and never touches the clones,
|
|
113
|
+
// so the clone-alignment precondition is a `filter` precondition only.
|
|
114
|
+
// A block whose children do not divide evenly still suggests correctly;
|
|
115
|
+
// requiring `groupSize` here would silently kill the panel on exactly
|
|
116
|
+
// the templates that made the fail-open necessary.
|
|
117
|
+
const mode = searchMode === 'suggest' ? 'suggest' : 'filter';
|
|
118
|
+
// Whether there is an index to search, NOT whether the matcher has loaded:
|
|
119
|
+
// the field has to be there for the keystroke that loads it.
|
|
120
|
+
const live = !suppressNavigation && Boolean(items == null ? void 0 : items.length) && (mode === 'suggest' || groupSize > 0);
|
|
121
|
+
const trimmed = query.trim();
|
|
122
|
+
let visible = children;
|
|
123
|
+
/**
|
|
124
|
+
* The one line under the results that says what the search covered —
|
|
125
|
+
* either a miss scoped to the set that came back empty, or a hit scoped
|
|
126
|
+
* to the window it was drawn from. Never both.
|
|
127
|
+
*/ let scopeNote = null;
|
|
128
|
+
if (mode === 'suggest') {
|
|
129
|
+
// The grid is deliberately untouched (Figma 496:1218): the panel is an
|
|
130
|
+
// overlay on a page the reader is still reading, not a filter.
|
|
131
|
+
visible = children;
|
|
132
|
+
} else if (live && trimmed && fuzzy) {
|
|
133
|
+
var _ref2;
|
|
134
|
+
const matched = new Set(fuzzy.search(trimmed).map((result)=>result.refIndex));
|
|
135
|
+
visible = childArray.filter((_, index)=>matched.has(Math.floor(index / groupSize)));
|
|
136
|
+
// HONEST scope (AGL-1516, AGL-2569): this block holds whatever window
|
|
137
|
+
// the expansion gave it, and pretending the search was global would
|
|
138
|
+
// turn every miss into a false "this post does not exist" — and every
|
|
139
|
+
// hit into a result set that looks like the whole answer.
|
|
140
|
+
const shown = (_ref2 = items == null ? void 0 : items.length) != null ? _ref2 : 0;
|
|
141
|
+
const { paginated, truncated } = searchScope({
|
|
142
|
+
perPage,
|
|
143
|
+
shown,
|
|
144
|
+
searchTotal,
|
|
145
|
+
searchCapped
|
|
146
|
+
});
|
|
147
|
+
if (!visible.length) {
|
|
148
|
+
scopeNote = /*#__PURE__*/ _jsx(Typography, {
|
|
149
|
+
variant: "body2",
|
|
150
|
+
sx: {
|
|
151
|
+
color: 'text.secondary'
|
|
152
|
+
},
|
|
153
|
+
children: !truncated ? `No matches for “${trimmed}”.` : paginated ? `No matches for “${trimmed}” on this page — other pages ` + 'are not searched.' : `No matches for “${trimmed}” in the ${shown} entries ` + 'shown here — the rest of the collection is not searched.'
|
|
154
|
+
});
|
|
155
|
+
} else if (truncated) {
|
|
156
|
+
// The hit half of the same honesty (AGL-2569). Matches are drawn from
|
|
157
|
+
// the same window the miss apologizes for, they carry no pager of
|
|
158
|
+
// their own, and nothing else on screen says the pages behind this
|
|
159
|
+
// one went unread — so a list that happens to be partial reads as
|
|
160
|
+
// complete. A block that holds the whole collection says nothing
|
|
161
|
+
// here: a disclaimer on a complete answer is noise, and every small
|
|
162
|
+
// site would carry it.
|
|
163
|
+
//
|
|
164
|
+
// No route out to `/search?q=` (AGL-88): the suggestion panel links
|
|
165
|
+
// there because it is the panel's only possible answer, but this
|
|
166
|
+
// mode has a working list under the field and that page does not
|
|
167
|
+
// exist yet. Trading a silent partial answer for a dead link is not
|
|
168
|
+
// the trade.
|
|
169
|
+
scopeNote = /*#__PURE__*/ _jsx(Typography, {
|
|
170
|
+
variant: "body2",
|
|
171
|
+
sx: {
|
|
172
|
+
color: 'text.secondary'
|
|
173
|
+
},
|
|
174
|
+
children: paginated ? 'Showing matches on this page — other pages are not searched.' : `Showing matches in the ${shown} entries here — the rest of ` + 'the collection is not searched.'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// The field renders when there is something to search, and as an inert
|
|
179
|
+
// affordance on editing surfaces so the author sees what they enabled.
|
|
180
|
+
// A live surface with nothing stamped (unknown collection, zero entries)
|
|
181
|
+
// renders no field at all — a search box over nothing is a lie.
|
|
182
|
+
const showField = suppressNavigation || live;
|
|
183
|
+
const field = !showField ? null : mode === 'suggest' ? // Rows in Fuse's SCORE order, the opposite of what `filter` does and
|
|
184
|
+
// deliberately: filtering narrows a chronological feed, so reshuffling
|
|
185
|
+
// it would be a second, unasked-for change to what the reader is
|
|
186
|
+
// looking at. A suggestion list is not a feed — it is an answer to a
|
|
187
|
+
// question, and the best answer belongs first.
|
|
188
|
+
/*#__PURE__*/ _jsx(SuggestSearchBox, _extends({
|
|
189
|
+
fuzzy: fuzzy,
|
|
190
|
+
requestFuzzy: requestFuzzy
|
|
191
|
+
}, items ? {
|
|
192
|
+
items
|
|
193
|
+
} : {}, searchPlaceholder === undefined ? {} : {
|
|
194
|
+
placeholder: searchPlaceholder
|
|
195
|
+
}, suppressNavigation ? {
|
|
196
|
+
inert: true
|
|
197
|
+
} : {}, {
|
|
198
|
+
// This block searched the entries IT rendered — a page window, an
|
|
199
|
+
// `entriesLimit` slice, or a bounded read. Never "this collection".
|
|
200
|
+
emptyText: (text)=>`No matches for “${text}” on this page.`
|
|
201
|
+
})) : /*#__PURE__*/ _jsx(SearchField, _extends({
|
|
202
|
+
value: query,
|
|
203
|
+
landmark: true
|
|
204
|
+
}, searchPlaceholder === undefined ? {} : {
|
|
205
|
+
placeholder: searchPlaceholder
|
|
206
|
+
}, suppressNavigation ? {
|
|
207
|
+
inert: true
|
|
208
|
+
} : {}, {
|
|
209
|
+
onQuery: (next)=>{
|
|
210
|
+
requestFuzzy();
|
|
211
|
+
setQuery(next);
|
|
212
|
+
}
|
|
213
|
+
}));
|
|
214
|
+
return /*#__PURE__*/ _jsxs(MuiStack, _extends({
|
|
215
|
+
ref: ref,
|
|
216
|
+
spacing: 4
|
|
217
|
+
}, props, {
|
|
218
|
+
[ENTRIES_SEARCH_ATTRIBUTE]: '',
|
|
219
|
+
children: [
|
|
220
|
+
field,
|
|
221
|
+
visible,
|
|
222
|
+
scopeNote
|
|
223
|
+
]
|
|
224
|
+
}));
|
|
225
|
+
});
|
|
226
|
+
SearchingEntries.displayName = 'AglynCollectionEntriesSearch';
|
|
227
|
+
|
|
228
|
+
//# sourceMappingURL=collection-entries-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/collection-entries-search.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 MuiStack from '@mui/material/Stack'\nimport Typography from '@mui/material/Typography'\nimport type { ReactNode } from 'react'\nimport { Children, forwardRef, useContext, useState } from 'react'\n// One coercion for every authored count in this bundle (AGL-1457) — number\n// fields round-trip as strings, and a second parser here would drift.\nimport { toCount } from '../utils/to-count'\nimport {\n ENTRIES_SEARCH_ATTRIBUTE,\n type CollectionEntriesProps,\n type CollectionSearchMode,\n} from './collection-entries'\nimport {\n SearchField,\n SuggestSearchBox,\n useEntryFuse,\n} from './collection-search-box'\n\n/**\n * The Collection Entries block with its search box on (AGL-1516, AGL-1525).\n *\n * Its own module, loaded only by a block whose `search` is on (AGL-3438): the\n * box draws MUI's InputBase, TextareaAutosize and Chip, about 22 KB the\n * entries block otherwise has no use for, and most blocks — aglyn.com/press's\n * among them — never turn search on. `collection-entries.tsx` renders the\n * plain block itself and reaches for this one through `loadEntriesSearch`.\n */\n\n/**\n * How much of the collection an entries block's search could actually see\n * (AGL-1516, AGL-2569) — `truncated` says the rendered window is smaller\n * than the set, `paginated` says the missing part lives on other pages of\n * this same list rather than merely outside the block.\n *\n * The test is whether the window is SMALLER THAN THE SET, which only the\n * server knows — `perPage` is the wrong proxy for it in both directions. A\n * block truncated by `entriesLimit` or by the 100-entry cap has no `perPage`\n * at all and would claim a global miss over 6 of 40 posts; a block whose\n * `perPage` exceeds its entry count is a single complete page and would\n * blame pages that do not exist. `searchTotal` is stamped with `searchIndex`,\n * so the two are always in step.\n *\n * Absent — a page cached before that stamp shipped — falls back to the old\n * `perPage` reading rather than guessing \"complete\": understating the scope\n * of a search is the safe direction to be wrong in.\n *\n * `searchCapped` is the second way a block can fail to hold the set, and the\n * invisible one: `searchTotal` counts the entries the server READ, and that\n * read is bounded. Past the bound `shown === searchTotal` is satisfied by a\n * block holding 100 of 400 posts, and the wording that claims to have looked\n * everywhere is exactly the branch it would take.\n *\n * Both the miss and the hit disclose off this one answer, so the two can\n * never disagree about the same block.\n */\nconst searchScope = ({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n}: {\n perPage?: CollectionEntriesProps['perPage']\n /** Entries this block searched — the length of its stamped index. */\n shown: number\n searchTotal?: number\n searchCapped?: boolean\n}): { paginated: boolean; truncated: boolean } => {\n const paginated = (toCount(perPage, 0) ?? 0) > 0\n return {\n paginated,\n truncated:\n typeof searchTotal === 'number'\n ? shown < searchTotal || Boolean(searchCapped)\n : paginated || Boolean(searchCapped),\n }\n}\n\n/**\n * Search (AGL-1516): a toolbar search box filters the rendered entry clones\n * client-side against the server-stamped `searchIndex` — fuzzy, via the same\n * Fuse the icon picker uses. The clones arrive as `children` in stamp order,\n * one group of template roots per entry, so group N of the children IS entry\n * N of the index. Matches keep the list's own order rather than Fuse's score\n * order: a blog list is chronological, and search narrows it, it does not\n * reshuffle it. In the besigner the field renders as an inert affordance (the\n * template renders once with literal tokens and there is no index to search),\n * matching how Category Pills go inert on editing surfaces.\n */\nexport const SearchingEntries = forwardRef<\n HTMLDivElement,\n CollectionEntriesProps\n>(\n // collectionSlug/entriesLimit/filter*/search* are compose-time or\n // search-only attributes: strip so they never hit the DOM.\n (\n {\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 ...props\n },\n ref,\n ) => {\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n const items = searchIndex\n /** `filter` mode only — `suggest` owns its own query (AGL-1516). */\n const [query, setQuery] = useState('')\n const { fuzzy, requestFuzzy } = useEntryFuse(items)\n const childArray = Children.toArray(children)\n // Template roots per entry. Every entry clones the same template, so the\n // children divide evenly; anything else means the children are not the\n // stamped clones, and filtering blind would hide the wrong cards —\n // fail open and render everything.\n const groupSize =\n items?.length && childArray.length % items.length === 0\n ? childArray.length / items.length\n : 0\n // `suggest` draws its rows from the INDEX and never touches the clones,\n // so the clone-alignment precondition is a `filter` precondition only.\n // A block whose children do not divide evenly still suggests correctly;\n // requiring `groupSize` here would silently kill the panel on exactly\n // the templates that made the fail-open necessary.\n const mode: CollectionSearchMode =\n searchMode === 'suggest' ? 'suggest' : 'filter'\n // Whether there is an index to search, NOT whether the matcher has loaded:\n // the field has to be there for the keystroke that loads it.\n const live =\n !suppressNavigation &&\n Boolean(items?.length) &&\n (mode === 'suggest' || groupSize > 0)\n const trimmed = query.trim()\n let visible: ReactNode[] | ReactNode = children\n /**\n * The one line under the results that says what the search covered —\n * either a miss scoped to the set that came back empty, or a hit scoped\n * to the window it was drawn from. Never both.\n */\n let scopeNote: ReactNode = null\n if (mode === 'suggest') {\n // The grid is deliberately untouched (Figma 496:1218): the panel is an\n // overlay on a page the reader is still reading, not a filter.\n visible = children\n } else if (live && trimmed && fuzzy) {\n const matched = new Set(\n fuzzy.search(trimmed).map((result) => result.refIndex),\n )\n visible = childArray.filter((_, index) =>\n matched.has(Math.floor(index / groupSize)),\n )\n // HONEST scope (AGL-1516, AGL-2569): this block holds whatever window\n // the expansion gave it, and pretending the search was global would\n // turn every miss into a false \"this post does not exist\" — and every\n // hit into a result set that looks like the whole answer.\n const shown = items?.length ?? 0\n const { paginated, truncated } = searchScope({\n perPage,\n shown,\n searchTotal,\n searchCapped,\n })\n if (!(visible as ReactNode[]).length) {\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {!truncated\n ? `No matches for “${trimmed}”.`\n : paginated\n ? `No matches for “${trimmed}” on this page — other pages ` +\n 'are not searched.'\n : `No matches for “${trimmed}” in the ${shown} entries ` +\n 'shown here — the rest of the collection is not searched.'}\n </Typography>\n )\n } else if (truncated) {\n // The hit half of the same honesty (AGL-2569). Matches are drawn from\n // the same window the miss apologizes for, they carry no pager of\n // their own, and nothing else on screen says the pages behind this\n // one went unread — so a list that happens to be partial reads as\n // complete. A block that holds the whole collection says nothing\n // here: a disclaimer on a complete answer is noise, and every small\n // site would carry it.\n //\n // No route out to `/search?q=` (AGL-88): the suggestion panel links\n // there because it is the panel's only possible answer, but this\n // mode has a working list under the field and that page does not\n // exist yet. Trading a silent partial answer for a dead link is not\n // the trade.\n scopeNote = (\n <Typography variant=\"body2\" sx={{ color: 'text.secondary' }}>\n {paginated\n ? 'Showing matches on this page — other pages are not searched.'\n : `Showing matches in the ${shown} entries here — the rest of ` +\n 'the collection is not searched.'}\n </Typography>\n )\n }\n }\n // The field renders when there is something to search, and as an inert\n // affordance on editing surfaces so the author sees what they enabled.\n // A live surface with nothing stamped (unknown collection, zero entries)\n // renders no field at all — a search box over nothing is a lie.\n const showField = suppressNavigation || live\n const field = !showField ? null : mode === 'suggest' ? (\n // Rows in Fuse's SCORE order, the opposite of what `filter` does and\n // deliberately: filtering narrows a chronological feed, so reshuffling\n // it would be a second, unasked-for change to what the reader is\n // looking at. A suggestion list is not a feed — it is an answer to a\n // question, and the best answer belongs first.\n <SuggestSearchBox\n fuzzy={fuzzy}\n requestFuzzy={requestFuzzy}\n {...(items ? { items } : {})}\n {...(searchPlaceholder === undefined\n ? {}\n : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n // This block searched the entries IT rendered — a page window, an\n // `entriesLimit` slice, or a bounded read. Never \"this collection\".\n emptyText={(text) => `No matches for “${text}” on this page.`}\n />\n ) : (\n <SearchField\n value={query}\n landmark\n {...(searchPlaceholder === undefined ? {} : { placeholder: searchPlaceholder })}\n {...(suppressNavigation ? { inert: true } : {})}\n onQuery={(next) => {\n requestFuzzy()\n setQuery(next)\n }}\n />\n )\n return (\n <MuiStack\n ref={ref}\n spacing={4}\n {...props}\n // What the browser looks for before it hydrates the page, so this\n // module is fetched alongside the element rather than after it\n // (`prepareEntriesSearch`).\n {...{ [ENTRIES_SEARCH_ATTRIBUTE]: '' }}\n >\n {field}\n {visible}\n {scopeNote}\n </MuiStack>\n )\n },\n)\nSearchingEntries.displayName = 'AglynCollectionEntriesSearch'\n"],"names":["Aglyn","MuiStack","Typography","Children","forwardRef","useContext","useState","toCount","ENTRIES_SEARCH_ATTRIBUTE","SearchField","SuggestSearchBox","useEntryFuse","searchScope","perPage","shown","searchTotal","searchCapped","paginated","truncated","Boolean","SearchingEntries","collectionSlug","entriesLimit","filterCategory","filterTag","page","firstPageOnly","search","searchMode","searchPlaceholder","searchIndex","children","props","ref","suppressNavigation","ScreenLinkContext","items","query","setQuery","fuzzy","requestFuzzy","childArray","toArray","groupSize","length","mode","live","trimmed","trim","visible","scopeNote","matched","Set","map","result","refIndex","filter","_","index","has","Math","floor","variant","sx","color","showField","field","undefined","placeholder","inert","emptyText","text","value","landmark","onQuery","next","spacing","displayName"],"mappings":"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"}
|
|
@@ -114,22 +114,57 @@ export declare const COLLECTION_SEARCH_MODE_OPTIONS: ReadonlyArray<{
|
|
|
114
114
|
value: CollectionSearchMode;
|
|
115
115
|
label: string;
|
|
116
116
|
}>;
|
|
117
|
+
/** The searching variant's module (AGL-3438). */
|
|
118
|
+
type EntriesSearchModule = typeof import('./collection-entries-search');
|
|
119
|
+
/**
|
|
120
|
+
* What a searching block stamps on its root. The browser looks for it before
|
|
121
|
+
* the page hydrates (`prepareEntriesSearch`); nothing styles or targets it.
|
|
122
|
+
*/
|
|
123
|
+
export declare const ENTRIES_SEARCH_ATTRIBUTE = "data-aglyn-entries-search";
|
|
124
|
+
/**
|
|
125
|
+
* The searching variant, fetched once per visit however many blocks ask. A
|
|
126
|
+
* failed fetch (a deploy mid-visit) is forgotten rather than cached, so the
|
|
127
|
+
* next render that asks tries again.
|
|
128
|
+
*/
|
|
129
|
+
export declare const loadEntriesSearch: () => Promise<EntriesSearchModule>;
|
|
130
|
+
/**
|
|
131
|
+
* Loads the searching variant BEFORE the element renders, wherever a render
|
|
132
|
+
* that waited for it would cost something (AGL-3438). The plugin calls it as
|
|
133
|
+
* the element registers (`prepare` in `plugin.ts`), and awaits it.
|
|
134
|
+
*
|
|
135
|
+
* - **On the server, always.** A render that suspended there would hold the
|
|
136
|
+
* whole streamed shell for the fetch — and a first, cold one would do it
|
|
137
|
+
* on a page the ISR cache then keeps.
|
|
138
|
+
* - **Where the whole library is loading** (`everything`): the console and
|
|
139
|
+
* the besigner, where an author can turn search on at any moment.
|
|
140
|
+
* - **In a browser about to hydrate a searching block**, which the server
|
|
141
|
+
* HTML says by carrying {@link ENTRIES_SEARCH_ATTRIBUTE}. The fetch then
|
|
142
|
+
* rides along with the element's own instead of starting when hydration
|
|
143
|
+
* reaches the block.
|
|
144
|
+
*
|
|
145
|
+
* Anywhere else nothing is fetched: a page whose blocks have search off —
|
|
146
|
+
* most of them — never downloads the box. A searching block that renders
|
|
147
|
+
* without it (a client-side navigation onto one) suspends until it lands,
|
|
148
|
+
* which holds the navigation, never a half-drawn list.
|
|
149
|
+
*/
|
|
150
|
+
export declare const prepareEntriesSearch: (everything: boolean) => Promise<unknown> | undefined;
|
|
117
151
|
/**
|
|
118
152
|
* Repeats its children once per published entry of a content collection
|
|
119
153
|
* (AGL-551) — the collections sibling of the dataset repeatable. The tenant
|
|
120
154
|
* expands it at compose time with `{{entry.*}}` tokens; in the besigner the
|
|
121
155
|
* template renders once with literal tokens, matching the repeatable UX.
|
|
122
156
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
157
|
+
* With `search` on (AGL-1516) the block is `SearchingEntries`, from its own
|
|
158
|
+
* module (AGL-3438). Off — the default, and most blocks — it is the plain
|
|
159
|
+
* stack below, and the search box's MUI input never reaches the page.
|
|
160
|
+
*
|
|
161
|
+
* There is NO Suspense boundary around the searching variant, on purpose. A
|
|
162
|
+
* boundary would have to be in the server's tree too, and a client render
|
|
163
|
+
* that suspended inside it would show its fallback: a list without its box,
|
|
164
|
+
* then the box pushing it down. Without one, a render that has to wait holds
|
|
165
|
+
* whatever is above it — the hydration of the page (whose server HTML stays
|
|
166
|
+
* exactly as it is), or a navigation (whose old page stays up) — and
|
|
167
|
+
* `prepareEntriesSearch` makes sure the published page almost never has to.
|
|
133
168
|
*/
|
|
134
169
|
declare const CollectionEntries: import("react").ForwardRefExoticComponent<Omit<CollectionEntriesProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
135
170
|
export declare const collectionEntriesSchema: Aglyn.ComponentSchema<CollectionEntriesProps>;
|