@aglyn/plugins-mui 1.0.0-beta.233 → 1.0.0-beta.235
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 +27 -0
- package/src/lib/components/app-bar.js +43 -2
- package/src/lib/components/app-bar.js.map +1 -1
- package/src/lib/components/product.js +3 -2
- package/src/lib/components/product.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.235",
|
|
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.235",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.235",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.235",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.235",
|
|
32
|
+
"@aglyn/shared-ui-theme": "1.0.0-beta.235",
|
|
33
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.235",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"mobx": "^7.0.5",
|
|
36
36
|
"mobx-react-lite": "^5.1.0"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import * as Aglyn from '@aglyn/aglyn';
|
|
18
18
|
import { type AppBarProps } from '@mui/material/AppBar';
|
|
19
|
+
import { type Theme } from '@mui/material/styles';
|
|
19
20
|
export interface AglynAppBarProps extends AppBarProps {
|
|
20
21
|
/**
|
|
21
22
|
* Compacts the bar's Toolbar Content to the dense height once the page
|
|
@@ -28,7 +29,33 @@ export interface AglynAppBarProps extends AppBarProps {
|
|
|
28
29
|
* the moment they scroll up. Meant for a pinned (sticky/fixed) bar.
|
|
29
30
|
*/
|
|
30
31
|
hideOnScroll?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Sits over a photo hero (AGL-3660): on a page whose first band runs under
|
|
34
|
+
* the header (a Section with `underHeader`), the bar lies over that band
|
|
35
|
+
* with no background of its own and its words in white, and scrolls away
|
|
36
|
+
* with it; on every other page it is the bar it always was.
|
|
37
|
+
*/
|
|
38
|
+
overHero?: boolean;
|
|
31
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Over a page that opens under the header, the bar lies on the photo. One
|
|
42
|
+
* rule the browser applies by what the page holds, so the server render, the
|
|
43
|
+
* published page and every page of a layout share one class, and a page
|
|
44
|
+
* without such a band is untouched.
|
|
45
|
+
*/
|
|
46
|
+
export declare const overHeroSx: (theme: Theme) => {
|
|
47
|
+
"body:has([data-aglyn-under-header]) &": {
|
|
48
|
+
position: string;
|
|
49
|
+
top: string;
|
|
50
|
+
left: number;
|
|
51
|
+
right: number;
|
|
52
|
+
backgroundColor: string;
|
|
53
|
+
backgroundImage: string;
|
|
54
|
+
boxShadow: string;
|
|
55
|
+
borderBottom: number;
|
|
56
|
+
color: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
32
59
|
/**
|
|
33
60
|
* MUI's AppBar behind the cleared-prop guard (AGL-1226).
|
|
34
61
|
*
|
|
@@ -25,7 +25,38 @@ 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
|
+
*/ export const overHeroSx = (theme)=>({
|
|
36
|
+
[`body:has([${UNDER_HEADER_ATTRIBUTE}]) &`]: {
|
|
37
|
+
position: 'absolute',
|
|
38
|
+
// Below the owner's admin bar when one is up (the tenant publishes its
|
|
39
|
+
// live height on `<html>`), at the very top for everyone else. Absolute
|
|
40
|
+
// to the initial containing block, the bar's `<html>` margin never
|
|
41
|
+
// moves this header — the variable is what does (AGL-3660).
|
|
42
|
+
top: 'var(--aglyn-admin-bar-height, 0px)',
|
|
43
|
+
left: 0,
|
|
44
|
+
right: 0,
|
|
45
|
+
backgroundColor: 'transparent',
|
|
46
|
+
backgroundImage: 'none',
|
|
47
|
+
boxShadow: 'none',
|
|
48
|
+
borderBottom: 0,
|
|
49
|
+
color: theme.palette.common.white
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
function withOverHeroSx(sx) {
|
|
53
|
+
return [
|
|
54
|
+
overHeroSx,
|
|
55
|
+
...Array.isArray(sx) ? sx : [
|
|
56
|
+
sx
|
|
57
|
+
]
|
|
58
|
+
];
|
|
59
|
+
}
|
|
29
60
|
/** Scroll distance before a shrinking bar compacts. */ const SHRINK_THRESHOLD = 16;
|
|
30
61
|
/** Scroll distance before a hiding bar may slide away — past its own height. */ const HIDE_THRESHOLD = 120;
|
|
31
62
|
/**
|
|
@@ -104,10 +135,14 @@ ScrollAwareAppBar.displayName = 'AglynScrollAwareAppBar';
|
|
|
104
135
|
let _ref = [
|
|
105
136
|
_0,
|
|
106
137
|
_1
|
|
107
|
-
], [_ref1, ..._rest] = _ref, { shrinkOnScroll, hideOnScroll } = _ref1,
|
|
138
|
+
], [_ref1, ..._rest] = _ref, { shrinkOnScroll, hideOnScroll, overHero } = _ref1, rest = _object_without_properties_loose(_ref1, [
|
|
108
139
|
"shrinkOnScroll",
|
|
109
|
-
"hideOnScroll"
|
|
140
|
+
"hideOnScroll",
|
|
141
|
+
"overHero"
|
|
110
142
|
]), [ref] = _rest;
|
|
143
|
+
const props = overHero ? _extends({}, rest, {
|
|
144
|
+
sx: withOverHeroSx(rest.sx)
|
|
145
|
+
}) : rest;
|
|
111
146
|
return shrinkOnScroll || hideOnScroll ? createElement(ScrollAwareAppBar, _extends({}, props, {
|
|
112
147
|
shrink: Boolean(shrinkOnScroll),
|
|
113
148
|
hide: Boolean(hideOnScroll),
|
|
@@ -145,6 +180,12 @@ export const schema = {
|
|
|
145
180
|
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
181
|
component: Aglyn.FieldComponentType.SWITCH,
|
|
147
182
|
label: 'Hide while scrolling down?'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'overHero',
|
|
186
|
+
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.',
|
|
187
|
+
component: Aglyn.FieldComponentType.SWITCH,
|
|
188
|
+
label: 'Sit over a photo hero?'
|
|
148
189
|
}
|
|
149
190
|
]
|
|
150
191
|
};
|
|
@@ -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 */\nexport const overHeroSx = (theme: Theme) => ({\n [`body:has([${UNDER_HEADER_ATTRIBUTE}]) &`]: {\n position: 'absolute',\n // Below the owner's admin bar when one is up (the tenant publishes its\n // live height on `<html>`), at the very top for everyone else. Absolute\n // to the initial containing block, the bar's `<html>` margin never\n // moves this header — the variable is what does (AGL-3660).\n top: 'var(--aglyn-admin-bar-height, 0px)',\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":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,mBAAmB,QAAQ,yBAAwB;AAC5D,SAASC,cAAc,QAAQ,kDAAiD;AAChF,OAAOC,eAAqC,uBAAsB;AAElE,SAASC,aAAa,EAAEC,UAAU,QAAkB,QAAO;AAC3D,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SACEC,oBAAoB,EACpBC,wBAAwB,EACxBC,6BAA6B,QACxB,6BAAyB;AAChC,SAASC,gBAAgB,EAAEC,cAAc,QAAQ,gCAA4B;AAC7E,SAASC,gBAAgB,QAAQ,iCAA6B;AAC9D,SAASC,gBAAgB,QAAQ,iCAA6B;AAC9D,SAASC,sBAAsB,QAAQ,eAAW;AAClD,SAASC,MAAMC,SAAS,QAAQ,eAAW;AAuB3C;;;;;CAKC,GACD,OAAO,MAAMC,aAAa,CAACC,QAAkB,CAAA;QAC3C,CAAC,CAAC,UAAU,EAAEJ,uBAAuB,IAAI,CAAC,CAAC,EAAE;YAC3CK,UAAU;YACV,uEAAuE;YACvE,wEAAwE;YACxE,mEAAmE;YACnE,4DAA4D;YAC5DC,KAAK;YACLC,MAAM;YACNC,OAAO;YACPC,iBAAiB;YACjBC,iBAAiB;YACjBC,WAAW;YACXC,cAAc;YACdC,OAAOT,MAAMU,OAAO,CAACC,MAAM,CAACC,KAAK;QACnC;IACF,CAAA,EAAE;AAEF,SAASC,eAAeC,EAAqB;IAC3C,OAAO;QAACf;WAAgBgB,MAAMC,OAAO,CAACF,MAAMA,KAAK;YAACA;SAAG;KAAE;AACzD;AAEA,qDAAqD,GACrD,MAAMG,mBAAmB;AACzB,8EAA8E,GAC9E,MAAMC,iBAAiB;AAEvB;;;CAGC,GACD,MAAMC,WAA2B,CAACnB,QAAW,CAAA;QAC3CoB,YAAYpB,MAAMqB,WAAW,CAACC,MAAM,CAAC;YAAC;YAAa;SAAa,EAAE;YAChEC,UAAUvB,MAAMqB,WAAW,CAACE,QAAQ,CAACC,OAAO;QAC9C;QACA,sBAAsB;YACpBJ,YAAYpB,MAAMqB,WAAW,CAACC,MAAM,CAAC,cAAc;gBACjDC,UAAUvB,MAAMqB,WAAW,CAACE,QAAQ,CAACC,OAAO;YAC9C;QACF;QACA,qCAAqC;YACnC,8BAA8B;YAC9BC,WAAWzB,MAAM0B,OAAO,CAAC;QAC3B;QACA,yBAAyB;YACvBC,WAAW;QACb;IACF,CAAA;AAEA,SAASC,aAAad,EAAqB;IACzC,OAAO;QAACK;WAAcJ,MAAMC,OAAO,CAACF,MAAMA,KAAK;YAACA;SAAG;KAAE;AACvD;AAEA,SAASe,aAAaC,KAAkB,EAAEC,GAAqB;IAC7D,OAAO7C,cAAcD,WAAW,aAK3BI,qBAAqBK,iBAAiBoC;QACzCC;;AAEJ;AAOA;;;;;;CAMC,GACD,MAAMC,oBAAoB7C,WACxB;;;;iCAAC,EAAE8C,MAAM,EAAEC,IAAI,EAAY,UAAPJ;;;SAASC;WAC3B7C,cAAcF,gBAAgB;QAC5BmD,WAAWlB;QACXmB,gBAAgB;YAAED,WAAWjB;QAAe;QAC5CmB,UAAU,CAAC,EAAEC,oBAAoB,EAAEC,uBAAuB,EAAE,GAC1DV,aACE,aACKC;gBACHhB,IAAIc,aAAaE,MAAMhB,EAAE;eACrB;gBACF,iBACEmB,UAAUM,0BAA0B,KAAKC;gBAC3C,sBACEN,QAAQI,uBAAuB,KAAKE;YACxC,IAEFT;IAEN;;AAEJC,kBAAkBS,WAAW,GAAG;AAEhC;;;;;;;;CAQC,GACD,MAAMC,SAASvD,WACb;;;;iCAAC,EAAEwD,cAAc,EAAEC,YAAY,EAAEC,QAAQ,EAAW,UAANC;;;;SAAQf;IACpD,MAAMD,QAAQe,WAAW,aAAKC;QAAMhC,IAAID,eAAeiC,KAAKhC,EAAE;SAAMgC;IACpE,OAAOH,kBAAkBC,eACrB1D,cAAc8C,mBAAmB,aAC5BF;QACHG,QAAQc,QAAQJ;QAChBT,MAAMa,QAAQH;QACdb;UAEFF,aAAaC,OAAOC;AAC1B;AAEFW,OAAOD,WAAW,GAAG;AAErB,wEAAwE;AACxE,OAAO,MAAM5C,KAAwB,YAAW;AAEhD,OAAO,MAAMmD,SAAkD;IAC7DC,KAAKpD;IACLqD,UAAU9D;IACVqD,aAAa;IACbU,aACE;IACFC,UAAUtE,MAAMuE,iBAAiB,CAACC,UAAU;IAC5CC,MAAM;QACJC,MAAMzE,oBAAoByE,IAAI;QAC9B1C,IAAI;YAAEL,OAAO;QAAU;IACzB;IACAgD,YAAY;QACVnE,yBAAyB;QACzBC;QACAC;QACAC;QACA;YACEiE,MAAM;YACNP,aACE,qEACA;YACFQ,WAAW7E,MAAM8E,kBAAkB,CAACC,MAAM;YAC1CC,OAAO;QACT;QACA;YACEJ,MAAM;YACNP,aACE,uEACA;YACFQ,WAAW7E,MAAM8E,kBAAkB,CAACC,MAAM;YAC1CC,OAAO;QACT;QACA;YACEJ,MAAM;YACNP,aACE,wEACA,oEACA;YACFQ,WAAW7E,MAAM8E,kBAAkB,CAACC,MAAM;YAC1CC,OAAO;QACT;KACD;AACH,EAAC;AAED,OAAO,MAAMC,UAAgC;IAC3C;QACEd,KAAKtD,iBAAiBE;QACtBmE,MAAMlF,MAAMmF,QAAQ,CAACC,MAAM;QAC3BzB,aAAa;QACbS,UAAU9D;QACV+D,aAAa;QACbC,UAAUtE,MAAMuE,iBAAiB,CAACC,UAAU;QAC5CC,MAAMP,OAAOO,IAAI;QACjBY,MAAM;YACJlB,KAAK;YACLmB,aAAavE;YACbqD,UAAU9D;YACViF,OAAO;gBACL;oBACEpB,KAAK;oBACLmB,aAAatE;oBACboD,UAAU9D;oBACV0C,OAAO,CAAC;gBACV;aACD;QACH;IACF;CACD,CAAA;AAED,eAAeY,OAAM"}
|
|
@@ -16,7 +16,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
17
|
* See the License for the specific language governing permissions and
|
|
18
18
|
* limitations under the License.
|
|
19
|
-
*/ import
|
|
19
|
+
*/ import { advertisingConsentField } from "@aglyn/aglyn/app-utils/advertising-consent";
|
|
20
|
+
import * as Aglyn from "@aglyn/aglyn";
|
|
20
21
|
import { buildBeginCheckoutParams, trackEventBeforeNavigation } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
21
22
|
import { mdiCartOutline } from "@aglyn/shared-data-mdi";
|
|
22
23
|
import Alert from "@mui/material/Alert";
|
|
@@ -77,7 +78,7 @@ export const ID = 'product';
|
|
|
77
78
|
productId
|
|
78
79
|
}, coupon.trim() ? {
|
|
79
80
|
couponCode: coupon.trim()
|
|
80
|
-
} : {}))
|
|
81
|
+
} : {}, advertisingConsentField(hostId)))
|
|
81
82
|
});
|
|
82
83
|
const payload = await response.json().catch(()=>({}));
|
|
83
84
|
if (response.ok && (payload == null ? void 0 : payload.url)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/product.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n buildBeginCheckoutParams,\n trackEventBeforeNavigation,\n} from '@aglyn/aglyn/app-utils/analytics-events'\nimport { mdiCartOutline } from '@aglyn/shared-data-mdi'\nimport Alert from '@mui/material/Alert'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport CardContent from '@mui/material/CardContent'\nimport CardMedia from '@mui/material/CardMedia'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'product'\n\nexport interface ProductProps {\n /** Id of the product doc under hosts/{hostId}/products. */\n productId?: string\n name?: string\n /** Display price; the charge always uses the product doc's price. */\n priceUsd?: string\n description?: string\n imageUrl?: string\n buyLabel?: string\n /** Shows a coupon-code input above the buy button (AGL-96). */\n showCoupon?: boolean\n}\n\n/**\n * Commerce Starter product block (AGL-90): displays from its own props,\n * but Buy posts {hostId, productId} to the tenant's /api/commerce/checkout,\n * which prices from the server-side product doc — canvas props can't forge\n * a charge. Inert without SiteContext (besigner canvas/preview).\n */\nconst Product = forwardRef<HTMLDivElement, ProductProps>((props, ref) => {\n const {\n productId,\n name,\n priceUsd,\n description,\n imageUrl: storedImageUrl,\n buyLabel,\n showCoupon,\n ...rest\n } = props\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n // A media-picker target, so it can hold a media reference (AGL-1215) as\n // well as any URL form that predates it. `siteRelativeMediaSrc` rather\n // than the bare resolver (AGL-1726): one of those older forms is an\n // absolute `https://{subdomain}.aglyn.app/api/media/cdn/…`, which on a\n // white-label storefront names our platform in the customer's page source\n // and fetches cross-origin from their own domain.\n const imageUrl = Aglyn.siteRelativeMediaSrc(storedImageUrl, { hostId })\n const [status, setStatus] = useState<'idle' | 'sending' | 'error'>('idle')\n const [coupon, setCoupon] = useState('')\n const [message, setMessage] = useState('')\n const soldOut = message === 'Sold out'\n\n const handleBuy = useCallback(async () => {\n if (!hostId || !productId || status === 'sending') return\n setStatus('sending')\n setMessage('')\n try {\n const response = await siteFetch('/api/commerce/checkout', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n productId,\n ...(coupon.trim() ? { couponCode: coupon.trim() } : {}),\n }),\n })\n const payload = await response.json().catch(() => ({}))\n if (response.ok && payload?.url) {\n // The Starter block's `begin_checkout`. It takes real money through\n // Stripe and reported nothing, so a site built from this block alone\n // showed GA4 a `purchase` with no checkout step in front of it — which\n // reads as a 0% checkout rate rather than as an unmeasured path.\n //\n // Fired only after the server has ACCEPTED the request and returned a\n // session url, never on the click: a count inflated by sold-out\n // products and rejected coupons describes attempts, not checkouts.\n //\n // `value` is the price the SHOPPER SAW, which is the block's own prop.\n // The charge is priced server-side from the product doc — an author\n // who edits one without the other has a storefront that lies to\n // shoppers before it lies to GA — and an unparseable or absent price\n // reports no event at all, because a `value: 0` on a real sale is a\n // worse number than a missing one.\n const shownPrice = Number(priceUsd)\n if (Number.isFinite(shownPrice) && shownPrice > 0) {\n await trackEventBeforeNavigation(\n 'begin_checkout',\n buildBeginCheckoutParams({\n items: [\n {\n item_id: productId,\n item_name: name || 'Product',\n price: shownPrice,\n quantity: 1,\n },\n ],\n }),\n )\n }\n window.location.assign(payload.url)\n return\n }\n // Server messages are visitor-safe (AGL-96): \"Sold out\", \"Invalid\n // or expired coupon\", etc.\n setMessage(String(payload?.error ?? ''))\n setStatus('error')\n } catch {\n setStatus('error')\n }\n }, [hostId, productId, status, coupon, siteFetch])\n\n return (\n <Card ref={ref} variant=\"outlined\" sx={{ maxWidth: 360 }} {...rest}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={name ?? ''}\n sx={{ height: 200, objectFit: 'cover' }}\n // Deferred (AGL-2486). A Product card is a 360px-wide card dropped\n // into a page; it is never the page's lead image, and before this\n // it carried no loading hint at all — i.e. EAGER at default\n // priority, outranking the `lazy` + `low` Image elements in the\n // section the reader was actually looking at.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : null}\n <CardContent>\n <Typography variant=\"h6\">{name || 'Product name'}</Typography>\n {priceUsd ? (\n <Typography variant=\"subtitle1\" color=\"text.secondary\">\n {`$${priceUsd}`}\n </Typography>\n ) : null}\n {description ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {description}\n </Typography>\n ) : null}\n {status === 'error' ? (\n <Alert severity={soldOut ? 'info' : 'error'} sx={{ mt: 1 }}>\n {message || 'Checkout is unavailable right now.'}\n </Alert>\n ) : null}\n {showCoupon && !soldOut ? (\n <TextField\n size=\"small\"\n fullWidth\n label=\"Coupon code\"\n value={coupon}\n onChange={(event) => setCoupon(event.target.value)}\n sx={{ mt: 1.5 }}\n />\n ) : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n sx={{ mt: 2 }}\n disabled={status === 'sending' || soldOut}\n onClick={handleBuy}\n fullWidth\n >\n {soldOut\n ? 'Sold out'\n : status === 'sending'\n ? 'Redirecting…'\n : buyLabel || 'Buy now'}\n </Button>\n </CardContent>\n </Card>\n )\n})\nProduct.displayName = 'AglynProduct'\n\nexport const schema: Aglyn.ComponentSchema<ProductProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Product',\n description: 'A single product card with a buy button, priced on the server.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n description:\n 'Product id from the Products card on the host dashboard — the ' +\n 'charge uses that product’s price.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Product id',\n },\n {\n name: 'name',\n description: 'Displayed product name.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Name',\n },\n {\n name: 'priceUsd',\n description: 'Displayed price (the charge uses the product doc).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Price (USD)',\n },\n {\n name: 'description',\n description: 'Short product description.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Description',\n },\n {\n name: 'imageUrl',\n description: 'Product image URL (copy from the Media library).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Image URL',\n },\n {\n name: 'buyLabel',\n description: 'Buy button label.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Button label',\n },\n {\n name: 'showCoupon',\n label: 'Show coupon field',\n description:\n 'Adds a coupon-code input; codes are managed on the Products card.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Product',\n pluginId: BUNDLE_ID,\n description: 'Product card with a Stripe Checkout buy button',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n name: 'My product',\n priceUsd: '25',\n description: 'A short description of what the buyer gets.',\n buyLabel: 'Buy now',\n },\n },\n },\n]\n\nexport default Product\n"],"names":["Aglyn","buildBeginCheckoutParams","trackEventBeforeNavigation","mdiCartOutline","Alert","Button","Card","CardContent","CardMedia","TextField","Typography","forwardRef","useCallback","useState","BUNDLE_ID","generatePresetId","ID","Product","props","ref","productId","name","priceUsd","description","imageUrl","storedImageUrl","buyLabel","showCoupon","rest","hostId","useSite","siteFetch","useSiteFetch","siteRelativeMediaSrc","status","setStatus","coupon","setCoupon","message","setMessage","soldOut","handleBuy","response","method","headers","body","JSON","stringify","trim","couponCode","payload","json","catch","ok","url","shownPrice","Number","isFinite","items","item_id","item_name","price","quantity","window","location","assign","String","error","variant","sx","maxWidth","component","image","alt","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","color","mt","severity","size","fullWidth","label","value","onChange","event","target","disabled","onClick","displayName","schema","$id","pluginId","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","FieldComponentType","TEXT_FIELD","CHECKBOX","presets","type","NodeType","PRESET","data","componentId"],"mappings":";;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SACEC,wBAAwB,EACxBC,0BAA0B,QACrB,0CAAyC;AAChD,SAASC,cAAc,QAAQ,yBAAwB;AACvD,OAAOC,WAAW,sBAAqB;AACvC,OAAOC,YAAY,uBAAsB;AACzC,OAAOC,UAAU,qBAAoB;AACrC,OAAOC,iBAAiB,4BAA2B;AACnD,OAAOC,eAAe,0BAAyB;AAC/C,OAAOC,eAAe,0BAAyB;AAC/C,OAAOC,gBAAgB,2BAA0B;AACjD,SAASC,UAAU,EAAEC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AACzD,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,gBAAgB,QAAQ,iCAA6B;AAE9D,iEAAiE;AACjE,OAAO,MAAMC,KAAwB,UAAS;AAe9C;;;;;CAKC,GACD,MAAMC,wBAAUN,WAAyC,CAACO,OAAOC;IAC/D,MAAM,EACJC,SAAS,EACTC,IAAI,EACJC,QAAQ,EACRC,WAAW,EACXC,UAAUC,cAAc,EACxBC,QAAQ,EACRC,UAAU,EAEX,GAAGT,OADCU,wCACDV;;;;;;;;;IACJ,MAAM,EAAEW,MAAM,EAAE,GAAG7B,MAAM8B,OAAO;IAChC,MAAMC,YAAY/B,MAAMgC,YAAY;IACpC,wEAAwE;IACxE,uEAAuE;IACvE,oEAAoE;IACpE,uEAAuE;IACvE,0EAA0E;IAC1E,kDAAkD;IAClD,MAAMR,WAAWxB,MAAMiC,oBAAoB,CAACR,gBAAgB;QAAEI;IAAO;IACrE,MAAM,CAACK,QAAQC,UAAU,GAAGtB,SAAuC;IACnE,MAAM,CAACuB,QAAQC,UAAU,GAAGxB,SAAS;IACrC,MAAM,CAACyB,SAASC,WAAW,GAAG1B,SAAS;IACvC,MAAM2B,UAAUF,YAAY;IAE5B,MAAMG,YAAY7B,YAAY;QAC5B,IAAI,CAACiB,UAAU,CAACT,aAAac,WAAW,WAAW;QACnDC,UAAU;QACVI,WAAW;QACX,IAAI;;YACF,MAAMG,WAAW,MAAMX,UAAU,0BAA0B;gBACzDY,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC;oBACnBlB;oBACAT;mBACIgB,OAAOY,IAAI,KAAK;oBAAEC,YAAYb,OAAOY,IAAI;gBAAG,IAAI,CAAC;YAEzD;YACA,MAAME,UAAU,MAAMR,SAASS,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YACpD,IAAIV,SAASW,EAAE,KAAIH,2BAAAA,QAASI,GAAG,GAAE;gBAC/B,oEAAoE;gBACpE,qEAAqE;gBACrE,uEAAuE;gBACvE,iEAAiE;gBACjE,EAAE;gBACF,sEAAsE;gBACtE,gEAAgE;gBAChE,mEAAmE;gBACnE,EAAE;gBACF,uEAAuE;gBACvE,oEAAoE;gBACpE,gEAAgE;gBAChE,qEAAqE;gBACrE,oEAAoE;gBACpE,mCAAmC;gBACnC,MAAMC,aAAaC,OAAOlC;gBAC1B,IAAIkC,OAAOC,QAAQ,CAACF,eAAeA,aAAa,GAAG;oBACjD,MAAMrD,2BACJ,kBACAD,yBAAyB;wBACvByD,OAAO;4BACL;gCACEC,SAASvC;gCACTwC,WAAWvC,QAAQ;gCACnBwC,OAAON;gCACPO,UAAU;4BACZ;yBACD;oBACH;gBAEJ;gBACAC,OAAOC,QAAQ,CAACC,MAAM,CAACf,QAAQI,GAAG;gBAClC;YACF;YACA,kEAAkE;YAClE,2BAA2B;YAC3Bf,WAAW2B,eAAOhB,2BAAAA,QAASiB,KAAK,mBAAI;YACpChC,UAAU;QACZ,EAAE,eAAM;YACNA,UAAU;QACZ;IACF,GAAG;QAACN;QAAQT;QAAWc;QAAQE;QAAQL;KAAU;IAEjD,qBACE,MAACzB;QAAKa,KAAKA;QAAKiD,SAAQ;QAAWC,IAAI;YAAEC,UAAU;QAAI;OAAO1C;;YAC3DJ,yBACC,KAAChB;gBACC+D,WAAU;gBACVC,OAAOhD;gBACPiD,GAAG,EAAEpD,eAAAA,OAAQ;gBACbgD,IAAI;oBAAEK,QAAQ;oBAAKC,WAAW;gBAAQ;eAMlC3E,MAAM4E,yBAAyB,KAEnC;0BACJ,MAACrE;;kCACC,KAACG;wBAAW0D,SAAQ;kCAAM/C,QAAQ;;oBACjCC,yBACC,KAACZ;wBAAW0D,SAAQ;wBAAYS,OAAM;kCACnC,CAAC,CAAC,EAAEvD,UAAU;yBAEf;oBACHC,4BACC,KAACb;wBAAW0D,SAAQ;wBAAQC,IAAI;4BAAES,IAAI;wBAAE;kCACrCvD;yBAED;oBACHW,WAAW,wBACV,KAAC9B;wBAAM2E,UAAUvC,UAAU,SAAS;wBAAS6B,IAAI;4BAAES,IAAI;wBAAE;kCACtDxC,WAAW;yBAEZ;oBACHX,cAAc,CAACa,wBACd,KAAC/B;wBACCuE,MAAK;wBACLC,SAAS;wBACTC,OAAM;wBACNC,OAAO/C;wBACPgD,UAAU,CAACC,QAAUhD,UAAUgD,MAAMC,MAAM,CAACH,KAAK;wBACjDd,IAAI;4BAAES,IAAI;wBAAI;yBAEd;kCACJ,KAACzE;wBACC+D,SAAQ;wBACRS,OAAM;wBACNR,IAAI;4BAAES,IAAI;wBAAE;wBACZS,UAAUrD,WAAW,aAAaM;wBAClCgD,SAAS/C;wBACTwC,SAAS;kCAERzC,UACG,aACAN,WAAW,YACT,iBACAR,YAAY;;;;;;AAK5B;AACAT,QAAQwE,WAAW,GAAG;AAEtB,OAAO,MAAMC,SAA8C;IACzDC,KAAK3E;IACL4E,UAAU9E;IACV2E,aAAa;IACblE,aAAa;IACbsE,UAAU7F,MAAM8F,iBAAiB,CAACC,QAAQ;IAC1CC,MAAM;QAAEC,MAAM9F,eAAe8F,IAAI;QAAE5B,IAAI;YAAEQ,OAAO;QAAU;IAAE;IAC5DqB,OAAO;QAAEC,aAAanG,MAAMoG,YAAY,CAACC,OAAO;IAAC;IACjDC,YAAY;QACV;YACEjF,MAAM;YACNE,aACE,mEACA;YACFgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACNE,aAAa;YACbgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACNE,aAAa;YACbgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACNE,aAAa;YACbgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACNE,aAAa;YACbgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACNE,aAAa;YACbgD,WAAWvE,MAAMuG,kBAAkB,CAACC,UAAU;YAC9CtB,OAAO;QACT;QACA;YACE7D,MAAM;YACN6D,OAAO;YACP3D,aACE;YACFgD,WAAWvE,MAAMuG,kBAAkB,CAACE,QAAQ;QAC9C;KACD;AACH,EAAC;AAED,OAAO,MAAMC,UAAgC;IAC3C;QACEf,KAAK5E,iBAAiBC;QACtB2F,MAAM3G,MAAM4G,QAAQ,CAACC,MAAM;QAC3BpB,aAAa;QACbG,UAAU9E;QACVS,aAAa;QACbsE,UAAU7F,MAAM8F,iBAAiB,CAACC,QAAQ;QAC1CC,MAAM;YAAEC,MAAM9F,eAAe8F,IAAI;YAAE5B,IAAI;gBAAEQ,OAAO;YAAU;QAAE;QAC5DiC,MAAM;YACJnB,KAAK;YACLoB,aAAa/F;YACb4E,UAAU9E;YACVI,OAAO;gBACLG,MAAM;gBACNC,UAAU;gBACVC,aAAa;gBACbG,UAAU;YACZ;QACF;IACF;CACD,CAAA;AAED,eAAeT,QAAO"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/product.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 { advertisingConsentField } from '@aglyn/aglyn/app-utils/advertising-consent'\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n buildBeginCheckoutParams,\n trackEventBeforeNavigation,\n} from '@aglyn/aglyn/app-utils/analytics-events'\nimport { mdiCartOutline } from '@aglyn/shared-data-mdi'\nimport Alert from '@mui/material/Alert'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport CardContent from '@mui/material/CardContent'\nimport CardMedia from '@mui/material/CardMedia'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'product'\n\nexport interface ProductProps {\n /** Id of the product doc under hosts/{hostId}/products. */\n productId?: string\n name?: string\n /** Display price; the charge always uses the product doc's price. */\n priceUsd?: string\n description?: string\n imageUrl?: string\n buyLabel?: string\n /** Shows a coupon-code input above the buy button (AGL-96). */\n showCoupon?: boolean\n}\n\n/**\n * Commerce Starter product block (AGL-90): displays from its own props,\n * but Buy posts {hostId, productId} to the tenant's /api/commerce/checkout,\n * which prices from the server-side product doc — canvas props can't forge\n * a charge. Inert without SiteContext (besigner canvas/preview).\n */\nconst Product = forwardRef<HTMLDivElement, ProductProps>((props, ref) => {\n const {\n productId,\n name,\n priceUsd,\n description,\n imageUrl: storedImageUrl,\n buyLabel,\n showCoupon,\n ...rest\n } = props\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n // A media-picker target, so it can hold a media reference (AGL-1215) as\n // well as any URL form that predates it. `siteRelativeMediaSrc` rather\n // than the bare resolver (AGL-1726): one of those older forms is an\n // absolute `https://{subdomain}.aglyn.app/api/media/cdn/…`, which on a\n // white-label storefront names our platform in the customer's page source\n // and fetches cross-origin from their own domain.\n const imageUrl = Aglyn.siteRelativeMediaSrc(storedImageUrl, { hostId })\n const [status, setStatus] = useState<'idle' | 'sending' | 'error'>('idle')\n const [coupon, setCoupon] = useState('')\n const [message, setMessage] = useState('')\n const soldOut = message === 'Sold out'\n\n const handleBuy = useCallback(async () => {\n if (!hostId || !productId || status === 'sending') return\n setStatus('sending')\n setMessage('')\n try {\n const response = await siteFetch('/api/commerce/checkout', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n productId,\n ...(coupon.trim() ? { couponCode: coupon.trim() } : {}),\n // The visitor's advertising consent and the vendors' browser ids, only\n // where that consent grants advertising (AGL-3694); absent otherwise.\n ...advertisingConsentField(hostId),\n }),\n })\n const payload = await response.json().catch(() => ({}))\n if (response.ok && payload?.url) {\n // The Starter block's `begin_checkout`. It takes real money through\n // Stripe and reported nothing, so a site built from this block alone\n // showed GA4 a `purchase` with no checkout step in front of it — which\n // reads as a 0% checkout rate rather than as an unmeasured path.\n //\n // Fired only after the server has ACCEPTED the request and returned a\n // session url, never on the click: a count inflated by sold-out\n // products and rejected coupons describes attempts, not checkouts.\n //\n // `value` is the price the SHOPPER SAW, which is the block's own prop.\n // The charge is priced server-side from the product doc — an author\n // who edits one without the other has a storefront that lies to\n // shoppers before it lies to GA — and an unparseable or absent price\n // reports no event at all, because a `value: 0` on a real sale is a\n // worse number than a missing one.\n const shownPrice = Number(priceUsd)\n if (Number.isFinite(shownPrice) && shownPrice > 0) {\n await trackEventBeforeNavigation(\n 'begin_checkout',\n buildBeginCheckoutParams({\n items: [\n {\n item_id: productId,\n item_name: name || 'Product',\n price: shownPrice,\n quantity: 1,\n },\n ],\n }),\n )\n }\n window.location.assign(payload.url)\n return\n }\n // Server messages are visitor-safe (AGL-96): \"Sold out\", \"Invalid\n // or expired coupon\", etc.\n setMessage(String(payload?.error ?? ''))\n setStatus('error')\n } catch {\n setStatus('error')\n }\n }, [hostId, productId, status, coupon, siteFetch])\n\n return (\n <Card ref={ref} variant=\"outlined\" sx={{ maxWidth: 360 }} {...rest}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={name ?? ''}\n sx={{ height: 200, objectFit: 'cover' }}\n // Deferred (AGL-2486). A Product card is a 360px-wide card dropped\n // into a page; it is never the page's lead image, and before this\n // it carried no loading hint at all — i.e. EAGER at default\n // priority, outranking the `lazy` + `low` Image elements in the\n // section the reader was actually looking at.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : null}\n <CardContent>\n <Typography variant=\"h6\">{name || 'Product name'}</Typography>\n {priceUsd ? (\n <Typography variant=\"subtitle1\" color=\"text.secondary\">\n {`$${priceUsd}`}\n </Typography>\n ) : null}\n {description ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {description}\n </Typography>\n ) : null}\n {status === 'error' ? (\n <Alert severity={soldOut ? 'info' : 'error'} sx={{ mt: 1 }}>\n {message || 'Checkout is unavailable right now.'}\n </Alert>\n ) : null}\n {showCoupon && !soldOut ? (\n <TextField\n size=\"small\"\n fullWidth\n label=\"Coupon code\"\n value={coupon}\n onChange={(event) => setCoupon(event.target.value)}\n sx={{ mt: 1.5 }}\n />\n ) : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n sx={{ mt: 2 }}\n disabled={status === 'sending' || soldOut}\n onClick={handleBuy}\n fullWidth\n >\n {soldOut\n ? 'Sold out'\n : status === 'sending'\n ? 'Redirecting…'\n : buyLabel || 'Buy now'}\n </Button>\n </CardContent>\n </Card>\n )\n})\nProduct.displayName = 'AglynProduct'\n\nexport const schema: Aglyn.ComponentSchema<ProductProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Product',\n description: 'A single product card with a buy button, priced on the server.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n description:\n 'Product id from the Products card on the host dashboard — the ' +\n 'charge uses that product’s price.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Product id',\n },\n {\n name: 'name',\n description: 'Displayed product name.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Name',\n },\n {\n name: 'priceUsd',\n description: 'Displayed price (the charge uses the product doc).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Price (USD)',\n },\n {\n name: 'description',\n description: 'Short product description.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Description',\n },\n {\n name: 'imageUrl',\n description: 'Product image URL (copy from the Media library).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Image URL',\n },\n {\n name: 'buyLabel',\n description: 'Buy button label.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Button label',\n },\n {\n name: 'showCoupon',\n label: 'Show coupon field',\n description:\n 'Adds a coupon-code input; codes are managed on the Products card.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Product',\n pluginId: BUNDLE_ID,\n description: 'Product card with a Stripe Checkout buy button',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n name: 'My product',\n priceUsd: '25',\n description: 'A short description of what the buyer gets.',\n buyLabel: 'Buy now',\n },\n },\n },\n]\n\nexport default Product\n"],"names":["advertisingConsentField","Aglyn","buildBeginCheckoutParams","trackEventBeforeNavigation","mdiCartOutline","Alert","Button","Card","CardContent","CardMedia","TextField","Typography","forwardRef","useCallback","useState","BUNDLE_ID","generatePresetId","ID","Product","props","ref","productId","name","priceUsd","description","imageUrl","storedImageUrl","buyLabel","showCoupon","rest","hostId","useSite","siteFetch","useSiteFetch","siteRelativeMediaSrc","status","setStatus","coupon","setCoupon","message","setMessage","soldOut","handleBuy","response","method","headers","body","JSON","stringify","trim","couponCode","payload","json","catch","ok","url","shownPrice","Number","isFinite","items","item_id","item_name","price","quantity","window","location","assign","String","error","variant","sx","maxWidth","component","image","alt","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","color","mt","severity","size","fullWidth","label","value","onChange","event","target","disabled","onClick","displayName","schema","$id","pluginId","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","FieldComponentType","TEXT_FIELD","CHECKBOX","presets","type","NodeType","PRESET","data","componentId"],"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"}
|