@aglyn/plugins-mui 1.0.0-beta.143

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.
Files changed (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -0
  3. package/package.json +47 -0
  4. package/src/index.d.ts +21 -0
  5. package/src/index.js +22 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/accordion.d.ts +96 -0
  8. package/src/lib/components/accordion.js +392 -0
  9. package/src/lib/components/accordion.js.map +1 -0
  10. package/src/lib/components/app-bar.d.ts +32 -0
  11. package/src/lib/components/app-bar.js +86 -0
  12. package/src/lib/components/app-bar.js.map +1 -0
  13. package/src/lib/components/blocks.d.ts +46 -0
  14. package/src/lib/components/blocks.js +1007 -0
  15. package/src/lib/components/blocks.js.map +1 -0
  16. package/src/lib/components/box.d.ts +47 -0
  17. package/src/lib/components/box.js +125 -0
  18. package/src/lib/components/box.js.map +1 -0
  19. package/src/lib/components/breadcrumbs.d.ts +44 -0
  20. package/src/lib/components/breadcrumbs.js +172 -0
  21. package/src/lib/components/breadcrumbs.js.map +1 -0
  22. package/src/lib/components/button.d.ts +89 -0
  23. package/src/lib/components/button.js +272 -0
  24. package/src/lib/components/button.js.map +1 -0
  25. package/src/lib/components/card.d.ts +61 -0
  26. package/src/lib/components/card.js +336 -0
  27. package/src/lib/components/card.js.map +1 -0
  28. package/src/lib/components/collection.d.ts +514 -0
  29. package/src/lib/components/collection.js +2999 -0
  30. package/src/lib/components/collection.js.map +1 -0
  31. package/src/lib/components/container.d.ts +42 -0
  32. package/src/lib/components/container.js +169 -0
  33. package/src/lib/components/container.js.map +1 -0
  34. package/src/lib/components/custom-html.d.ts +78 -0
  35. package/src/lib/components/custom-html.js +219 -0
  36. package/src/lib/components/custom-html.js.map +1 -0
  37. package/src/lib/components/data-table.d.ts +57 -0
  38. package/src/lib/components/data-table.js +216 -0
  39. package/src/lib/components/data-table.js.map +1 -0
  40. package/src/lib/components/document-root.d.ts +77 -0
  41. package/src/lib/components/document-root.js +118 -0
  42. package/src/lib/components/document-root.js.map +1 -0
  43. package/src/lib/components/drawer.d.ts +93 -0
  44. package/src/lib/components/drawer.js +566 -0
  45. package/src/lib/components/drawer.js.map +1 -0
  46. package/src/lib/components/function-controls.d.ts +66 -0
  47. package/src/lib/components/function-controls.js +207 -0
  48. package/src/lib/components/function-controls.js.map +1 -0
  49. package/src/lib/components/function-scope.d.ts +110 -0
  50. package/src/lib/components/function-scope.js +478 -0
  51. package/src/lib/components/function-scope.js.map +1 -0
  52. package/src/lib/components/function-widget.d.ts +75 -0
  53. package/src/lib/components/function-widget.js +307 -0
  54. package/src/lib/components/function-widget.js.map +1 -0
  55. package/src/lib/components/grid.d.ts +91 -0
  56. package/src/lib/components/grid.js +322 -0
  57. package/src/lib/components/grid.js.map +1 -0
  58. package/src/lib/components/icon.d.ts +54 -0
  59. package/src/lib/components/icon.js +163 -0
  60. package/src/lib/components/icon.js.map +1 -0
  61. package/src/lib/components/image-list.d.ts +58 -0
  62. package/src/lib/components/image-list.js +336 -0
  63. package/src/lib/components/image-list.js.map +1 -0
  64. package/src/lib/components/image.d.ts +154 -0
  65. package/src/lib/components/image.js +451 -0
  66. package/src/lib/components/image.js.map +1 -0
  67. package/src/lib/components/inline-text.d.ts +74 -0
  68. package/src/lib/components/inline-text.js +407 -0
  69. package/src/lib/components/inline-text.js.map +1 -0
  70. package/src/lib/components/language-switcher.d.ts +35 -0
  71. package/src/lib/components/language-switcher.js +179 -0
  72. package/src/lib/components/language-switcher.js.map +1 -0
  73. package/src/lib/components/layout-slot.d.ts +68 -0
  74. package/src/lib/components/layout-slot.js +192 -0
  75. package/src/lib/components/layout-slot.js.map +1 -0
  76. package/src/lib/components/link-box-accessible-name.d.ts +79 -0
  77. package/src/lib/components/link-box-accessible-name.js +158 -0
  78. package/src/lib/components/link-box-accessible-name.js.map +1 -0
  79. package/src/lib/components/link-box.d.ts +66 -0
  80. package/src/lib/components/link-box.js +212 -0
  81. package/src/lib/components/link-box.js.map +1 -0
  82. package/src/lib/components/list-item-text.d.ts +22 -0
  83. package/src/lib/components/list-item-text.js +86 -0
  84. package/src/lib/components/list-item-text.js.map +1 -0
  85. package/src/lib/components/list-item.d.ts +32 -0
  86. package/src/lib/components/list-item.js +127 -0
  87. package/src/lib/components/list-item.js.map +1 -0
  88. package/src/lib/components/list.d.ts +34 -0
  89. package/src/lib/components/list.js +108 -0
  90. package/src/lib/components/list.js.map +1 -0
  91. package/src/lib/components/markdown.d.ts +129 -0
  92. package/src/lib/components/markdown.js +853 -0
  93. package/src/lib/components/markdown.js.map +1 -0
  94. package/src/lib/components/nav-menu.d.ts +68 -0
  95. package/src/lib/components/nav-menu.js +683 -0
  96. package/src/lib/components/nav-menu.js.map +1 -0
  97. package/src/lib/components/pagination.d.ts +59 -0
  98. package/src/lib/components/pagination.js +243 -0
  99. package/src/lib/components/pagination.js.map +1 -0
  100. package/src/lib/components/paper.d.ts +47 -0
  101. package/src/lib/components/paper.js +202 -0
  102. package/src/lib/components/paper.js.map +1 -0
  103. package/src/lib/components/plugin-frame.d.ts +64 -0
  104. package/src/lib/components/plugin-frame.js +335 -0
  105. package/src/lib/components/plugin-frame.js.map +1 -0
  106. package/src/lib/components/plugin.d.ts +95 -0
  107. package/src/lib/components/plugin.js +304 -0
  108. package/src/lib/components/plugin.js.map +1 -0
  109. package/src/lib/components/product.d.ts +40 -0
  110. package/src/lib/components/product.js +287 -0
  111. package/src/lib/components/product.js.map +1 -0
  112. package/src/lib/components/reusable-instance.d.ts +57 -0
  113. package/src/lib/components/reusable-instance.js +126 -0
  114. package/src/lib/components/reusable-instance.js.map +1 -0
  115. package/src/lib/components/screen-link.d.ts +85 -0
  116. package/src/lib/components/screen-link.js +324 -0
  117. package/src/lib/components/screen-link.js.map +1 -0
  118. package/src/lib/components/search-box.d.ts +41 -0
  119. package/src/lib/components/search-box.js +125 -0
  120. package/src/lib/components/search-box.js.map +1 -0
  121. package/src/lib/components/section.d.ts +56 -0
  122. package/src/lib/components/section.js +132 -0
  123. package/src/lib/components/section.js.map +1 -0
  124. package/src/lib/components/stack.d.ts +55 -0
  125. package/src/lib/components/stack.js +309 -0
  126. package/src/lib/components/stack.js.map +1 -0
  127. package/src/lib/components/tabs.d.ts +288 -0
  128. package/src/lib/components/tabs.js +788 -0
  129. package/src/lib/components/tabs.js.map +1 -0
  130. package/src/lib/components/theme-mode-switcher.d.ts +38 -0
  131. package/src/lib/components/theme-mode-switcher.js +168 -0
  132. package/src/lib/components/theme-mode-switcher.js.map +1 -0
  133. package/src/lib/components/toolbar.d.ts +37 -0
  134. package/src/lib/components/toolbar.js +117 -0
  135. package/src/lib/components/toolbar.js.map +1 -0
  136. package/src/lib/components/typography.d.ts +33 -0
  137. package/src/lib/components/typography.js +433 -0
  138. package/src/lib/components/typography.js.map +1 -0
  139. package/src/lib/components/video-lightbox-controls.d.ts +131 -0
  140. package/src/lib/components/video-lightbox-controls.js +536 -0
  141. package/src/lib/components/video-lightbox-controls.js.map +1 -0
  142. package/src/lib/components/video-lightbox.d.ts +60 -0
  143. package/src/lib/components/video-lightbox.js +247 -0
  144. package/src/lib/components/video-lightbox.js.map +1 -0
  145. package/src/lib/components/video-playback-beacon.d.ts +80 -0
  146. package/src/lib/components/video-playback-beacon.js +128 -0
  147. package/src/lib/components/video-playback-beacon.js.map +1 -0
  148. package/src/lib/components/video-player-frame.d.ts +58 -0
  149. package/src/lib/components/video-player-frame.js +51 -0
  150. package/src/lib/components/video-player-frame.js.map +1 -0
  151. package/src/lib/components/video.d.ts +220 -0
  152. package/src/lib/components/video.js +874 -0
  153. package/src/lib/components/video.js.map +1 -0
  154. package/src/lib/constants/bundle-common.d.ts +17 -0
  155. package/src/lib/constants/bundle-common.js +18 -0
  156. package/src/lib/constants/bundle-common.js.map +1 -0
  157. package/src/lib/constants/field-presets.d.ts +93 -0
  158. package/src/lib/constants/field-presets.js +248 -0
  159. package/src/lib/constants/field-presets.js.map +1 -0
  160. package/src/lib/plugin.d.ts +80 -0
  161. package/src/lib/plugin.js +599 -0
  162. package/src/lib/plugin.js.map +1 -0
  163. package/src/lib/utils/author-html-label.d.ts +73 -0
  164. package/src/lib/utils/author-html-label.js +89 -0
  165. package/src/lib/utils/author-html-label.js.map +1 -0
  166. package/src/lib/utils/drop-cleared-props.d.ts +18 -0
  167. package/src/lib/utils/drop-cleared-props.js +74 -0
  168. package/src/lib/utils/drop-cleared-props.js.map +1 -0
  169. package/src/lib/utils/element-picker.d.ts +99 -0
  170. package/src/lib/utils/element-picker.js +140 -0
  171. package/src/lib/utils/element-picker.js.map +1 -0
  172. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  173. package/src/lib/utils/generate-preset-id.js +25 -0
  174. package/src/lib/utils/generate-preset-id.js.map +1 -0
  175. package/src/lib/utils/link-target-props.d.ts +53 -0
  176. package/src/lib/utils/link-target-props.js +53 -0
  177. package/src/lib/utils/link-target-props.js.map +1 -0
@@ -0,0 +1,566 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ /**
5
+ * @license
6
+ * Copyright 2026 Aglyn LLC
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */ import * as Aglyn from "@aglyn/aglyn";
20
+ import { mdiClose, mdiDockLeft, mdiMenu } from "@aglyn/shared-data-mdi";
21
+ import { MdiIcon } from "@aglyn/shared-ui-jsx";
22
+ import Box from "@mui/material/Box";
23
+ import MuiDrawer from "@mui/material/Drawer";
24
+ import IconButton from "@mui/material/IconButton";
25
+ import Stack from "@mui/material/Stack";
26
+ import Typography from "@mui/material/Typography";
27
+ import { forwardRef, useEffect, useMemo, useState } from "react";
28
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
29
+ import { generatePresetId } from "../utils/generate-preset-id.js";
30
+ // Component ids are persisted in screen documents; never rename.
31
+ export const DRAWER_ID = 'muiDrawer';
32
+ export const DRAWER_TOGGLE_ID = 'muiDrawerToggle';
33
+ /** All four edges MUI's Drawer supports. */ export const DRAWER_ANCHORS = [
34
+ 'left',
35
+ 'right',
36
+ 'top',
37
+ 'bottom'
38
+ ];
39
+ /** Horizontal anchors are the ones the `width` setting applies to. */ export function isSideAnchor(anchor) {
40
+ return anchor !== 'top' && anchor !== 'bottom';
41
+ }
42
+ /**
43
+ * The node id encoded in the renderer's stable `data-aglyn="leaf:<id>"`
44
+ * attribute (the same selector contract the interactions system uses).
45
+ */ export function parseLeafNodeId(dataAglyn) {
46
+ const match = /^leaf:(.+)$/.exec(String(dataAglyn != null ? dataAglyn : ''));
47
+ return (match == null ? void 0 : match[1]) || undefined;
48
+ }
49
+ /**
50
+ * True while the besigner canvas flags this leaf's subtree as holding the
51
+ * current selection (AGL-571): the renderer stamps
52
+ * `data-aglyn-selected-within` on a leaf whenever the node itself or any
53
+ * descendant is selected, and drops it when selection leaves the subtree.
54
+ * Live surfaces never set it, so absence simply means "render collapsed" —
55
+ * the same neutral `data-aglyn*` leaf contract `parseLeafNodeId` reads.
56
+ */ export function isLeafSelectedWithin(rest) {
57
+ return rest['data-aglyn-selected-within'] != null;
58
+ }
59
+ /**
60
+ * Mounted drawers in mount order. Broadcast commands (no target node id)
61
+ * are answered by the FIRST registered drawer only, so a page with one
62
+ * drawer — the overwhelmingly common case — needs no wiring at all.
63
+ */ const mountedDrawers = [];
64
+ /** Test seam: the current broadcast owner (first mounted drawer). */ export function firstMountedDrawer() {
65
+ return mountedDrawers[0];
66
+ }
67
+ /**
68
+ * The canvas marker's box (AGL-1236). A drawer is a portal — it contributes
69
+ * nothing to the layout of the row it sits in — and on the canvas it has to
70
+ * cost exactly as little.
71
+ *
72
+ * `position: absolute` is doing the real work, and zero-sizing alone was not
73
+ * enough: a zero-WIDTH flex child is still a flex child, so the nav's
74
+ * `justify-content: space-between` kept it as a distribution point and its
75
+ * `gap: 24px` still applied on both sides — which pushed START FREE in off
76
+ * the right edge for an element that renders nowhere near it. Out of flow,
77
+ * the row lays out exactly as it ships.
78
+ *
79
+ * The visible chip is `fixed` to the canvas frame, so it no longer matters
80
+ * that an abspos flex child takes its static position from the container's
81
+ * justify-content (that is what once parked the chip on top of the logo).
82
+ */ const MARKER_SX = {
83
+ position: 'absolute',
84
+ width: 0,
85
+ height: 0,
86
+ overflow: 'visible',
87
+ m: 0,
88
+ p: 0
89
+ };
90
+ /**
91
+ * Slide-in drawer (AGL-562): a canvas children slot that opens from the
92
+ * page edge — the mobile-menu building block. Opens/closes/toggles via
93
+ * the shared window event bus (`dispatchDrawerCommand`), reachable from
94
+ * the interactions system's drawer steps and the Menu Button element.
95
+ * SSR ships it closed; the canvas shows a slim collapsed placeholder that
96
+ * expands its contents inline only while the drawer or one of its
97
+ * descendants is selected (AGL-571) — full drawer designability is
98
+ * AGL-572.
99
+ */ const DrawerElement = /*#__PURE__*/ forwardRef((props, ref)=>{
100
+ const { anchor, width, children, sx } = props, rest = _object_without_properties_loose(props, [
101
+ "anchor",
102
+ "width",
103
+ "children",
104
+ "sx"
105
+ ]);
106
+ // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).
107
+ const nodeSx = Array.isArray(sx) ? sx : sx ? [
108
+ sx
109
+ ] : [];
110
+ const resolvedAnchor = DRAWER_ANCHORS.includes(anchor) ? anchor : 'left';
111
+ // A top/bottom sheet spans the viewport; a width would pin it to a
112
+ // narrow strip against the edge, which is never what was meant.
113
+ const sideAnchored = isSideAnchor(resolvedAnchor);
114
+ const { editorInert } = Aglyn.useScreenLink(undefined);
115
+ const [open, setOpen] = useState(false);
116
+ // The renderer stamps the node id on every leaf; commands target it.
117
+ const nodeId = useMemo(()=>parseLeafNodeId(rest['data-aglyn']), // eslint-disable-next-line react-hooks/exhaustive-deps
118
+ [
119
+ rest['data-aglyn']
120
+ ]);
121
+ useEffect(()=>{
122
+ if (editorInert || !nodeId) return undefined;
123
+ mountedDrawers.push(nodeId);
124
+ const unsubscribe = Aglyn.subscribeDrawerCommands((detail)=>{
125
+ // Match on the un-namespaced suffix (AGL-573): an interaction
126
+ // authored on a layout-scoped drawer stores the raw canvas id
127
+ // while this drawer's live id is `layout__`-namespaced (or vice
128
+ // versa) — leafIdsMatch compares them equal without re-authoring.
129
+ const targeted = detail.nodeId ? Aglyn.leafIdsMatch(detail.nodeId, nodeId) : mountedDrawers[0] === nodeId;
130
+ if (!targeted) return;
131
+ if (detail.command === 'open') setOpen(true);
132
+ else if (detail.command === 'close') setOpen(false);
133
+ else setOpen((value)=>!value);
134
+ });
135
+ return ()=>{
136
+ unsubscribe();
137
+ const index = mountedDrawers.indexOf(nodeId);
138
+ if (index >= 0) mountedDrawers.splice(index, 1);
139
+ };
140
+ }, [
141
+ editorInert,
142
+ nodeId
143
+ ]);
144
+ if (editorInert) {
145
+ // Editor affordance: a selectable marker mirroring the live
146
+ // hidden-until-opened drawer (AGL-571). While the drawer or a
147
+ // descendant is selected, the contents expand inline as a real
148
+ // design surface sized to the configured width with the live
149
+ // panel's padding (AGL-572), so links and headers render full size.
150
+ const authoring = isLeafSelectedWithin(rest);
151
+ if (authoring) {
152
+ // Selected: render the panel the way it actually opens — an overlay
153
+ // pinned to its anchor edge, at the configured width, with the same
154
+ // padding and close row as the live drawer (AGL-1236). It used to be
155
+ // an inline block in the parent's flow, so selecting it shoved the
156
+ // nav row apart and showed a column that looked nothing like the
157
+ // panel that ships. Zero-size wrapper + `fixed` child, so the design
158
+ // surface behind it is untouched while it is open.
159
+ const edge = {
160
+ left: 0,
161
+ right: 0,
162
+ top: 0,
163
+ bottom: 0
164
+ };
165
+ const opposite = {
166
+ left: 'right',
167
+ right: 'left',
168
+ top: 'bottom',
169
+ bottom: 'top'
170
+ };
171
+ delete edge[opposite[resolvedAnchor]];
172
+ return /*#__PURE__*/ _jsx(Box, _extends({
173
+ ref: ref
174
+ }, rest, {
175
+ sx: MARKER_SX,
176
+ children: /*#__PURE__*/ _jsxs(Box, {
177
+ sx: [
178
+ _extends({
179
+ position: 'fixed'
180
+ }, edge, {
181
+ zIndex: 3,
182
+ overflowY: 'auto',
183
+ backgroundColor: 'background.paper',
184
+ boxShadow: 16
185
+ }),
186
+ sideAnchored ? {
187
+ width: width || 280,
188
+ maxWidth: '90vw',
189
+ p: 2
190
+ } : {
191
+ width: 'auto',
192
+ p: 2
193
+ },
194
+ ...nodeSx
195
+ ],
196
+ children: [
197
+ /*#__PURE__*/ _jsx(Stack, {
198
+ direction: "row",
199
+ sx: {
200
+ justifyContent: 'flex-end',
201
+ mb: 1
202
+ },
203
+ children: /*#__PURE__*/ _jsx(IconButton, {
204
+ "aria-label": "Close menu",
205
+ size: "small",
206
+ disabled: true,
207
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
208
+ path: mdiClose.path
209
+ })
210
+ })
211
+ }),
212
+ children
213
+ ]
214
+ })
215
+ }));
216
+ }
217
+ // Unselected: a ZERO-SIZE anchor in flow, with its chip lifted out of
218
+ // the design surface to the canvas's top-right (AGL-1236).
219
+ //
220
+ // A drawer is a portal — on the live site it contributes nothing to
221
+ // the layout of the row it sits in — so a placeholder that consumed
222
+ // 280px made the canvas disagree with the page it renders: the
223
+ // marketing nav's logo collapsed and overlapped the first link.
224
+ //
225
+ // Two things were needed, and the first alone was not enough.
226
+ // Absolute positioning on the marker itself takes its static position
227
+ // from the flex container's justify-content, so under `space-between`
228
+ // it jumped to the START of the row and sat on top of the logo. The
229
+ // marker is therefore a zero-size in-flow box — correct place in the
230
+ // row, no reserved space — and the chip is `fixed` to the top-right
231
+ // of the canvas so it never covers content at all.
232
+ //
233
+ // Author `sx` deliberately does NOT apply here: on the live site
234
+ // those styles land on the panel body, which is not on screen while
235
+ // the drawer is closed. Applying `width: 280` to the marker would put
236
+ // the footprint straight back.
237
+ return /*#__PURE__*/ _jsx(Box, _extends({
238
+ ref: ref
239
+ }, rest, {
240
+ sx: MARKER_SX,
241
+ children: /*#__PURE__*/ _jsx(Typography, {
242
+ variant: "caption",
243
+ color: "text.secondary",
244
+ sx: {
245
+ // `fixed` resolves against the canvas viewport frame (it is a
246
+ // containing block), so a negative top puts the chip in the
247
+ // gutter ABOVE the design surface rather than over it.
248
+ position: 'fixed',
249
+ top: -26,
250
+ insetInlineEnd: 0,
251
+ zIndex: 2,
252
+ px: 0.75,
253
+ py: 0.25,
254
+ border: '1px dashed',
255
+ borderColor: 'divider',
256
+ borderRadius: 1,
257
+ backgroundColor: 'background.paper',
258
+ whiteSpace: 'nowrap',
259
+ lineHeight: 1.4,
260
+ opacity: 0.85
261
+ },
262
+ children: `Drawer · ${resolvedAnchor}`
263
+ })
264
+ }));
265
+ }
266
+ return /*#__PURE__*/ _jsx(MuiDrawer, _extends({
267
+ ref: ref
268
+ }, rest, {
269
+ anchor: resolvedAnchor,
270
+ open: open,
271
+ onClose: ()=>setOpen(false),
272
+ variant: "temporary",
273
+ children: /*#__PURE__*/ _jsxs(Box, {
274
+ sx: [
275
+ sideAnchored ? {
276
+ width: width || 280,
277
+ maxWidth: '90vw',
278
+ p: 2
279
+ } : {
280
+ width: 'auto',
281
+ p: 2
282
+ },
283
+ ...nodeSx
284
+ ],
285
+ children: [
286
+ /*#__PURE__*/ _jsx(Stack, {
287
+ direction: "row",
288
+ sx: {
289
+ justifyContent: 'flex-end',
290
+ mb: 1
291
+ },
292
+ children: /*#__PURE__*/ _jsx(IconButton, {
293
+ "aria-label": "Close menu",
294
+ onClick: ()=>setOpen(false),
295
+ size: "small",
296
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
297
+ path: mdiClose.path
298
+ })
299
+ })
300
+ }),
301
+ children
302
+ ]
303
+ })
304
+ }));
305
+ });
306
+ DrawerElement.displayName = 'AglynDrawer';
307
+ /**
308
+ * Menu button (AGL-562, targeting moved to interactions in AGL-572): a
309
+ * hamburger icon button whose click broadcast-toggles the page's first
310
+ * drawer over the shared command bus — the zero-config default that
311
+ * makes the one-insert Mobile Nav preset work. Targeting a specific
312
+ * drawer is an interaction (*When clicked → Open/close a drawer*), the
313
+ * same openDrawer/closeDrawer/toggleDrawer steps any element can use;
314
+ * the legacy `targetNodeId` attribute is discarded here so persisted
315
+ * values neither retarget the click nor leak into the DOM. Inert on the
316
+ * static canvas so canvas clicks only select; live (incl. Preview) it toggles.
317
+ */ export const DrawerToggle = /*#__PURE__*/ forwardRef((props, ref)=>{
318
+ const { targetNodeId: _ignoredLegacyBinding, ariaLabel } = props, rest = _object_without_properties_loose(props, [
319
+ "targetNodeId",
320
+ "ariaLabel"
321
+ ]);
322
+ const { editorInert } = Aglyn.useScreenLink(undefined);
323
+ return /*#__PURE__*/ _jsx(IconButton, _extends({
324
+ ref: ref,
325
+ color: "inherit",
326
+ "aria-label": ariaLabel || 'Open menu',
327
+ onClick: editorInert ? undefined : ()=>Aglyn.dispatchDrawerCommand('toggle')
328
+ }, rest, {
329
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
330
+ path: mdiMenu.path
331
+ })
332
+ }));
333
+ });
334
+ DrawerToggle.displayName = 'AglynDrawerToggle';
335
+ export const drawerSchema = {
336
+ $id: DRAWER_ID,
337
+ pluginId: BUNDLE_ID,
338
+ displayName: 'Drawer',
339
+ description: 'A panel that slides in from an edge. Pair it with a Menu Button.',
340
+ category: Aglyn.ComponentCategory.NAVIGATION,
341
+ icon: {
342
+ path: mdiDockLeft.path,
343
+ sx: {
344
+ color: '#2196f3'
345
+ }
346
+ },
347
+ attributes: [
348
+ {
349
+ name: 'anchor',
350
+ description: 'Which edge of the page the drawer slides in from.',
351
+ component: Aglyn.FieldComponentType.SELECT,
352
+ label: 'Slides in from',
353
+ // `left` is one of MUI's four anchors and the value `resolvedAnchor`
354
+ // already falls back to, so the sentinel is real (AGL-1451). Spelled
355
+ // `''` it could not persist (AGL-1191), which meant a drawer moved to
356
+ // the right could never be moved back.
357
+ options: [
358
+ {
359
+ value: 'left',
360
+ label: 'Left (default)'
361
+ },
362
+ {
363
+ value: 'right',
364
+ label: 'Right'
365
+ },
366
+ {
367
+ value: 'top',
368
+ label: 'Top'
369
+ },
370
+ {
371
+ value: 'bottom',
372
+ label: 'Bottom'
373
+ }
374
+ ]
375
+ },
376
+ {
377
+ name: 'width',
378
+ description: 'Width of the open drawer, e.g. 280px or 20rem. Top and ' + 'bottom drawers span the viewport instead.',
379
+ component: Aglyn.FieldComponentType.CSS_DIMENSION,
380
+ label: 'Width',
381
+ // A width on a top/bottom sheet is ignored by the renderer, so the
382
+ // control is hidden rather than left to do nothing.
383
+ condition: {
384
+ when: 'anchor',
385
+ is: [
386
+ 'top',
387
+ 'bottom'
388
+ ],
389
+ notMatch: true
390
+ }
391
+ }
392
+ ]
393
+ };
394
+ export const drawerToggleSchema = {
395
+ $id: DRAWER_TOGGLE_ID,
396
+ pluginId: BUNDLE_ID,
397
+ displayName: 'Menu Button',
398
+ description: "A hamburger button that opens the page's drawer.",
399
+ category: Aglyn.ComponentCategory.NAVIGATION,
400
+ icon: {
401
+ path: mdiMenu.path,
402
+ sx: {
403
+ color: '#2196f3'
404
+ }
405
+ },
406
+ flags: {
407
+ selfClosing: Aglyn.FEATURE_FLAG.ENABLED
408
+ },
409
+ // No drawer-binding attribute (AGL-572): clicking toggles the page's
410
+ // first drawer out of the box, and targeting a specific drawer is an
411
+ // interaction (*When clicked → Open/close a drawer*), per the AGL-568
412
+ // rule that behavior config rides interactions, not bespoke props.
413
+ attributes: [
414
+ {
415
+ name: 'ariaLabel',
416
+ description: 'Accessible name announced by screen readers.',
417
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
418
+ label: 'Accessibility label'
419
+ }
420
+ ]
421
+ };
422
+ const navLink = (label)=>({
423
+ $id: null,
424
+ componentId: 'muiScreenLink',
425
+ pluginId: BUNDLE_ID,
426
+ props: {
427
+ children: label,
428
+ color: 'inherit'
429
+ }
430
+ });
431
+ /** Vertical link stack every drawer preset starts from. */ const drawerLinkStack = {
432
+ $id: null,
433
+ componentId: 'muiStack',
434
+ pluginId: BUNDLE_ID,
435
+ props: {
436
+ spacing: 1
437
+ },
438
+ // On the node's own sx, not in props (AGL-1346): `props.sx` renders but
439
+ // the Styles panel cannot edit or clear it.
440
+ sx: {
441
+ alignItems: 'stretch'
442
+ },
443
+ nodes: [
444
+ navLink('Home'),
445
+ navLink('About'),
446
+ navLink('Contact')
447
+ ]
448
+ };
449
+ export const drawerPresets = [
450
+ {
451
+ $id: generatePresetId(DRAWER_ID),
452
+ type: Aglyn.NodeType.PRESET,
453
+ displayName: 'Drawer',
454
+ pluginId: BUNDLE_ID,
455
+ description: 'Slide-in panel with a vertical link stack; open it with a Menu ' + 'Button or an interaction',
456
+ category: Aglyn.ComponentCategory.NAVIGATION,
457
+ icon: drawerSchema.icon,
458
+ data: {
459
+ $id: null,
460
+ componentId: DRAWER_ID,
461
+ pluginId: BUNDLE_ID,
462
+ props: {},
463
+ nodes: [
464
+ drawerLinkStack
465
+ ]
466
+ }
467
+ },
468
+ {
469
+ $id: generatePresetId(DRAWER_TOGGLE_ID),
470
+ type: Aglyn.NodeType.PRESET,
471
+ displayName: 'Menu Button',
472
+ pluginId: BUNDLE_ID,
473
+ description: 'Hamburger icon button that opens a drawer',
474
+ category: Aglyn.ComponentCategory.NAVIGATION,
475
+ icon: drawerToggleSchema.icon,
476
+ data: {
477
+ $id: null,
478
+ componentId: DRAWER_TOGGLE_ID,
479
+ pluginId: BUNDLE_ID,
480
+ props: {}
481
+ }
482
+ },
483
+ {
484
+ // One-insert mobile navigation (AGL-562): hamburger (hidden on
485
+ // desktop) wired to a drawer of links (link cluster hidden below
486
+ // desktop). Media-band sx mirrors the styles panel's Visibility
487
+ // control, so authors can adjust it there afterwards.
488
+ $id: generatePresetId(DRAWER_ID, 'mobile-nav'),
489
+ type: Aglyn.NodeType.PRESET,
490
+ displayName: 'Mobile Nav',
491
+ pluginId: BUNDLE_ID,
492
+ description: 'Menu button plus drawer for small screens, with an inline link ' + 'row on desktop — a working responsive nav in one insert',
493
+ category: Aglyn.ComponentCategory.NAVIGATION,
494
+ icon: {
495
+ path: mdiMenu.path,
496
+ sx: {
497
+ color: '#1976d2'
498
+ }
499
+ },
500
+ data: {
501
+ $id: null,
502
+ componentId: 'muiStack',
503
+ pluginId: BUNDLE_ID,
504
+ props: {
505
+ direction: 'row',
506
+ spacing: 1
507
+ },
508
+ sx: {
509
+ alignItems: 'center'
510
+ },
511
+ nodes: [
512
+ {
513
+ $id: null,
514
+ componentId: DRAWER_TOGGLE_ID,
515
+ pluginId: BUNDLE_ID,
516
+ props: {
517
+ ariaLabel: 'Open menu'
518
+ },
519
+ // Hidden where the inline links show. On `sx`, which is what the
520
+ // panel's Visibility switches read and write (AGL-1346) — in
521
+ // `props.sx` this band was invisible to the very control the
522
+ // comment below promises authors can adjust it with.
523
+ sx: {
524
+ [Aglyn.VISIBILITY_BAND_MEDIA.desktop]: {
525
+ display: 'none'
526
+ }
527
+ }
528
+ },
529
+ {
530
+ $id: null,
531
+ componentId: 'muiStack',
532
+ pluginId: BUNDLE_ID,
533
+ props: {
534
+ direction: 'row',
535
+ spacing: 1
536
+ },
537
+ sx: {
538
+ [Aglyn.VISIBILITY_BAND_MEDIA.mobile]: {
539
+ display: 'none'
540
+ },
541
+ [Aglyn.VISIBILITY_BAND_MEDIA.tablet]: {
542
+ display: 'none'
543
+ }
544
+ },
545
+ nodes: [
546
+ navLink('Home'),
547
+ navLink('About'),
548
+ navLink('Contact')
549
+ ]
550
+ },
551
+ {
552
+ $id: null,
553
+ componentId: DRAWER_ID,
554
+ pluginId: BUNDLE_ID,
555
+ props: {},
556
+ nodes: [
557
+ drawerLinkStack
558
+ ]
559
+ }
560
+ ]
561
+ }
562
+ }
563
+ ];
564
+ export default DrawerElement;
565
+
566
+ //# sourceMappingURL=drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/drawer.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 { mdiClose, mdiDockLeft, mdiMenu } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport Box from '@mui/material/Box'\nimport MuiDrawer from '@mui/material/Drawer'\nimport IconButton from '@mui/material/IconButton'\nimport Stack from '@mui/material/Stack'\nimport type { SxProps } from '@mui/material/styles'\nimport Typography from '@mui/material/Typography'\nimport {\n forwardRef,\n type ReactNode,\n useEffect,\n useMemo,\n useState,\n} 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 DRAWER_ID: Aglyn.ComponentId = 'muiDrawer'\nexport const DRAWER_TOGGLE_ID: Aglyn.ComponentId = 'muiDrawerToggle'\n\nexport type DrawerAnchor = 'left' | 'right' | 'top' | 'bottom'\n\n/** All four edges MUI's Drawer supports. */\nexport const DRAWER_ANCHORS: DrawerAnchor[] = ['left', 'right', 'top', 'bottom']\n\n/** Horizontal anchors are the ones the `width` setting applies to. */\nexport function isSideAnchor(anchor: unknown): boolean {\n return anchor !== 'top' && anchor !== 'bottom'\n}\n\nexport interface DrawerElementProps {\n /** Which edge the drawer slides in from. */\n anchor?: DrawerAnchor\n /** CSS width of the open panel (default 280px). Side anchors only. */\n width?: string\n /**\n * Authored node styles, handed over by the renderer rather than typed into\n * an attribute — recomposed below so the panel's own sx is merged, not\n * replaced (AGL-1323 declared it; the merge predates it).\n */\n sx?: SxProps\n children?: ReactNode\n}\n\nexport interface DrawerToggleProps {\n /**\n * Legacy pre-AGL-572 binding attribute. Persisted values are accepted\n * and silently ignored (no migration): behavior config rides the\n * interactions system (AGL-568), so explicit targeting is authored as\n * *When clicked → Open/close a drawer* on the button instead.\n */\n targetNodeId?: string\n /** Accessible name for the icon button. */\n ariaLabel?: string\n}\n\n/**\n * The node id encoded in the renderer's stable `data-aglyn=\"leaf:<id>\"`\n * attribute (the same selector contract the interactions system uses).\n */\nexport function parseLeafNodeId(dataAglyn: unknown): string | undefined {\n const match = /^leaf:(.+)$/.exec(String(dataAglyn ?? ''))\n return match?.[1] || undefined\n}\n\n/**\n * True while the besigner canvas flags this leaf's subtree as holding the\n * current selection (AGL-571): the renderer stamps\n * `data-aglyn-selected-within` on a leaf whenever the node itself or any\n * descendant is selected, and drops it when selection leaves the subtree.\n * Live surfaces never set it, so absence simply means \"render collapsed\" —\n * the same neutral `data-aglyn*` leaf contract `parseLeafNodeId` reads.\n */\nexport function isLeafSelectedWithin(rest: Record<string, unknown>): boolean {\n return rest['data-aglyn-selected-within'] != null\n}\n\n/**\n * Mounted drawers in mount order. Broadcast commands (no target node id)\n * are answered by the FIRST registered drawer only, so a page with one\n * drawer — the overwhelmingly common case — needs no wiring at all.\n */\nconst mountedDrawers: string[] = []\n\n/** Test seam: the current broadcast owner (first mounted drawer). */\nexport function firstMountedDrawer(): string | undefined {\n return mountedDrawers[0]\n}\n\n/**\n * The canvas marker's box (AGL-1236). A drawer is a portal — it contributes\n * nothing to the layout of the row it sits in — and on the canvas it has to\n * cost exactly as little.\n *\n * `position: absolute` is doing the real work, and zero-sizing alone was not\n * enough: a zero-WIDTH flex child is still a flex child, so the nav's\n * `justify-content: space-between` kept it as a distribution point and its\n * `gap: 24px` still applied on both sides — which pushed START FREE in off\n * the right edge for an element that renders nowhere near it. Out of flow,\n * the row lays out exactly as it ships.\n *\n * The visible chip is `fixed` to the canvas frame, so it no longer matters\n * that an abspos flex child takes its static position from the container's\n * justify-content (that is what once parked the chip on top of the logo).\n */\nconst MARKER_SX = {\n position: 'absolute',\n width: 0,\n height: 0,\n overflow: 'visible',\n m: 0,\n p: 0,\n} as const\n\n/**\n * Slide-in drawer (AGL-562): a canvas children slot that opens from the\n * page edge — the mobile-menu building block. Opens/closes/toggles via\n * the shared window event bus (`dispatchDrawerCommand`), reachable from\n * the interactions system's drawer steps and the Menu Button element.\n * SSR ships it closed; the canvas shows a slim collapsed placeholder that\n * expands its contents inline only while the drawer or one of its\n * descendants is selected (AGL-571) — full drawer designability is\n * AGL-572.\n */\nconst DrawerElement = forwardRef<HTMLDivElement, DrawerElementProps>(\n (props, ref) => {\n const { anchor, width, children, sx, ...rest } = props\n // Node styles ride the renderer-merged sx; recompose (stack.ts pattern).\n const nodeSx = Array.isArray(sx) ? sx : sx ? [sx] : []\n const resolvedAnchor: DrawerAnchor = DRAWER_ANCHORS.includes(\n anchor as DrawerAnchor,\n )\n ? (anchor as DrawerAnchor)\n : 'left'\n // A top/bottom sheet spans the viewport; a width would pin it to a\n // narrow strip against the edge, which is never what was meant.\n const sideAnchored = isSideAnchor(resolvedAnchor)\n const { editorInert } = Aglyn.useScreenLink(undefined)\n const [open, setOpen] = useState(false)\n // The renderer stamps the node id on every leaf; commands target it.\n const nodeId = useMemo(\n () => parseLeafNodeId((rest as Record<string, unknown>)['data-aglyn']),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [(rest as Record<string, unknown>)['data-aglyn']],\n )\n\n useEffect(() => {\n if (editorInert || !nodeId) return undefined\n mountedDrawers.push(nodeId)\n const unsubscribe = Aglyn.subscribeDrawerCommands((detail) => {\n // Match on the un-namespaced suffix (AGL-573): an interaction\n // authored on a layout-scoped drawer stores the raw canvas id\n // while this drawer's live id is `layout__`-namespaced (or vice\n // versa) — leafIdsMatch compares them equal without re-authoring.\n const targeted = detail.nodeId\n ? Aglyn.leafIdsMatch(detail.nodeId, nodeId)\n : mountedDrawers[0] === nodeId\n if (!targeted) return\n if (detail.command === 'open') setOpen(true)\n else if (detail.command === 'close') setOpen(false)\n else setOpen((value) => !value)\n })\n return () => {\n unsubscribe()\n const index = mountedDrawers.indexOf(nodeId)\n if (index >= 0) mountedDrawers.splice(index, 1)\n }\n }, [editorInert, nodeId])\n\n if (editorInert) {\n // Editor affordance: a selectable marker mirroring the live\n // hidden-until-opened drawer (AGL-571). While the drawer or a\n // descendant is selected, the contents expand inline as a real\n // design surface sized to the configured width with the live\n // panel's padding (AGL-572), so links and headers render full size.\n const authoring = isLeafSelectedWithin(rest as Record<string, unknown>)\n if (authoring) {\n // Selected: render the panel the way it actually opens — an overlay\n // pinned to its anchor edge, at the configured width, with the same\n // padding and close row as the live drawer (AGL-1236). It used to be\n // an inline block in the parent's flow, so selecting it shoved the\n // nav row apart and showed a column that looked nothing like the\n // panel that ships. Zero-size wrapper + `fixed` child, so the design\n // surface behind it is untouched while it is open.\n const edge = { left: 0, right: 0, top: 0, bottom: 0 }\n const opposite = { left: 'right', right: 'left', top: 'bottom', bottom: 'top' } as const\n delete (edge as Record<string, number>)[opposite[resolvedAnchor]]\n return (\n <Box\n ref={ref}\n {...rest}\n sx={MARKER_SX}\n >\n <Box\n sx={[\n {\n position: 'fixed',\n ...edge,\n zIndex: 3,\n overflowY: 'auto',\n backgroundColor: 'background.paper',\n boxShadow: 16,\n },\n sideAnchored\n ? { width: width || 280, maxWidth: '90vw', p: 2 }\n : { width: 'auto', p: 2 },\n ...nodeSx,\n ]}\n >\n <Stack direction=\"row\" sx={{ justifyContent: 'flex-end', mb: 1 }}>\n <IconButton aria-label=\"Close menu\" size=\"small\" disabled>\n <MdiIcon path={mdiClose.path} />\n </IconButton>\n </Stack>\n {children}\n </Box>\n </Box>\n )\n }\n // Unselected: a ZERO-SIZE anchor in flow, with its chip lifted out of\n // the design surface to the canvas's top-right (AGL-1236).\n //\n // A drawer is a portal — on the live site it contributes nothing to\n // the layout of the row it sits in — so a placeholder that consumed\n // 280px made the canvas disagree with the page it renders: the\n // marketing nav's logo collapsed and overlapped the first link.\n //\n // Two things were needed, and the first alone was not enough.\n // Absolute positioning on the marker itself takes its static position\n // from the flex container's justify-content, so under `space-between`\n // it jumped to the START of the row and sat on top of the logo. The\n // marker is therefore a zero-size in-flow box — correct place in the\n // row, no reserved space — and the chip is `fixed` to the top-right\n // of the canvas so it never covers content at all.\n //\n // Author `sx` deliberately does NOT apply here: on the live site\n // those styles land on the panel body, which is not on screen while\n // the drawer is closed. Applying `width: 280` to the marker would put\n // the footprint straight back.\n return (\n <Box\n ref={ref}\n {...rest}\n sx={MARKER_SX}\n >\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{\n // `fixed` resolves against the canvas viewport frame (it is a\n // containing block), so a negative top puts the chip in the\n // gutter ABOVE the design surface rather than over it.\n position: 'fixed',\n top: -26,\n insetInlineEnd: 0,\n zIndex: 2,\n px: 0.75,\n py: 0.25,\n border: '1px dashed',\n borderColor: 'divider',\n borderRadius: 1,\n backgroundColor: 'background.paper',\n whiteSpace: 'nowrap',\n lineHeight: 1.4,\n opacity: 0.85,\n }}\n >\n {`Drawer · ${resolvedAnchor}`}\n </Typography>\n </Box>\n )\n }\n\n return (\n <MuiDrawer\n ref={ref}\n {...rest}\n anchor={resolvedAnchor}\n open={open}\n onClose={() => setOpen(false)}\n variant=\"temporary\"\n >\n {/* Node styles land on the panel body, where authors expect\n padding/background edits to show. */}\n <Box\n sx={[\n sideAnchored\n ? { width: width || 280, maxWidth: '90vw', p: 2 }\n : { width: 'auto', p: 2 },\n ...nodeSx,\n ]}\n >\n <Stack direction=\"row\" sx={{ justifyContent: 'flex-end', mb: 1 }}>\n <IconButton\n aria-label=\"Close menu\"\n onClick={() => setOpen(false)}\n size=\"small\"\n >\n <MdiIcon path={mdiClose.path} />\n </IconButton>\n </Stack>\n {children}\n </Box>\n </MuiDrawer>\n )\n },\n)\nDrawerElement.displayName = 'AglynDrawer'\n\n/**\n * Menu button (AGL-562, targeting moved to interactions in AGL-572): a\n * hamburger icon button whose click broadcast-toggles the page's first\n * drawer over the shared command bus — the zero-config default that\n * makes the one-insert Mobile Nav preset work. Targeting a specific\n * drawer is an interaction (*When clicked → Open/close a drawer*), the\n * same openDrawer/closeDrawer/toggleDrawer steps any element can use;\n * the legacy `targetNodeId` attribute is discarded here so persisted\n * values neither retarget the click nor leak into the DOM. Inert on the\n * static canvas so canvas clicks only select; live (incl. Preview) it toggles.\n */\nexport const DrawerToggle = forwardRef<HTMLButtonElement, DrawerToggleProps>(\n (props, ref) => {\n const { targetNodeId: _ignoredLegacyBinding, ariaLabel, ...rest } = props\n const { editorInert } = Aglyn.useScreenLink(undefined)\n return (\n <IconButton\n ref={ref}\n color=\"inherit\"\n aria-label={ariaLabel || 'Open menu'}\n onClick={\n editorInert ? undefined : () => Aglyn.dispatchDrawerCommand('toggle')\n }\n {...rest}\n >\n <MdiIcon path={mdiMenu.path} />\n </IconButton>\n )\n },\n)\nDrawerToggle.displayName = 'AglynDrawerToggle'\n\nexport const drawerSchema: Aglyn.ComponentSchema<DrawerElementProps> = {\n $id: DRAWER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Drawer',\n description:\n 'A panel that slides in from an edge. Pair it with a Menu Button.',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiDockLeft.path, sx: { color: '#2196f3' } },\n attributes: [\n {\n name: 'anchor',\n description: 'Which edge of the page the drawer slides in from.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Slides in from',\n // `left` is one of MUI's four anchors and the value `resolvedAnchor`\n // already falls back to, so the sentinel is real (AGL-1451). Spelled\n // `''` it could not persist (AGL-1191), which meant a drawer moved to\n // the right could never be moved back.\n options: [\n { value: 'left', label: 'Left (default)' },\n { value: 'right', label: 'Right' },\n { value: 'top', label: 'Top' },\n { value: 'bottom', label: 'Bottom' },\n ],\n },\n {\n name: 'width',\n description:\n 'Width of the open drawer, e.g. 280px or 20rem. Top and ' +\n 'bottom drawers span the viewport instead.',\n component: Aglyn.FieldComponentType.CSS_DIMENSION,\n label: 'Width',\n // A width on a top/bottom sheet is ignored by the renderer, so the\n // control is hidden rather than left to do nothing.\n condition: { when: 'anchor', is: ['top', 'bottom'], notMatch: true },\n },\n ],\n}\n\nexport const drawerToggleSchema: Aglyn.ComponentSchema<DrawerToggleProps> = {\n $id: DRAWER_TOGGLE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Menu Button',\n description: \"A hamburger button that opens the page's drawer.\",\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMenu.path, sx: { color: '#2196f3' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n // No drawer-binding attribute (AGL-572): clicking toggles the page's\n // first drawer out of the box, and targeting a specific drawer is an\n // interaction (*When clicked → Open/close a drawer*), per the AGL-568\n // rule that behavior config rides interactions, not bespoke props.\n attributes: [\n {\n name: 'ariaLabel',\n description: 'Accessible name announced by screen readers.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Accessibility label',\n },\n ],\n}\n\nconst navLink = (label: string) => ({\n $id: null,\n componentId: 'muiScreenLink',\n pluginId: BUNDLE_ID,\n props: { children: label, color: 'inherit' },\n})\n\n/** Vertical link stack every drawer preset starts from. */\nconst drawerLinkStack = {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { spacing: 1 },\n // On the node's own sx, not in props (AGL-1346): `props.sx` renders but\n // the Styles panel cannot edit or clear it.\n sx: { alignItems: 'stretch' },\n nodes: [navLink('Home'), navLink('About'), navLink('Contact')],\n}\n\nexport const drawerPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(DRAWER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Drawer',\n pluginId: BUNDLE_ID,\n description:\n 'Slide-in panel with a vertical link stack; open it with a Menu ' +\n 'Button or an interaction',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: drawerSchema.icon,\n data: {\n $id: null,\n componentId: DRAWER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [drawerLinkStack],\n },\n },\n {\n $id: generatePresetId(DRAWER_TOGGLE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Menu Button',\n pluginId: BUNDLE_ID,\n description: 'Hamburger icon button that opens a drawer',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: drawerToggleSchema.icon,\n data: {\n $id: null,\n componentId: DRAWER_TOGGLE_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n // One-insert mobile navigation (AGL-562): hamburger (hidden on\n // desktop) wired to a drawer of links (link cluster hidden below\n // desktop). Media-band sx mirrors the styles panel's Visibility\n // control, so authors can adjust it there afterwards.\n $id: generatePresetId(DRAWER_ID, 'mobile-nav'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Mobile Nav',\n pluginId: BUNDLE_ID,\n description:\n 'Menu button plus drawer for small screens, with an inline link ' +\n 'row on desktop — a working responsive nav in one insert',\n category: Aglyn.ComponentCategory.NAVIGATION,\n icon: { path: mdiMenu.path, sx: { color: '#1976d2' } },\n data: {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n sx: { alignItems: 'center' },\n nodes: [\n {\n $id: null,\n componentId: DRAWER_TOGGLE_ID,\n pluginId: BUNDLE_ID,\n props: { ariaLabel: 'Open menu' },\n // Hidden where the inline links show. On `sx`, which is what the\n // panel's Visibility switches read and write (AGL-1346) — in\n // `props.sx` this band was invisible to the very control the\n // comment below promises authors can adjust it with.\n sx: {\n [Aglyn.VISIBILITY_BAND_MEDIA.desktop]: { display: 'none' },\n },\n },\n {\n $id: null,\n componentId: 'muiStack',\n pluginId: BUNDLE_ID,\n props: { direction: 'row', spacing: 1 },\n sx: {\n [Aglyn.VISIBILITY_BAND_MEDIA.mobile]: { display: 'none' },\n [Aglyn.VISIBILITY_BAND_MEDIA.tablet]: { display: 'none' },\n },\n nodes: [navLink('Home'), navLink('About'), navLink('Contact')],\n },\n {\n $id: null,\n componentId: DRAWER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n nodes: [drawerLinkStack],\n },\n ],\n },\n },\n]\n\nexport default DrawerElement\n"],"names":["Aglyn","mdiClose","mdiDockLeft","mdiMenu","MdiIcon","Box","MuiDrawer","IconButton","Stack","Typography","forwardRef","useEffect","useMemo","useState","BUNDLE_ID","generatePresetId","DRAWER_ID","DRAWER_TOGGLE_ID","DRAWER_ANCHORS","isSideAnchor","anchor","parseLeafNodeId","dataAglyn","match","exec","String","undefined","isLeafSelectedWithin","rest","mountedDrawers","firstMountedDrawer","MARKER_SX","position","width","height","overflow","m","p","DrawerElement","props","ref","children","sx","nodeSx","Array","isArray","resolvedAnchor","includes","sideAnchored","editorInert","useScreenLink","open","setOpen","nodeId","push","unsubscribe","subscribeDrawerCommands","detail","targeted","leafIdsMatch","command","value","index","indexOf","splice","authoring","edge","left","right","top","bottom","opposite","zIndex","overflowY","backgroundColor","boxShadow","maxWidth","direction","justifyContent","mb","aria-label","size","disabled","path","variant","color","insetInlineEnd","px","py","border","borderColor","borderRadius","whiteSpace","lineHeight","opacity","onClose","onClick","displayName","DrawerToggle","targetNodeId","_ignoredLegacyBinding","ariaLabel","dispatchDrawerCommand","drawerSchema","$id","pluginId","description","category","ComponentCategory","NAVIGATION","icon","attributes","name","component","FieldComponentType","SELECT","label","options","CSS_DIMENSION","condition","when","is","notMatch","drawerToggleSchema","flags","selfClosing","FEATURE_FLAG","ENABLED","TEXT_FIELD","navLink","componentId","drawerLinkStack","spacing","alignItems","nodes","drawerPresets","type","NodeType","PRESET","data","VISIBILITY_BAND_MEDIA","desktop","display","mobile","tablet"],"mappings":"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"}