@hjmds/react 0.8.2
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/README.md +146 -0
- package/dist/actions.d.ts +61 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +54 -0
- package/dist/actions.js.map +1 -0
- package/dist/advanced-display.d.ts +133 -0
- package/dist/advanced-display.d.ts.map +1 -0
- package/dist/advanced-display.js +201 -0
- package/dist/advanced-display.js.map +1 -0
- package/dist/advanced-forms.d.ts +116 -0
- package/dist/advanced-forms.d.ts.map +1 -0
- package/dist/advanced-forms.js +234 -0
- package/dist/advanced-forms.js.map +1 -0
- package/dist/advanced-navigation.d.ts +24 -0
- package/dist/advanced-navigation.d.ts.map +1 -0
- package/dist/advanced-navigation.js +25 -0
- package/dist/advanced-navigation.js.map +1 -0
- package/dist/bottom-navigation.d.ts +37 -0
- package/dist/bottom-navigation.d.ts.map +1 -0
- package/dist/bottom-navigation.js +132 -0
- package/dist/bottom-navigation.js.map +1 -0
- package/dist/date-picker.d.ts +24 -0
- package/dist/date-picker.d.ts.map +1 -0
- package/dist/date-picker.js +81 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/display-public.d.ts +5 -0
- package/dist/display-public.d.ts.map +1 -0
- package/dist/display-public.js +5 -0
- package/dist/display-public.js.map +1 -0
- package/dist/display.d.ts +77 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +51 -0
- package/dist/display.js.map +1 -0
- package/dist/evidence.d.ts +940 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +98 -0
- package/dist/evidence.js.map +1 -0
- package/dist/feedback.d.ts +89 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +41 -0
- package/dist/feedback.js.map +1 -0
- package/dist/file-picker.d.ts +30 -0
- package/dist/file-picker.d.ts.map +1 -0
- package/dist/file-picker.js +40 -0
- package/dist/file-picker.js.map +1 -0
- package/dist/forms-public.d.ts +8 -0
- package/dist/forms-public.d.ts.map +1 -0
- package/dist/forms-public.js +8 -0
- package/dist/forms-public.js.map +1 -0
- package/dist/forms.d.ts +160 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +178 -0
- package/dist/forms.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +17 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +68 -0
- package/dist/internal.js.map +1 -0
- package/dist/layout.d.ts +134 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +167 -0
- package/dist/layout.js.map +1 -0
- package/dist/navigation-public.d.ts +6 -0
- package/dist/navigation-public.d.ts.map +1 -0
- package/dist/navigation-public.js +6 -0
- package/dist/navigation-public.js.map +1 -0
- package/dist/navigation.d.ts +65 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +196 -0
- package/dist/navigation.js.map +1 -0
- package/dist/number-field.d.ts +55 -0
- package/dist/number-field.d.ts.map +1 -0
- package/dist/number-field.js +107 -0
- package/dist/number-field.js.map +1 -0
- package/dist/otp-field.d.ts +2 -0
- package/dist/otp-field.d.ts.map +1 -0
- package/dist/otp-field.js +2 -0
- package/dist/otp-field.js.map +1 -0
- package/dist/overlays.d.ts +192 -0
- package/dist/overlays.d.ts.map +1 -0
- package/dist/overlays.js +949 -0
- package/dist/overlays.js.map +1 -0
- package/dist/password-field.d.ts +2 -0
- package/dist/password-field.d.ts.map +1 -0
- package/dist/password-field.js +2 -0
- package/dist/password-field.js.map +1 -0
- package/dist/portal.d.ts +32 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +264 -0
- package/dist/portal.js.map +1 -0
- package/dist/provider.d.ts +38 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +87 -0
- package/dist/provider.js.map +1 -0
- package/dist/select.d.ts +72 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +353 -0
- package/dist/select.js.map +1 -0
- package/dist/selection.d.ts +160 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +225 -0
- package/dist/selection.js.map +1 -0
- package/dist/slider.d.ts +35 -0
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +150 -0
- package/dist/slider.js.map +1 -0
- package/dist/steps.d.ts +29 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +10 -0
- package/dist/steps.js.map +1 -0
- package/dist/styles.css +1349 -0
- package/dist/supplemental-display.d.ts +59 -0
- package/dist/supplemental-display.d.ts.map +1 -0
- package/dist/supplemental-display.js +136 -0
- package/dist/supplemental-display.js.map +1 -0
- package/dist/supplemental-navigation.d.ts +28 -0
- package/dist/supplemental-navigation.d.ts.map +1 -0
- package/dist/supplemental-navigation.js +106 -0
- package/dist/supplemental-navigation.js.map +1 -0
- package/dist/theme.d.ts +5 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +55 -0
- package/dist/theme.js.map +1 -0
- package/dist/toast.d.ts +41 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +250 -0
- package/dist/toast.js.map +1 -0
- package/dist/upload-item.d.ts +18 -0
- package/dist/upload-item.d.ts.map +1 -0
- package/dist/upload-item.js +19 -0
- package/dist/upload-item.js.map +1 -0
- package/package.json +151 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { comboboxRecipe, selectRecipe, } from "@hjmds/design-contracts/recipes";
|
|
3
|
+
import { formRecipe, } from "@hjmds/design-contracts/components/form";
|
|
4
|
+
import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState, } from "react";
|
|
5
|
+
import { Field } from "./forms.js";
|
|
6
|
+
import { classNames, composeRefs, useControllableState } from "./internal.js";
|
|
7
|
+
import { AnchoredPortal, useAnchoredPopup } from "./portal.js";
|
|
8
|
+
function useControlId(id, prefix) {
|
|
9
|
+
const generated = useId().replaceAll(":", "");
|
|
10
|
+
return id ?? `hjm-${prefix}-${generated}`;
|
|
11
|
+
}
|
|
12
|
+
function validateOptions(options, component) {
|
|
13
|
+
if (options.length === 0)
|
|
14
|
+
throw new TypeError(`${component} requires at least one option`);
|
|
15
|
+
const values = new Set();
|
|
16
|
+
for (const option of options) {
|
|
17
|
+
if (option.value.trim().length === 0) {
|
|
18
|
+
throw new TypeError(`${component} option value must not be empty`);
|
|
19
|
+
}
|
|
20
|
+
if (option.label.trim().length === 0) {
|
|
21
|
+
throw new TypeError(`${component} option label must not be empty`);
|
|
22
|
+
}
|
|
23
|
+
if (values.has(option.value)) {
|
|
24
|
+
throw new TypeError(`Duplicate ${component} option value: ${option.value}`);
|
|
25
|
+
}
|
|
26
|
+
values.add(option.value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** A native select keeps browser keyboard, form, autofill, and mobile picker behavior. */
|
|
30
|
+
export const NativeSelect = forwardRef(function NativeSelect({ id, label, description, error, options, placeholder, value: valueProp, defaultValue = "", onValueChange, size = selectRecipe.defaults.size, density = selectRecipe.defaults.density, fieldClassName, className, disabled = false, required = false, ...props }, ref) {
|
|
31
|
+
validateOptions(options, "Select");
|
|
32
|
+
const controlId = useControlId(id, "select");
|
|
33
|
+
const [value, setValue] = useControllableState({
|
|
34
|
+
...(valueProp === undefined ? {} : { value: valueProp }),
|
|
35
|
+
defaultValue,
|
|
36
|
+
...(onValueChange === undefined ? {} : { onChange: onValueChange }),
|
|
37
|
+
});
|
|
38
|
+
if (value !== "" &&
|
|
39
|
+
!options.some((option) => option.value === value && !option.disabled)) {
|
|
40
|
+
throw new RangeError(`Select value must identify an enabled option: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
return (_jsx(Field, { controlId: controlId, label: label, description: description, error: error, required: required, disabled: disabled, className: classNames("hjm-select", fieldClassName), "data-size": size, "data-density": density, children: (controlProps) => (_jsxs("div", { className: "hjm-field__control hjm-select__control", children: [_jsxs("select", { ...props, ...controlProps, ref: ref, value: value, className: classNames("hjm-field__input hjm-select__native", className), onChange: (event) => setValue(event.target.value), children: [placeholder !== undefined ? (_jsx("option", { value: "", disabled: required, children: placeholder })) : null, options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }), _jsx("span", { className: "hjm-select__indicator", "aria-hidden": "true", children: "\u2304" })] })) }));
|
|
43
|
+
});
|
|
44
|
+
function nextEnabled(items, current, direction) {
|
|
45
|
+
if (items.length === 0)
|
|
46
|
+
return -1;
|
|
47
|
+
for (let offset = 1; offset <= items.length; offset += 1) {
|
|
48
|
+
const candidate = (current + direction * offset + items.length) % items.length;
|
|
49
|
+
if (!items[candidate]?.disabled)
|
|
50
|
+
return candidate;
|
|
51
|
+
}
|
|
52
|
+
return -1;
|
|
53
|
+
}
|
|
54
|
+
export const Combobox = forwardRef(function Combobox({ id, name, label, description, error, items, value: valueProp, defaultValue = "", onValueChange, inputValue: inputValueProp, defaultInputValue, onInputValueChange, open: openProp, defaultOpen = false, onOpenChange, emptyMessage, loading = false, loadingMessage, selectionRequiredMessage, openOnFocus = true, size = comboboxRecipe.defaults.size, density = comboboxRecipe.defaults.density, align = "start", fieldClassName, portalContainer, className, disabled = false, required = false, autoComplete = "off", onFocus, onBlur, onKeyDown, ...props }, forwardedRef) {
|
|
55
|
+
validateOptions(items, "Combobox");
|
|
56
|
+
const controlId = useControlId(id, "combobox");
|
|
57
|
+
const listboxId = `${controlId}-listbox`;
|
|
58
|
+
const rootRef = useRef(null);
|
|
59
|
+
const inputRef = useRef(null);
|
|
60
|
+
const suppressFocusOpenRef = useRef(false);
|
|
61
|
+
const listboxRef = useRef(null);
|
|
62
|
+
const [listboxNode, setListboxNode] = useState(null);
|
|
63
|
+
const popupPosition = useAnchoredPopup(inputRef, listboxNode, {
|
|
64
|
+
align,
|
|
65
|
+
matchAnchorWidth: true,
|
|
66
|
+
zIndex: 800,
|
|
67
|
+
});
|
|
68
|
+
const setListboxRef = useCallback((node) => {
|
|
69
|
+
listboxRef.current = node;
|
|
70
|
+
setListboxNode(node);
|
|
71
|
+
}, []);
|
|
72
|
+
const [selectedValue, setSelectedValue] = useControllableState({
|
|
73
|
+
...(valueProp === undefined ? {} : { value: valueProp }),
|
|
74
|
+
defaultValue,
|
|
75
|
+
...(onValueChange === undefined ? {} : { onChange: onValueChange }),
|
|
76
|
+
});
|
|
77
|
+
const selectedItem = items.find((item) => item.value === selectedValue);
|
|
78
|
+
if (selectedValue !== "" && (!selectedItem || selectedItem.disabled)) {
|
|
79
|
+
throw new RangeError(`Combobox value must identify an enabled option: ${selectedValue}`);
|
|
80
|
+
}
|
|
81
|
+
if (required && selectionRequiredMessage.trim().length === 0) {
|
|
82
|
+
throw new TypeError("Combobox selectionRequiredMessage must not be empty");
|
|
83
|
+
}
|
|
84
|
+
const [query, setQuery] = useControllableState({
|
|
85
|
+
...(inputValueProp === undefined ? {} : { value: inputValueProp }),
|
|
86
|
+
defaultValue: defaultInputValue ?? selectedItem?.label ?? "",
|
|
87
|
+
...(onInputValueChange === undefined ? {} : { onChange: onInputValueChange }),
|
|
88
|
+
});
|
|
89
|
+
const [open, setOpenState] = useControllableState({
|
|
90
|
+
...(openProp === undefined ? {} : { value: openProp }),
|
|
91
|
+
defaultValue: defaultOpen,
|
|
92
|
+
});
|
|
93
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
inputRef.current?.setCustomValidity(required && selectedValue === "" ? selectionRequiredMessage : "");
|
|
96
|
+
}, [required, selectedValue, selectionRequiredMessage]);
|
|
97
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
98
|
+
const filteredItems = useMemo(() => items.filter((item) => {
|
|
99
|
+
if (normalizedQuery.length === 0)
|
|
100
|
+
return true;
|
|
101
|
+
const searchable = [item.label, ...(item.keywords ?? [])]
|
|
102
|
+
.join(" ")
|
|
103
|
+
.toLocaleLowerCase();
|
|
104
|
+
return searchable.includes(normalizedQuery);
|
|
105
|
+
}), [items, normalizedQuery]);
|
|
106
|
+
const setOpen = (next, reason) => {
|
|
107
|
+
if (next === open)
|
|
108
|
+
return;
|
|
109
|
+
setOpenState(next);
|
|
110
|
+
onOpenChange?.(next, reason);
|
|
111
|
+
if (!next)
|
|
112
|
+
setActiveIndex(-1);
|
|
113
|
+
};
|
|
114
|
+
const firstEnabled = () => filteredItems.findIndex((item) => !item.disabled);
|
|
115
|
+
const lastEnabled = () => {
|
|
116
|
+
for (let index = filteredItems.length - 1; index >= 0; index -= 1) {
|
|
117
|
+
if (!filteredItems[index]?.disabled)
|
|
118
|
+
return index;
|
|
119
|
+
}
|
|
120
|
+
return -1;
|
|
121
|
+
};
|
|
122
|
+
const selectItem = (item) => {
|
|
123
|
+
if (item.disabled)
|
|
124
|
+
return;
|
|
125
|
+
setSelectedValue(item.value);
|
|
126
|
+
setQuery(item.label);
|
|
127
|
+
setOpen(false, "selection");
|
|
128
|
+
queueMicrotask(() => {
|
|
129
|
+
const input = inputRef.current;
|
|
130
|
+
if (!input || document.activeElement === input)
|
|
131
|
+
return;
|
|
132
|
+
suppressFocusOpenRef.current = true;
|
|
133
|
+
input.focus();
|
|
134
|
+
queueMicrotask(() => {
|
|
135
|
+
suppressFocusOpenRef.current = false;
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
const handleKeyDown = (event) => {
|
|
140
|
+
onKeyDown?.(event);
|
|
141
|
+
if (event.defaultPrevented)
|
|
142
|
+
return;
|
|
143
|
+
if (loading &&
|
|
144
|
+
["ArrowDown", "ArrowUp", "Home", "End", "Enter"].includes(event.key)) {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
if (!open) {
|
|
151
|
+
setOpen(true, "keyboard");
|
|
152
|
+
setActiveIndex(event.key === "ArrowDown" ? firstEnabled() : lastEnabled());
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
setActiveIndex((current) => nextEnabled(filteredItems, current, event.key === "ArrowDown" ? 1 : -1));
|
|
156
|
+
}
|
|
157
|
+
else if (open && event.key === "Home") {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
setActiveIndex(firstEnabled());
|
|
160
|
+
}
|
|
161
|
+
else if (open && event.key === "End") {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
setActiveIndex(lastEnabled());
|
|
164
|
+
}
|
|
165
|
+
else if (open && event.key === "Enter" && activeIndex >= 0) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
const item = filteredItems[activeIndex];
|
|
168
|
+
if (item)
|
|
169
|
+
selectItem(item);
|
|
170
|
+
}
|
|
171
|
+
else if (open && event.key === "Escape") {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
setOpen(false, "escape");
|
|
174
|
+
}
|
|
175
|
+
else if (open && event.key === "Tab") {
|
|
176
|
+
setOpen(false, "blur");
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const activeItem = activeIndex >= 0 ? filteredItems[activeIndex] : undefined;
|
|
180
|
+
return (_jsx(Field, { controlId: controlId, label: label, description: description, error: error, required: required, disabled: disabled, className: classNames("hjm-combobox", fieldClassName), "data-size": size, "data-density": density, "data-state": disabled ? "disabled" : open ? "open" : "closed", children: (controlProps) => (_jsxs("div", { ref: rootRef, className: "hjm-combobox__anchor", onBlur: (event) => {
|
|
181
|
+
if (!event.currentTarget.contains(event.relatedTarget) &&
|
|
182
|
+
!listboxRef.current?.contains(event.relatedTarget))
|
|
183
|
+
setOpen(false, "blur");
|
|
184
|
+
}, children: [_jsxs("div", { className: "hjm-field__control hjm-combobox__control", children: [_jsx("input", { ...props, ...controlProps, ref: composeRefs(forwardedRef, inputRef), className: classNames("hjm-field__input hjm-combobox__input", className), value: query, role: "combobox", autoComplete: autoComplete, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-expanded": open, "aria-activedescendant": open && activeItem ? `${controlId}-option-${activeIndex}` : undefined, onFocus: (event) => {
|
|
185
|
+
onFocus?.(event);
|
|
186
|
+
if (!event.defaultPrevented &&
|
|
187
|
+
openOnFocus &&
|
|
188
|
+
!suppressFocusOpenRef.current)
|
|
189
|
+
setOpen(true, "focus");
|
|
190
|
+
}, onBlur: onBlur, onChange: (event) => {
|
|
191
|
+
setQuery(event.target.value);
|
|
192
|
+
setSelectedValue("");
|
|
193
|
+
setOpen(true, "input");
|
|
194
|
+
setActiveIndex(-1);
|
|
195
|
+
}, onKeyDown: handleKeyDown }), loading ? _jsx("span", { className: "hjm-combobox__spinner", "aria-hidden": "true" }) : null, _jsx("span", { className: "hjm-select__indicator", "aria-hidden": "true", children: "\u2304" })] }), name ? _jsx("input", { type: "hidden", name: name, value: selectedValue }) : null, open ? (_jsx(AnchoredPortal, { anchorRef: inputRef, ssrFallback: "inline", ...(portalContainer === undefined ? {} : { container: portalContainer }), children: _jsx("div", { ref: setListboxRef, id: listboxId, role: "listbox", className: "hjm-combobox__listbox", "data-density": density, "aria-label": typeof label === "string" ? label : undefined, "aria-busy": loading || undefined, "data-placement": popupPosition.placement, "data-align": popupPosition.align, style: popupPosition.style, children: loading ? (_jsx("div", { className: "hjm-combobox__message", role: "status", children: loadingMessage })) : filteredItems.length === 0 ? (_jsx("div", { className: "hjm-combobox__message", children: emptyMessage })) : (filteredItems.map((item, index) => (_jsx("div", { id: `${controlId}-option-${index}`, role: "option", "aria-selected": item.value === selectedValue, "aria-disabled": item.disabled || undefined, className: "hjm-combobox__option", "data-state": item.disabled
|
|
196
|
+
? "disabled"
|
|
197
|
+
: index === activeIndex
|
|
198
|
+
? "active"
|
|
199
|
+
: item.value === selectedValue
|
|
200
|
+
? "selected"
|
|
201
|
+
: "idle", onMouseDown: (event) => event.preventDefault(), onMouseMove: () => {
|
|
202
|
+
if (!item.disabled)
|
|
203
|
+
setActiveIndex(index);
|
|
204
|
+
}, onClick: () => selectItem(item), children: item.label }, item.value)))) }) })) : null] })) }));
|
|
205
|
+
});
|
|
206
|
+
export const Form = forwardRef(function Form({ onSubmit, busy = false, formError, actions, density = formRecipe.defaults.density, className, children, ...props }, ref) {
|
|
207
|
+
const [submitting, setSubmitting] = useState(false);
|
|
208
|
+
const submittingRef = useRef(false);
|
|
209
|
+
const effectiveBusy = busy || submitting;
|
|
210
|
+
const handleSubmit = (event) => {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
if (effectiveBusy || submittingRef.current)
|
|
213
|
+
return;
|
|
214
|
+
let result;
|
|
215
|
+
try {
|
|
216
|
+
result = onSubmit(event);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
if (result && typeof result.then === "function") {
|
|
222
|
+
submittingRef.current = true;
|
|
223
|
+
setSubmitting(true);
|
|
224
|
+
void Promise.resolve(result)
|
|
225
|
+
.catch(() => undefined)
|
|
226
|
+
.finally(() => {
|
|
227
|
+
submittingRef.current = false;
|
|
228
|
+
setSubmitting(false);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
return (_jsx("form", { ...props, ref: ref, className: classNames("hjm-form", className), "data-density": density, "data-state": effectiveBusy ? "busy" : formError ? "error" : "idle", "aria-busy": effectiveBusy || undefined, onSubmit: handleSubmit, children: _jsxs("fieldset", { className: "hjm-form__fieldset", disabled: effectiveBusy, children: [_jsx("div", { className: "hjm-form__fields", children: children }), formError ? (_jsx("div", { className: "hjm-form__error", role: "alert", children: formError })) : null, actions ? _jsx("div", { className: "hjm-form__actions", children: actions }) : null] }) }));
|
|
233
|
+
});
|
|
234
|
+
//# sourceMappingURL=advanced-forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced-forms.js","sourceRoot":"","sources":["../src/advanced-forms.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n comboboxRecipe,\n selectRecipe,\n type SelectDensity,\n type SelectSize,\n} from \"@hjmds/design-contracts/recipes\";\nimport {\n formRecipe,\n type FormDensity,\n} from \"@hjmds/design-contracts/components/form\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type FormEvent,\n type FormHTMLAttributes,\n type InputHTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n type SelectHTMLAttributes,\n} from \"react\";\nimport { Field } from \"./forms.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport { AnchoredPortal, useAnchoredPopup } from \"./portal.js\";\n\nfunction useControlId(id: string | undefined, prefix: string): string {\n const generated = useId().replaceAll(\":\", \"\");\n return id ?? `hjm-${prefix}-${generated}`;\n}\n\nexport type SelectOption = Readonly<{\n value: string;\n label: string;\n disabled?: boolean;\n}>;\n\nfunction validateOptions(options: readonly SelectOption[], component: string): void {\n if (options.length === 0) throw new TypeError(`${component} requires at least one option`);\n const values = new Set<string>();\n for (const option of options) {\n if (option.value.trim().length === 0) {\n throw new TypeError(`${component} option value must not be empty`);\n }\n if (option.label.trim().length === 0) {\n throw new TypeError(`${component} option label must not be empty`);\n }\n if (values.has(option.value)) {\n throw new TypeError(`Duplicate ${component} option value: ${option.value}`);\n }\n values.add(option.value);\n }\n}\n\nexport type NativeSelectProps = Omit<\n SelectHTMLAttributes<HTMLSelectElement>,\n \"children\" | \"defaultValue\" | \"onChange\" | \"size\" | \"value\"\n> &\n Readonly<{\n label: ReactNode;\n description?: ReactNode;\n error?: ReactNode;\n options: readonly SelectOption[];\n placeholder?: string;\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n size?: SelectSize;\n density?: SelectDensity;\n fieldClassName?: string;\n }>;\n\n/** A native select keeps browser keyboard, form, autofill, and mobile picker behavior. */\nexport const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(function NativeSelect(\n {\n id,\n label,\n description,\n error,\n options,\n placeholder,\n value: valueProp,\n defaultValue = \"\",\n onValueChange,\n size = selectRecipe.defaults.size,\n density = selectRecipe.defaults.density,\n fieldClassName,\n className,\n disabled = false,\n required = false,\n ...props\n },\n ref,\n) {\n validateOptions(options, \"Select\");\n const controlId = useControlId(id, \"select\");\n const [value, setValue] = useControllableState({\n ...(valueProp === undefined ? {} : { value: valueProp }),\n defaultValue,\n ...(onValueChange === undefined ? {} : { onChange: onValueChange }),\n });\n if (\n value !== \"\" &&\n !options.some((option) => option.value === value && !option.disabled)\n ) {\n throw new RangeError(`Select value must identify an enabled option: ${value}`);\n }\n\n return (\n <Field\n controlId={controlId}\n label={label}\n description={description}\n error={error}\n required={required}\n disabled={disabled}\n className={classNames(\"hjm-select\", fieldClassName)}\n data-size={size}\n data-density={density}\n >\n {(controlProps) => (\n <div className=\"hjm-field__control hjm-select__control\">\n <select\n {...props}\n {...controlProps}\n ref={ref}\n value={value}\n className={classNames(\"hjm-field__input hjm-select__native\", className)}\n onChange={(event: ChangeEvent<HTMLSelectElement>) => setValue(event.target.value)}\n >\n {placeholder !== undefined ? (\n <option value=\"\" disabled={required}>\n {placeholder}\n </option>\n ) : null}\n {options.map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n <span className=\"hjm-select__indicator\" aria-hidden=\"true\">⌄</span>\n </div>\n )}\n </Field>\n );\n});\n\nexport type ComboboxItem = Readonly<{\n value: string;\n label: string;\n keywords?: readonly string[];\n disabled?: boolean;\n}>;\n\nexport type ComboboxOpenChangeReason =\n | \"focus\"\n | \"input\"\n | \"keyboard\"\n | \"selection\"\n | \"escape\"\n | \"blur\";\n\nexport type ComboboxProps = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"children\" | \"defaultValue\" | \"onChange\" | \"role\" | \"size\" | \"value\"\n> &\n Readonly<{\n label: ReactNode;\n description?: ReactNode;\n error?: ReactNode;\n items: readonly ComboboxItem[];\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n inputValue?: string;\n defaultInputValue?: string;\n onInputValueChange?: (value: string) => void;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, reason: ComboboxOpenChangeReason) => void;\n /** Localized content rendered when filtering returns no items. */\n emptyMessage: ReactNode;\n loading?: boolean;\n /** Localized content announced while options are loading. */\n loadingMessage: ReactNode;\n /** Localized native-validation message for an empty required selection. */\n selectionRequiredMessage: string;\n openOnFocus?: boolean;\n size?: SelectSize;\n density?: SelectDensity;\n /** Logical listbox alignment against the input; automatically mirrors in RTL. */\n align?: \"start\" | \"end\";\n fieldClassName?: string;\n portalContainer?: HTMLElement;\n }>;\n\nfunction nextEnabled(\n items: readonly ComboboxItem[],\n current: number,\n direction: 1 | -1,\n): number {\n if (items.length === 0) return -1;\n for (let offset = 1; offset <= items.length; offset += 1) {\n const candidate = (current + direction * offset + items.length) % items.length;\n if (!items[candidate]?.disabled) return candidate;\n }\n return -1;\n}\n\nexport const Combobox = forwardRef<HTMLInputElement, ComboboxProps>(\n function Combobox(\n {\n id,\n name,\n label,\n description,\n error,\n items,\n value: valueProp,\n defaultValue = \"\",\n onValueChange,\n inputValue: inputValueProp,\n defaultInputValue,\n onInputValueChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n emptyMessage,\n loading = false,\n loadingMessage,\n selectionRequiredMessage,\n openOnFocus = true,\n size = comboboxRecipe.defaults.size,\n density = comboboxRecipe.defaults.density,\n align = \"start\",\n fieldClassName,\n portalContainer,\n className,\n disabled = false,\n required = false,\n autoComplete = \"off\",\n onFocus,\n onBlur,\n onKeyDown,\n ...props\n },\n forwardedRef,\n ) {\n validateOptions(items, \"Combobox\");\n const controlId = useControlId(id, \"combobox\");\n const listboxId = `${controlId}-listbox`;\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const suppressFocusOpenRef = useRef(false);\n const listboxRef = useRef<HTMLDivElement>(null);\n const [listboxNode, setListboxNode] = useState<HTMLDivElement | null>(null);\n const popupPosition = useAnchoredPopup(inputRef, listboxNode, {\n align,\n matchAnchorWidth: true,\n zIndex: 800,\n });\n const setListboxRef = useCallback((node: HTMLDivElement | null) => {\n listboxRef.current = node;\n setListboxNode(node);\n }, []);\n const [selectedValue, setSelectedValue] = useControllableState({\n ...(valueProp === undefined ? {} : { value: valueProp }),\n defaultValue,\n ...(onValueChange === undefined ? {} : { onChange: onValueChange }),\n });\n const selectedItem = items.find((item) => item.value === selectedValue);\n if (selectedValue !== \"\" && (!selectedItem || selectedItem.disabled)) {\n throw new RangeError(\n `Combobox value must identify an enabled option: ${selectedValue}`,\n );\n }\n if (required && selectionRequiredMessage.trim().length === 0) {\n throw new TypeError(\"Combobox selectionRequiredMessage must not be empty\");\n }\n const [query, setQuery] = useControllableState({\n ...(inputValueProp === undefined ? {} : { value: inputValueProp }),\n defaultValue: defaultInputValue ?? selectedItem?.label ?? \"\",\n ...(onInputValueChange === undefined ? {} : { onChange: onInputValueChange }),\n });\n const [open, setOpenState] = useControllableState({\n ...(openProp === undefined ? {} : { value: openProp }),\n defaultValue: defaultOpen,\n });\n const [activeIndex, setActiveIndex] = useState(-1);\n useEffect(() => {\n inputRef.current?.setCustomValidity(\n required && selectedValue === \"\" ? selectionRequiredMessage : \"\",\n );\n }, [required, selectedValue, selectionRequiredMessage]);\n const normalizedQuery = query.trim().toLocaleLowerCase();\n const filteredItems = useMemo(\n () =>\n items.filter((item) => {\n if (normalizedQuery.length === 0) return true;\n const searchable = [item.label, ...(item.keywords ?? [])]\n .join(\" \")\n .toLocaleLowerCase();\n return searchable.includes(normalizedQuery);\n }),\n [items, normalizedQuery],\n );\n\n const setOpen = (next: boolean, reason: ComboboxOpenChangeReason) => {\n if (next === open) return;\n setOpenState(next);\n onOpenChange?.(next, reason);\n if (!next) setActiveIndex(-1);\n };\n const firstEnabled = () => filteredItems.findIndex((item) => !item.disabled);\n const lastEnabled = () => {\n for (let index = filteredItems.length - 1; index >= 0; index -= 1) {\n if (!filteredItems[index]?.disabled) return index;\n }\n return -1;\n };\n const selectItem = (item: ComboboxItem) => {\n if (item.disabled) return;\n setSelectedValue(item.value);\n setQuery(item.label);\n setOpen(false, \"selection\");\n queueMicrotask(() => {\n const input = inputRef.current;\n if (!input || document.activeElement === input) return;\n suppressFocusOpenRef.current = true;\n input.focus();\n queueMicrotask(() => {\n suppressFocusOpenRef.current = false;\n });\n });\n };\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (\n loading &&\n [\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\", \"Enter\"].includes(event.key)\n ) {\n event.preventDefault();\n return;\n }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (!open) {\n setOpen(true, \"keyboard\");\n setActiveIndex(\n event.key === \"ArrowDown\" ? firstEnabled() : lastEnabled(),\n );\n return;\n }\n setActiveIndex((current) =>\n nextEnabled(filteredItems, current, event.key === \"ArrowDown\" ? 1 : -1),\n );\n } else if (open && event.key === \"Home\") {\n event.preventDefault();\n setActiveIndex(firstEnabled());\n } else if (open && event.key === \"End\") {\n event.preventDefault();\n setActiveIndex(lastEnabled());\n } else if (open && event.key === \"Enter\" && activeIndex >= 0) {\n event.preventDefault();\n const item = filteredItems[activeIndex];\n if (item) selectItem(item);\n } else if (open && event.key === \"Escape\") {\n event.preventDefault();\n setOpen(false, \"escape\");\n } else if (open && event.key === \"Tab\") {\n setOpen(false, \"blur\");\n }\n };\n const activeItem = activeIndex >= 0 ? filteredItems[activeIndex] : undefined;\n\n return (\n <Field\n controlId={controlId}\n label={label}\n description={description}\n error={error}\n required={required}\n disabled={disabled}\n className={classNames(\"hjm-combobox\", fieldClassName)}\n data-size={size}\n data-density={density}\n data-state={disabled ? \"disabled\" : open ? \"open\" : \"closed\"}\n >\n {(controlProps) => (\n <div\n ref={rootRef}\n className=\"hjm-combobox__anchor\"\n onBlur={(event) => {\n if (\n !event.currentTarget.contains(event.relatedTarget) &&\n !listboxRef.current?.contains(event.relatedTarget)\n ) setOpen(false, \"blur\");\n }}\n >\n <div className=\"hjm-field__control hjm-combobox__control\">\n <input\n {...props}\n {...controlProps}\n ref={composeRefs(forwardedRef, inputRef)}\n className={classNames(\"hjm-field__input hjm-combobox__input\", className)}\n value={query}\n role=\"combobox\"\n autoComplete={autoComplete}\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-expanded={open}\n aria-activedescendant={\n open && activeItem ? `${controlId}-option-${activeIndex}` : undefined\n }\n onFocus={(event) => {\n onFocus?.(event);\n if (\n !event.defaultPrevented &&\n openOnFocus &&\n !suppressFocusOpenRef.current\n ) setOpen(true, \"focus\");\n }}\n onBlur={onBlur}\n onChange={(event) => {\n setQuery(event.target.value);\n setSelectedValue(\"\");\n setOpen(true, \"input\");\n setActiveIndex(-1);\n }}\n onKeyDown={handleKeyDown}\n />\n {loading ? <span className=\"hjm-combobox__spinner\" aria-hidden=\"true\" /> : null}\n <span className=\"hjm-select__indicator\" aria-hidden=\"true\">⌄</span>\n </div>\n {name ? <input type=\"hidden\" name={name} value={selectedValue} /> : null}\n {open ? (\n <AnchoredPortal\n anchorRef={inputRef}\n ssrFallback=\"inline\"\n {...(portalContainer === undefined ? {} : { container: portalContainer })}\n >\n <div\n ref={setListboxRef}\n id={listboxId}\n role=\"listbox\"\n className=\"hjm-combobox__listbox\"\n data-density={density}\n aria-label={typeof label === \"string\" ? label : undefined}\n aria-busy={loading || undefined}\n data-placement={popupPosition.placement}\n data-align={popupPosition.align}\n style={popupPosition.style}\n >\n {loading ? (\n <div className=\"hjm-combobox__message\" role=\"status\">{loadingMessage}</div>\n ) : filteredItems.length === 0 ? (\n <div className=\"hjm-combobox__message\">{emptyMessage}</div>\n ) : (\n filteredItems.map((item, index) => (\n <div\n key={item.value}\n id={`${controlId}-option-${index}`}\n role=\"option\"\n aria-selected={item.value === selectedValue}\n aria-disabled={item.disabled || undefined}\n className=\"hjm-combobox__option\"\n data-state={\n item.disabled\n ? \"disabled\"\n : index === activeIndex\n ? \"active\"\n : item.value === selectedValue\n ? \"selected\"\n : \"idle\"\n }\n onMouseDown={(event) => event.preventDefault()}\n onMouseMove={() => {\n if (!item.disabled) setActiveIndex(index);\n }}\n onClick={() => selectItem(item)}\n >\n {item.label}\n </div>\n ))\n )}\n </div>\n </AnchoredPortal>\n ) : null}\n </div>\n )}\n </Field>\n );\n },\n);\n\nexport type FormSubmitHandler = (\n event: FormEvent<HTMLFormElement>,\n) => void | Promise<void>;\n\nexport type FormProps = Omit<FormHTMLAttributes<HTMLFormElement>, \"onSubmit\"> &\n Readonly<{\n onSubmit: FormSubmitHandler;\n busy?: boolean;\n formError?: ReactNode;\n actions?: ReactNode;\n density?: FormDensity;\n }>;\n\nexport const Form = forwardRef<HTMLFormElement, FormProps>(function Form(\n {\n onSubmit,\n busy = false,\n formError,\n actions,\n density = formRecipe.defaults.density,\n className,\n children,\n ...props\n },\n ref,\n) {\n const [submitting, setSubmitting] = useState(false);\n const submittingRef = useRef(false);\n const effectiveBusy = busy || submitting;\n\n const handleSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (effectiveBusy || submittingRef.current) return;\n let result: void | Promise<void>;\n try {\n result = onSubmit(event);\n } catch (error) {\n throw error;\n }\n if (result && typeof result.then === \"function\") {\n submittingRef.current = true;\n setSubmitting(true);\n void Promise.resolve(result)\n .catch(() => undefined)\n .finally(() => {\n submittingRef.current = false;\n setSubmitting(false);\n });\n }\n };\n\n return (\n <form\n {...props}\n ref={ref}\n className={classNames(\"hjm-form\", className)}\n data-density={density}\n data-state={effectiveBusy ? \"busy\" : formError ? \"error\" : \"idle\"}\n aria-busy={effectiveBusy || undefined}\n onSubmit={handleSubmit}\n >\n <fieldset className=\"hjm-form__fieldset\" disabled={effectiveBusy}>\n <div className=\"hjm-form__fields\">{children}</div>\n {formError ? (\n <div className=\"hjm-form__error\" role=\"alert\">{formError}</div>\n ) : null}\n {actions ? <div className=\"hjm-form__actions\">{actions}</div> : null}\n </fieldset>\n </form>\n );\n});\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type BreadcrumbItemDescriptor } from "@hjmds/design-contracts/components/breadcrumb";
|
|
2
|
+
import { type ComposePaginationAccessibleName, type PaginationChangeHandler, type PaginationDescriptor, type PaginationLabels } from "@hjmds/design-contracts/components/pagination";
|
|
3
|
+
import { type HTMLAttributes, type ReactElement, type ReactNode, type RefAttributes } from "react";
|
|
4
|
+
export type BreadcrumbProps<Id extends string = string> = Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
|
|
5
|
+
label: string;
|
|
6
|
+
items: readonly BreadcrumbItemDescriptor<Id>[];
|
|
7
|
+
separator?: ReactNode;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const Breadcrumb: <Id extends string = string>(props: BreadcrumbProps<Id> & RefAttributes<HTMLElement>) => ReactElement;
|
|
10
|
+
export type PaginationProps = Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
|
|
11
|
+
label: string;
|
|
12
|
+
descriptor: PaginationDescriptor;
|
|
13
|
+
labels: PaginationLabels;
|
|
14
|
+
composeAccessibleName: ComposePaginationAccessibleName;
|
|
15
|
+
onPageChange: PaginationChangeHandler;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const Pagination: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
|
|
18
|
+
label: string;
|
|
19
|
+
descriptor: PaginationDescriptor;
|
|
20
|
+
labels: PaginationLabels;
|
|
21
|
+
composeAccessibleName: ComposePaginationAccessibleName;
|
|
22
|
+
onPageChange: PaginationChangeHandler;
|
|
23
|
+
}> & RefAttributes<HTMLElement>>;
|
|
24
|
+
//# sourceMappingURL=advanced-navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced-navigation.d.ts","sourceRoot":"","sources":["../src/advanced-navigation.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACtB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAGf,MAAM,MAAM,eAAe,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CAC5D,cAAc,CAAC,WAAW,CAAC,EAC3B,UAAU,CACX,GACC,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,wBAAwB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC,CAAC;AAiDL,eAAO,MAAM,UAAU,EAAkC,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAClF,KAAK,EAAE,eAAe,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,KACpD,YAAY,CAAC;AAElB,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,GACzE,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,oBAAoB,CAAC;IACjC,MAAM,EAAE,gBAAgB,CAAC;IACzB,qBAAqB,EAAE,+BAA+B,CAAC;IACvD,YAAY,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEL,eAAO,MAAM,UAAU;WAPZ,MAAM;gBACD,oBAAoB;YACxB,gBAAgB;2BACD,+BAA+B;kBACxC,uBAAuB;gCA6ExC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { resolveBreadcrumbDescriptor, } from "@hjmds/design-contracts/components/breadcrumb";
|
|
3
|
+
import { resolvePaginationDescriptor, } from "@hjmds/design-contracts/components/pagination";
|
|
4
|
+
import { forwardRef, } from "react";
|
|
5
|
+
import { classNames } from "./internal.js";
|
|
6
|
+
function BreadcrumbInner({ label, items, separator = "›", className, ...props }, ref) {
|
|
7
|
+
if (label.trim().length === 0)
|
|
8
|
+
throw new TypeError("Breadcrumb label must not be empty");
|
|
9
|
+
const descriptor = resolveBreadcrumbDescriptor({ items });
|
|
10
|
+
return (_jsx("nav", { ...props, ref: ref, className: classNames("hjm-breadcrumb", className), "aria-label": label, children: _jsx("ol", { className: "hjm-breadcrumb__list", children: descriptor.items.map((item, index) => (_jsxs("li", { className: "hjm-breadcrumb__item", children: [item.current ? (_jsx("span", { className: "hjm-breadcrumb__current", "aria-current": "page", children: item.label })) : (_jsx("a", { className: "hjm-breadcrumb__link", href: item.destination.href, "data-destination": item.destination.kind, children: item.label })), index < descriptor.items.length - 1 ? (_jsx("span", { className: "hjm-breadcrumb__separator", "aria-hidden": "true", children: separator })) : null] }, item.id))) }) }));
|
|
11
|
+
}
|
|
12
|
+
export const Breadcrumb = forwardRef(BreadcrumbInner);
|
|
13
|
+
export const Pagination = forwardRef(function Pagination({ label, descriptor, labels, composeAccessibleName, onPageChange, className, ...props }, ref) {
|
|
14
|
+
if (label.trim().length === 0)
|
|
15
|
+
throw new TypeError("Pagination label must not be empty");
|
|
16
|
+
const resolved = resolvePaginationDescriptor(descriptor, {
|
|
17
|
+
labels,
|
|
18
|
+
composeAccessibleName,
|
|
19
|
+
});
|
|
20
|
+
return (_jsx("nav", { ...props, ref: ref, className: classNames("hjm-pagination", className), "aria-label": label, children: _jsxs("ul", { className: "hjm-pagination__list", children: [_jsx("li", { children: _jsx("button", { type: "button", className: "hjm-pagination__item hjm-pagination__previous", "aria-label": resolved.labels.previous, disabled: !resolved.hasPrevious, onClick: () => onPageChange(resolved.currentPage - 1, "previous"), children: _jsx("span", { "aria-hidden": "true", children: "\u2039" }) }) }), resolved.items.map((item, index) => item.type === "ellipsis" ? (_jsx("li", { children: _jsx("span", { className: "hjm-pagination__ellipsis", "aria-hidden": "true", children: "\u2026" }) }, `ellipsis-${index}`)) : (_jsx("li", { children: _jsx("button", { type: "button", className: "hjm-pagination__item", "data-state": item.current ? "current" : "idle", "aria-current": item.current ? "page" : undefined, "aria-label": item.accessibleName, onClick: () => {
|
|
21
|
+
if (!item.current)
|
|
22
|
+
onPageChange(item.page, "page");
|
|
23
|
+
}, children: item.page }) }, item.page))), _jsx("li", { children: _jsx("button", { type: "button", className: "hjm-pagination__item hjm-pagination__next", "aria-label": resolved.labels.next, disabled: !resolved.hasNext, onClick: () => onPageChange(resolved.currentPage + 1, "next"), children: _jsx("span", { "aria-hidden": "true", children: "\u203A" }) }) })] }) }));
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=advanced-navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced-navigation.js","sourceRoot":"","sources":["../src/advanced-navigation.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveBreadcrumbDescriptor,\n type BreadcrumbItemDescriptor,\n} from \"@hjmds/design-contracts/components/breadcrumb\";\nimport {\n resolvePaginationDescriptor,\n type ComposePaginationAccessibleName,\n type PaginationChangeHandler,\n type PaginationDescriptor,\n type PaginationLabels,\n} from \"@hjmds/design-contracts/components/pagination\";\nimport {\n forwardRef,\n type ForwardedRef,\n type HTMLAttributes,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n} from \"react\";\nimport { classNames } from \"./internal.js\";\n\nexport type BreadcrumbProps<Id extends string = string> = Omit<\n HTMLAttributes<HTMLElement>,\n \"children\"\n> &\n Readonly<{\n label: string;\n items: readonly BreadcrumbItemDescriptor<Id>[];\n separator?: ReactNode;\n }>;\n\nfunction BreadcrumbInner<Id extends string>(\n {\n label,\n items,\n separator = \"›\",\n className,\n ...props\n }: BreadcrumbProps<Id>,\n ref: ForwardedRef<HTMLElement>,\n) {\n if (label.trim().length === 0) throw new TypeError(\"Breadcrumb label must not be empty\");\n const descriptor = resolveBreadcrumbDescriptor({ items });\n return (\n <nav\n {...props}\n ref={ref}\n className={classNames(\"hjm-breadcrumb\", className)}\n aria-label={label}\n >\n <ol className=\"hjm-breadcrumb__list\">\n {descriptor.items.map((item, index) => (\n <li key={item.id} className=\"hjm-breadcrumb__item\">\n {item.current ? (\n <span className=\"hjm-breadcrumb__current\" aria-current=\"page\">\n {item.label}\n </span>\n ) : (\n <a\n className=\"hjm-breadcrumb__link\"\n href={item.destination.href}\n data-destination={item.destination.kind}\n >\n {item.label}\n </a>\n )}\n {index < descriptor.items.length - 1 ? (\n <span className=\"hjm-breadcrumb__separator\" aria-hidden=\"true\">\n {separator}\n </span>\n ) : null}\n </li>\n ))}\n </ol>\n </nav>\n );\n}\n\nexport const Breadcrumb = forwardRef(BreadcrumbInner) as <Id extends string = string>(\n props: BreadcrumbProps<Id> & RefAttributes<HTMLElement>,\n) => ReactElement;\n\nexport type PaginationProps = Omit<HTMLAttributes<HTMLElement>, \"children\"> &\n Readonly<{\n label: string;\n descriptor: PaginationDescriptor;\n labels: PaginationLabels;\n composeAccessibleName: ComposePaginationAccessibleName;\n onPageChange: PaginationChangeHandler;\n }>;\n\nexport const Pagination = forwardRef<HTMLElement, PaginationProps>(\n function Pagination(\n {\n label,\n descriptor,\n labels,\n composeAccessibleName,\n onPageChange,\n className,\n ...props\n },\n ref,\n ) {\n if (label.trim().length === 0) throw new TypeError(\"Pagination label must not be empty\");\n const resolved = resolvePaginationDescriptor(descriptor, {\n labels,\n composeAccessibleName,\n });\n return (\n <nav\n {...props}\n ref={ref}\n className={classNames(\"hjm-pagination\", className)}\n aria-label={label}\n >\n <ul className=\"hjm-pagination__list\">\n <li>\n <button\n type=\"button\"\n className=\"hjm-pagination__item hjm-pagination__previous\"\n aria-label={resolved.labels.previous}\n disabled={!resolved.hasPrevious}\n onClick={() => onPageChange(resolved.currentPage - 1, \"previous\")}\n >\n <span aria-hidden=\"true\">‹</span>\n </button>\n </li>\n {resolved.items.map((item, index) =>\n item.type === \"ellipsis\" ? (\n <li key={`ellipsis-${index}`}>\n <span className=\"hjm-pagination__ellipsis\" aria-hidden=\"true\">…</span>\n </li>\n ) : (\n <li key={item.page}>\n <button\n type=\"button\"\n className=\"hjm-pagination__item\"\n data-state={item.current ? \"current\" : \"idle\"}\n aria-current={item.current ? \"page\" : undefined}\n aria-label={item.accessibleName}\n onClick={() => {\n if (!item.current) onPageChange(item.page, \"page\");\n }}\n >\n {item.page}\n </button>\n </li>\n ),\n )}\n <li>\n <button\n type=\"button\"\n className=\"hjm-pagination__item hjm-pagination__next\"\n aria-label={resolved.labels.next}\n disabled={!resolved.hasNext}\n onClick={() => onPageChange(resolved.currentPage + 1, \"next\")}\n >\n <span aria-hidden=\"true\">›</span>\n </button>\n </li>\n </ul>\n </nav>\n );\n },\n);\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type BottomNavigationActivation, type BottomNavigationConfiguration, type BottomNavigationDescriptor, type ResolvedBottomNavigationItemDescriptor } from "@hjmds/design-contracts/components/bottom-navigation";
|
|
2
|
+
import { type AnchorHTMLAttributes, type HTMLAttributes, type MouseEvent, type ReactElement, type ReactNode, type RefAttributes } from "react";
|
|
3
|
+
export type BottomNavigationIconRenderProps<Key extends string = string, IconName extends string = string> = Readonly<{
|
|
4
|
+
item: ResolvedBottomNavigationItemDescriptor<Key, IconName>;
|
|
5
|
+
name: IconName;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
color: "currentColor";
|
|
8
|
+
size: number;
|
|
9
|
+
strokeWidth: number;
|
|
10
|
+
scale: number;
|
|
11
|
+
}>;
|
|
12
|
+
export type BottomNavigationLinkRenderProps = AnchorHTMLAttributes<HTMLAnchorElement> & Readonly<{
|
|
13
|
+
href: string;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
"data-state": "idle" | "selected";
|
|
16
|
+
}>;
|
|
17
|
+
export type BottomNavigationProps<Key extends string = string, IconName extends string = string> = Omit<HTMLAttributes<HTMLElement>, "aria-label" | "aria-labelledby" | "children" | "dir" | "onChange" | "role"> & Readonly<{
|
|
18
|
+
descriptor: BottomNavigationDescriptor<Key, IconName>;
|
|
19
|
+
configuration?: BottomNavigationConfiguration;
|
|
20
|
+
getHref: (item: ResolvedBottomNavigationItemDescriptor<Key, IconName>) => string;
|
|
21
|
+
renderIcon: (props: BottomNavigationIconRenderProps<Key, IconName>) => ReactNode;
|
|
22
|
+
/** Framework navigation adapter, for example Next.js Link. */
|
|
23
|
+
renderLink?: (props: BottomNavigationLinkRenderProps) => ReactElement;
|
|
24
|
+
primaryAction?: ReactNode;
|
|
25
|
+
onActivate?: (activation: BottomNavigationActivation<Key>) => void;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function isUnmodifiedPrimaryBottomNavigationClick(event: Pick<MouseEvent<HTMLAnchorElement>, "altKey" | "button" | "ctrlKey" | "defaultPrevented" | "metaKey" | "shiftKey">): boolean;
|
|
28
|
+
export declare function shouldHideBottomNavigationForKeyboard(input: Readonly<{
|
|
29
|
+
behavior: "hide" | "remain";
|
|
30
|
+
layoutViewportHeight: number;
|
|
31
|
+
visualViewportHeight: number;
|
|
32
|
+
visualViewportOffsetTop?: number;
|
|
33
|
+
visualViewportScale?: number;
|
|
34
|
+
}>): boolean;
|
|
35
|
+
export declare function getBottomNavigationGridColumn(index: number, itemCount: number, distribution: "equal" | "center-gap"): number | undefined;
|
|
36
|
+
export declare const BottomNavigation: <Key extends string = string, IconName extends string = string>(props: BottomNavigationProps<Key, IconName> & RefAttributes<HTMLElement>) => ReactElement | null;
|
|
37
|
+
//# sourceMappingURL=bottom-navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bottom-navigation.d.ts","sourceRoot":"","sources":["../src/bottom-navigation.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { resolveBottomNavigationActivation, resolveBottomNavigationConfiguration, resolveBottomNavigationDescriptor, } from "@hjmds/design-contracts/components/bottom-navigation";
|
|
3
|
+
import { bottomNavigationRecipe, iconRecipe, } from "@hjmds/design-contracts/recipes";
|
|
4
|
+
import { forwardRef, useEffect, useState, } from "react";
|
|
5
|
+
import { classNames } from "./internal.js";
|
|
6
|
+
import { useOptionalHjmTheme } from "./provider.js";
|
|
7
|
+
const virtualKeyboardMinimumOcclusion = 120;
|
|
8
|
+
export function isUnmodifiedPrimaryBottomNavigationClick(event) {
|
|
9
|
+
return (!event.defaultPrevented &&
|
|
10
|
+
event.button === 0 &&
|
|
11
|
+
!event.metaKey &&
|
|
12
|
+
!event.ctrlKey &&
|
|
13
|
+
!event.shiftKey &&
|
|
14
|
+
!event.altKey);
|
|
15
|
+
}
|
|
16
|
+
export function shouldHideBottomNavigationForKeyboard(input) {
|
|
17
|
+
if (input.behavior !== "hide")
|
|
18
|
+
return false;
|
|
19
|
+
if ((input.visualViewportScale ?? 1) !== 1)
|
|
20
|
+
return false;
|
|
21
|
+
const visibleBottom = input.visualViewportHeight + (input.visualViewportOffsetTop ?? 0);
|
|
22
|
+
return layoutViewportHeightIsValid(input.layoutViewportHeight) &&
|
|
23
|
+
input.layoutViewportHeight - visibleBottom >= virtualKeyboardMinimumOcclusion;
|
|
24
|
+
}
|
|
25
|
+
function layoutViewportHeightIsValid(value) {
|
|
26
|
+
return Number.isFinite(value) && value >= 0;
|
|
27
|
+
}
|
|
28
|
+
export function getBottomNavigationGridColumn(index, itemCount, distribution) {
|
|
29
|
+
if (distribution !== "center-gap")
|
|
30
|
+
return undefined;
|
|
31
|
+
const middle = itemCount / 2;
|
|
32
|
+
return index < middle ? index + 1 : index + 2;
|
|
33
|
+
}
|
|
34
|
+
function useKeyboardHidden(behavior) {
|
|
35
|
+
const [hidden, setHidden] = useState(false);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const viewport = window.visualViewport;
|
|
38
|
+
if (!viewport)
|
|
39
|
+
return;
|
|
40
|
+
const update = () => {
|
|
41
|
+
setHidden(shouldHideBottomNavigationForKeyboard({
|
|
42
|
+
behavior,
|
|
43
|
+
layoutViewportHeight: window.innerHeight,
|
|
44
|
+
visualViewportHeight: viewport.height,
|
|
45
|
+
visualViewportOffsetTop: viewport.offsetTop,
|
|
46
|
+
visualViewportScale: viewport.scale,
|
|
47
|
+
}));
|
|
48
|
+
};
|
|
49
|
+
update();
|
|
50
|
+
viewport.addEventListener("resize", update);
|
|
51
|
+
viewport.addEventListener("scroll", update);
|
|
52
|
+
return () => {
|
|
53
|
+
viewport.removeEventListener("resize", update);
|
|
54
|
+
viewport.removeEventListener("scroll", update);
|
|
55
|
+
};
|
|
56
|
+
}, [behavior]);
|
|
57
|
+
return hidden;
|
|
58
|
+
}
|
|
59
|
+
function BottomNavigationInner({ descriptor, configuration = {}, getHref, renderIcon, renderLink, primaryAction, onActivate, className, style, ...props }, ref) {
|
|
60
|
+
const resolved = resolveBottomNavigationDescriptor(descriptor);
|
|
61
|
+
const theme = useOptionalHjmTheme();
|
|
62
|
+
const direction = configuration.direction ?? theme?.environment.direction;
|
|
63
|
+
const presentation = resolveBottomNavigationConfiguration({
|
|
64
|
+
...configuration,
|
|
65
|
+
...(direction === undefined ? {} : { direction }),
|
|
66
|
+
}, resolved.items.length);
|
|
67
|
+
const hidden = useKeyboardHidden(presentation.keyboardBehavior);
|
|
68
|
+
const density = bottomNavigationRecipe.density[presentation.density];
|
|
69
|
+
const presentationRecipe = bottomNavigationRecipe.presentations[presentation.presentation];
|
|
70
|
+
const centerGap = bottomNavigationRecipe.distributions[presentation.distribution].centerGap;
|
|
71
|
+
const iconSize = iconRecipe.sizes[density.icon];
|
|
72
|
+
if (hidden)
|
|
73
|
+
return null;
|
|
74
|
+
return (_jsx("nav", { ...props, ref: ref, "aria-label": resolved.accessibilityLabel, className: classNames("hjm-bottom-navigation", className), "data-density": presentation.density, "data-distribution": presentation.distribution, "data-keyboard-behavior": presentation.keyboardBehavior, "data-presentation": presentation.presentation, dir: presentation.direction, style: {
|
|
75
|
+
"--hjm-bottom-navigation-center-gap": `${centerGap}px`,
|
|
76
|
+
"--hjm-bottom-navigation-columns": resolved.items.length,
|
|
77
|
+
"--hjm-bottom-navigation-half-columns": resolved.items.length / 2,
|
|
78
|
+
"--hjm-bottom-navigation-item-gap": `${density.gap}px`,
|
|
79
|
+
"--hjm-bottom-navigation-item-min-height": `${density.itemMinHeight}px`,
|
|
80
|
+
"--hjm-bottom-navigation-item-min-width": `${density.itemMinWidth}px`,
|
|
81
|
+
"--hjm-bottom-navigation-item-padding": `${density.padding}px`,
|
|
82
|
+
"--hjm-bottom-navigation-max-width": presentationRecipe.maxWidth
|
|
83
|
+
? `${presentationRecipe.maxWidth}px`
|
|
84
|
+
: "none",
|
|
85
|
+
"--hjm-bottom-navigation-outer-inline": `${presentationRecipe.outerPaddingHorizontal}px`,
|
|
86
|
+
"--hjm-bottom-navigation-outer-top": `${presentationRecipe.outerPaddingTop}px`,
|
|
87
|
+
...style,
|
|
88
|
+
}, children: _jsxs("div", { className: "hjm-bottom-navigation__surface", children: [_jsx("ul", { className: "hjm-bottom-navigation__list", children: resolved.items.map((item, index) => {
|
|
89
|
+
const selected = item.id === resolved.selectedKey;
|
|
90
|
+
const content = (_jsxs(_Fragment, { children: [_jsxs("span", { "aria-hidden": "true", className: "hjm-bottom-navigation__indicator", "data-state": selected ? "selected" : "idle", children: [renderIcon({
|
|
91
|
+
item,
|
|
92
|
+
name: item.icon.name,
|
|
93
|
+
selected,
|
|
94
|
+
color: "currentColor",
|
|
95
|
+
size: iconSize,
|
|
96
|
+
strokeWidth: selected
|
|
97
|
+
? bottomNavigationRecipe.icon.selectedEmphasis.strokeWidth.selected
|
|
98
|
+
: bottomNavigationRecipe.icon.selectedEmphasis.strokeWidth.idle,
|
|
99
|
+
scale: selected
|
|
100
|
+
? bottomNavigationRecipe.icon.selectedEmphasis.scale.selected
|
|
101
|
+
: bottomNavigationRecipe.icon.selectedEmphasis.scale.idle,
|
|
102
|
+
}), item.badge ? (_jsx("span", { "aria-hidden": "true", className: "hjm-bottom-navigation__badge", children: item.badge.visibleLabel })) : null] }), _jsx("span", { className: "hjm-bottom-navigation__label", children: item.label })] }));
|
|
103
|
+
const itemStyle = {
|
|
104
|
+
gridColumn: getBottomNavigationGridColumn(index, resolved.items.length, presentation.distribution),
|
|
105
|
+
};
|
|
106
|
+
if (item.disabled) {
|
|
107
|
+
return (_jsx("li", { style: itemStyle, children: _jsx("span", { "aria-disabled": "true", "aria-label": item.resolvedAccessibilityLabel, className: "hjm-bottom-navigation__item", "data-state": "disabled", role: "link", children: content }) }, item.id));
|
|
108
|
+
}
|
|
109
|
+
const href = getHref(item);
|
|
110
|
+
if (!href.trim()) {
|
|
111
|
+
throw new TypeError(`BottomNavigation href must not be empty: ${item.id}`);
|
|
112
|
+
}
|
|
113
|
+
const linkProps = {
|
|
114
|
+
"aria-current": selected ? "page" : undefined,
|
|
115
|
+
"aria-label": item.resolvedAccessibilityLabel,
|
|
116
|
+
className: "hjm-bottom-navigation__item",
|
|
117
|
+
"data-state": selected ? "selected" : "idle",
|
|
118
|
+
href,
|
|
119
|
+
onClick: (event) => {
|
|
120
|
+
if (!isUnmodifiedPrimaryBottomNavigationClick(event))
|
|
121
|
+
return;
|
|
122
|
+
const activation = resolveBottomNavigationActivation(descriptor, item.id);
|
|
123
|
+
if (activation)
|
|
124
|
+
onActivate?.(activation);
|
|
125
|
+
},
|
|
126
|
+
children: content,
|
|
127
|
+
};
|
|
128
|
+
return (_jsx("li", { style: itemStyle, children: renderLink ? renderLink(linkProps) : _jsx("a", { ...linkProps }) }, item.id));
|
|
129
|
+
}) }), primaryAction ? (_jsx("div", { className: "hjm-bottom-navigation__primary-action", children: primaryAction })) : null] }) }));
|
|
130
|
+
}
|
|
131
|
+
export const BottomNavigation = forwardRef(BottomNavigationInner);
|
|
132
|
+
//# sourceMappingURL=bottom-navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bottom-navigation.js","sourceRoot":"","sources":["../src/bottom-navigation.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,iCAAiC,EACjC,oCAAoC,EACpC,iCAAiC,GAKlC,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EACL,sBAAsB,EACtB,UAAU,GACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,UAAU,EACV,SAAS,EACT,QAAQ,GAST,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAwC5C,MAAM,UAAU,wCAAwC,CACtD,KAGC;IAED,OAAO,CACL,CAAC,KAAK,CAAC,gBAAgB;QACvB,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,KAAK,CAAC,OAAO;QACd,CAAC,KAAK,CAAC,OAAO;QACd,CAAC,KAAK,CAAC,QAAQ;QACf,CAAC,KAAK,CAAC,MAAM,CACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qCAAqC,CAAC,KAMpD;IACA,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC,KAAK,CAAC,mBAAmB,IAAI,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,MAAM,aAAa,GACjB,KAAK,CAAC,oBAAoB,GAAG,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC;IACpE,OAAO,2BAA2B,CAAC,KAAK,CAAC,oBAAoB,CAAC;QAC5D,KAAK,CAAC,oBAAoB,GAAG,aAAa,IAAI,+BAA+B,CAAC;AAClF,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAa;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,KAAa,EACb,SAAiB,EACjB,YAAoC;IAEpC,IAAI,YAAY,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC;IAC7B,OAAO,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,QAA2B;IACpD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC;QACvC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,SAAS,CACP,qCAAqC,CAAC;gBACpC,QAAQ;gBACR,oBAAoB,EAAE,MAAM,CAAC,WAAW;gBACxC,oBAAoB,EAAE,QAAQ,CAAC,MAAM;gBACrC,uBAAuB,EAAE,QAAQ,CAAC,SAAS;gBAC3C,mBAAmB,EAAE,QAAQ,CAAC,KAAK;aACpC,CAAC,CACH,CAAC;QACJ,CAAC,CAAC;QACF,MAAM,EAAE,CAAC;QACT,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,QAAQ,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjD,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACf,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,qBAAqB,CAI5B,EACE,UAAU,EACV,aAAa,GAAG,EAAE,EAClB,OAAO,EACP,UAAU,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EAC6B,EACvC,GAA8B;IAE9B,MAAM,QAAQ,GAAG,iCAAiC,CAAC,UAAU,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,IAAI,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC;IAC1E,MAAM,YAAY,GAAG,oCAAoC,CACvD;QACE,GAAG,aAAa;QAChB,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC;KAClD,EACD,QAAQ,CAAC,KAAK,CAAC,MAAM,CACtB,CAAC;IACF,MAAM,MAAM,GAAG,iBAAiB,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,sBAAsB,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,kBAAkB,GACtB,sBAAsB,CAAC,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IAClE,MAAM,SAAS,GACb,sBAAsB,CAAC,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC;IAC5E,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO,CACL,iBACM,KAAK,EACT,GAAG,EAAE,GAAG,gBACI,QAAQ,CAAC,kBAAkB,EACvC,SAAS,EAAE,UAAU,CAAC,uBAAuB,EAAE,SAAS,CAAC,kBAC3C,YAAY,CAAC,OAAO,uBACf,YAAY,CAAC,YAAY,4BACpB,YAAY,CAAC,gBAAgB,uBAClC,YAAY,CAAC,YAAY,EAC5C,GAAG,EAAE,YAAY,CAAC,SAAS,EAC3B,KAAK,EAAE;YACL,oCAAoC,EAAE,GAAG,SAAS,IAAI;YACtD,iCAAiC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM;YACxD,sCAAsC,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YACjE,kCAAkC,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI;YACtD,yCAAyC,EAAE,GAAG,OAAO,CAAC,aAAa,IAAI;YACvE,wCAAwC,EAAE,GAAG,OAAO,CAAC,YAAY,IAAI;YACrE,sCAAsC,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI;YAC9D,mCAAmC,EAAE,kBAAkB,CAAC,QAAQ;gBAC9D,CAAC,CAAC,GAAG,kBAAkB,CAAC,QAAQ,IAAI;gBACpC,CAAC,CAAC,MAAM;YACV,sCAAsC,EAAE,GAAG,kBAAkB,CAAC,sBAAsB,IAAI;YACxF,mCAAmC,EAAE,GAAG,kBAAkB,CAAC,eAAe,IAAI;YAC9E,GAAG,KAAK;SACQ,YAElB,eAAK,SAAS,EAAC,gCAAgC,aAC7C,aAAI,SAAS,EAAC,6BAA6B,YACxC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,WAAW,CAAC;wBAClD,MAAM,OAAO,GAAG,CACd,8BACE,+BACc,MAAM,EAClB,SAAS,EAAC,kCAAkC,gBAChC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,aAEzC,UAAU,CAAC;4CACV,IAAI;4CACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;4CACpB,QAAQ;4CACR,KAAK,EAAE,cAAc;4CACrB,IAAI,EAAE,QAAQ;4CACd,WAAW,EAAE,QAAQ;gDACnB,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,QAAQ;gDACnE,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI;4CACjE,KAAK,EAAE,QAAQ;gDACb,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ;gDAC7D,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI;yCAC5D,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CACZ,8BAAkB,MAAM,EAAC,SAAS,EAAC,8BAA8B,YAC9D,IAAI,CAAC,KAAK,CAAC,YAAY,GACnB,CACR,CAAC,CAAC,CAAC,IAAI,IACH,EACP,eAAM,SAAS,EAAC,8BAA8B,YAAE,IAAI,CAAC,KAAK,GAAQ,IACjE,CACJ,CAAC;wBACF,MAAM,SAAS,GAAG;4BAChB,UAAU,EAAE,6BAA6B,CACvC,KAAK,EACL,QAAQ,CAAC,KAAK,CAAC,MAAM,EACrB,YAAY,CAAC,YAAY,CAC1B;yBACF,CAAC;wBACF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;4BAClB,OAAO,CACL,aAAkB,KAAK,EAAE,SAAS,YAChC,gCACgB,MAAM,gBACR,IAAI,CAAC,0BAA0B,EAC3C,SAAS,EAAC,6BAA6B,gBAC5B,UAAU,EACrB,IAAI,EAAC,MAAM,YAEV,OAAO,GACH,IATA,IAAI,CAAC,EAAE,CAUX,CACN,CAAC;wBACJ,CAAC;wBACD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;wBAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;4BACjB,MAAM,IAAI,SAAS,CAAC,4CAA4C,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC7E,CAAC;wBACD,MAAM,SAAS,GAAoC;4BACjD,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;4BAC7C,YAAY,EAAE,IAAI,CAAC,0BAA0B;4BAC7C,SAAS,EAAE,6BAA6B;4BACxC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;4BAC5C,IAAI;4BACJ,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gCACjB,IAAI,CAAC,wCAAwC,CAAC,KAAK,CAAC;oCAAE,OAAO;gCAC7D,MAAM,UAAU,GAAG,iCAAiC,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;gCAC1E,IAAI,UAAU;oCAAE,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC;4BAC3C,CAAC;4BACD,QAAQ,EAAE,OAAO;yBAClB,CAAC;wBACF,OAAO,CACL,aAAkB,KAAK,EAAE,SAAS,YAC/B,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,eAAO,SAAS,GAAI,IADnD,IAAI,CAAC,EAAE,CAEX,CACN,CAAC;oBACJ,CAAC,CAAC,GACC,EACJ,aAAa,CAAC,CAAC,CAAC,CACf,cAAK,SAAS,EAAC,uCAAuC,YAAE,aAAa,GAAO,CAC7E,CAAC,CAAC,CAAC,IAAI,IACJ,GACF,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC,qBAAqB,CAKxC,CAAC","sourcesContent":["import {\n resolveBottomNavigationActivation,\n resolveBottomNavigationConfiguration,\n resolveBottomNavigationDescriptor,\n type BottomNavigationActivation,\n type BottomNavigationConfiguration,\n type BottomNavigationDescriptor,\n type ResolvedBottomNavigationItemDescriptor,\n} from \"@hjmds/design-contracts/components/bottom-navigation\";\nimport {\n bottomNavigationRecipe,\n iconRecipe,\n} from \"@hjmds/design-contracts/recipes\";\nimport {\n forwardRef,\n useEffect,\n useState,\n type AnchorHTMLAttributes,\n type CSSProperties,\n type ForwardedRef,\n type HTMLAttributes,\n type MouseEvent,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n} from \"react\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nconst virtualKeyboardMinimumOcclusion = 120;\n\nexport type BottomNavigationIconRenderProps<\n Key extends string = string,\n IconName extends string = string,\n> = Readonly<{\n item: ResolvedBottomNavigationItemDescriptor<Key, IconName>;\n name: IconName;\n selected: boolean;\n color: \"currentColor\";\n size: number;\n strokeWidth: number;\n scale: number;\n}>;\n\nexport type BottomNavigationLinkRenderProps = AnchorHTMLAttributes<HTMLAnchorElement> &\n Readonly<{\n href: string;\n children: ReactNode;\n \"data-state\": \"idle\" | \"selected\";\n }>;\n\nexport type BottomNavigationProps<\n Key extends string = string,\n IconName extends string = string,\n> = Omit<\n HTMLAttributes<HTMLElement>,\n \"aria-label\" | \"aria-labelledby\" | \"children\" | \"dir\" | \"onChange\" | \"role\"\n> &\n Readonly<{\n descriptor: BottomNavigationDescriptor<Key, IconName>;\n configuration?: BottomNavigationConfiguration;\n getHref: (item: ResolvedBottomNavigationItemDescriptor<Key, IconName>) => string;\n renderIcon: (props: BottomNavigationIconRenderProps<Key, IconName>) => ReactNode;\n /** Framework navigation adapter, for example Next.js Link. */\n renderLink?: (props: BottomNavigationLinkRenderProps) => ReactElement;\n primaryAction?: ReactNode;\n onActivate?: (activation: BottomNavigationActivation<Key>) => void;\n }>;\n\nexport function isUnmodifiedPrimaryBottomNavigationClick(\n event: Pick<\n MouseEvent<HTMLAnchorElement>,\n \"altKey\" | \"button\" | \"ctrlKey\" | \"defaultPrevented\" | \"metaKey\" | \"shiftKey\"\n >,\n): boolean {\n return (\n !event.defaultPrevented &&\n event.button === 0 &&\n !event.metaKey &&\n !event.ctrlKey &&\n !event.shiftKey &&\n !event.altKey\n );\n}\n\nexport function shouldHideBottomNavigationForKeyboard(input: Readonly<{\n behavior: \"hide\" | \"remain\";\n layoutViewportHeight: number;\n visualViewportHeight: number;\n visualViewportOffsetTop?: number;\n visualViewportScale?: number;\n}>): boolean {\n if (input.behavior !== \"hide\") return false;\n if ((input.visualViewportScale ?? 1) !== 1) return false;\n const visibleBottom =\n input.visualViewportHeight + (input.visualViewportOffsetTop ?? 0);\n return layoutViewportHeightIsValid(input.layoutViewportHeight) &&\n input.layoutViewportHeight - visibleBottom >= virtualKeyboardMinimumOcclusion;\n}\n\nfunction layoutViewportHeightIsValid(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\nexport function getBottomNavigationGridColumn(\n index: number,\n itemCount: number,\n distribution: \"equal\" | \"center-gap\",\n): number | undefined {\n if (distribution !== \"center-gap\") return undefined;\n const middle = itemCount / 2;\n return index < middle ? index + 1 : index + 2;\n}\n\nfunction useKeyboardHidden(behavior: \"hide\" | \"remain\"): boolean {\n const [hidden, setHidden] = useState(false);\n useEffect(() => {\n const viewport = window.visualViewport;\n if (!viewport) return;\n const update = () => {\n setHidden(\n shouldHideBottomNavigationForKeyboard({\n behavior,\n layoutViewportHeight: window.innerHeight,\n visualViewportHeight: viewport.height,\n visualViewportOffsetTop: viewport.offsetTop,\n visualViewportScale: viewport.scale,\n }),\n );\n };\n update();\n viewport.addEventListener(\"resize\", update);\n viewport.addEventListener(\"scroll\", update);\n return () => {\n viewport.removeEventListener(\"resize\", update);\n viewport.removeEventListener(\"scroll\", update);\n };\n }, [behavior]);\n return hidden;\n}\n\nfunction BottomNavigationInner<\n Key extends string = string,\n IconName extends string = string,\n>(\n {\n descriptor,\n configuration = {},\n getHref,\n renderIcon,\n renderLink,\n primaryAction,\n onActivate,\n className,\n style,\n ...props\n }: BottomNavigationProps<Key, IconName>,\n ref: ForwardedRef<HTMLElement>,\n) {\n const resolved = resolveBottomNavigationDescriptor(descriptor);\n const theme = useOptionalHjmTheme();\n const direction = configuration.direction ?? theme?.environment.direction;\n const presentation = resolveBottomNavigationConfiguration(\n {\n ...configuration,\n ...(direction === undefined ? {} : { direction }),\n },\n resolved.items.length,\n );\n const hidden = useKeyboardHidden(presentation.keyboardBehavior);\n const density = bottomNavigationRecipe.density[presentation.density];\n const presentationRecipe =\n bottomNavigationRecipe.presentations[presentation.presentation];\n const centerGap =\n bottomNavigationRecipe.distributions[presentation.distribution].centerGap;\n const iconSize = iconRecipe.sizes[density.icon];\n\n if (hidden) return null;\n\n return (\n <nav\n {...props}\n ref={ref}\n aria-label={resolved.accessibilityLabel}\n className={classNames(\"hjm-bottom-navigation\", className)}\n data-density={presentation.density}\n data-distribution={presentation.distribution}\n data-keyboard-behavior={presentation.keyboardBehavior}\n data-presentation={presentation.presentation}\n dir={presentation.direction}\n style={{\n \"--hjm-bottom-navigation-center-gap\": `${centerGap}px`,\n \"--hjm-bottom-navigation-columns\": resolved.items.length,\n \"--hjm-bottom-navigation-half-columns\": resolved.items.length / 2,\n \"--hjm-bottom-navigation-item-gap\": `${density.gap}px`,\n \"--hjm-bottom-navigation-item-min-height\": `${density.itemMinHeight}px`,\n \"--hjm-bottom-navigation-item-min-width\": `${density.itemMinWidth}px`,\n \"--hjm-bottom-navigation-item-padding\": `${density.padding}px`,\n \"--hjm-bottom-navigation-max-width\": presentationRecipe.maxWidth\n ? `${presentationRecipe.maxWidth}px`\n : \"none\",\n \"--hjm-bottom-navigation-outer-inline\": `${presentationRecipe.outerPaddingHorizontal}px`,\n \"--hjm-bottom-navigation-outer-top\": `${presentationRecipe.outerPaddingTop}px`,\n ...style,\n } as CSSProperties}\n >\n <div className=\"hjm-bottom-navigation__surface\">\n <ul className=\"hjm-bottom-navigation__list\">\n {resolved.items.map((item, index) => {\n const selected = item.id === resolved.selectedKey;\n const content = (\n <>\n <span\n aria-hidden=\"true\"\n className=\"hjm-bottom-navigation__indicator\"\n data-state={selected ? \"selected\" : \"idle\"}\n >\n {renderIcon({\n item,\n name: item.icon.name,\n selected,\n color: \"currentColor\",\n size: iconSize,\n strokeWidth: selected\n ? bottomNavigationRecipe.icon.selectedEmphasis.strokeWidth.selected\n : bottomNavigationRecipe.icon.selectedEmphasis.strokeWidth.idle,\n scale: selected\n ? bottomNavigationRecipe.icon.selectedEmphasis.scale.selected\n : bottomNavigationRecipe.icon.selectedEmphasis.scale.idle,\n })}\n {item.badge ? (\n <span aria-hidden=\"true\" className=\"hjm-bottom-navigation__badge\">\n {item.badge.visibleLabel}\n </span>\n ) : null}\n </span>\n <span className=\"hjm-bottom-navigation__label\">{item.label}</span>\n </>\n );\n const itemStyle = {\n gridColumn: getBottomNavigationGridColumn(\n index,\n resolved.items.length,\n presentation.distribution,\n ),\n };\n if (item.disabled) {\n return (\n <li key={item.id} style={itemStyle}>\n <span\n aria-disabled=\"true\"\n aria-label={item.resolvedAccessibilityLabel}\n className=\"hjm-bottom-navigation__item\"\n data-state=\"disabled\"\n role=\"link\"\n >\n {content}\n </span>\n </li>\n );\n }\n const href = getHref(item);\n if (!href.trim()) {\n throw new TypeError(`BottomNavigation href must not be empty: ${item.id}`);\n }\n const linkProps: BottomNavigationLinkRenderProps = {\n \"aria-current\": selected ? \"page\" : undefined,\n \"aria-label\": item.resolvedAccessibilityLabel,\n className: \"hjm-bottom-navigation__item\",\n \"data-state\": selected ? \"selected\" : \"idle\",\n href,\n onClick: (event) => {\n if (!isUnmodifiedPrimaryBottomNavigationClick(event)) return;\n const activation = resolveBottomNavigationActivation(descriptor, item.id);\n if (activation) onActivate?.(activation);\n },\n children: content,\n };\n return (\n <li key={item.id} style={itemStyle}>\n {renderLink ? renderLink(linkProps) : <a {...linkProps} />}\n </li>\n );\n })}\n </ul>\n {primaryAction ? (\n <div className=\"hjm-bottom-navigation__primary-action\">{primaryAction}</div>\n ) : null}\n </div>\n </nav>\n );\n}\n\nexport const BottomNavigation = forwardRef(BottomNavigationInner) as <\n Key extends string = string,\n IconName extends string = string,\n>(\n props: BottomNavigationProps<Key, IconName> & RefAttributes<HTMLElement>,\n) => ReactElement | null;\n"]}
|