@techsio/ui-kit 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/atoms/action-icon.js +52 -0
  2. package/dist/atoms/numeric-input.js +5 -4
  3. package/dist/molecules/accordion.js +6 -3
  4. package/dist/molecules/combobox.js +17 -22
  5. package/dist/molecules/dialog.js +9 -12
  6. package/dist/molecules/phone-input.js +2 -1
  7. package/dist/molecules/popover.js +22 -10
  8. package/dist/molecules/search-form.figma.js +13 -9
  9. package/dist/molecules/search-form.js +83 -51
  10. package/dist/molecules/select.js +15 -18
  11. package/dist/molecules/toast.js +7 -7
  12. package/dist/molecules/tree-view.js +3 -3
  13. package/dist/src/atoms/action-icon.d.ts +45 -0
  14. package/dist/src/atoms/action-icon.d.ts.map +1 -0
  15. package/dist/src/atoms/numeric-input.d.ts.map +1 -1
  16. package/dist/src/molecules/accordion.d.ts +9 -0
  17. package/dist/src/molecules/accordion.d.ts.map +1 -1
  18. package/dist/src/molecules/combobox.d.ts +10 -5
  19. package/dist/src/molecules/combobox.d.ts.map +1 -1
  20. package/dist/src/molecules/dialog.d.ts.map +1 -1
  21. package/dist/src/molecules/phone-input.d.ts.map +1 -1
  22. package/dist/src/molecules/popover.d.ts.map +1 -1
  23. package/dist/src/molecules/search-form.d.ts +61 -11
  24. package/dist/src/molecules/search-form.d.ts.map +1 -1
  25. package/dist/src/molecules/select.d.ts +1 -2
  26. package/dist/src/molecules/select.d.ts.map +1 -1
  27. package/dist/src/molecules/toast.d.ts.map +1 -1
  28. package/dist/stories/molecules/search-form.stories.d.ts +1 -0
  29. package/dist/stories/molecules/search-form.stories.d.ts.map +1 -1
  30. package/dist/stories/overview/component-comparison.stories.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/tokens/components/_icon-button.css +39 -0
  33. package/src/tokens/components/components.css +1 -0
  34. package/src/tokens/components/molecules/_search-form.css +9 -1
