smartsell-ui-kit 0.0.4 → 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,10 +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
+ import { useState } from "react";
6
7
  import { withAlpha as withAlpha2 } from "smartsell-sales-sdk";
7
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
+
8
66
  // src/theme.ts
9
67
  import { useMemo } from "react";
10
68
  import {
@@ -29,19 +87,9 @@ function useUiKitTheme(sdk) {
29
87
 
30
88
  // src/components/Button.tsx
31
89
  import { jsxs } from "react/jsx-runtime";
32
- var getSizeStyles = (size) => {
33
- if (size === "sm") {
34
- return { minHeight: 38, padding: "0 14px", fontSize: 13 };
35
- }
36
- if (size === "lg") {
37
- return { minHeight: 50, padding: "0 20px", fontSize: 15 };
38
- }
39
- return { minHeight: 44, padding: "0 18px", fontSize: 14 };
40
- };
41
90
  function Button({
42
91
  sdk,
43
92
  variant = "solid",
44
- tone = "primary",
45
93
  size = "md",
46
94
  fullWidth = false,
47
95
  leadingIcon,
@@ -50,46 +98,55 @@ function Button({
50
98
  disabled,
51
99
  style,
52
100
  type = "button",
101
+ onMouseEnter,
102
+ onMouseLeave,
103
+ onMouseDown,
104
+ onMouseUp,
105
+ onBlur,
106
+ onKeyDown,
107
+ onKeyUp,
108
+ onTouchStart,
109
+ onTouchEnd,
110
+ onTouchCancel,
53
111
  ...rest
54
112
  }) {
55
113
  const { theme } = useUiKitTheme(sdk);
56
- const palette = {
57
- primary: {
58
- background: theme.colors.primary,
59
- foreground: theme.colors.onPrimary,
60
- border: withAlpha2(theme.colors.primary, 0.3)
61
- },
62
- secondary: {
63
- background: theme.colors.secondary,
64
- foreground: theme.colors.onSecondary,
65
- border: withAlpha2(theme.colors.secondary, 0.3)
66
- },
67
- neutral: {
68
- background: theme.colors.surface,
69
- foreground: theme.colors.onSurface,
70
- border: withAlpha2(theme.colors.onSurface, 0.16)
71
- },
72
- danger: {
73
- background: theme.colors.negative,
74
- foreground: "#ffffff",
75
- border: withAlpha2(theme.colors.negative, 0.32)
76
- }
77
- }[tone];
114
+ const [isHovered, setIsHovered] = useState(false);
115
+ const [isPressed, setIsPressed] = useState(false);
78
116
  const variantStyles = {
79
117
  solid: {
80
- backgroundColor: palette.background,
81
- borderColor: palette.border,
82
- color: palette.foreground
118
+ backgroundColor: theme.colors.primary,
119
+ borderColor: withAlpha2(theme.colors.primary, 0.5),
120
+ color: theme.colors.onPrimary
83
121
  },
84
122
  outline: {
85
123
  backgroundColor: "transparent",
86
- borderColor: palette.border,
87
- color: tone === "neutral" ? palette.foreground : palette.background
124
+ borderColor: withAlpha2(theme.colors.text, 0.5),
125
+ color: theme.colors.onSurface
88
126
  },
89
127
  ghost: {
90
128
  backgroundColor: "transparent",
91
129
  borderColor: "transparent",
92
- color: tone === "neutral" ? palette.foreground : palette.background
130
+ color: theme.colors.onSurface
131
+ }
132
+ };
133
+ const interactiveColor = variant === "solid" ? theme.colors.primary : theme.colors.onSurface;
134
+ const isInteractive = !disabled;
135
+ const interactionStyles = {
136
+ solid: {
137
+ transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
138
+ boxShadow: "none",
139
+ filter: void 0
140
+ },
141
+ outline: {
142
+ transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
143
+ backgroundColor: isPressed ? withAlpha2(interactiveColor, 0.14) : isHovered ? withAlpha2(interactiveColor, 0.08) : "transparent",
144
+ boxShadow: "none"
145
+ },
146
+ ghost: {
147
+ transform: isPressed ? "translateY(1px) scale(0.985)" : isHovered ? "translateY(-1px)" : "translateY(0)",
148
+ backgroundColor: isPressed ? withAlpha2(interactiveColor, 0.12) : isHovered ? withAlpha2(interactiveColor, 0.06) : "transparent",
149
+ boxShadow: "none"
93
150
  }
94
151
  };
95
152
  return /* @__PURE__ */ jsxs(
@@ -98,6 +155,56 @@ function Button({
98
155
  ...rest,
99
156
  type,
100
157
  disabled,
158
+ onMouseEnter: (event) => {
159
+ if (isInteractive) {
160
+ setIsHovered(true);
161
+ }
162
+ onMouseEnter == null ? void 0 : onMouseEnter(event);
163
+ },
164
+ onMouseLeave: (event) => {
165
+ setIsHovered(false);
166
+ setIsPressed(false);
167
+ onMouseLeave == null ? void 0 : onMouseLeave(event);
168
+ },
169
+ onMouseDown: (event) => {
170
+ if (isInteractive) {
171
+ setIsPressed(true);
172
+ }
173
+ onMouseDown == null ? void 0 : onMouseDown(event);
174
+ },
175
+ onMouseUp: (event) => {
176
+ setIsPressed(false);
177
+ onMouseUp == null ? void 0 : onMouseUp(event);
178
+ },
179
+ onBlur: (event) => {
180
+ setIsHovered(false);
181
+ setIsPressed(false);
182
+ onBlur == null ? void 0 : onBlur(event);
183
+ },
184
+ onKeyDown: (event) => {
185
+ if (isInteractive && (event.key === " " || event.key === "Enter")) {
186
+ setIsPressed(true);
187
+ }
188
+ onKeyDown == null ? void 0 : onKeyDown(event);
189
+ },
190
+ onKeyUp: (event) => {
191
+ setIsPressed(false);
192
+ onKeyUp == null ? void 0 : onKeyUp(event);
193
+ },
194
+ onTouchStart: (event) => {
195
+ if (isInteractive) {
196
+ setIsPressed(true);
197
+ }
198
+ onTouchStart == null ? void 0 : onTouchStart(event);
199
+ },
200
+ onTouchEnd: (event) => {
201
+ setIsPressed(false);
202
+ onTouchEnd == null ? void 0 : onTouchEnd(event);
203
+ },
204
+ onTouchCancel: (event) => {
205
+ setIsPressed(false);
206
+ onTouchCancel == null ? void 0 : onTouchCancel(event);
207
+ },
101
208
  style: {
102
209
  display: "inline-flex",
103
210
  alignItems: "center",
@@ -110,12 +217,11 @@ function Button({
110
217
  cursor: disabled ? "not-allowed" : "pointer",
111
218
  fontWeight: 700,
112
219
  letterSpacing: "0.01em",
113
- lineHeight: 1,
114
220
  opacity: disabled ? 0.58 : 1,
115
- transition: "transform 160ms ease, opacity 160ms ease, background-color 160ms ease",
116
- boxShadow: variant === "solid" ? `0 14px 28px ${withAlpha2(palette.background, 0.18)}` : "none",
117
- ...getSizeStyles(size),
221
+ transition: "transform 140ms ease, opacity 160ms ease, background-color 160ms ease",
222
+ ...getButtonSizeStyles(size),
118
223
  ...variantStyles[variant],
224
+ ...isInteractive ? interactionStyles[variant] : void 0,
119
225
  ...style
120
226
  },
121
227
  children: [
@@ -130,43 +236,26 @@ function Button({
130
236
  // src/components/Card.tsx
131
237
  import { withAlpha as withAlpha3 } from "smartsell-sales-sdk";
132
238
  import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
133
- var getToneStyles = (tone, sdk) => {
134
- const { theme } = useUiKitTheme(sdk);
135
- const accent = {
136
- default: theme.colors.primary,
137
- primary: theme.colors.primary,
138
- secondary: theme.colors.secondary,
139
- warning: theme.colors.warning
140
- }[tone];
141
- return {
142
- accent,
143
- background: `linear-gradient(160deg, ${withAlpha3(theme.colors.surface, 0.98)}, ${withAlpha3(accent, 0.08)})`,
144
- border: withAlpha3(accent, 0.22)
145
- };
146
- };
147
239
  function Card({
148
240
  sdk,
149
241
  children,
150
242
  eyebrow,
151
243
  title,
152
244
  action,
153
- tone = "default",
154
245
  padded = true,
155
246
  style,
156
247
  ...rest
157
248
  }) {
158
- const { theme, mutedText } = useUiKitTheme(sdk);
159
- const toneStyles = getToneStyles(tone, sdk);
249
+ const { theme } = useUiKitTheme(sdk);
160
250
  return /* @__PURE__ */ jsxs2(
161
251
  "div",
162
252
  {
163
253
  ...rest,
164
254
  style: {
165
255
  borderRadius: 28,
166
- border: `1px solid ${toneStyles.border}`,
167
- background: toneStyles.background,
168
- color: theme.colors.onSurface,
169
- 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,
170
259
  padding: padded ? 24 : void 0,
171
260
  ...style
172
261
  },
@@ -187,7 +276,7 @@ function Card({
187
276
  "span",
188
277
  {
189
278
  style: {
190
- color: toneStyles.accent,
279
+ color: theme.colors.textLight,
191
280
  fontSize: 12,
192
281
  fontWeight: 800,
193
282
  letterSpacing: "0.16em",
@@ -200,7 +289,7 @@ function Card({
200
289
  "div",
201
290
  {
202
291
  style: {
203
- color: theme.colors.onSurface,
292
+ color: theme.colors.text,
204
293
  fontSize: 24,
205
294
  fontWeight: 700,
206
295
  lineHeight: 1.2
@@ -209,7 +298,7 @@ function Card({
209
298
  }
210
299
  ) : null
211
300
  ] }),
212
- action ? /* @__PURE__ */ jsx("div", { style: { color: mutedText }, children: action }) : null
301
+ action ? /* @__PURE__ */ jsx("div", { style: { color: theme.colors.text }, children: action }) : null
213
302
  ]
214
303
  }
215
304
  ),
@@ -267,7 +356,7 @@ function ComposableModal({
267
356
  [cancelLabel, confirmLabel, description, isBusy, isOpen, onClose, onConfirm, payload, title]
268
357
  );
269
358
  const defaultFooter = hideDefaultFooter ? void 0 : footerFallback != null ? footerFallback : /* @__PURE__ */ jsxs3(Fragment, { children: [
270
- /* @__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 }),
271
360
  onConfirm ? /* @__PURE__ */ jsx2(Button, { sdk, onClick: onConfirm, disabled: isBusy, children: confirmLabel }) : null
272
361
  ] });
273
362
  const defaultHeader = headerFallback != null ? headerFallback : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
@@ -319,124 +408,412 @@ function ComposableModal({
319
408
  );
320
409
  }
321
410
 
322
- // src/components/Field.tsx
323
- import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
324
- 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({
325
437
  sdk,
326
438
  label,
327
439
  hint,
328
440
  error,
329
441
  htmlFor,
330
442
  required = false,
443
+ useLabelWrapper = true,
331
444
  children,
332
445
  style
333
446
  }) {
447
+ if (!label && !hint && !error) {
448
+ return /* @__PURE__ */ jsx3(Fragment2, { children });
449
+ }
334
450
  const { theme, mutedText } = useUiKitTheme(sdk);
335
- return /* @__PURE__ */ jsxs4(
336
- "label",
337
- {
338
- htmlFor,
339
- style: {
340
- display: "flex",
341
- flexDirection: "column",
342
- gap: 8,
343
- width: "100%",
344
- ...style
345
- },
346
- children: [
347
- (label || hint) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
348
- label ? /* @__PURE__ */ jsxs4(
349
- "span",
350
- {
351
- style: {
352
- color: theme.colors.onSurface,
353
- fontSize: 14,
354
- fontWeight: 700,
355
- letterSpacing: "0.01em"
356
- },
357
- children: [
358
- label,
359
- required ? /* @__PURE__ */ jsx3("span", { style: { color: theme.colors.negative, marginLeft: 4 }, children: "*" }) : null
360
- ]
361
- }
362
- ) : null,
363
- hint ? /* @__PURE__ */ jsx3(
364
- "span",
365
- {
366
- style: {
367
- color: mutedText,
368
- fontSize: 13,
369
- lineHeight: 1.45
370
- },
371
- children: hint
372
- }
373
- ) : null
374
- ] }),
375
- children,
376
- 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(
377
472
  "span",
378
473
  {
379
474
  style: {
380
- color: theme.colors.negative,
475
+ color: mutedText,
381
476
  fontSize: 13,
382
- fontWeight: 600,
383
477
  lineHeight: 1.45
384
478
  },
385
- children: error
479
+ children: hint
386
480
  }
387
481
  ) : null
388
- ]
389
- }
390
- );
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
+ ] });
391
530
  }
392
-
393
- // src/components/Input.tsx
394
- import { withAlpha as withAlpha4 } from "smartsell-sales-sdk";
395
- import { jsx as jsx4 } from "react/jsx-runtime";
396
- var getPadding = (size) => {
397
- if (size === "sm") return "10px 12px";
398
- if (size === "lg") return "16px 18px";
399
- return "13px 16px";
400
- };
401
531
  function Input({
402
532
  sdk,
403
533
  label,
404
534
  hint,
405
535
  error,
406
536
  controlSize = "md",
537
+ showPasswordToggle = false,
407
538
  id,
408
539
  disabled,
409
540
  style,
541
+ type = "text",
410
542
  ...rest
411
543
  }) {
412
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;
413
556
  const inputStyle = {
414
557
  width: "100%",
558
+ boxSizing: "border-box",
559
+ height: controlMetrics.height,
415
560
  borderRadius: 16,
416
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
417
- 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 }),
418
567
  outline: "none",
419
- color: theme.colors.onSurface,
420
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
568
+ fontSize: controlMetrics.fontSize,
569
+ lineHeight: controlMetrics.lineHeight,
421
570
  boxShadow: error ? `0 0 0 3px ${withAlpha4(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
422
571
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
423
572
  ...style
424
573
  };
425
- const input = /* @__PURE__ */ jsx4("input", { ...rest, id, disabled, style: inputStyle });
426
- if (!label && !hint && !error) {
427
- return input;
428
- }
429
- 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 });
430
686
  }
431
687
 
432
688
  // src/components/Select.tsx
689
+ import {
690
+ Children,
691
+ isValidElement,
692
+ useEffect,
693
+ useId,
694
+ useRef,
695
+ useState as useState3
696
+ } from "react";
433
697
  import { withAlpha as withAlpha5 } from "smartsell-sales-sdk";
434
- import { jsx as jsx5 } from "react/jsx-runtime";
435
- 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) => {
436
702
  if (size === "sm") return "10px 12px";
437
- if (size === "lg") return "16px 18px";
438
- return "13px 16px";
703
+ if (size === "lg") return "14px 16px";
704
+ return "12px 14px";
439
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
+ }
440
817
  function Select({
441
818
  sdk,
442
819
  label,
@@ -447,32 +824,845 @@ function Select({
447
824
  disabled,
448
825
  style,
449
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,
450
839
  ...rest
451
840
  }) {
452
- const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
453
- const selectStyle = {
454
- width: "100%",
455
- borderRadius: 16,
456
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
457
- padding: getPadding2(controlSize),
458
- outline: "none",
459
- color: theme.colors.onSurface,
460
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
461
- boxShadow: error ? `0 0 0 3px ${withAlpha5(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
462
- transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
463
- ...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 }));
464
1010
  };
465
- const select = /* @__PURE__ */ jsx5("select", { ...rest, id, disabled, style: selectStyle, children });
466
- if (!label && !hint && !error) {
467
- return select;
468
- }
469
- 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
+ );
470
1660
  }
471
1661
 
472
1662
  // src/components/Textarea.tsx
473
1663
  import { withAlpha as withAlpha6 } from "smartsell-sales-sdk";
474
- import { jsx as jsx6 } from "react/jsx-runtime";
475
- var getPadding3 = (size) => {
1664
+ import { jsx as jsx5 } from "react/jsx-runtime";
1665
+ var getPadding = (size) => {
476
1666
  if (size === "sm") return "10px 12px";
477
1667
  if (size === "lg") return "16px 18px";
478
1668
  return "13px 16px";
@@ -491,29 +1681,28 @@ function Textarea({
491
1681
  }) {
492
1682
  const { theme, borderSoft, focusRing, surfaceMuted } = useUiKitTheme(sdk);
493
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,
494
1689
  width: "100%",
1690
+ boxSizing: "border-box",
495
1691
  borderRadius: 16,
496
- border: `1px solid ${error ? theme.colors.negative : borderSoft}`,
497
- padding: getPadding3(controlSize),
1692
+ padding: getPadding(controlSize),
498
1693
  outline: "none",
499
- color: theme.colors.onSurface,
500
- backgroundColor: disabled ? surfaceMuted : theme.colors.surface,
501
1694
  boxShadow: error ? `0 0 0 3px ${withAlpha6(theme.colors.negative, 0.12)}` : `0 0 0 0 ${focusRing}`,
502
1695
  transition: "border-color 160ms ease, box-shadow 160ms ease, background-color 160ms ease",
503
1696
  resize: "vertical",
504
1697
  ...style
505
1698
  };
506
- const textarea = /* @__PURE__ */ jsx6("textarea", { ...rest, id, disabled, rows, style: textareaStyle });
507
- if (!label && !hint && !error) {
508
- return textarea;
509
- }
510
- 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 });
511
1701
  }
512
1702
  export {
513
1703
  Button,
514
1704
  Card,
515
1705
  ComposableModal,
516
- Field,
517
1706
  Input,
518
1707
  Modal,
519
1708
  Select,