@inkra/react 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +213 -2
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/Yozai-OFL.txt +94 -0
- package/dist/Button.d.ts +36 -0
- package/dist/Button.js +89 -0
- package/dist/PencilIcon.d.ts +22 -0
- package/dist/PencilIcon.js +212 -0
- package/dist/PencilLine.d.ts +10 -0
- package/dist/PencilLine.js +85 -0
- package/dist/PencilSurface.d.ts +19 -0
- package/dist/PencilSurface.js +19 -0
- package/dist/PencilText.d.ts +16 -0
- package/dist/PencilText.js +205 -0
- package/dist/accordion/Accordion.d.ts +27 -0
- package/dist/accordion/Accordion.js +40 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/assets/ui-hand-yozai.woff2 +0 -0
- package/dist/breadcrumb/index.d.ts +16 -0
- package/dist/breadcrumb/index.js +25 -0
- package/dist/calendar/index.d.ts +21 -0
- package/dist/calendar/index.js +41 -0
- package/dist/content/index.d.ts +106 -0
- package/dist/content/index.js +80 -0
- package/dist/day-agenda/index.d.ts +43 -0
- package/dist/day-agenda/index.js +47 -0
- package/dist/day-agenda/layout.d.ts +20 -0
- package/dist/day-agenda/layout.js +60 -0
- package/dist/display/index.d.ts +32 -0
- package/dist/display/index.js +59 -0
- package/dist/dropdown-menu/index.d.ts +34 -0
- package/dist/dropdown-menu/index.js +40 -0
- package/dist/form/EditablePencilText.d.ts +13 -0
- package/dist/form/EditablePencilText.js +312 -0
- package/dist/form/Field.d.ts +13 -0
- package/dist/form/Field.js +26 -0
- package/dist/form/Input.d.ts +27 -0
- package/dist/form/Input.js +97 -0
- package/dist/form/InputAction.d.ts +7 -0
- package/dist/form/InputAction.js +24 -0
- package/dist/form/Textarea.d.ts +5 -0
- package/dist/form/Textarea.js +23 -0
- package/dist/form/context.d.ts +12 -0
- package/dist/form/context.js +10 -0
- package/dist/form/controlProps.d.ts +23 -0
- package/dist/form/controlProps.js +20 -0
- package/dist/form/index.d.ts +7 -0
- package/dist/form/index.js +3 -0
- package/dist/form/types.d.ts +6 -0
- package/dist/form/types.js +1 -0
- package/dist/images/layers-2x.png +0 -0
- package/dist/images/layers.png +0 -0
- package/dist/images/marker-icon.png +0 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +22 -0
- package/dist/input-number/index.d.ts +16 -0
- package/dist/input-number/index.js +77 -0
- package/dist/internal/InputActionContext.d.ts +1 -0
- package/dist/internal/InputActionContext.js +3 -0
- package/dist/internal/cache.d.ts +14 -0
- package/dist/internal/cache.js +54 -0
- package/dist/internal/line.d.ts +4 -0
- package/dist/internal/line.js +21 -0
- package/dist/internal/paper.d.ts +1 -0
- package/dist/internal/paper.js +65 -0
- package/dist/internal/random.d.ts +3 -0
- package/dist/internal/random.js +30 -0
- package/dist/internal/recovery.d.ts +1 -0
- package/dist/internal/recovery.js +52 -0
- package/dist/internal/stroke.d.ts +24 -0
- package/dist/internal/stroke.js +144 -0
- package/dist/internal/surfaceRenderer.d.ts +33 -0
- package/dist/internal/surfaceRenderer.js +175 -0
- package/dist/internal/textQueue.d.ts +1 -0
- package/dist/internal/textQueue.js +37 -0
- package/dist/internal/textRenderer.d.ts +27 -0
- package/dist/internal/textRenderer.js +213 -0
- package/dist/internal/travelIcons.d.ts +24 -0
- package/dist/internal/travelIcons.js +14 -0
- package/dist/internal/useBrowserLayoutEffect.d.ts +2 -0
- package/dist/internal/useBrowserLayoutEffect.js +3 -0
- package/dist/internal/usePencilSurface.d.ts +3 -0
- package/dist/internal/usePencilSurface.js +108 -0
- package/dist/internal/useSelectionDrawing.d.ts +2 -0
- package/dist/internal/useSelectionDrawing.js +86 -0
- package/dist/internal/wash.d.ts +16 -0
- package/dist/internal/wash.js +149 -0
- package/dist/journey-map/MapScale.d.ts +4 -0
- package/dist/journey-map/MapScale.js +52 -0
- package/dist/journey-map/index.d.ts +39 -0
- package/dist/journey-map/index.js +195 -0
- package/dist/journey-map/terrain.d.ts +8 -0
- package/dist/journey-map/terrain.js +127 -0
- package/dist/overlay/Overlay.d.ts +65 -0
- package/dist/overlay/Overlay.js +77 -0
- package/dist/overlay/Popover.d.ts +43 -0
- package/dist/overlay/Popover.js +45 -0
- package/dist/overlay/index.d.ts +4 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/themeBridge.d.ts +8 -0
- package/dist/overlay/themeBridge.js +98 -0
- package/dist/pencil.d.ts +10 -0
- package/dist/pencil.js +39 -0
- package/dist/select/MultipleSelect.d.ts +44 -0
- package/dist/select/MultipleSelect.js +124 -0
- package/dist/select/SearchableSelect.d.ts +45 -0
- package/dist/select/SearchableSelect.js +50 -0
- package/dist/select/Select.d.ts +109 -0
- package/dist/select/Select.js +205 -0
- package/dist/select/index.d.ts +4 -0
- package/dist/select/index.js +2 -0
- package/dist/selection/Checkbox.d.ts +9 -0
- package/dist/selection/Checkbox.js +37 -0
- package/dist/selection/RadioGroup.d.ts +23 -0
- package/dist/selection/RadioGroup.js +82 -0
- package/dist/selection/Switch.d.ts +8 -0
- package/dist/selection/Switch.js +36 -0
- package/dist/selection/index.d.ts +6 -0
- package/dist/selection/index.js +3 -0
- package/dist/selection/useNativeChecked.d.ts +13 -0
- package/dist/selection/useNativeChecked.js +27 -0
- package/dist/slider/index.d.ts +11 -0
- package/dist/slider/index.js +45 -0
- package/dist/styles.css +3663 -0
- package/dist/tabs/index.d.ts +22 -0
- package/dist/tabs/index.js +42 -0
- package/dist/tokens.css +355 -0
- package/dist/tooltip/Tooltip.d.ts +24 -0
- package/dist/tooltip/Tooltip.js +32 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/package.json +100 -4
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
3
|
+
import { createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { Button } from '../Button.js';
|
|
5
|
+
import { SearchableSelect } from './SearchableSelect.js';
|
|
6
|
+
import { MultipleSelect, MultipleSelectContext, MultipleSelectRoot } from './MultipleSelect.js';
|
|
7
|
+
import { mergeAriaIds, useFieldContext } from '../form/context.js';
|
|
8
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
9
|
+
import { PencilLine } from '../PencilLine.js';
|
|
10
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
11
|
+
import { PencilText } from '../PencilText.js';
|
|
12
|
+
import { PortalThemeBridge, PortalThemeProvider, usePortalTriggerRef, } from '../overlay/themeBridge.js';
|
|
13
|
+
const chevronDownPaths = [
|
|
14
|
+
{ d: 'M6 9L12 15L18 9' },
|
|
15
|
+
];
|
|
16
|
+
const chevronUpPaths = [
|
|
17
|
+
{ d: 'M6 15L12 9L18 15' },
|
|
18
|
+
];
|
|
19
|
+
const checkPaths = [
|
|
20
|
+
{ d: 'M5 12.5L10 17L19 7' },
|
|
21
|
+
];
|
|
22
|
+
function classes(...values) {
|
|
23
|
+
return values.filter(Boolean).join(' ');
|
|
24
|
+
}
|
|
25
|
+
function isAriaInvalid(value) {
|
|
26
|
+
return value === true || value === 'true' || value === 'grammar' || value === 'spelling';
|
|
27
|
+
}
|
|
28
|
+
function useItemAppearanceVersion() {
|
|
29
|
+
const copyRef = useRef(null);
|
|
30
|
+
const [version, setVersion] = useState(0);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const item = copyRef.current?.closest('[role="option"]');
|
|
33
|
+
if (!item || typeof MutationObserver === 'undefined')
|
|
34
|
+
return undefined;
|
|
35
|
+
const observer = new MutationObserver(() => setVersion(current => current + 1));
|
|
36
|
+
observer.observe(item, {
|
|
37
|
+
attributes: true,
|
|
38
|
+
attributeFilter: ['data-highlighted', 'data-state', 'data-selected'],
|
|
39
|
+
});
|
|
40
|
+
return () => observer.disconnect();
|
|
41
|
+
}, []);
|
|
42
|
+
return { copyRef, version };
|
|
43
|
+
}
|
|
44
|
+
const SelectFieldContext = createContext(null);
|
|
45
|
+
export function SelectRoot({ dir, disabled, invalid = false, readOnly, required, ...props }) {
|
|
46
|
+
const field = useFieldContext();
|
|
47
|
+
const [directionNode, setDirectionNode] = useState(null);
|
|
48
|
+
const [inferredDirection, setInferredDirection] = useState();
|
|
49
|
+
const captureDirectionNode = useCallback((node) => {
|
|
50
|
+
setDirectionNode(node);
|
|
51
|
+
}, []);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (dir || !directionNode || typeof window === 'undefined')
|
|
54
|
+
return undefined;
|
|
55
|
+
const refresh = () => {
|
|
56
|
+
const directionSource = directionNode.parentElement ?? directionNode;
|
|
57
|
+
const direction = getComputedStyle(directionSource).direction === 'rtl' ? 'rtl' : 'ltr';
|
|
58
|
+
setInferredDirection(current => current === direction ? current : direction);
|
|
59
|
+
};
|
|
60
|
+
refresh();
|
|
61
|
+
if (typeof MutationObserver === 'undefined')
|
|
62
|
+
return undefined;
|
|
63
|
+
const observer = new MutationObserver(refresh);
|
|
64
|
+
for (let target = directionNode.parentElement; target; target = target.parentElement) {
|
|
65
|
+
observer.observe(target, {
|
|
66
|
+
attributes: true,
|
|
67
|
+
attributeFilter: ['class', 'data-theme', 'data-inkra-theme', 'dir', 'style'],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return () => observer.disconnect();
|
|
71
|
+
}, [dir, directionNode]);
|
|
72
|
+
const state = useMemo(() => ({
|
|
73
|
+
captureDirectionNode,
|
|
74
|
+
controlId: field?.controlId,
|
|
75
|
+
descriptionId: field?.descriptionId,
|
|
76
|
+
errorId: field?.errorId,
|
|
77
|
+
disabled: Boolean(disabled || field?.disabled),
|
|
78
|
+
readOnly: Boolean(readOnly || field?.readOnly),
|
|
79
|
+
invalid: Boolean(invalid || field?.invalid),
|
|
80
|
+
required: Boolean(required || field?.required),
|
|
81
|
+
}), [captureDirectionNode, disabled, field, invalid, readOnly, required]);
|
|
82
|
+
return (_jsx(PortalThemeProvider, { children: _jsx(SelectFieldContext.Provider, { value: state, children: props.multiple ? _jsx(MultipleSelectRoot, { ...props, dir: dir ?? inferredDirection, disabled: state.disabled, readOnly: state.readOnly, required: state.required }) : _jsx(SelectPrimitive.Root, { ...props, dir: dir ?? inferredDirection, disabled: state.disabled, required: state.required, onValueChange: value => { if (!state.readOnly)
|
|
83
|
+
props.onValueChange?.(value); }, onOpenChange: open => { if (!state.readOnly)
|
|
84
|
+
props.onOpenChange?.(open); }, open: state.readOnly ? false : props.open }) }) }));
|
|
85
|
+
}
|
|
86
|
+
export const SelectTrigger = forwardRef(function SelectTrigger({ 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, children, className, disabled = false, icon, startIcon, endIcon, hideValue = false, hideIndicator = false, iconOnly = false, fullWidth = true, textAlign = 'start', id, invalid = false, seed, size = 'md', ...props }, forwardedRef) {
|
|
87
|
+
const generatedId = useId();
|
|
88
|
+
const multiple = useContext(MultipleSelectContext);
|
|
89
|
+
const Trigger = multiple ? MultipleSelect.Trigger : SelectPrimitive.Trigger;
|
|
90
|
+
const Icon = multiple ? MultipleSelect.Icon : SelectPrimitive.Icon;
|
|
91
|
+
const field = useContext(SelectFieldContext);
|
|
92
|
+
const resolvedId = field?.controlId ?? id;
|
|
93
|
+
const resolvedDisabled = Boolean(disabled || field?.disabled);
|
|
94
|
+
const resolvedInvalid = Boolean(invalid || field?.invalid || isAriaInvalid(ariaInvalid));
|
|
95
|
+
const resolvedSeed = seed ?? `inkra-select-${resolvedId ?? generatedId}`;
|
|
96
|
+
const valueHidden = hideValue || iconOnly;
|
|
97
|
+
const valueId = `${generatedId}-value`;
|
|
98
|
+
const describedBy = mergeAriaIds(ariaDescribedBy, field?.descriptionId, field?.errorId, valueHidden ? valueId : undefined);
|
|
99
|
+
const errorMessage = mergeAriaIds(ariaErrorMessage, resolvedInvalid ? field?.errorId : undefined);
|
|
100
|
+
const portalRef = usePortalTriggerRef(forwardedRef);
|
|
101
|
+
const captureDirectionNode = field?.captureDirectionNode;
|
|
102
|
+
const ref = useCallback((node) => {
|
|
103
|
+
portalRef(node);
|
|
104
|
+
captureDirectionNode?.(node);
|
|
105
|
+
}, [captureDirectionNode, portalRef]);
|
|
106
|
+
const defaultIndicator = (_jsx(PencilIcon, { className: "inkra-select__trigger-glyph", paths: chevronDownPaths, seed: `${resolvedSeed}-chevron`, size: size === 'sm' ? 24 : size === 'lg' ? 32 : 28, sourceSize: 24 }));
|
|
107
|
+
const triggerIcon = icon === undefined ? defaultIndicator : icon;
|
|
108
|
+
return (_jsx(Trigger, { ...props, "aria-describedby": describedBy, "aria-errormessage": errorMessage, "aria-invalid": resolvedInvalid ? true : ariaInvalid, "aria-readonly": field?.readOnly || undefined, className: classes('inkra-select__trigger', className), "data-invalid": resolvedInvalid || undefined, "data-size": size, "data-icon-only": iconOnly || undefined, "data-full-width": fullWidth, "data-text-align": textAlign, disabled: resolvedDisabled, id: resolvedId, ref: ref, onKeyDown: event => {
|
|
109
|
+
props.onKeyDown?.(event);
|
|
110
|
+
if (field?.readOnly && event.key !== 'Tab' && !event.ctrlKey && !event.metaKey)
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
}, onPointerDown: event => {
|
|
113
|
+
props.onPointerDown?.(event);
|
|
114
|
+
if (field?.readOnly)
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
}, children: _jsxs(PencilSurface, { as: "span", className: "inkra-select__trigger-surface", radius: 8, seed: resolvedSeed, variant: resolvedInvalid ? 'danger' : 'outline', children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-select__focus-ring", radius: 8, seed: `${resolvedSeed}-focus`, variant: "outline" }), iconOnly ? (_jsx("span", { className: "inkra-select__trigger-slot", "aria-hidden": "true", children: startIcon ?? endIcon ?? triggerIcon ?? defaultIndicator })) : startIcon != null ? (_jsx("span", { className: "inkra-select__trigger-slot", "aria-hidden": "true", children: startIcon })) : null, _jsx("span", { id: valueId, className: valueHidden ? 'inkra-select__trigger-value-hidden' : 'inkra-select__trigger-value', children: children }), !iconOnly && endIcon != null && _jsx("span", { className: "inkra-select__trigger-slot", "aria-hidden": "true", children: endIcon }), iconOnly || hideIndicator || triggerIcon == null ? null : (_jsx(Icon, { asChild: true, children: _jsx("span", { className: "inkra-select__trigger-icon", children: triggerIcon }) }))] }) }));
|
|
117
|
+
});
|
|
118
|
+
export const SelectValue = forwardRef(function SelectValue({ children, placeholder, seed, ...props }, ref) {
|
|
119
|
+
const generatedId = useId();
|
|
120
|
+
const Value = useContext(MultipleSelectContext) ? MultipleSelect.Value : SelectPrimitive.Value;
|
|
121
|
+
const copy = (value, suffix) => typeof value === 'string' ? (_jsx(PencilText, { className: "inkra-select__value-copy", ellipsis: true, seed: seed ?? `inkra-select-value-${generatedId}-${suffix}`, children: value })) : value;
|
|
122
|
+
return (_jsx(Value, { ...props, placeholder: copy(placeholder, 'placeholder'), ref: ref, children: copy(children, 'value') }));
|
|
123
|
+
});
|
|
124
|
+
export function SelectPortal({ children, ...props }) {
|
|
125
|
+
const Portal = useContext(MultipleSelectContext) ? MultipleSelect.Portal : SelectPrimitive.Portal;
|
|
126
|
+
return (_jsx(Portal, { ...props, children: _jsx(PortalThemeBridge, { children: children }) }));
|
|
127
|
+
}
|
|
128
|
+
export const SelectContent = forwardRef(function SelectContent({ children, className, seed, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
129
|
+
const generatedId = useId();
|
|
130
|
+
const Content = useContext(MultipleSelectContext) ? MultipleSelect.Content : SelectPrimitive.Content;
|
|
131
|
+
return (_jsx(Content, { ...props, className: classes('inkra-select__content', className), ref: ref, children: _jsx(PencilSurface, { className: classes('inkra-select__content-surface', surfaceClassName), radius: surfaceRadius, seed: seed ?? `inkra-select-content-${generatedId}`, variant: surfaceVariant, children: children }) }));
|
|
132
|
+
});
|
|
133
|
+
export const SelectViewport = forwardRef(function SelectViewport({ className, ...props }, ref) {
|
|
134
|
+
const Viewport = useContext(MultipleSelectContext) ? MultipleSelect.Viewport : SelectPrimitive.Viewport;
|
|
135
|
+
return (_jsx(Viewport, { ...props, className: classes('inkra-select__viewport', className), ref: ref }));
|
|
136
|
+
});
|
|
137
|
+
export const SelectGroup = forwardRef(function SelectGroup({ className, ...props }, ref) {
|
|
138
|
+
const Group = useContext(MultipleSelectContext) ? MultipleSelect.Group : SelectPrimitive.Group;
|
|
139
|
+
return (_jsx(Group, { ...props, className: classes('inkra-select__group', className), ref: ref }));
|
|
140
|
+
});
|
|
141
|
+
export const SelectLabel = forwardRef(function SelectLabel({ children, className, seed, ...props }, ref) {
|
|
142
|
+
const generatedId = useId();
|
|
143
|
+
const Label = useContext(MultipleSelectContext) ? MultipleSelect.Label : SelectPrimitive.Label;
|
|
144
|
+
return (_jsx(Label, { ...props, className: classes('inkra-select__label', className), ref: ref, children: _jsx(PencilText, { seed: seed ?? `inkra-select-label-${generatedId}`, children: children }) }));
|
|
145
|
+
});
|
|
146
|
+
export const SelectItem = forwardRef(function SelectItem({ children, className, seed, surfaceClassName, value, ...props }, ref) {
|
|
147
|
+
const Item = useContext(MultipleSelectContext) ? MultipleSelect.Item : SelectPrimitive.Item;
|
|
148
|
+
return (_jsx(Item, { ...props, className: classes('inkra-select__item', className), ref: ref, value: value, children: _jsx(PencilSurface, { className: classes('inkra-select__item-surface', surfaceClassName), seed: seed ?? `inkra-select-item-${value}`, variant: "selection", children: children }) }));
|
|
149
|
+
});
|
|
150
|
+
export const SelectItemText = forwardRef(function SelectItemText({ children, seed, ...props }, ref) {
|
|
151
|
+
const generatedId = useId();
|
|
152
|
+
const ItemText = useContext(MultipleSelectContext) ? MultipleSelect.ItemText : SelectPrimitive.ItemText;
|
|
153
|
+
const { copyRef, version } = useItemAppearanceVersion();
|
|
154
|
+
return (_jsx(ItemText, { ...props, ref: ref, children: _jsx(PencilText, { className: "inkra-select__item-text-copy", ref: copyRef, seed: seed ?? `inkra-select-item-text-${generatedId}`, children: children }, version) }));
|
|
155
|
+
});
|
|
156
|
+
export const SelectItemIndicator = forwardRef(function SelectItemIndicator({ children, className, seed, ...props }, ref) {
|
|
157
|
+
const generatedId = useId();
|
|
158
|
+
const ItemIndicator = useContext(MultipleSelectContext) ? MultipleSelect.ItemIndicator : SelectPrimitive.ItemIndicator;
|
|
159
|
+
return (_jsx(ItemIndicator, { ...props, className: classes('inkra-select__item-indicator', className), ref: ref, children: children ?? (_jsx(PencilIcon, { paths: checkPaths, seed: seed ?? `inkra-select-indicator-${generatedId}`, size: 16, sourceSize: 24 })) }));
|
|
160
|
+
});
|
|
161
|
+
export const SelectScrollUpButton = forwardRef(function SelectScrollUpButton({ children, className, seed, ...props }, ref) {
|
|
162
|
+
const generatedId = useId();
|
|
163
|
+
const ScrollUpButton = useContext(MultipleSelectContext) ? MultipleSelect.ScrollUpButton : SelectPrimitive.ScrollUpButton;
|
|
164
|
+
return (_jsx(ScrollUpButton, { ...props, className: classes('inkra-select__scroll-button', className), ref: ref, children: children ?? (_jsx(PencilIcon, { paths: chevronUpPaths, seed: seed ?? `inkra-select-scroll-up-${generatedId}`, size: 16, sourceSize: 24 })) }));
|
|
165
|
+
});
|
|
166
|
+
export const SelectScrollDownButton = forwardRef(function SelectScrollDownButton({ children, className, seed, ...props }, ref) {
|
|
167
|
+
const generatedId = useId();
|
|
168
|
+
const ScrollDownButton = useContext(MultipleSelectContext) ? MultipleSelect.ScrollDownButton : SelectPrimitive.ScrollDownButton;
|
|
169
|
+
return (_jsx(ScrollDownButton, { ...props, className: classes('inkra-select__scroll-button', className), ref: ref, children: children ?? (_jsx(PencilIcon, { paths: chevronDownPaths, seed: seed ?? `inkra-select-scroll-down-${generatedId}`, size: 16, sourceSize: 24 })) }));
|
|
170
|
+
});
|
|
171
|
+
export const SelectSeparator = forwardRef(function SelectSeparator({ className, seed, ...props }, ref) {
|
|
172
|
+
const generatedId = useId();
|
|
173
|
+
const Separator = useContext(MultipleSelectContext) ? MultipleSelect.Separator : SelectPrimitive.Separator;
|
|
174
|
+
return (_jsx(Separator, { ...props, className: classes('inkra-select__separator', className), ref: ref, children: _jsx(PencilLine, { seed: seed ?? `inkra-select-separator-${generatedId}`, weight: 0.8 }) }));
|
|
175
|
+
});
|
|
176
|
+
export const SelectClear = forwardRef(function SelectClear({ children, className, disabled, onClick, ...props }, ref) {
|
|
177
|
+
const state = useContext(MultipleSelectContext);
|
|
178
|
+
if (!state)
|
|
179
|
+
throw new Error('Select.Clear must be used inside Select.Root with multiple.');
|
|
180
|
+
return _jsx(Button, { ...props, className: classes('inkra-select__clear', className), ref: ref, type: "button", variant: "ghost", disabled: disabled || state.disabled || state.readOnly || state.values.length === 0, onClick: event => {
|
|
181
|
+
onClick?.(event);
|
|
182
|
+
if (event.defaultPrevented)
|
|
183
|
+
return;
|
|
184
|
+
state.change([]);
|
|
185
|
+
state.triggerRef.current?.focus({ preventScroll: true });
|
|
186
|
+
}, children: children ?? 'Clear' });
|
|
187
|
+
});
|
|
188
|
+
export const Select = Object.freeze({
|
|
189
|
+
Searchable: SearchableSelect,
|
|
190
|
+
Root: SelectRoot,
|
|
191
|
+
Trigger: SelectTrigger,
|
|
192
|
+
Value: SelectValue,
|
|
193
|
+
Portal: SelectPortal,
|
|
194
|
+
Content: SelectContent,
|
|
195
|
+
Viewport: SelectViewport,
|
|
196
|
+
Group: SelectGroup,
|
|
197
|
+
Label: SelectLabel,
|
|
198
|
+
Item: SelectItem,
|
|
199
|
+
ItemText: SelectItemText,
|
|
200
|
+
ItemIndicator: SelectItemIndicator,
|
|
201
|
+
ScrollUpButton: SelectScrollUpButton,
|
|
202
|
+
ScrollDownButton: SelectScrollDownButton,
|
|
203
|
+
Separator: SelectSeparator,
|
|
204
|
+
Clear: SelectClear,
|
|
205
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Select, SelectClear, SelectContent, SelectGroup, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectPortal, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SelectViewport, } from './Select.js';
|
|
2
|
+
export { SearchableSelect } from './SearchableSelect.js';
|
|
3
|
+
export type { SearchableSelectProps, SearchableSelectOption } from './SearchableSelect.js';
|
|
4
|
+
export type { SelectClearProps, SelectContentProps, SelectGroupProps, SelectItemIndicatorProps, SelectItemProps, SelectItemTextProps, SelectLabelProps, SelectPortalProps, SelectRootProps, SelectSingleRootProps, SelectMultipleRootProps, SelectOption, SelectScrollDownButtonProps, SelectScrollUpButtonProps, SelectSeparatorProps, SelectSize, SelectTriggerProps, SelectValueProps, SelectViewportProps, } from './Select.js';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { Select, SelectClear, SelectContent, SelectGroup, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectPortal, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SelectViewport, } from './Select.js';
|
|
2
|
+
export { SearchableSelect } from './SearchableSelect.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'children' | 'type'> {
|
|
3
|
+
label: ReactNode;
|
|
4
|
+
description?: ReactNode;
|
|
5
|
+
indeterminate?: boolean;
|
|
6
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
7
|
+
seed?: string | number;
|
|
8
|
+
}
|
|
9
|
+
export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, useImperativeHandle, useRef } from 'react';
|
|
3
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { useNativeChecked } from './useNativeChecked.js';
|
|
7
|
+
const checkPaths = [{ d: 'M 5 12 L 10 17 L 19 7' }];
|
|
8
|
+
const mixedPaths = [{ d: 'M 5 12 L 19 12' }];
|
|
9
|
+
function ids(...values) {
|
|
10
|
+
const value = values.filter(Boolean).join(' ');
|
|
11
|
+
return value || undefined;
|
|
12
|
+
}
|
|
13
|
+
export const Checkbox = forwardRef(function Checkbox({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, checked, className, defaultChecked, description, disabled = false, id, indeterminate = false, label, onChange, onCheckedChange, seed, ...props }, forwardedRef) {
|
|
14
|
+
const generatedId = useId();
|
|
15
|
+
const inputId = id ?? `inkra-checkbox-${generatedId}`;
|
|
16
|
+
const labelId = `${inputId}-label`;
|
|
17
|
+
const descriptionId = description == null ? undefined : `${inputId}-description`;
|
|
18
|
+
const resolvedLabelledBy = ariaLabel == null && ariaLabelledBy == null ? labelId : ariaLabelledBy;
|
|
19
|
+
const resolvedSeed = seed ?? inputId;
|
|
20
|
+
const inputRef = useRef(null);
|
|
21
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
22
|
+
const { controlled, syncUncontrolled, visualChecked } = useNativeChecked({
|
|
23
|
+
checked,
|
|
24
|
+
defaultChecked,
|
|
25
|
+
indeterminate,
|
|
26
|
+
inputRef,
|
|
27
|
+
});
|
|
28
|
+
const selected = visualChecked || indeterminate;
|
|
29
|
+
return (_jsxs("label", { className: ['inkra-selection-control', 'inkra-checkbox', className].filter(Boolean).join(' '), "data-checked": visualChecked || undefined, "data-disabled": disabled || undefined, "data-indeterminate": indeterminate || undefined, htmlFor: inputId, children: [_jsx("input", { ...props, "aria-checked": indeterminate ? 'mixed' : undefined, "aria-describedby": ids(ariaDescribedBy, descriptionId), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, checked: controlled ? checked : undefined, className: "inkra-selection-control__input", defaultChecked: controlled ? undefined : defaultChecked, disabled: disabled, id: inputId, onChange: (event) => {
|
|
30
|
+
const nextChecked = event.currentTarget.checked;
|
|
31
|
+
event.currentTarget.indeterminate = indeterminate;
|
|
32
|
+
if (!controlled)
|
|
33
|
+
syncUncontrolled(nextChecked);
|
|
34
|
+
onChange?.(event);
|
|
35
|
+
onCheckedChange?.(nextChecked);
|
|
36
|
+
}, ref: inputRef, type: "checkbox" }), _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-selection-control__mark inkra-checkbox__mark", radius: 3, seed: `${resolvedSeed}-mark`, variant: selected ? 'quiet' : 'outline', children: selected ? (_jsx(PencilIcon, { className: "inkra-selection-control__glyph", paths: indeterminate ? mixedPaths : checkPaths, seed: `${resolvedSeed}-${indeterminate ? 'mixed' : 'check'}`, size: 18, sourceSize: 24 })) : null }), _jsxs("span", { className: "inkra-selection-control__copy", children: [typeof label === 'string' ? (_jsx(PencilText, { as: "span", className: "inkra-selection-control__label", id: labelId, seed: `${resolvedSeed}-label`, children: label })) : _jsx("span", { className: "inkra-selection-control__label", id: labelId, children: label }), description == null ? null : (_jsx("span", { className: "inkra-selection-control__description", id: descriptionId, children: description }))] })] }));
|
|
37
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FieldsetHTMLAttributes, InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export type RadioGroupVariant = 'radio' | 'segmented';
|
|
3
|
+
export type RadioGroupOrientation = 'horizontal' | 'vertical';
|
|
4
|
+
export interface RadioGroupProps extends Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'defaultValue' | 'onChange'> {
|
|
5
|
+
name: string;
|
|
6
|
+
value?: string;
|
|
7
|
+
defaultValue?: string;
|
|
8
|
+
label?: ReactNode;
|
|
9
|
+
description?: ReactNode;
|
|
10
|
+
onValueChange?: (value: string) => void;
|
|
11
|
+
orientation?: RadioGroupOrientation;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
variant?: RadioGroupVariant;
|
|
14
|
+
}
|
|
15
|
+
export interface RadioGroupItemProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'checked' | 'children' | 'defaultChecked' | 'name' | 'type'> {
|
|
16
|
+
value: string;
|
|
17
|
+
label: ReactNode;
|
|
18
|
+
description?: ReactNode;
|
|
19
|
+
icon?: ReactNode;
|
|
20
|
+
seed?: string | number;
|
|
21
|
+
}
|
|
22
|
+
export declare const RadioGroup: import("react").ForwardRefExoticComponent<RadioGroupProps & import("react").RefAttributes<HTMLFieldSetElement>>;
|
|
23
|
+
export declare const RadioGroupItem: import("react").ForwardRefExoticComponent<RadioGroupItemProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, forwardRef, useContext, useEffect, useId, useImperativeHandle, useRef, useState, } from 'react';
|
|
3
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { useSelectionDrawing } from '../internal/useSelectionDrawing.js';
|
|
7
|
+
const ringPaths = [
|
|
8
|
+
{ d: 'M 12 2 C 18 2 22 6 22 12 C 22 18 18 22 12 22 C 6 22 2 18 2 12 C 2 6 6 2 12 2 Z' },
|
|
9
|
+
];
|
|
10
|
+
const selectedPaths = [
|
|
11
|
+
...ringPaths,
|
|
12
|
+
{ d: 'M 12 7 C 15 7 17 9 17 12 C 17 15 15 17 12 17 C 9 17 7 15 7 12 C 7 9 9 7 12 7 Z', mode: 'wash' },
|
|
13
|
+
{ d: 'M 12 7 C 15 7 17 9 17 12 C 17 15 15 17 12 17 C 9 17 7 15 7 12 C 7 9 9 7 12 7 Z' },
|
|
14
|
+
];
|
|
15
|
+
const RadioGroupContext = createContext(null);
|
|
16
|
+
function ids(...values) {
|
|
17
|
+
const value = values.filter(Boolean).join(' ');
|
|
18
|
+
return value || undefined;
|
|
19
|
+
}
|
|
20
|
+
export const RadioGroup = forwardRef(function RadioGroup({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, children, className, defaultValue, description, disabled = false, form, label, name, onValueChange, orientation = 'vertical', required = false, value, variant = 'radio', ...props }, forwardedRef) {
|
|
21
|
+
const generatedId = useId();
|
|
22
|
+
const descriptionId = description == null ? undefined : `inkra-radio-group-${generatedId}-description`;
|
|
23
|
+
const rootRef = useRef(null);
|
|
24
|
+
const initialDefaultValueRef = useRef(defaultValue);
|
|
25
|
+
useImperativeHandle(forwardedRef, () => rootRef.current);
|
|
26
|
+
const controlled = value !== undefined;
|
|
27
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(initialDefaultValueRef.current);
|
|
28
|
+
const currentValue = controlled ? value : uncontrolledValue;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const root = rootRef.current;
|
|
31
|
+
const ownerForm = root?.form;
|
|
32
|
+
if (!root || !ownerForm || controlled)
|
|
33
|
+
return undefined;
|
|
34
|
+
const handleReset = () => {
|
|
35
|
+
queueMicrotask(() => {
|
|
36
|
+
const checkedInput = root.querySelector('input[type="radio"]:checked');
|
|
37
|
+
setUncontrolledValue(checkedInput?.value);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
ownerForm.addEventListener('reset', handleReset);
|
|
41
|
+
return () => ownerForm.removeEventListener('reset', handleReset);
|
|
42
|
+
}, [controlled]);
|
|
43
|
+
const context = {
|
|
44
|
+
controlled,
|
|
45
|
+
defaultValue: initialDefaultValueRef.current,
|
|
46
|
+
disabled,
|
|
47
|
+
form,
|
|
48
|
+
name,
|
|
49
|
+
onItemChange(event) {
|
|
50
|
+
if (!event.currentTarget.checked)
|
|
51
|
+
return;
|
|
52
|
+
if (!controlled)
|
|
53
|
+
setUncontrolledValue(event.currentTarget.value);
|
|
54
|
+
onValueChange?.(event.currentTarget.value);
|
|
55
|
+
},
|
|
56
|
+
required,
|
|
57
|
+
variant,
|
|
58
|
+
value: currentValue,
|
|
59
|
+
};
|
|
60
|
+
return (_jsx(RadioGroupContext.Provider, { value: context, children: _jsxs("fieldset", { ...props, "aria-describedby": ids(ariaDescribedBy, descriptionId), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className: ['inkra-radio-group', className].filter(Boolean).join(' '), "data-orientation": orientation, "data-variant": variant, disabled: disabled, form: form, ref: rootRef, children: [label == null ? null : typeof label === 'string' ? (_jsx(PencilText, { as: "legend", className: "inkra-radio-group__label", seed: `inkra-radio-group-${generatedId}-label`, children: label })) : _jsx("legend", { className: "inkra-radio-group__label", children: label }), description == null ? null : (_jsx("span", { className: "inkra-radio-group__description", id: descriptionId, children: description })), variant === 'segmented' ? (_jsx(PencilSurface, { className: "inkra-radio-group__frame", radius: 8, seed: `inkra-radio-group-${generatedId}-frame`, variant: "outline", children: _jsx("div", { className: "inkra-radio-group__items", "data-orientation": orientation, children: children }) })) : _jsx("div", { className: "inkra-radio-group__items", "data-orientation": orientation, children: children })] }) }));
|
|
61
|
+
});
|
|
62
|
+
export const RadioGroupItem = forwardRef(function RadioGroupItem({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, className, description, disabled = false, form, icon, id, label, onChange, required = false, seed, value, ...props }, ref) {
|
|
63
|
+
const context = useContext(RadioGroupContext);
|
|
64
|
+
if (!context)
|
|
65
|
+
throw new Error('RadioGroupItem must be rendered inside RadioGroup');
|
|
66
|
+
const generatedId = useId();
|
|
67
|
+
const inputId = id ?? `inkra-radio-${generatedId}`;
|
|
68
|
+
const labelId = `${inputId}-label`;
|
|
69
|
+
const descriptionId = description == null ? undefined : `${inputId}-description`;
|
|
70
|
+
const resolvedLabelledBy = ariaLabel == null && ariaLabelledBy == null ? labelId : ariaLabelledBy;
|
|
71
|
+
const selected = context.value === value;
|
|
72
|
+
const selectionRef = useRef(null);
|
|
73
|
+
useSelectionDrawing(selectionRef, selected);
|
|
74
|
+
const itemDisabled = context.disabled || disabled;
|
|
75
|
+
const resolvedSeed = seed ?? inputId;
|
|
76
|
+
const visibleLabel = typeof label === 'string' ? (_jsx(PencilText, { as: "span", className: "inkra-selection-control__label", id: labelId, seed: `${resolvedSeed}-label`, children: label })) : _jsx("span", { className: "inkra-selection-control__label", id: labelId, children: label });
|
|
77
|
+
const visibleDescription = description == null ? null : (_jsx("span", { className: "inkra-selection-control__description", id: descriptionId, children: description }));
|
|
78
|
+
return (_jsxs("label", { className: ['inkra-selection-control', 'inkra-radio-group__item', className].filter(Boolean).join(' '), "data-checked": selected || undefined, "data-disabled": itemDisabled || undefined, htmlFor: inputId, children: [_jsx("input", { ...props, "aria-describedby": ids(ariaDescribedBy, descriptionId), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, checked: context.controlled ? selected : undefined, className: "inkra-selection-control__input", defaultChecked: context.controlled ? undefined : context.defaultValue === value, disabled: itemDisabled, form: form ?? context.form, id: inputId, name: context.name, onChange: (event) => {
|
|
79
|
+
onChange?.(event);
|
|
80
|
+
context.onItemChange(event);
|
|
81
|
+
}, ref: ref, required: required || context.required, type: "radio", value: value }), context.variant === 'segmented' ? (_jsxs("span", { className: "inkra-radio-group__segment", children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-radio-segment__surface inkra-radio-segment__surface--selection", ref: selectionRef, seed: `${resolvedSeed}-segment-selection`, variant: "selection" }), _jsxs("span", { className: "inkra-radio-segment__content", children: [icon == null ? null : _jsx("span", { "aria-hidden": "true", className: "inkra-radio-group__icon", children: icon }), _jsxs("span", { className: "inkra-selection-control__copy", children: [visibleLabel, visibleDescription] })] })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: "inkra-selection-control__mark inkra-radio-group__mark", children: _jsx(PencilIcon, { className: "inkra-selection-control__glyph", paths: selected ? selectedPaths : ringPaths, seed: `${resolvedSeed}-${selected ? 'selected' : 'ring'}`, size: 24, sourceSize: 24 }) }), icon == null ? null : _jsx("span", { "aria-hidden": "true", className: "inkra-radio-group__icon", children: icon }), _jsxs("span", { className: "inkra-selection-control__copy", children: [visibleLabel, visibleDescription] })] }))] }));
|
|
82
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'children' | 'role' | 'type'> {
|
|
3
|
+
label: ReactNode;
|
|
4
|
+
description?: ReactNode;
|
|
5
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
6
|
+
seed?: string | number;
|
|
7
|
+
}
|
|
8
|
+
export declare const Switch: import("react").ForwardRefExoticComponent<SwitchProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, useImperativeHandle, useRef } from 'react';
|
|
3
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { useNativeChecked } from './useNativeChecked.js';
|
|
7
|
+
const thumbPaths = [
|
|
8
|
+
{ d: 'M 20 3 C 30 3 37 10 37 20 C 37 30 30 37 20 37 C 10 37 3 30 3 20 C 3 10 10 3 20 3 Z', mode: 'wash' },
|
|
9
|
+
{ d: 'M 20 3 C 30 3 37 10 37 20 C 37 30 30 37 20 37 C 10 37 3 30 3 20 C 3 10 10 3 20 3 Z' },
|
|
10
|
+
];
|
|
11
|
+
function ids(...values) {
|
|
12
|
+
const value = values.filter(Boolean).join(' ');
|
|
13
|
+
return value || undefined;
|
|
14
|
+
}
|
|
15
|
+
export const Switch = forwardRef(function Switch({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, checked, className, defaultChecked, description, disabled = false, id, label, onChange, onCheckedChange, seed, ...props }, forwardedRef) {
|
|
16
|
+
const generatedId = useId();
|
|
17
|
+
const inputId = id ?? `inkra-switch-${generatedId}`;
|
|
18
|
+
const labelId = `${inputId}-label`;
|
|
19
|
+
const descriptionId = description == null ? undefined : `${inputId}-description`;
|
|
20
|
+
const resolvedLabelledBy = ariaLabel == null && ariaLabelledBy == null ? labelId : ariaLabelledBy;
|
|
21
|
+
const resolvedSeed = seed ?? inputId;
|
|
22
|
+
const inputRef = useRef(null);
|
|
23
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
24
|
+
const { controlled, syncUncontrolled, visualChecked } = useNativeChecked({
|
|
25
|
+
checked,
|
|
26
|
+
defaultChecked,
|
|
27
|
+
inputRef,
|
|
28
|
+
});
|
|
29
|
+
return (_jsxs("label", { className: ['inkra-selection-control', 'inkra-switch', className].filter(Boolean).join(' '), "data-checked": visualChecked || undefined, "data-disabled": disabled || undefined, htmlFor: inputId, children: [_jsx("input", { ...props, "aria-describedby": ids(ariaDescribedBy, descriptionId), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, checked: controlled ? checked : undefined, className: "inkra-selection-control__input", defaultChecked: controlled ? undefined : defaultChecked, disabled: disabled, id: inputId, onChange: (event) => {
|
|
30
|
+
const nextChecked = event.currentTarget.checked;
|
|
31
|
+
if (!controlled)
|
|
32
|
+
syncUncontrolled(nextChecked);
|
|
33
|
+
onChange?.(event);
|
|
34
|
+
onCheckedChange?.(nextChecked);
|
|
35
|
+
}, ref: inputRef, role: "switch", type: "checkbox" }), _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-switch__track", radius: 8, seed: `${resolvedSeed}-track`, variant: visualChecked ? 'quiet' : 'outline', children: _jsx(PencilIcon, { className: "inkra-switch__thumb", paths: thumbPaths, seed: `${resolvedSeed}-thumb`, size: 24, sourceSize: 40 }) }), _jsxs("span", { className: "inkra-selection-control__copy", children: [typeof label === 'string' ? (_jsx(PencilText, { as: "span", className: "inkra-selection-control__label", id: labelId, seed: `${resolvedSeed}-label`, children: label })) : _jsx("span", { className: "inkra-selection-control__label", id: labelId, children: label }), description == null ? null : (_jsx("span", { className: "inkra-selection-control__description", id: descriptionId, children: description }))] })] }));
|
|
36
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Checkbox } from './Checkbox.js';
|
|
2
|
+
export type { CheckboxProps } from './Checkbox.js';
|
|
3
|
+
export { RadioGroup, RadioGroupItem } from './RadioGroup.js';
|
|
4
|
+
export type { RadioGroupItemProps, RadioGroupOrientation, RadioGroupProps, RadioGroupVariant, } from './RadioGroup.js';
|
|
5
|
+
export { Switch } from './Switch.js';
|
|
6
|
+
export type { SwitchProps } from './Switch.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
interface NativeCheckedOptions {
|
|
3
|
+
checked?: boolean;
|
|
4
|
+
defaultChecked?: boolean;
|
|
5
|
+
inputRef: RefObject<HTMLInputElement | null>;
|
|
6
|
+
indeterminate?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function useNativeChecked({ checked, defaultChecked, inputRef, indeterminate, }: NativeCheckedOptions): {
|
|
9
|
+
controlled: boolean;
|
|
10
|
+
visualChecked: boolean;
|
|
11
|
+
syncUncontrolled: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
|
|
3
|
+
export function useNativeChecked({ checked, defaultChecked, inputRef, indeterminate = false, }) {
|
|
4
|
+
const controlled = checked !== undefined;
|
|
5
|
+
const [uncontrolledChecked, setUncontrolledChecked] = useState(Boolean(defaultChecked));
|
|
6
|
+
useBrowserLayoutEffect(() => {
|
|
7
|
+
const input = inputRef.current;
|
|
8
|
+
if (input)
|
|
9
|
+
input.indeterminate = indeterminate;
|
|
10
|
+
}, [indeterminate, inputRef]);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const input = inputRef.current;
|
|
13
|
+
const form = input?.form;
|
|
14
|
+
if (!input || !form || controlled)
|
|
15
|
+
return undefined;
|
|
16
|
+
const handleReset = () => {
|
|
17
|
+
queueMicrotask(() => setUncontrolledChecked(input.checked));
|
|
18
|
+
};
|
|
19
|
+
form.addEventListener('reset', handleReset);
|
|
20
|
+
return () => form.removeEventListener('reset', handleReset);
|
|
21
|
+
}, [controlled, inputRef]);
|
|
22
|
+
return {
|
|
23
|
+
controlled,
|
|
24
|
+
visualChecked: controlled ? Boolean(checked) : uncontrolledChecked,
|
|
25
|
+
syncUncontrolled: setUncontrolledChecked,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
2
|
+
export type SliderSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export type SliderVariant = 'standard' | 'inset';
|
|
4
|
+
export interface SliderProps extends Omit<SliderPrimitive.SliderProps, 'children' | 'asChild'> {
|
|
5
|
+
size?: SliderSize;
|
|
6
|
+
variant?: SliderVariant;
|
|
7
|
+
seed?: string | number;
|
|
8
|
+
thumbLabels?: readonly string[];
|
|
9
|
+
getValueLabel?: (value: number, index: number) => string;
|
|
10
|
+
}
|
|
11
|
+
export declare const Slider: import("react").ForwardRefExoticComponent<SliderProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as SliderPrimitive from '@radix-ui/react-slider';
|
|
3
|
+
import { forwardRef, useEffect, useId, useImperativeHandle, useRef, useState } from 'react';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
export const Slider = forwardRef(function Slider({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-valuetext': ariaValueText, className, defaultValue, dir, disabled = false, getValueLabel, inverted = false, min = 0, max = 100, onValueChange, orientation = 'horizontal', seed, size = 'md', thumbLabels, value, variant = 'standard', ...props }, forwardedRef) {
|
|
6
|
+
const generatedId = useId();
|
|
7
|
+
const rootRef = useRef(null);
|
|
8
|
+
useImperativeHandle(forwardedRef, () => rootRef.current);
|
|
9
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(() => defaultValue ?? [min]);
|
|
10
|
+
const [inferredDir, setInferredDir] = useState('ltr');
|
|
11
|
+
const values = value ?? uncontrolledValue;
|
|
12
|
+
const resolvedDir = dir ?? inferredDir;
|
|
13
|
+
const resolvedSeed = seed ?? `inkra-slider-${generatedId}`;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const root = rootRef.current;
|
|
16
|
+
if (dir || !root)
|
|
17
|
+
return undefined;
|
|
18
|
+
const refresh = () => setInferredDir(getComputedStyle(root.parentElement ?? root).direction === 'rtl' ? 'rtl' : 'ltr');
|
|
19
|
+
refresh();
|
|
20
|
+
const observer = new MutationObserver(refresh);
|
|
21
|
+
for (let parent = root.parentElement; parent; parent = parent.parentElement) {
|
|
22
|
+
observer.observe(parent, { attributes: true, attributeFilter: ['dir', 'class', 'style'] });
|
|
23
|
+
}
|
|
24
|
+
return () => observer.disconnect();
|
|
25
|
+
}, [dir]);
|
|
26
|
+
const percentage = (number) => max > min ? Math.min(100, Math.max(0, (number - min) / (max - min) * 100)) : 0;
|
|
27
|
+
const low = values.length > 1 ? percentage(Math.min(...values)) : 0;
|
|
28
|
+
const high = values.length ? percentage(Math.max(...values)) : 0;
|
|
29
|
+
const reversed = orientation === 'horizontal' ? (resolvedDir === 'rtl') !== inverted : !inverted;
|
|
30
|
+
const start = reversed ? 100 - high : low;
|
|
31
|
+
const end = reversed ? low : 100 - high;
|
|
32
|
+
const clip = {
|
|
33
|
+
clipPath: orientation === 'horizontal' ? `inset(0 ${end}% 0 ${start}%)` : `inset(${start}% 0 ${end}% 0)`,
|
|
34
|
+
};
|
|
35
|
+
return _jsxs(SliderPrimitive.Root, { ...props, className: ['inkra-slider', className].filter(Boolean).join(' '), "data-size": size, "data-variant": variant, defaultValue: defaultValue, dir: resolvedDir, disabled: disabled, inverted: inverted, min: min, max: max, onValueChange: next => {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
setUncontrolledValue(next);
|
|
38
|
+
onValueChange?.(next);
|
|
39
|
+
}, orientation: orientation, ref: rootRef, value: value, children: [_jsxs(SliderPrimitive.Track, { className: "inkra-slider__track", children: [_jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slider__track-surface", variant: "outline", radius: variant === 'inset' ? 8 : 3, seed: `${resolvedSeed}-track` }), _jsx("span", { "aria-hidden": "true", className: "inkra-slider__range", style: clip, children: _jsx(PencilSurface, { as: "span", className: "inkra-slider__range-surface", variant: "selection", seed: `${resolvedSeed}-range` }) })] }), values.map((current, index) => {
|
|
40
|
+
const thumbLabel = thumbLabels?.[index];
|
|
41
|
+
const labelledBy = thumbLabel ? undefined : ariaLabelledBy;
|
|
42
|
+
const label = thumbLabel ?? (ariaLabel ? `${ariaLabel}${values.length > 1 ? ` ${index + 1}` : ''}` : labelledBy ? undefined : `Value${values.length > 1 ? ` ${index + 1}` : ''}`);
|
|
43
|
+
return _jsx(SliderPrimitive.Thumb, { "aria-describedby": ariaDescribedBy, "aria-label": label, "aria-labelledby": labelledBy, "aria-valuetext": getValueLabel?.(current, index) ?? ariaValueText, className: "inkra-slider__thumb", children: _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-slider__thumb-surface", variant: "paper", radius: 8, seed: `${resolvedSeed}-thumb-${index}`, children: _jsx(PencilSurface, { as: "span", className: "inkra-slider__focus-ring", variant: "outline", radius: 8, seed: `${resolvedSeed}-focus-${index}` }) }) }, index);
|
|
44
|
+
})] });
|
|
45
|
+
});
|