@financedistrict/apps-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +119 -0
- package/dist/Radio-BYWz1n7p.d.cts +32 -0
- package/dist/Radio-BYWz1n7p.d.ts +32 -0
- package/dist/SelectTrigger-By0iPBI3.d.cts +78 -0
- package/dist/SelectTrigger-By0iPBI3.d.ts +78 -0
- package/dist/accordion.cjs +322 -0
- package/dist/accordion.d.cts +156 -0
- package/dist/accordion.d.ts +156 -0
- package/dist/accordion.js +2 -0
- package/dist/apps-ui.umd.js +1 -0
- package/dist/button.cjs +366 -0
- package/dist/button.d.cts +52 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +3 -0
- package/dist/checkbox.cjs +74 -0
- package/dist/checkbox.d.cts +63 -0
- package/dist/checkbox.d.ts +63 -0
- package/dist/checkbox.js +2 -0
- package/dist/chunk-3RU7JMFE.js +27 -0
- package/dist/chunk-5TWRK4NW.js +783 -0
- package/dist/chunk-7EOSDFS3.js +9 -0
- package/dist/chunk-AIQ4UOGK.js +142 -0
- package/dist/chunk-B3MMGTPV.js +200 -0
- package/dist/chunk-DAHQYLUI.js +146 -0
- package/dist/chunk-DGBXADSS.js +1 -0
- package/dist/chunk-E6KPNTWW.js +295 -0
- package/dist/chunk-EOAB6G2D.js +149 -0
- package/dist/chunk-FAOVKDVM.js +32 -0
- package/dist/chunk-FV76Z4Z2.js +78 -0
- package/dist/chunk-GLTLBUZ7.js +285 -0
- package/dist/chunk-GTZFV43T.js +154 -0
- package/dist/chunk-HRGZNL4A.js +88 -0
- package/dist/chunk-IEVN3J2F.js +98 -0
- package/dist/chunk-IRS3T6LF.js +352 -0
- package/dist/chunk-IYYHCQXG.js +146 -0
- package/dist/chunk-JIXW3455.js +89 -0
- package/dist/chunk-N4VAP2GB.js +77 -0
- package/dist/chunk-SJ2ZRYVV.js +167 -0
- package/dist/chunk-SNU4YXOR.js +66 -0
- package/dist/chunk-TT6JQATV.js +99 -0
- package/dist/chunk-UPQ3ZPCO.js +66 -0
- package/dist/chunk-UVUSVFOH.js +264 -0
- package/dist/chunk-WJFPGGJB.js +255 -0
- package/dist/chunk-WXEBQA7A.js +371 -0
- package/dist/chunk-Y2O4YYZQ.js +184 -0
- package/dist/cut-corner.cjs +111 -0
- package/dist/cut-corner.d.cts +49 -0
- package/dist/cut-corner.d.ts +49 -0
- package/dist/cut-corner.js +1 -0
- package/dist/dialog.cjs +441 -0
- package/dist/dialog.d.cts +240 -0
- package/dist/dialog.d.ts +240 -0
- package/dist/dialog.js +3 -0
- package/dist/feature-card.cjs +182 -0
- package/dist/feature-card.d.cts +54 -0
- package/dist/feature-card.d.ts +54 -0
- package/dist/feature-card.js +2 -0
- package/dist/filter.cjs +873 -0
- package/dist/filter.d.cts +129 -0
- package/dist/filter.d.ts +129 -0
- package/dist/filter.js +6 -0
- package/dist/index.cjs +4583 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +27 -0
- package/dist/inline-select.cjs +856 -0
- package/dist/inline-select.d.cts +92 -0
- package/dist/inline-select.d.ts +92 -0
- package/dist/inline-select.js +6 -0
- package/dist/input.cjs +263 -0
- package/dist/input.d.cts +157 -0
- package/dist/input.d.ts +157 -0
- package/dist/input.js +4 -0
- package/dist/panel.cjs +185 -0
- package/dist/panel.d.cts +103 -0
- package/dist/panel.d.ts +103 -0
- package/dist/panel.js +2 -0
- package/dist/property-grid.cjs +110 -0
- package/dist/property-grid.d.cts +56 -0
- package/dist/property-grid.d.ts +56 -0
- package/dist/property-grid.js +2 -0
- package/dist/radio.cjs +74 -0
- package/dist/radio.d.cts +39 -0
- package/dist/radio.d.ts +39 -0
- package/dist/radio.js +2 -0
- package/dist/scrollbar.cjs +34 -0
- package/dist/scrollbar.d.cts +35 -0
- package/dist/scrollbar.d.ts +35 -0
- package/dist/scrollbar.js +1 -0
- package/dist/search-bar.cjs +408 -0
- package/dist/search-bar.d.cts +42 -0
- package/dist/search-bar.d.ts +42 -0
- package/dist/search-bar.js +4 -0
- package/dist/select.cjs +998 -0
- package/dist/select.d.cts +357 -0
- package/dist/select.d.ts +357 -0
- package/dist/select.js +5 -0
- package/dist/selectable-feature-card.cjs +159 -0
- package/dist/selectable-feature-card.d.cts +74 -0
- package/dist/selectable-feature-card.d.ts +74 -0
- package/dist/selectable-feature-card.js +3 -0
- package/dist/sidebar-item.cjs +175 -0
- package/dist/sidebar-item.d.cts +75 -0
- package/dist/sidebar-item.d.ts +75 -0
- package/dist/sidebar-item.js +2 -0
- package/dist/switch.cjs +86 -0
- package/dist/switch.d.cts +72 -0
- package/dist/switch.d.ts +72 -0
- package/dist/switch.js +2 -0
- package/dist/table-cell.cjs +196 -0
- package/dist/table-cell.d.cts +76 -0
- package/dist/table-cell.d.ts +76 -0
- package/dist/table-cell.js +2 -0
- package/dist/table-title.cjs +34 -0
- package/dist/table-title.d.cts +16 -0
- package/dist/table-title.d.ts +16 -0
- package/dist/table-title.js +2 -0
- package/dist/tabs.cjs +299 -0
- package/dist/tabs.d.cts +116 -0
- package/dist/tabs.d.ts +116 -0
- package/dist/tabs.js +2 -0
- package/dist/toast.cjs +87 -0
- package/dist/toast.d.cts +48 -0
- package/dist/toast.d.ts +48 -0
- package/dist/toast.js +2 -0
- package/dist/token-input.cjs +415 -0
- package/dist/token-input.d.cts +90 -0
- package/dist/token-input.d.ts +90 -0
- package/dist/token-input.js +3 -0
- package/dist/tooltip.cjs +384 -0
- package/dist/tooltip.d.cts +142 -0
- package/dist/tooltip.d.ts +142 -0
- package/dist/tooltip.js +2 -0
- package/package.json +200 -0
- package/src/fonts.css +33 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { cn } from './chunk-7EOSDFS3.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { MinusIcon, PlusIcon } from '@phosphor-icons/react';
|
|
4
|
+
import { cva } from 'class-variance-authority';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var accordionRootVariants = cva(["flex flex-col"]);
|
|
8
|
+
var accordionItemVariants = cva(["border-b border-card-border"], {
|
|
9
|
+
variants: {
|
|
10
|
+
disabled: {
|
|
11
|
+
true: "opacity-60",
|
|
12
|
+
false: ""
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
disabled: false
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
var accordionTriggerVariants = cva(
|
|
20
|
+
[
|
|
21
|
+
"group flex w-full cursor-pointer items-start gap-4 pt-3 text-left",
|
|
22
|
+
"outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
23
|
+
"disabled:cursor-not-allowed"
|
|
24
|
+
],
|
|
25
|
+
{
|
|
26
|
+
variants: {
|
|
27
|
+
open: {
|
|
28
|
+
true: "pb-2",
|
|
29
|
+
false: "pb-3"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
open: false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
var accordionTriggerTitleVariants = cva([
|
|
38
|
+
"body-02-medium min-w-0 flex-1 break-words text-card-foreground"
|
|
39
|
+
]);
|
|
40
|
+
var accordionTriggerIconVariants = cva([
|
|
41
|
+
"flex shrink-0 items-center text-card-foreground-muted transition-colors",
|
|
42
|
+
"group-hover:text-card-foreground",
|
|
43
|
+
"[&_svg]:size-4 [&_svg]:shrink-0"
|
|
44
|
+
]);
|
|
45
|
+
var accordionContentVariants = cva([
|
|
46
|
+
"body-03 pb-3 text-card-foreground-muted"
|
|
47
|
+
]);
|
|
48
|
+
var AccordionContext = React.createContext(
|
|
49
|
+
null
|
|
50
|
+
);
|
|
51
|
+
function useAccordionContext(part) {
|
|
52
|
+
const context = React.useContext(AccordionContext);
|
|
53
|
+
if (context === null) {
|
|
54
|
+
throw new Error(`${part} must be used within <Accordion.Root>`);
|
|
55
|
+
}
|
|
56
|
+
return context;
|
|
57
|
+
}
|
|
58
|
+
var AccordionItemContext = React.createContext(null);
|
|
59
|
+
function useAccordionItemContext(part) {
|
|
60
|
+
const context = React.useContext(AccordionItemContext);
|
|
61
|
+
if (context === null) {
|
|
62
|
+
throw new Error(`${part} must be used within <Accordion.Item>`);
|
|
63
|
+
}
|
|
64
|
+
return context;
|
|
65
|
+
}
|
|
66
|
+
function setRef(ref, node) {
|
|
67
|
+
if (typeof ref === "function") ref(node);
|
|
68
|
+
else if (ref) ref.current = node;
|
|
69
|
+
}
|
|
70
|
+
function AccordionRoot({
|
|
71
|
+
type = "single",
|
|
72
|
+
value: valueProp,
|
|
73
|
+
defaultValue,
|
|
74
|
+
onValueChange,
|
|
75
|
+
collapsible = true,
|
|
76
|
+
className,
|
|
77
|
+
children,
|
|
78
|
+
ref,
|
|
79
|
+
...props
|
|
80
|
+
}) {
|
|
81
|
+
const isMultiple = type === "multiple";
|
|
82
|
+
const isControlled = valueProp !== void 0;
|
|
83
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(
|
|
84
|
+
() => defaultValue !== void 0 ? defaultValue : isMultiple ? [] : null
|
|
85
|
+
);
|
|
86
|
+
const openModel = isControlled ? valueProp : uncontrolledValue;
|
|
87
|
+
const commit = (next) => {
|
|
88
|
+
if (!isControlled) setUncontrolledValue(next);
|
|
89
|
+
onValueChange?.(next);
|
|
90
|
+
};
|
|
91
|
+
const isOpen = (itemValue) => isMultiple ? Array.isArray(openModel) && openModel.includes(itemValue) : openModel === itemValue;
|
|
92
|
+
const toggle = (itemValue) => {
|
|
93
|
+
if (isMultiple) {
|
|
94
|
+
const current2 = Array.isArray(openModel) ? openModel : [];
|
|
95
|
+
commit(
|
|
96
|
+
current2.includes(itemValue) ? current2.filter((v) => v !== itemValue) : [...current2, itemValue]
|
|
97
|
+
);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const current = Array.isArray(openModel) ? null : openModel;
|
|
101
|
+
if (current === itemValue) {
|
|
102
|
+
commit(collapsible ? null : itemValue);
|
|
103
|
+
} else {
|
|
104
|
+
commit(itemValue);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const triggersRef = React.useRef(/* @__PURE__ */ new Set());
|
|
108
|
+
const registerTrigger = React.useCallback((node) => {
|
|
109
|
+
triggersRef.current.add(node);
|
|
110
|
+
}, []);
|
|
111
|
+
const unregisterTrigger = React.useCallback((node) => {
|
|
112
|
+
triggersRef.current.delete(node);
|
|
113
|
+
}, []);
|
|
114
|
+
const onTriggerKeyDown = (event) => {
|
|
115
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
116
|
+
const triggers = Array.from(triggersRef.current).filter((el) => !el.disabled).sort(
|
|
117
|
+
(a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
|
|
118
|
+
);
|
|
119
|
+
if (triggers.length === 0) return;
|
|
120
|
+
const currentIndex = triggers.indexOf(event.currentTarget);
|
|
121
|
+
if (currentIndex === -1) return;
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
let nextIndex = currentIndex;
|
|
124
|
+
switch (event.key) {
|
|
125
|
+
case "ArrowDown":
|
|
126
|
+
nextIndex = (currentIndex + 1) % triggers.length;
|
|
127
|
+
break;
|
|
128
|
+
case "ArrowUp":
|
|
129
|
+
nextIndex = (currentIndex - 1 + triggers.length) % triggers.length;
|
|
130
|
+
break;
|
|
131
|
+
case "Home":
|
|
132
|
+
nextIndex = 0;
|
|
133
|
+
break;
|
|
134
|
+
case "End":
|
|
135
|
+
nextIndex = triggers.length - 1;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
triggers[nextIndex]?.focus();
|
|
139
|
+
};
|
|
140
|
+
const contextValue = {
|
|
141
|
+
type,
|
|
142
|
+
isOpen,
|
|
143
|
+
toggle,
|
|
144
|
+
registerTrigger,
|
|
145
|
+
unregisterTrigger,
|
|
146
|
+
onTriggerKeyDown
|
|
147
|
+
};
|
|
148
|
+
return /* @__PURE__ */ jsx(AccordionContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
149
|
+
"div",
|
|
150
|
+
{
|
|
151
|
+
ref,
|
|
152
|
+
"data-slot": "accordion",
|
|
153
|
+
className: cn(accordionRootVariants(), className),
|
|
154
|
+
...props,
|
|
155
|
+
children
|
|
156
|
+
}
|
|
157
|
+
) });
|
|
158
|
+
}
|
|
159
|
+
function AccordionItem({
|
|
160
|
+
value,
|
|
161
|
+
disabled = false,
|
|
162
|
+
className,
|
|
163
|
+
children,
|
|
164
|
+
ref,
|
|
165
|
+
...props
|
|
166
|
+
}) {
|
|
167
|
+
const { isOpen } = useAccordionContext("Accordion.Item");
|
|
168
|
+
const open = isOpen(value);
|
|
169
|
+
const reactId = React.useId();
|
|
170
|
+
const triggerId = `${reactId}-trigger`;
|
|
171
|
+
const contentId = `${reactId}-content`;
|
|
172
|
+
const itemContext = {
|
|
173
|
+
value,
|
|
174
|
+
open,
|
|
175
|
+
disabled,
|
|
176
|
+
triggerId,
|
|
177
|
+
contentId
|
|
178
|
+
};
|
|
179
|
+
return /* @__PURE__ */ jsx(AccordionItemContext.Provider, { value: itemContext, children: /* @__PURE__ */ jsx(
|
|
180
|
+
"div",
|
|
181
|
+
{
|
|
182
|
+
ref,
|
|
183
|
+
"data-slot": "accordion-item",
|
|
184
|
+
"data-state": open ? "open" : "closed",
|
|
185
|
+
"data-disabled": disabled || void 0,
|
|
186
|
+
className: cn(accordionItemVariants({ disabled }), className),
|
|
187
|
+
...props,
|
|
188
|
+
children
|
|
189
|
+
}
|
|
190
|
+
) });
|
|
191
|
+
}
|
|
192
|
+
function AccordionTrigger({
|
|
193
|
+
className,
|
|
194
|
+
children,
|
|
195
|
+
onClick,
|
|
196
|
+
onKeyDown,
|
|
197
|
+
ref,
|
|
198
|
+
...props
|
|
199
|
+
}) {
|
|
200
|
+
const { toggle, registerTrigger, unregisterTrigger, onTriggerKeyDown } = useAccordionContext("Accordion.Trigger");
|
|
201
|
+
const { value, open, disabled, triggerId, contentId } = useAccordionItemContext("Accordion.Trigger");
|
|
202
|
+
const buttonRef = React.useRef(null);
|
|
203
|
+
const setButtonRef = (node) => {
|
|
204
|
+
buttonRef.current = node;
|
|
205
|
+
setRef(ref, node);
|
|
206
|
+
};
|
|
207
|
+
React.useEffect(() => {
|
|
208
|
+
const node = buttonRef.current;
|
|
209
|
+
if (!node) return;
|
|
210
|
+
registerTrigger(node);
|
|
211
|
+
return () => unregisterTrigger(node);
|
|
212
|
+
}, [registerTrigger, unregisterTrigger]);
|
|
213
|
+
return /* @__PURE__ */ jsxs(
|
|
214
|
+
"button",
|
|
215
|
+
{
|
|
216
|
+
...props,
|
|
217
|
+
ref: setButtonRef,
|
|
218
|
+
type: "button",
|
|
219
|
+
id: triggerId,
|
|
220
|
+
"data-slot": "accordion-trigger",
|
|
221
|
+
"aria-expanded": open,
|
|
222
|
+
"aria-controls": open ? contentId : void 0,
|
|
223
|
+
disabled,
|
|
224
|
+
className: cn(accordionTriggerVariants({ open }), className),
|
|
225
|
+
onClick: (event) => {
|
|
226
|
+
onClick?.(event);
|
|
227
|
+
if (!event.defaultPrevented) toggle(value);
|
|
228
|
+
},
|
|
229
|
+
onKeyDown: (event) => {
|
|
230
|
+
onKeyDown?.(event);
|
|
231
|
+
if (!event.defaultPrevented) onTriggerKeyDown(event);
|
|
232
|
+
},
|
|
233
|
+
children: [
|
|
234
|
+
/* @__PURE__ */ jsx(
|
|
235
|
+
"span",
|
|
236
|
+
{
|
|
237
|
+
"data-slot": "accordion-trigger-title",
|
|
238
|
+
className: accordionTriggerTitleVariants(),
|
|
239
|
+
children
|
|
240
|
+
}
|
|
241
|
+
),
|
|
242
|
+
/* @__PURE__ */ jsx(
|
|
243
|
+
"span",
|
|
244
|
+
{
|
|
245
|
+
"data-slot": "accordion-trigger-icon",
|
|
246
|
+
"aria-hidden": "true",
|
|
247
|
+
className: accordionTriggerIconVariants(),
|
|
248
|
+
children: open ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(PlusIcon, {})
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
function AccordionContent({
|
|
256
|
+
className,
|
|
257
|
+
children,
|
|
258
|
+
ref,
|
|
259
|
+
...props
|
|
260
|
+
}) {
|
|
261
|
+
const { open, triggerId, contentId } = useAccordionItemContext("Accordion.Content");
|
|
262
|
+
if (!open) return null;
|
|
263
|
+
return /* @__PURE__ */ jsx(
|
|
264
|
+
"div",
|
|
265
|
+
{
|
|
266
|
+
ref,
|
|
267
|
+
role: "region",
|
|
268
|
+
id: contentId,
|
|
269
|
+
"aria-labelledby": triggerId,
|
|
270
|
+
"data-slot": "accordion-content",
|
|
271
|
+
"data-state": "open",
|
|
272
|
+
className: cn(accordionContentVariants(), className),
|
|
273
|
+
...props,
|
|
274
|
+
children
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
var Accordion = {
|
|
279
|
+
Root: AccordionRoot,
|
|
280
|
+
Item: AccordionItem,
|
|
281
|
+
Trigger: AccordionTrigger,
|
|
282
|
+
Content: AccordionContent
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger, accordionContentVariants, accordionItemVariants, accordionRootVariants, accordionTriggerIconVariants, accordionTriggerTitleVariants, accordionTriggerVariants };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Input } from './chunk-AIQ4UOGK.js';
|
|
2
|
+
import { cn } from './chunk-7EOSDFS3.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { XIcon, MagnifyingGlassIcon } from '@phosphor-icons/react';
|
|
5
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function useDebouncedCallback(fn, delayMs) {
|
|
8
|
+
const fnRef = React.useRef(fn);
|
|
9
|
+
const delayRef = React.useRef(delayMs);
|
|
10
|
+
fnRef.current = fn;
|
|
11
|
+
delayRef.current = delayMs;
|
|
12
|
+
const timerRef = React.useRef(null);
|
|
13
|
+
const debounced = React.useMemo(() => {
|
|
14
|
+
const clear = () => {
|
|
15
|
+
if (timerRef.current !== null) {
|
|
16
|
+
clearTimeout(timerRef.current);
|
|
17
|
+
timerRef.current = null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const call = (...args) => {
|
|
21
|
+
clear();
|
|
22
|
+
timerRef.current = setTimeout(() => {
|
|
23
|
+
timerRef.current = null;
|
|
24
|
+
fnRef.current(...args);
|
|
25
|
+
}, delayRef.current);
|
|
26
|
+
};
|
|
27
|
+
call.cancel = clear;
|
|
28
|
+
call.flush = (...args) => {
|
|
29
|
+
clear();
|
|
30
|
+
fnRef.current(...args);
|
|
31
|
+
};
|
|
32
|
+
return call;
|
|
33
|
+
}, []);
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
return () => {
|
|
36
|
+
if (timerRef.current !== null) {
|
|
37
|
+
clearTimeout(timerRef.current);
|
|
38
|
+
timerRef.current = null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
return debounced;
|
|
43
|
+
}
|
|
44
|
+
var DEFAULT_DEBOUNCE_MS = 300;
|
|
45
|
+
function SearchBar({
|
|
46
|
+
onSearch,
|
|
47
|
+
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
48
|
+
hint,
|
|
49
|
+
wrapperClassName,
|
|
50
|
+
fieldClassName,
|
|
51
|
+
className,
|
|
52
|
+
disabled,
|
|
53
|
+
value,
|
|
54
|
+
defaultValue,
|
|
55
|
+
onChange,
|
|
56
|
+
onKeyDown,
|
|
57
|
+
ref,
|
|
58
|
+
...props
|
|
59
|
+
}) {
|
|
60
|
+
const isControlled = value !== void 0;
|
|
61
|
+
const innerRef = React.useRef(null);
|
|
62
|
+
const setInputRef = React.useCallback(
|
|
63
|
+
(node) => {
|
|
64
|
+
innerRef.current = node;
|
|
65
|
+
if (typeof ref === "function") ref(node);
|
|
66
|
+
else if (ref)
|
|
67
|
+
ref.current = node;
|
|
68
|
+
},
|
|
69
|
+
[ref]
|
|
70
|
+
);
|
|
71
|
+
const [hasValueState, setHasValueState] = React.useState(
|
|
72
|
+
() => String(value ?? defaultValue ?? "").length > 0
|
|
73
|
+
);
|
|
74
|
+
const hasValue = isControlled ? String(value ?? "").length > 0 : hasValueState;
|
|
75
|
+
const debouncedSearch = useDebouncedCallback((next) => {
|
|
76
|
+
onSearch?.(next);
|
|
77
|
+
}, debounceMs);
|
|
78
|
+
const handleChange = (event) => {
|
|
79
|
+
onChange?.(event);
|
|
80
|
+
if (!isControlled) setHasValueState(event.currentTarget.value.length > 0);
|
|
81
|
+
debouncedSearch(event.currentTarget.value);
|
|
82
|
+
};
|
|
83
|
+
const handleKeyDown = (event) => {
|
|
84
|
+
onKeyDown?.(event);
|
|
85
|
+
if (event.key === "Enter") {
|
|
86
|
+
debouncedSearch.flush(event.currentTarget.value);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const handleClear = () => {
|
|
90
|
+
const input = innerRef.current;
|
|
91
|
+
if (!input) return;
|
|
92
|
+
const setter = Object.getOwnPropertyDescriptor(
|
|
93
|
+
window.HTMLInputElement.prototype,
|
|
94
|
+
"value"
|
|
95
|
+
)?.set;
|
|
96
|
+
setter?.call(input, "");
|
|
97
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
98
|
+
if (!isControlled) setHasValueState(false);
|
|
99
|
+
debouncedSearch.cancel();
|
|
100
|
+
onSearch?.("");
|
|
101
|
+
input.focus();
|
|
102
|
+
};
|
|
103
|
+
const showClear = hasValue && !disabled;
|
|
104
|
+
return /* @__PURE__ */ jsx(
|
|
105
|
+
Input,
|
|
106
|
+
{
|
|
107
|
+
type: "search",
|
|
108
|
+
variation: "field",
|
|
109
|
+
size: "lg",
|
|
110
|
+
ref: setInputRef,
|
|
111
|
+
disabled,
|
|
112
|
+
hint,
|
|
113
|
+
value,
|
|
114
|
+
defaultValue,
|
|
115
|
+
onChange: handleChange,
|
|
116
|
+
onKeyDown: handleKeyDown,
|
|
117
|
+
className: cn(
|
|
118
|
+
// Suppress the browser's native `type=search` clear affordance
|
|
119
|
+
// (WebKit/Chrome render `::-webkit-search-cancel-button`) — SearchBar
|
|
120
|
+
// provides its own clear button, so the native one would show a second
|
|
121
|
+
// "X". `hidden` (display:none) is used rather than `appearance-none`:
|
|
122
|
+
// that pseudo-element is governed by `-webkit-appearance`, which the
|
|
123
|
+
// unprefixed `appearance:none` Tailwind emits does not affect.
|
|
124
|
+
// Caller `className` is merged after and can still override.
|
|
125
|
+
"[&::-webkit-search-cancel-button]:hidden",
|
|
126
|
+
className
|
|
127
|
+
),
|
|
128
|
+
wrapperClassName,
|
|
129
|
+
fieldClassName,
|
|
130
|
+
leftSlot: /* @__PURE__ */ jsx(MagnifyingGlassIcon, { weight: "regular" }),
|
|
131
|
+
rightSlot: showClear ? /* @__PURE__ */ jsx(
|
|
132
|
+
"button",
|
|
133
|
+
{
|
|
134
|
+
type: "button",
|
|
135
|
+
"aria-label": "Clear search",
|
|
136
|
+
onClick: handleClear,
|
|
137
|
+
"data-slot": "search-clear",
|
|
138
|
+
className: cn(
|
|
139
|
+
// Reset native button chrome; inherit the field's icon colour.
|
|
140
|
+
"inline-flex cursor-pointer items-center justify-center rounded-xs bg-transparent p-0 text-current",
|
|
141
|
+
// Standard ADR-0010 system focus ring (this button is a momentary
|
|
142
|
+
// control — the ADR-0011 no-ring exception applies only to the
|
|
143
|
+
// text input itself, NOT to this button).
|
|
144
|
+
"outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-focus"
|
|
145
|
+
),
|
|
146
|
+
children: /* @__PURE__ */ jsx(XIcon, { weight: "regular", "aria-hidden": "true" })
|
|
147
|
+
}
|
|
148
|
+
) : void 0,
|
|
149
|
+
...props
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export { SearchBar };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Radio } from './chunk-UPQ3ZPCO.js';
|
|
2
|
+
import { cn } from './chunk-7EOSDFS3.js';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var selectableFeatureCardVariants = cva([
|
|
7
|
+
"group overflow-hidden rounded-md border border-border bg-card-background",
|
|
8
|
+
"has-[:checked]:border-brand-primary-border",
|
|
9
|
+
"has-[:disabled]:opacity-60"
|
|
10
|
+
]);
|
|
11
|
+
var selectableFeatureCardRowVariants = cva(
|
|
12
|
+
[
|
|
13
|
+
"flex h-16 items-center gap-3 bg-card-background px-4 py-3",
|
|
14
|
+
"cursor-pointer has-[:disabled]:cursor-not-allowed"
|
|
15
|
+
],
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
editable: {
|
|
19
|
+
true: "group-has-[:checked]:border-b group-has-[:checked]:border-border",
|
|
20
|
+
false: ""
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: { editable: false }
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
var selectableFeatureCardTextVariants = cva(
|
|
27
|
+
"flex min-w-0 flex-1 flex-col"
|
|
28
|
+
);
|
|
29
|
+
var selectableFeatureCardTrailingVariants = cva(
|
|
30
|
+
"flex shrink-0 items-center text-card-foreground-muted [&_svg]:size-6 [&_svg]:shrink-0"
|
|
31
|
+
);
|
|
32
|
+
var selectableFeatureCardBottomVariants = cva(
|
|
33
|
+
"hidden bg-card-accent p-4 group-has-[:checked]:block"
|
|
34
|
+
);
|
|
35
|
+
function SelectableFeatureCard({
|
|
36
|
+
title,
|
|
37
|
+
subtitle,
|
|
38
|
+
trailing,
|
|
39
|
+
variation = "basic",
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
ref,
|
|
43
|
+
...radioProps
|
|
44
|
+
}) {
|
|
45
|
+
const editable = variation === "editable";
|
|
46
|
+
return /* @__PURE__ */ jsxs(
|
|
47
|
+
"div",
|
|
48
|
+
{
|
|
49
|
+
"data-slot": "selectable-feature-card",
|
|
50
|
+
className: cn(selectableFeatureCardVariants(), className),
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsxs("label", { className: selectableFeatureCardRowVariants({ editable }), children: [
|
|
53
|
+
/* @__PURE__ */ jsx(Radio, { ref, ...radioProps }),
|
|
54
|
+
/* @__PURE__ */ jsxs(
|
|
55
|
+
"span",
|
|
56
|
+
{
|
|
57
|
+
"data-slot": "selectable-feature-card-text",
|
|
58
|
+
className: selectableFeatureCardTextVariants(),
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsx("span", { className: "body-02-medium truncate text-card-foreground", children: title }),
|
|
61
|
+
subtitle != null && /* @__PURE__ */ jsx("span", { className: "body-03 truncate text-card-foreground-muted", children: subtitle })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
trailing != null && /* @__PURE__ */ jsx(
|
|
66
|
+
"span",
|
|
67
|
+
{
|
|
68
|
+
"data-slot": "selectable-feature-card-trailing",
|
|
69
|
+
"aria-hidden": "true",
|
|
70
|
+
className: selectableFeatureCardTrailingVariants(),
|
|
71
|
+
children: trailing
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
] }),
|
|
75
|
+
editable && /* @__PURE__ */ jsx(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
"data-slot": "selectable-feature-card-bottom",
|
|
79
|
+
className: selectableFeatureCardBottomVariants(),
|
|
80
|
+
children
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { SelectableFeatureCard, selectableFeatureCardBottomVariants, selectableFeatureCardRowVariants, selectableFeatureCardTextVariants, selectableFeatureCardTrailingVariants, selectableFeatureCardVariants };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
// src/input/inputVariants.ts
|
|
4
|
+
var inputVariants = cva(
|
|
5
|
+
[
|
|
6
|
+
// Field row layout — icons + input on one line, vertically centred.
|
|
7
|
+
"flex w-full items-center",
|
|
8
|
+
// Colour transitions for the focus border change.
|
|
9
|
+
"transition-colors",
|
|
10
|
+
// I-beam text cursor over the WHOLE field row (not just the inner input),
|
|
11
|
+
// so clicking anywhere in the padding/icon area reads as "type here". This
|
|
12
|
+
// is the only hover affordance — hover intentionally does NOT recolour the
|
|
13
|
+
// border (user-authorized deviation from the Figma). `cursor-text` sits in
|
|
14
|
+
// the base; `has-[:disabled]:cursor-not-allowed` below is declared AFTER it
|
|
15
|
+
// so it wins on a disabled field (same-specificity utilities, later class
|
|
16
|
+
// takes precedence — confirmed by tailwind-merge ordering).
|
|
17
|
+
"cursor-text",
|
|
18
|
+
// Disabled: not-allowed cursor + block pointer interaction on the whole
|
|
19
|
+
// field (the native `disabled` on the input already stops typing; icons are
|
|
20
|
+
// dimmed separately by the `disabled`-derived `iconStateClass` in <Input>).
|
|
21
|
+
// Declared after `cursor-text` so it overrides the I-beam when disabled.
|
|
22
|
+
"has-[:disabled]:cursor-not-allowed"
|
|
23
|
+
],
|
|
24
|
+
{
|
|
25
|
+
variants: {
|
|
26
|
+
variation: {
|
|
27
|
+
field: [
|
|
28
|
+
"bg-input-background border border-input-border rounded-sm",
|
|
29
|
+
// Focus (mouse or keyboard) strengthens the whole border to the
|
|
30
|
+
// accent. Hover does NOT change colour (cursor-only affordance).
|
|
31
|
+
"focus-within:border-input-border-accent"
|
|
32
|
+
],
|
|
33
|
+
line: [
|
|
34
|
+
"border-b border-input-border",
|
|
35
|
+
// Focus (mouse or keyboard) strengthens the bottom line to the
|
|
36
|
+
// accent. Hover does NOT change colour (cursor-only affordance).
|
|
37
|
+
"focus-within:border-input-border-accent"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
size: {
|
|
41
|
+
// sm/lg heights reuse the Button scale; gap follows Figma per size.
|
|
42
|
+
sm: "h-8 gap-1",
|
|
43
|
+
lg: "h-10 gap-2"
|
|
44
|
+
},
|
|
45
|
+
error: {
|
|
46
|
+
// Error out-ranks focus: declare the destructive border for the resting
|
|
47
|
+
// state AND its `focus-within:` form so an errored field never recolours
|
|
48
|
+
// to the accent when focused (a bare `border-…` alone would lose to the
|
|
49
|
+
// variation's `focus-within:` accent utility above). No `hover:` form is
|
|
50
|
+
// needed — hover no longer changes colour on any variation. The `error`
|
|
51
|
+
// variant's classes are emitted after the variation's, so at equal
|
|
52
|
+
// specificity the destructive `focus-within:` wins by order.
|
|
53
|
+
true: "border-input-destructive-foreground-accent focus-within:border-input-destructive-foreground-accent",
|
|
54
|
+
false: ""
|
|
55
|
+
},
|
|
56
|
+
// Bottom-line visibility toggle. LINE ONLY — the hide effect is applied
|
|
57
|
+
// via a compound variant below (scoped to `variation:line`), so `field`
|
|
58
|
+
// is unaffected. Boolean → `true`/`false`; `true` (default) keeps today's
|
|
59
|
+
// behaviour. No classes here: `true` is the resting `border-input-border`
|
|
60
|
+
// from the variation, and `false`'s hide is the compound variant.
|
|
61
|
+
showBottomLine: {
|
|
62
|
+
true: "",
|
|
63
|
+
false: ""
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
compoundVariants: [
|
|
67
|
+
// field padding — Figma field-lg = px-3 (12px) / py-2 (8px).
|
|
68
|
+
{ variation: "field", size: "lg", className: "px-3 py-2" },
|
|
69
|
+
// field-sm: DERIVED. px-2 (8px = spacing/2) steps down from field-lg's
|
|
70
|
+
// px-3 (12px) and equals py-2 for a uniform spacing/2 inset all around.
|
|
71
|
+
{ variation: "field", size: "sm", className: "px-2 py-2" },
|
|
72
|
+
// line — no horizontal padding (Figma px-0); vertical padding per size.
|
|
73
|
+
{ variation: "line", size: "lg", className: "py-2" },
|
|
74
|
+
{ variation: "line", size: "sm", className: "py-2.5" },
|
|
75
|
+
// Hide the resting bottom line (LINE ONLY): recolour to transparent while
|
|
76
|
+
// keeping `border-b` for a stable 1px width (no layout shift). Gated on
|
|
77
|
+
// `error:false` so an errored field keeps its destructive rest border
|
|
78
|
+
// (transparent would otherwise last-win over it). Focus still wins:
|
|
79
|
+
// `focus-within:border-input-border-accent` out-ranks `border-transparent`
|
|
80
|
+
// (later in class order), so the line reappears on focus. `field` is
|
|
81
|
+
// untouched — this compound only matches `variation:line`.
|
|
82
|
+
{
|
|
83
|
+
variation: "line",
|
|
84
|
+
showBottomLine: false,
|
|
85
|
+
error: false,
|
|
86
|
+
className: "border-transparent"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
defaultVariants: {
|
|
90
|
+
variation: "field",
|
|
91
|
+
size: "lg",
|
|
92
|
+
error: false,
|
|
93
|
+
showBottomLine: true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
export { inputVariants };
|