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/dist/index.js CHANGED
@@ -1,20 +1,124 @@
1
1
  import {
2
- Modal
3
- } from "./chunk-BI3QBKFG.js";
2
+ Modal,
3
+ withAlpha
4
+ } from "./chunk-YWH2VIAD.js";
4
5
 
5
6
  // src/components/Button.tsx
6
7
  import { useState } from "react";
7
- import { withAlpha as withAlpha2 } from "smartsell-sales-sdk";
8
+
9
+ // src/components/controlSize.ts
10
+ var CONTROL_METRICS_BY_SIZE = {
11
+ sm: {
12
+ height: 30,
13
+ fontSize: 13,
14
+ lineHeight: "16px",
15
+ paddingX: 12,
16
+ paddingY: 6,
17
+ buttonPaddingX: 14,
18
+ selectPaddingRight: 40,
19
+ selectIndicatorOffset: 12
20
+ },
21
+ md: {
22
+ height: 38,
23
+ fontSize: 14,
24
+ lineHeight: "18px",
25
+ paddingX: 16,
26
+ paddingY: 9,
27
+ buttonPaddingX: 18,
28
+ selectPaddingRight: 44,
29
+ selectIndicatorOffset: 16
30
+ },
31
+ lg: {
32
+ height: 46,
33
+ fontSize: 15,
34
+ lineHeight: "18px",
35
+ paddingX: 18,
36
+ paddingY: 13,
37
+ buttonPaddingX: 20,
38
+ selectPaddingRight: 48,
39
+ selectIndicatorOffset: 18
40
+ }
41
+ };
42
+ var getControlMetrics = (size) => CONTROL_METRICS_BY_SIZE[size];
43
+ var getTextInputPadding = (size, options = {}) => {
44
+ var _a, _b;
45
+ const metrics = getControlMetrics(size);
46
+ const leadingInset = (_a = options.leadingInset) != null ? _a : 0;
47
+ const trailingInset = (_b = options.trailingInset) != null ? _b : 0;
48
+ return `${metrics.paddingY}px ${metrics.paddingX + trailingInset}px ${metrics.paddingY}px ${metrics.paddingX + leadingInset}px`;
49
+ };
50
+ var getSelectControlPadding = (size) => {
51
+ const metrics = getControlMetrics(size);
52
+ return `${metrics.paddingY}px ${metrics.selectPaddingRight}px ${metrics.paddingY}px ${metrics.paddingX}px`;
53
+ };
54
+ var getSelectIndicatorOffset = (size) => getControlMetrics(size).selectIndicatorOffset;
55
+ var getButtonSizeStyles = (size) => {
56
+ const metrics = getControlMetrics(size);
57
+ return {
58
+ boxSizing: "border-box",
59
+ height: metrics.height,
60
+ padding: `0 ${metrics.buttonPaddingX}px`,
61
+ fontSize: metrics.fontSize,
62
+ lineHeight: metrics.lineHeight
63
+ };
64
+ };
8
65
 
9
66
  // src/theme.ts
10
67
  import { useMemo } from "react";
11
- import {
12
- useSalesTheme,
13
- withAlpha
14
- } from "smartsell-sales-sdk";
15
- function useUiKitTheme(sdk) {
16
- const { theme } = useSalesTheme(sdk);
68
+ import { useThemeValue } from "smartsell-theme";
69
+ var DEFAULT_THEME_COLORS = {
70
+ primary: "#111827",
71
+ secondary: "#6b7280",
72
+ background: "#f8fafc",
73
+ surface: "#ffffff",
74
+ text: "#111827",
75
+ textLight: "#6b7280",
76
+ positive: "#16a34a",
77
+ negative: "#dc2626",
78
+ warning: "#d97706",
79
+ info: "#2563eb",
80
+ onPrimary: "#ffffff"
81
+ };
82
+ function normalizeUiKitTheme(theme) {
83
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
84
+ const colors = theme.colors;
85
+ const primary = (_a = colors.primary) != null ? _a : DEFAULT_THEME_COLORS.primary;
86
+ const background = (_b = colors.background) != null ? _b : DEFAULT_THEME_COLORS.background;
87
+ const surface = (_c = colors.surface) != null ? _c : background;
88
+ const text = (_f = (_e = (_d = colors.text) != null ? _d : colors.onSurface) != null ? _e : colors.onBackground) != null ? _f : DEFAULT_THEME_COLORS.text;
89
+ const textLight = (_h = (_g = colors.textLight) != null ? _g : colors.onSurfaceLight) != null ? _h : text;
90
+ const normalizedColors = {
91
+ ...theme.colors,
92
+ primary,
93
+ primaryLight: (_i = colors.primaryLight) != null ? _i : primary,
94
+ primaryDark: (_j = colors.primaryDark) != null ? _j : primary,
95
+ secondary: (_k = colors.secondary) != null ? _k : DEFAULT_THEME_COLORS.secondary,
96
+ background,
97
+ backgroundLight: (_l = colors.backgroundLight) != null ? _l : background,
98
+ surface,
99
+ text,
100
+ textLight,
101
+ positive: (_m = colors.positive) != null ? _m : DEFAULT_THEME_COLORS.positive,
102
+ negative: (_n = colors.negative) != null ? _n : DEFAULT_THEME_COLORS.negative,
103
+ warning: (_o = colors.warning) != null ? _o : DEFAULT_THEME_COLORS.warning,
104
+ info: (_p = colors.info) != null ? _p : DEFAULT_THEME_COLORS.info,
105
+ onPrimary: (_q = colors.onPrimary) != null ? _q : DEFAULT_THEME_COLORS.onPrimary,
106
+ onSecondary: (_r = colors.onSecondary) != null ? _r : text,
107
+ onBackground: (_s = colors.onBackground) != null ? _s : text,
108
+ onSurface: (_t = colors.onSurface) != null ? _t : text,
109
+ onSurfaceLight: (_u = colors.onSurfaceLight) != null ? _u : textLight,
110
+ onWarning: (_v = colors.onWarning) != null ? _v : text
111
+ };
112
+ return {
113
+ ...theme,
114
+ colors: normalizedColors,
115
+ color: normalizedColors
116
+ };
117
+ }
118
+ function useUiKitTheme() {
119
+ const activeTheme = useThemeValue();
17
120
  return useMemo(() => {
121
+ const theme = normalizeUiKitTheme(activeTheme);
18
122
  return {
19
123
  theme,
20
124
  borderSoft: withAlpha(theme.colors.onSurface, 0.1),
@@ -25,24 +129,13 @@ function useUiKitTheme(sdk) {
25
129
  focusRing: withAlpha(theme.colors.primary, 0.22),
26
130
  mutedText: withAlpha(theme.colors.onSurface, 0.72)
27
131
  };
28
- }, [theme]);
132
+ }, [activeTheme]);
29
133
  }
30
134
 
31
135
  // src/components/Button.tsx
32
136
  import { jsxs } from "react/jsx-runtime";
