smartsell-ui-kit 0.0.5 → 0.0.6

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,11 +1,68 @@
1
1
  import {
2
2
  Modal
3
- } from "./chunk-BI3QBKFG.js";
3
+ } from "./chunk-UIFTQDYM.js";
4
4
 
5
5
  // src/components/Button.tsx
6
6
  import { useState } from "react";
7
7
  import { withAlpha as withAlpha2 } from "smartsell-sales-sdk";
8
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
+ };
65
+
9
66
  // src/theme.ts
10
67
  import { useMemo } from "react";
11
68
  import {
@@ -30,19 +87,9 @@ function useUiKitTheme(sdk) {
30
87
 
31
88
  // src/components/Button.tsx
32
89
  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
90
  function Button({
43
91
  sdk,
44
92
  variant = "solid",
45
- tone = "primary",
46
93
  size = "md",
47
94
  fullWidth = false,
48
95
  leadingIcon,
@@ -66,57 +113,35 @@ function Button({
66
113
  const { theme } = useUiKitTheme(sdk);
67
114
  const [isHovered, setIsHovered] = useState(false);
68
115
  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
116
  const variantStyles = {
92
117
  solid: {
93
- backgroundColor: palette.background,
94
- borderColor: palette.border,
95
- color: palette.foreground
118
+ backgroundColor: theme.colors.primary,
119
+ borderColor: withAlpha2(theme.colors.primary, 0.5),
120
+ color: theme.colors.onPrimary
96
121
  },
97
122
  outline: {
98
123
  backgroundColor: "transparent",
99
- borderColor: palette.border,
100
- color: tone === "neutral" ? palette.foreground : palette.background
124
+ borderColor: withAlpha2(theme.colors.text, 0.5),
125
+ color: theme.colors.onSurface
101
126
  },
102
127
  ghost: {
103
128
  backgroundColor: "transparent",
104
129
  borderColor: "transparent",
105
- color: tone === "neutral" ? palette.foreground : palette.background
130
+ color: theme.colors.onSurface
106
131
  }
107
132
  };
108
- const interactiveColor = tone === "neutral" ? palette.foreground : palette.background;
133
+ const interactiveColor = variant === "solid" ? theme.colors.primary : theme.colors.onSurface;
109
134
  const isInteractive = !disabled;
110
135
  const interactionStyles = {
111
136
  solid: {
112
137
  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
138
+ boxShadow: "none",
139
+ filter: void 0
115
140
  },
116
141
  outline: {
117
142
  transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
118
143
  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"
144
+ boxShadow: "none"
120
145
  },
121
146
  ghost: {
122
147
  transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
@@ -192,10 +217,9 @@ function Button({
192
217
  cursor: disabled ? "not-allowed" : "pointer",
193
218
  fontWeight: 700,
194
219
  letterSpacing: "0.01em",
195
- lineHeight: 1,
196
220
  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),
221
+ transition: "transform 140ms ease, opacity 160ms ease, background-color 160ms ease",
222
+ ...getButtonSizeStyles(size),
199
223
  ...variantStyles[variant],
200
224
  ...isInteractive ? interactionStyles[variant] : void 0,
201
225
  ...style
@@ -212,43 +236,26 @@ function Button({
212
236
  // src/components/Card.tsx
213
237
  import { withAlpha as withAlpha3 } from "smartsell-sales-sdk";
214
238
  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
239
  function Card({
230
240
  sdk,
231
241
  children,
232
242
  eyebrow,
233
243
  title,
234
244
  action,
235
- tone = "default",
236
245
  padded = true,
237
246
  style,
238
247
  ...rest
239
248
  }) {
240
- const { theme, mutedText } = useUiKitTheme(sdk);
241
- const toneStyles = getToneStyles(tone, sdk);
249
+ const { theme } = useUiKitTheme(sdk);
242
250
  return /* @__PURE__ */ jsxs2(
243
251
  "div",
244
252
  {
245
253
  ...rest,
246
254
  style: {
247
255
  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)}`,
256
+ border: `1px solid ${withAlpha3(theme.colors.text, 0.5)}`,
257
+ background: `${withAlpha3(theme.colors.background, 0.7)}`,
258
+ color: theme.colors.text,
252
259
  padding: padded ? 24 : void 0,
253
260
  ...style
254
261
  },
@@ -269,7 +276,7 @@ function Card({
269
276
  "span",
270
277
  {
271
278
  style: {
272
- color: toneStyles.accent,
279
+ color: theme.colors.textLight,
273
280
  fontSize: 12,
274
281
  fontWeight: 800,
275
282
  letterSpacing: "0.16em",
@@ -282,7 +289,7 @@ function Card({
282
289
  "div",
283
290
  {
284
291
  style: {
285
- color: theme.colors.onSurface,
292
+ color: theme.colors.text,
286
293
  fontSize: 24,
287
294
  fontWeight: 700,
288
295
  lineHeight: 1.2
@@ -291,7 +298,7 @@ function Card({
291
298
  }
292
299
  ) : null
293
300
  ] }),
294
- action ? /* @__PURE__ */ jsx("div", { style: { color: mutedText }, children: action }) : null
301
+ action ? /* @__PURE__ */ jsx("div", { style: { color: theme.colors.text }, children: action }) : null
295
302
  ]
296
303
  }
297
304
  ),
@@ -349,7 +356,7 @@ function ComposableModal({
349
356
  [cancelLabel, confirmLabel, description, isBusy, isOpen, onClose, onConfirm, payload, title]
350
357
  );
351
358
  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 }),
359
+ /* @__PURE__ */ jsx2(Button, { sdk, variant: "outline", onClick: onClose, disabled: isBusy, children: cancelLabel }),
353
360
  onConfirm ? /* @__PURE__ */ jsx2(Button, { sdk, onClick: onConfirm, disabled: isBusy, children: confirmLabel }) : null
354
361
  ] });
355
362
  const defaultHeader = headerFallback != null ? headerFallback : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
@@ -401,124 +408,412 @@ function ComposableModal({
401
408
  );
402
409
  }
403
410
 
404
- // src/components/Field.tsx
405
- import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
406
- function Field({
411
+ // src/components/Input.tsx
412
+ import { useState as useState2 } from "react";
413
+ import { withAlpha as withAlpha4 } from "smartsell-sales-sdk";
414
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
415
+ var PASSWORD_TOGGLE_METRICS_BY_SIZE = {
416
+ sm: {
417
+ buttonSize: 18,
418
+ leftOffset: 8,
419
+ contentGap: 8,
420
+ iconSize: 14
421
+ },
422
+ md: {
423
+ buttonSize: 20,
424
+ leftOffset: 10,
425
+ contentGap: 10,
426
+ iconSize: 16
427
+ },
428
+ lg: {
429
+ buttonSize: 22,
430
+ leftOffset: 12,
431
+ contentGap: 12,
432
+ iconSize: 18
433
+ }
434
+ };
435
+ var getPasswordToggleMetrics = (size) => PASSWORD_TOGGLE_METRICS_BY_SIZE[size];
436
+ function ControlFrame({
407
437
  sdk,
408
438
  label,
409
439
  hint,
410
440
  error,
411
441
  htmlFor,
412
442
  required = false,
443
+ useLabelWrapper = true,
413
444
  children,
414
445
  style
415
446
  }) {
447
+ if (!label && !hint && !error) {
448
+ return /* @__PURE__ */ jsx3(Fragment2, { children });
449
+ }
416
450
  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(
451
+ const labelStyle = {
452
+ color: theme.colors.onSurface,
453
+ fontSize: 14,
454
+ fontWeight: 700,
455
+ letterSpacing: "0.01em"
456
+ };
457
+ const wrapperStyle = {
458
+ display: "flex",
459
+ flexDirection: "column",
460
+ gap: 8,
461
+ width: "100%",
462
+ ...style
463
+ };
464
+ if (!useLabelWrapper) {
465
+ return /* @__PURE__ */ jsxs4("div", { style: wrapperStyle, children: [
466
+ label || hint ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
467
+ label ? /* @__PURE__ */ jsxs4("label", { htmlFor, style: labelStyle, children: [
468
+ label,
469
+ required ? /* @__PURE__ */ jsx3("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
470
+ ] }) : null,
471
+ hint ? /* @__PURE__ */ jsx3(
459
472
  "span",
460
473
  {
461
474
  style: {
462
- color: theme.colors.negative,
475
+ color: mutedText,
463
476
  fontSize: 13,
464
- fontWeight: 600,
465
477
  lineHeight: 1.45
466
478
  },
467
- children: error
479
+ children: hint
468
480
  }
469
481
  ) : null
470
- ]
471
- }
472
- );
482
+ ] }) : null,
483
+ children,
484
+ error ? /* @__PURE__ */ jsx3(
485
+ "span",
486
+ {
487
+ style: {
488
+ color: theme.colors.negative,
489
+ fontSize: 13,
490
+ fontWeight: 600,
491
+ lineHeight: 1.45
492
+ },
493
+ children: error
494
+ }
495
+ ) : null
496
+ ] });
497
+ }
498
+ return /* @__PURE__ */ jsxs4("label", { htmlFor, style: wrapperStyle, children: [
499
+ label || hint ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
500
+ label ? /* @__PURE__ */ jsxs4("span", { style: labelStyle, children: [
501
+ label,
502
+ required ? /* @__PURE__ */ jsx3("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
503
+ ] }) : null,
504
+ hint ? /* @__PURE__ */ jsx3(
505
+ "span",
506
+ {
507
+ style: {
508
+ color: mutedText,
509
+ fontSize: 13,
510
+ lineHeight: 1.45
511
+ },
512
+ children: hint
513
+ }
514
+ ) : null
515
+ ] }) : null,
516
+ children,
517
+ error ? /* @__PURE__ */ jsx3(
518
+ "span",
519
+ {
520
+ style: {
521
+ color: theme.colors.negative,
522
+ fontSize: 13,
523
+ fontWeight: 600,
524
+ lineHeight: 1.45
525
+ },
526
+ children: error
527
+ }
528
+ ) : null
529
+ ] });
473
530
  }
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
531
  function Input({
484
532
  sdk,
485
533
  label,
486
534
  hint,
487
535
  error,
488
536
  controlSize = "md",
537
+ showPasswordToggle = false,
489
538
  id,
490
539
  disabled,
491
540
  style,
541
+ type = "text",
492
542
  ...rest
493
543
  }) {
494
544
  const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
545
+ const controlMetrics = getControlMetrics(controlSize);
546
+ const passwordToggleMetrics = getPasswordToggleMetrics(controlSize);
547
+ const [isPasswordVisible, setIsPasswordVisible] = useState2(false);
548
+ const isPasswordField = type === "password";
549
+ const canTogglePasswordVisibility = isPasswordField && showPasswordToggle;
550
+ const passwordToggleInset = Math.max(
551
+ 0,
552
+ passwordToggleMetrics.leftOffset + passwordToggleMetrics.buttonSize + passwordToggleMetrics.contentGap - controlMetrics.paddingX
553
+ );
554
+ const leadingInset = Number(canTogglePasswordVisibility) * passwordToggleInset;
555
+ const resolvedType = canTogglePasswordVisibility ? isPasswordVisible ? "text" : "password" : type;
495
556
  const inputStyle = {
496
557
  width: "100%",
558
+ boxSizing: "border-box",
559
+ height: controlMetrics.height,
497
560
  borderRadius: 16,
498
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
499
- padding: getPadding(controlSize),
561
+ border: `1px solid ${error ? theme.colors.negative : withAlpha4(theme.colors.textLight, 0.5)}`,
562
+ backgroundColor: theme.colors.surface,
563
+ color: theme.colors.text,
564
+ cursor: disabled ? "not-allowed" : "text",
565
+ opacity: disabled ? 0.7 : 1,
566
+ padding: getTextInputPadding(controlSize, { leadingInset }),
500
567
  outline: "none",
501
- color: theme.colors.onSurface,
502
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
568
+ fontSize: controlMetrics.fontSize,
569
+ lineHeight: controlMetrics.lineHeight,
503
570
  boxShadow: error ? `0 0 0 3px ${withAlpha4(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
504
571
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
505
572
  ...style
506
573
  };
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 });
574
+ const input = canTogglePasswordVisibility ? /* @__PURE__ */ jsxs4("div", { style: { position: "relative", width: "100%" }, children: [
575
+ /* @__PURE__ */ jsx3(
576
+ "button",
577
+ {
578
+ type: "button",
579
+ "aria-label": isPasswordVisible ? "Hide password" : "Show password",
580
+ "aria-pressed": isPasswordVisible,
581
+ disabled,
582
+ onMouseDown: (event) => {
583
+ event.preventDefault();
584
+ },
585
+ onClick: () => {
586
+ setIsPasswordVisible((currentValue) => !currentValue);
587
+ },
588
+ style: {
589
+ position: "absolute",
590
+ top: "50%",
591
+ left: passwordToggleMetrics.leftOffset,
592
+ transform: "translateY(-50%)",
593
+ zIndex: 1,
594
+ width: passwordToggleMetrics.buttonSize,
595
+ height: passwordToggleMetrics.buttonSize,
596
+ display: "inline-flex",
597
+ alignItems: "center",
598
+ justifyContent: "center",
599
+ border: "none",
600
+ padding: 0,
601
+ margin: 0,
602
+ borderRadius: 999,
603
+ backgroundColor: "transparent",
604
+ color: isPasswordVisible ? theme.colors.primary : theme.colors.textLight,
605
+ cursor: disabled ? "not-allowed" : "pointer",
606
+ opacity: disabled ? 0.5 : 1
607
+ },
608
+ children: isPasswordVisible ? /* @__PURE__ */ jsxs4(
609
+ "svg",
610
+ {
611
+ width: passwordToggleMetrics.iconSize,
612
+ height: passwordToggleMetrics.iconSize,
613
+ viewBox: "0 0 20 20",
614
+ fill: "none",
615
+ "aria-hidden": "true",
616
+ children: [
617
+ /* @__PURE__ */ jsx3(
618
+ "path",
619
+ {
620
+ d: "M2.5 3.5L17.5 16.5",
621
+ stroke: "currentColor",
622
+ strokeWidth: "1.8",
623
+ strokeLinecap: "round"
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsx3(
627
+ "path",
628
+ {
629
+ d: "M8.825 8.786a2 2 0 0 0 2.389 2.389",
630
+ stroke: "currentColor",
631
+ strokeWidth: "1.8",
632
+ strokeLinecap: "round",
633
+ strokeLinejoin: "round"
634
+ }
635
+ ),
636
+ /* @__PURE__ */ jsx3(
637
+ "path",
638
+ {
639
+ 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",
640
+ stroke: "currentColor",
641
+ strokeWidth: "1.8",
642
+ strokeLinecap: "round",
643
+ strokeLinejoin: "round"
644
+ }
645
+ ),
646
+ /* @__PURE__ */ jsx3(
647
+ "path",
648
+ {
649
+ 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",
650
+ stroke: "currentColor",
651
+ strokeWidth: "1.8",
652
+ strokeLinecap: "round",
653
+ strokeLinejoin: "round"
654
+ }
655
+ )
656
+ ]
657
+ }
658
+ ) : /* @__PURE__ */ jsxs4(
659
+ "svg",
660
+ {
661
+ width: passwordToggleMetrics.iconSize,
662
+ height: passwordToggleMetrics.iconSize,
663
+ viewBox: "0 0 20 20",
664
+ fill: "none",
665
+ "aria-hidden": "true",
666
+ children: [
667
+ /* @__PURE__ */ jsx3(
668
+ "path",
669
+ {
670
+ 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",
671
+ stroke: "currentColor",
672
+ strokeWidth: "1.8",
673
+ strokeLinecap: "round",
674
+ strokeLinejoin: "round"
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsx3("circle", { cx: "10", cy: "10", r: "2.25", stroke: "currentColor", strokeWidth: "1.8" })
678
+ ]
679
+ }
680
+ )
681
+ }
682
+ ),
683
+ /* @__PURE__ */ jsx3("input", { ...rest, id, type: resolvedType, disabled, style: inputStyle })
684
+ ] }) : /* @__PURE__ */ jsx3("input", { ...rest, id, type: resolvedType, disabled, style: inputStyle });
685
+ return /* @__PURE__ */ jsx3(ControlFrame, { sdk, label, hint, error, htmlFor: id, required: rest.required, children: input });
512
686
  }
513
687
 
514
688
  // src/components/Select.tsx
689
+ import {
690
+ Children,
691
+ isValidElement,
692
+ useEffect,
693
+ useId,
694
+ useRef,
695
+ useState as useState3
696
+ } from "react";
515
697
  import { withAlpha as withAlpha5 } from "smartsell-sales-sdk";
516
- import { jsx as jsx5 } from "react/jsx-runtime";
517
- var getPadding2 = (size) => {
698
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
699
+ var MENU_OFFSET = 8;
700
+ var MENU_MAX_HEIGHT = 280;
701
+ var getOptionPadding = (size) => {
518
702
  if (size === "sm") return "10px 12px";
519
- if (size === "lg") return "16px 18px";
520
- return "13px 16px";
703
+ if (size === "lg") return "14px 16px";
704
+ return "12px 14px";
521
705
  };
706
+ function getTextContent(node) {
707
+ if (node == null || typeof node === "boolean") {
708
+ return "";
709
+ }
710
+ if (typeof node === "string" || typeof node === "number") {
711
+ return String(node);
712
+ }
713
+ if (Array.isArray(node)) {
714
+ return node.map(getTextContent).join("");
715
+ }
716
+ if (isValidElement(node)) {
717
+ return getTextContent(node.props.children);
718
+ }
719
+ return "";
720
+ }
721
+ function normalizeSelectValue(value) {
722
+ if (Array.isArray(value)) {
723
+ return value.length > 0 ? normalizeSelectValue(value[0]) : "";
724
+ }
725
+ return value == null ? "" : String(value);
726
+ }
727
+ function normalizeSelectValues(value) {
728
+ if (Array.isArray(value)) {
729
+ return value.map((item) => normalizeSelectValue(item));
730
+ }
731
+ if (value == null) {
732
+ return [];
733
+ }
734
+ return [normalizeSelectValue(value)];
735
+ }
736
+ function buildOption(keyPrefix, index, props, groupLabel, groupDisabled = false) {
737
+ var _a;
738
+ const rawValue = props.value !== void 0 ? props.value : getTextContent(props.children).trim();
739
+ const normalizedValue = normalizeSelectValue(rawValue);
740
+ return {
741
+ key: `${keyPrefix}-${index}-${normalizedValue}`,
742
+ value: normalizedValue,
743
+ label: (_a = props.children) != null ? _a : normalizedValue,
744
+ disabled: groupDisabled || Boolean(props.disabled)
745
+ };
746
+ }
747
+ function parseSelectChildren(children) {
748
+ const groups = [];
749
+ const standaloneOptions = [];
750
+ let optionIndex = 0;
751
+ let groupIndex = 0;
752
+ const flushStandaloneOptions = () => {
753
+ if (standaloneOptions.length === 0) {
754
+ return;
755
+ }
756
+ groups.push({
757
+ key: `standalone-${groups.length}`,
758
+ options: [...standaloneOptions]
759
+ });
760
+ standaloneOptions.length = 0;
761
+ };
762
+ Children.forEach(children, (child) => {
763
+ if (!isValidElement(child)) {
764
+ return;
765
+ }
766
+ if (child.type === "option") {
767
+ standaloneOptions.push(buildOption("option", optionIndex, child.props));
768
+ optionIndex += 1;
769
+ return;
770
+ }
771
+ if (child.type !== "optgroup") {
772
+ return;
773
+ }
774
+ flushStandaloneOptions();
775
+ const groupOptions = [];
776
+ let groupOptionIndex = 0;
777
+ Children.forEach(child.props.children, (optionChild) => {
778
+ if (!isValidElement(optionChild) || optionChild.type !== "option") {
779
+ return;
780
+ }
781
+ groupOptions.push(
782
+ buildOption(
783
+ `group-${groupIndex}-option`,
784
+ groupOptionIndex,
785
+ optionChild.props,
786
+ child.props.label,
787
+ Boolean(child.props.disabled)
788
+ )
789
+ );
790
+ groupOptionIndex += 1;
791
+ });
792
+ if (groupOptions.length > 0) {
793
+ groups.push({
794
+ key: `group-${groupIndex}`,
795
+ label: child.props.label,
796
+ options: groupOptions
797
+ });
798
+ groupIndex += 1;
799
+ }
800
+ });
801
+ flushStandaloneOptions();
802
+ return groups;
803
+ }
804
+ function filterSelectGroups(groups, query) {
805
+ const normalizedQuery = query.trim().toLowerCase();
806
+ if (!normalizedQuery) {
807
+ return groups;
808
+ }
809
+ return groups.map((group) => ({
810
+ ...group,
811
+ options: group.options.filter((option) => {
812
+ const optionText = getTextContent(option.label).trim().toLowerCase();
813
+ return optionText.includes(normalizedQuery);
814
+ })
815
+ })).filter((group) => group.options.length > 0);
816
+ }
522
817
  function Select({
523
818
  sdk,
524
819
  label,
@@ -529,32 +824,845 @@ function Select({
529
824
  disabled,
530
825
  style,
531
826
  children,
827
+ value,
828
+ defaultValue,
829
+ onChange,
830
+ onFocus,
831
+ onBlur,
832
+ onKeyDown,
833
+ onClick,
834
+ autoFocus,
835
+ className,
836
+ isMulti,
837
+ isFilterable,
838
+ multiple,
532
839
  ...rest
533
840
  }) {
534
- const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
535
- const selectStyle = {
536
- width: "100%",
537
- borderRadius: 16,
538
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
539
- padding: getPadding2(controlSize),
540
- 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}`,
544
- transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
545
- ...style
841
+ var _a, _b, _c, _d, _e, _f, _g, _h;
842
+ const { theme, borderSoft, mutedText, surfaceMuted, surfaceRaised } = useUiKitTheme(sdk);
843
+ const generatedId = useId().replace(/:/g, "");
844
+ const resolvedId = id != null ? id : `ss-select-${generatedId}`;
845
+ const isMultiSelect = Boolean(isMulti || multiple);
846
+ const hasFilter = Boolean(isFilterable);
847
+ const groups = parseSelectChildren(children);
848
+ const flatOptions = groups.flatMap((group) => group.options);
849
+ const enabledOptions = flatOptions.filter((option) => !option.disabled);
850
+ const isControlled = value !== void 0;
851
+ const fallbackValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue) : isMultiSelect ? [] : ((_a = enabledOptions[0]) == null ? void 0 : _a.value) ? [enabledOptions[0].value] : [];
852
+ const [uncontrolledValues, setUncontrolledValues] = useState3(fallbackValues);
853
+ const [isOpen, setIsOpen] = useState3(false);
854
+ const [filterQuery, setFilterQuery] = useState3("");
855
+ const [menuPlacement, setMenuPlacement] = useState3("bottom");
856
+ const [isPlacementResolved, setIsPlacementResolved] = useState3(false);
857
+ const [highlightedValue, setHighlightedValue] = useState3((_d = (_c = fallbackValues[0]) != null ? _c : (_b = enabledOptions[0]) == null ? void 0 : _b.value) != null ? _d : "");
858
+ const shellRef = useRef(null);
859
+ const buttonRef = useRef(null);
860
+ const menuRef = useRef(null);
861
+ const searchInputRef = useRef(null);
862
+ const hiddenSelectRef = useRef(null);
863
+ const selectedValues = isControlled ? normalizeSelectValues(value) : uncontrolledValues;
864
+ const selectedValue = (_e = selectedValues[0]) != null ? _e : "";
865
+ const selectedOptions = flatOptions.filter((option) => selectedValues.includes(option.value));
866
+ const selectedOption = (_f = selectedOptions[0]) != null ? _f : null;
867
+ 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;
868
+ const displayContent = displayLabel != null ? displayLabel : "\xA0";
869
+ const visibleGroups = hasFilter ? filterSelectGroups(groups, filterQuery) : groups;
870
+ const visibleOptions = visibleGroups.flatMap((group) => group.options);
871
+ const visibleEnabledOptions = visibleOptions.filter((option) => !option.disabled);
872
+ const visibleEnabledValues = visibleEnabledOptions.map((option) => option.value);
873
+ const selectedValuesKey = selectedValues.join("\0");
874
+ const visibleEnabledValuesKey = visibleEnabledValues.join("\0");
875
+ const indicatorOffset = getSelectIndicatorOffset(controlSize);
876
+ const controlMetrics = getControlMetrics(controlSize);
877
+ const listboxId = `${resolvedId}-listbox`;
878
+ const scrollbarThumbColor = withAlpha5(theme.colors.onSurface, 0.22);
879
+ const scrollbarThumbHoverColor = withAlpha5(theme.colors.onSurface, 0.32);
880
+ const highlightedOption = (_h = visibleOptions.find((option) => option.value === highlightedValue)) != null ? _h : null;
881
+ const optionHoverBackground = withAlpha5(theme.colors.primary, 1);
882
+ const optionSelectedBackground = withAlpha5(theme.colors.primary, 1);
883
+ const triggerShadow = error ? `0 0 0 3px ${withAlpha5(theme.colors.negative, 0.12)}` : "none";
884
+ const buttonPassthroughProps = Object.fromEntries(
885
+ Object.entries(rest).filter(([key]) => key.startsWith("aria-") || key.startsWith("data-"))
886
+ );
887
+ useEffect(() => {
888
+ var _a2, _b2, _c2, _d2;
889
+ if (isControlled) {
890
+ return;
891
+ }
892
+ const invalidValues = uncontrolledValues.filter(
893
+ (currentValue) => !flatOptions.some((option) => option.value === currentValue)
894
+ );
895
+ if (invalidValues.length === 0) {
896
+ return;
897
+ }
898
+ const nextValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue).filter(
899
+ (currentValue) => flatOptions.some((option) => option.value === currentValue)
900
+ ) : isMultiSelect ? [] : ((_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) ? [enabledOptions[0].value] : [];
901
+ setUncontrolledValues(nextValues);
902
+ setHighlightedValue((_d2 = (_c2 = nextValues[0]) != null ? _c2 : (_b2 = enabledOptions[0]) == null ? void 0 : _b2.value) != null ? _d2 : "");
903
+ }, [children, defaultValue, enabledOptions, flatOptions, isControlled, isMultiSelect, uncontrolledValues]);
904
+ useEffect(() => {
905
+ var _a2, _b2, _c2;
906
+ if (!isOpen) {
907
+ setMenuPlacement("bottom");
908
+ setIsPlacementResolved(false);
909
+ return;
910
+ }
911
+ const nextHighlightedValue = (_c2 = (_b2 = [...selectedValues].reverse().find((currentValue) => visibleEnabledValues.includes(currentValue))) != null ? _b2 : (_a2 = visibleEnabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "";
912
+ setHighlightedValue(nextHighlightedValue);
913
+ }, [isOpen, selectedValuesKey, visibleEnabledValuesKey]);
914
+ useEffect(() => {
915
+ var _a2;
916
+ if (!isOpen || !hasFilter) {
917
+ return;
918
+ }
919
+ (_a2 = searchInputRef.current) == null ? void 0 : _a2.focus();
920
+ }, [hasFilter, isOpen]);
921
+ useEffect(() => {
922
+ if (!isOpen) {
923
+ return;
924
+ }
925
+ const frameId = window.requestAnimationFrame(() => {
926
+ var _a2;
927
+ const triggerRect = (_a2 = buttonRef.current) == null ? void 0 : _a2.getBoundingClientRect();
928
+ const menuElement = menuRef.current;
929
+ if (!triggerRect || !menuElement) {
930
+ setMenuPlacement("bottom");
931
+ setIsPlacementResolved(true);
932
+ return;
933
+ }
934
+ const measuredMenuHeight = menuElement.getBoundingClientRect().height;
935
+ const menuHeight = Math.min(measuredMenuHeight || menuElement.scrollHeight, MENU_MAX_HEIGHT);
936
+ const availableBelow = window.innerHeight - triggerRect.bottom - MENU_OFFSET;
937
+ const availableAbove = triggerRect.top - MENU_OFFSET;
938
+ const nextPlacement = availableBelow < menuHeight && availableAbove >= menuHeight ? "top" : "bottom";
939
+ setMenuPlacement(nextPlacement);
940
+ setIsPlacementResolved(true);
941
+ });
942
+ return () => {
943
+ window.cancelAnimationFrame(frameId);
944
+ };
945
+ }, [isOpen, visibleOptions.length]);
946
+ useEffect(() => {
947
+ if (isOpen) {
948
+ return;
949
+ }
950
+ setFilterQuery("");
951
+ }, [isOpen]);
952
+ useEffect(() => {
953
+ if (!isOpen) {
954
+ return;
955
+ }
956
+ const handlePointerDown = (event) => {
957
+ var _a2;
958
+ if ((_a2 = shellRef.current) == null ? void 0 : _a2.contains(event.target)) {
959
+ return;
960
+ }
961
+ setIsOpen(false);
962
+ };
963
+ document.addEventListener("pointerdown", handlePointerDown);
964
+ return () => {
965
+ document.removeEventListener("pointerdown", handlePointerDown);
966
+ };
967
+ }, [isOpen]);
968
+ useEffect(() => {
969
+ var _a2;
970
+ if (isControlled) {
971
+ return;
972
+ }
973
+ const form = (_a2 = hiddenSelectRef.current) == null ? void 0 : _a2.form;
974
+ if (!form) {
975
+ return;
976
+ }
977
+ const handleReset = () => {
978
+ queueMicrotask(() => {
979
+ var _a3, _b2, _c2, _d2;
980
+ const nextValues = defaultValue !== void 0 ? normalizeSelectValues(defaultValue) : isMultiSelect ? [] : ((_a3 = enabledOptions[0]) == null ? void 0 : _a3.value) ? [enabledOptions[0].value] : [];
981
+ setUncontrolledValues(nextValues);
982
+ setHighlightedValue((_d2 = (_c2 = nextValues[0]) != null ? _c2 : (_b2 = enabledOptions[0]) == null ? void 0 : _b2.value) != null ? _d2 : "");
983
+ setIsOpen(false);
984
+ });
985
+ };
986
+ form.addEventListener("reset", handleReset);
987
+ return () => {
988
+ form.removeEventListener("reset", handleReset);
989
+ };
990
+ }, [defaultValue, enabledOptions, isControlled, isMultiSelect]);
991
+ const syncHiddenSelect = (nextValues) => {
992
+ var _a2, _b2;
993
+ const selectElement = hiddenSelectRef.current;
994
+ if (!selectElement) {
995
+ return;
996
+ }
997
+ if (isMultiSelect) {
998
+ Array.from(selectElement.options).forEach((optionElement) => {
999
+ optionElement.selected = nextValues.includes(optionElement.value);
1000
+ });
1001
+ } else {
1002
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value");
1003
+ if (descriptor == null ? void 0 : descriptor.set) {
1004
+ descriptor.set.call(selectElement, (_a2 = nextValues[0]) != null ? _a2 : "");
1005
+ } else {
1006
+ selectElement.value = (_b2 = nextValues[0]) != null ? _b2 : "";
1007
+ }
1008
+ }
1009
+ selectElement.dispatchEvent(new Event("change", { bubbles: true }));
546
1010
  };
547
- const select = /* @__PURE__ */ jsx5("select", { ...rest, id, disabled, style: selectStyle, children });
548
- if (!label && !hint && !error) {
549
- return select;
550
- }
551
- return /* @__PURE__ */ jsx5(Field, { sdk, label, hint, error, htmlFor: id, children: select });
1011
+ const updateMultiSelection = (nextValues) => {
1012
+ var _a2, _b2, _c2;
1013
+ if (!isMultiSelect || disabled) {
1014
+ return;
1015
+ }
1016
+ if (!isControlled) {
1017
+ setUncontrolledValues(nextValues);
1018
+ }
1019
+ setHighlightedValue((_c2 = (_b2 = nextValues[nextValues.length - 1]) != null ? _b2 : (_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "");
1020
+ syncHiddenSelect(nextValues);
1021
+ };
1022
+ const toggleVisibleSelection = (checked) => {
1023
+ if (!isMultiSelect || disabled || visibleEnabledValues.length === 0) {
1024
+ return;
1025
+ }
1026
+ const nextValues = checked ? Array.from(/* @__PURE__ */ new Set([...selectedValues, ...visibleEnabledValues])) : selectedValues.filter((currentValue) => !visibleEnabledValues.includes(currentValue));
1027
+ updateMultiSelection(nextValues);
1028
+ };
1029
+ const commitSelection = (nextValue) => {
1030
+ var _a2;
1031
+ if (disabled) {
1032
+ if (!isMultiSelect) {
1033
+ setIsOpen(false);
1034
+ }
1035
+ return;
1036
+ }
1037
+ if (isMultiSelect) {
1038
+ const nextValues = selectedValues.includes(nextValue) ? selectedValues.filter((currentValue) => currentValue !== nextValue) : [...selectedValues, nextValue];
1039
+ updateMultiSelection(nextValues);
1040
+ return;
1041
+ }
1042
+ if (nextValue === selectedValue) {
1043
+ setIsOpen(false);
1044
+ return;
1045
+ }
1046
+ if (!isControlled) {
1047
+ setUncontrolledValues([nextValue]);
1048
+ }
1049
+ setHighlightedValue(nextValue);
1050
+ setIsOpen(false);
1051
+ syncHiddenSelect([nextValue]);
1052
+ (_a2 = buttonRef.current) == null ? void 0 : _a2.focus();
1053
+ };
1054
+ const handleHiddenSelectChange = (event) => {
1055
+ var _a2, _b2, _c2;
1056
+ const nextValues = isMultiSelect ? Array.from(event.currentTarget.selectedOptions, (option) => option.value) : [event.currentTarget.value];
1057
+ if (!isControlled) {
1058
+ setUncontrolledValues(nextValues);
1059
+ }
1060
+ setHighlightedValue((_c2 = (_b2 = nextValues[nextValues.length - 1]) != null ? _b2 : (_a2 = enabledOptions[0]) == null ? void 0 : _a2.value) != null ? _c2 : "");
1061
+ onChange == null ? void 0 : onChange(event);
1062
+ };
1063
+ const visibleSelectedCount = visibleEnabledValues.filter((currentValue) => selectedValues.includes(currentValue)).length;
1064
+ const hasSomeVisibleSelected = visibleSelectedCount > 0 && visibleSelectedCount < visibleEnabledValues.length;
1065
+ const hasAllVisibleSelected = visibleEnabledValues.length > 0 && visibleSelectedCount === visibleEnabledValues.length;
1066
+ const canToggleVisibleSelection = !disabled && visibleEnabledValues.length > 0;
1067
+ const bulkToggleControl = isMultiSelect ? /* @__PURE__ */ jsx4(
1068
+ "button",
1069
+ {
1070
+ type: "button",
1071
+ role: "checkbox",
1072
+ "aria-label": "Toggle all visible options",
1073
+ "aria-checked": hasAllVisibleSelected ? true : hasSomeVisibleSelected ? "mixed" : false,
1074
+ disabled: !canToggleVisibleSelection,
1075
+ onMouseDown: (event) => {
1076
+ event.preventDefault();
1077
+ event.stopPropagation();
1078
+ },
1079
+ onClick: (event) => {
1080
+ event.stopPropagation();
1081
+ toggleVisibleSelection(!hasAllVisibleSelected);
1082
+ },
1083
+ style: {
1084
+ width: 20,
1085
+ height: 20,
1086
+ padding: 0,
1087
+ margin: 0,
1088
+ borderRadius: 999,
1089
+ border: `1.5px solid ${hasAllVisibleSelected || hasSomeVisibleSelected ? theme.colors.secondary : withAlpha5(theme.colors.onSurface, 0.22)}`,
1090
+ backgroundColor: hasAllVisibleSelected || hasSomeVisibleSelected ? theme.colors.secondary : theme.colors.surface,
1091
+ display: "inline-flex",
1092
+ alignItems: "center",
1093
+ justifyContent: "center",
1094
+ flexShrink: 0,
1095
+ cursor: canToggleVisibleSelection ? "pointer" : "not-allowed",
1096
+ opacity: canToggleVisibleSelection ? 1 : 0.45
1097
+ },
1098
+ children: hasAllVisibleSelected ? /* @__PURE__ */ jsx4(
1099
+ "span",
1100
+ {
1101
+ "aria-hidden": "true",
1102
+ style: {
1103
+ width: 8,
1104
+ height: 8,
1105
+ borderRadius: 999,
1106
+ backgroundColor: theme.colors.surface
1107
+ }
1108
+ }
1109
+ ) : hasSomeVisibleSelected ? /* @__PURE__ */ jsx4(
1110
+ "span",
1111
+ {
1112
+ "aria-hidden": "true",
1113
+ style: {
1114
+ width: 8,
1115
+ height: 2,
1116
+ borderRadius: 999,
1117
+ backgroundColor: theme.colors.surface
1118
+ }
1119
+ }
1120
+ ) : null
1121
+ }
1122
+ ) : null;
1123
+ const moveHighlight = (direction) => {
1124
+ if (visibleEnabledOptions.length === 0) {
1125
+ return;
1126
+ }
1127
+ const currentIndex = visibleEnabledOptions.findIndex((option) => option.value === highlightedValue);
1128
+ if (currentIndex === -1) {
1129
+ setHighlightedValue(
1130
+ direction === 1 ? visibleEnabledOptions[0].value : visibleEnabledOptions[visibleEnabledOptions.length - 1].value
1131
+ );
1132
+ return;
1133
+ }
1134
+ const nextIndex = Math.max(0, Math.min(visibleEnabledOptions.length - 1, currentIndex + direction));
1135
+ setHighlightedValue(visibleEnabledOptions[nextIndex].value);
1136
+ };
1137
+ const handleTriggerClick = (event) => {
1138
+ onClick == null ? void 0 : onClick(event);
1139
+ if (event.defaultPrevented || disabled || flatOptions.length === 0) {
1140
+ return;
1141
+ }
1142
+ setIsOpen((current) => !current);
1143
+ };
1144
+ const handleTriggerFocus = (event) => {
1145
+ onFocus == null ? void 0 : onFocus(event);
1146
+ };
1147
+ const handleTriggerBlur = (event) => {
1148
+ var _a2;
1149
+ const nextTarget = event.relatedTarget;
1150
+ if (!((_a2 = shellRef.current) == null ? void 0 : _a2.contains(nextTarget))) {
1151
+ setIsOpen(false);
1152
+ }
1153
+ onBlur == null ? void 0 : onBlur(event);
1154
+ };
1155
+ const handleTriggerKeyDown = (event) => {
1156
+ var _a2, _b2, _c2;
1157
+ onKeyDown == null ? void 0 : onKeyDown(event);
1158
+ if (event.defaultPrevented || disabled || visibleEnabledOptions.length === 0) {
1159
+ return;
1160
+ }
1161
+ if (event.key === "ArrowDown") {
1162
+ event.preventDefault();
1163
+ if (!isOpen) {
1164
+ setIsOpen(true);
1165
+ setHighlightedValue((_a2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _a2 : visibleEnabledOptions[0].value);
1166
+ return;
1167
+ }
1168
+ moveHighlight(1);
1169
+ return;
1170
+ }
1171
+ if (event.key === "ArrowUp") {
1172
+ event.preventDefault();
1173
+ if (!isOpen) {
1174
+ setIsOpen(true);
1175
+ setHighlightedValue((_b2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _b2 : visibleEnabledOptions[visibleEnabledOptions.length - 1].value);
1176
+ return;
1177
+ }
1178
+ moveHighlight(-1);
1179
+ return;
1180
+ }
1181
+ if (event.key === "Home" && isOpen) {
1182
+ event.preventDefault();
1183
+ setHighlightedValue(visibleEnabledOptions[0].value);
1184
+ return;
1185
+ }
1186
+ if (event.key === "End" && isOpen) {
1187
+ event.preventDefault();
1188
+ setHighlightedValue(visibleEnabledOptions[visibleEnabledOptions.length - 1].value);
1189
+ return;
1190
+ }
1191
+ if (event.key === "Enter" || event.key === " ") {
1192
+ event.preventDefault();
1193
+ if (!isOpen) {
1194
+ setIsOpen(true);
1195
+ setHighlightedValue((_c2 = selectedOption == null ? void 0 : selectedOption.value) != null ? _c2 : visibleEnabledOptions[0].value);
1196
+ return;
1197
+ }
1198
+ if (highlightedOption && !highlightedOption.disabled) {
1199
+ commitSelection(highlightedOption.value);
1200
+ }
1201
+ return;
1202
+ }
1203
+ if (event.key === "Escape" && isOpen) {
1204
+ event.preventDefault();
1205
+ setIsOpen(false);
1206
+ return;
1207
+ }
1208
+ if (event.key === "Tab") {
1209
+ setIsOpen(false);
1210
+ }
1211
+ };
1212
+ const handleFilterKeyDown = (event) => {
1213
+ var _a2;
1214
+ if (event.key === "ArrowDown") {
1215
+ event.preventDefault();
1216
+ moveHighlight(1);
1217
+ return;
1218
+ }
1219
+ if (event.key === "ArrowUp") {
1220
+ event.preventDefault();
1221
+ moveHighlight(-1);
1222
+ return;
1223
+ }
1224
+ if (event.key === "Enter") {
1225
+ event.preventDefault();
1226
+ if (highlightedOption && !highlightedOption.disabled) {
1227
+ commitSelection(highlightedOption.value);
1228
+ }
1229
+ return;
1230
+ }
1231
+ if (event.key === "Escape") {
1232
+ event.preventDefault();
1233
+ setIsOpen(false);
1234
+ (_a2 = buttonRef.current) == null ? void 0 : _a2.focus();
1235
+ return;
1236
+ }
1237
+ if (event.key === "Tab") {
1238
+ setIsOpen(false);
1239
+ }
1240
+ };
1241
+ const select = /* @__PURE__ */ jsxs5("div", { ref: shellRef, style: { position: "relative", width: "100%" }, children: [
1242
+ /* @__PURE__ */ jsxs5(
1243
+ "button",
1244
+ {
1245
+ ...buttonPassthroughProps,
1246
+ id: resolvedId,
1247
+ ref: buttonRef,
1248
+ type: "button",
1249
+ className,
1250
+ title: rest.title,
1251
+ disabled: disabled || flatOptions.length === 0,
1252
+ autoFocus,
1253
+ tabIndex: rest.tabIndex,
1254
+ role: "combobox",
1255
+ "aria-controls": listboxId,
1256
+ "aria-expanded": isOpen,
1257
+ "aria-haspopup": "listbox",
1258
+ "aria-invalid": error ? true : rest["aria-invalid"],
1259
+ "aria-required": rest.required,
1260
+ onClick: handleTriggerClick,
1261
+ onFocus: handleTriggerFocus,
1262
+ onBlur: handleTriggerBlur,
1263
+ onKeyDown: handleTriggerKeyDown,
1264
+ style: {
1265
+ width: "100%",
1266
+ boxSizing: "border-box",
1267
+ height: controlMetrics.height,
1268
+ borderRadius: 16,
1269
+ border: `1px solid ${error ? theme.colors.negative : withAlpha5(theme.colors.textLight, 0.5)}`,
1270
+ backgroundColor: theme.colors.surface,
1271
+ color: displayLabel ? theme.colors.onSurface : mutedText,
1272
+ cursor: disabled ? "not-allowed" : "pointer",
1273
+ opacity: disabled ? 0.7 : 1,
1274
+ padding: getSelectControlPadding(controlSize),
1275
+ outline: "none",
1276
+ font: "inherit",
1277
+ fontSize: controlMetrics.fontSize,
1278
+ lineHeight: controlMetrics.lineHeight,
1279
+ transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
1280
+ display: "flex",
1281
+ alignItems: "center",
1282
+ justifyContent: "space-between",
1283
+ gap: 12,
1284
+ minWidth: 0,
1285
+ textAlign: "left",
1286
+ ...style
1287
+ },
1288
+ children: [
1289
+ /* @__PURE__ */ jsx4(
1290
+ "span",
1291
+ {
1292
+ style: {
1293
+ display: "block",
1294
+ flex: "1 1 auto",
1295
+ minWidth: 0,
1296
+ overflow: "hidden",
1297
+ textOverflow: "ellipsis",
1298
+ whiteSpace: "nowrap"
1299
+ },
1300
+ children: displayContent
1301
+ }
1302
+ ),
1303
+ /* @__PURE__ */ jsx4(
1304
+ "span",
1305
+ {
1306
+ "aria-hidden": "true",
1307
+ style: {
1308
+ position: "absolute",
1309
+ right: indicatorOffset,
1310
+ top: "50%",
1311
+ transform: `translateY(-50%) rotate(${isOpen ? 180 : 0}deg)`,
1312
+ transformOrigin: "center",
1313
+ color: disabled ? withAlpha5(theme.colors.onSurface, 0.42) : mutedText,
1314
+ display: "flex",
1315
+ alignItems: "center",
1316
+ justifyContent: "center",
1317
+ pointerEvents: "none",
1318
+ transition: "transform 160ms ease, color 160ms ease"
1319
+ },
1320
+ children: /* @__PURE__ */ jsx4("svg", { width: "18", height: "18", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx4(
1321
+ "path",
1322
+ {
1323
+ d: "M5.75 7.75L10 12l4.25-4.25",
1324
+ stroke: "currentColor",
1325
+ strokeWidth: "1.8",
1326
+ strokeLinecap: "round",
1327
+ strokeLinejoin: "round"
1328
+ }
1329
+ ) })
1330
+ }
1331
+ )
1332
+ ]
1333
+ }
1334
+ ),
1335
+ /* @__PURE__ */ jsx4(
1336
+ "select",
1337
+ {
1338
+ ...rest,
1339
+ id: `${resolvedId}-native`,
1340
+ ref: hiddenSelectRef,
1341
+ name: rest.name,
1342
+ disabled,
1343
+ multiple: isMultiSelect,
1344
+ value: isMultiSelect ? selectedValues : selectedValue,
1345
+ onChange: handleHiddenSelectChange,
1346
+ tabIndex: -1,
1347
+ "aria-hidden": "true",
1348
+ style: {
1349
+ position: "absolute",
1350
+ width: 1,
1351
+ height: 1,
1352
+ padding: 0,
1353
+ margin: 0,
1354
+ border: 0,
1355
+ opacity: 0,
1356
+ pointerEvents: "none",
1357
+ clipPath: "inset(50%)"
1358
+ },
1359
+ children
1360
+ }
1361
+ ),
1362
+ isOpen ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1363
+ /* @__PURE__ */ jsx4("style", { children: `
1364
+ #${listboxId} {
1365
+ scrollbar-width: thin;
1366
+ scrollbar-color: ${scrollbarThumbColor} transparent;
1367
+ }
1368
+
1369
+ #${listboxId}::-webkit-scrollbar {
1370
+ width: 8px;
1371
+ }
1372
+
1373
+ #${listboxId}::-webkit-scrollbar-track {
1374
+ background: transparent;
1375
+ }
1376
+
1377
+ #${listboxId}::-webkit-scrollbar-thumb {
1378
+ background: ${scrollbarThumbColor};
1379
+ border-radius: 999px;
1380
+ border: 2px solid transparent;
1381
+ background-clip: padding-box;
1382
+ }
1383
+
1384
+ #${listboxId}::-webkit-scrollbar-thumb:hover {
1385
+ background: ${scrollbarThumbHoverColor};
1386
+ background-clip: padding-box;
1387
+ }
1388
+ ` }),
1389
+ /* @__PURE__ */ jsxs5(
1390
+ "div",
1391
+ {
1392
+ ref: menuRef,
1393
+ style: {
1394
+ position: "absolute",
1395
+ top: menuPlacement === "bottom" ? `calc(100% + ${MENU_OFFSET}px)` : "auto",
1396
+ bottom: menuPlacement === "top" ? `calc(100% + ${MENU_OFFSET}px)` : "auto",
1397
+ left: 0,
1398
+ right: 0,
1399
+ zIndex: 30,
1400
+ maxHeight: MENU_MAX_HEIGHT,
1401
+ overflow: "hidden",
1402
+ boxSizing: "border-box",
1403
+ borderRadius: 20,
1404
+ border: `1px solid ${withAlpha5(theme.colors.textLight, 0.5)}`,
1405
+ backgroundColor: surfaceRaised,
1406
+ // boxShadow: `0 10px 24px ${withAlpha(theme.colors.onSurface, 0.08)}`,
1407
+ padding: 8,
1408
+ backdropFilter: "blur(6px)",
1409
+ visibility: isPlacementResolved ? "visible" : "hidden",
1410
+ display: "flex",
1411
+ flexDirection: "column",
1412
+ gap: 4
1413
+ },
1414
+ children: [
1415
+ hasFilter ? /* @__PURE__ */ jsxs5(
1416
+ "div",
1417
+ {
1418
+ style: {
1419
+ display: "flex",
1420
+ alignItems: "center",
1421
+ justifyContent: "flex-start",
1422
+ gap: 8,
1423
+ padding: "4px 4px 10px",
1424
+ borderBottom: `1px solid ${withAlpha5(theme.colors.onSurface, 0.08)}`,
1425
+ flexShrink: 0
1426
+ },
1427
+ children: [
1428
+ bulkToggleControl,
1429
+ /* @__PURE__ */ jsx4(
1430
+ "input",
1431
+ {
1432
+ ref: searchInputRef,
1433
+ type: "search",
1434
+ value: filterQuery,
1435
+ onChange: (event) => {
1436
+ setFilterQuery(event.currentTarget.value);
1437
+ },
1438
+ onKeyDown: handleFilterKeyDown,
1439
+ onMouseDown: (event) => {
1440
+ event.stopPropagation();
1441
+ },
1442
+ placeholder: "Search options",
1443
+ "aria-label": "Search options",
1444
+ style: {
1445
+ flex: "1 1 auto",
1446
+ width: "100%",
1447
+ border: `1px solid ${withAlpha5(theme.colors.textLight, 0.5)}`,
1448
+ backgroundColor: theme.colors.surface,
1449
+ padding: "8px 12px",
1450
+ borderRadius: 12,
1451
+ font: "inherit",
1452
+ fontSize: 12,
1453
+ lineHeight: 1.4,
1454
+ color: theme.colors.textLight,
1455
+ outline: "none",
1456
+ boxSizing: "border-box"
1457
+ }
1458
+ }
1459
+ )
1460
+ ]
1461
+ }
1462
+ ) : isMultiSelect ? /* @__PURE__ */ jsx4(
1463
+ "div",
1464
+ {
1465
+ style: {
1466
+ display: "flex",
1467
+ alignItems: "center",
1468
+ justifyContent: "flex-start",
1469
+ padding: "4px 4px 10px",
1470
+ borderBottom: `1px solid ${withAlpha5(theme.colors.onSurface, 0.08)}`,
1471
+ flexShrink: 0
1472
+ },
1473
+ children: bulkToggleControl
1474
+ }
1475
+ ) : null,
1476
+ /* @__PURE__ */ jsx4(
1477
+ "div",
1478
+ {
1479
+ id: listboxId,
1480
+ role: "listbox",
1481
+ "aria-labelledby": resolvedId,
1482
+ "aria-multiselectable": isMultiSelect || void 0,
1483
+ style: {
1484
+ overflowY: "auto",
1485
+ overflowX: "hidden",
1486
+ minHeight: 0,
1487
+ flex: "1 1 auto"
1488
+ },
1489
+ children: visibleGroups.length === 0 ? /* @__PURE__ */ jsx4(
1490
+ "div",
1491
+ {
1492
+ style: {
1493
+ padding: "14px 12px",
1494
+ color: mutedText,
1495
+ fontSize: 13,
1496
+ textAlign: "center"
1497
+ },
1498
+ children: "No options found."
1499
+ }
1500
+ ) : visibleGroups.map((group) => /* @__PURE__ */ jsxs5(
1501
+ "div",
1502
+ {
1503
+ style: {
1504
+ display: "flex",
1505
+ flexDirection: "column",
1506
+ gap: 4,
1507
+ marginTop: group.label ? 4 : 0
1508
+ },
1509
+ children: [
1510
+ group.label ? /* @__PURE__ */ jsx4(
1511
+ "span",
1512
+ {
1513
+ style: {
1514
+ padding: "6px 8px 2px",
1515
+ color: mutedText,
1516
+ fontSize: 11,
1517
+ fontWeight: 800,
1518
+ letterSpacing: "0.08em",
1519
+ textTransform: "uppercase"
1520
+ },
1521
+ children: group.label
1522
+ }
1523
+ ) : null,
1524
+ group.options.map((option) => {
1525
+ const isSelected = selectedValues.includes(option.value);
1526
+ const isHighlighted = option.value === highlightedValue;
1527
+ const isActive = isSelected || isHighlighted;
1528
+ return /* @__PURE__ */ jsxs5(
1529
+ "div",
1530
+ {
1531
+ role: "option",
1532
+ "aria-selected": isSelected,
1533
+ "aria-disabled": option.disabled,
1534
+ onMouseEnter: () => {
1535
+ if (!option.disabled) {
1536
+ setHighlightedValue(option.value);
1537
+ }
1538
+ },
1539
+ onMouseDown: (event) => {
1540
+ event.preventDefault();
1541
+ },
1542
+ onClick: () => {
1543
+ if (!option.disabled) {
1544
+ commitSelection(option.value);
1545
+ }
1546
+ },
1547
+ style: {
1548
+ display: "flex",
1549
+ alignItems: "center",
1550
+ justifyContent: "space-between",
1551
+ gap: 12,
1552
+ width: "100%",
1553
+ minWidth: 0,
1554
+ boxSizing: "border-box",
1555
+ borderRadius: 14,
1556
+ padding: getOptionPadding(controlSize),
1557
+ cursor: option.disabled ? "not-allowed" : "pointer",
1558
+ color: option.disabled ? withAlpha5(theme.colors.onSurface, 0.38) : isActive ? theme.colors.onPrimary : theme.colors.onSurface,
1559
+ backgroundColor: isSelected ? optionSelectedBackground : isHighlighted ? optionHoverBackground : "transparent",
1560
+ fontWeight: isSelected ? 700 : 500,
1561
+ transition: "background-color 140ms ease, color 140ms ease"
1562
+ },
1563
+ children: [
1564
+ /* @__PURE__ */ jsxs5(
1565
+ "span",
1566
+ {
1567
+ style: {
1568
+ display: "flex",
1569
+ alignItems: "center",
1570
+ gap: 10,
1571
+ flex: "1 1 auto",
1572
+ minWidth: 0
1573
+ },
1574
+ children: [
1575
+ isMultiSelect ? /* @__PURE__ */ jsx4(
1576
+ "span",
1577
+ {
1578
+ "aria-hidden": "true",
1579
+ style: {
1580
+ width: 10,
1581
+ height: 10,
1582
+ borderRadius: 999,
1583
+ flexShrink: 0,
1584
+ border: `1.5px solid ${isSelected ? theme.colors.secondary : "currentColor"}`,
1585
+ backgroundColor: isSelected ? theme.colors.secondary : "transparent",
1586
+ opacity: option.disabled ? 0.45 : 1,
1587
+ transition: "background-color 140ms ease, border-color 140ms ease, opacity 140ms ease"
1588
+ }
1589
+ }
1590
+ ) : null,
1591
+ /* @__PURE__ */ jsx4(
1592
+ "span",
1593
+ {
1594
+ style: {
1595
+ display: "block",
1596
+ flex: "1 1 auto",
1597
+ minWidth: 0,
1598
+ overflow: "hidden",
1599
+ textOverflow: "ellipsis",
1600
+ whiteSpace: "nowrap"
1601
+ },
1602
+ children: option.label
1603
+ }
1604
+ )
1605
+ ]
1606
+ }
1607
+ ),
1608
+ isSelected ? /* @__PURE__ */ jsx4(
1609
+ "span",
1610
+ {
1611
+ "aria-hidden": "true",
1612
+ style: {
1613
+ display: "flex",
1614
+ alignItems: "center",
1615
+ justifyContent: "center",
1616
+ color: theme.colors.primary,
1617
+ flexShrink: 0
1618
+ },
1619
+ children: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx4(
1620
+ "path",
1621
+ {
1622
+ d: "M5.75 10.25L8.5 13l5.75-6",
1623
+ stroke: "currentColor",
1624
+ strokeWidth: "1.8",
1625
+ strokeLinecap: "round",
1626
+ strokeLinejoin: "round"
1627
+ }
1628
+ ) })
1629
+ }
1630
+ ) : null
1631
+ ]
1632
+ },
1633
+ option.key
1634
+ );
1635
+ })
1636
+ ]
1637
+ },
1638
+ group.key
1639
+ ))
1640
+ }
1641
+ )
1642
+ ]
1643
+ }
1644
+ )
1645
+ ] }) : null
1646
+ ] });
1647
+ return /* @__PURE__ */ jsx4(
1648
+ ControlFrame,
1649
+ {
1650
+ sdk,
1651
+ label,
1652
+ hint,
1653
+ error,
1654
+ htmlFor: resolvedId,
1655
+ required: rest.required,
1656
+ useLabelWrapper: false,
1657
+ children: select
1658
+ }
1659
+ );
552
1660
  }
553
1661
 
554
1662
  // src/components/Textarea.tsx
555
1663
  import { withAlpha as withAlpha6 } from "smartsell-sales-sdk";
556
- import { jsx as jsx6 } from "react/jsx-runtime";
557
- var getPadding3 = (size) => {
1664
+ import { jsx as jsx5 } from "react/jsx-runtime";
1665
+ var getPadding = (size) => {
558
1666
  if (size === "sm") return "10px 12px";
559
1667
  if (size === "lg") return "16px 18px";
560
1668
  return "13px 16px";
@@ -573,29 +1681,28 @@ function Textarea({
573
1681
  }) {
574
1682
  const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
575
1683
  const textareaStyle = {
1684
+ border: `1px solid ${error ? theme.colors.negative : withAlpha6(theme.colors.textLight, 0.5)}`,
1685
+ backgroundColor: theme.colors.surface,
1686
+ color: theme.colors.text,
1687
+ cursor: disabled ? "not-allowed" : "text",
1688
+ opacity: disabled ? 0.7 : 1,
576
1689
  width: "100%",
1690
+ boxSizing: "border-box",
577
1691
  borderRadius: 16,
578
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
579
- padding: getPadding3(controlSize),
1692
+ padding: getPadding(controlSize),
580
1693
  outline: "none",
581
- color: theme.colors.onSurface,
582
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
583
1694
  boxShadow: error ? `0 0 0 3px ${withAlpha6(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
584
1695
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
585
1696
  resize: "vertical",
586
1697
  ...style
587
1698
  };
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 });
1699
+ const textarea = /* @__PURE__ */ jsx5("textarea", { ...rest, id, disabled, rows, style: textareaStyle });
1700
+ return /* @__PURE__ */ jsx5(ControlFrame, { sdk, label, hint, error, htmlFor: id, required: rest.required, children: textarea });
593
1701
  }
594
1702
  export {
595
1703
  Button,
596
1704
  Card,
597
1705
  ComposableModal,
598
- Field,
599
1706
  Input,
600
1707
  Modal,
601
1708
  Select,