@aglyn/plugins-mui 1.0.0-beta.233 → 1.0.0-beta.234
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/app-bar.d.ts +7 -0
- package/src/lib/components/app-bar.js +39 -2
- package/src/lib/components/app-bar.js.map +1 -1
- package/src/lib/components/section.d.ts +8 -0
- package/src/lib/components/section.js +13 -3
- package/src/lib/components/section.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.234",
|
|
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.234",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.234",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.234",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.234",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.234",
|
|
33
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.234",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"mobx": "^7.0.5",
|
|
36
36
|
"mobx-react-lite": "^5.1.0"
|
|
@@ -28,6 +28,13 @@ export interface AglynAppBarProps extends AppBarProps {
|
|
|
28
28
|
* the moment they scroll up. Meant for a pinned (sticky/fixed) bar.
|
|
29
29
|
*/
|
|
30
30
|
hideOnScroll?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Sits over a photo hero (AGL-3660): on a page whose first band runs under
|
|
33
|
+
* the header (a Section with `underHeader`), the bar lies over that band
|
|
34
|
+
* with no background of its own and its words in white, and scrolls away
|
|
35
|
+
* with it; on every other page it is the bar it always was.
|
|
36
|
+
*/
|
|
37
|
+
overHero?: boolean;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* MUI's AppBar behind the cleared-prop guard (AGL-1226).
|
|
@@ -25,7 +25,34 @@ import { applySemanticElement, semanticElementAttribute, semanticElementLabelAtt
|
|
|
25
25
|
import { FIELD_COLOR_ALT1, FIELD_POSITION } from "../constants/field-presets.js";
|
|
26
26
|
import { dropClearedProps } from "../utils/drop-cleared-props.js";
|
|
27
27
|
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
28
|
+
import { UNDER_HEADER_ATTRIBUTE } from "./section.js";
|
|
28
29
|
import { ID as toolbarId } from "./toolbar.js";
|
|
30
|
+
/**
|
|
31
|
+
* Over a page that opens under the header, the bar lies on the photo. One
|
|
32
|
+
* rule the browser applies by what the page holds, so the server render, the
|
|
33
|
+
* published page and every page of a layout share one class, and a page
|
|
34
|
+
* without such a band is untouched.
|
|
35
|
+
*/ const overHeroSx = (theme)=>({
|
|
36
|
+
[`body:has([${UNDER_HEADER_ATTRIBUTE}]) &`]: {
|
|
37
|
+
position: 'absolute',
|
|
38
|
+
top: 0,
|
|
39
|
+
left: 0,
|
|
40
|
+
right: 0,
|
|
41
|
+
backgroundColor: 'transparent',
|
|
42
|
+
backgroundImage: 'none',
|
|
43
|
+
boxShadow: 'none',
|
|
44
|
+
borderBottom: 0,
|
|
45
|
+
color: theme.palette.common.white
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
function withOverHeroSx(sx) {
|
|
49
|
+
return [
|
|
50
|
+
overHeroSx,
|
|
51
|
+
...Array.isArray(sx) ? sx : [
|
|
52
|
+
sx
|
|
53
|
+
]
|
|
54
|
+
];
|
|
55
|
+
}
|
|
29
56
|
/** Scroll distance before a shrinking bar compacts. */ const SHRINK_THRESHOLD = 16;
|
|
30
57
|
/** Scroll distance before a hiding bar may slide away — past its own height. */ const HIDE_THRESHOLD = 120;
|
|
31
58
|
/**
|
|
@@ -104,10 +131,14 @@ ScrollAwareAppBar.displayName = 'AglynScrollAwareAppBar';
|
|
|
104
131
|
let _ref = [
|
|
105
132
|
_0,
|
|
106
133
|
_1
|
|
107
|
-
], [_ref1, ..._rest] = _ref, { shrinkOnScroll, hideOnScroll } = _ref1,
|
|
134
|
+
], [_ref1, ..._rest] = _ref, { shrinkOnScroll, hideOnScroll, overHero } = _ref1, rest = _object_without_properties_loose(_ref1, [
|
|
108
135
|
"shrinkOnScroll",
|
|
109
|
-
"hideOnScroll"
|
|
136
|
+
"hideOnScroll",
|
|
137
|
+
"overHero"
|
|
110
138
|
]), [ref] = _rest;
|
|
139
|
+
const props = overHero ? _extends({}, rest, {
|
|
140
|
+
sx: withOverHeroSx(rest.sx)
|
|
141
|
+
}) : rest;
|
|
111
142
|
return shrinkOnScroll || hideOnScroll ? createElement(ScrollAwareAppBar, _extends({}, props, {
|
|
112
143
|
shrink: Boolean(shrinkOnScroll),
|
|
113
144
|
hide: Boolean(hideOnScroll),
|
|
@@ -145,6 +176,12 @@ export const schema = {
|
|
|
145
176
|
description: 'Slides the bar up out of view while the visitor scrolls down, and ' + 'back as soon as they scroll up. Pair with a Sticky or Fixed position.',
|
|
146
177
|
component: Aglyn.FieldComponentType.SWITCH,
|
|
147
178
|
label: 'Hide while scrolling down?'
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'overHero',
|
|
182
|
+
description: 'On a page whose first band is set to run under the header, the bar ' + 'sits over that photo with no background and white words. Other ' + 'pages keep the bar as it is.',
|
|
183
|
+
component: Aglyn.FieldComponentType.SWITCH,
|
|
184
|
+
label: 'Sit over a photo hero?'
|
|
148
185
|
}
|
|
149
186
|
]
|
|
150
187
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/app-bar.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 { mdiPageLayoutHeader } from '@aglyn/shared-data-mdi'\nimport { ScrollReaction } from '@aglyn/shared-ui-jsx/components/scroll-reaction'\nimport MuiAppBar, { type AppBarProps } from '@mui/material/AppBar'\nimport { type SxProps, type Theme } from '@mui/material/styles'\nimport { createElement, forwardRef, type Ref } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { FIELD_COLOR_ALT1, FIELD_POSITION } from '../constants/field-presets'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport { ID as toolbarId } from './toolbar'\n\nexport interface AglynAppBarProps extends AppBarProps {\n /**\n * Compacts the bar's Toolbar Content to the dense height once the page\n * has scrolled, and back at the top. Meant for a pinned (sticky/fixed)\n * bar, where the full height costs the reader a band of every screen.\n */\n shrinkOnScroll?: boolean\n /**\n * Slides the bar up out of view while the visitor scrolls down, and back\n * the moment they scroll up. Meant for a pinned (sticky/fixed) bar.\n */\n hideOnScroll?: boolean\n}\n\n/** Scroll distance before a shrinking bar compacts. */\nconst SHRINK_THRESHOLD = 16\n/** Scroll distance before a hiding bar may slide away — past its own height. */\nconst HIDE_THRESHOLD = 120\n\n/**\n * Both scroll states ride data attributes, not a re-styled sx, so the\n * server render and every scroll position share one emotion class.\n */\nconst scrollSx: SxProps<Theme> = (theme) => ({\n transition: theme.transitions.create(['transform', 'box-shadow'], {\n duration: theme.transitions.duration.shorter,\n }),\n '& .MuiToolbar-root': {\n transition: theme.transitions.create('min-height', {\n duration: theme.transitions.duration.shorter,\n }),\n },\n '&[data-scrolled] .MuiToolbar-root': {\n // MUI's dense toolbar height.\n minHeight: theme.spacing(6),\n },\n '&[data-scroll-hidden]': {\n transform: 'translateY(-100%)',\n },\n})\n\nfunction withScrollSx(sx: AppBarProps['sx']): AppBarProps['sx'] {\n return [scrollSx, ...(Array.isArray(sx) ? sx : [sx])] as AppBarProps['sx']\n}\n\nfunction renderAppBar(props: AppBarProps, ref: Ref<HTMLElement>) {\n return createElement(MuiAppBar, {\n // Unset leaves MUI's own default, which is `header` — the banner\n // landmark every site's chrome depends on (AGL-2525). A resolver that\n // answered `div` for \"unset\" would have stripped it the moment the\n // picker appeared.\n ...applySemanticElement(dropClearedProps(props) as Record<string, unknown>),\n ref,\n })\n}\n\ninterface ScrollAwareAppBarProps extends AppBarProps {\n shrink: boolean\n hide: boolean\n}\n\n/**\n * Its own component so the scroll listeners exist only on a bar that asked\n * to react — a hook behind a prop check would break the rules of hooks.\n * `ScrollReaction` answers both questions: past the threshold (shrink),\n * and scrolling down rather than up (hide), which is what MUI's scroll\n * trigger means by hysteresis.\n */\nconst ScrollAwareAppBar = forwardRef<HTMLElement, ScrollAwareAppBarProps>(\n ({ shrink, hide, ...props }, ref) =>\n createElement(ScrollReaction, {\n threshold: SHRINK_THRESHOLD,\n withHysteresis: { threshold: HIDE_THRESHOLD },\n children: ({ activeWithHysteresis, activeWithoutHysteresis }) =>\n renderAppBar(\n {\n ...props,\n sx: withScrollSx(props.sx),\n ...({\n 'data-scrolled':\n shrink && activeWithoutHysteresis ? '' : undefined,\n 'data-scroll-hidden':\n hide && activeWithHysteresis ? '' : undefined,\n } as AppBarProps),\n },\n ref,\n ),\n }),\n)\nScrollAwareAppBar.displayName = 'AglynScrollAwareAppBar'\n\n/**\n * MUI's AppBar behind the cleared-prop guard (AGL-1226).\n *\n * It was exported raw, which left BOTH of its authorable attributes exposed:\n * `color` and `position` are each run through MUI's `capitalize`, so clearing\n * either one persists a null that throws during SSR and 500s the page. A\n * wrapper is the only place to intercept it, since the schema's props reach\n * MUI directly. `createElement` rather than JSX keeps this a `.ts` file.\n */\nconst AppBar = forwardRef<HTMLElement, AglynAppBarProps>(\n ({ shrinkOnScroll, hideOnScroll, ...props }, ref) =>\n shrinkOnScroll || hideOnScroll\n ? createElement(ScrollAwareAppBar, {\n ...props,\n shrink: Boolean(shrinkOnScroll),\n hide: Boolean(hideOnScroll),\n ref,\n })\n : renderAppBar(props, ref),\n)\nAppBar.displayName = 'AglynAppBar'\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiAppBar'\n\nexport const schema: Aglyn.ComponentSchema<AglynAppBarProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'App Bar',\n description:\n 'The bar across the top of the site — put a Toolbar Content inside it.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: {\n path: mdiPageLayoutHeader.path,\n sx: { color: '#2196f3' },\n },\n attributes: [\n semanticElementAttribute('this bar'),\n semanticElementLabelAttribute(),\n FIELD_COLOR_ALT1,\n FIELD_POSITION,\n {\n name: 'shrinkOnScroll',\n description:\n 'Compacts the bar to the dense height once the page scrolls, and ' +\n 'restores it at the top. Pair with a Sticky or Fixed position.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Shrink when scrolled?',\n },\n {\n name: 'hideOnScroll',\n description:\n 'Slides the bar up out of view while the visitor scrolls down, and ' +\n 'back as soon as they scroll up. Pair with a Sticky or Fixed position.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Hide while scrolling down?',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'App Bar',\n pluginId: BUNDLE_ID,\n description: 'An app bar preset with the app bar and toolbar content nodes',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: toolbarId,\n pluginId: BUNDLE_ID,\n props: {},\n },\n ],\n },\n },\n]\n\nexport default AppBar\n"],"names":["Aglyn","mdiPageLayoutHeader","ScrollReaction","MuiAppBar","createElement","forwardRef","BUNDLE_ID","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","FIELD_COLOR_ALT1","FIELD_POSITION","dropClearedProps","generatePresetId","ID","toolbarId","SHRINK_THRESHOLD","HIDE_THRESHOLD","scrollSx","theme","transition","transitions","create","duration","shorter","minHeight","spacing","transform","withScrollSx","sx","Array","isArray","renderAppBar","props","ref","ScrollAwareAppBar","shrink","hide","threshold","withHysteresis","children","activeWithHysteresis","activeWithoutHysteresis","undefined","displayName","AppBar","shrinkOnScroll","hideOnScroll","Boolean","schema","$id","pluginId","description","category","ComponentCategory","NAVIGATION","icon","path","color","attributes","name","component","FieldComponentType","SWITCH","label","presets","type","NodeType","PRESET","data","componentId","nodes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/app-bar.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 { mdiPageLayoutHeader } from '@aglyn/shared-data-mdi'\nimport { ScrollReaction } from '@aglyn/shared-ui-jsx/components/scroll-reaction'\nimport MuiAppBar, { type AppBarProps } from '@mui/material/AppBar'\nimport { type SxProps, type Theme } from '@mui/material/styles'\nimport { createElement, forwardRef, type Ref } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { FIELD_COLOR_ALT1, FIELD_POSITION } from '../constants/field-presets'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport { UNDER_HEADER_ATTRIBUTE } from './section'\nimport { ID as toolbarId } from './toolbar'\n\nexport interface AglynAppBarProps extends AppBarProps {\n /**\n * Compacts the bar's Toolbar Content to the dense height once the page\n * has scrolled, and back at the top. Meant for a pinned (sticky/fixed)\n * bar, where the full height costs the reader a band of every screen.\n */\n shrinkOnScroll?: boolean\n /**\n * Slides the bar up out of view while the visitor scrolls down, and back\n * the moment they scroll up. Meant for a pinned (sticky/fixed) bar.\n */\n hideOnScroll?: boolean\n /**\n * Sits over a photo hero (AGL-3660): on a page whose first band runs under\n * the header (a Section with `underHeader`), the bar lies over that band\n * with no background of its own and its words in white, and scrolls away\n * with it; on every other page it is the bar it always was.\n */\n overHero?: boolean\n}\n\n/**\n * Over a page that opens under the header, the bar lies on the photo. One\n * rule the browser applies by what the page holds, so the server render, the\n * published page and every page of a layout share one class, and a page\n * without such a band is untouched.\n */\nconst overHeroSx: SxProps<Theme> = (theme) => ({\n [`body:has([${UNDER_HEADER_ATTRIBUTE}]) &`]: {\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n backgroundColor: 'transparent',\n backgroundImage: 'none',\n boxShadow: 'none',\n borderBottom: 0,\n color: theme.palette.common.white,\n },\n})\n\nfunction withOverHeroSx(sx: AppBarProps['sx']): AppBarProps['sx'] {\n return [overHeroSx, ...(Array.isArray(sx) ? sx : [sx])] as AppBarProps['sx']\n}\n\n/** Scroll distance before a shrinking bar compacts. */\nconst SHRINK_THRESHOLD = 16\n/** Scroll distance before a hiding bar may slide away — past its own height. */\nconst HIDE_THRESHOLD = 120\n\n/**\n * Both scroll states ride data attributes, not a re-styled sx, so the\n * server render and every scroll position share one emotion class.\n */\nconst scrollSx: SxProps<Theme> = (theme) => ({\n transition: theme.transitions.create(['transform', 'box-shadow'], {\n duration: theme.transitions.duration.shorter,\n }),\n '& .MuiToolbar-root': {\n transition: theme.transitions.create('min-height', {\n duration: theme.transitions.duration.shorter,\n }),\n },\n '&[data-scrolled] .MuiToolbar-root': {\n // MUI's dense toolbar height.\n minHeight: theme.spacing(6),\n },\n '&[data-scroll-hidden]': {\n transform: 'translateY(-100%)',\n },\n})\n\nfunction withScrollSx(sx: AppBarProps['sx']): AppBarProps['sx'] {\n return [scrollSx, ...(Array.isArray(sx) ? sx : [sx])] as AppBarProps['sx']\n}\n\nfunction renderAppBar(props: AppBarProps, ref: Ref<HTMLElement>) {\n return createElement(MuiAppBar, {\n // Unset leaves MUI's own default, which is `header` — the banner\n // landmark every site's chrome depends on (AGL-2525). A resolver that\n // answered `div` for \"unset\" would have stripped it the moment the\n // picker appeared.\n ...applySemanticElement(dropClearedProps(props) as Record<string, unknown>),\n ref,\n })\n}\n\ninterface ScrollAwareAppBarProps extends AppBarProps {\n shrink: boolean\n hide: boolean\n}\n\n/**\n * Its own component so the scroll listeners exist only on a bar that asked\n * to react — a hook behind a prop check would break the rules of hooks.\n * `ScrollReaction` answers both questions: past the threshold (shrink),\n * and scrolling down rather than up (hide), which is what MUI's scroll\n * trigger means by hysteresis.\n */\nconst ScrollAwareAppBar = forwardRef<HTMLElement, ScrollAwareAppBarProps>(\n ({ shrink, hide, ...props }, ref) =>\n createElement(ScrollReaction, {\n threshold: SHRINK_THRESHOLD,\n withHysteresis: { threshold: HIDE_THRESHOLD },\n children: ({ activeWithHysteresis, activeWithoutHysteresis }) =>\n renderAppBar(\n {\n ...props,\n sx: withScrollSx(props.sx),\n ...({\n 'data-scrolled':\n shrink && activeWithoutHysteresis ? '' : undefined,\n 'data-scroll-hidden':\n hide && activeWithHysteresis ? '' : undefined,\n } as AppBarProps),\n },\n ref,\n ),\n }),\n)\nScrollAwareAppBar.displayName = 'AglynScrollAwareAppBar'\n\n/**\n * MUI's AppBar behind the cleared-prop guard (AGL-1226).\n *\n * It was exported raw, which left BOTH of its authorable attributes exposed:\n * `color` and `position` are each run through MUI's `capitalize`, so clearing\n * either one persists a null that throws during SSR and 500s the page. A\n * wrapper is the only place to intercept it, since the schema's props reach\n * MUI directly. `createElement` rather than JSX keeps this a `.ts` file.\n */\nconst AppBar = forwardRef<HTMLElement, AglynAppBarProps>(\n ({ shrinkOnScroll, hideOnScroll, overHero, ...rest }, ref) => {\n const props = overHero ? { ...rest, sx: withOverHeroSx(rest.sx) } : rest\n return shrinkOnScroll || hideOnScroll\n ? createElement(ScrollAwareAppBar, {\n ...props,\n shrink: Boolean(shrinkOnScroll),\n hide: Boolean(hideOnScroll),\n ref,\n })\n : renderAppBar(props, ref)\n },\n)\nAppBar.displayName = 'AglynAppBar'\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiAppBar'\n\nexport const schema: Aglyn.ComponentSchema<AglynAppBarProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'App Bar',\n description:\n 'The bar across the top of the site — put a Toolbar Content inside it.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: {\n path: mdiPageLayoutHeader.path,\n sx: { color: '#2196f3' },\n },\n attributes: [\n semanticElementAttribute('this bar'),\n semanticElementLabelAttribute(),\n FIELD_COLOR_ALT1,\n FIELD_POSITION,\n {\n name: 'shrinkOnScroll',\n description:\n 'Compacts the bar to the dense height once the page scrolls, and ' +\n 'restores it at the top. Pair with a Sticky or Fixed position.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Shrink when scrolled?',\n },\n {\n name: 'hideOnScroll',\n description:\n 'Slides the bar up out of view while the visitor scrolls down, and ' +\n 'back as soon as they scroll up. Pair with a Sticky or Fixed position.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Hide while scrolling down?',\n },\n {\n name: 'overHero',\n description:\n 'On a page whose first band is set to run under the header, the bar ' +\n 'sits over that photo with no background and white words. Other ' +\n 'pages keep the bar as it is.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Sit over a photo hero?',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'App Bar',\n pluginId: BUNDLE_ID,\n description: 'An app bar preset with the app bar and toolbar content nodes',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n nodes: [\n {\n $id: null,\n componentId: toolbarId,\n pluginId: BUNDLE_ID,\n props: {},\n },\n ],\n },\n },\n]\n\nexport default AppBar\n"],"names":["Aglyn","mdiPageLayoutHeader","ScrollReaction","MuiAppBar","createElement","forwardRef","BUNDLE_ID","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","FIELD_COLOR_ALT1","FIELD_POSITION","dropClearedProps","generatePresetId","UNDER_HEADER_ATTRIBUTE","ID","toolbarId","overHeroSx","theme","position","top","left","right","backgroundColor","backgroundImage","boxShadow","borderBottom","color","palette","common","white","withOverHeroSx","sx","Array","isArray","SHRINK_THRESHOLD","HIDE_THRESHOLD","scrollSx","transition","transitions","create","duration","shorter","minHeight","spacing","transform","withScrollSx","renderAppBar","props","ref","ScrollAwareAppBar","shrink","hide","threshold","withHysteresis","children","activeWithHysteresis","activeWithoutHysteresis","undefined","displayName","AppBar","shrinkOnScroll","hideOnScroll","overHero","rest","Boolean","schema","$id","pluginId","description","category","ComponentCategory","NAVIGATION","icon","path","attributes","name","component","FieldComponentType","SWITCH","label","presets","type","NodeType","PRESET","data","componentId","nodes"],"mappings":"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"}
|
|
@@ -41,8 +41,16 @@ export interface SectionProps {
|
|
|
41
41
|
element?: SectionElement;
|
|
42
42
|
/** Accessible name announced for landmark elements. */
|
|
43
43
|
ariaLabel?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The band runs up under the site's header (AGL-3660): a page's first
|
|
46
|
+
* band, a full-bleed photo, marks itself so a header that offers to sit
|
|
47
|
+
* over a photo (`muiAppBar`'s `overHero`) does, on this page only.
|
|
48
|
+
*/
|
|
49
|
+
underHeader?: boolean;
|
|
44
50
|
children?: JSX.Children;
|
|
45
51
|
}
|
|
52
|
+
/** The mark a band that runs under the header carries, which `muiAppBar` reads. */
|
|
53
|
+
export declare const UNDER_HEADER_ATTRIBUTE = "data-aglyn-under-header";
|
|
46
54
|
/**
|
|
47
55
|
* Semantic grouping container (AGL-336): renders children inside the
|
|
48
56
|
* chosen W3C element so pages keep meaningful document structure
|
|
@@ -51,6 +51,7 @@ export const ID = 'section';
|
|
|
51
51
|
'header',
|
|
52
52
|
'footer'
|
|
53
53
|
];
|
|
54
|
+
/** The mark a band that runs under the header carries, which `muiAppBar` reads. */ export const UNDER_HEADER_ATTRIBUTE = 'data-aglyn-under-header';
|
|
54
55
|
/**
|
|
55
56
|
* Semantic grouping container (AGL-336): renders children inside the
|
|
56
57
|
* chosen W3C element so pages keep meaningful document structure
|
|
@@ -58,9 +59,10 @@ export const ID = 'section';
|
|
|
58
59
|
* div-soup. Behaves like any container: droppable, stylable via sx,
|
|
59
60
|
* classes, and interactions.
|
|
60
61
|
*/ const Section = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
61
|
-
const { element, ariaLabel, children } = props, rest = _object_without_properties_loose(props, [
|
|
62
|
+
const { element, ariaLabel, underHeader, children } = props, rest = _object_without_properties_loose(props, [
|
|
62
63
|
"element",
|
|
63
64
|
"ariaLabel",
|
|
65
|
+
"underHeader",
|
|
64
66
|
"children"
|
|
65
67
|
]);
|
|
66
68
|
const component = SECTION_ELEMENTS.includes(element) ? element : 'section';
|
|
@@ -68,7 +70,9 @@ export const ID = 'section';
|
|
|
68
70
|
ref: ref,
|
|
69
71
|
component: component,
|
|
70
72
|
"aria-label": ariaLabel || undefined
|
|
71
|
-
},
|
|
73
|
+
}, underHeader ? {
|
|
74
|
+
[UNDER_HEADER_ATTRIBUTE]: ''
|
|
75
|
+
} : {}, rest, {
|
|
72
76
|
children: children
|
|
73
77
|
}));
|
|
74
78
|
});
|
|
@@ -108,7 +112,13 @@ export const schema = {
|
|
|
108
112
|
description: 'Names the landmark for screen readers (recommended when a page ' + 'has several of the same element).',
|
|
109
113
|
component: Aglyn.FieldComponentType.TEXT_FIELD
|
|
110
114
|
},
|
|
111
|
-
colorSchemeAttribute()
|
|
115
|
+
colorSchemeAttribute(),
|
|
116
|
+
{
|
|
117
|
+
name: 'underHeader',
|
|
118
|
+
label: 'Run under the header?',
|
|
119
|
+
description: "Starts this band at the very top of the page, under the site's " + 'header, when the header is set to sit over a photo. Use it on the ' + "page's first band, a full-width photo.",
|
|
120
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
121
|
+
}
|
|
112
122
|
]
|
|
113
123
|
};
|
|
114
124
|
export const presets = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/section.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 { mdiPageLayoutBody } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { colorSchemeAttribute } from '../utils/color-scheme-attribute'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'section'\n\n/**\n * W3C sectioning/grouping elements the component may render as (AGL-336).\n *\n * ⛔ `main` IS NOT ONE OF THEM (AGL-2486). A published page carries exactly\n * one `main` landmark, placed by composition on the page's content region —\n * the layout's slot, or the screen root when there is no layout, each of which\n * offers its own element picker. An author-selectable `main` on a grouping\n * container that may appear any number of times per page could only ever\n * produce a SECOND one — and two `main` elements is a worse\n * accessibility outcome than none, because the landmark stops naming anything\n * in particular. It was offered in this select, with field help recommending\n * it, which made the duplicate the documented choice rather than a mistake.\n *\n * Removing it degrades rather than breaks: the resolver below falls back to\n * `section` for any value not in this list, so a stored `element: 'main'` on a\n * published screen renders as `<section>` — still a grouping element, still\n * where the author put it, and no longer competing with the layout's landmark.\n */\nexport const SECTION_ELEMENTS = [\n 'section',\n 'div',\n 'article',\n 'aside',\n 'nav',\n 'header',\n 'footer',\n] as const\nexport type SectionElement = (typeof SECTION_ELEMENTS)[number]\n\nexport interface SectionProps {\n /** The DOM element rendered; defaults to `section`. */\n element?: SectionElement\n /** Accessible name announced for landmark elements. */\n ariaLabel?: string\n children?: JSX.Children\n}\n\n/**\n * Semantic grouping container (AGL-336): renders children inside the\n * chosen W3C element so pages keep meaningful document structure\n * (landmarks for assistive tech, better SEO outlines) instead of\n * div-soup. Behaves like any container: droppable, stylable via sx,\n * classes, and interactions.\n */\nconst Section = forwardRef<HTMLElement, SectionProps>((props, ref) => {\n const { element, ariaLabel, children, ...rest } = props\n const component = SECTION_ELEMENTS.includes(element as SectionElement)\n ? (element as SectionElement)\n : 'section'\n return (\n <Box\n ref={ref}\n component={component}\n aria-label={ariaLabel || undefined}\n {...rest}\n >\n {children}\n </Box>\n )\n})\nSection.displayName = 'AglynSection'\n\nexport const schema: Aglyn.ComponentSchema<SectionProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Section',\n // Static, and its children sit directly in its root: a subtree it heads\n // keeps its server HTML until the visitor nears it (AGL-3581).\n flags: {\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n childrenInRoot: Aglyn.FEATURE_FLAG.ENABLED,\n },\n description:\n 'Groups components inside a semantic HTML element (section, article, nav, …).',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiPageLayoutBody.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this section renders as, per W3C semantics. ' +\n 'Use section/article/aside/nav/header/footer for meaningful ' +\n 'structure; div for purely visual grouping.',\n component: Aglyn.FieldComponentType.SELECT,\n options: SECTION_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'ariaLabel',\n label: 'Accessible label',\n description:\n 'Names the landmark for screen readers (recommended when a page ' +\n 'has several of the same element).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n colorSchemeAttribute(),\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Section',\n pluginId: BUNDLE_ID,\n description: 'Semantic grouping container (section/div/article/…)',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiPageLayoutBody.path, sx: { color: '#2196f3' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'section' },\n },\n },\n]\n\nexport default Section\n"],"names":["Aglyn","mdiPageLayoutBody","Box","forwardRef","BUNDLE_ID","colorSchemeAttribute","generatePresetId","ID","SECTION_ELEMENTS","Section","props","ref","element","ariaLabel","children","rest","component","includes","aria-label","undefined","displayName","schema","$id","pluginId","flags","lazyHydration","FEATURE_FLAG","ENABLED","childrenInRoot","description","category","ComponentCategory","LAYOUT","icon","path","sx","color","attributes","name","label","FieldComponentType","SELECT","options","map","value","TEXT_FIELD","presets","type","NodeType","PRESET","data","componentId"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,OAAOC,SAAS,oBAAmB;AACnC,SAASC,UAAU,QAAQ,QAAO;AAClC,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,oBAAoB,QAAQ,qCAAiC;AACtE,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,UAAS;AAE9C;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAMC,mBAAmB;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/section.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 { mdiPageLayoutBody } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { colorSchemeAttribute } from '../utils/color-scheme-attribute'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'section'\n\n/**\n * W3C sectioning/grouping elements the component may render as (AGL-336).\n *\n * ⛔ `main` IS NOT ONE OF THEM (AGL-2486). A published page carries exactly\n * one `main` landmark, placed by composition on the page's content region —\n * the layout's slot, or the screen root when there is no layout, each of which\n * offers its own element picker. An author-selectable `main` on a grouping\n * container that may appear any number of times per page could only ever\n * produce a SECOND one — and two `main` elements is a worse\n * accessibility outcome than none, because the landmark stops naming anything\n * in particular. It was offered in this select, with field help recommending\n * it, which made the duplicate the documented choice rather than a mistake.\n *\n * Removing it degrades rather than breaks: the resolver below falls back to\n * `section` for any value not in this list, so a stored `element: 'main'` on a\n * published screen renders as `<section>` — still a grouping element, still\n * where the author put it, and no longer competing with the layout's landmark.\n */\nexport const SECTION_ELEMENTS = [\n 'section',\n 'div',\n 'article',\n 'aside',\n 'nav',\n 'header',\n 'footer',\n] as const\nexport type SectionElement = (typeof SECTION_ELEMENTS)[number]\n\nexport interface SectionProps {\n /** The DOM element rendered; defaults to `section`. */\n element?: SectionElement\n /** Accessible name announced for landmark elements. */\n ariaLabel?: string\n /**\n * The band runs up under the site's header (AGL-3660): a page's first\n * band, a full-bleed photo, marks itself so a header that offers to sit\n * over a photo (`muiAppBar`'s `overHero`) does, on this page only.\n */\n underHeader?: boolean\n children?: JSX.Children\n}\n\n/** The mark a band that runs under the header carries, which `muiAppBar` reads. */\nexport const UNDER_HEADER_ATTRIBUTE = 'data-aglyn-under-header'\n\n/**\n * Semantic grouping container (AGL-336): renders children inside the\n * chosen W3C element so pages keep meaningful document structure\n * (landmarks for assistive tech, better SEO outlines) instead of\n * div-soup. Behaves like any container: droppable, stylable via sx,\n * classes, and interactions.\n */\nconst Section = forwardRef<HTMLElement, SectionProps>((props, ref) => {\n const { element, ariaLabel, underHeader, children, ...rest } = props\n const component = SECTION_ELEMENTS.includes(element as SectionElement)\n ? (element as SectionElement)\n : 'section'\n return (\n <Box\n ref={ref}\n component={component}\n aria-label={ariaLabel || undefined}\n {...(underHeader ? { [UNDER_HEADER_ATTRIBUTE]: '' } : {})}\n {...rest}\n >\n {children}\n </Box>\n )\n})\nSection.displayName = 'AglynSection'\n\nexport const schema: Aglyn.ComponentSchema<SectionProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Section',\n // Static, and its children sit directly in its root: a subtree it heads\n // keeps its server HTML until the visitor nears it (AGL-3581).\n flags: {\n lazyHydration: Aglyn.FEATURE_FLAG.ENABLED,\n childrenInRoot: Aglyn.FEATURE_FLAG.ENABLED,\n },\n description:\n 'Groups components inside a semantic HTML element (section, article, nav, …).',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiPageLayoutBody.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'element',\n label: 'Component',\n description:\n 'The DOM element this section renders as, per W3C semantics. ' +\n 'Use section/article/aside/nav/header/footer for meaningful ' +\n 'structure; div for purely visual grouping.',\n component: Aglyn.FieldComponentType.SELECT,\n options: SECTION_ELEMENTS.map((value) => ({ value, label: value })),\n },\n {\n name: 'ariaLabel',\n label: 'Accessible label',\n description:\n 'Names the landmark for screen readers (recommended when a page ' +\n 'has several of the same element).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n colorSchemeAttribute(),\n {\n name: 'underHeader',\n label: 'Run under the header?',\n description:\n \"Starts this band at the very top of the page, under the site's \" +\n 'header, when the header is set to sit over a photo. Use it on the ' +\n \"page's first band, a full-width photo.\",\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Section',\n pluginId: BUNDLE_ID,\n description: 'Semantic grouping container (section/div/article/…)',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiPageLayoutBody.path, sx: { color: '#2196f3' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { element: 'section' },\n },\n },\n]\n\nexport default Section\n"],"names":["Aglyn","mdiPageLayoutBody","Box","forwardRef","BUNDLE_ID","colorSchemeAttribute","generatePresetId","ID","SECTION_ELEMENTS","UNDER_HEADER_ATTRIBUTE","Section","props","ref","element","ariaLabel","underHeader","children","rest","component","includes","aria-label","undefined","displayName","schema","$id","pluginId","flags","lazyHydration","FEATURE_FLAG","ENABLED","childrenInRoot","description","category","ComponentCategory","LAYOUT","icon","path","sx","color","attributes","name","label","FieldComponentType","SELECT","options","map","value","TEXT_FIELD","SWITCH","presets","type","NodeType","PRESET","data","componentId"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,OAAOC,SAAS,oBAAmB;AACnC,SAASC,UAAU,QAAQ,QAAO;AAClC,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,oBAAoB,QAAQ,qCAAiC;AACtE,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,UAAS;AAE9C;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAMC,mBAAmB;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAiBV,iFAAiF,GACjF,OAAO,MAAMC,yBAAyB,0BAAyB;AAE/D;;;;;;CAMC,GACD,MAAMC,wBAAUP,WAAsC,CAACQ,OAAOC;IAC5D,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAEC,WAAW,EAAEC,QAAQ,EAAW,GAAGL,OAATM,wCAASN;;;;;;IAC/D,MAAMO,YAAYV,iBAAiBW,QAAQ,CAACN,WACvCA,UACD;IACJ,qBACE,KAACX;QACCU,KAAKA;QACLM,WAAWA;QACXE,cAAYN,aAAaO;OACpBN,cAAc;QAAE,CAACN,uBAAuB,EAAE;IAAG,IAAI,CAAC,GACnDQ;kBAEHD;;AAGP;AACAN,QAAQY,WAAW,GAAG;AAEtB,OAAO,MAAMC,SAA8C;IACzDC,KAAKjB;IACLkB,UAAUrB;IACVkB,aAAa;IACb,wEAAwE;IACxE,+DAA+D;IAC/DI,OAAO;QACLC,eAAe3B,MAAM4B,YAAY,CAACC,OAAO;QACzCC,gBAAgB9B,MAAM4B,YAAY,CAACC,OAAO;IAC5C;IACAE,aACE;IACFC,UAAUhC,MAAMiC,iBAAiB,CAACC,MAAM;IACxCC,MAAM;QAAEC,MAAMnC,kBAAkBmC,IAAI;QAAEC,IAAI;YAAEC,OAAO;QAAU;IAAE;IAC/DC,YAAY;QACV;YACEC,MAAM;YACNC,OAAO;YACPV,aACE,iEACA,gEACA;YACFb,WAAWlB,MAAM0C,kBAAkB,CAACC,MAAM;YAC1CC,SAASpC,iBAAiBqC,GAAG,CAAC,CAACC,QAAW,CAAA;oBAAEA;oBAAOL,OAAOK;gBAAM,CAAA;QAClE;QACA;YACEN,MAAM;YACNC,OAAO;YACPV,aACE,oEACA;YACFb,WAAWlB,MAAM0C,kBAAkB,CAACK,UAAU;QAChD;QACA1C;QACA;YACEmC,MAAM;YACNC,OAAO;YACPV,aACE,oEACA,uEACA;YACFb,WAAWlB,MAAM0C,kBAAkB,CAACM,MAAM;QAC5C;KACD;AACH,EAAC;AAED,OAAO,MAAMC,UAAgC;IAC3C;QACEzB,KAAKlB,iBAAiBC;QACtB2C,MAAMlD,MAAMmD,QAAQ,CAACC,MAAM;QAC3B9B,aAAa;QACbG,UAAUrB;QACV2B,aAAa;QACbC,UAAUhC,MAAMiC,iBAAiB,CAACC,MAAM;QACxCC,MAAM;YAAEC,MAAMnC,kBAAkBmC,IAAI;YAAEC,IAAI;gBAAEC,OAAO;YAAU;QAAE;QAC/De,MAAM;YACJ7B,KAAK;YACL8B,aAAa/C;YACbkB,UAAUrB;YACVO,OAAO;gBAAEE,SAAS;YAAU;QAC9B;IACF;CACD,CAAA;AAED,eAAeH,QAAO"}
|