33
- var getSizeStyles = (size) => {
34
- if (size === "sm") {
35
- return { minHeight: 38, padding: "0 14px", fontSize: 13 };
36
- }
37
- if (size === "lg") {
38
- return { minHeight: 50, padding: "0 20px", fontSize: 15 };
39
- }
40
- return { minHeight: 44, padding: "0 18px", fontSize: 14 };
41
- };
42
137
  function Button({
43
- sdk,
44
138
  variant = "solid",
45
- tone = "primary",
46
139
  size = "md",
47
140
  fullWidth = false,
48
141
  leadingIcon,
@@ -63,64 +156,42 @@ function Button({
63
156
  onTouchCancel,
64
157
  ...rest
65
158
  }) {
66
- const { theme } = useUiKitTheme(sdk);
159
+ const { theme } = useUiKitTheme();
67
160
  const [isHovered, setIsHovered] = useState(false);
68
161
  const [isPressed, setIsPressed] = useState(false);
69
- const palette = {
70
- primary: {
71
- background: theme.colors.primary,
72
- foreground: theme.colors.onPrimary,
73
- border: withAlpha2(theme.colors.primary, 0.3)
74
- },
75
- secondary: {
76
- background: theme.colors.secondary,
77
- foreground: theme.colors.onSecondary,
78
- border: withAlpha2(theme.colors.secondary, 0.3)
79
- },
80
- neutral: {
81
- background: theme.colors.surface,
82
- foreground: theme.colors.onSurface,
83
- border: withAlpha2(theme.colors.onSurface, 0.16)
84
- },
85
- danger: {
86
- background: theme.colors.negative,
87
- foreground: "#ffffff",
88
- border: withAlpha2(theme.colors.negative, 0.32)
89
- }
90
- }[tone];
91
162
  const variantStyles = {
92
163
  solid: {
93
- backgroundColor: palette.background,
94
- borderColor: palette.border,
95
- color: palette.foreground
164
+ backgroundColor: theme.colors.primary,
165
+ borderColor: withAlpha(theme.colors.primary, 0.5),
166
+ color: theme.colors.onPrimary
96
167
  },
97
168
  outline: {
98
169
  backgroundColor: "transparent",
99
- borderColor: palette.border,
100
- color: tone === "neutral" ? palette.foreground : palette.background
170
+ borderColor: withAlpha(theme.colors.text, 0.5),
171
+ color: theme.colors.onSurface
101
172
  },
102
173
  ghost: {
103
174
  backgroundColor: "transparent",
104
175
  borderColor: "transparent",
105
- color: tone === "neutral" ? palette.foreground : palette.background
176
+ color: theme.colors.onSurface
106
177
  }
107
178
  };
108
- const interactiveColor = tone === "neutral" ? palette.foreground : palette.background;
179
+ const interactiveColor = variant === "solid" ? theme.colors.primary : theme.colors.onSurface;
109
180
  const isInteractive = !disabled;
110
181
  const interactionStyles = {
111
182
  solid: {
112
183
  transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
113
- boxShadow: isPressed ? `0 8px 16px ${withAlpha2(palette.background, 0.2)}` : isHovered ? `0 18px 36px ${withAlpha2(palette.background, 0.26)}` : `0 14px 28px ${withAlpha2(palette.background, 0.18)}`,
114
- filter: isPressed ? "brightness(0.95)" : isHovered ? "brightness(1.03)" : void 0
184
+ boxShadow: "none",
185
+ filter: void 0
115
186
  },
116
187
  outline: {
117
188
  transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
118
- backgroundColor: isPressed ? withAlpha2(interactiveColor, 0.14) : isHovered ? withAlpha2(interactiveColor, 0.08) : "transparent",
119
- boxShadow: isPressed ? `0 6px 12px ${withAlpha2(theme.colors.onSurface, 0.08)}` : isHovered ? `0 12px 24px ${withAlpha2(theme.colors.onSurface, 0.08)}` : "none"
189
+ backgroundColor: isPressed ? withAlpha(interactiveColor, 0.14) : isHovered ? withAlpha(interactiveColor, 0.08) : "transparent",
190
+ boxShadow: "none"
120
191
  },
121
192
  ghost: {
122
193
  transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
123
- backgroundColor: isPressed ? withAlpha2(interactiveColor, 0.12) : isHovered ? withAlpha2(interactiveColor, 0.06) : "transparent",
194
+ backgroundColor: isPressed ? withAlpha(interactiveColor, 0.12) : isHovered ? withAlpha(interactiveColor, 0.06) : "transparent",
124
195
  boxShadow: "none"
125
196
  }
126
197
  };
@@ -192,10 +263,9 @@ function Button({
192
263
  cursor: disabled ? "not-allowed" : "pointer",
193
264
  fontWeight: 700,
194
265
  letterSpacing: "0.01em",
195
- lineHeight: 1,
196
266
  opacity: disabled ? 0.58 : 1,
197
- transition: "transform 140ms ease, opacity 160ms ease, background-color 160ms ease, box-shadow 160ms ease, filter 160ms ease",
198
- ...getSizeStyles(size),
267
+ transition: "transform 140ms ease, opacity 160ms ease, background-color 160ms ease",
268
+ ...getButtonSizeStyles(size),
199
269
  ...variantStyles[variant],
200
270
  ...isInteractive ? interactionStyles[variant] : void 0,
201
271
  ...style
@@ -210,45 +280,26 @@ function Button({
210
280
  }
211
281
 
212
282
  // src/components/Card.tsx
213
- import { withAlpha as withAlpha3 } from "smartsell-sales-sdk";
214
283
  import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
215
- var getToneStyles = (tone, sdk) => {
216
- const { theme } = useUiKitTheme(sdk);
217
- const accent = {
218
- default: theme.colors.primary,
219
- primary: theme.colors.primary,
220
- secondary: theme.colors.secondary,
221
- warning: theme.colors.warning
222
- }[tone];
223
- return {
224
- accent,
225
- background: `linear-gradient(160deg, ${withAlpha3(theme.colors.surface, 0.98)}, ${withAlpha3(accent, 0.08)})`,
226
- border: withAlpha3(accent, 0.22)
227
- };
228
- };
229
284
  function Card({
230
- sdk,
231
285
  children,
232
286
  eyebrow,
233
287
  title,
234
288
  action,
235
- tone = "default",
236
289
  padded = true,
237
290
  style,
238
291
  ...rest
239
292
  }) {
240
- const { theme, mutedText } = useUiKitTheme(sdk);
241
- const toneStyles = getToneStyles(tone, sdk);
293
+ const { theme } = useUiKitTheme();
242
294
  return /* @__PURE__ */ jsxs2(
243
295
  "div",
244
296
  {
245
297
  ...rest,
246
298
  style: {
247
299
  borderRadius: 28,
248
- border: `1px solid ${toneStyles.border}`,
249
- background: toneStyles.background,
250
- color: theme.colors.onSurface,
251
- boxShadow: `0 24px 48px ${withAlpha3(theme.colors.onSurface, 0.06)}`,
300
+ border: `1px solid ${withAlpha(theme.colors.text, 0.5)}`,
301
+ background: `${withAlpha(theme.colors.background, 0.7)}`,
302
+ color: theme.colors.text,
252
303
  padding: padded ? 24 : void 0,
253
304
  ...style
254
305
  },
@@ -269,7 +320,7 @@ function Card({
269
320
  "span",
270
321
  {
271
322
  style: {
272
- color: toneStyles.accent,
323
+ color: theme.colors.textLight,
273
324
  fontSize: 12,
274
325
  fontWeight: 800,
275
326
  letterSpacing: "0.16em",
@@ -282,7 +333,7 @@ function Card({
282
333
  "div",
283
334
  {
284
335
  style: {
285
- color: theme.colors.onSurface,
336
+ color: theme.colors.text,
286
337
  fontSize: 24,
287
338
  fontWeight: 700,
288
339
  lineHeight: 1.2
@@ -291,7 +342,7 @@ function Card({
291
342
  }
292
343
  ) : null
293
344
  ] }),
294
- action ? /* @__PURE__ */ jsx("div", { style: { color: mutedText }, children: action }) : null
345
+ action ? /* @__PURE__ */ jsx("div", { style: { color: theme.colors.text }, children: action }) : null
295
346
  ]
296
347
  }
297
348
  ),
@@ -301,22 +352,265 @@ function Card({
301
352
  );
302
353
  }
303
354
 
355
+ // src/components/Checkbox.tsx
356
+ import { useEffect, useId, useRef, useState as useState2 } from "react";
357
+ import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
358
+ var CHECKBOX_SIZE_BY_CONTROL_SIZE = {
359
+ sm: 18,
360
+ md: 20,
361
+ lg: 22
362
+ };
363
+ var getCheckboxSize = (controlSize) => CHECKBOX_SIZE_BY_CONTROL_SIZE[controlSize];
364
+ function CheckIcon({ size, color }) {
365
+ return /* @__PURE__ */ jsx2("svg", { width: size, height: size, viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(
366
+ "path",
367
+ {
368
+ d: "M5.75 10.25L8.5 13l5.75-6",
369
+ stroke: color,
370
+ strokeWidth: "1.8",
371
+ strokeLinecap: "round",
372
+ strokeLinejoin: "round"
373
+ }
374
+ ) });
375
+ }
376
+ function MixedIcon({ size, color }) {
377
+ return /* @__PURE__ */ jsx2("svg", { width: size, height: size, viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M5.5 10H14.5", stroke: color, strokeWidth: "1.8", strokeLinecap: "round" }) });
378
+ }
379
+ function CheckboxIndicator({
380
+ checked,
381
+ indeterminate = false,
382
+ disabled = false,
383
+ size = 20,
384
+ checkedColor,
385
+ iconColor,
386
+ uncheckedBorderColor,
387
+ uncheckedBackgroundColor = "transparent",
388
+ style
389
+ }) {
390
+ const isActive = checked || indeterminate;
391
+ return /* @__PURE__ */ jsxs3(
392
+ "span",
393
+ {
394
+ "aria-hidden": "true",
395
+ style: {
396
+ width: size,
397
+ height: size,
398
+ borderRadius: 999,
399
+ boxSizing: "border-box",
400
+ flexShrink: 0,
401
+ display: "inline-flex",
402
+ alignItems: "center",
403
+ justifyContent: "center",
404
+ border: `1.5px solid ${isActive ? checkedColor : uncheckedBorderColor}`,
405
+ backgroundColor: isActive ? checkedColor : uncheckedBackgroundColor,
406
+ opacity: disabled ? 0.45 : 1,
407
+ transition: "background-color 140ms ease, border-color 140ms ease, opacity 140ms ease",
408
+ ...style
409
+ },
410
+ children: [
411
+ indeterminate ? /* @__PURE__ */ jsx2(MixedIcon, { size: Math.max(10, size - 6), color: iconColor }) : null,
412
+ !indeterminate && checked ? /* @__PURE__ */ jsx2(CheckIcon, { size: Math.max(10, size - 6), color: iconColor }) : null
413
+ ]
414
+ }
415
+ );
416
+ }
417
+ function Checkbox({
418
+ label,
419
+ hint,
420
+ error,
421
+ controlSize = "md",
422
+ id,
423
+ checked,
424
+ defaultChecked,
425
+ disabled,
426
+ indeterminate = false,
427
+ required,
428
+ className,
429
+ style,
430
+ onChange,
431
+ onFocus,
432
+ onBlur,
433
+ ...rest
434
+ }) {
435
+ const { theme, mutedText } = useUiKitTheme();
436
+ const controlMetrics = getControlMetrics(controlSize);
437
+ const indicatorSize = getCheckboxSize(controlSize);
438
+ const resolvedId = id != null ? id : `ss-checkbox-${useId().replace(/:/g, "")}`;
439
+ const inputRef = useRef(null);
440
+ const [isFocused, setIsFocused] = useState2(false);
441
+ const [internalChecked, setInternalChecked] = useState2(Boolean(defaultChecked));
442
+ const isControlled = checked !== void 0;
443
+ const resolvedChecked = isControlled ? Boolean(checked) : internalChecked;
444
+ useEffect(() => {
445
+ if (!inputRef.current) {
446
+ return;
447
+ }
448
+ inputRef.current.indeterminate = indeterminate;
449
+ }, [indeterminate, resolvedChecked]);
450
+ return /* @__PURE__ */ jsx2(
451
+ "div",
452
+ {
453
+ className,
454
+ style: {
455
+ width: "100%",
456
+ display: "flex",
457
+ flexDirection: "column",
458
+ gap: 6,
459
+ ...style
460
+ },
461
+ children: /* @__PURE__ */ jsxs3(
462
+ "label",
463
+ {
464
+ htmlFor: resolvedId,
465
+ style: {
466
+ display: "inline-flex",
467
+ alignItems: label || hint ? "flex-start" : "center",
468
+ gap: 10,
469
+ width: label || hint || error ? "100%" : "auto",
470
+ cursor: disabled ? "not-allowed" : "pointer",
471
+ opacity: disabled ? 0.7 : 1,
472
+ userSelect: "none"
473
+ },
474
+ children: [
475
+ /* @__PURE__ */ jsxs3(
476
+ "span",
477
+ {
478
+ style: {
479
+ position: "relative",
480
+ display: "inline-flex",
481
+ alignItems: "center",
482
+ justifyContent: "center",
483
+ flexShrink: 0,
484
+ padding: 2,
485
+ marginTop: label || hint ? 1 : 0,
486
+ borderRadius: 999,
487
+ boxShadow: isFocused ? `0 0 0 3px ${withAlpha(theme.colors.primary, 0.16)}` : "none",
488
+ transition: "box-shadow 140ms ease"
489
+ },
490
+ children: [
491
+ /* @__PURE__ */ jsx2(
492
+ "input",
493
+ {
494
+ ...rest,
495
+ ref: inputRef,
496
+ id: resolvedId,
497
+ type: "checkbox",
498
+ checked: resolvedChecked,
499
+ disabled,
500
+ required,
501
+ onChange: (event) => {
502
+ if (!isControlled) {
503
+ setInternalChecked(event.currentTarget.checked);
504
+ }
505
+ onChange == null ? void 0 : onChange(event);
506
+ },
507
+ onFocus: (event) => {
508
+ setIsFocused(true);
509
+ onFocus == null ? void 0 : onFocus(event);
510
+ },
511
+ onBlur: (event) => {
512
+ setIsFocused(false);
513
+ onBlur == null ? void 0 : onBlur(event);
514
+ },
515
+ style: {
516
+ position: "absolute",
517
+ inset: 0,
518
+ width: "100%",
519
+ height: "100%",
520
+ margin: 0,
521
+ opacity: 0,
522
+ cursor: disabled ? "not-allowed" : "pointer"
523
+ }
524
+ }
525
+ ),
526
+ /* @__PURE__ */ jsx2(
527
+ CheckboxIndicator,
528
+ {
529
+ checked: resolvedChecked,
530
+ indeterminate,
531
+ disabled,
532
+ size: indicatorSize,
533
+ checkedColor: theme.colors.primary,
534
+ iconColor: theme.colors.onPrimary,
535
+ uncheckedBorderColor: withAlpha(theme.colors.onSurface, 0.24),
536
+ uncheckedBackgroundColor: theme.colors.surface
537
+ }
538
+ )
539
+ ]
540
+ }
541
+ ),
542
+ label || hint || error ? /* @__PURE__ */ jsxs3(
543
+ "span",
544
+ {
545
+ style: {
546
+ display: "flex",
547
+ flexDirection: "column",
548
+ gap: 4,
549
+ minWidth: 0
550
+ },
551
+ children: [
552
+ label ? /* @__PURE__ */ jsxs3(
553
+ "span",
554
+ {
555
+ style: {
556
+ color: theme.colors.onSurface,
557
+ fontSize: controlMetrics.fontSize,
558
+ lineHeight: controlMetrics.lineHeight,
559
+ fontWeight: 600
560
+ },
561
+ children: [
562
+ label,
563
+ required ? /* @__PURE__ */ jsx2("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
564
+ ]
565
+ }
566
+ ) : null,
567
+ hint ? /* @__PURE__ */ jsx2(
568
+ "span",
569
+ {
570
+ style: {
571
+ color: mutedText,
572
+ fontSize: 13,
573
+ lineHeight: 1.45
574
+ },
575
+ children: hint
576
+ }
577
+ ) : null,
578
+ error ? /* @__PURE__ */ jsx2(
579
+ "span",
580
+ {
581
+ style: {
582
+ color: theme.colors.negative,
583
+ fontSize: 13,
584
+ lineHeight: 1.45,
585
+ fontWeight: 600
586
+ },
587
+ children: error
588
+ }
589
+ ) : null
590
+ ]
591
+ }
592
+ ) : null
593
+ ]
594
+ }
595
+ )
596
+ }
597
+ );
598
+ }
599
+
304
600
  // src/components/ComposableModal.tsx
305
601
  import { useMemo as useMemo2 } from "react";
306
- import { SlotRenderer } from "smartsell-sales-sdk";
307
- import { Fragment, jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
602
+ import { Fragment, jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
308
603
  var createModalSlotIds = (slotBaseId) => ({
309
604
  header: `${slotBaseId}:modal-header`,
310
605
  body: `${slotBaseId}:modal-body`,
311
606
  footer: `${slotBaseId}:modal-footer`
312
607
  });
313
608
  function ComposableModal({
314
- sdk,
315
609
  isOpen,
316
610
  onClose,
317
611
  title,
318
- description,
319
612
  slotBaseId,
613
+ renderSlot,
320
614
  payload,
321
615
  onConfirm,
322
616
  confirmLabel = "Confirm",
@@ -332,12 +626,11 @@ function ComposableModal({
332
626
  footerFallback,
333
627
  children
334
628
  }) {
335
- const theme = useUiKitTheme(sdk);
629
+ const theme = useUiKitTheme();
336
630
  const slotIds = createModalSlotIds(slotBaseId);
337
631
  const context = useMemo2(
338
632
  () => ({
339
633
  title,
340
- description,
341
634
  payload,
342
635
  close: onClose,
343
636
  confirm: onConfirm,
@@ -346,48 +639,44 @@ function ComposableModal({
346
639
  isOpen,
347
640
  isBusy
348
641
  }),
349
- [cancelLabel, confirmLabel, description, isBusy, isOpen, onClose, onConfirm, payload, title]
642
+ [cancelLabel, confirmLabel, isBusy, isOpen, onClose, onConfirm, payload, title]
350
643
  );
351
- const defaultFooter = hideDefaultFooter ? void 0 : footerFallback != null ? footerFallback : /* @__PURE__ */ jsxs3(Fragment, { children: [
352
- /* @__PURE__ */ jsx2(Button, { sdk, variant: "outline", tone: "neutral", onClick: onClose, disabled: isBusy, children: cancelLabel }),
353
- onConfirm ? /* @__PURE__ */ jsx2(Button, { sdk, onClick: onConfirm, disabled: isBusy, children: confirmLabel }) : null
354
- ] });
355
- const defaultHeader = headerFallback != null ? headerFallback : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
356
- /* @__PURE__ */ jsx2(
357
- "h2",
358
- {
359
- style: {
360
- margin: 0,
361
- fontSize: 30,
362
- fontWeight: 800,
363
- lineHeight: 1.1
364
- },
365
- children: title
366
- }
367
- ),
368
- description ? /* @__PURE__ */ jsx2(
369
- "p",
370
- {
371
- style: {
372
- margin: 0,
373
- fontSize: 15,
374
- lineHeight: 1.6,
375
- opacity: 0.78
376
- },
377
- children: description
378
- }
379
- ) : null
644
+ const defaultFooter = hideDefaultFooter ? void 0 : footerFallback != null ? footerFallback : /* @__PURE__ */ jsxs4(Fragment, { children: [
645
+ /* @__PURE__ */ jsx3(Button, { variant: "outline", onClick: onClose, disabled: isBusy, children: cancelLabel }),
646
+ onConfirm ? /* @__PURE__ */ jsx3(Button, { onClick: onConfirm, disabled: isBusy, children: confirmLabel }) : null
380
647
  ] });
381
- const headerContent = /* @__PURE__ */ jsx2(SlotRenderer, { id: slotIds.header, sdk, context, children: defaultHeader });
382
- const bodyContent = /* @__PURE__ */ jsx2(SlotRenderer, { id: slotIds.body, sdk, context, children });
383
- const footerContent = /* @__PURE__ */ jsx2(SlotRenderer, { id: slotIds.footer, sdk, context, children: defaultFooter });
384
- return /* @__PURE__ */ jsx2(
648
+ const defaultHeader = headerFallback != null ? headerFallback : /* @__PURE__ */ jsx3(
649
+ "h2",
650
+ {
651
+ style: {
652
+ margin: 0,
653
+ fontSize: 30,
654
+ fontWeight: 800,
655
+ lineHeight: 1.1
656
+ },
657
+ children: title
658
+ }
659
+ );
660
+ const renderSlotContent = (id, slotChildren) => {
661
+ if (!renderSlot) {
662
+ return slotChildren;
663
+ }
664
+ return renderSlot({
665
+ id,
666
+ context,
667
+ children: slotChildren
668
+ });
669
+ };
670
+ const headerContent = renderSlotContent(slotIds.header, defaultHeader);
671
+ const bodyContent = renderSlotContent(slotIds.body, children);
672
+ const footerContent = renderSlotContent(slotIds.footer, defaultFooter);
673
+ return /* @__PURE__ */ jsx3(
385
674
  Modal,
386
675
  {
387
676
  isOpen,
388
677
  onClose,
389
678
  title,
390
- description,
679
+ isBusy,
391
680
  headerContent,
392
681
  footer: footerContent,
393
682
  theme,
@@ -401,166 +690,1829 @@ function ComposableModal({
401
690
  );
402
691
  }
403
692
 
404
- // src/components/Field.tsx
405
- import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
406
- function Field({
407
- sdk,
693
+ // src/components/DatePicker.tsx
694
+ import {
695
+ useEffect as useEffect2,
696
+ useMemo as useMemo3,
697
+ useRef as useRef2,
698
+ useState as useState4
699
+ } from "react";
700
+
701
+ // src/components/Input.tsx
702
+ import { useState as useState3 } from "react";
703
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
704
+ var PASSWORD_TOGGLE_METRICS_BY_SIZE = {
705
+ sm: {
706
+ buttonSize: 18,
707
+ leftOffset: 8,
708
+ contentGap: 8,
709
+ iconSize: 14
710
+ },
711
+ md: {
712
+ buttonSize: 20,
713
+ leftOffset: 10,
714
+ contentGap: 10,
715
+ iconSize: 16
716
+ },
717
+ lg: {
718
+ buttonSize: 22,
719
+ leftOffset: 12,
720
+ contentGap: 12,
721
+ iconSize: 18
722
+ }
723
+ };
724
+ var getPasswordToggleMetrics = (size) => PASSWORD_TOGGLE_METRICS_BY_SIZE[size];
725
+ function ControlFrame({
408
726
  label,
409
727
  hint,
410
728
  error,
411
729
  htmlFor,
412
730
  required = false,
731
+ useLabelWrapper = true,
413
732
  children,
414
733
  style
415
734
  }) {
416
- const { theme, mutedText } = useUiKitTheme(sdk);
417
- return /* @__PURE__ */ jsxs4(
418
- "label",
419
- {
420
- htmlFor,
421
- style: {
422
- display: "flex",
423
- flexDirection: "column",
424
- gap: 8,
425
- width: "100%",
426
- ...style
427
- },
428
- children: [
429
- (label || hint) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
430
- label ? /* @__PURE__ */ jsxs4(
431
- "span",
432
- {
433
- style: {
434
- color: theme.colors.onSurface,
435
- fontSize: 14,
436
- fontWeight: 700,
437
- letterSpacing: "0.01em"
438
- },
439
- children: [
440
- label,
441
- required ? /* @__PURE__ */ jsx3("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
442
- ]
443
- }
444
- ) : null,
445
- hint ? /* @__PURE__ */ jsx3(
446
- "span",
447
- {
448
- style: {
449
- color: mutedText,
450
- fontSize: 13,
451
- lineHeight: 1.45
452
- },
453
- children: hint
454
- }
455
- ) : null
456
- ] }),
457
- children,
458
- error ? /* @__PURE__ */ jsx3(
735
+ const { theme, mutedText } = useUiKitTheme();
736
+ if (!label && !hint && !error) {
737
+ return /* @__PURE__ */ jsx4(Fragment2, { children });
738
+ }
739
+ const labelStyle = {
740
+ color: theme.colors.onSurface,
741
+ fontSize: 14,
742
+ fontWeight: 700,
743
+ letterSpacing: "0.01em"
744
+ };
745
+ const wrapperStyle = {
746
+ display: "flex",
747
+ flexDirection: "column",
748
+ gap: 8,
749
+ width: "100%",
750
+ ...style
751
+ };
752
+ if (!useLabelWrapper) {
753
+ return /* @__PURE__ */ jsxs5("div", { style: wrapperStyle, children: [
754
+ label || hint ? /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
755
+ label ? /* @__PURE__ */ jsxs5("label", { htmlFor, style: labelStyle, children: [
756
+ label,
757
+ required ? /* @__PURE__ */ jsx4("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
758
+ ] }) : null,
759
+ hint ? /* @__PURE__ */ jsx4(
459
760
  "span",
460
761
  {
461
762
  style: {
462
- color: theme.colors.negative,
763
+ color: mutedText,
463
764
  fontSize: 13,
464
- fontWeight: 600,
465
765
  lineHeight: 1.45
466
766
  },
467
- children: error
767
+ children: hint
468
768
  }
469
769
  ) : null
470
- ]
471
- }
472
- );
770
+ ] }) : null,
771
+ children,
772
+ error ? /* @__PURE__ */ jsx4(
773
+ "span",
774
+ {
775
+ style: {
776
+ color: theme.colors.negative,
777
+ fontSize: 13,
778
+ fontWeight: 600,
779
+ lineHeight: 1.45
780
+ },
781
+ children: error
782
+ }
783
+ ) : null
784
+ ] });
785
+ }
786
+ return /* @__PURE__ */ jsxs5("label", { htmlFor, style: wrapperStyle, children: [
787
+ label || hint ? /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
788
+ label ? /* @__PURE__ */ jsxs5("span", { style: labelStyle, children: [
789
+ label,
790
+ required ? /* @__PURE__ */ jsx4("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
791
+ ] }) : null,
792
+ hint ? /* @__PURE__ */ jsx4(
793
+ "span",
794
+ {
795
+ style: {
796
+ color: mutedText,
797
+ fontSize: 13,
798
+ lineHeight: 1.45
799
+ },
800
+ children: hint
801
+ }
802
+ ) : null
803
+ ] }) : null,
804
+ children,
805
+ error ? /* @__PURE__ */ jsx4(
806
+ "span",
807
+ {
808
+ style: {
809
+ color: theme.colors.negative,
810
+ fontSize: 13,
811
+ fontWeight: 600,
812
+ lineHeight: 1.45
813
+ },
814
+ children: error
815
+ }
816
+ ) : null
817
+ ] });
473
818
  }
474
-
475
- // src/components/Input.tsx
476
- import { withAlpha as withAlpha4 } from "smartsell-sales-sdk";
477
- import { jsx as jsx4 } from "react/jsx-runtime";
478
- var getPadding = (size) => {
479
- if (size === "sm") return "10px 12px";
480
- if (size === "lg") return "16px 18px";
481
- return "13px 16px";
482
- };
483
819
  function Input({
484
- sdk,
485
820
  label,
486
821
  hint,
487
822
  error,
488
823
  controlSize = "md",
824
+ showPasswordToggle = false,
489
825
  id,
490
826
  disabled,
491
827
  style,
828
+ type = "text",
492
829
  ...rest
493
830
  }) {
494
- const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
831
+ const { theme, focusRing } = useUiKitTheme();
832
+ const controlMetrics = getControlMetrics(controlSize);
833
+ const passwordToggleMetrics = getPasswordToggleMetrics(controlSize);
834
+ const [isPasswordVisible, setIsPasswordVisible] = useState3(false);
835
+ const isPasswordField = type === "password";
836
+ const canTogglePasswordVisibility = isPasswordField && showPasswordToggle;
837
+ const passwordToggleInset = Math.max(
838
+ 0,
839
+ passwordToggleMetrics.leftOffset + passwordToggleMetrics.buttonSize + passwordToggleMetrics.contentGap - controlMetrics.paddingX
840
+ );
841
+ const leadingInset = Number(canTogglePasswordVisibility) * passwordToggleInset;
842
+ const resolvedType = canTogglePasswordVisibility ? isPasswordVisible ? "text" : "password" : type;
495
843
  const inputStyle = {
496
844
  width: "100%",
845
+ boxSizing: "border-box",
846
+ height: controlMetrics.height,
497
847
  borderRadius: 16,
498
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
499
- padding: getPadding(controlSize),
848
+ border: `1px solid ${error ? theme.colors.negative : withAlpha(theme.colors.textLight, 0.5)}`,
849
+ backgroundColor: theme.colors.surface,
850
+ color: theme.colors.text,
851
+ cursor: disabled ? "not-allowed" : "text",
852
+ opacity: disabled ? 0.7 : 1,
853
+ padding: getTextInputPadding(controlSize, { leadingInset }),
500
854
  outline: "none",
501
- color: theme.colors.onSurface,
502
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
503
- boxShadow: error ? `0 0 0 3px ${withAlpha4(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
855
+ fontSize: controlMetrics.fontSize,
856
+ lineHeight: controlMetrics.lineHeight,
857
+ boxShadow: error ? `0 0 0 3px ${withAlpha(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
504
858
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
505
859
  ...style
506
860
  };
507
- const input = /* @__PURE__ */ jsx4("input", { ...rest, id, disabled, style: inputStyle });
508
- if (!label && !hint && !error) {
509
- return input;
510
- }
511
- return /* @__PURE__ */ jsx4(Field, { sdk, label, hint, error, htmlFor: id, children: input });
861
+ const input = canTogglePasswordVisibility ? /* @__PURE__ */ jsxs5("div", { style: { position: "relative", width: "100%" }, children: [
862
+ /* @__PURE__ */ jsx4(
863
+ "button",
864
+ {
865
+ type: "button",
866
+ "aria-label": isPasswordVisible ? "Hide password" : "Show password",
867
+ "aria-pressed": isPasswordVisible,
868
+ disabled,
869
+ onMouseDown: (event) => {
870
+ event.preventDefault();
871
+ },
872
+ onClick: () => {
873
+ setIsPasswordVisible((currentValue) => !currentValue);
874
+ },
875
+ style: {
876
+ position: "absolute",
877
+ top: "50%",
878
+ left: passwordToggleMetrics.leftOffset,
879
+ transform: "translateY(-50%)",
880
+ zIndex: 1,
881
+ width: passwordToggleMetrics.buttonSize,
882
+ height: passwordToggleMetrics.buttonSize,
883
+ display: "inline-flex",
884
+ alignItems: "center",
885
+ justifyContent: "center",
886
+ border: "none",
887
+ padding: 0,
888
+ margin: 0,
889
+ borderRadius: 999,
890
+ backgroundColor: "transparent",
891
+ color: isPasswordVisible ? theme.colors.primary : theme.colors.textLight,
892
+ cursor: disabled ? "not-allowed" : "pointer",
893
+ opacity: disabled ? 0.5 : 1
894
+ },
895
+ children: isPasswordVisible ? /* @__PURE__ */ jsxs5(
896
+ "svg",
897
+ {
898
+ width: passwordToggleMetrics.iconSize,
899
+ height: passwordToggleMetrics.iconSize,
900
+ viewBox: "0 0 20 20",
901
+ fill: "none",
902
+ "aria-hidden": "true",
903
+ children: [
904
+ /* @__PURE__ */ jsx4(
905
+ "path",
906
+ {
907
+ d: "M2.5 3.5L17.5 16.5",
908
+ stroke: "currentColor",
909
+ strokeWidth: "1.8",
910
+ strokeLinecap: "round"
911
+ }
912
+ ),
913
+ /* @__PURE__ */ jsx4(
914
+ "path",
915
+ {
916
+ d: "M8.825 8.786a2 2 0 0 0 2.389 2.389",
917
+ stroke: "currentColor",
918
+ strokeWidth: "1.8",
919
+ strokeLinecap: "round",
920
+ strokeLinejoin: "round"
921
+ }
922
+ ),
923
+ /* @__PURE__ */ jsx4(
924
+ "path",
925
+ {
926
+ d: "M4.42 6.774C3.422 7.73 2.777 8.815 2.5 9.375c1.182 2.39 3.934 5.625 7.5 5.625 1.155 0 2.224-.34 3.166-.847",
927
+ stroke: "currentColor",
928
+ strokeWidth: "1.8",
929
+ strokeLinecap: "round",
930
+ strokeLinejoin: "round"
931
+ }
932
+ ),
933
+ /* @__PURE__ */ jsx4(
934
+ "path",
935
+ {
936
+ d: "M7.043 4.455A7.7 7.7 0 0 1 10 3.75c3.566 0 6.318 3.235 7.5 5.625-.39.787-1.504 2.588-3.259 3.986",
937
+ stroke: "currentColor",
938
+ strokeWidth: "1.8",
939
+ strokeLinecap: "round",
940
+ strokeLinejoin: "round"
941
+ }
942
+ )
943
+ ]
944
+ }
945
+ ) : /* @__PURE__ */ jsxs5(
946
+ "svg",
947
+ {
948
+ width: passwordToggleMetrics.iconSize,
949
+ height: passwordToggleMetrics.iconSize,
950
+ viewBox: "0 0 20 20",
951
+ fill: "none",
952
+ "aria-hidden": "true",
953
+ children: [
954
+ /* @__PURE__ */ jsx4(
955
+ "path",
956
+ {
957
+ d: "M2.5 10c1.182-2.39 3.934-5.625 7.5-5.625S16.318 7.61 17.5 10c-1.182 2.39-3.934 5.625-7.5 5.625S3.682 12.39 2.5 10Z",
958
+ stroke: "currentColor",
959
+ strokeWidth: "1.8",
960
+ strokeLinecap: "round",
961
+ strokeLinejoin: "round"
962
+ }
963
+ ),
964
+ /* @__PURE__ */ jsx4("circle", { cx: "10", cy: "10", r: "2.25", stroke: "currentColor", strokeWidth: "1.8" })
965
+ ]
966
+ }
967
+ )
968
+ }
969
+ ),
970
+ /* @__PURE__ */ jsx4("input", { ...rest, id, type: resolvedType, disabled, style: inputStyle })
971
+ ] }) : /* @__PURE__ */ jsx4("input", { ...rest, id, type: resolvedType, disabled, style: inputStyle });
972
+ return /* @__PURE__ */ jsx4(ControlFrame, { label, hint, error, htmlFor: id, required: rest.required, children: input });
512
973
  }
513
974
 
514
- // src/components/Select.tsx
515
- import { withAlpha as withAlpha5 } from "smartsell-sales-sdk";
516
- import { jsx as jsx5 } from "react/jsx-runtime";
517
- var getPadding2 = (size) => {
518
- if (size === "sm") return "10px 12px";
519
- if (size === "lg") return "16px 18px";
520
- return "13px 16px";
975
+ // src/components/DatePicker.tsx
976
+ import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
977
+ var DEFAULT_FORMAT_BY_MODE = {
978
+ date: "DD/MM/YYYY",
979
+ datetime: "DD/MM/YYYY HH:mm"
521
980
  };
522
- function Select({
523
- sdk,
981
+ var HTML_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
982
+ var HTML_DATETIME_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::\d{2})?$/;
983
+ var FORMAT_TOKENS = [
984
+ {
985
+ token: "YYYY",
986
+ key: "year",
987
+ pattern: "(\\d{4})",
988
+ formatter: (date) => String(date.getFullYear())
989
+ },
990
+ {
991
+ token: "MM",
992
+ key: "month",
993
+ pattern: "(\\d{1,2})",
994
+ formatter: (date) => padNumber(date.getMonth() + 1)
995
+ },
996
+ {
997
+ token: "DD",
998
+ key: "day",
999
+ pattern: "(\\d{1,2})",
1000
+ formatter: (date) => padNumber(date.getDate())
1001
+ },
1002
+ {
1003
+ token: "HH",
1004
+ key: "hour",
1005
+ pattern: "(\\d{1,2})",
1006
+ formatter: (date) => padNumber(date.getHours())
1007
+ },
1008
+ {
1009
+ token: "mm",
1010
+ key: "minute",
1011
+ pattern: "(\\d{1,2})",
1012
+ formatter: (date) => padNumber(date.getMinutes())
1013
+ }
1014
+ ];
1015
+ var ACCESSORY_METRICS_BY_SIZE = {
1016
+ sm: {
1017
+ buttonSize: 18,
1018
+ iconSize: 14,
1019
+ rightOffset: 8,
1020
+ clusterGap: 6,
1021
+ contentGap: 8
1022
+ },
1023
+ md: {
1024
+ buttonSize: 20,
1025
+ iconSize: 16,
1026
+ rightOffset: 10,
1027
+ clusterGap: 8,
1028
+ contentGap: 10
1029
+ },
1030
+ lg: {
1031
+ buttonSize: 22,
1032
+ iconSize: 18,
1033
+ rightOffset: 12,
1034
+ clusterGap: 8,
1035
+ contentGap: 12
1036
+ }
1037
+ };
1038
+ function padNumber(value, length = 2) {
1039
+ return String(value).padStart(length, "0");
1040
+ }
1041
+ function escapeRegExp(value) {
1042
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1043
+ }
1044
+ function isValidDateInstance(value) {
1045
+ return !Number.isNaN(value.getTime());
1046
+ }
1047
+ function createValidatedDate(year, month, day, hour = 0, minute = 0) {
1048
+ const nextDate = new Date(year, month - 1, day, hour, minute, 0, 0);
1049
+ if (!isValidDateInstance(nextDate)) {
1050
+ return null;
1051
+ }
1052
+ if (nextDate.getFullYear() !== year || nextDate.getMonth() !== month - 1 || nextDate.getDate() !== day || nextDate.getHours() !== hour || nextDate.getMinutes() !== minute) {
1053
+ return null;
1054
+ }
1055
+ return nextDate;
1056
+ }
1057
+ function normalizeDatePrecision(date, mode) {
1058
+ if (mode === "date") {
1059
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
1060
+ }
1061
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), 0, 0);
1062
+ }
1063
+ function toLocalInputValue(date, mode) {
1064
+ const normalizedDate = normalizeDatePrecision(date, mode);
1065
+ const datePart = `${normalizedDate.getFullYear()}-${padNumber(normalizedDate.getMonth() + 1)}-${padNumber(normalizedDate.getDate())}`;
1066
+ if (mode === "date") {
1067
+ return datePart;
1068
+ }
1069
+ return `${datePart}T${padNumber(normalizedDate.getHours())}:${padNumber(normalizedDate.getMinutes())}`;
1070
+ }
1071
+ function parseHtmlDateLikeValue(value) {
1072
+ const trimmedValue = value.trim();
1073
+ const dateMatch = HTML_DATE_PATTERN.exec(trimmedValue);
1074
+ if (dateMatch) {
1075
+ return createValidatedDate(
1076
+ Number(dateMatch[1]),
1077
+ Number(dateMatch[2]),
1078
+ Number(dateMatch[3])
1079
+ );
1080
+ }
1081
+ const datetimeMatch = HTML_DATETIME_PATTERN.exec(trimmedValue);
1082
+ if (datetimeMatch) {
1083
+ return createValidatedDate(
1084
+ Number(datetimeMatch[1]),
1085
+ Number(datetimeMatch[2]),
1086
+ Number(datetimeMatch[3]),
1087
+ Number(datetimeMatch[4]),
1088
+ Number(datetimeMatch[5])
1089
+ );
1090
+ }
1091
+ return null;
1092
+ }
1093
+ function parseFormattedDateValue(value, format, mode) {
1094
+ const orderedParts = [];
1095
+ let expression = "^";
1096
+ for (let index = 0; index < format.length; ) {
1097
+ const token = FORMAT_TOKENS.find(({ token: currentToken }) => format.startsWith(currentToken, index));
1098
+ if (token) {
1099
+ expression += token.pattern;
1100
+ orderedParts.push(token.key);
1101
+ index += token.token.length;
1102
+ continue;
1103
+ }
1104
+ expression += escapeRegExp(format[index]);
1105
+ index += 1;
1106
+ }
1107
+ expression += "$";
1108
+ const match = new RegExp(expression).exec(value.trim());
1109
+ if (!match) {
1110
+ return null;
1111
+ }
1112
+ const resolvedParts = {
1113
+ year: 0,
1114
+ month: 1,
1115
+ day: 1,
1116
+ hour: 0,
1117
+ minute: 0
1118
+ };
1119
+ orderedParts.forEach((key, index) => {
1120
+ resolvedParts[key] = Number(match[index + 1]);
1121
+ });
1122
+ if (!resolvedParts.year || !resolvedParts.month || !resolvedParts.day) {
1123
+ return null;
1124
+ }
1125
+ return createValidatedDate(
1126
+ resolvedParts.year,
1127
+ resolvedParts.month,
1128
+ resolvedParts.day,
1129
+ mode === "datetime" ? resolvedParts.hour : 0,
1130
+ mode === "datetime" ? resolvedParts.minute : 0
1131
+ );
1132
+ }
1133
+ function parseDateValue(value, mode, format) {
1134
+ if (value == null || value === "") {
1135
+ return null;
1136
+ }
1137
+ if (value instanceof Date) {
1138
+ return isValidDateInstance(value) ? normalizeDatePrecision(value, mode) : null;
1139
+ }
1140
+ if (typeof value !== "string") {
1141
+ return null;
1142
+ }
1143
+ const parsedFormattedDate = parseFormattedDateValue(value, format, mode);
1144
+ if (parsedFormattedDate) {
1145
+ return normalizeDatePrecision(parsedFormattedDate, mode);
1146
+ }
1147
+ const parsedHtmlDate = parseHtmlDateLikeValue(value);
1148
+ if (parsedHtmlDate) {
1149
+ return normalizeDatePrecision(parsedHtmlDate, mode);
1150
+ }
1151
+ const parsedNativeDate = new Date(value);
1152
+ return isValidDateInstance(parsedNativeDate) ? normalizeDatePrecision(parsedNativeDate, mode) : null;
1153
+ }
1154
+ function formatDateValue(date, format) {
1155
+ return FORMAT_TOKENS.reduce((currentFormat, token) => {
1156
+ return currentFormat.split(token.token).join(token.formatter(date));
1157
+ }, format);
1158
+ }
1159
+ function getLaterDate(leftDate, rightDate) {
1160
+ if (!leftDate) return rightDate;
1161
+ if (!rightDate) return leftDate;
1162
+ return leftDate.getTime() >= rightDate.getTime() ? leftDate : rightDate;
1163
+ }
1164
+ function getEarlierDate(leftDate, rightDate) {
1165
+ if (!leftDate) return rightDate;
1166
+ if (!rightDate) return leftDate;
1167
+ return leftDate.getTime() <= rightDate.getTime() ? leftDate : rightDate;
1168
+ }
1169
+ function CalendarIcon({ size }) {
1170
+ return /* @__PURE__ */ jsxs6("svg", { width: size, height: size, viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: [
1171
+ /* @__PURE__ */ jsx5("rect", { x: "3", y: "4.5", width: "14", height: "12.5", rx: "3", stroke: "currentColor", strokeWidth: "1.6" }),
1172
+ /* @__PURE__ */ jsx5("path", { d: "M6.25 2.75V6", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
1173
+ /* @__PURE__ */ jsx5("path", { d: "M13.75 2.75V6", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
1174
+ /* @__PURE__ */ jsx5("path", { d: "M3 7.5H17", stroke: "currentColor", strokeWidth: "1.6" }),
1175
+ /* @__PURE__ */ jsx5("circle", { cx: "10", cy: "11.5", r: "1.2", fill: "currentColor" })
1176
+ ] });
1177
+ }
1178
+ function ClearIcon({ size }) {
1179
+ return /* @__PURE__ */ jsxs6("svg", { width: size, height: size, viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: [
1180
+ /* @__PURE__ */ jsx5("path", { d: "M5 5L15 15", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }),
1181
+ /* @__PURE__ */ jsx5("path", { d: "M15 5L5 15", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })
1182
+ ] });
1183
+ }
1184
+ function DatePicker({
524
1185
  label,
525
1186
  hint,
526
1187
  error,
527
1188
  controlSize = "md",
1189
+ value,
1190
+ defaultValue,
1191
+ onChange,
1192
+ mode = "date",
1193
+ format,
1194
+ allowPastDates = true,
1195
+ allowFutureDates = true,
1196
+ minDate,
1197
+ maxDate,
1198
+ minuteStep = 5,
1199
+ clearable = true,
1200
+ showPickerButton = true,
1201
+ isDateUnavailable,
1202
+ invalidDateMessage,
1203
+ unavailableDateMessage,
1204
+ pickerButtonLabel,
1205
+ clearButtonLabel,
528
1206
  id,
1207
+ name,
529
1208
  disabled,
1209
+ readOnly,
1210
+ required,
530
1211
  style,
531
- children,
1212
+ className,
1213
+ placeholder,
1214
+ onBlur,
1215
+ onFocus,
1216
+ onKeyDown,
532
1217
  ...rest
533
1218
  }) {
534
- const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
535
- const selectStyle = {
1219
+ var _a;
1220
+ const { theme, focusRing } = useUiKitTheme();
1221
+ const controlMetrics = getControlMetrics(controlSize);
1222
+ const accessoryMetrics = ACCESSORY_METRICS_BY_SIZE[controlSize];
1223
+ const generatedIdRef = useRef2();
1224
+ const visibleInputRef = useRef2(null);
1225
+ const nativeInputRef = useRef2(null);
1226
+ const [internalError, setInternalError] = useState4();
1227
+ const resolvedFormat = (format == null ? void 0 : format.trim()) ? format : DEFAULT_FORMAT_BY_MODE[mode];
1228
+ const isControlled = value !== void 0;
1229
+ const nativePickerType = mode === "datetime" ? "datetime-local" : "date";
1230
+ if (!generatedIdRef.current) {
1231
+ generatedIdRef.current = `ss-date-picker-${Math.random().toString(36).slice(2, 10)}`;
1232
+ }
1233
+ const resolvedId = id != null ? id : generatedIdRef.current;
1234
+ const normalizedDefaultValue = useMemo3(() => {
1235
+ const parsedDefaultValue = parseDateValue(defaultValue, mode, resolvedFormat);
1236
+ return parsedDefaultValue ? toLocalInputValue(parsedDefaultValue, mode) : null;
1237
+ }, [defaultValue, mode, resolvedFormat]);
1238
+ const [uncontrolledValue, setUncontrolledValue] = useState4(normalizedDefaultValue);
1239
+ const committedDate = useMemo3(() => {
1240
+ const sourceValue = isControlled ? value : uncontrolledValue;
1241
+ return parseDateValue(sourceValue, mode, resolvedFormat);
1242
+ }, [isControlled, mode, resolvedFormat, uncontrolledValue, value]);
1243
+ const committedValue = committedDate ? toLocalInputValue(committedDate, mode) : null;
1244
+ const committedDisplayValue = committedDate ? formatDateValue(committedDate, resolvedFormat) : "";
1245
+ const [draftValue, setDraftValue] = useState4(committedDisplayValue);
1246
+ useEffect2(() => {
1247
+ setDraftValue(committedDisplayValue);
1248
+ setInternalError(void 0);
1249
+ }, [committedDisplayValue]);
1250
+ useEffect2(() => {
1251
+ if (!isControlled) {
1252
+ setUncontrolledValue((currentValue) => {
1253
+ if (currentValue == null) {
1254
+ return currentValue;
1255
+ }
1256
+ const parsedCurrentValue = parseDateValue(currentValue, mode, resolvedFormat);
1257
+ return parsedCurrentValue ? toLocalInputValue(parsedCurrentValue, mode) : null;
1258
+ });
1259
+ }
1260
+ }, [isControlled, mode, resolvedFormat]);
1261
+ const parsedMinDate = useMemo3(() => {
1262
+ const explicitMinDate = parseDateValue(minDate, mode, resolvedFormat);
1263
+ const guardedMinDate = allowPastDates ? null : normalizeDatePrecision(/* @__PURE__ */ new Date(), mode);
1264
+ return getLaterDate(explicitMinDate, guardedMinDate);
1265
+ }, [allowPastDates, minDate, mode, resolvedFormat]);
1266
+ const parsedMaxDate = useMemo3(() => {
1267
+ const explicitMaxDate = parseDateValue(maxDate, mode, resolvedFormat);
1268
+ const guardedMaxDate = allowFutureDates ? null : normalizeDatePrecision(/* @__PURE__ */ new Date(), mode);
1269
+ return getEarlierDate(explicitMaxDate, guardedMaxDate);
1270
+ }, [allowFutureDates, maxDate, mode, resolvedFormat]);
1271
+ const hasExternalError = error !== void 0 && error !== null && error !== "";
1272
+ const hasConfigurationConflict = Boolean(parsedMinDate && parsedMaxDate && parsedMinDate.getTime() > parsedMaxDate.getTime());
1273
+ const derivedError = hasConfigurationConflict ? "Current date rules do not allow any selection." : internalError;
1274
+ const resolvedError = hasExternalError ? error : derivedError;
1275
+ const hasAnyValue = draftValue.trim().length > 0 || Boolean(committedValue);
1276
+ const canOpenPicker = showPickerButton && !disabled && !readOnly && !hasConfigurationConflict;
1277
+ const canClear = clearable && !disabled && !readOnly && !required && hasAnyValue;
1278
+ const visibleButtonCount = Number(canOpenPicker) + Number(canClear);
1279
+ const trailingInset = visibleButtonCount > 0 ? Math.max(
1280
+ 0,
1281
+ accessoryMetrics.rightOffset + visibleButtonCount * accessoryMetrics.buttonSize + (visibleButtonCount - 1) * accessoryMetrics.clusterGap + accessoryMetrics.contentGap - controlMetrics.paddingX
1282
+ ) : 0;
1283
+ const getValidationMessage = (parsedDate) => {
1284
+ if (parsedMinDate && parsedDate.getTime() < parsedMinDate.getTime()) {
1285
+ return `Choose a value on or after ${formatDateValue(parsedMinDate, resolvedFormat)}.`;
1286
+ }
1287
+ if (parsedMaxDate && parsedDate.getTime() > parsedMaxDate.getTime()) {
1288
+ return `Choose a value on or before ${formatDateValue(parsedMaxDate, resolvedFormat)}.`;
1289
+ }
1290
+ if (mode === "datetime" && minuteStep > 1 && parsedDate.getMinutes() % minuteStep !== 0) {
1291
+ return `Minutes must use ${minuteStep}-minute intervals.`;
1292
+ }
1293
+ if (isDateUnavailable == null ? void 0 : isDateUnavailable(parsedDate)) {
1294
+ return unavailableDateMessage != null ? unavailableDateMessage : "This date is not available.";
1295
+ }
1296
+ return void 0;
1297
+ };
1298
+ const emitChange = (nextValue, context) => {
1299
+ onChange == null ? void 0 : onChange(nextValue, context);
1300
+ };
1301
+ const commitValue = (nextValue, reason) => {
1302
+ if (hasConfigurationConflict) {
1303
+ setInternalError("Current date rules do not allow any selection.");
1304
+ return false;
1305
+ }
1306
+ if (!nextValue) {
1307
+ if (!isControlled) {
1308
+ setUncontrolledValue(null);
1309
+ }
1310
+ setDraftValue("");
1311
+ setInternalError(void 0);
1312
+ emitChange(null, {
1313
+ reason,
1314
+ date: null,
1315
+ nativeValue: "",
1316
+ displayValue: ""
1317
+ });
1318
+ return true;
1319
+ }
1320
+ const parsedDate = parseHtmlDateLikeValue(nextValue);
1321
+ if (!parsedDate) {
1322
+ setInternalError(invalidDateMessage != null ? invalidDateMessage : `Enter a valid value using ${resolvedFormat}.`);
1323
+ return false;
1324
+ }
1325
+ const normalizedDate = normalizeDatePrecision(parsedDate, mode);
1326
+ const validationMessage = getValidationMessage(normalizedDate);
1327
+ if (validationMessage) {
1328
+ setInternalError(validationMessage);
1329
+ return false;
1330
+ }
1331
+ const normalizedValue = toLocalInputValue(normalizedDate, mode);
1332
+ const displayValue = formatDateValue(normalizedDate, resolvedFormat);
1333
+ if (!isControlled) {
1334
+ setUncontrolledValue(normalizedValue);
1335
+ }
1336
+ setDraftValue(displayValue);
1337
+ setInternalError(void 0);
1338
+ emitChange(normalizedValue, {
1339
+ reason,
1340
+ date: normalizedDate,
1341
+ nativeValue: normalizedValue,
1342
+ displayValue
1343
+ });
1344
+ return true;
1345
+ };
1346
+ const commitDraftValue = () => {
1347
+ var _a2, _b;
1348
+ const trimmedDraftValue = draftValue.trim();
1349
+ if (!trimmedDraftValue) {
1350
+ return commitValue(null, "clear");
1351
+ }
1352
+ const parsedDraftDate = (_b = (_a2 = parseFormattedDateValue(trimmedDraftValue, resolvedFormat, mode)) != null ? _a2 : parseHtmlDateLikeValue(trimmedDraftValue)) != null ? _b : parseDateValue(trimmedDraftValue, mode, resolvedFormat);
1353
+ if (!parsedDraftDate) {
1354
+ setInternalError(invalidDateMessage != null ? invalidDateMessage : `Enter a valid value using ${resolvedFormat}.`);
1355
+ return false;
1356
+ }
1357
+ return commitValue(toLocalInputValue(parsedDraftDate, mode), "input");
1358
+ };
1359
+ const openNativePicker = () => {
1360
+ var _a2, _b;
1361
+ if (!canOpenPicker || !nativeInputRef.current) {
1362
+ return;
1363
+ }
1364
+ const parsedDraftDate = draftValue.trim() ? (_a2 = parseFormattedDateValue(draftValue, resolvedFormat, mode)) != null ? _a2 : parseHtmlDateLikeValue(draftValue) : null;
1365
+ if (parsedDraftDate) {
1366
+ nativeInputRef.current.value = toLocalInputValue(parsedDraftDate, mode);
1367
+ }
1368
+ const pickerInput = nativeInputRef.current;
1369
+ try {
1370
+ if (pickerInput.showPicker) {
1371
+ pickerInput.showPicker();
1372
+ return;
1373
+ }
1374
+ pickerInput.focus();
1375
+ } catch {
1376
+ (_b = visibleInputRef.current) == null ? void 0 : _b.focus();
1377
+ }
1378
+ };
1379
+ const inputStyle = {
536
1380
  width: "100%",
1381
+ boxSizing: "border-box",
1382
+ height: controlMetrics.height,
537
1383
  borderRadius: 16,
538
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
539
- padding: getPadding2(controlSize),
1384
+ border: `1px solid ${resolvedError ? theme.colors.negative : withAlpha(theme.colors.textLight, 0.5)}`,
1385
+ backgroundColor: theme.colors.surface,
1386
+ color: theme.colors.text,
1387
+ cursor: disabled ? "not-allowed" : readOnly ? "default" : "text",
1388
+ opacity: disabled ? 0.7 : 1,
1389
+ padding: getTextInputPadding(controlSize, { trailingInset }),
540
1390
  outline: "none",
541
- color: theme.colors.onSurface,
542
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
543
- boxShadow: error ? `0 0 0 3px ${withAlpha5(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
1391
+ fontSize: controlMetrics.fontSize,
1392
+ lineHeight: controlMetrics.lineHeight,
1393
+ boxShadow: resolvedError ? `0 0 0 3px ${withAlpha(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
544
1394
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
545
1395
  ...style
546
1396
  };
547
- const select = /* @__PURE__ */ jsx5("select", { ...rest, id, disabled, style: selectStyle, children });
548
- if (!label && !hint && !error) {
549
- return select;
1397
+ const accessoryButtonStyle = {
1398
+ width: accessoryMetrics.buttonSize,
1399
+ height: accessoryMetrics.buttonSize,
1400
+ border: "none",
1401
+ padding: 0,
1402
+ margin: 0,
1403
+ display: "inline-flex",
1404
+ alignItems: "center",
1405
+ justifyContent: "center",
1406
+ borderRadius: 999,
1407
+ backgroundColor: "transparent",
1408
+ color: theme.colors.textLight,
1409
+ cursor: disabled || readOnly ? "not-allowed" : "pointer",
1410
+ opacity: disabled ? 0.5 : 1
1411
+ };
1412
+ const input = /* @__PURE__ */ jsxs6("div", { style: { position: "relative", width: "100%" }, children: [
1413
+ /* @__PURE__ */ jsx5(
1414
+ "input",
1415
+ {
1416
+ ...rest,
1417
+ ref: visibleInputRef,
1418
+ id: resolvedId,
1419
+ className,
1420
+ type: "text",
1421
+ value: draftValue,
1422
+ disabled,
1423
+ readOnly,
1424
+ required,
1425
+ placeholder: placeholder != null ? placeholder : resolvedFormat,
1426
+ autoComplete: (_a = rest.autoComplete) != null ? _a : "off",
1427
+ spellCheck: false,
1428
+ "aria-invalid": Boolean(resolvedError),
1429
+ onChange: (event) => {
1430
+ setDraftValue(event.target.value);
1431
+ setInternalError(void 0);
1432
+ },
1433
+ onFocus: (event) => {
1434
+ setInternalError(void 0);
1435
+ onFocus == null ? void 0 : onFocus(event);
1436
+ },
1437
+ onBlur: (event) => {
1438
+ commitDraftValue();
1439
+ onBlur == null ? void 0 : onBlur(event);
1440
+ },
1441
+ onKeyDown: (event) => {
1442
+ onKeyDown == null ? void 0 : onKeyDown(event);
1443
+ if (event.defaultPrevented) {
1444
+ return;
1445
+ }
1446
+ if (event.key === "Enter") {
1447
+ event.preventDefault();
1448
+ commitDraftValue();
1449
+ return;
1450
+ }
1451
+ if (event.key === "Escape") {
1452
+ event.preventDefault();
1453
+ setDraftValue(committedDisplayValue);
1454
+ setInternalError(void 0);
1455
+ }
1456
+ },
1457
+ style: inputStyle
1458
+ }
1459
+ ),
1460
+ /* @__PURE__ */ jsx5(
1461
+ "input",
1462
+ {
1463
+ ref: nativeInputRef,
1464
+ tabIndex: -1,
1465
+ "aria-hidden": "true",
1466
+ name,
1467
+ type: nativePickerType,
1468
+ value: committedValue != null ? committedValue : "",
1469
+ min: parsedMinDate ? toLocalInputValue(parsedMinDate, mode) : void 0,
1470
+ max: parsedMaxDate ? toLocalInputValue(parsedMaxDate, mode) : void 0,
1471
+ step: mode === "datetime" ? Math.max(1, minuteStep) * 60 : void 0,
1472
+ disabled: disabled || readOnly || hasConfigurationConflict,
1473
+ onChange: (event) => {
1474
+ commitValue(event.target.value || null, "picker");
1475
+ },
1476
+ style: {
1477
+ position: "absolute",
1478
+ width: 1,
1479
+ height: 1,
1480
+ padding: 0,
1481
+ margin: -1,
1482
+ overflow: "hidden",
1483
+ clip: "rect(0, 0, 0, 0)",
1484
+ whiteSpace: "nowrap",
1485
+ border: 0,
1486
+ opacity: 0,
1487
+ pointerEvents: "none"
1488
+ }
1489
+ }
1490
+ ),
1491
+ visibleButtonCount > 0 ? /* @__PURE__ */ jsxs6(
1492
+ "div",
1493
+ {
1494
+ style: {
1495
+ position: "absolute",
1496
+ top: "50%",
1497
+ right: accessoryMetrics.rightOffset,
1498
+ transform: "translateY(-50%)",
1499
+ display: "inline-flex",
1500
+ alignItems: "center",
1501
+ gap: accessoryMetrics.clusterGap
1502
+ },
1503
+ children: [
1504
+ canClear ? /* @__PURE__ */ jsx5(
1505
+ "button",
1506
+ {
1507
+ type: "button",
1508
+ "aria-label": clearButtonLabel != null ? clearButtonLabel : "Clear date",
1509
+ disabled: disabled || readOnly,
1510
+ onMouseDown: (event) => {
1511
+ event.preventDefault();
1512
+ },
1513
+ onClick: () => {
1514
+ var _a2;
1515
+ commitValue(null, "clear");
1516
+ (_a2 = visibleInputRef.current) == null ? void 0 : _a2.focus();
1517
+ },
1518
+ style: accessoryButtonStyle,
1519
+ children: /* @__PURE__ */ jsx5(ClearIcon, { size: accessoryMetrics.iconSize })
1520
+ }
1521
+ ) : null,
1522
+ canOpenPicker ? /* @__PURE__ */ jsx5(
1523
+ "button",
1524
+ {
1525
+ type: "button",
1526
+ "aria-label": pickerButtonLabel != null ? pickerButtonLabel : mode === "datetime" ? "Open date and time picker" : "Open date picker",
1527
+ disabled: disabled || readOnly || hasConfigurationConflict,
1528
+ onMouseDown: (event) => {
1529
+ event.preventDefault();
1530
+ },
1531
+ onClick: openNativePicker,
1532
+ style: accessoryButtonStyle,
1533
+ children: /* @__PURE__ */ jsx5(CalendarIcon, { size: accessoryMetrics.iconSize })
1534
+ }
1535
+ ) : null
1536
+ ]
1537
+ }
1538
+ ) : null
1539
+ ] });
1540
+ return /* @__PURE__ */ jsx5(
1541
+ ControlFrame,
1542
+ {
1543
+ label,
1544
+ hint,
1545
+ error: resolvedError,
1546
+ htmlFor: resolvedId,
1547
+ required,
1548
+ children: input
1549
+ }
1550
+ );
1551
+ }
1552
+
1553
+ // src/components/Select.tsx
1554
+ import {
1555
+ Children,
1556
+ isValidElement,
1557
+ useEffect as useEffect3,
1558
+ useId as useId2,
1559
+ useRef as useRef3,
1560
+ useState as useState5
1561
+ } from "react";
1562
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs7 } from "react/jsx-runtime";
1563
+ var MENU_OFFSET = 8;
1564
+ var MENU_MAX_HEIGHT = 280;
1565
+ var getOptionPadding = (size) => {
1566
+ if (size === "sm") return "10px 12px";
1567
+ if (size === "lg") return "14px 16px";
1568
+ return "12px 14px";
1569
+ };
1570
+ function getTextContent(node) {
1571
+ if (node == null || typeof node === "boolean") {
1572
+ return "";
1573
+ }
1574
+ if (typeof node === "string" || typeof node === "number") {
1575
+ return String(node);
1576
+ }
1577
+ if (Array.isArray(node)) {
1578
+ return node.map(getTextContent).join("");
1579
+ }
1580
+ if (isValidElement(node)) {
1581
+ return getTextContent(node.props.children);
1582
+ }
1583
+ return "";
1584
+ }
1585
+ function normalizeSelectValue(value) {
1586
+ if (Array.isArray(value)) {
1587
+ return value.length > 0 ? normalizeSelectValue(value[0]) : "";
1588
+ }
1589
+ return value == null ? "" : String(value);
1590
+ }
1591
+ function normalizeSelectValues(value) {
1592
+ if (Array.isArray(value)) {
1593
+ return value.map((item) => normalizeSelectValue(item));
1594
+ }
1595
+ if (value == null) {
1596
+ return [];
1597
+ }
1598
+ return [normalizeSelectValue(value)];
1599
+ }
1600
+ function buildOption(keyPrefix, index, props, groupLabel, groupDisabled = false) {
1601
+ var _a;
1602
+ const rawValue = props.value !== void 0 ? props.value : getTextContent(props.children).trim();
1603
+ const normalizedValue = normalizeSelectValue(rawValue);
1604
+ return {
1605
+ key: `${keyPrefix}-${index}-${normalizedValue}`,
1606
+ value: normalizedValue,
1607
+ label: (_a = props.children) != null ? _a : normalizedValue,
1608
+ disabled: groupDisabled || Boolean(props.disabled)
1609
+ };
1610
+ }
1611
+ function parseSelectChildren(children) {
1612
+ const groups = [];
1613
+ const standaloneOptions = [];
1614
+ let optionIndex = 0;
1615
+ let groupIndex = 0;
1616
+ const flushStandaloneOptions = () => {
1617
+ if (standaloneOptions.length === 0) {
1618
+ return;
1619
+ }
1620
+ groups.push({
1621
+ key: `standalone-${groups.length}`,
1622
+ options: [...standaloneOptions]
1623
+ });
1624
+ standaloneOptions.length = 0;
1625
+ };
1626
+ Children.forEach(children, (child) => {
1627
+ if (!isValidElement(child)) {
1628
+ return;
1629
+ }
1630
+ if (child.type === "option") {
1631
+ standaloneOptions.push(buildOption("option", optionIndex, child.props));
1632
+ optionIndex += 1;
1633
+ return;
1634
+ }
1635
+ if (child.type !== "optgroup") {
1636
+ return;
1637
+ }
1638
+ flushStandaloneOptions();
1639
+ const groupOptions = [];
1640
+ let groupOptionIndex = 0;
1641
+ Children.forEach(child.props.children, (optionChild) => {
1642
+ if (!isValidElement(optionChild) || optionChild.type !== "option") {
1643
+ return;
1644
+ }
1645
+ groupOptions.push(
1646
+ buildOption(
1647
+ `group-${groupIndex}-option`,
1648
+ groupOptionIndex,
1649
+ optionChild.props,
1650
+ child.props.label,
1651
+ Boolean(child.props.disabled)
1652
+ )
1653
+ );
1654
+ groupOptionIndex += 1;
1655
+ });
1656
+ if (groupOptions.length > 0) {
1657
+ groups.push({
1658
+ key: `group-${groupIndex}`,
1659
+ label: child.props.label,
1660
+ options: groupOptions
1661
+ });
1662
+ groupIndex += 1;
1663
+ }
1664
+ });
1665
+ flushStandaloneOptions();
1666
+ return groups;
1667
+ }
1668
+ function filterSelectGroups(groups, query) {
1669
+ const normalizedQuery = query.trim().toLowerCase();
1670
+ if (!normalizedQuery) {
1671
+ return groups;
550
1672
  }
551
- return /* @__PURE__ */ jsx5(Field, { sdk, label, hint, error, htmlFor: id, children: select });
1673
+ return groups.map((group) => ({
1674
+ ...group,
1675
+ options: group.options.filter((option) => {
1676
+ const optionText = getTextContent(option.label).trim().toLowerCase();
1677
+ return optionText.includes(normalizedQuery);
1678
+ })
1679
+ })).filter((group) => group.options.length > 0);
1680
+ }
1681
+ function Select({
1682
+ label,
1683
+ hint,
1684
+ error,
1685
+ controlSize = "md",
1686
+ id,
1687
+ disabled,
1688
+ style,
1689
+ children,
1690
+ value,
1691
+ defaultValue,
1692
+ onChange,
1693
+ onFocus,
1694
+ onBlur,
1695
+ onKeyDown,
1696
+ onClick,
1697
+ autoFocus,
1698
+ className,
1699
+ isMulti,
1700
+ isFilterable,
1701
+ multiple,
1702
+ ...rest
1703
+ }) {
1704
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1705
+ const { theme, mutedText, surfaceRaised } = useUiKitTheme();
1706
+ const generatedId = useId2().replace(/:/g, "");
1707
+ const resolvedId = id != null ? id : `ss-select-${generatedId}`;
1708
+ const isMultiSelect = Boolean(isMulti || multiple);
1709
+ const hasFilter = Boolean(isFilterable);
1710
+ const groups = parseSelectChildren(children);
1711
+ const flatOptions = groups.flatMap((group) => group.options);
1712
+ const enabledOptions = flatOptions.filter((option) => !option.disabled);
1713
+ const isControlled = value !== void 0;
1714
+ const fallbackValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue) : isMultiSelect ? [] : ((_a = enabledOptions[0]) == null ? void 0 : _a.value) ? [enabledOptions[0].value] : [];
1715
+ const [uncontrolledValues, setUncontrolledValues] = useState5(fallbackValues);
1716
+ const [isOpen, setIsOpen] = useState5(false);
1717
+ const [filterQuery, setFilterQuery] = useState5("");
1718
+ const [menuPlacement, setMenuPlacement] = useState5("bottom");
1719
+ const [isPlacementResolved, setIsPlacementResolved] = useState5(false);
1720
+ const [highlightedValue, setHighlightedValue] = useState5((_d = (_c = fallbackValues[0]) != null ? _c : (_b = enabledOptions[0]) == null ? void 0 : _b.value) != null ? _d : "");
1721
+ const shellRef = useRef3(null);
1722
+ const buttonRef = useRef3(null);
1723
+ const menuRef = useRef3(null);
1724
+ const searchInputRef = useRef3(null);
1725
+ const hiddenSelectRef = useRef3(null);
1726
+ const selectedValues = isControlled ? normalizeSelectValues(value) : uncontrolledValues;
1727
+ const selectedValue = (_e = selectedValues[0]) != null ? _e : "";
1728
+ const selectedOptions = flatOptions.filter((option) => selectedValues.includes(option.value));
1729
+ const selectedOption = (_f = selectedOptions[0]) != null ? _f : null;
1730
+ const displayLabel = isMultiSelect ? selectedOptions.length > 0 ? selectedOptions.map((option) => getTextContent(option.label).trim() || option.value).join(", ") : null : (_g = selectedOption == null ? void 0 : selectedOption.label) != null ? _g : selectedValue ? selectedValue : null;
1731
+ const displayContent = displayLabel != null ? displayLabel : "\xA0";
1732
+ const visibleGroups = hasFilter ? filterSelectGroups(groups, filterQuery) : groups;
1733
+ const visibleOptions = visibleGroups.flatMap((group) => group.options);
1734
+ const visibleEnabledOptions = visibleOptions.filter((option) => !option.disabled);
1735
+ const visibleEnabledValues = visibleEnabledOptions.map((option) => option.value);
1736
+ const selectedValuesKey = selectedValues.join("\0");
1737
+ const visibleEnabledValuesKey = visibleEnabledValues.join("\0");
1738
+ const indicatorOffset = getSelectIndicatorOffset(controlSize);
1739
+ const controlMetrics = getControlMetrics(controlSize);
1740
+ const listboxId = `${resolvedId}-listbox`;
1741
+ const scrollbarThumbColor = withAlpha(theme.colors.onSurface, 0.22);
1742
+ const scrollbarThumbHoverColor = withAlpha(theme.colors.onSurface, 0.32);
1743
+ const highlightedOption = (_h = visibleOptions.find((option) => option.value === highlightedValue)) != null ? _h : null;
1744
+ const optionHoverBackground = withAlpha(theme.colors.primary, 1);
1745
+ const optionSelectedBackground = withAlpha(theme.colors.primary, 1);
1746
+ const triggerShadow = error ? `0 0 0 3px ${withAlpha(theme.colors.negative, 0.12)}` : "none";
1747
+ const buttonPassthroughProps = Object.fromEntries(
1748
+ Object.entries(rest).filter(([key]) => key.startsWith("aria-") || key.startsWith("data-"))
1749
+ );
1750
+ useEffect3(() => {
1751
+ var _a2, _b2, _c2, _d2;
1752
+ if (isControlled) {
1753
+ return;
1754
+ }
1755
+ const invalidValues = uncontrolledValues.filter(
1756
+ (currentValue) => !flatOptions.some((option) => option.value === currentValue)
1757
+ );
1758
+ if (invalidValues.length === 0) {
1759
+ return;
1760
+ }
1761
+ const nextValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue).filter(
1762
+ (currentValue) => flatOptions.some((option) => option.value === currentValue)
1763
+ ) : isMultiSelect ? [] : ((_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) ? [enabledOptions[0].value] : [];
1764
+ setUncontrolledValues(nextValues);
1765
+ setHighlightedValue((_d2 = (_c2 = nextValues[0]) != null ? _c2 : (_b2 = enabledOptions[0]) == null ? void 0 : _b2.value) != null ? _d2 : "");
1766
+ }, [children, defaultValue, enabledOptions, flatOptions, isControlled, isMultiSelect, uncontrolledValues]);
1767
+ useEffect3(() => {
1768
+ var _a2, _b2, _c2;
1769
+ if (!isOpen) {
1770
+ setMenuPlacement("bottom");
1771
+ setIsPlacementResolved(false);
1772
+ return;
1773
+ }
1774
+ const nextHighlightedValue = (_c2 = (_b2 = [...selectedValues].reverse().find((currentValue) => visibleEnabledValues.includes(currentValue))) != null ? _b2 : (_a2 = visibleEnabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "";
1775
+ setHighlightedValue(nextHighlightedValue);
1776
+ }, [isOpen, selectedValuesKey, visibleEnabledValuesKey]);
1777
+ useEffect3(() => {
1778
+ var _a2;
1779
+ if (!isOpen || !hasFilter) {
1780
+ return;
1781
+ }
1782
+ (_a2 = searchInputRef.current) == null ? void 0 : _a2.focus();
1783
+ }, [hasFilter, isOpen]);
1784
+ useEffect3(() => {
1785
+ if (!isOpen) {
1786
+ return;
1787
+ }
1788
+ const frameId = window.requestAnimationFrame(() => {
1789
+ var _a2;
1790
+ const triggerRect = (_a2 = buttonRef.current) == null ? void 0 : _a2.getBoundingClientRect();
1791
+ const menuElement = menuRef.current;
1792
+ if (!triggerRect || !menuElement) {
1793
+ setMenuPlacement("bottom");
1794
+ setIsPlacementResolved(true);
1795
+ return;
1796
+ }
1797
+ const measuredMenuHeight = menuElement.getBoundingClientRect().height;
1798
+ const menuHeight = Math.min(measuredMenuHeight || menuElement.scrollHeight, MENU_MAX_HEIGHT);
1799
+ const availableBelow = window.innerHeight - triggerRect.bottom - MENU_OFFSET;
1800
+ const availableAbove = triggerRect.top - MENU_OFFSET;
1801
+ const nextPlacement = availableBelow < menuHeight && availableAbove >= menuHeight ? "top" : "bottom";
1802
+ setMenuPlacement(nextPlacement);
1803
+ setIsPlacementResolved(true);
1804
+ });
1805
+ return () => {
1806
+ window.cancelAnimationFrame(frameId);
1807
+ };
1808
+ }, [isOpen, visibleOptions.length]);
1809
+ useEffect3(() => {
1810
+ if (isOpen) {
1811
+ return;
1812
+ }
1813
+ setFilterQuery("");
1814
+ }, [isOpen]);
1815
+ useEffect3(() => {
1816
+ if (!isOpen) {
1817
+ return;
1818
+ }
1819
+ const handlePointerDown = (event) => {
1820
+ var _a2;
1821
+ if ((_a2 = shellRef.current) == null ? void 0 : _a2.contains(event.target)) {
1822
+ return;
1823
+ }
1824
+ setIsOpen(false);
1825
+ };
1826
+ document.addEventListener("pointerdown", handlePointerDown);
1827
+ return () => {
1828
+ document.removeEventListener("pointerdown", handlePointerDown);
1829
+ };
1830
+ }, [isOpen]);
1831
+ useEffect3(() => {
1832
+ var _a2;
1833
+ if (isControlled) {
1834
+ return;
1835
+ }
1836
+ const form = (_a2 = hiddenSelectRef.current) == null ? void 0 : _a2.form;
1837
+ if (!form) {
1838
+ return;
1839
+ }
1840
+ const handleReset = () => {
1841
+ queueMicrotask(() => {
1842
+ var _a3, _b2, _c2, _d2;
1843
+ const nextValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue) : isMultiSelect ? [] : ((_a3 = enabledOptions[0]) == null ? void 0 : _a3.value) ? [enabledOptions[0].value] : [];
1844
+ setUncontrolledValues(nextValues);
1845
+ setHighlightedValue((_d2 = (_c2 = nextValues[0]) != null ? _c2 : (_b2 = enabledOptions[0]) == null ? void 0 : _b2.value) != null ? _d2 : "");
1846
+ setIsOpen(false);
1847
+ });
1848
+ };
1849
+ form.addEventListener("reset", handleReset);
1850
+ return () => {
1851
+ form.removeEventListener("reset", handleReset);
1852
+ };
1853
+ }, [defaultValue, enabledOptions, isControlled, isMultiSelect]);
1854
+ const syncHiddenSelect = (nextValues) => {
1855
+ var _a2, _b2;
1856
+ const selectElement = hiddenSelectRef.current;
1857
+ if (!selectElement) {
1858
+ return;
1859
+ }
1860
+ if (isMultiSelect) {
1861
+ Array.from(selectElement.options).forEach((optionElement) => {
1862
+ optionElement.selected = nextValues.includes(optionElement.value);
1863
+ });
1864
+ } else {
1865
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value");
1866
+ if (descriptor == null ? void 0 : descriptor.set) {
1867
+ descriptor.set.call(selectElement, (_a2 = nextValues[0]) != null ? _a2 : "");
1868
+ } else {
1869
+ selectElement.value = (_b2 = nextValues[0]) != null ? _b2 : "";
1870
+ }
1871
+ }
1872
+ selectElement.dispatchEvent(new Event("change", { bubbles: true }));
1873
+ };
1874
+ const updateMultiSelection = (nextValues) => {
1875
+ var _a2, _b2, _c2;
1876
+ if (!isMultiSelect || disabled) {
1877
+ return;
1878
+ }
1879
+ if (!isControlled) {
1880
+ setUncontrolledValues(nextValues);
1881
+ }
1882
+ setHighlightedValue((_c2 = (_b2 = nextValues[nextValues.length - 1]) != null ? _b2 : (_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "");
1883
+ syncHiddenSelect(nextValues);
1884
+ };
1885
+ const toggleVisibleSelection = (checked) => {
1886
+ if (!isMultiSelect || disabled || visibleEnabledValues.length === 0) {
1887
+ return;
1888
+ }
1889
+ const nextValues = checked ? Array.from(/* @__PURE__ */ new Set([...selectedValues, ...visibleEnabledValues])) : selectedValues.filter((currentValue) => !visibleEnabledValues.includes(currentValue));
1890
+ updateMultiSelection(nextValues);
1891
+ };
1892
+ const commitSelection = (nextValue) => {
1893
+ var _a2;
1894
+ if (disabled) {
1895
+ if (!isMultiSelect) {
1896
+ setIsOpen(false);
1897
+ }
1898
+ return;
1899
+ }
1900
+ if (isMultiSelect) {
1901
+ const nextValues = selectedValues.includes(nextValue) ? selectedValues.filter((currentValue) => currentValue !== nextValue) : [...selectedValues, nextValue];
1902
+ updateMultiSelection(nextValues);
1903
+ return;
1904
+ }
1905
+ if (nextValue === selectedValue) {
1906
+ setIsOpen(false);
1907
+ return;
1908
+ }
1909
+ if (!isControlled) {
1910
+ setUncontrolledValues([nextValue]);
1911
+ }
1912
+ setHighlightedValue(nextValue);
1913
+ setIsOpen(false);
1914
+ syncHiddenSelect([nextValue]);
1915
+ (_a2 = buttonRef.current) == null ? void 0 : _a2.focus();
1916
+ };
1917
+ const handleHiddenSelectChange = (event) => {
1918
+ var _a2, _b2, _c2;
1919
+ const nextValues = isMultiSelect ? Array.from(event.currentTarget.selectedOptions, (option) => option.value) : [event.currentTarget.value];
1920
+ if (!isControlled) {
1921
+ setUncontrolledValues(nextValues);
1922
+ }
1923
+ setHighlightedValue((_c2 = (_b2 = nextValues[nextValues.length - 1]) != null ? _b2 : (_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "");
1924
+ onChange == null ? void 0 : onChange(event);
1925
+ };
1926
+ const visibleSelectedCount = visibleEnabledValues.filter((currentValue) => selectedValues.includes(currentValue)).length;
1927
+ const hasSomeVisibleSelected = visibleSelectedCount > 0 && visibleSelectedCount < visibleEnabledValues.length;
1928
+ const hasAllVisibleSelected = visibleEnabledValues.length > 0 && visibleSelectedCount === visibleEnabledValues.length;
1929
+ const canToggleVisibleSelection = !disabled && visibleEnabledValues.length > 0;
1930
+ const bulkToggleControl = isMultiSelect ? /* @__PURE__ */ jsx6(
1931
+ "button",
1932
+ {
1933
+ type: "button",
1934
+ role: "checkbox",
1935
+ "aria-label": "Toggle all visible options",
1936
+ "aria-checked": hasAllVisibleSelected ? true : hasSomeVisibleSelected ? "mixed" : false,
1937
+ disabled: !canToggleVisibleSelection,
1938
+ onMouseDown: (event) => {
1939
+ event.preventDefault();
1940
+ event.stopPropagation();
1941
+ },
1942
+ onClick: (event) => {
1943
+ event.stopPropagation();
1944
+ toggleVisibleSelection(!hasAllVisibleSelected);
1945
+ },
1946
+ style: {
1947
+ width: 20,
1948
+ height: 20,
1949
+ padding: 0,
1950
+ margin: 0,
1951
+ display: "inline-flex",
1952
+ alignItems: "center",
1953
+ justifyContent: "center",
1954
+ flexShrink: 0,
1955
+ border: "none",
1956
+ background: "transparent",
1957
+ cursor: canToggleVisibleSelection ? "pointer" : "not-allowed",
1958
+ opacity: canToggleVisibleSelection ? 1 : 0.45
1959
+ },
1960
+ children: /* @__PURE__ */ jsx6(
1961
+ CheckboxIndicator,
1962
+ {
1963
+ checked: hasAllVisibleSelected,
1964
+ indeterminate: hasSomeVisibleSelected,
1965
+ disabled: !canToggleVisibleSelection,
1966
+ checkedColor: theme.colors.secondary,
1967
+ iconColor: theme.colors.surface,
1968
+ uncheckedBorderColor: withAlpha(theme.colors.onSurface, 0.22),
1969
+ uncheckedBackgroundColor: theme.colors.surface
1970
+ }
1971
+ )
1972
+ }
1973
+ ) : null;
1974
+ const moveHighlight = (direction) => {
1975
+ if (visibleEnabledOptions.length === 0) {
1976
+ return;
1977
+ }
1978
+ const currentIndex = visibleEnabledOptions.findIndex((option) => option.value === highlightedValue);
1979
+ if (currentIndex === -1) {
1980
+ setHighlightedValue(
1981
+ direction === 1 ? visibleEnabledOptions[0].value : visibleEnabledOptions[visibleEnabledOptions.length - 1].value
1982
+ );
1983
+ return;
1984
+ }
1985
+ const nextIndex = Math.max(0, Math.min(visibleEnabledOptions.length - 1, currentIndex + direction));
1986
+ setHighlightedValue(visibleEnabledOptions[nextIndex].value);
1987
+ };
1988
+ const handleTriggerClick = (event) => {
1989
+ onClick == null ? void 0 : onClick(event);
1990
+ if (event.defaultPrevented || disabled || flatOptions.length === 0) {
1991
+ return;
1992
+ }
1993
+ setIsOpen((current) => !current);
1994
+ };
1995
+ const handleTriggerFocus = (event) => {
1996
+ onFocus == null ? void 0 : onFocus(event);
1997
+ };
1998
+ const handleTriggerBlur = (event) => {
1999
+ var _a2;
2000
+ const nextTarget = event.relatedTarget;
2001
+ if (!((_a2 = shellRef.current) == null ? void 0 : _a2.contains(nextTarget))) {
2002
+ setIsOpen(false);
2003
+ }
2004
+ onBlur == null ? void 0 : onBlur(event);
2005
+ };
2006
+ const handleTriggerKeyDown = (event) => {
2007
+ var _a2, _b2, _c2;
2008
+ onKeyDown == null ? void 0 : onKeyDown(event);
2009
+ if (event.defaultPrevented || disabled || visibleEnabledOptions.length === 0) {
2010
+ return;
2011
+ }
2012
+ if (event.key === "ArrowDown") {
2013
+ event.preventDefault();
2014
+ if (!isOpen) {
2015
+ setIsOpen(true);
2016
+ setHighlightedValue((_a2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _a2 : visibleEnabledOptions[0].value);
2017
+ return;
2018
+ }
2019
+ moveHighlight(1);
2020
+ return;
2021
+ }
2022
+ if (event.key === "ArrowUp") {
2023
+ event.preventDefault();
2024
+ if (!isOpen) {
2025
+ setIsOpen(true);
2026
+ setHighlightedValue((_b2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _b2 : visibleEnabledOptions[visibleEnabledOptions.length - 1].value);
2027
+ return;
2028
+ }
2029
+ moveHighlight(-1);
2030
+ return;
2031
+ }
2032
+ if (event.key === "Home" && isOpen) {
2033
+ event.preventDefault();
2034
+ setHighlightedValue(visibleEnabledOptions[0].value);
2035
+ return;
2036
+ }
2037
+ if (event.key === "End" && isOpen) {
2038
+ event.preventDefault();
2039
+ setHighlightedValue(visibleEnabledOptions[visibleEnabledOptions.length - 1].value);
2040
+ return;
2041
+ }
2042
+ if (event.key === "Enter" || event.key === " ") {
2043
+ event.preventDefault();
2044
+ if (!isOpen) {
2045
+ setIsOpen(true);
2046
+ setHighlightedValue((_c2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _c2 : visibleEnabledOptions[0].value);
2047
+ return;
2048
+ }
2049
+ if (highlightedOption && !highlightedOption.disabled) {
2050
+ commitSelection(highlightedOption.value);
2051
+ }
2052
+ return;
2053
+ }
2054
+ if (event.key === "Escape" && isOpen) {
2055
+ event.preventDefault();
2056
+ setIsOpen(false);
2057
+ return;
2058
+ }
2059
+ if (event.key === "Tab") {
2060
+ setIsOpen(false);
2061
+ }
2062
+ };
2063
+ const handleFilterKeyDown = (event) => {
2064
+ var _a2;
2065
+ if (event.key === "ArrowDown") {
2066
+ event.preventDefault();
2067
+ moveHighlight(1);
2068
+ return;
2069
+ }
2070
+ if (event.key === "ArrowUp") {
2071
+ event.preventDefault();
2072
+ moveHighlight(-1);
2073
+ return;
2074
+ }
2075
+ if (event.key === "Enter") {
2076
+ event.preventDefault();
2077
+ if (highlightedOption && !highlightedOption.disabled) {
2078
+ commitSelection(highlightedOption.value);
2079
+ }
2080
+ return;
2081
+ }
2082
+ if (event.key === "Escape") {
2083
+ event.preventDefault();
2084
+ setIsOpen(false);
2085
+ (_a2 = buttonRef.current) == null ? void 0 : _a2.focus();
2086
+ return;
2087
+ }
2088
+ if (event.key === "Tab") {
2089
+ setIsOpen(false);
2090
+ }
2091
+ };
2092
+ const select = /* @__PURE__ */ jsxs7("div", { ref: shellRef, style: { position: "relative", width: "100%" }, children: [
2093
+ /* @__PURE__ */ jsxs7(
2094
+ "button",
2095
+ {
2096
+ ...buttonPassthroughProps,
2097
+ id: resolvedId,
2098
+ ref: buttonRef,
2099
+ type: "button",
2100
+ className,
2101
+ title: rest.title,
2102
+ disabled: disabled || flatOptions.length === 0,
2103
+ autoFocus,
2104
+ tabIndex: rest.tabIndex,
2105
+ role: "combobox",
2106
+ "aria-controls": listboxId,
2107
+ "aria-expanded": isOpen,
2108
+ "aria-haspopup": "listbox",
2109
+ "aria-invalid": error ? true : rest["aria-invalid"],
2110
+ "aria-required": rest.required,
2111
+ onClick: handleTriggerClick,
2112
+ onFocus: handleTriggerFocus,
2113
+ onBlur: handleTriggerBlur,
2114
+ onKeyDown: handleTriggerKeyDown,
2115
+ style: {
2116
+ width: "100%",
2117
+ boxSizing: "border-box",
2118
+ height: controlMetrics.height,
2119
+ borderRadius: 16,
2120
+ border: `1px solid ${error ? theme.colors.negative : withAlpha(theme.colors.textLight, 0.5)}`,
2121
+ backgroundColor: theme.colors.surface,
2122
+ color: displayLabel ? theme.colors.onSurface : mutedText,
2123
+ cursor: disabled ? "not-allowed" : "pointer",
2124
+ opacity: disabled ? 0.7 : 1,
2125
+ padding: getSelectControlPadding(controlSize),
2126
+ outline: "none",
2127
+ font: "inherit",
2128
+ fontSize: controlMetrics.fontSize,
2129
+ lineHeight: controlMetrics.lineHeight,
2130
+ transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
2131
+ display: "flex",
2132
+ alignItems: "center",
2133
+ justifyContent: "space-between",
2134
+ gap: 12,
2135
+ minWidth: 0,
2136
+ textAlign: "left",
2137
+ ...style
2138
+ },
2139
+ children: [
2140
+ /* @__PURE__ */ jsx6(
2141
+ "span",
2142
+ {
2143
+ style: {
2144
+ display: "block",
2145
+ flex: "1 1 auto",
2146
+ minWidth: 0,
2147
+ overflow: "hidden",
2148
+ textOverflow: "ellipsis",
2149
+ whiteSpace: "nowrap"
2150
+ },
2151
+ children: displayContent
2152
+ }
2153
+ ),
2154
+ /* @__PURE__ */ jsx6(
2155
+ "span",
2156
+ {
2157
+ "aria-hidden": "true",
2158
+ style: {
2159
+ position: "absolute",
2160
+ right: indicatorOffset,
2161
+ top: "50%",
2162
+ transform: `translateY(-50%) rotate(${isOpen ? 180 : 0}deg)`,
2163
+ transformOrigin: "center",
2164
+ color: disabled ? withAlpha(theme.colors.onSurface, 0.42) : mutedText,
2165
+ display: "flex",
2166
+ alignItems: "center",
2167
+ justifyContent: "center",
2168
+ pointerEvents: "none",
2169
+ transition: "transform 160ms ease, color 160ms ease"
2170
+ },
2171
+ children: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx6(
2172
+ "path",
2173
+ {
2174
+ d: "M5.75 7.75L10 12l4.25-4.25",
2175
+ stroke: "currentColor",
2176
+ strokeWidth: "1.8",
2177
+ strokeLinecap: "round",
2178
+ strokeLinejoin: "round"
2179
+ }
2180
+ ) })
2181
+ }
2182
+ )
2183
+ ]
2184
+ }
2185
+ ),
2186
+ /* @__PURE__ */ jsx6(
2187
+ "select",
2188
+ {
2189
+ ...rest,
2190
+ id: `${resolvedId}-native`,
2191
+ ref: hiddenSelectRef,
2192
+ name: rest.name,
2193
+ disabled,
2194
+ multiple: isMultiSelect,
2195
+ value: isMultiSelect ? selectedValues : selectedValue,
2196
+ onChange: handleHiddenSelectChange,
2197
+ tabIndex: -1,
2198
+ "aria-hidden": "true",
2199
+ style: {
2200
+ position: "absolute",
2201
+ width: 1,
2202
+ height: 1,
2203
+ padding: 0,
2204
+ margin: 0,
2205
+ border: 0,
2206
+ opacity: 0,
2207
+ pointerEvents: "none",
2208
+ clipPath: "inset(50%)"
2209
+ },
2210
+ children
2211
+ }
2212
+ ),
2213
+ isOpen ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
2214
+ /* @__PURE__ */ jsx6("style", { children: `
2215
+ #${listboxId} {
2216
+ scrollbar-width: thin;
2217
+ scrollbar-color: ${scrollbarThumbColor} transparent;
2218
+ }
2219
+
2220
+ #${listboxId}::-webkit-scrollbar {
2221
+ width: 8px;
2222
+ }
2223
+
2224
+ #${listboxId}::-webkit-scrollbar-track {
2225
+ background: transparent;
2226
+ }
2227
+
2228
+ #${listboxId}::-webkit-scrollbar-thumb {
2229
+ background: ${scrollbarThumbColor};
2230
+ border-radius: 999px;
2231
+ border: 2px solid transparent;
2232
+ background-clip: padding-box;
2233
+ }
2234
+
2235
+ #${listboxId}::-webkit-scrollbar-thumb:hover {
2236
+ background: ${scrollbarThumbHoverColor};
2237
+ background-clip: padding-box;
2238
+ }
2239
+ ` }),
2240
+ /* @__PURE__ */ jsxs7(
2241
+ "div",
2242
+ {
2243
+ ref: menuRef,
2244
+ style: {
2245
+ position: "absolute",
2246
+ top: menuPlacement === "bottom" ? `calc(100% + ${MENU_OFFSET}px)` : "auto",
2247
+ bottom: menuPlacement === "top" ? `calc(100% + ${MENU_OFFSET}px)` : "auto",
2248
+ left: 0,
2249
+ right: 0,
2250
+ zIndex: 30,
2251
+ maxHeight: MENU_MAX_HEIGHT,
2252
+ overflow: "hidden",
2253
+ boxSizing: "border-box",
2254
+ borderRadius: 20,
2255
+ border: `1px solid ${withAlpha(theme.colors.textLight, 0.5)}`,
2256
+ backgroundColor: surfaceRaised,
2257
+ // boxShadow: `0 10px 24px ${withAlpha(theme.colors.onSurface, 0.08)}`,
2258
+ padding: 8,
2259
+ backdropFilter: "blur(6px)",
2260
+ visibility: isPlacementResolved ? "visible" : "hidden",
2261
+ display: "flex",
2262
+ flexDirection: "column",
2263
+ gap: 4
2264
+ },
2265
+ children: [
2266
+ hasFilter ? /* @__PURE__ */ jsxs7(
2267
+ "div",
2268
+ {
2269
+ style: {
2270
+ display: "flex",
2271
+ alignItems: "center",
2272
+ justifyContent: "flex-start",
2273
+ gap: 8,
2274
+ padding: "4px 4px 10px",
2275
+ borderBottom: `1px solid ${withAlpha(theme.colors.onSurface, 0.08)}`,
2276
+ flexShrink: 0
2277
+ },
2278
+ children: [
2279
+ bulkToggleControl,
2280
+ /* @__PURE__ */ jsx6(
2281
+ "input",
2282
+ {
2283
+ ref: searchInputRef,
2284
+ type: "search",
2285
+ value: filterQuery,
2286
+ onChange: (event) => {
2287
+ setFilterQuery(event.currentTarget.value);
2288
+ },
2289
+ onKeyDown: handleFilterKeyDown,
2290
+ onMouseDown: (event) => {
2291
+ event.stopPropagation();
2292
+ },
2293
+ placeholder: "Search options",
2294
+ "aria-label": "Search options",
2295
+ style: {
2296
+ flex: "1 1 auto",
2297
+ width: "100%",
2298
+ border: `1px solid ${withAlpha(theme.colors.textLight, 0.5)}`,
2299
+ backgroundColor: theme.colors.surface,
2300
+ padding: "8px 12px",
2301
+ borderRadius: 12,
2302
+ font: "inherit",
2303
+ fontSize: 12,
2304
+ lineHeight: 1.4,
2305
+ color: theme.colors.textLight,
2306
+ outline: "none",
2307
+ boxSizing: "border-box"
2308
+ }
2309
+ }
2310
+ )
2311
+ ]
2312
+ }
2313
+ ) : isMultiSelect ? /* @__PURE__ */ jsx6(
2314
+ "div",
2315
+ {
2316
+ style: {
2317
+ display: "flex",
2318
+ alignItems: "center",
2319
+ justifyContent: "flex-start",
2320
+ padding: "4px 4px 10px",
2321
+ borderBottom: `1px solid ${withAlpha(theme.colors.onSurface, 0.08)}`,
2322
+ flexShrink: 0
2323
+ },
2324
+ children: bulkToggleControl
2325
+ }
2326
+ ) : null,
2327
+ /* @__PURE__ */ jsx6(
2328
+ "div",
2329
+ {
2330
+ id: listboxId,
2331
+ role: "listbox",
2332
+ "aria-labelledby": resolvedId,
2333
+ "aria-multiselectable": isMultiSelect || void 0,
2334
+ style: {
2335
+ overflowY: "auto",
2336
+ overflowX: "hidden",
2337
+ minHeight: 0,
2338
+ flex: "1 1 auto"
2339
+ },
2340
+ children: visibleGroups.length === 0 ? /* @__PURE__ */ jsx6(
2341
+ "div",
2342
+ {
2343
+ style: {
2344
+ padding: "14px 12px",
2345
+ color: mutedText,
2346
+ fontSize: 13,
2347
+ textAlign: "center"
2348
+ },
2349
+ children: "No options found."
2350
+ }
2351
+ ) : visibleGroups.map((group) => /* @__PURE__ */ jsxs7(
2352
+ "div",
2353
+ {
2354
+ style: {
2355
+ display: "flex",
2356
+ flexDirection: "column",
2357
+ gap: 4,
2358
+ marginTop: group.label ? 4 : 0
2359
+ },
2360
+ children: [
2361
+ group.label ? /* @__PURE__ */ jsx6(
2362
+ "span",
2363
+ {
2364
+ style: {
2365
+ padding: "6px 8px 2px",
2366
+ color: mutedText,
2367
+ fontSize: 11,
2368
+ fontWeight: 800,
2369
+ letterSpacing: "0.08em",
2370
+ textTransform: "uppercase"
2371
+ },
2372
+ children: group.label
2373
+ }
2374
+ ) : null,
2375
+ group.options.map((option) => {
2376
+ const isSelected = selectedValues.includes(option.value);
2377
+ const isHighlighted = option.value === highlightedValue;
2378
+ const isActive = isSelected || isHighlighted;
2379
+ return /* @__PURE__ */ jsxs7(
2380
+ "div",
2381
+ {
2382
+ role: "option",
2383
+ "aria-selected": isSelected,
2384
+ "aria-disabled": option.disabled,
2385
+ onMouseEnter: () => {
2386
+ if (!option.disabled) {
2387
+ setHighlightedValue(option.value);
2388
+ }
2389
+ },
2390
+ onMouseDown: (event) => {
2391
+ event.preventDefault();
2392
+ },
2393
+ onClick: () => {
2394
+ if (!option.disabled) {
2395
+ commitSelection(option.value);
2396
+ }
2397
+ },
2398
+ style: {
2399
+ display: "flex",
2400
+ alignItems: "center",
2401
+ justifyContent: "space-between",
2402
+ gap: 12,
2403
+ width: "100%",
2404
+ minWidth: 0,
2405
+ boxSizing: "border-box",
2406
+ borderRadius: 14,
2407
+ padding: getOptionPadding(controlSize),
2408
+ cursor: option.disabled ? "not-allowed" : "pointer",
2409
+ color: option.disabled ? withAlpha(theme.colors.onSurface, 0.38) : isActive ? theme.colors.onPrimary : theme.colors.onSurface,
2410
+ backgroundColor: isSelected ? optionSelectedBackground : isHighlighted ? optionHoverBackground : "transparent",
2411
+ fontWeight: isSelected ? 700 : 500,
2412
+ transition: "background-color 140ms ease, color 140ms ease"
2413
+ },
2414
+ children: [
2415
+ /* @__PURE__ */ jsxs7(
2416
+ "span",
2417
+ {
2418
+ style: {
2419
+ display: "flex",
2420
+ alignItems: "center",
2421
+ gap: 10,
2422
+ flex: "1 1 auto",
2423
+ minWidth: 0
2424
+ },
2425
+ children: [
2426
+ isMultiSelect ? /* @__PURE__ */ jsx6(
2427
+ CheckboxIndicator,
2428
+ {
2429
+ checked: isSelected,
2430
+ disabled: option.disabled,
2431
+ size: 16,
2432
+ checkedColor: isActive ? theme.colors.onPrimary : theme.colors.primary,
2433
+ iconColor: isActive ? theme.colors.primary : theme.colors.onPrimary,
2434
+ uncheckedBorderColor: isActive ? theme.colors.onPrimary : withAlpha(theme.colors.onSurface, 0.26),
2435
+ uncheckedBackgroundColor: "transparent"
2436
+ }
2437
+ ) : null,
2438
+ /* @__PURE__ */ jsx6(
2439
+ "span",
2440
+ {
2441
+ style: {
2442
+ display: "block",
2443
+ flex: "1 1 auto",
2444
+ minWidth: 0,
2445
+ overflow: "hidden",
2446
+ textOverflow: "ellipsis",
2447
+ whiteSpace: "nowrap"
2448
+ },
2449
+ children: option.label
2450
+ }
2451
+ )
2452
+ ]
2453
+ }
2454
+ ),
2455
+ !isMultiSelect && isSelected ? /* @__PURE__ */ jsx6(
2456
+ "span",
2457
+ {
2458
+ "aria-hidden": "true",
2459
+ style: {
2460
+ display: "flex",
2461
+ alignItems: "center",
2462
+ justifyContent: "center",
2463
+ color: theme.colors.primary,
2464
+ flexShrink: 0
2465
+ },
2466
+ children: /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx6(
2467
+ "path",
2468
+ {
2469
+ d: "M5.75 10.25L8.5 13l5.75-6",
2470
+ stroke: "currentColor",
2471
+ strokeWidth: "1.8",
2472
+ strokeLinecap: "round",
2473
+ strokeLinejoin: "round"
2474
+ }
2475
+ ) })
2476
+ }
2477
+ ) : null
2478
+ ]
2479
+ },
2480
+ option.key
2481
+ );
2482
+ })
2483
+ ]
2484
+ },
2485
+ group.key
2486
+ ))
2487
+ }
2488
+ )
2489
+ ]
2490
+ }
2491
+ )
2492
+ ] }) : null
2493
+ ] });
2494
+ return /* @__PURE__ */ jsx6(
2495
+ ControlFrame,
2496
+ {
2497
+ label,
2498
+ hint,
2499
+ error,
2500
+ htmlFor: resolvedId,
2501
+ required: rest.required,
2502
+ useLabelWrapper: false,
2503
+ children: select
2504
+ }
2505
+ );
552
2506
  }
553
2507
 
554
2508
  // src/components/Textarea.tsx
555
- import { withAlpha as withAlpha6 } from "smartsell-sales-sdk";
556
- import { jsx as jsx6 } from "react/jsx-runtime";
557
- var getPadding3 = (size) => {
2509
+ import { jsx as jsx7 } from "react/jsx-runtime";
2510
+ var getPadding = (size) => {
558
2511
  if (size === "sm") return "10px 12px";
559
2512
  if (size === "lg") return "16px 18px";
560
2513
  return "13px 16px";
561
2514
  };
562
2515
  function Textarea({
563
- sdk,
564
2516
  label,
565
2517
  hint,
566
2518
  error,
@@ -571,31 +2523,32 @@ function Textarea({
571
2523
  rows = 5,
572
2524
  ...rest
573
2525
  }) {
574
- const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
2526
+ const { theme, focusRing } = useUiKitTheme();
575
2527
  const textareaStyle = {
2528
+ border: `1px solid ${error ? theme.colors.negative : withAlpha(theme.colors.textLight, 0.5)}`,
2529
+ backgroundColor: theme.colors.surface,
2530
+ color: theme.colors.text,
2531
+ cursor: disabled ? "not-allowed" : "text",
2532
+ opacity: disabled ? 0.7 : 1,
576
2533
  width: "100%",
2534
+ boxSizing: "border-box",
577
2535
  borderRadius: 16,
578
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
579
- padding: getPadding3(controlSize),
2536
+ padding: getPadding(controlSize),
580
2537
  outline: "none",
581
- color: theme.colors.onSurface,
582
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
583
- boxShadow: error ? `0 0 0 3px ${withAlpha6(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
2538
+ boxShadow: error ? `0 0 0 3px ${withAlpha(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
584
2539
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
585
2540
  resize: "vertical",
586
2541
  ...style
587
2542
  };
588
- const textarea = /* @__PURE__ */ jsx6("textarea", { ...rest, id, disabled, rows, style: textareaStyle });
589
- if (!label && !hint && !error) {
590
- return textarea;
591
- }
592
- return /* @__PURE__ */ jsx6(Field, { sdk, label, hint, error, htmlFor: id, children: textarea });
2543
+ const textarea = /* @__PURE__ */ jsx7("textarea", { ...rest, id, disabled, rows, style: textareaStyle });
2544
+ return /* @__PURE__ */ jsx7(ControlFrame, { label, hint, error, htmlFor: id, required: rest.required, children: textarea });
593
2545
  }
594
2546
  export {
595
2547
  Button,
596
2548
  Card,
2549
+ Checkbox,
597
2550
  ComposableModal,
598
- Field,
2551
+ DatePicker,
599
2552
  Input,
600
2553
  Modal,
601
2554
  Select,