@fanvue/ui 1.17.3 → 1.18.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/cjs/components/Accordion/Accordion.cjs +29 -0
- package/dist/cjs/components/Accordion/Accordion.cjs.map +1 -0
- package/dist/cjs/components/Accordion/AccordionContent.cjs +44 -0
- package/dist/cjs/components/Accordion/AccordionContent.cjs.map +1 -0
- package/dist/cjs/components/Accordion/AccordionItem.cjs +40 -0
- package/dist/cjs/components/Accordion/AccordionItem.cjs.map +1 -0
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs +55 -0
- package/dist/cjs/components/Accordion/AccordionTrigger.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs +239 -0
- package/dist/cjs/components/Autocomplete/Autocomplete.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs +52 -0
- package/dist/cjs/components/Autocomplete/AutocompleteDropdownContent.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs +53 -0
- package/dist/cjs/components/Autocomplete/AutocompleteOptionItem.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs +36 -0
- package/dist/cjs/components/Autocomplete/AutocompleteTag.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/constants.cjs +15 -0
- package/dist/cjs/components/Autocomplete/constants.cjs.map +1 -0
- package/dist/cjs/components/Autocomplete/useAutocomplete.cjs +284 -0
- package/dist/cjs/components/Autocomplete/useAutocomplete.cjs.map +1 -0
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs +68 -0
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs.map +1 -0
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs +79 -0
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs.map +1 -0
- package/dist/cjs/components/Chip/Chip.cjs +1 -8
- package/dist/cjs/components/Chip/Chip.cjs.map +1 -1
- package/dist/cjs/components/Dialog/Dialog.cjs +170 -0
- package/dist/cjs/components/Dialog/Dialog.cjs.map +1 -0
- package/dist/cjs/components/Drawer/Drawer.cjs +151 -0
- package/dist/cjs/components/Drawer/Drawer.cjs.map +1 -0
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs +136 -0
- package/dist/cjs/components/MobileStepper/MobileStepper.cjs.map +1 -0
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs +2 -0
- package/dist/cjs/components/ProgressBar/ProgressBar.cjs.map +1 -1
- package/dist/cjs/index.cjs +37 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/Accordion/Accordion.mjs +11 -0
- package/dist/components/Accordion/Accordion.mjs.map +1 -0
- package/dist/components/Accordion/AccordionContent.mjs +26 -0
- package/dist/components/Accordion/AccordionContent.mjs.map +1 -0
- package/dist/components/Accordion/AccordionItem.mjs +22 -0
- package/dist/components/Accordion/AccordionItem.mjs.map +1 -0
- package/dist/components/Accordion/AccordionTrigger.mjs +37 -0
- package/dist/components/Accordion/AccordionTrigger.mjs.map +1 -0
- package/dist/components/Autocomplete/Autocomplete.mjs +221 -0
- package/dist/components/Autocomplete/Autocomplete.mjs.map +1 -0
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs +52 -0
- package/dist/components/Autocomplete/AutocompleteDropdownContent.mjs.map +1 -0
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs +53 -0
- package/dist/components/Autocomplete/AutocompleteOptionItem.mjs.map +1 -0
- package/dist/components/Autocomplete/AutocompleteTag.mjs +36 -0
- package/dist/components/Autocomplete/AutocompleteTag.mjs.map +1 -0
- package/dist/components/Autocomplete/constants.mjs +15 -0
- package/dist/components/Autocomplete/constants.mjs.map +1 -0
- package/dist/components/Autocomplete/useAutocomplete.mjs +267 -0
- package/dist/components/Autocomplete/useAutocomplete.mjs.map +1 -0
- package/dist/components/BottomNavigation/BottomNavigation.mjs +51 -0
- package/dist/components/BottomNavigation/BottomNavigation.mjs.map +1 -0
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs +62 -0
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs.map +1 -0
- package/dist/components/Chip/Chip.mjs +1 -8
- package/dist/components/Chip/Chip.mjs.map +1 -1
- package/dist/components/Dialog/Dialog.mjs +152 -0
- package/dist/components/Dialog/Dialog.mjs.map +1 -0
- package/dist/components/Drawer/Drawer.mjs +133 -0
- package/dist/components/Drawer/Drawer.mjs.map +1 -0
- package/dist/components/MobileStepper/MobileStepper.mjs +119 -0
- package/dist/components/MobileStepper/MobileStepper.mjs.map +1 -0
- package/dist/components/ProgressBar/ProgressBar.mjs +2 -0
- package/dist/components/ProgressBar/ProgressBar.mjs.map +1 -1
- package/dist/index.d.ts +510 -0
- package/dist/index.mjs +37 -0
- package/dist/index.mjs.map +1 -1
- package/dist/styles/theme.css +26 -0
- package/package.json +5 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AutocompleteTag.cjs","sources":["../../../../src/components/Autocomplete/AutocompleteTag.tsx"],"sourcesContent":["import type * as React from \"react\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport type { AutocompleteOption } from \"./Autocomplete\";\nimport { getLabel } from \"./constants\";\n\nexport function AutocompleteTag({\n option,\n disabled,\n onRemove,\n renderTag,\n}: {\n option: AutocompleteOption;\n disabled: boolean;\n onRemove: () => void;\n renderTag?: (option: AutocompleteOption, onRemove: () => void) => React.ReactNode;\n}) {\n if (renderTag) {\n return <span>{renderTag(option, onRemove)}</span>;\n }\n\n return (\n <span className=\"typography-regular-body-sm inline-flex max-w-full items-center gap-1 rounded-md bg-neutral-200 px-2 py-0.5 text-foreground-default\">\n <span className=\"truncate\">{getLabel(option)}</span>\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={`Remove ${getLabel(option)}`}\n className=\"flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm text-foreground-secondary hover:text-foreground-default active:scale-95\"\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n disabled={disabled}\n >\n <CloseIcon className=\"size-3\" />\n </button>\n </span>\n );\n}\n"],"names":["jsx","jsxs","getLabel","CloseIcon"],"mappings":";;;;;;AAKO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,MAAI,WAAW;AACb,WAAOA,2BAAAA,IAAC,QAAA,EAAM,UAAA,UAAU,QAAQ,QAAQ,GAAE;AAAA,EAC5C;AAEA,SACEC,2BAAAA,KAAC,QAAA,EAAK,WAAU,sIACd,UAAA;AAAA,IAAAD,+BAAC,QAAA,EAAK,WAAU,YAAY,UAAAE,UAAAA,SAAS,MAAM,GAAE;AAAA,IAC7CF,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,cAAY,UAAUE,mBAAS,MAAM,CAAC;AAAA,QACtC,WAAU;AAAA,QACV,SAAS,CAAC,MAAM;AACd,YAAE,gBAAA;AACF,mBAAA;AAAA,QACF;AAAA,QACA;AAAA,QAEA,UAAAF,2BAAAA,IAACG,UAAAA,WAAA,EAAU,WAAU,SAAA,CAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EAChC,GACF;AAEJ;;"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const CREATE_PREFIX = "__create__";
|
|
5
|
+
function defaultFilter(option, query) {
|
|
6
|
+
const label = option.label ?? option.value;
|
|
7
|
+
return label.toLowerCase().includes(query.toLowerCase());
|
|
8
|
+
}
|
|
9
|
+
function getLabel(option) {
|
|
10
|
+
return option.label ?? option.value;
|
|
11
|
+
}
|
|
12
|
+
exports.CREATE_PREFIX = CREATE_PREFIX;
|
|
13
|
+
exports.defaultFilter = defaultFilter;
|
|
14
|
+
exports.getLabel = getLabel;
|
|
15
|
+
//# sourceMappingURL=constants.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.cjs","sources":["../../../../src/components/Autocomplete/constants.ts"],"sourcesContent":["import type { AutocompleteOption } from \"./Autocomplete\";\n\nexport const CREATE_PREFIX = \"__create__\";\n\nexport function defaultFilter(option: AutocompleteOption, query: string): boolean {\n const label = option.label ?? option.value;\n return label.toLowerCase().includes(query.toLowerCase());\n}\n\nexport function getLabel(option: AutocompleteOption): string {\n return option.label ?? option.value;\n}\n"],"names":[],"mappings":";;;AAEO,MAAM,gBAAgB;AAEtB,SAAS,cAAc,QAA4B,OAAwB;AAChF,QAAM,QAAQ,OAAO,SAAS,OAAO;AACrC,SAAO,MAAM,YAAA,EAAc,SAAS,MAAM,aAAa;AACzD;AAEO,SAAS,SAAS,QAAoC;AAC3D,SAAO,OAAO,SAAS,OAAO;AAChC;;;;"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const constants = require("./constants.cjs");
|
|
6
|
+
function _interopNamespaceDefault(e) {
|
|
7
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
8
|
+
if (e) {
|
|
9
|
+
for (const k in e) {
|
|
10
|
+
if (k !== "default") {
|
|
11
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: () => e[k]
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
n.default = e;
|
|
20
|
+
return Object.freeze(n);
|
|
21
|
+
}
|
|
22
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
23
|
+
function useAutocomplete(props) {
|
|
24
|
+
const {
|
|
25
|
+
id,
|
|
26
|
+
options,
|
|
27
|
+
disabled = false,
|
|
28
|
+
filterFn,
|
|
29
|
+
creatable = false,
|
|
30
|
+
creatableLabel,
|
|
31
|
+
onCreate,
|
|
32
|
+
inputValue: inputValueProp,
|
|
33
|
+
onInputChange,
|
|
34
|
+
open: openProp,
|
|
35
|
+
defaultOpen = false,
|
|
36
|
+
onOpenChange
|
|
37
|
+
} = props;
|
|
38
|
+
const generatedId = React__namespace.useId();
|
|
39
|
+
const inputId = id ?? generatedId;
|
|
40
|
+
const helperTextId = `${inputId}-helper`;
|
|
41
|
+
const listboxId = `${inputId}-listbox`;
|
|
42
|
+
const inputRef = React__namespace.useRef(null);
|
|
43
|
+
const listRef = React__namespace.useRef(null);
|
|
44
|
+
const isMulti = props.multiple === true;
|
|
45
|
+
const [internalValue, setInternalValue] = React__namespace.useState(
|
|
46
|
+
!isMulti && props.defaultValue || null
|
|
47
|
+
);
|
|
48
|
+
const selectedValue = !isMulti ? props.value !== void 0 ? props.value : internalValue : null;
|
|
49
|
+
const [internalMultiValue, setInternalMultiValue] = React__namespace.useState(
|
|
50
|
+
isMulti && props.defaultValue ? props.defaultValue : []
|
|
51
|
+
);
|
|
52
|
+
const selectedMultiValues = isMulti ? props.value !== void 0 ? props.value : internalMultiValue : [];
|
|
53
|
+
const [internalInputValue, setInternalInputValue] = React__namespace.useState("");
|
|
54
|
+
const searchText = inputValueProp !== void 0 ? inputValueProp : internalInputValue;
|
|
55
|
+
const [internalOpen, setInternalOpen] = React__namespace.useState(defaultOpen);
|
|
56
|
+
const isOpen = openProp !== void 0 ? openProp : internalOpen;
|
|
57
|
+
const setOpen = React__namespace.useCallback(
|
|
58
|
+
(next) => {
|
|
59
|
+
if (openProp === void 0) setInternalOpen(next);
|
|
60
|
+
onOpenChange?.(next);
|
|
61
|
+
},
|
|
62
|
+
[openProp, onOpenChange]
|
|
63
|
+
);
|
|
64
|
+
const [activeIndex, setActiveIndex] = React__namespace.useState(-1);
|
|
65
|
+
const filter = filterFn ?? constants.defaultFilter;
|
|
66
|
+
const filteredOptions = React__namespace.useMemo(() => {
|
|
67
|
+
if (!searchText) return options;
|
|
68
|
+
return options.filter((o) => filter(o, searchText));
|
|
69
|
+
}, [options, searchText, filter]);
|
|
70
|
+
const showCreate = creatable && searchText.length > 0 && !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());
|
|
71
|
+
const visibleOptions = React__namespace.useMemo(() => {
|
|
72
|
+
if (!showCreate) return filteredOptions;
|
|
73
|
+
const createOption = {
|
|
74
|
+
value: `${constants.CREATE_PREFIX}${searchText}`,
|
|
75
|
+
label: creatableLabel ? creatableLabel(searchText) : searchText
|
|
76
|
+
};
|
|
77
|
+
return [...filteredOptions, createOption];
|
|
78
|
+
}, [filteredOptions, showCreate, searchText, creatableLabel]);
|
|
79
|
+
const prevOptionsLengthRef = React__namespace.useRef(visibleOptions.length);
|
|
80
|
+
const prevSearchTextRef = React__namespace.useRef(searchText);
|
|
81
|
+
React__namespace.useEffect(() => {
|
|
82
|
+
if (searchText !== prevSearchTextRef.current || visibleOptions.length !== prevOptionsLengthRef.current) {
|
|
83
|
+
setActiveIndex(-1);
|
|
84
|
+
prevSearchTextRef.current = searchText;
|
|
85
|
+
prevOptionsLengthRef.current = visibleOptions.length;
|
|
86
|
+
}
|
|
87
|
+
}, [searchText, visibleOptions.length]);
|
|
88
|
+
React__namespace.useEffect(() => {
|
|
89
|
+
if (activeIndex < 0 || !listRef.current) return;
|
|
90
|
+
const el = listRef.current.querySelector(`[data-option-index="${activeIndex}"]`);
|
|
91
|
+
if (typeof el?.scrollIntoView === "function") {
|
|
92
|
+
el.scrollIntoView({ block: "nearest" });
|
|
93
|
+
}
|
|
94
|
+
}, [activeIndex]);
|
|
95
|
+
function clearInputText() {
|
|
96
|
+
if (inputValueProp === void 0) setInternalInputValue("");
|
|
97
|
+
onInputChange?.("");
|
|
98
|
+
}
|
|
99
|
+
function selectSingle(val) {
|
|
100
|
+
if (!isMulti && props.value === void 0) setInternalValue(val);
|
|
101
|
+
if (!isMulti) props.onChange?.(val);
|
|
102
|
+
}
|
|
103
|
+
function toggleMulti(val) {
|
|
104
|
+
const next = selectedMultiValues.includes(val) ? selectedMultiValues.filter((v) => v !== val) : [...selectedMultiValues, val];
|
|
105
|
+
if (isMulti && props.value === void 0) setInternalMultiValue(next);
|
|
106
|
+
if (isMulti) props.onChange?.(next);
|
|
107
|
+
}
|
|
108
|
+
function handleSelect(option) {
|
|
109
|
+
if (option.value.startsWith(constants.CREATE_PREFIX)) {
|
|
110
|
+
const raw = option.value.slice(constants.CREATE_PREFIX.length);
|
|
111
|
+
onCreate?.(raw);
|
|
112
|
+
if (isMulti) {
|
|
113
|
+
toggleMulti(raw);
|
|
114
|
+
} else {
|
|
115
|
+
selectSingle(raw);
|
|
116
|
+
setOpen(false);
|
|
117
|
+
}
|
|
118
|
+
clearInputText();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (isMulti) {
|
|
122
|
+
toggleMulti(option.value);
|
|
123
|
+
clearInputText();
|
|
124
|
+
} else {
|
|
125
|
+
selectSingle(option.value);
|
|
126
|
+
clearInputText();
|
|
127
|
+
setOpen(false);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function handleInputChange(e) {
|
|
131
|
+
const val = e.target.value;
|
|
132
|
+
if (inputValueProp === void 0) setInternalInputValue(val);
|
|
133
|
+
onInputChange?.(val);
|
|
134
|
+
if (!isOpen) setOpen(true);
|
|
135
|
+
}
|
|
136
|
+
function findNextEnabled(start, direction) {
|
|
137
|
+
let idx = start;
|
|
138
|
+
while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {
|
|
139
|
+
idx += direction;
|
|
140
|
+
}
|
|
141
|
+
return idx >= 0 && idx < visibleOptions.length ? idx : null;
|
|
142
|
+
}
|
|
143
|
+
function handleKeyDown(e) {
|
|
144
|
+
if (disabled) return;
|
|
145
|
+
switch (e.key) {
|
|
146
|
+
case "ArrowDown": {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
if (!isOpen) {
|
|
149
|
+
setOpen(true);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "ArrowUp": {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
if (!isOpen) {
|
|
158
|
+
setOpen(true);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case "Enter": {
|
|
165
|
+
if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
const opt = visibleOptions[activeIndex];
|
|
168
|
+
if (opt && !opt.disabled) handleSelect(opt);
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "Escape": {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
if (isOpen) setOpen(false);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case "Backspace": {
|
|
178
|
+
if (isMulti && searchText === "" && selectedMultiValues.length > 0) {
|
|
179
|
+
const lastVal = selectedMultiValues[selectedMultiValues.length - 1];
|
|
180
|
+
if (lastVal !== void 0) toggleMulti(lastVal);
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "Home": {
|
|
185
|
+
if (isOpen) {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
const idx = findNextEnabled(0, 1);
|
|
188
|
+
if (idx !== null) setActiveIndex(idx);
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case "End": {
|
|
193
|
+
if (isOpen) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
const idx = findNextEnabled(visibleOptions.length - 1, -1);
|
|
196
|
+
if (idx !== null) setActiveIndex(idx);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function handleClear(e) {
|
|
203
|
+
e.stopPropagation();
|
|
204
|
+
if (isMulti) {
|
|
205
|
+
if (props.value === void 0) setInternalMultiValue([]);
|
|
206
|
+
if (isMulti) props.onChange?.([]);
|
|
207
|
+
} else {
|
|
208
|
+
selectSingle(null);
|
|
209
|
+
}
|
|
210
|
+
clearInputText();
|
|
211
|
+
inputRef.current?.focus();
|
|
212
|
+
}
|
|
213
|
+
function handleBlur(e) {
|
|
214
|
+
const container = e.currentTarget.closest("[data-autocomplete-root]");
|
|
215
|
+
if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (e.relatedTarget instanceof Node && e.relatedTarget.closest?.("[data-radix-popper-content-wrapper]")) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (isOpen) setOpen(false);
|
|
222
|
+
}
|
|
223
|
+
function handleFocus() {
|
|
224
|
+
if (!disabled && !isOpen) setOpen(true);
|
|
225
|
+
}
|
|
226
|
+
function handleContainerClick() {
|
|
227
|
+
if (!disabled) {
|
|
228
|
+
inputRef.current?.focus();
|
|
229
|
+
if (!isOpen) setOpen(true);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function handleOpenChange(next) {
|
|
233
|
+
setOpen(next);
|
|
234
|
+
if (!next) {
|
|
235
|
+
clearInputText();
|
|
236
|
+
setActiveIndex(-1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const selectedOption = React__namespace.useMemo(
|
|
240
|
+
() => options.find((o) => o.value === selectedValue),
|
|
241
|
+
[options, selectedValue]
|
|
242
|
+
);
|
|
243
|
+
const selectedMultiOptions = React__namespace.useMemo(
|
|
244
|
+
() => selectedMultiValues.map((v) => options.find((o) => o.value === v)).filter(Boolean),
|
|
245
|
+
[options, selectedMultiValues]
|
|
246
|
+
);
|
|
247
|
+
const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;
|
|
248
|
+
const displayInputValue = React__namespace.useMemo(() => {
|
|
249
|
+
if (searchText) return searchText;
|
|
250
|
+
if (isMulti || isOpen) return "";
|
|
251
|
+
return selectedOption ? constants.getLabel(selectedOption) : "";
|
|
252
|
+
}, [searchText, isMulti, isOpen, selectedOption]);
|
|
253
|
+
const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : void 0;
|
|
254
|
+
return {
|
|
255
|
+
inputId,
|
|
256
|
+
helperTextId,
|
|
257
|
+
listboxId,
|
|
258
|
+
inputRef,
|
|
259
|
+
listRef,
|
|
260
|
+
isMulti,
|
|
261
|
+
isOpen,
|
|
262
|
+
selectedValue,
|
|
263
|
+
selectedMultiValues,
|
|
264
|
+
selectedMultiOptions,
|
|
265
|
+
searchText,
|
|
266
|
+
visibleOptions,
|
|
267
|
+
activeIndex,
|
|
268
|
+
activeDescendantId,
|
|
269
|
+
hasClearableValue,
|
|
270
|
+
displayInputValue,
|
|
271
|
+
setActiveIndex,
|
|
272
|
+
handleSelect,
|
|
273
|
+
handleInputChange,
|
|
274
|
+
handleKeyDown,
|
|
275
|
+
handleClear,
|
|
276
|
+
handleBlur,
|
|
277
|
+
handleFocus,
|
|
278
|
+
handleContainerClick,
|
|
279
|
+
handleOpenChange,
|
|
280
|
+
toggleMulti
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
exports.useAutocomplete = useAutocomplete;
|
|
284
|
+
//# sourceMappingURL=useAutocomplete.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAutocomplete.cjs","sources":["../../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset only when search text or option count actually changes\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":["React","defaultFilter","CREATE_PREFIX","getLabel"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
const BottomNavigationContext = React__namespace.createContext({
|
|
25
|
+
showLabelsOnlyWhenActive: false,
|
|
26
|
+
hideLabels: false
|
|
27
|
+
});
|
|
28
|
+
function useBottomNavigationContext() {
|
|
29
|
+
return React__namespace.useContext(BottomNavigationContext);
|
|
30
|
+
}
|
|
31
|
+
const BottomNavigation = React__namespace.forwardRef(
|
|
32
|
+
({
|
|
33
|
+
className,
|
|
34
|
+
children,
|
|
35
|
+
value,
|
|
36
|
+
onValueChange,
|
|
37
|
+
showLabelsOnlyWhenActive = false,
|
|
38
|
+
hideLabels = false,
|
|
39
|
+
hideOnDesktop = false,
|
|
40
|
+
...props
|
|
41
|
+
}, ref) => {
|
|
42
|
+
const contextValue = React__namespace.useMemo(
|
|
43
|
+
() => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),
|
|
44
|
+
[value, onValueChange, showLabelsOnlyWhenActive, hideLabels]
|
|
45
|
+
);
|
|
46
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BottomNavigationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
47
|
+
"nav",
|
|
48
|
+
{
|
|
49
|
+
ref,
|
|
50
|
+
className: cn.cn(
|
|
51
|
+
"fixed inset-x-0 bottom-0",
|
|
52
|
+
"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around",
|
|
53
|
+
"border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]",
|
|
54
|
+
"pb-[env(safe-area-inset-bottom,0px)]",
|
|
55
|
+
hideOnDesktop && "md:hidden",
|
|
56
|
+
className
|
|
57
|
+
),
|
|
58
|
+
style: { zIndex: "var(--fanvue-ui-portal-z-index, 50)", ...props.style },
|
|
59
|
+
...props,
|
|
60
|
+
children
|
|
61
|
+
}
|
|
62
|
+
) });
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
BottomNavigation.displayName = "BottomNavigation";
|
|
66
|
+
exports.BottomNavigation = BottomNavigation;
|
|
67
|
+
exports.useBottomNavigationContext = useBottomNavigationContext;
|
|
68
|
+
//# sourceMappingURL=BottomNavigation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BottomNavigation.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, labels are only shown on the active action. @default false */\n showLabelsOnlyWhenActive?: boolean;\n /** When `true`, all labels are hidden. @default false */\n hideLabels?: boolean;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n showLabelsOnlyWhenActive: boolean;\n hideLabels: boolean;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({\n showLabelsOnlyWhenActive: false,\n hideLabels: false,\n});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n (\n {\n className,\n children,\n value,\n onValueChange,\n showLabelsOnlyWhenActive = false,\n hideLabels = false,\n hideOnDesktop = false,\n ...props\n },\n ref,\n ) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),\n [value, onValueChange, showLabelsOnlyWhenActive, hideLabels],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAM,0BAA0BA,iBAAM,cAA4C;AAAA,EAChF,0BAA0B;AAAA,EAC1B,YAAY;AACd,CAAC;AAEM,SAAS,6BAA2D;AACzE,SAAOA,iBAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmBA,iBAAM;AAAA,EACpC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,2BAA2B;AAAA,IAC3B,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAeA,iBAAM;AAAA,MACzB,OAAO,EAAE,OAAO,eAAe,0BAA0B,WAAA;AAAA,MACzD,CAAC,OAAO,eAAe,0BAA0B,UAAU;AAAA,IAAA;AAG7D,WACEC,2BAAAA,IAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAAA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;;;"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const reactSlot = require("@radix-ui/react-slot");
|
|
6
|
+
const React = require("react");
|
|
7
|
+
const cn = require("../../utils/cn.cjs");
|
|
8
|
+
const BottomNavigation = require("./BottomNavigation.cjs");
|
|
9
|
+
function _interopNamespaceDefault(e) {
|
|
10
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
11
|
+
if (e) {
|
|
12
|
+
for (const k in e) {
|
|
13
|
+
if (k !== "default") {
|
|
14
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: () => e[k]
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
26
|
+
const BottomNavigationAction = React__namespace.forwardRef(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {
|
|
27
|
+
const {
|
|
28
|
+
value: selectedValue,
|
|
29
|
+
onValueChange,
|
|
30
|
+
showLabelsOnlyWhenActive,
|
|
31
|
+
hideLabels
|
|
32
|
+
} = BottomNavigation.useBottomNavigationContext();
|
|
33
|
+
const isActive = selectedValue === value;
|
|
34
|
+
const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);
|
|
35
|
+
const handleClick = (e) => {
|
|
36
|
+
onValueChange?.(value);
|
|
37
|
+
onClick?.(e);
|
|
38
|
+
};
|
|
39
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
41
|
+
Comp,
|
|
42
|
+
{
|
|
43
|
+
ref,
|
|
44
|
+
...!asChild && { type: "button" },
|
|
45
|
+
"aria-current": isActive ? "page" : void 0,
|
|
46
|
+
"aria-label": !showLabel && label ? label : void 0,
|
|
47
|
+
"data-state": isActive ? "active" : "inactive",
|
|
48
|
+
className: cn.cn(
|
|
49
|
+
"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2",
|
|
50
|
+
"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
|
|
51
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page",
|
|
52
|
+
isActive ? "text-brand-accent-default" : "text-foreground-tertiary hover:text-foreground-secondary",
|
|
53
|
+
className
|
|
54
|
+
),
|
|
55
|
+
onClick: handleClick,
|
|
56
|
+
...props,
|
|
57
|
+
children: [
|
|
58
|
+
asChild && /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slottable, { children }),
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex", children: [
|
|
60
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-7", "aria-hidden": "true", children: icon }),
|
|
61
|
+
badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -end-1 -top-0.5", children: badge })
|
|
62
|
+
] }),
|
|
63
|
+
showLabel && label && /* @__PURE__ */ jsxRuntime.jsx(
|
|
64
|
+
"span",
|
|
65
|
+
{
|
|
66
|
+
className: cn.cn(
|
|
67
|
+
"typography-semibold-body-xs min-w-0 max-w-full truncate",
|
|
68
|
+
isActive ? "text-brand-accent-default" : "text-foreground-tertiary"
|
|
69
|
+
),
|
|
70
|
+
children: label
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
BottomNavigationAction.displayName = "BottomNavigationAction";
|
|
78
|
+
exports.BottomNavigationAction = BottomNavigationAction;
|
|
79
|
+
//# sourceMappingURL=BottomNavigationAction.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BottomNavigationAction.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed above the label. */\n icon: React.ReactElement;\n /** Text label displayed below the icon. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const {\n value: selectedValue,\n onValueChange,\n showLabelsOnlyWhenActive,\n hideLabels,\n } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={!showLabel && label ? label : undefined}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n isActive\n ? \"text-brand-accent-default\"\n : \"text-foreground-tertiary hover:text-foreground-secondary\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-0.5\">{badge}</span>}\n </span>\n {showLabel && label && (\n <span\n className={cn(\n \"typography-semibold-body-xs min-w-0 max-w-full truncate\",\n isActive ? \"text-brand-accent-default\" : \"text-foreground-tertiary\",\n )}\n >\n {label}\n </span>\n )}\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":["React","useBottomNavigationContext","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmBO,MAAM,yBAAyBA,iBAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACEC,4CAAA;AAEJ,QAAM,WAAW,kBAAkB;AACnC,QAAM,YAAY,CAAC,eAAe,CAAC,4BAA4B;AAE/D,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY,CAAC,aAAa,QAAQ,QAAQ;AAAA,MAC1C,cAAY,WAAW,WAAW;AAAA,MAClC,WAAWC,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,WACI,8BACA;AAAA,QACJ;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAWC,2BAAAA,IAACC,uBAAW,SAAA,CAAS;AAAA,QACjCH,2BAAAA,KAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAAE,+BAAC,QAAA,EAAK,WAAU,eAAc,eAAY,QACvC,UAAA,MACH;AAAA,UACC,SAASA,2BAAAA,IAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,GAC9D;AAAA,QACC,aAAa,SACZA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,GAAAA;AAAAA,cACT;AAAA,cACA,WAAW,8BAA8B;AAAA,YAAA;AAAA,YAG1C,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAED,uBAAuB,cAAc;;"}
|
|
@@ -79,14 +79,7 @@ const Chip = React__namespace.forwardRef(
|
|
|
79
79
|
children: asChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slottable, { children }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
80
80
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-0.5 overflow-hidden px-3", children: [
|
|
81
81
|
leftDot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-2 shrink-0 rounded-full bg-current", "aria-hidden": "true" }),
|
|
82
|
-
leftIcon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
83
|
-
"span",
|
|
84
|
-
{
|
|
85
|
-
className: "flex size-5 shrink-0 items-center justify-center",
|
|
86
|
-
"aria-hidden": "true",
|
|
87
|
-
children: leftIcon
|
|
88
|
-
}
|
|
89
|
-
),
|
|
82
|
+
leftIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-center justify-center", "aria-hidden": "true", children: leftIcon }),
|
|
90
83
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children }),
|
|
91
84
|
rightIcon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
92
85
|
"span",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-semibold-body-sm relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-lg\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-50 text-foreground-onaccentinverse\",\n !isDark && selected && \"bg-brand-accent-muted text-neutral-400\",\n !isDark && !selected && \"bg-neutral-100 text-neutral-400\",\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n \"hover:bg-brand-accent-muted active:bg-brand-accent-muted\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-300\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span
|
|
1
|
+
{"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"typography-semibold-body-sm relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-lg\" : \"rounded-full\",\n // Size\n size === \"32\" && \"h-8 py-1\",\n size === \"40\" && \"h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-50 text-foreground-onaccentinverse\",\n !isDark && selected && \"bg-brand-accent-muted text-neutral-400\",\n !isDark && !selected && \"bg-neutral-100 text-neutral-400\",\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n \"hover:bg-brand-accent-muted active:bg-brand-accent-muted\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-neutral-300\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {notificationLabel && (\n <span className=\"typography-semibold-body-sm absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-foreground-default px-1 text-foreground-inverse\">\n {notificationLabel}\n </span>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":["React","Slot","jsx","cn","Slottable","jsxs","Fragment"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA0CO,MAAM,OAAOA,iBAAM;AAAA,EACxB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QAEG;AACH,UAAM,gBAAgB,CAAC,CAAC,WAAW,CAAC;AACpC,UAAM,OAAO,UAAUC,UAAAA,OAAO,gBAAgB,WAAW;AACzD,UAAM,SAAS,YAAY;AAE3B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA;AAAA,UAEA,YAAY,WAAW,eAAe;AAAA;AAAA,UAEtC,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA;AAAA,UAEjB,UAAU;AAAA,UACV,CAAC,UAAU,YAAY;AAAA,UACvB,CAAC,UAAU,CAAC,YAAY;AAAA;AAAA,UAExB,iBAAiB,CAAC,YAAY;AAAA,UAC9B,iBACE,CAAC,YACD,CAAC,UACD,CAAC,YACD;AAAA;AAAA,UAEF;AAAA;AAAA,UAEA,YAAY,UAAU;AAAA,UACtB,YAAY,CAAC,UAAU;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAI,iBAAiB;AAAA,UACpB,MAAM;AAAA,UACN;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA;AAAA,QAED,GAAI,CAAC,iBAAiB,YAAY,EAAE,iBAAiB,KAAA;AAAA,QACrD,GAAI,YAAY,EAAE,iBAAiB,GAAA;AAAA,QACnC,GAAG;AAAA,QAEH,UAAA,UACCD,2BAAAA,IAACE,qBAAA,EAAW,SAAA,CAAS,IAErBC,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,UAAAD,2BAAAA,KAAC,QAAA,EAAK,WAAU,0DACb,UAAA;AAAA,YAAA,WACCH,2BAAAA,IAAC,QAAA,EAAK,WAAU,2CAA0C,eAAY,QAAO;AAAA,YAE9E,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,6CAA4C,eAAY,QACrE,UAAA,UACH;AAAA,YAEFA,2BAAAA,IAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,YAC5C,aACCA,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,eAAY;AAAA,gBAEX,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,GAEJ;AAAA,UACC,qBACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,qKACb,UAAA,kBAAA,CACH;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,KAAK,cAAc;;"}
|