@aglyn/shared-ui-jsx 1.0.0-beta.235 → 1.0.0-beta.237

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/shared-ui-jsx",
3
- "version": "1.0.0-beta.235",
3
+ "version": "1.0.0-beta.237",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,10 +25,10 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/shared-data-mdi": "1.0.0-beta.235",
29
- "@aglyn/shared-ui-theme": "1.0.0-beta.235",
30
- "@aglyn/shared-util-tools": "1.0.0-beta.235",
31
- "@aglyn/shared-util-vendor": "1.0.0-beta.235",
28
+ "@aglyn/shared-data-mdi": "1.0.0-beta.237",
29
+ "@aglyn/shared-ui-theme": "1.0.0-beta.237",
30
+ "@aglyn/shared-util-tools": "1.0.0-beta.237",
31
+ "@aglyn/shared-util-vendor": "1.0.0-beta.237",
32
32
  "@mdi/js": "^7.4.47",
33
33
  "@swc/helpers": "0.5.23",
34
34
  "change-case": "^5.4.4",
@@ -0,0 +1,137 @@
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
+ /**
18
+ * How a lightbox looks and closes (AGL-3717), as plain data.
19
+ *
20
+ * ## Why this is its own module
21
+ *
22
+ * `lightbox-dialog.tsx` names `@mui/material/Dialog`, and the Dialog stack is
23
+ * kept off every first paint (AGL-1290): an element reaches the dialog through
24
+ * `lazy(() => import())` only once a visitor asks for it. What an element has
25
+ * to know BEFORE that — which props are the lightbox's, so it does not spread
26
+ * them onto its own DOM, and what each one is called in a props panel — lives
27
+ * here, with no React and no MUI, so reading it costs a page nothing.
28
+ *
29
+ * The field list is generic on purpose: it says what each setting is and what
30
+ * kind of control edits it, never how a particular editor draws that control.
31
+ * Every element that opens a lightbox maps it onto its own schema, so the
32
+ * Image, the Image List, the Video and the Lightbox container all offer the
33
+ * same settings under the same names, and a reusable component's property can
34
+ * be bound to any of them the same way.
35
+ */
36
+ /** What draws the close control. */
37
+ export declare const LIGHTBOX_CLOSE_STYLES: readonly ["icon", "filled", "text"];
38
+ export type LightboxCloseStyle = (typeof LIGHTBOX_CLOSE_STYLES)[number];
39
+ /** Where the close control sits: in the frame's top corner, or the screen's. */
40
+ export declare const LIGHTBOX_CLOSE_POSITIONS: readonly ["inside-end", "inside-start", "outside-end"];
41
+ export type LightboxClosePosition = (typeof LIGHTBOX_CLOSE_POSITIONS)[number];
42
+ /** Where a picture's caption goes: under it, over its foot, or nowhere. */
43
+ export declare const LIGHTBOX_CAPTION_PLACEMENTS: readonly ["below", "overlay", "hidden"];
44
+ export type LightboxCaptionPlacement = (typeof LIGHTBOX_CAPTION_PLACEMENTS)[number];
45
+ /** How the lightbox arrives. A visitor who asks for reduced motion gets `none`. */
46
+ export declare const LIGHTBOX_TRANSITIONS: readonly ["fade", "zoom", "none"];
47
+ export type LightboxTransition = (typeof LIGHTBOX_TRANSITIONS)[number];
48
+ /**
49
+ * The resolved settings. Every look field is optional and absent means "the
50
+ * element's own default", which is what keeps an element that existed before
51
+ * these settings — the Video — rendering exactly as it did. The two closing
52
+ * rules are always resolved, because a dialog has to decide them.
53
+ */
54
+ export interface LightboxAppearance {
55
+ backdropColor?: string;
56
+ /** 0–100. */
57
+ backdropOpacity?: number;
58
+ /** Pixels of blur behind the backdrop. */
59
+ backdropBlur?: number;
60
+ /** Any CSS length. */
61
+ maxWidth?: string;
62
+ maxHeight?: string;
63
+ padding?: string;
64
+ /** Corner radius of the frame, in pixels. */
65
+ radius?: number;
66
+ closeStyle?: LightboxCloseStyle;
67
+ closePosition?: LightboxClosePosition;
68
+ captionPlacement?: LightboxCaptionPlacement;
69
+ transition?: LightboxTransition;
70
+ closeOnBackdrop: boolean;
71
+ closeOnEscape: boolean;
72
+ }
73
+ /**
74
+ * The prop each setting is stored under on an element. Persisted in screen
75
+ * documents; never rename a value.
76
+ */
77
+ export declare const LIGHTBOX_APPEARANCE_PROPS: {
78
+ readonly backdropColor: "lightboxBackdropColor";
79
+ readonly backdropOpacity: "lightboxBackdropOpacity";
80
+ readonly backdropBlur: "lightboxBackdropBlur";
81
+ readonly maxWidth: "lightboxMaxWidth";
82
+ readonly maxHeight: "lightboxMaxHeight";
83
+ readonly padding: "lightboxPadding";
84
+ readonly radius: "lightboxRadius";
85
+ readonly closeStyle: "lightboxCloseStyle";
86
+ readonly closePosition: "lightboxClosePosition";
87
+ readonly captionPlacement: "lightboxCaptionPlacement";
88
+ readonly transition: "lightboxTransition";
89
+ readonly closeOnBackdrop: "lightboxCloseOnBackdrop";
90
+ readonly closeOnEscape: "lightboxCloseOnEscape";
91
+ };
92
+ export type LightboxAppearancePropName = (typeof LIGHTBOX_APPEARANCE_PROPS)[keyof typeof LIGHTBOX_APPEARANCE_PROPS];
93
+ /** Every appearance prop name, for an element to keep off its own DOM. */
94
+ export declare const LIGHTBOX_APPEARANCE_PROP_NAMES: readonly LightboxAppearancePropName[];
95
+ /** The props an element carries for its lightbox's look, as stored. */
96
+ export type LightboxAppearanceProps = Partial<Record<LightboxAppearancePropName, string | number | boolean | null>>;
97
+ /** The kind of control a setting is edited with. */
98
+ export type LightboxFieldKind = 'color' | 'number' | 'dimension' | 'select' | 'switch';
99
+ export interface LightboxFieldDescriptor {
100
+ name: LightboxAppearancePropName;
101
+ label: string;
102
+ description: string;
103
+ kind: LightboxFieldKind;
104
+ options?: ReadonlyArray<{
105
+ value: string;
106
+ label: string;
107
+ }>;
108
+ }
109
+ /**
110
+ * The settings, in the order a props panel lists them. Each select's first
111
+ * option is the default and a real value, so an author can always move back
112
+ * to it (a `''` option could not persist, AGL-1191).
113
+ */
114
+ export declare const LIGHTBOX_APPEARANCE_FIELDS: readonly LightboxFieldDescriptor[];
115
+ /**
116
+ * Reads the settings off an element's props. Anything unreadable is treated
117
+ * as unset rather than thrown on, because a stored document can hold any
118
+ * value and a lightbox that refuses to open over a bad radius is worse than
119
+ * one with square corners.
120
+ */
121
+ export declare function readLightboxAppearance(props: Readonly<Record<string, unknown>> | null | undefined): LightboxAppearance;
122
+ /**
123
+ * Splits an element's props into its lightbox settings and everything else,
124
+ * so the rest can be spread onto the element's DOM without a dozen unknown
125
+ * attributes riding along.
126
+ */
127
+ export declare function splitLightboxAppearanceProps<T extends object>(props: T): {
128
+ appearance: LightboxAppearance;
129
+ rest: Omit<T, LightboxAppearancePropName>;
130
+ };
131
+ /**
132
+ * The backdrop's paint: the color at the opacity asked for. A hex color is
133
+ * written as `rgba()`; any other color — a name, an `rgb()`, a theme variable
134
+ * — is mixed with transparency, which every browser that renders the dialog
135
+ * supports. `undefined` when neither is set, so the theme's backdrop stays.
136
+ */
137
+ export declare function lightboxBackdropBackground(appearance: Pick<LightboxAppearance, 'backdropColor' | 'backdropOpacity'>): string | undefined;
@@ -0,0 +1,308 @@
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
+ * How a lightbox looks and closes (AGL-3717), as plain data.
18
+ *
19
+ * ## Why this is its own module
20
+ *
21
+ * `lightbox-dialog.tsx` names `@mui/material/Dialog`, and the Dialog stack is
22
+ * kept off every first paint (AGL-1290): an element reaches the dialog through
23
+ * `lazy(() => import())` only once a visitor asks for it. What an element has
24
+ * to know BEFORE that — which props are the lightbox's, so it does not spread
25
+ * them onto its own DOM, and what each one is called in a props panel — lives
26
+ * here, with no React and no MUI, so reading it costs a page nothing.
27
+ *
28
+ * The field list is generic on purpose: it says what each setting is and what
29
+ * kind of control edits it, never how a particular editor draws that control.
30
+ * Every element that opens a lightbox maps it onto its own schema, so the
31
+ * Image, the Image List, the Video and the Lightbox container all offer the
32
+ * same settings under the same names, and a reusable component's property can
33
+ * be bound to any of them the same way.
34
+ */ /** What draws the close control. */ export const LIGHTBOX_CLOSE_STYLES = [
35
+ 'icon',
36
+ 'filled',
37
+ 'text'
38
+ ];
39
+ /** Where the close control sits: in the frame's top corner, or the screen's. */ export const LIGHTBOX_CLOSE_POSITIONS = [
40
+ 'inside-end',
41
+ 'inside-start',
42
+ 'outside-end'
43
+ ];
44
+ /** Where a picture's caption goes: under it, over its foot, or nowhere. */ export const LIGHTBOX_CAPTION_PLACEMENTS = [
45
+ 'below',
46
+ 'overlay',
47
+ 'hidden'
48
+ ];
49
+ /** How the lightbox arrives. A visitor who asks for reduced motion gets `none`. */ export const LIGHTBOX_TRANSITIONS = [
50
+ 'fade',
51
+ 'zoom',
52
+ 'none'
53
+ ];
54
+ /**
55
+ * The prop each setting is stored under on an element. Persisted in screen
56
+ * documents; never rename a value.
57
+ */ export const LIGHTBOX_APPEARANCE_PROPS = {
58
+ backdropColor: 'lightboxBackdropColor',
59
+ backdropOpacity: 'lightboxBackdropOpacity',
60
+ backdropBlur: 'lightboxBackdropBlur',
61
+ maxWidth: 'lightboxMaxWidth',
62
+ maxHeight: 'lightboxMaxHeight',
63
+ padding: 'lightboxPadding',
64
+ radius: 'lightboxRadius',
65
+ closeStyle: 'lightboxCloseStyle',
66
+ closePosition: 'lightboxClosePosition',
67
+ captionPlacement: 'lightboxCaptionPlacement',
68
+ transition: 'lightboxTransition',
69
+ closeOnBackdrop: 'lightboxCloseOnBackdrop',
70
+ closeOnEscape: 'lightboxCloseOnEscape'
71
+ };
72
+ /** Every appearance prop name, for an element to keep off its own DOM. */ export const LIGHTBOX_APPEARANCE_PROP_NAMES = Object.values(LIGHTBOX_APPEARANCE_PROPS);
73
+ /**
74
+ * The settings, in the order a props panel lists them. Each select's first
75
+ * option is the default and a real value, so an author can always move back
76
+ * to it (a `''` option could not persist, AGL-1191).
77
+ */ export const LIGHTBOX_APPEARANCE_FIELDS = [
78
+ {
79
+ name: 'lightboxBackdropColor',
80
+ label: 'Lightbox backdrop color',
81
+ description: 'The color behind the lightbox. Leave empty for a dark backdrop.',
82
+ kind: 'color'
83
+ },
84
+ {
85
+ name: 'lightboxBackdropOpacity',
86
+ label: 'Lightbox backdrop opacity',
87
+ description: 'How solid the backdrop is, from 0 (clear) to 100 (solid).',
88
+ kind: 'number'
89
+ },
90
+ {
91
+ name: 'lightboxBackdropBlur',
92
+ label: 'Lightbox backdrop blur',
93
+ description: 'Blurs the page behind the lightbox, in pixels. 0 for none.',
94
+ kind: 'number'
95
+ },
96
+ {
97
+ name: 'lightboxMaxWidth',
98
+ label: 'Lightbox max width',
99
+ description: 'The widest the lightbox frame grows, e.g. 1200px or 90vw.',
100
+ kind: 'dimension'
101
+ },
102
+ {
103
+ name: 'lightboxMaxHeight',
104
+ label: 'Lightbox max height',
105
+ description: 'The tallest the lightbox frame grows, e.g. 90vh.',
106
+ kind: 'dimension'
107
+ },
108
+ {
109
+ name: 'lightboxPadding',
110
+ label: 'Lightbox padding',
111
+ description: 'Space between the frame edge and what it shows.',
112
+ kind: 'dimension'
113
+ },
114
+ {
115
+ name: 'lightboxRadius',
116
+ label: 'Lightbox corner radius',
117
+ description: 'Rounds the frame corners, in pixels.',
118
+ kind: 'number'
119
+ },
120
+ {
121
+ name: 'lightboxCloseStyle',
122
+ label: 'Lightbox close button',
123
+ description: 'How the close button looks.',
124
+ kind: 'select',
125
+ options: [
126
+ {
127
+ value: 'icon',
128
+ label: 'Icon (default)'
129
+ },
130
+ {
131
+ value: 'filled',
132
+ label: 'Icon on a circle'
133
+ },
134
+ {
135
+ value: 'text',
136
+ label: 'The word Close'
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ name: 'lightboxClosePosition',
142
+ label: 'Lightbox close position',
143
+ description: 'Where the close button sits.',
144
+ kind: 'select',
145
+ options: [
146
+ {
147
+ value: 'inside-end',
148
+ label: 'Frame, top right (default)'
149
+ },
150
+ {
151
+ value: 'inside-start',
152
+ label: 'Frame, top left'
153
+ },
154
+ {
155
+ value: 'outside-end',
156
+ label: 'Screen, top right'
157
+ }
158
+ ]
159
+ },
160
+ {
161
+ name: 'lightboxCaptionPlacement',
162
+ label: 'Lightbox caption',
163
+ description: 'Where a picture caption shows in the lightbox.',
164
+ kind: 'select',
165
+ options: [
166
+ {
167
+ value: 'below',
168
+ label: 'Below the picture (default)'
169
+ },
170
+ {
171
+ value: 'overlay',
172
+ label: 'Over the picture'
173
+ },
174
+ {
175
+ value: 'hidden',
176
+ label: 'Hidden'
177
+ }
178
+ ]
179
+ },
180
+ {
181
+ name: 'lightboxTransition',
182
+ label: 'Lightbox transition',
183
+ description: 'How the lightbox opens. Visitors who ask for reduced motion always get none.',
184
+ kind: 'select',
185
+ options: [
186
+ {
187
+ value: 'fade',
188
+ label: 'Fade (default)'
189
+ },
190
+ {
191
+ value: 'zoom',
192
+ label: 'Zoom'
193
+ },
194
+ {
195
+ value: 'none',
196
+ label: 'None'
197
+ }
198
+ ]
199
+ },
200
+ {
201
+ name: 'lightboxCloseOnBackdrop',
202
+ label: 'Close on backdrop click',
203
+ description: 'Closes the lightbox when a visitor clicks outside it. On by default.',
204
+ kind: 'switch'
205
+ },
206
+ {
207
+ name: 'lightboxCloseOnEscape',
208
+ label: 'Close on Escape',
209
+ description: 'Closes the lightbox when a visitor presses Escape. On by default.',
210
+ kind: 'switch'
211
+ }
212
+ ];
213
+ const text = (value)=>{
214
+ if (typeof value === 'number' && Number.isFinite(value)) return `${value}`;
215
+ if (typeof value !== 'string') return undefined;
216
+ const trimmed = value.trim();
217
+ return trimmed ? trimmed : undefined;
218
+ };
219
+ const number = (value, min, max)=>{
220
+ if (value === null || value === undefined || value === '') return undefined;
221
+ const parsed = typeof value === 'number' ? value : Number(String(value).trim());
222
+ if (!Number.isFinite(parsed)) return undefined;
223
+ return Math.min(Math.max(parsed, min), max);
224
+ };
225
+ const oneOf = (value, allowed)=>typeof value === 'string' && allowed.includes(value) ? value : undefined;
226
+ /**
227
+ * A yes/no that defaults to yes: only an explicit no turns it off. The
228
+ * strings are what a document, a reusable component's text substitution or
229
+ * an older editor may hold.
230
+ */ const onUnlessNo = (value)=>!(value === false || value === 'false' || value === 'no' || value === 0);
231
+ /** A unitless length is pixels, as everywhere else on an element. */ const length = (value)=>{
232
+ const raw = text(value);
233
+ if (raw === undefined) return undefined;
234
+ return /^\d+(\.\d+)?$/.test(raw) ? `${raw}px` : raw;
235
+ };
236
+ /**
237
+ * Reads the settings off an element's props. Anything unreadable is treated
238
+ * as unset rather than thrown on, because a stored document can hold any
239
+ * value and a lightbox that refuses to open over a bad radius is worse than
240
+ * one with square corners.
241
+ */ export function readLightboxAppearance(props) {
242
+ const bag = props != null ? props : {};
243
+ const P = LIGHTBOX_APPEARANCE_PROPS;
244
+ const appearance = {
245
+ closeOnBackdrop: onUnlessNo(bag[P.closeOnBackdrop]),
246
+ closeOnEscape: onUnlessNo(bag[P.closeOnEscape])
247
+ };
248
+ const backdropColor = text(bag[P.backdropColor]);
249
+ if (backdropColor) appearance.backdropColor = backdropColor;
250
+ const backdropOpacity = number(bag[P.backdropOpacity], 0, 100);
251
+ if (backdropOpacity !== undefined) appearance.backdropOpacity = backdropOpacity;
252
+ const backdropBlur = number(bag[P.backdropBlur], 0, 64);
253
+ if (backdropBlur !== undefined) appearance.backdropBlur = backdropBlur;
254
+ const maxWidth = length(bag[P.maxWidth]);
255
+ if (maxWidth) appearance.maxWidth = maxWidth;
256
+ const maxHeight = length(bag[P.maxHeight]);
257
+ if (maxHeight) appearance.maxHeight = maxHeight;
258
+ const padding = length(bag[P.padding]);
259
+ if (padding) appearance.padding = padding;
260
+ const radius = number(bag[P.radius], 0, 999);
261
+ if (radius !== undefined) appearance.radius = radius;
262
+ const closeStyle = oneOf(bag[P.closeStyle], LIGHTBOX_CLOSE_STYLES);
263
+ if (closeStyle) appearance.closeStyle = closeStyle;
264
+ const closePosition = oneOf(bag[P.closePosition], LIGHTBOX_CLOSE_POSITIONS);
265
+ if (closePosition) appearance.closePosition = closePosition;
266
+ const captionPlacement = oneOf(bag[P.captionPlacement], LIGHTBOX_CAPTION_PLACEMENTS);
267
+ if (captionPlacement) appearance.captionPlacement = captionPlacement;
268
+ const transition = oneOf(bag[P.transition], LIGHTBOX_TRANSITIONS);
269
+ if (transition) appearance.transition = transition;
270
+ return appearance;
271
+ }
272
+ /**
273
+ * Splits an element's props into its lightbox settings and everything else,
274
+ * so the rest can be spread onto the element's DOM without a dozen unknown
275
+ * attributes riding along.
276
+ */ export function splitLightboxAppearanceProps(props) {
277
+ const rest = {};
278
+ const names = new Set(LIGHTBOX_APPEARANCE_PROP_NAMES);
279
+ for (const [key, value] of Object.entries(props)){
280
+ if (!names.has(key)) rest[key] = value;
281
+ }
282
+ return {
283
+ appearance: readLightboxAppearance(props),
284
+ rest: rest
285
+ };
286
+ }
287
+ /**
288
+ * The backdrop's paint: the color at the opacity asked for. A hex color is
289
+ * written as `rgba()`; any other color — a name, an `rgb()`, a theme variable
290
+ * — is mixed with transparency, which every browser that renders the dialog
291
+ * supports. `undefined` when neither is set, so the theme's backdrop stays.
292
+ */ export function lightboxBackdropBackground(appearance) {
293
+ const { backdropColor, backdropOpacity } = appearance;
294
+ if (!backdropColor && backdropOpacity === undefined) return undefined;
295
+ const color = backdropColor != null ? backdropColor : '#000000';
296
+ const opacity = backdropOpacity != null ? backdropOpacity : 80;
297
+ const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color);
298
+ if (hex) {
299
+ const digits = hex[1].length === 3 ? hex[1].split('').map((digit)=>digit + digit).join('') : hex[1];
300
+ const red = parseInt(digits.slice(0, 2), 16);
301
+ const green = parseInt(digits.slice(2, 4), 16);
302
+ const blue = parseInt(digits.slice(4, 6), 16);
303
+ return `rgba(${red}, ${green}, ${blue}, ${opacity / 100})`;
304
+ }
305
+ return `color-mix(in srgb, ${color} ${opacity}%, transparent)`;
306
+ }
307
+
308
+ //# sourceMappingURL=lightbox-appearance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../libs/shared/ui/jsx/src/lib/components/lightbox/lightbox-appearance.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\n/**\n * How a lightbox looks and closes (AGL-3717), as plain data.\n *\n * ## Why this is its own module\n *\n * `lightbox-dialog.tsx` names `@mui/material/Dialog`, and the Dialog stack is\n * kept off every first paint (AGL-1290): an element reaches the dialog through\n * `lazy(() => import())` only once a visitor asks for it. What an element has\n * to know BEFORE that — which props are the lightbox's, so it does not spread\n * them onto its own DOM, and what each one is called in a props panel — lives\n * here, with no React and no MUI, so reading it costs a page nothing.\n *\n * The field list is generic on purpose: it says what each setting is and what\n * kind of control edits it, never how a particular editor draws that control.\n * Every element that opens a lightbox maps it onto its own schema, so the\n * Image, the Image List, the Video and the Lightbox container all offer the\n * same settings under the same names, and a reusable component's property can\n * be bound to any of them the same way.\n */\n\n/** What draws the close control. */\nexport const LIGHTBOX_CLOSE_STYLES = ['icon', 'filled', 'text'] as const\nexport type LightboxCloseStyle = (typeof LIGHTBOX_CLOSE_STYLES)[number]\n\n/** Where the close control sits: in the frame's top corner, or the screen's. */\nexport const LIGHTBOX_CLOSE_POSITIONS = [\n 'inside-end',\n 'inside-start',\n 'outside-end',\n] as const\nexport type LightboxClosePosition = (typeof LIGHTBOX_CLOSE_POSITIONS)[number]\n\n/** Where a picture's caption goes: under it, over its foot, or nowhere. */\nexport const LIGHTBOX_CAPTION_PLACEMENTS = ['below', 'overlay', 'hidden'] as const\nexport type LightboxCaptionPlacement =\n (typeof LIGHTBOX_CAPTION_PLACEMENTS)[number]\n\n/** How the lightbox arrives. A visitor who asks for reduced motion gets `none`. */\nexport const LIGHTBOX_TRANSITIONS = ['fade', 'zoom', 'none'] as const\nexport type LightboxTransition = (typeof LIGHTBOX_TRANSITIONS)[number]\n\n/**\n * The resolved settings. Every look field is optional and absent means \"the\n * element's own default\", which is what keeps an element that existed before\n * these settings — the Video — rendering exactly as it did. The two closing\n * rules are always resolved, because a dialog has to decide them.\n */\nexport interface LightboxAppearance {\n backdropColor?: string\n /** 0–100. */\n backdropOpacity?: number\n /** Pixels of blur behind the backdrop. */\n backdropBlur?: number\n /** Any CSS length. */\n maxWidth?: string\n maxHeight?: string\n padding?: string\n /** Corner radius of the frame, in pixels. */\n radius?: number\n closeStyle?: LightboxCloseStyle\n closePosition?: LightboxClosePosition\n captionPlacement?: LightboxCaptionPlacement\n transition?: LightboxTransition\n closeOnBackdrop: boolean\n closeOnEscape: boolean\n}\n\n/**\n * The prop each setting is stored under on an element. Persisted in screen\n * documents; never rename a value.\n */\nexport const LIGHTBOX_APPEARANCE_PROPS = {\n backdropColor: 'lightboxBackdropColor',\n backdropOpacity: 'lightboxBackdropOpacity',\n backdropBlur: 'lightboxBackdropBlur',\n maxWidth: 'lightboxMaxWidth',\n maxHeight: 'lightboxMaxHeight',\n padding: 'lightboxPadding',\n radius: 'lightboxRadius',\n closeStyle: 'lightboxCloseStyle',\n closePosition: 'lightboxClosePosition',\n captionPlacement: 'lightboxCaptionPlacement',\n transition: 'lightboxTransition',\n closeOnBackdrop: 'lightboxCloseOnBackdrop',\n closeOnEscape: 'lightboxCloseOnEscape',\n} as const satisfies Record<keyof LightboxAppearance, string>\n\nexport type LightboxAppearancePropName =\n (typeof LIGHTBOX_APPEARANCE_PROPS)[keyof typeof LIGHTBOX_APPEARANCE_PROPS]\n\n/** Every appearance prop name, for an element to keep off its own DOM. */\nexport const LIGHTBOX_APPEARANCE_PROP_NAMES: readonly LightboxAppearancePropName[] =\n Object.values(LIGHTBOX_APPEARANCE_PROPS)\n\n/** The props an element carries for its lightbox's look, as stored. */\nexport type LightboxAppearanceProps = Partial<\n Record<LightboxAppearancePropName, string | number | boolean | null>\n>\n\n/** The kind of control a setting is edited with. */\nexport type LightboxFieldKind =\n | 'color'\n | 'number'\n | 'dimension'\n | 'select'\n | 'switch'\n\nexport interface LightboxFieldDescriptor {\n name: LightboxAppearancePropName\n label: string\n description: string\n kind: LightboxFieldKind\n options?: ReadonlyArray<{ value: string; label: string }>\n}\n\n/**\n * The settings, in the order a props panel lists them. Each select's first\n * option is the default and a real value, so an author can always move back\n * to it (a `''` option could not persist, AGL-1191).\n */\nexport const LIGHTBOX_APPEARANCE_FIELDS: readonly LightboxFieldDescriptor[] = [\n {\n name: 'lightboxBackdropColor',\n label: 'Lightbox backdrop color',\n description: 'The color behind the lightbox. Leave empty for a dark backdrop.',\n kind: 'color',\n },\n {\n name: 'lightboxBackdropOpacity',\n label: 'Lightbox backdrop opacity',\n description: 'How solid the backdrop is, from 0 (clear) to 100 (solid).',\n kind: 'number',\n },\n {\n name: 'lightboxBackdropBlur',\n label: 'Lightbox backdrop blur',\n description: 'Blurs the page behind the lightbox, in pixels. 0 for none.',\n kind: 'number',\n },\n {\n name: 'lightboxMaxWidth',\n label: 'Lightbox max width',\n description: 'The widest the lightbox frame grows, e.g. 1200px or 90vw.',\n kind: 'dimension',\n },\n {\n name: 'lightboxMaxHeight',\n label: 'Lightbox max height',\n description: 'The tallest the lightbox frame grows, e.g. 90vh.',\n kind: 'dimension',\n },\n {\n name: 'lightboxPadding',\n label: 'Lightbox padding',\n description: 'Space between the frame edge and what it shows.',\n kind: 'dimension',\n },\n {\n name: 'lightboxRadius',\n label: 'Lightbox corner radius',\n description: 'Rounds the frame corners, in pixels.',\n kind: 'number',\n },\n {\n name: 'lightboxCloseStyle',\n label: 'Lightbox close button',\n description: 'How the close button looks.',\n kind: 'select',\n options: [\n { value: 'icon', label: 'Icon (default)' },\n { value: 'filled', label: 'Icon on a circle' },\n { value: 'text', label: 'The word Close' },\n ],\n },\n {\n name: 'lightboxClosePosition',\n label: 'Lightbox close position',\n description: 'Where the close button sits.',\n kind: 'select',\n options: [\n { value: 'inside-end', label: 'Frame, top right (default)' },\n { value: 'inside-start', label: 'Frame, top left' },\n { value: 'outside-end', label: 'Screen, top right' },\n ],\n },\n {\n name: 'lightboxCaptionPlacement',\n label: 'Lightbox caption',\n description: 'Where a picture caption shows in the lightbox.',\n kind: 'select',\n options: [\n { value: 'below', label: 'Below the picture (default)' },\n { value: 'overlay', label: 'Over the picture' },\n { value: 'hidden', label: 'Hidden' },\n ],\n },\n {\n name: 'lightboxTransition',\n label: 'Lightbox transition',\n description:\n 'How the lightbox opens. Visitors who ask for reduced motion always get none.',\n kind: 'select',\n options: [\n { value: 'fade', label: 'Fade (default)' },\n { value: 'zoom', label: 'Zoom' },\n { value: 'none', label: 'None' },\n ],\n },\n {\n name: 'lightboxCloseOnBackdrop',\n label: 'Close on backdrop click',\n description: 'Closes the lightbox when a visitor clicks outside it. On by default.',\n kind: 'switch',\n },\n {\n name: 'lightboxCloseOnEscape',\n label: 'Close on Escape',\n description: 'Closes the lightbox when a visitor presses Escape. On by default.',\n kind: 'switch',\n },\n]\n\nconst text = (value: unknown): string | undefined => {\n if (typeof value === 'number' && Number.isFinite(value)) return `${value}`\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed ? trimmed : undefined\n}\n\nconst number = (\n value: unknown,\n min: number,\n max: number,\n): number | undefined => {\n if (value === null || value === undefined || value === '') return undefined\n const parsed = typeof value === 'number' ? value : Number(String(value).trim())\n if (!Number.isFinite(parsed)) return undefined\n return Math.min(Math.max(parsed, min), max)\n}\n\nconst oneOf = <T extends string>(\n value: unknown,\n allowed: readonly T[],\n): T | undefined =>\n typeof value === 'string' && (allowed as readonly string[]).includes(value)\n ? (value as T)\n : undefined\n\n/**\n * A yes/no that defaults to yes: only an explicit no turns it off. The\n * strings are what a document, a reusable component's text substitution or\n * an older editor may hold.\n */\nconst onUnlessNo = (value: unknown): boolean =>\n !(value === false || value === 'false' || value === 'no' || value === 0)\n\n/** A unitless length is pixels, as everywhere else on an element. */\nconst length = (value: unknown): string | undefined => {\n const raw = text(value)\n if (raw === undefined) return undefined\n return /^\\d+(\\.\\d+)?$/.test(raw) ? `${raw}px` : raw\n}\n\n/**\n * Reads the settings off an element's props. Anything unreadable is treated\n * as unset rather than thrown on, because a stored document can hold any\n * value and a lightbox that refuses to open over a bad radius is worse than\n * one with square corners.\n */\nexport function readLightboxAppearance(\n props: Readonly<Record<string, unknown>> | null | undefined,\n): LightboxAppearance {\n const bag = props ?? {}\n const P = LIGHTBOX_APPEARANCE_PROPS\n const appearance: LightboxAppearance = {\n closeOnBackdrop: onUnlessNo(bag[P.closeOnBackdrop]),\n closeOnEscape: onUnlessNo(bag[P.closeOnEscape]),\n }\n const backdropColor = text(bag[P.backdropColor])\n if (backdropColor) appearance.backdropColor = backdropColor\n const backdropOpacity = number(bag[P.backdropOpacity], 0, 100)\n if (backdropOpacity !== undefined) appearance.backdropOpacity = backdropOpacity\n const backdropBlur = number(bag[P.backdropBlur], 0, 64)\n if (backdropBlur !== undefined) appearance.backdropBlur = backdropBlur\n const maxWidth = length(bag[P.maxWidth])\n if (maxWidth) appearance.maxWidth = maxWidth\n const maxHeight = length(bag[P.maxHeight])\n if (maxHeight) appearance.maxHeight = maxHeight\n const padding = length(bag[P.padding])\n if (padding) appearance.padding = padding\n const radius = number(bag[P.radius], 0, 999)\n if (radius !== undefined) appearance.radius = radius\n const closeStyle = oneOf(bag[P.closeStyle], LIGHTBOX_CLOSE_STYLES)\n if (closeStyle) appearance.closeStyle = closeStyle\n const closePosition = oneOf(bag[P.closePosition], LIGHTBOX_CLOSE_POSITIONS)\n if (closePosition) appearance.closePosition = closePosition\n const captionPlacement = oneOf(\n bag[P.captionPlacement],\n LIGHTBOX_CAPTION_PLACEMENTS,\n )\n if (captionPlacement) appearance.captionPlacement = captionPlacement\n const transition = oneOf(bag[P.transition], LIGHTBOX_TRANSITIONS)\n if (transition) appearance.transition = transition\n return appearance\n}\n\n/**\n * Splits an element's props into its lightbox settings and everything else,\n * so the rest can be spread onto the element's DOM without a dozen unknown\n * attributes riding along.\n */\nexport function splitLightboxAppearanceProps<T extends object>(\n props: T,\n): { appearance: LightboxAppearance; rest: Omit<T, LightboxAppearancePropName> } {\n const rest: Record<string, unknown> = {}\n const names = new Set<string>(LIGHTBOX_APPEARANCE_PROP_NAMES)\n for (const [key, value] of Object.entries(props)) {\n if (!names.has(key)) rest[key] = value\n }\n return {\n appearance: readLightboxAppearance(props as Record<string, unknown>),\n rest: rest as Omit<T, LightboxAppearancePropName>,\n }\n}\n\n/**\n * The backdrop's paint: the color at the opacity asked for. A hex color is\n * written as `rgba()`; any other color — a name, an `rgb()`, a theme variable\n * — is mixed with transparency, which every browser that renders the dialog\n * supports. `undefined` when neither is set, so the theme's backdrop stays.\n */\nexport function lightboxBackdropBackground(\n appearance: Pick<LightboxAppearance, 'backdropColor' | 'backdropOpacity'>,\n): string | undefined {\n const { backdropColor, backdropOpacity } = appearance\n if (!backdropColor && backdropOpacity === undefined) return undefined\n const color = backdropColor ?? '#000000'\n const opacity = backdropOpacity ?? 80\n const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color)\n if (hex) {\n const digits =\n hex[1].length === 3\n ? hex[1]\n .split('')\n .map((digit) => digit + digit)\n .join('')\n : hex[1]\n const red = parseInt(digits.slice(0, 2), 16)\n const green = parseInt(digits.slice(2, 4), 16)\n const blue = parseInt(digits.slice(4, 6), 16)\n return `rgba(${red}, ${green}, ${blue}, ${opacity / 100})`\n }\n return `color-mix(in srgb, ${color} ${opacity}%, transparent)`\n}\n"],"names":["LIGHTBOX_CLOSE_STYLES","LIGHTBOX_CLOSE_POSITIONS","LIGHTBOX_CAPTION_PLACEMENTS","LIGHTBOX_TRANSITIONS","LIGHTBOX_APPEARANCE_PROPS","backdropColor","backdropOpacity","backdropBlur","maxWidth","maxHeight","padding","radius","closeStyle","closePosition","captionPlacement","transition","closeOnBackdrop","closeOnEscape","LIGHTBOX_APPEARANCE_PROP_NAMES","Object","values","LIGHTBOX_APPEARANCE_FIELDS","name","label","description","kind","options","value","text","Number","isFinite","undefined","trimmed","trim","number","min","max","parsed","String","Math","oneOf","allowed","includes","onUnlessNo","length","raw","test","readLightboxAppearance","props","bag","P","appearance","splitLightboxAppearanceProps","rest","names","Set","key","entries","has","lightboxBackdropBackground","color","opacity","hex","exec","digits","split","map","digit","join","red","parseInt","slice","green","blue"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type DialogProps } from '@mui/material/Dialog';
18
+ import type { SxProps, Theme } from '@mui/material/styles';
19
+ import type { KeyboardEvent, ReactNode } from 'react';
20
+ import { type LightboxAppearance } from './lightbox-appearance';
21
+ /** Why the dialog was asked to close. */
22
+ export type LightboxCloseReason = 'escapeKeyDown' | 'backdropClick' | 'closeButton';
23
+ /** The appearance every lightbox takes when an element sets none. */
24
+ export declare const DEFAULT_LIGHTBOX_APPEARANCE: LightboxAppearance;
25
+ export interface LightboxDialogProps {
26
+ open: boolean;
27
+ onClose: () => void;
28
+ /** The dialog's accessible name. Required: an unnamed dialog is a defect. */
29
+ label: string;
30
+ /** The close control's accessible name. */
31
+ closeLabel?: string;
32
+ appearance?: LightboxAppearance;
33
+ /**
34
+ * A chance to refuse one close: return `true` to keep the dialog open. The
35
+ * Video uses it so `Escape` in full screen leaves full screen first.
36
+ */
37
+ interceptClose?: (reason: LightboxCloseReason) => boolean;
38
+ onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;
39
+ /**
40
+ * `false` when the content draws its own {@link LightboxCloseButton} — the
41
+ * Video does, inside the frame that goes full screen.
42
+ */
43
+ closeButton?: boolean;
44
+ /** The frame's width rule when the appearance names no max width. */
45
+ maxWidth?: DialogProps['maxWidth'];
46
+ fullWidth?: boolean;
47
+ /** Extra styles for the paper — the Video's black ground, for one. */
48
+ paperSx?: SxProps<Theme>;
49
+ /** Extra styles for the close control. */
50
+ closeButtonSx?: SxProps<Theme>;
51
+ children?: ReactNode;
52
+ }
53
+ /** `prefers-reduced-motion` — the transitions drop to nothing under it. */
54
+ export declare function useLightboxReducedMotion(): boolean;
55
+ export interface LightboxCloseButtonProps {
56
+ onClick: () => void;
57
+ label: string;
58
+ appearance?: LightboxAppearance;
59
+ sx?: SxProps<Theme>;
60
+ }
61
+ /** The close control: the same one in every lightbox, styled by appearance. */
62
+ export declare function LightboxCloseButton(props: LightboxCloseButtonProps): import("react").JSX.Element;
63
+ export declare namespace LightboxCloseButton {
64
+ var displayName: string;
65
+ }
66
+ /**
67
+ * The dialog. Mounted by a caller once armed; renders nothing while closed.
68
+ */
69
+ export declare function LightboxDialog(props: LightboxDialogProps): import("react").JSX.Element;
70
+ export declare namespace LightboxDialog {
71
+ var displayName: string;
72
+ }
73
+ /** The frame's inner box, for content that wants the paper's full height. */
74
+ export declare function LightboxFrame(props: {
75
+ children?: ReactNode;
76
+ sx?: SxProps<Theme>;
77
+ }): import("react").JSX.Element;
78
+ export default LightboxDialog;