@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.
- package/dist/atoms/action-icon.js +52 -0
- package/dist/atoms/numeric-input.js +5 -4
- package/dist/molecules/accordion.js +6 -3
- package/dist/molecules/combobox.js +17 -22
- package/dist/molecules/dialog.js +9 -12
- package/dist/molecules/phone-input.js +2 -1
- package/dist/molecules/popover.js +22 -10
- package/dist/molecules/search-form.figma.js +13 -9
- package/dist/molecules/search-form.js +83 -51
- package/dist/molecules/select.js +15 -18
- package/dist/molecules/toast.js +7 -7
- package/dist/molecules/tree-view.js +3 -3
- package/dist/src/atoms/action-icon.d.ts +45 -0
- package/dist/src/atoms/action-icon.d.ts.map +1 -0
- package/dist/src/atoms/numeric-input.d.ts.map +1 -1
- package/dist/src/molecules/accordion.d.ts +9 -0
- package/dist/src/molecules/accordion.d.ts.map +1 -1
- package/dist/src/molecules/combobox.d.ts +10 -5
- package/dist/src/molecules/combobox.d.ts.map +1 -1
- package/dist/src/molecules/dialog.d.ts.map +1 -1
- package/dist/src/molecules/phone-input.d.ts.map +1 -1
- package/dist/src/molecules/popover.d.ts.map +1 -1
- package/dist/src/molecules/search-form.d.ts +61 -11
- package/dist/src/molecules/search-form.d.ts.map +1 -1
- package/dist/src/molecules/select.d.ts +1 -2
- package/dist/src/molecules/select.d.ts.map +1 -1
- package/dist/src/molecules/toast.d.ts.map +1 -1
- package/dist/stories/molecules/search-form.stories.d.ts +1 -0
- package/dist/stories/molecules/search-form.stories.d.ts.map +1 -1
- package/dist/stories/overview/component-comparison.stories.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/tokens/components/_icon-button.css +39 -0
- package/src/tokens/components/components.css +1 -0
- 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
|
|
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-
|
|
50
|
-
"text-
|
|
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-
|
|
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
|
|
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",
|
|
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,
|
|
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(
|
|
224
|
-
|
|
225
|
-
size: "
|
|
226
|
-
|
|
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 ??
|
|
236
|
+
size: triggerIconSize ?? "current"
|
|
242
237
|
})
|
|
243
238
|
})
|
|
244
239
|
]
|
package/dist/molecules/dialog.js
CHANGED
|
@@ -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
|
|
27
|
+
"font-dialog-title text-dialog-title text-dialog-title-fg"
|
|
27
28
|
],
|
|
28
29
|
description: [
|
|
29
|
-
"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(
|
|
267
|
+
!hideCloseButton && /*#__PURE__*/ jsx(ActionIcon, {
|
|
273
268
|
className: closeTrigger(),
|
|
274
|
-
|
|
269
|
+
icon: "token-icon-dialog-close",
|
|
270
|
+
size: "md",
|
|
271
|
+
tone: "neutral",
|
|
275
272
|
...api.getCloseTriggerProps(),
|
|
276
|
-
|
|
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
|
|
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
|
|
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:
|
|
259
|
-
onClick:
|
|
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=
|
|
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__*/
|
|
16
|
+
example: ({ size, gapped })=>/*#__PURE__*/ jsx(SearchForm, {
|
|
17
|
+
gapped: gapped,
|
|
16
18
|
size: size,
|
|
17
|
-
children:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"focus-within:
|
|
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
|
-
"
|
|
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
|
-
"
|
|
25
|
+
"relative shrink-0",
|
|
26
|
+
"focus-visible:z-10"
|
|
30
27
|
],
|
|
31
28
|
clearButton: [
|
|
32
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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",
|
|
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
|
-
|
|
171
|
-
|
|
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:
|
|
179
|
-
|
|
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 };
|