@@ -0,0 +1,52 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { tv } from "../utils.js";
3
+ import { Icon } from "./icon.js";
4
+ const actionIconVariants = tv({
5
+ base: [
6
+ "inline-flex shrink-0 cursor-pointer items-center justify-center",
7
+ "rounded-icon-control text-icon-control-fg",
8
+ "transition-colors duration-200 motion-reduce:transition-none",
9
+ "focus-visible:outline-(style:--default-ring-style) focus-visible:outline-(length:--default-ring-width)",
10
+ "focus-visible:outline-offset-(length:--default-ring-offset) focus-visible:outline-icon-control-ring",
11
+ "disabled:cursor-not-allowed disabled:text-icon-control-fg-disabled"
12
+ ],
13
+ variants: {
14
+ size: {
15
+ sm: "size-icon-control-sm text-icon-control-sm",
16
+ md: "size-icon-control-md text-icon-control-md",
17
+ lg: "size-icon-control-lg text-icon-control-lg"
18
+ },
19
+ tone: {
20
+ neutral: [
21
+ "hover:bg-icon-control-bg-hover",
22
+ "active:bg-icon-control-bg-active"
23
+ ],
24
+ danger: [
25
+ "hover:bg-icon-control-bg-danger-hover hover:text-icon-control-fg-danger-hover",
26
+ "active:bg-icon-control-bg-danger-active"
27
+ ]
28
+ }
29
+ },
30
+ defaultVariants: {
31
+ size: "md",
32
+ tone: "neutral"
33
+ }
34
+ });
35
+ function ActionIcon({ icon, size, tone, type = "button", className, ref, ...props }) {
36
+ return /*#__PURE__*/ jsx("button", {
37
+ className: actionIconVariants({
38
+ size,
39
+ tone,
40
+ className
41
+ }),
42
+ ref: ref,
43
+ type: type,
44
+ ...props,
45
+ children: /*#__PURE__*/ jsx(Icon, {
46
+ icon: icon,
47
+ size: "current"
48
+ })
49
+ });
50
+ }
51
+ ActionIcon.displayName = "ActionIcon";
52
+ export { ActionIcon };
@@ -41,16 +41,17 @@ const numericInputVariants = tv({
41
41
  "duration-0 data-invalid:focus:border-input-border-danger-focus"
42
42
  ],
43
43
  triggerContainer: [
44
- "flex flex-col gap-px self-stretch bg-numeric-input-trigger-container-bg"
44
+ "flex flex-col gap-px self-stretch",
45
+ "border-numeric-input-border border-s"
45
46
  ],
46
47
  trigger: [
47
48
  "flex flex-1 place-items-center",
48
49
  "px-numeric-input-trigger-x py-numeric-input-trigger-y",
49
- "bg-numeric-input-trigger-bg hover:bg-numeric-input-trigger-bg-hover",
50
- "text-numeric-input-trigger-fg hover:text-numeric-input-trigger-fg-hover",
50
+ "bg-transparent hover:bg-icon-control-bg-hover active:bg-icon-control-bg-active",
51
+ "text-icon-control-fg",
51
52
  "cursor-pointer",
52
53
  "transition-colors duration-200 motion-reduce:transition-none",
53
- "disabled:cursor-not-allowed"
54
+ "disabled:cursor-not-allowed disabled:text-icon-control-fg-disabled"
54
55
  ],
55
56
  scrubber: "absolute inset-0 cursor-ew-resize"
56
57
  },
@@ -58,17 +58,20 @@ const accordionVariants = tv({
58
58
  sm: {
59
59
  title: "p-accordion-title-sm text-accordion-title-sm",
60
60
  content: "px-accordion-content-x-sm text-accordion-content-sm",
61
- subtitle: "text-accordion-subtitle-sm"
61
+ subtitle: "text-accordion-subtitle-sm",
62
+ icon: "text-icon-control-sm"
62
63
  },
63
64
  md: {
64
65
  title: "p-accordion-title-md text-accordion-title-md",
65
66
  content: "p-accordion-content-md text-accordion-content-md",
66
- subtitle: "text-accordion-subtitle-md"
67
+ subtitle: "text-accordion-subtitle-md",
68
+ icon: "text-icon-control-md"
67
69
  },
68
70
  lg: {
69
71
  title: "p-accordion-title-lg text-accordion-title-lg",
70
72
  content: "p-accordion-content-lg text-accordion-content-lg",
71
- subtitle: "text-accordion-subtitle-lg"
73
+ subtitle: "text-accordion-subtitle-lg",
74
+ icon: "text-icon-control-lg"
72
75
  }
73
76
  }
74
77
  },
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { collection as combobox_collection, connect, machine } from "@zag-js/combobox";
3
3
  import { Portal, normalizeProps, useMachine } from "@zag-js/react";
4
4
  import { useEffect, useId, useState } from "react";
5
+ import { ActionIcon } from "../atoms/action-icon.js";
5
6
  import { Button } from "../atoms/button.js";
6
7
  import { Icon } from "../atoms/icon.js";
7
8
  import { Input } from "../atoms/input.js";
