smartsell-ui-kit 0.0.5 → 0.1.0

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/README.md CHANGED
@@ -5,29 +5,33 @@
5
5
  It is designed for tenant developers who should not need to know how the core app is implemented.
6
6
  The package stays on the public contract surface:
7
7
 
8
- - theme-aware components powered by `smartsell-sales-sdk`
8
+ - theme-aware components powered by `smartsell-theme`
9
9
  - inline-style primitives that do not depend on private app CSS files
10
10
  - a composable modal helper with predictable slot ids for `header`, `body`, and `footer`
11
11
 
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- npm install react smartsell-sales-sdk smartsell-ui-kit
15
+ npm install react smartsell-theme smartsell-ui-kit
16
16
  ```
17
17
 
18
+ If you want to connect `ComposableModal` to SmartSell slot overrides, install `smartsell-sales-sdk` as well.
19
+
18
20
  ## Basic button and card
19
21
 
20
22
  ```tsx
23
+ import { ThemeProvider } from 'smartsell-theme';
21
24
  import { Button, Card } from 'smartsell-ui-kit';
22
- import type { ModulePageProps } from 'smartsell-sales-sdk';
23
25
 
24
- export default function ExamplePage({ sdk }: ModulePageProps) {
26
+ export default function ExamplePage() {
25
27
  return (
26
- <Card sdk={sdk} title="Example" eyebrow="UI kit">
27
- <Button sdk={sdk} onClick={() => sdk.navigation.goBack()}>
28
- Go back
29
- </Button>
30
- </Card>
28
+ <ThemeProvider>
29
+ <Card title="Example" eyebrow="UI kit">
30
+ <Button onClick={() => window.history.back()}>
31
+ Go back
32
+ </Button>
33
+ </Card>
34
+ </ThemeProvider>
31
35
  );
32
36
  }
33
37
  ```
@@ -37,22 +41,30 @@ export default function ExamplePage({ sdk }: ModulePageProps) {
37
41
  Use `ComposableModal` when you want the page to keep modal state while tenants customize visual sections independently.
38
42
 
39
43
  ```tsx
44
+ import { SlotRenderer, type ModulePageProps, type SlotComponentProps } from 'smartsell-sales-sdk';
40
45
  import { ComposableModal, createModalSlotIds } from 'smartsell-ui-kit';
41
46
 
42
47
  const slotIds = createModalSlotIds('order-review');
43
48
 
44
- <ComposableModal
45
- sdk={sdk}
46
- isOpen={isOpen}
47
- onClose={() => setIsOpen(false)}
48
- onConfirm={submitOrder}
49
- title="Order review"
50
- description="Validate the order before sending it."
51
- slotBaseId="order-review"
52
- payload={{ orderNumber: 'SO-1001', total: 421.9 }}
53
- >
54
- <div>Default modal body</div>
55
- </ComposableModal>
49
+ export default function ExamplePage({ sdk }: ModulePageProps) {
50
+ return (
51
+ <ComposableModal
52
+ isOpen={isOpen}
53
+ onClose={() => setIsOpen(false)}
54
+ onConfirm={submitOrder}
55
+ title="Order review"
56
+ slotBaseId="order-review"
57
+ renderSlot={({ id, context, children }) => (
58
+ <SlotRenderer id={id} sdk={sdk} context={context}>
59
+ {children}
60
+ </SlotRenderer>
61
+ )}
62
+ payload={{ orderNumber: 'SO-1001', total: 421.9 }}
63
+ >
64
+ <div>Default modal body</div>
65
+ </ComposableModal>
66
+ );
67
+ }
56
68
  ```
57
69
 
58
70
  Slot ids generated by `createModalSlotIds('order-review')`:
@@ -82,7 +94,7 @@ type OrderPayload = {
82
94
 
83
95
  function CustomFooter({ sdk, context }: SlotComponentProps<ModalSlotContext<OrderPayload>>) {
84
96
  return (
85
- <Button sdk={sdk} onClick={() => context?.confirm?.()}>
97
+ <Button onClick={() => context?.confirm?.()}>
86
98
  Send order {context?.payload?.orderNumber}
87
99
  </Button>
88
100
  );
@@ -122,8 +134,8 @@ npm publish
122
134
  The repository now includes a local Storybook workspace so you can inspect and interact with components before publishing the package.
123
135
 
124
136
  - grouped sidebar navigation for buttons, surfaces, forms, and modals
125
- - live Controls for component props such as size, tone, labels, states, and modal behaviors
126
- - a mocked `SmartSellSDK` so theme-aware components render without depending on the main app runtime
137
+ - live Controls for component props such as size, variants, labels, states, and modal behaviors
138
+ - a mocked `ThemeProvider` so components render with the same theme contract used in host apps
127
139
 
128
140
  Run it directly with npm:
129
141
 
@@ -0,0 +1,573 @@
1
+ // src/components/Modal.tsx
2
+ import { useEffect, useMemo, useState } from "react";
3
+
4
+ // src/color.ts
5
+ function withAlpha(hex, alpha) {
6
+ const normalized = hex.replace("#", "").trim();
7
+ const value = normalized.length === 3 ? normalized.split("").map((char) => char + char).join("") : normalized;
8
+ const red = Number.parseInt(value.slice(0, 2), 16);
9
+ const green = Number.parseInt(value.slice(2, 4), 16);
10
+ const blue = Number.parseInt(value.slice(4, 6), 16);
11
+ return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
12
+ }
13
+
14
+ // src/components/Modal.tsx
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ var defaultThemeColors = {
17
+ background: "#f8fafc",
18
+ surface: "#ffffff",
19
+ onSurface: "#0f172a",
20
+ onBackground: "#0f172a",
21
+ primary: "#0f766e",
22
+ onPrimary: "#ffffff",
23
+ secondary: "#1d4ed8",
24
+ onSecondary: "#ffffff",
25
+ negative: "#dc2626",
26
+ warning: "#d97706"
27
+ };
28
+ var srOnlyStyle = {
29
+ position: "absolute",
30
+ width: 1,
31
+ height: 1,
32
+ padding: 0,
33
+ margin: -1,
34
+ overflow: "hidden",
35
+ clip: "rect(0, 0, 0, 0)",
36
+ whiteSpace: "nowrap",
37
+ border: 0
38
+ };
39
+ var isUiKitThemeTokens = (theme) => {
40
+ return Boolean(theme && typeof theme === "object" && "theme" in theme);
41
+ };
42
+ var resolveTheme = (theme) => {
43
+ var _a;
44
+ const colorOverrides = isUiKitThemeTokens(theme) ? theme.theme.colors : (_a = theme == null ? void 0 : theme.colors) != null ? _a : {};
45
+ const colors = {
46
+ ...defaultThemeColors,
47
+ ...colorOverrides
48
+ };
49
+ return {
50
+ colors,
51
+ borderSoft: isUiKitThemeTokens(theme) ? theme.borderSoft : withAlpha(colors.onSurface, 0.1),
52
+ borderStrong: isUiKitThemeTokens(theme) ? theme.borderStrong : withAlpha(colors.onSurface, 0.16),
53
+ surfaceMuted: isUiKitThemeTokens(theme) ? theme.surfaceMuted : withAlpha(colors.onSurface, 0.04),
54
+ surfaceRaised: isUiKitThemeTokens(theme) ? theme.surfaceRaised : withAlpha(colors.surface, 0.94),
55
+ overlay: isUiKitThemeTokens(theme) ? theme.overlay : withAlpha("#030712", 0.62),
56
+ mutedText: isUiKitThemeTokens(theme) ? theme.mutedText : withAlpha(colors.onSurface, 0.72)
57
+ };
58
+ };
59
+ var normalizeWidth = (maxWidth) => {
60
+ if (typeof maxWidth === "number") {
61
+ return `${maxWidth}px`;
62
+ }
63
+ return maxWidth != null ? maxWidth : "720px";
64
+ };
65
+ var isLikelyMobileViewport = () => {
66
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
67
+ return false;
68
+ }
69
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
70
+ navigator.userAgent
71
+ );
72
+ };
73
+ var createTitleId = (title) => {
74
+ if (typeof title === "string" || typeof title === "number") {
75
+ const normalized = String(title).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
76
+ return `smartsell-modal-${normalized || "dialog"}`;
77
+ }
78
+ return "smartsell-modal-dialog";
79
+ };
80
+ var isLegacyModalProps = (props) => {
81
+ return "showModal" in props;
82
+ };
83
+ var normalizeLegacyAction = (action, fallbackDisabled = false) => {
84
+ var _a, _b, _c;
85
+ return {
86
+ label: action.name,
87
+ onClick: action.onClick,
88
+ appearance: (_a = action.type) != null ? _a : "contained",
89
+ style: (_b = action.style) != null ? _b : { maxWidth: "150px" },
90
+ disabled: (_c = action.disable) != null ? _c : fallbackDisabled
91
+ };
92
+ };
93
+ var normalizeModalProps = (props) => {
94
+ var _a, _b;
95
+ if (!isLegacyModalProps(props)) {
96
+ return props;
97
+ }
98
+ const isBusy = Boolean(props.isLoading);
99
+ const disableAction = Boolean(props.disableActionBtn || props.isLoading);
100
+ return {
101
+ isOpen: props.showModal,
102
+ onClose: props.closeModal,
103
+ title: props.title,
104
+ children: props.children,
105
+ theme: props.theme,
106
+ maximized: props.maximized,
107
+ contentStyle: props.style,
108
+ bodyStyle: props.bodyStyle,
109
+ hideCloseButton: props.hideCloseIcon,
110
+ disableDismiss: isBusy,
111
+ closeOnBackdrop: false,
112
+ closeOnEscape: false,
113
+ isBusy,
114
+ closeAction: props.onlyCloseButton ? {
115
+ label: "Close",
116
+ onClick: props.closeModal,
117
+ appearance: "contained",
118
+ disabled: isBusy
119
+ } : void 0,
120
+ cancelAction: props.onlyCloseButton || !props.cancelButton ? void 0 : {
121
+ label: "Cancel",
122
+ onClick: props.cancelButton,
123
+ appearance: "text",
124
+ style: { maxWidth: "100px" }
125
+ },
126
+ confirmAction: props.onlyCloseButton || props.hideActionButton || !props.action ? void 0 : {
127
+ label: (_a = props.actionNameBtn) != null ? _a : "Save",
128
+ onClick: props.action,
129
+ appearance: "contained",
130
+ style: { maxWidth: "100px" },
131
+ disabled: disableAction
132
+ },
133
+ extraActions: props.onlyCloseButton ? void 0 : (_b = props.additionalActions) == null ? void 0 : _b.map((action) => normalizeLegacyAction(action, disableAction))
134
+ };
135
+ };
136
+ function ModalActionButton({
137
+ action,
138
+ theme
139
+ }) {
140
+ var _a, _b;
141
+ const { colors } = theme;
142
+ const {
143
+ label,
144
+ onClick,
145
+ appearance = "contained",
146
+ disabled = false,
147
+ style
148
+ } = action;
149
+ const buttonStyle = {
150
+ width: "100%",
151
+ display: "flex",
152
+ alignItems: "center",
153
+ justifyContent: "center",
154
+ borderRadius: "0.4rem",
155
+ padding: appearance === "link" ? 0 : "0.9rem 1.15rem",
156
+ boxSizing: "border-box",
157
+ cursor: disabled ? "not-allowed" : "pointer",
158
+ fontWeight: 700,
159
+ fontSize: "1.05rem",
160
+ lineHeight: 1.2,
161
+ margin: 0,
162
+ textAlign: "center",
163
+ transition: "0.5s",
164
+ opacity: disabled ? 0.5 : 1,
165
+ filter: disabled ? "saturate(0)" : void 0
166
+ };
167
+ if (appearance === "text") {
168
+ buttonStyle.background = "none";
169
+ buttonStyle.border = "1px solid transparent";
170
+ buttonStyle.color = colors.primary;
171
+ buttonStyle.boxShadow = "none";
172
+ } else if (appearance === "link") {
173
+ buttonStyle.background = "none";
174
+ buttonStyle.border = "none";
175
+ buttonStyle.color = colors.primary;
176
+ buttonStyle.boxShadow = "none";
177
+ } else if (appearance === "outlined") {
178
+ buttonStyle.background = "none";
179
+ buttonStyle.border = `2px solid ${colors.primary}`;
180
+ buttonStyle.color = colors.primary;
181
+ buttonStyle.boxShadow = `0 14px 28px ${withAlpha(colors.onSurface, 0.08)}`;
182
+ } else {
183
+ buttonStyle.background = colors.primary;
184
+ buttonStyle.border = "1px solid transparent";
185
+ buttonStyle.color = colors.onPrimary;
186
+ buttonStyle.boxShadow = `0 14px 28px ${withAlpha(colors.primary, 0.22)}`;
187
+ }
188
+ return /* @__PURE__ */ jsx(
189
+ "div",
190
+ {
191
+ style: {
192
+ width: (_a = style == null ? void 0 : style.width) != null ? _a : (style == null ? void 0 : style.maxWidth) ? "100%" : "max-content",
193
+ display: "flex",
194
+ alignItems: "center",
195
+ justifyContent: "center",
196
+ margin: 0,
197
+ marginBottom: (_b = style == null ? void 0 : style.marginBottom) != null ? _b : 0,
198
+ ...style
199
+ },
200
+ children: /* @__PURE__ */ jsx(
201
+ "button",
202
+ {
203
+ type: "button",
204
+ onClick: disabled ? void 0 : onClick,
205
+ disabled,
206
+ style: buttonStyle,
207
+ children: label
208
+ }
209
+ )
210
+ }
211
+ );
212
+ }
213
+ function ModalBusyIndicator({
214
+ theme
215
+ }) {
216
+ return /* @__PURE__ */ jsxs(
217
+ "div",
218
+ {
219
+ "aria-live": "polite",
220
+ role: "status",
221
+ style: {
222
+ position: "relative",
223
+ display: "inline-flex",
224
+ alignItems: "center",
225
+ justifyContent: "center",
226
+ width: 56,
227
+ height: 56,
228
+ borderRadius: 999,
229
+ backgroundColor: withAlpha(theme.colors.surface, 0.88),
230
+ boxShadow: `0 18px 36px ${withAlpha(theme.colors.onSurface, 0.08)}`
231
+ },
232
+ children: [
233
+ /* @__PURE__ */ jsx("span", { style: srOnlyStyle, children: "Loading" }),
234
+ /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", width: "32", height: "32", viewBox: "0 0 40 40", fill: "none", children: [
235
+ /* @__PURE__ */ jsx(
236
+ "circle",
237
+ {
238
+ cx: "20",
239
+ cy: "20",
240
+ r: "16",
241
+ stroke: withAlpha(theme.colors.onSurface, 0.12),
242
+ strokeWidth: "4"
243
+ }
244
+ ),
245
+ /* @__PURE__ */ jsx(
246
+ "path",
247
+ {
248
+ d: "M20 4C28.837 4 36 11.163 36 20",
249
+ stroke: theme.colors.primary,
250
+ strokeWidth: "4",
251
+ strokeLinecap: "round",
252
+ children: /* @__PURE__ */ jsx(
253
+ "animateTransform",
254
+ {
255
+ attributeName: "transform",
256
+ type: "rotate",
257
+ from: "0 20 20",
258
+ to: "360 20 20",
259
+ dur: "0.75s",
260
+ repeatCount: "indefinite"
261
+ }
262
+ )
263
+ }
264
+ )
265
+ ] })
266
+ ]
267
+ }
268
+ );
269
+ }
270
+ function UnifiedModal({
271
+ isOpen,
272
+ onClose,
273
+ children,
274
+ title,
275
+ headerContent,
276
+ footer,
277
+ confirmAction,
278
+ cancelAction,
279
+ closeAction,
280
+ extraActions,
281
+ maxWidth,
282
+ closeOnBackdrop = true,
283
+ closeOnEscape = true,
284
+ hideCloseButton = false,
285
+ hideExpandButton = false,
286
+ disableDismiss = false,
287
+ maximized,
288
+ contentStyle,
289
+ bodyStyle,
290
+ isBusy = false,
291
+ theme
292
+ }) {
293
+ var _a, _b;
294
+ const modalTheme = useMemo(() => resolveTheme(theme), [theme]);
295
+ const [isMaximized, setIsMaximized] = useState(maximized != null ? maximized : false);
296
+ const titleId = useMemo(() => createTitleId(title), [title]);
297
+ const shouldDisableDismiss = disableDismiss || isBusy;
298
+ useEffect(() => {
299
+ if (typeof maximized === "boolean") {
300
+ setIsMaximized(maximized);
301
+ return;
302
+ }
303
+ if (isLikelyMobileViewport()) {
304
+ setIsMaximized(true);
305
+ }
306
+ }, [maximized]);
307
+ useEffect(() => {
308
+ if (!isOpen || typeof window === "undefined" || typeof document === "undefined") {
309
+ return;
310
+ }
311
+ const previousOverflow = document.body.style.overflow;
312
+ document.body.style.overflow = "hidden";
313
+ const handleKeyDown = (event) => {
314
+ if (!shouldDisableDismiss && closeOnEscape && event.key === "Escape") {
315
+ onClose();
316
+ }
317
+ };
318
+ window.addEventListener("keydown", handleKeyDown);
319
+ return () => {
320
+ document.body.style.overflow = previousOverflow;
321
+ window.removeEventListener("keydown", handleKeyDown);
322
+ };
323
+ }, [closeOnEscape, isOpen, onClose, shouldDisableDismiss]);
324
+ if (!isOpen) {
325
+ return null;
326
+ }
327
+ const resolvedHeader = headerContent != null ? headerContent : title ? /* @__PURE__ */ jsx(
328
+ "h2",
329
+ {
330
+ id: titleId,
331
+ style: {
332
+ margin: 0,
333
+ color: modalTheme.colors.onSurface,
334
+ fontSize: 30,
335
+ fontWeight: 800,
336
+ lineHeight: 1.1
337
+ },
338
+ children: title
339
+ }
340
+ ) : null;
341
+ let footerContent = footer;
342
+ if (footerContent === void 0) {
343
+ if (closeAction && !closeAction.hidden) {
344
+ footerContent = /* @__PURE__ */ jsx(
345
+ ModalActionButton,
346
+ {
347
+ action: { ...closeAction, disabled: closeAction.disabled || isBusy },
348
+ theme: modalTheme
349
+ }
350
+ );
351
+ } else {
352
+ const resolvedActions = [
353
+ cancelAction ? {
354
+ ...cancelAction,
355
+ appearance: (_a = cancelAction.appearance) != null ? _a : "text",
356
+ disabled: cancelAction.disabled || isBusy
357
+ } : void 0,
358
+ ...(extraActions != null ? extraActions : []).map((action) => {
359
+ var _a2;
360
+ return {
361
+ ...action,
362
+ appearance: (_a2 = action.appearance) != null ? _a2 : "contained",
363
+ disabled: action.disabled || isBusy
364
+ };
365
+ }),
366
+ confirmAction ? {
367
+ ...confirmAction,
368
+ appearance: (_b = confirmAction.appearance) != null ? _b : "contained",
369
+ disabled: confirmAction.disabled || isBusy
370
+ } : void 0
371
+ ].reduce((items, action) => {
372
+ if (!action || action.hidden) {
373
+ return items;
374
+ }
375
+ items.push(action);
376
+ return items;
377
+ }, []);
378
+ footerContent = resolvedActions.length > 0 ? resolvedActions.map((action, index) => /* @__PURE__ */ jsx(
379
+ ModalActionButton,
380
+ {
381
+ action,
382
+ theme: modalTheme
383
+ },
384
+ `modal-action-${index}`
385
+ )) : void 0;
386
+ }
387
+ }
388
+ const shouldRenderFooter = footerContent !== void 0 && footerContent !== null && footerContent !== false;
389
+ return /* @__PURE__ */ jsx(
390
+ "div",
391
+ {
392
+ "aria-label": headerContent ? typeof title === "string" ? title : "Dialog" : void 0,
393
+ "aria-labelledby": headerContent ? void 0 : title ? titleId : void 0,
394
+ "aria-busy": isBusy,
395
+ "aria-modal": "true",
396
+ role: "dialog",
397
+ onMouseDown: (event) => {
398
+ if (!shouldDisableDismiss && closeOnBackdrop && event.target === event.currentTarget) {
399
+ onClose();
400
+ }
401
+ },
402
+ style: {
403
+ position: "fixed",
404
+ inset: 0,
405
+ zIndex: 70,
406
+ display: "flex",
407
+ alignItems: "center",
408
+ justifyContent: "center",
409
+ padding: isMaximized ? 0 : 16,
410
+ backgroundColor: modalTheme.overlay,
411
+ backdropFilter: "blur(8px)"
412
+ },
413
+ children: /* @__PURE__ */ jsxs(
414
+ "div",
415
+ {
416
+ style: {
417
+ position: "relative",
418
+ display: "flex",
419
+ flexDirection: "column",
420
+ width: isMaximized ? "100vw" : "min(100%, 100%)",
421
+ maxWidth: isMaximized ? "100vw" : normalizeWidth(maxWidth),
422
+ height: isMaximized ? "100vh" : void 0,
423
+ maxHeight: isMaximized ? "100vh" : "calc(100vh - 32px)",
424
+ minWidth: isMaximized ? "100vw" : 300,
425
+ borderRadius: isMaximized ? 0 : 30,
426
+ border: `1px solid ${modalTheme.borderSoft}`,
427
+ background: `linear-gradient(180deg, ${modalTheme.surfaceRaised}, ${modalTheme.colors.surface})`,
428
+ boxShadow: `0 34px 80px ${withAlpha("#020617", 0.28)}`,
429
+ color: modalTheme.colors.onSurface,
430
+ overflow: "hidden",
431
+ ...contentStyle
432
+ },
433
+ children: [
434
+ /* @__PURE__ */ jsxs(
435
+ "div",
436
+ {
437
+ style: {
438
+ display: "flex",
439
+ alignItems: "flex-start",
440
+ justifyContent: "space-between",
441
+ gap: 12,
442
+ padding: "18px 20px 12px",
443
+ borderBottom: `1px solid ${withAlpha(modalTheme.colors.onSurface, 0.06)}`
444
+ },
445
+ children: [
446
+ /* @__PURE__ */ jsx(
447
+ "div",
448
+ {
449
+ style: {
450
+ flex: 1,
451
+ opacity: isBusy ? 0.38 : 1,
452
+ transition: "opacity 180ms ease"
453
+ },
454
+ children: resolvedHeader
455
+ }
456
+ ),
457
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
458
+ !hideExpandButton ? /* @__PURE__ */ jsx(
459
+ "button",
460
+ {
461
+ "aria-label": isMaximized ? "Minimize modal" : "Maximize modal",
462
+ title: isMaximized ? "Minimize" : "Maximize",
463
+ type: "button",
464
+ onClick: () => setIsMaximized((current) => !current),
465
+ style: {
466
+ height: 40,
467
+ width: 40,
468
+ borderRadius: 999,
469
+ border: `1px solid ${modalTheme.borderSoft}`,
470
+ backgroundColor: "transparent",
471
+ color: modalTheme.colors.onSurface,
472
+ cursor: "pointer",
473
+ fontSize: 18,
474
+ fontWeight: 700
475
+ },
476
+ children: isMaximized ? "\u25A2" : "\u25A1"
477
+ }
478
+ ) : null,
479
+ !hideCloseButton ? /* @__PURE__ */ jsx(
480
+ "button",
481
+ {
482
+ "aria-label": "Close modal",
483
+ type: "button",
484
+ onClick: shouldDisableDismiss ? void 0 : onClose,
485
+ style: {
486
+ height: 40,
487
+ width: 40,
488
+ borderRadius: 999,
489
+ border: `1px solid ${modalTheme.borderSoft}`,
490
+ backgroundColor: "transparent",
491
+ color: modalTheme.colors.onSurface,
492
+ cursor: shouldDisableDismiss ? "not-allowed" : "pointer",
493
+ fontSize: 20,
494
+ fontWeight: 700,
495
+ opacity: shouldDisableDismiss ? 0.5 : 1
496
+ },
497
+ children: "\xD7"
498
+ }
499
+ ) : null
500
+ ] })
501
+ ]
502
+ }
503
+ ),
504
+ /* @__PURE__ */ jsxs(
505
+ "div",
506
+ {
507
+ style: {
508
+ flex: 1,
509
+ minHeight: 0,
510
+ position: "relative",
511
+ overflow: "auto",
512
+ padding: 24,
513
+ ...bodyStyle
514
+ },
515
+ children: [
516
+ /* @__PURE__ */ jsx(
517
+ "div",
518
+ {
519
+ style: {
520
+ opacity: isBusy ? 0.38 : 1,
521
+ filter: isBusy ? "saturate(0.8)" : "none",
522
+ pointerEvents: isBusy ? "none" : "auto",
523
+ transition: "opacity 180ms ease, filter 180ms ease"
524
+ },
525
+ children
526
+ }
527
+ ),
528
+ isBusy ? /* @__PURE__ */ jsx(
529
+ "div",
530
+ {
531
+ style: {
532
+ position: "absolute",
533
+ inset: 0,
534
+ display: "flex",
535
+ alignItems: "center",
536
+ justifyContent: "center",
537
+ backgroundColor: withAlpha(modalTheme.colors.surface, 0.58),
538
+ backdropFilter: "blur(2px)"
539
+ },
540
+ children: /* @__PURE__ */ jsx(ModalBusyIndicator, { theme: modalTheme })
541
+ }
542
+ ) : null
543
+ ]
544
+ }
545
+ ),
546
+ shouldRenderFooter ? /* @__PURE__ */ jsx(
547
+ "div",
548
+ {
549
+ style: {
550
+ display: "flex",
551
+ justifyContent: "flex-end",
552
+ gap: 10,
553
+ padding: "12px 20px 16px",
554
+ borderTop: `1px solid ${withAlpha(modalTheme.colors.onSurface, 0.06)}`
555
+ },
556
+ children: footerContent
557
+ }
558
+ ) : null
559
+ ]
560
+ }
561
+ )
562
+ }
563
+ );
564
+ }
565
+ function Modal(props) {
566
+ return /* @__PURE__ */ jsx(UnifiedModal, { ...normalizeModalProps(props) });
567
+ }
568
+
569
+ export {
570
+ withAlpha,
571
+ Modal
572
+ };
573
+ //# sourceMappingURL=chunk-YWH2VIAD.js.map