@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,336 @@
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 { mdiImageMultiple, mdiImageMultipleOutline } from "@aglyn/shared-data-mdi";
21
+ import MuiImageList from "@mui/material/ImageList";
22
+ import MuiImageListItem from "@mui/material/ImageListItem";
23
+ import MuiImageListItemBar from "@mui/material/ImageListItemBar";
24
+ import { forwardRef } from "react";
25
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
26
+ import { dropClearedProps } from "../utils/drop-cleared-props.js";
27
+ import { generatePresetId } from "../utils/generate-preset-id.js";
28
+ // Component ids are persisted in screen documents; never rename.
29
+ export const IMAGE_LIST_ID = 'muiImageList';
30
+ export const IMAGE_LIST_ITEM_ID = 'muiImageListItem';
31
+ /** Number fields round-trip as strings; MUI's grid math needs numbers. */ export function toCount(value, fallback) {
32
+ if (value == null || value === '') return fallback;
33
+ const parsed = Number(value);
34
+ if (!Number.isFinite(parsed) || parsed < 0) return fallback;
35
+ return Math.round(parsed);
36
+ }
37
+ /**
38
+ * Image list (https://mui.com/material-ui/react-image-list/): a masonry
39
+ * or quilted grid of images, denser than a Grid of cards.
40
+ */ const ImageListElement = /*#__PURE__*/ forwardRef((rawProps, ref)=>{
41
+ // Cleared props dropped before the resolvers below read them
42
+ // (AGL-1451); `rest` also spreads straight into MUI.
43
+ const props = dropClearedProps(rawProps);
44
+ const { cols, gap, rowHeight, variant, children } = props, rest = _object_without_properties_loose(props, [
45
+ "cols",
46
+ "gap",
47
+ "rowHeight",
48
+ "variant",
49
+ "children"
50
+ ]);
51
+ const resolvedVariant = [
52
+ 'standard',
53
+ 'quilted',
54
+ 'masonry',
55
+ 'woven'
56
+ ].includes(variant) ? variant : 'standard';
57
+ const height = toCount(rowHeight);
58
+ return /*#__PURE__*/ _jsx(MuiImageList, _extends({
59
+ ref: ref,
60
+ variant: resolvedVariant,
61
+ cols: toCount(cols, 3),
62
+ gap: toCount(gap, 4),
63
+ // A masonry list sizes rows from the images themselves; a fixed
64
+ // rowHeight silently defeats the whole variant.
65
+ rowHeight: resolvedVariant === 'masonry' ? 'auto' : height != null ? height : 'auto'
66
+ }, rest, {
67
+ // MUI types ImageList's children as NonNullable; the renderer
68
+ // always supplies the tile subtree.
69
+ children: children
70
+ }));
71
+ });
72
+ ImageListElement.displayName = 'AglynImageList';
73
+ /**
74
+ * One tile. The image itself is an ordinary Image element dropped
75
+ * inside, so tiles keep the media-CDN srcSet, lazy loading and
76
+ * empty-source placeholder (AGL-74/175) rather than a second, worse
77
+ * image implementation living here.
78
+ */ export const ImageListItemElement = /*#__PURE__*/ forwardRef((rawProps, ref)=>{
79
+ const props = dropClearedProps(rawProps);
80
+ const { cols, rows, title, subtitle, barPosition, children } = props, rest = _object_without_properties_loose(props, [
81
+ "cols",
82
+ "rows",
83
+ "title",
84
+ "subtitle",
85
+ "barPosition",
86
+ "children"
87
+ ]);
88
+ return /*#__PURE__*/ _jsxs(MuiImageListItem, _extends({
89
+ ref: ref,
90
+ cols: toCount(cols, 1),
91
+ rows: toCount(rows, 1)
92
+ }, rest, {
93
+ children: [
94
+ children,
95
+ title || subtitle ? /*#__PURE__*/ _jsx(MuiImageListItemBar, {
96
+ title: title || undefined,
97
+ subtitle: subtitle || undefined,
98
+ position: barPosition || 'bottom'
99
+ }) : null
100
+ ]
101
+ }));
102
+ });
103
+ ImageListItemElement.displayName = 'AglynImageListItem';
104
+ /** Row height is meaningless on masonry — MUI forces `auto` there. */ const NOT_MASONRY = {
105
+ when: 'variant',
106
+ is: 'masonry',
107
+ notMatch: true
108
+ };
109
+ export const imageListSchema = {
110
+ $id: IMAGE_LIST_ID,
111
+ pluginId: BUNDLE_ID,
112
+ displayName: 'Image List',
113
+ description: 'Dense grid of images — standard, quilted, masonry or woven.',
114
+ category: Aglyn.ComponentCategory.MEDIA,
115
+ icon: {
116
+ path: mdiImageMultiple.path,
117
+ sx: {
118
+ color: '#7b1fa2'
119
+ }
120
+ },
121
+ restrictChildren: [
122
+ Aglyn.LinealDirectiveFlag.LIMIT_TO,
123
+ {
124
+ components: [
125
+ IMAGE_LIST_ITEM_ID
126
+ ]
127
+ }
128
+ ],
129
+ attributes: [
130
+ {
131
+ name: 'variant',
132
+ label: 'Variant',
133
+ description: 'Standard is a plain grid; quilted lets tiles span columns and ' + 'rows; masonry keeps each image’s own aspect ratio; woven ' + 'alternates tile sizes.',
134
+ component: Aglyn.FieldComponentType.SELECT,
135
+ // A real sentinel, not a deletion (AGL-1451): `standard` is one of
136
+ // MUI's four named variants and the resolver above already accepts
137
+ // it by name, so the option is a genuine choice — and the one an
138
+ // author needs in order to move a list BACK off quilted or masonry.
139
+ // Spelled `''` it could not persist (AGL-1191), so that move
140
+ // silently reverted.
141
+ options: [
142
+ {
143
+ value: 'standard',
144
+ label: 'Standard (default)'
145
+ },
146
+ {
147
+ value: 'quilted',
148
+ label: 'Quilted'
149
+ },
150
+ {
151
+ value: 'masonry',
152
+ label: 'Masonry'
153
+ },
154
+ {
155
+ value: 'woven',
156
+ label: 'Woven'
157
+ }
158
+ ]
159
+ },
160
+ {
161
+ name: 'cols',
162
+ label: 'Columns',
163
+ description: 'Number of columns. Default 3.',
164
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
165
+ type: 'number'
166
+ },
167
+ {
168
+ name: 'gap',
169
+ label: 'Gap',
170
+ description: 'Space between tiles, in px. Default 4.',
171
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
172
+ type: 'number'
173
+ },
174
+ {
175
+ name: 'rowHeight',
176
+ label: 'Row height',
177
+ description: 'Row height in px. Leave blank to size rows from the images.',
178
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
179
+ type: 'number',
180
+ condition: NOT_MASONRY
181
+ }
182
+ ]
183
+ };
184
+ export const imageListItemSchema = {
185
+ $id: IMAGE_LIST_ITEM_ID,
186
+ pluginId: BUNDLE_ID,
187
+ displayName: 'Image List Item',
188
+ description: 'One tile of an image list, with an optional caption bar.',
189
+ category: Aglyn.ComponentCategory.MEDIA,
190
+ icon: {
191
+ path: mdiImageMultipleOutline.path,
192
+ sx: {
193
+ color: '#7b1fa2'
194
+ }
195
+ },
196
+ attributes: [
197
+ {
198
+ name: 'title',
199
+ label: 'Caption',
200
+ description: 'Caption bar text. Leave both caption fields blank for no bar.',
201
+ component: Aglyn.FieldComponentType.TEXT_FIELD
202
+ },
203
+ {
204
+ name: 'subtitle',
205
+ label: 'Caption subtitle',
206
+ description: 'Second line of the caption bar.',
207
+ component: Aglyn.FieldComponentType.TEXT_FIELD
208
+ },
209
+ {
210
+ name: 'barPosition',
211
+ label: 'Caption position',
212
+ description: 'Where the caption bar sits relative to the image.',
213
+ component: Aglyn.FieldComponentType.SELECT,
214
+ // `bottom` is MUI's own position value and the `|| 'bottom'`
215
+ // fallback below already resolves to it, so the sentinel is the
216
+ // value the code was reaching for anyway (AGL-1451).
217
+ options: [
218
+ {
219
+ value: 'bottom',
220
+ label: 'Bottom (default)'
221
+ },
222
+ {
223
+ value: 'top',
224
+ label: 'Top'
225
+ },
226
+ {
227
+ value: 'below',
228
+ label: 'Below the image'
229
+ }
230
+ ]
231
+ },
232
+ // The spans are read only by the quilted layout, but the variant
233
+ // that decides that lives on the PARENT list. A `condition` is
234
+ // evaluated against this node's own attribute form and cannot see
235
+ // it, so these stay visible and say so in their help text — a
236
+ // condition here would hide them permanently.
237
+ {
238
+ name: 'cols',
239
+ label: 'Column span',
240
+ description: 'How many columns this tile spans. Only the parent list’s ' + 'Quilted variant reads this.',
241
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
242
+ type: 'number'
243
+ },
244
+ {
245
+ name: 'rows',
246
+ label: 'Row span',
247
+ description: 'How many rows this tile spans. Only the parent list’s Quilted ' + 'variant reads this.',
248
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
249
+ type: 'number'
250
+ }
251
+ ]
252
+ };
253
+ const tile = (caption)=>({
254
+ $id: null,
255
+ componentId: IMAGE_LIST_ITEM_ID,
256
+ pluginId: BUNDLE_ID,
257
+ props: {
258
+ title: caption
259
+ },
260
+ nodes: [
261
+ {
262
+ $id: null,
263
+ componentId: 'image',
264
+ pluginId: BUNDLE_ID,
265
+ props: {
266
+ alt: caption,
267
+ height: '160px',
268
+ objectFit: 'cover'
269
+ }
270
+ }
271
+ ]
272
+ });
273
+ export const imageListPresets = [
274
+ {
275
+ $id: generatePresetId(IMAGE_LIST_ID),
276
+ type: Aglyn.NodeType.PRESET,
277
+ displayName: 'Image List',
278
+ pluginId: BUNDLE_ID,
279
+ description: 'Three-column gallery with captioned tiles',
280
+ category: Aglyn.ComponentCategory.MEDIA,
281
+ icon: imageListSchema.icon,
282
+ data: {
283
+ $id: null,
284
+ componentId: IMAGE_LIST_ID,
285
+ pluginId: BUNDLE_ID,
286
+ props: {
287
+ cols: 3,
288
+ gap: 8,
289
+ rowHeight: 160
290
+ },
291
+ nodes: [
292
+ tile('First image'),
293
+ tile('Second image'),
294
+ tile('Third image')
295
+ ]
296
+ }
297
+ },
298
+ {
299
+ $id: generatePresetId(IMAGE_LIST_ID, 'masonry'),
300
+ type: Aglyn.NodeType.PRESET,
301
+ displayName: 'Image List Masonry',
302
+ pluginId: BUNDLE_ID,
303
+ description: 'Gallery that keeps each image’s own aspect ratio',
304
+ category: Aglyn.ComponentCategory.MEDIA,
305
+ icon: imageListSchema.icon,
306
+ data: {
307
+ $id: null,
308
+ componentId: IMAGE_LIST_ID,
309
+ pluginId: BUNDLE_ID,
310
+ props: {
311
+ variant: 'masonry',
312
+ cols: 3,
313
+ gap: 8
314
+ },
315
+ nodes: [
316
+ tile(''),
317
+ tile(''),
318
+ tile(''),
319
+ tile('')
320
+ ]
321
+ }
322
+ },
323
+ {
324
+ $id: generatePresetId(IMAGE_LIST_ITEM_ID),
325
+ type: Aglyn.NodeType.PRESET,
326
+ displayName: 'Image List Item',
327
+ pluginId: BUNDLE_ID,
328
+ description: 'One more tile to add to an existing image list',
329
+ category: Aglyn.ComponentCategory.MEDIA,
330
+ icon: imageListItemSchema.icon,
331
+ data: tile('')
332
+ }
333
+ ];
334
+ export default ImageListElement;
335
+
336
+ //# sourceMappingURL=image-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/image-list.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 { mdiImageMultiple, mdiImageMultipleOutline } from '@aglyn/shared-data-mdi'\nimport MuiImageList from '@mui/material/ImageList'\nimport MuiImageListItem from '@mui/material/ImageListItem'\nimport MuiImageListItemBar from '@mui/material/ImageListItemBar'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const IMAGE_LIST_ID: Aglyn.ComponentId = 'muiImageList'\nexport const IMAGE_LIST_ITEM_ID: Aglyn.ComponentId = 'muiImageListItem'\n\nexport type ImageListVariant = 'standard' | 'quilted' | 'masonry' | 'woven'\n\nexport interface ImageListElementProps {\n cols?: number | string\n gap?: number | string\n /** Row height in px, or blank for `auto`. */\n rowHeight?: number | string\n variant?: ImageListVariant\n children?: ReactNode\n}\n\nexport interface ImageListItemElementProps {\n /** Columns this tile spans (quilted lists). */\n cols?: number | string\n /** Rows this tile spans (quilted lists). */\n rows?: number | string\n /** Caption bar text; the bar is omitted entirely when both are blank. */\n title?: string\n subtitle?: string\n barPosition?: 'below' | 'top' | 'bottom'\n children?: ReactNode\n}\n\n/** Number fields round-trip as strings; MUI's grid math needs numbers. */\nexport function toCount(value: unknown, fallback?: number): number | undefined {\n if (value == null || value === '') return fallback\n const parsed = Number(value)\n if (!Number.isFinite(parsed) || parsed < 0) return fallback\n return Math.round(parsed)\n}\n\n/**\n * Image list (https://mui.com/material-ui/react-image-list/): a masonry\n * or quilted grid of images, denser than a Grid of cards.\n */\nconst ImageListElement = forwardRef<HTMLUListElement, ImageListElementProps>(\n (rawProps, ref) => {\n // Cleared props dropped before the resolvers below read them\n // (AGL-1451); `rest` also spreads straight into MUI.\n const props = dropClearedProps(rawProps)\n const { cols, gap, rowHeight, variant, children, ...rest } = props\n const resolvedVariant: ImageListVariant = (\n ['standard', 'quilted', 'masonry', 'woven'] as const\n ).includes(variant as ImageListVariant)\n ? (variant as ImageListVariant)\n : 'standard'\n const height = toCount(rowHeight)\n return (\n <MuiImageList\n ref={ref}\n variant={resolvedVariant}\n cols={toCount(cols, 3)}\n gap={toCount(gap, 4)}\n // A masonry list sizes rows from the images themselves; a fixed\n // rowHeight silently defeats the whole variant.\n rowHeight={resolvedVariant === 'masonry' ? 'auto' : height ?? 'auto'}\n {...rest}\n // MUI types ImageList's children as NonNullable; the renderer\n // always supplies the tile subtree.\n children={children as NonNullable<ReactNode>}\n />\n )\n },\n)\nImageListElement.displayName = 'AglynImageList'\n\n/**\n * One tile. The image itself is an ordinary Image element dropped\n * inside, so tiles keep the media-CDN srcSet, lazy loading and\n * empty-source placeholder (AGL-74/175) rather than a second, worse\n * image implementation living here.\n */\nexport const ImageListItemElement = forwardRef<\n HTMLLIElement,\n ImageListItemElementProps\n>((rawProps, ref) => {\n const props = dropClearedProps(rawProps)\n const { cols, rows, title, subtitle, barPosition, children, ...rest } = props\n return (\n <MuiImageListItem\n ref={ref}\n cols={toCount(cols, 1)}\n rows={toCount(rows, 1)}\n {...rest}\n >\n {children}\n {title || subtitle ? (\n <MuiImageListItemBar\n title={title || undefined}\n subtitle={subtitle || undefined}\n position={barPosition || 'bottom'}\n />\n ) : null}\n </MuiImageListItem>\n )\n})\nImageListItemElement.displayName = 'AglynImageListItem'\n\n/** Row height is meaningless on masonry — MUI forces `auto` there. */\nconst NOT_MASONRY = { when: 'variant', is: 'masonry', notMatch: true }\n\nexport const imageListSchema: Aglyn.ComponentSchema<ImageListElementProps> = {\n $id: IMAGE_LIST_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Image List',\n description: 'Dense grid of images — standard, quilted, masonry or woven.',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiImageMultiple.path, sx: { color: '#7b1fa2' } },\n restrictChildren: [\n Aglyn.LinealDirectiveFlag.LIMIT_TO,\n { components: [IMAGE_LIST_ITEM_ID] },\n ],\n attributes: [\n {\n name: 'variant',\n label: 'Variant',\n description:\n 'Standard is a plain grid; quilted lets tiles span columns and ' +\n 'rows; masonry keeps each image’s own aspect ratio; woven ' +\n 'alternates tile sizes.',\n component: Aglyn.FieldComponentType.SELECT,\n // A real sentinel, not a deletion (AGL-1451): `standard` is one of\n // MUI's four named variants and the resolver above already accepts\n // it by name, so the option is a genuine choice — and the one an\n // author needs in order to move a list BACK off quilted or masonry.\n // Spelled `''` it could not persist (AGL-1191), so that move\n // silently reverted.\n options: [\n { value: 'standard', label: 'Standard (default)' },\n { value: 'quilted', label: 'Quilted' },\n { value: 'masonry', label: 'Masonry' },\n { value: 'woven', label: 'Woven' },\n ],\n },\n {\n name: 'cols',\n label: 'Columns',\n description: 'Number of columns. Default 3.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'gap',\n label: 'Gap',\n description: 'Space between tiles, in px. Default 4.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'rowHeight',\n label: 'Row height',\n description:\n 'Row height in px. Leave blank to size rows from the images.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n condition: NOT_MASONRY,\n },\n ],\n}\n\nexport const imageListItemSchema: Aglyn.ComponentSchema<ImageListItemElementProps> =\n {\n $id: IMAGE_LIST_ITEM_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Image List Item',\n description: 'One tile of an image list, with an optional caption bar.',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: { path: mdiImageMultipleOutline.path, sx: { color: '#7b1fa2' } },\n attributes: [\n {\n name: 'title',\n label: 'Caption',\n description:\n 'Caption bar text. Leave both caption fields blank for no bar.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'subtitle',\n label: 'Caption subtitle',\n description: 'Second line of the caption bar.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'barPosition',\n label: 'Caption position',\n description: 'Where the caption bar sits relative to the image.',\n component: Aglyn.FieldComponentType.SELECT,\n // `bottom` is MUI's own position value and the `|| 'bottom'`\n // fallback below already resolves to it, so the sentinel is the\n // value the code was reaching for anyway (AGL-1451).\n options: [\n { value: 'bottom', label: 'Bottom (default)' },\n { value: 'top', label: 'Top' },\n { value: 'below', label: 'Below the image' },\n ],\n },\n // The spans are read only by the quilted layout, but the variant\n // that decides that lives on the PARENT list. A `condition` is\n // evaluated against this node's own attribute form and cannot see\n // it, so these stay visible and say so in their help text — a\n // condition here would hide them permanently.\n {\n name: 'cols',\n label: 'Column span',\n description:\n 'How many columns this tile spans. Only the parent list’s ' +\n 'Quilted variant reads this.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'rows',\n label: 'Row span',\n description:\n 'How many rows this tile spans. Only the parent list’s Quilted ' +\n 'variant reads this.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n }\n\nconst tile = (caption: string) => ({\n $id: null,\n componentId: IMAGE_LIST_ITEM_ID,\n pluginId: BUNDLE_ID,\n props: { title: caption },\n nodes: [\n {\n $id: null,\n componentId: 'image',\n pluginId: BUNDLE_ID,\n props: { alt: caption, height: '160px', objectFit: 'cover' },\n },\n ],\n})\n\nexport const imageListPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(IMAGE_LIST_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image List',\n pluginId: BUNDLE_ID,\n description: 'Three-column gallery with captioned tiles',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: imageListSchema.icon,\n data: {\n $id: null,\n componentId: IMAGE_LIST_ID,\n pluginId: BUNDLE_ID,\n props: { cols: 3, gap: 8, rowHeight: 160 },\n nodes: [tile('First image'), tile('Second image'), tile('Third image')],\n },\n },\n {\n $id: generatePresetId(IMAGE_LIST_ID, 'masonry'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image List Masonry',\n pluginId: BUNDLE_ID,\n description: 'Gallery that keeps each image’s own aspect ratio',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: imageListSchema.icon,\n data: {\n $id: null,\n componentId: IMAGE_LIST_ID,\n pluginId: BUNDLE_ID,\n props: { variant: 'masonry', cols: 3, gap: 8 },\n nodes: [tile(''), tile(''), tile(''), tile('')],\n },\n },\n {\n $id: generatePresetId(IMAGE_LIST_ITEM_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Image List Item',\n pluginId: BUNDLE_ID,\n description: 'One more tile to add to an existing image list',\n category: Aglyn.ComponentCategory.MEDIA,\n icon: imageListItemSchema.icon,\n data: tile(''),\n },\n]\n\nexport default ImageListElement\n"],"names":["Aglyn","mdiImageMultiple","mdiImageMultipleOutline","MuiImageList","MuiImageListItem","MuiImageListItemBar","forwardRef","BUNDLE_ID","dropClearedProps","generatePresetId","IMAGE_LIST_ID","IMAGE_LIST_ITEM_ID","toCount","value","fallback","parsed","Number","isFinite","Math","round","ImageListElement","rawProps","ref","props","cols","gap","rowHeight","variant","children","rest","resolvedVariant","includes","height","displayName","ImageListItemElement","rows","title","subtitle","barPosition","undefined","position","NOT_MASONRY","when","is","notMatch","imageListSchema","$id","pluginId","description","category","ComponentCategory","MEDIA","icon","path","sx","color","restrictChildren","LinealDirectiveFlag","LIMIT_TO","components","attributes","name","label","component","FieldComponentType","SELECT","options","TEXT_FIELD","type","condition","imageListItemSchema","tile","caption","componentId","nodes","alt","objectFit","imageListPresets","NodeType","PRESET","data"],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import * as Aglyn from '@aglyn/aglyn';
18
+ import type { SxProps } from '@mui/material/styles';
19
+ import { type ReactNode } from 'react';
20
+ export declare const ID: Aglyn.ComponentId;
21
+ /**
22
+ * The node ids of the images that load eagerly: the first image the LAYOUT
23
+ * chain renders and the first image the SCREEN renders, each in document
24
+ * order (AGL-2486).
25
+ *
26
+ * Every image used to render `loading="lazy"` — the hero included. That is
27
+ * the worst possible default for the one image that is almost always the LCP
28
+ * element: a lazy image is not fetched until layout has run and the browser
29
+ * has decided it is near the viewport, and it is fetched at LOW priority when
30
+ * it finally is. Lighthouse reports that as "LCP request discovery", and it is
31
+ * also why an image four sections down could finish before the one the reader
32
+ * is looking at: with everything lazy and everything low, nothing outranked
33
+ * anything, so the order was whatever the network felt like.
34
+ *
35
+ * So the lead images get `loading="eager"` and the rest get
36
+ * `fetchpriority="low"`, which is the browser-level knob that stops
37
+ * below-the-fold images competing with the ones above them.
38
+ *
39
+ * ONE lead image was not enough. On any site whose header carries a logo, the
40
+ * first image in document order is that logo, so the screen's own hero — the
41
+ * element Lighthouse names as the LCP on aglyn.com's solution pages at
42
+ * 375x812 — stayed `lazy` + `low`: undiscovered until layout, then fetched
43
+ * behind every script on the page (measured 2026-09-08: 1.5 s of load delay
44
+ * and 1.5 s of load time for a 13 KB image). Composition namespaces layout
45
+ * nodes by origin ({@link Aglyn.isLayoutComposedNodeId}), so the walk keeps
46
+ * one lead PER ORIGIN: the layout's first image (the logo) and the screen's
47
+ * first image (the hero). A layout image further down — a mega-menu
48
+ * illustration, the footer mark — is still deferred, as is every later
49
+ * screen image. In the besigner canvas nothing is layout-composed, so the
50
+ * screen's first image is the only lead, exactly as before.
51
+ *
52
+ * Neither lead gets `fetchpriority="high"`, and the reasoning is at the
53
+ * `fetchPriority` prop below. Short version: "first image of its origin" is
54
+ * still not "the LCP element", and a ranking claim needs evidence this
55
+ * function does not have.
56
+ *
57
+ * Resolved from the tree rather than a render-order counter on purpose: the
58
+ * renderer walks the tree in document order on the server AND on hydrate, but
59
+ * a mutable counter would double-count under React's concurrent re-renders
60
+ * and hand the priority to a different image on the client than the one the
61
+ * HTML gave it. A pure function of the tree cannot disagree with itself — and
62
+ * it is deliberately NOT memoized on the root object, because the canvas
63
+ * mutates that tree in place while an author works.
64
+ *
65
+ * The walk STOPS once both leads are known — on a page with a header logo
66
+ * and a hero that is O(nodes above the hero), a dozen or two on a normal
67
+ * page — and at the first image on a screen with no layout.
68
+ *
69
+ * An image with no `src` renders a placeholder box and no `<img>` at all, so
70
+ * it cannot be the LCP element and is skipped.
71
+ */
72
+ export declare function leadImageNodeIds(root: Aglyn.NodeSchema | undefined): readonly string[];
73
+ export interface ImageProps {
74
+ /**
75
+ * Where the image comes from (AGL-72). Either a **media reference** —
76
+ * `media:{scope}/{mediaId}`, what "Browse media" now stores (AGL-1215) —
77
+ * or any URL, which covers both the legacy values already in published
78
+ * documents and an author-typed hotlink. `resolveMediaSrc` decides.
79
+ */
80
+ src?: string;
81
+ alt?: string;
82
+ /**
83
+ * Explicit decorative choice (AGL-1305): ON forces `alt=""` (and drops
84
+ * the tooltip) no matter what the alt field says, so screen readers skip
85
+ * the image. Distinct from simply leaving alt unset, which also renders
86
+ * `alt=""` today but records no intent.
87
+ */
88
+ decorative?: boolean;
89
+ /** Tooltip shown on hover — the native img `title` attribute. */
90
+ title?: string;
91
+ /** Native loading hint; unset stays lazy, exactly as before AGL-1305. */
92
+ loading?: 'lazy' | 'eager';
93
+ objectFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
94
+ /** CSS width (e.g. "100%", "320px"); defaults to 100%. */
95
+ width?: string;
96
+ /** CSS height (e.g. "240px"); defaults to auto. */
97
+ height?: string;
98
+ /**
99
+ * The asset's own pixel dimensions, copied off the media document when the
100
+ * image was picked (AGL-2486), and read from the asset again when the page
101
+ * is composed, so a replace with a different shape reaches every page that
102
+ * places it (AGL-2833).
103
+ *
104
+ * NOT author controls, which is why neither appears in the schema below:
105
+ * they describe the file, and the CSS `width`/`height` above describe the
106
+ * placement. The pair becomes the `<img>`'s intrinsic `width`/`height`
107
+ * attributes, which is the only thing that lets the browser reserve the
108
+ * right box before the bytes arrive — with `width: 100%; height: auto` the
109
+ * element is otherwise zero-height until the image decodes, and every image
110
+ * on the page shifts the layout as it lands.
111
+ *
112
+ * An attribute pair is a RATIO here, not a size: CSS wins for the used
113
+ * dimensions either way, so a stale value costs nothing but a reservation
114
+ * of the wrong shape. Both must be present and positive or neither is
115
+ * emitted — one alone gives the browser no ratio and would be read as a
116
+ * real dimension.
117
+ *
118
+ * Read from the node like any other prop, so this element never fetches a
119
+ * media document: the composition has already laid the asset's current
120
+ * pair over the pick's (`media-asset-facts.ts`), and the pick's copy is
121
+ * what renders wherever that read cannot answer. `srcSet` still selects
122
+ * which variant is downloaded; these only say what shape it will be.
123
+ */
124
+ intrinsicWidth?: number;
125
+ /** See `intrinsicWidth` — the two are only ever used as a pair. */
126
+ intrinsicHeight?: number;
127
+ /** Border radius in px. */
128
+ radius?: number;
129
+ /** Target screen id — resolved rename-safe like Screen Link (AGL-339). */
130
+ screenId?: string;
131
+ /** External URL, used only when no `screenId` is set. */
132
+ href?: string;
133
+ /**
134
+ * Authored node styles, handed over by the renderer rather than typed into
135
+ * an attribute — recomposed below so the author's Styles-panel values are
136
+ * merged rather than replaced (AGL-1240).
137
+ */
138
+ sx?: SxProps;
139
+ /**
140
+ * Accepted and dropped: an `<img>` is a void element and React throws on
141
+ * ANY children value reaching one, which 500'd whole pages (AGL-579).
142
+ * Declared because the implementation deliberately discards it (AGL-1323).
143
+ */
144
+ children?: ReactNode;
145
+ }
146
+ /**
147
+ * Image element (AGL-74): renders a plain img with fit/size/radius
148
+ * controls; an empty src shows a labeled placeholder so the element stays
149
+ * visible and selectable in the editor.
150
+ */
151
+ declare const Image: import("react").ForwardRefExoticComponent<ImageProps & import("react").RefAttributes<HTMLElement>>;
152
+ export declare const schema: Aglyn.ComponentSchema<ImageProps>;
153
+ export declare const presets: Aglyn.PresetSchema[];
154
+ export default Image;