@@ -34,16 +35,13 @@ const comboboxVariants = tv({
34
35
  "data-[validation=warning]:border-combobox-border-warning"
35
36
  ],
36
37
  input: [
37
- "relative h-full w-full border-none bg-combobox-input-bg-base",
38
+ "relative h-full min-w-0 flex-1 border-none bg-combobox-input-bg-base",
38
39
  "hover:bg-combobox-input-bg-hover focus-visible:outline-none",
39
40
  "focus:bg-combobox-input-bg-focus",
40
41
  "placeholder:text-combobox-fg-placeholder",
41
42
  "data-disabled:text-combobox-fg-disabled",
42
43
  "data-disabled:bg-combobox-bg-disabled"
43
44
  ],
44
- clearTrigger: [
45
- "absolute right-combobox-clear-right h-full p-combobox-trigger"
46
- ],
47
45
  trigger: [
48
46
  "group flex h-full shrink-0 items-center justify-center",
49
47
  "font-normal",
@@ -92,14 +90,13 @@ const comboboxVariants = tv({
92
90
  compoundSlots: [
93
91
  {
94
92
  slots: [
95
- "clearTrigger",
96
93
  "trigger"
97
94
  ],
98
95
  class: [
99
96
  "focus-visible:outline-(style:--default-ring-style) focus-visible:outline-(length:--default-ring-width)",
100
97
  "focus-visible:outline-combobox-ring",
101
98
  "focus-visible:outline-offset-(length:--default-ring-offset)",
102
- "text-combobox-trigger-fg-base text-combobox-trigger",
99
+ "text-combobox-trigger text-combobox-trigger-fg-base",
103
100
  "hover:text-combobox-trigger-fg-hover",
104
101
  "motion-safe:transition-colors motion-safe:duration-200 motion-reduce:transition-none",
105
102
  "hover:bg-combobox-trigger-bg-hover",
@@ -115,7 +112,8 @@ const comboboxVariants = tv({
115
112
  item: "p-combobox-item-sm text-combobox-item-sm",
116
113
  emptyState: "p-combobox-item-sm text-combobox-item-sm",
117
114
  input: "p-combobox-input-sm",
118
- content: "text-combobox-sm"
115
+ content: "text-combobox-sm",
116
+ triggerIndicator: "text-icon-control-sm"
119
117
  },
120
118
  md: {
121
119
  root: "gap-combobox-md",
@@ -123,7 +121,8 @@ const comboboxVariants = tv({
123
121
  item: "p-combobox-item-md text-combobox-item-md",
124
122
  emptyState: "p-combobox-item-md text-combobox-item-md",
125
123
  input: "p-combobox-input-md",
126
- content: "text-combobox-md"
124
+ content: "text-combobox-md",
125
+ triggerIndicator: "text-icon-control-md"
127
126
  },
128
127
  lg: {
129
128
  root: "gap-combobox-lg",
@@ -131,7 +130,8 @@ const comboboxVariants = tv({
131
130
  item: "p-combobox-item-lg text-combobox-item-lg",
132
131
  emptyState: "p-combobox-item-lg text-combobox-item-lg",
133
132
  input: "p-combobox-input-lg",
134
- content: "text-combobox-lg"
133
+ content: "text-combobox-lg",
134
+ triggerIndicator: "text-icon-control-lg"
135
135
  }
136
136
  }
137
137
  },
@@ -139,8 +139,7 @@ const comboboxVariants = tv({
139
139
  size: "md"
140
140
  }
141
141
  });
142
- function Combobox({ id, name, label, size, placeholder = "Select option", disabled = false, readOnly = false, required = false, items = [], value, defaultValue, inputValue, multiple = false, validateStatus, helpText, showHelpTextIcon = true, noResultsMessage = 'No results found for "{inputValue}"', clearable = true, selectionBehavior = "replace", closeOnSelect = true, allowCustomValue = false, loopFocus = true, autoFocus = false, triggerIcon = "token-icon-combobox-chevron", triggerIconSize, clearIcon = "token-icon-combobox-clear", clearIconSize, inputBehavior = "autocomplete", onChange, onInputValueChange, onOpenChange }) {
143
- const resolvedChevronIconSize = "sm" === size ? "sm" : "md";
142
+ function Combobox({ id, name, label, size, placeholder = "Select option", disabled = false, readOnly = false, required = false, items = [], value, defaultValue, inputValue, multiple = false, validateStatus, helpText, showHelpTextIcon = true, noResultsMessage = 'No results found for "{inputValue}"', clearable = true, selectionBehavior = "replace", closeOnSelect = true, allowCustomValue = false, loopFocus = true, autoFocus = false, triggerIcon = "token-icon-combobox-chevron", triggerIconSize, clearIcon = "token-icon-combobox-clear", inputBehavior = "autocomplete", onChange, onInputValueChange, onOpenChange }) {
144
143
  const generatedId = useId();
145
144
  const uniqueId = id || generatedId;
146
145
  const [options, setOptions] = useState(items);
@@ -192,7 +191,7 @@ function Combobox({ id, name, label, size, placeholder = "Select option", disabl
192
191
  const api = connect(service, normalizeProps);
193
192
  const inputProps = api.getInputProps();
194
193
  const { ...restInputProps } = inputProps;
195
- const { root, label: labelStyles, control, input, trigger, positioner, content, list, clearTrigger, item: itemSlot, emptyState, triggerIndicator } = comboboxVariants({
194
+ const { root, label: labelStyles, control, input, trigger, positioner, content, list, item: itemSlot, emptyState, triggerIndicator } = comboboxVariants({
196
195
  size
197
196
  });
198
197
  const hasOptions = api.collection.size > 0;
@@ -220,15 +219,11 @@ function Combobox({ id, name, label, size, placeholder = "Select option", disabl
220
219
  required: required,
221
220
  size: size
222
221
  }),
223
- clearable && api.value.length > 0 && /*#__PURE__*/ jsx(Button, {
224
- className: clearTrigger(),
225
- size: "current",
226
- theme: "unstyled",
227
- ...api.getClearTriggerProps(),
228
- children: /*#__PURE__*/ jsx(Icon, {
229
- icon: clearIcon,
230
- size: clearIconSize ?? "current"
231
- })
222
+ clearable && api.value.length > 0 && /*#__PURE__*/ jsx(ActionIcon, {
223
+ icon: clearIcon,
224
+ size: size ?? "md",
225
+ tone: "neutral",
226
+ ...api.getClearTriggerProps()
232
227
  }),
233
228
  /*#__PURE__*/ jsx(Button, {
234
229
  ...api.getTriggerProps(),
@@ -238,7 +233,7 @@ function Combobox({ id, name, label, size, placeholder = "Select option", disabl
238
233
  children: /*#__PURE__*/ jsx(Icon, {
239
234
  className: triggerIndicator(),
240
235
  icon: triggerIcon,
241
- size: triggerIconSize ?? resolvedChevronIconSize
236
+ size: triggerIconSize ?? "current"
242
237
  })
243
238
  })
244
239
  ]
@@ -3,6 +3,7 @@ import { connect, machine } from "@zag-js/dialog";
3
3
  import { Portal, normalizeProps, useMachine } from "@zag-js/react";
4
4
  import { useId } from "react";
5
5
  import { tv } from "tailwind-variants";
6
+ import { ActionIcon } from "../atoms/action-icon.js";
6
7
  import { Button } from "../atoms/button.js";
7
8
  const dialogVariants = tv({
8
9
  slots: {
@@ -23,20 +24,14 @@ const dialogVariants = tv({
23
24
  "focus-visible:outline-offset-(length:--default-ring-offset)"
24
25
  ],
25
26
  title: [
26
- "font-dialog-title text-dialog-title-fg text-dialog-title"
27
+ "font-dialog-title text-dialog-title text-dialog-title-fg"
27
28
  ],
28
29
  description: [
29
- "text-dialog-description-fg text-dialog-description"
30
+ "text-dialog-description text-dialog-description-fg"
30
31
  ],
31
32
  trigger: [],
32
33
  closeTrigger: [
33
- "absolute top-dialog-close-trigger-offset right-dialog-close-trigger-offset",
34
- "flex items-center justify-center",
35
- "rounded-dialog-close-trigger p-dialog-close-trigger",
36
- "text-dialog-close-trigger-fg",
37
- "focus-visible:outline-(style:--default-ring-style) focus-visible:outline-(length:--default-ring-width)",
38
- "focus-visible:outline-dialog-ring",
39
- "focus-visible:outline-offset-(length:--default-ring-offset)"
34
+ "absolute top-dialog-close-trigger-offset right-dialog-close-trigger-offset"
40
35
  ],
41
36
  actions: "mt-auto flex shrink-0 justify-end gap-dialog-actions pt-dialog-actions-top"
42
37
  },
@@ -269,11 +264,13 @@ function Dialog({ id, open, onOpenChange, initialFocusEl, finalFocusEl, role = "
269
264
  }),
270
265
  ...api.getContentProps(),
271
266
  children: [
272
- !hideCloseButton && /*#__PURE__*/ jsx(Button, {
267
+ !hideCloseButton && /*#__PURE__*/ jsx(ActionIcon, {
273
268
  className: closeTrigger(),
274
- theme: "borderless",
269
+ icon: "token-icon-dialog-close",
270
+ size: "md",
271
+ tone: "neutral",
275
272
  ...api.getCloseTriggerProps(),
276
- icon: "token-icon-dialog-close"
273
+ "aria-label": "Close dialog"
277
274
  }),
278
275
  title && /*#__PURE__*/ jsx("h2", {
279
276
  className: titleSlot(),
@@ -86,7 +86,8 @@ const phoneInputVariants = tv({
86
86
  "border-(length:--border-phone-input-trigger)",
87
87
  "focus-visible:outline-none",
88
88
  "w-phone-input-trigger",
89
- "focus-visible:bg-phone-input-trigger-bg-hover"
89
+ "focus-visible:bg-phone-input-trigger-bg-hover",
90
+ "rounded-e-none"
90
91
  ],
91
92
  countryValue: [
92
93
  "flex items-center gap-phone-input-country-value"
@@ -2,6 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { connect, machine } from "@zag-js/popover";
3
3
  import { Portal, mergeProps, normalizeProps, useMachine } from "@zag-js/react";
4
4
  import { createContext, useContext, useId } from "react";
5
+ import { ActionIcon } from "../atoms/action-icon.js";
5
6
  import { Button } from "../atoms/button.js";
6
7
  import { tv } from "../utils.js";
7
8
  const popoverVariants = tv({
@@ -31,12 +32,11 @@ const popoverVariants = tv({
31
32
  "mb-popover-title"
32
33
  ],
33
34
  description: [
34
- "text-popover-description-fg text-popover-description",
35
+ "text-popover-description text-popover-description-fg",
35
36
  "leading-normal"
36
37
  ],
37
38
  closeTrigger: [
38
- "absolute top-2 right-2",
39
- "text-popover-close-trigger-fg"
39
+ "absolute top-2 right-2"
40
40
  ]
41
41
  },
42
42
  variants: {
@@ -248,18 +248,30 @@ Popover.CloseTrigger = function({ children, className, icon, onClick, ref, size
248
248
  const { api, styles } = usePopoverContext();
249
249
  const { onClick: onMachineClick, ...machineCloseTriggerProps } = api.getCloseTriggerProps();
250
250
  const buttonProps = mergeProps(props, machineCloseTriggerProps);
251
- const closeIcon = icon ?? (children ? void 0 : "token-icon-close");
251
+ const handleClick = (event)=>{
252
+ onClick?.(event);
253
+ if (!event.defaultPrevented) onMachineClick?.(event);
254
+ };
255
+ if (!children) return /*#__PURE__*/ jsx(ActionIcon, {
256
+ ...buttonProps,
257
+ "aria-label": "Close popover",
258
+ className: styles.closeTrigger({
259
+ className
260
+ }),
261
+ icon: icon ?? "token-icon-close",
262
+ onClick: handleClick,
263
+ ref: ref,
264
+ size: "md",
265
+ tone: "neutral",
266
+ type: type
267
+ });
252
268
  return /*#__PURE__*/ jsx(Button, {
253
269
  ...buttonProps,
254
- "aria-label": children ? void 0 : "Close popover",
255
270
  className: styles.closeTrigger({
256
271
  className
257
272
  }),
258
- icon: closeIcon,
259
- onClick: (event)=>{
260
- onClick?.(event);
261
- if (!event.defaultPrevented) onMachineClick?.(event);
262
- },
273
+ icon: icon,
274
+ onClick: handleClick,
263
275
  ref: ref,
264
276
  size: size,
265
277
  theme: theme,
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import code_connect from "@figma/code-connect";
3
3
  import { SearchForm } from "./search-form.js";
4
- code_connect.connect(SearchForm, "https://www.figma.com/design/12xb1pqXKwE2vbOByN3ntg/New-Design-System-vol.-2?node-id=1146-48", {
4
+ code_connect.connect(SearchForm, "https://www.figma.com/design/12xb1pqXKwE2vbOByN3ntg/New-Design-System-vol.-2?node-id=2620-122", {
5
5
  imports: [
6
6
  'import { SearchForm } from "@libs/ui/molecules/search-form"'
7
7
  ],
@@ -10,15 +10,19 @@ code_connect.connect(SearchForm, "https://www.figma.com/design/12xb1pqXKwE2vbOBy
10
10
  sm: "sm",
11
11
  md: "md",
12
12
  lg: "lg"
13
- })
13
+ }),
14
+ gapped: code_connect.boolean("gapped")
14
15
  },
15
- example: ({ size })=>/*#__PURE__*/ jsxs(SearchForm, {
16
+ example: ({ size, gapped })=>/*#__PURE__*/ jsx(SearchForm, {
17
+ gapped: gapped,
16
18
  size: size,
17
- children: [
18
- /*#__PURE__*/ jsx(SearchForm.Input, {
19
- placeholder: "Search..."
20
- }),
21
- /*#__PURE__*/ jsx(SearchForm.Button, {})
22
- ]
19
+ children: /*#__PURE__*/ jsxs(SearchForm.Control, {
20
+ children: [
21
+ /*#__PURE__*/ jsx(SearchForm.Input, {
22
+ placeholder: "Search..."
23
+ }),
24
+ /*#__PURE__*/ jsx(SearchForm.Button, {})
25
+ ]
26
+ })
23
27
  })
24
28
  });
@@ -1,5 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useId, useState } from "react";
2
+ import { createContext, useContext, useEffect, useId, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { ActionIcon } from "../atoms/action-icon.js";
3
5
  import { Button } from "../atoms/button.js";
4
6
  import { Input } from "../atoms/input.js";
5
7
  import { Label } from "../atoms/label.js";
@@ -10,46 +12,54 @@ const searchFormVariants = tv({
10
12
  "relative grid"
11
13
  ],
12
14
  control: [
13
- "relative flex items-center overflow-hidden",
14
- "form-control-base",
15
- "hover:border-input-border-hover",
16
- "focus-within:border-input-border-focus",
17
- "focus-within:outline-(style:--default-ring-style) focus-within:outline-(length:--default-ring-width)",
18
- "focus-within:outline-input-ring",
19
- "focus-within:outline-offset-(length:--default-ring-offset)",
20
- "transition-colors duration-200 motion-reduce:transition-none"
15
+ "flex items-stretch"
16
+ ],
17
+ inputWrapper: [
18
+ "relative min-w-0 flex-1",
19
+ "focus-within:z-10"
21
20
  ],
22
21
  input: [
23
- "peer",
24
- "min-w-0 flex-1",
25
- "border-none bg-transparent",
26
- "focus-visible:outline-none"
22
+ "w-full"
27
23
  ],
28
24
  button: [
29
- "h-full shrink-0 items-center rounded-l-none"
25
+ "relative shrink-0",
26
+ "focus-visible:z-10"
30
27
  ],
31
28
  clearButton: [
32
- "h-full shrink-0 rounded-none p-search-form-clear-button",
33
- "peer-hover:bg-input-hover peer-focus:bg-input-focus"
29
+ "-translate-y-1/2 absolute top-1/2"
34
30
  ]
35
31
  },
36
32
  variants: {
37
33
  size: {
38
34
  sm: {
39
35
  root: "gap-search-form-sm",
40
- control: "h-form-control-sm rounded-search-form-sm"
36
+ button: "h-form-control-sm",
37
+ clearButton: "end-(length:--padding-input-sm)"
41
38
  },
42
39
  md: {
43
40
  root: "gap-search-form-md",
44
- control: "h-form-control-md rounded-search-form-md"
41
+ button: "h-form-control-md",
42
+ clearButton: "end-(length:--padding-input-md)"
45
43
  },
46
44
  lg: {
47
- root: "gap-search-form-lg"
45
+ root: "gap-search-form-lg",
46
+ button: "h-form-control-lg",
47
+ clearButton: "end-(length:--padding-input-lg)"
48
+ }
49
+ },
50
+ gapped: {
51
+ false: {
52
+ input: "rounded-e-none",
53
+ button: "rounded-s-none"
54
+ },
55
+ true: {
56
+ control: "gap-search-form-gapped"
48
57
  }
49
58
  }
50
59
  },
51
60
  defaultVariants: {
52
- size: "md"
61
+ size: "md",
62
+ gapped: false
53
63
  }
54
64
  });
55
65
  const SearchFormContext = /*#__PURE__*/ createContext(null);
@@ -58,10 +68,12 @@ function useSearchFormContext() {
58
68
  if (!context) throw new Error("SearchForm components must be used within SearchForm");
59
69
  return context;
60
70
  }
61
- function SearchForm({ size = "md", children, defaultValue = "", value, onValueChange, className, ref, onSubmit, ...props }) {
71
+ function SearchForm({ size = "md", gapped = false, children, defaultValue = "", value, onValueChange, className, ref, onSubmit, ...props }) {
62
72
  const generatedId = useId();
63
73
  const inputId = `search-input-${generatedId}`;
64
74
  const [internalValue, setInternalValue] = useState(defaultValue);
75
+ const [clearSlot, setClearSlot] = useState(null);
76
+ const [hasClearButton, setHasClearButton] = useState(false);
65
77
  const isControlled = void 0 !== value;
66
78
  const inputValue = isControlled ? value : internalValue;
67
79
  const setInputValue = (newValue)=>{
@@ -76,16 +88,22 @@ function SearchForm({ size = "md", children, defaultValue = "", value, onValueCh
76
88
  onSubmit?.(e);
77
89
  };
78
90
  const styles = searchFormVariants({
79
- size
91
+ size,
92
+ gapped
80
93
  });
81
94
  return /*#__PURE__*/ jsx(SearchFormContext.Provider, {
82
95
  value: {
83
96
  size,
97
+ gapped,
84
98
  inputId,
85
99
  inputValue,
86
100
  setInputValue,
87
101
  clearInput,
88
- hasValue: inputValue.length > 0
102
+ hasValue: inputValue.length > 0,
103
+ clearSlot,
104
+ setClearSlot,
105
+ hasClearButton,
106
+ setHasClearButton
89
107
  },
90
108
  children: /*#__PURE__*/ jsx("search", {
91
109
  children: /*#__PURE__*/ jsx("form", {
@@ -111,9 +129,10 @@ SearchForm.Label = function({ children, className, ...props }) {
111
129
  });
112
130
  };
113
131
  SearchForm.Control = function({ children, className, ref, ...props }) {
114
- const { size } = useSearchFormContext();
132
+ const { size, gapped } = useSearchFormContext();
115
133
  const styles = searchFormVariants({
116
- size
134
+ size,
135
+ gapped
117
136
  });
118
137
  return /*#__PURE__*/ jsx("div", {
119
138
  className: styles.control({
@@ -125,29 +144,36 @@ SearchForm.Control = function({ children, className, ref, ...props }) {
125
144
  });
126
145
  };
127
146
  SearchForm.Input = function({ className, placeholder = "Search...", ref, ...props }) {
128
- const { inputId, inputValue, setInputValue, size } = useSearchFormContext();
147
+ const { inputId, inputValue, setInputValue, size, gapped, hasValue, hasClearButton, setClearSlot } = useSearchFormContext();
129
148
  const styles = searchFormVariants({
130
- size
149
+ size,
150
+ gapped
131
151
  });
132
- return /*#__PURE__*/ jsx(Input, {
133
- "aria-label": props["aria-label"] || "Search",
134
- className: styles.input({
135
- className
136
- }),
137
- id: inputId,
138
- onChange: (e)=>setInputValue(e.target.value),
139
- placeholder: placeholder,
140
- ref: ref,
141
- size: size,
142
- type: "search",
143
- value: inputValue,
144
- ...props
152
+ return /*#__PURE__*/ jsx("div", {
153
+ className: styles.inputWrapper(),
154
+ ref: setClearSlot,
155
+ children: /*#__PURE__*/ jsx(Input, {
156
+ "aria-label": props["aria-label"] || "Search",
157
+ className: styles.input({
158
+ className
159
+ }),
160
+ id: inputId,
161
+ onChange: (e)=>setInputValue(e.target.value),
162
+ placeholder: placeholder,
163
+ ref: ref,
164
+ size: size,
165
+ type: "search",
166
+ value: inputValue,
167
+ withButtonInside: hasValue && hasClearButton ? "right" : void 0,
168
+ ...props
169
+ })
145
170
  });
146
171
  };
147
172
  SearchForm.Button = function({ className, children, showSearchIcon = false, icon, iconPosition = "right", ...props }) {
148
- const { size } = useSearchFormContext();
173
+ const { size, gapped } = useSearchFormContext();
149
174
  const styles = searchFormVariants({
150
- size
175
+ size,
176
+ gapped
151
177
  });
152
178
  const effectiveIcon = icon ?? (showSearchIcon ? "token-icon-search" : void 0);
153
179
  return /*#__PURE__*/ jsx(Button, {
@@ -162,24 +188,30 @@ SearchForm.Button = function({ className, children, showSearchIcon = false, icon
162
188
  children: children
163
189
  });
164
190
  };
165
- SearchForm.ClearButton = function({ className, icon = "token-icon-close", theme = "unstyled", ...props }) {
166
- const { size, clearInput, hasValue, inputValue } = useSearchFormContext();
191
+ SearchForm.ClearButton = function({ className, icon = "token-icon-close", tone = "neutral", ...props }) {
192
+ const { size, gapped, clearInput, hasValue, inputValue, clearSlot, setHasClearButton } = useSearchFormContext();
167
193
  const styles = searchFormVariants({
168
- size
194
+ size,
195
+ gapped
169
196
  });
170
- if (!hasValue) return null;
171
- return /*#__PURE__*/ jsx(Button, {
197
+ useEffect(()=>{
198
+ setHasClearButton(true);
199
+ return ()=>setHasClearButton(false);
200
+ }, [
201
+ setHasClearButton
202
+ ]);
203
+ if (!(hasValue && clearSlot)) return null;
204
+ return /*#__PURE__*/ createPortal(/*#__PURE__*/ jsx(ActionIcon, {
172
205
  "aria-label": `Clear search: ${inputValue}`,
173
206
  className: styles.clearButton({
174
207
  className
175
208
  }),
176
209
  icon: icon,
177
210
  onClick: clearInput,
178
- size: "current",
179
- theme: theme,
180
- type: "button",
211
+ size: size,
212
+ tone: tone,
181
213
  ...props
182
- });
214
+ }), clearSlot);
183
215
  };
184
216
  SearchForm.displayName = "SearchForm";
185
217
  export { SearchForm, searchFormVariants, useSearchFormContext };