@inkra/react 0.3.0 → 0.5.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/README.md +113 -3
- package/dist/Button.js +3 -2
- package/dist/PencilIcon.d.ts +10 -0
- package/dist/PencilIcon.js +83 -39
- package/dist/PencilLine.js +9 -1
- package/dist/PencilText.js +15 -9
- package/dist/accordion/Accordion.js +65 -5
- package/dist/alert-dialog/index.js +14 -4
- package/dist/audio-player/index.d.ts +2 -0
- package/dist/audio-player/index.js +131 -34
- package/dist/breadcrumb/index.js +4 -1
- package/dist/calendar/index.d.ts +4 -1
- package/dist/calendar/index.js +37 -12
- package/dist/calendar/locales.d.ts +7 -0
- package/dist/calendar/locales.js +2 -0
- package/dist/carousel/index.js +20 -10
- package/dist/choice-grid/index.d.ts +6 -1
- package/dist/choice-grid/index.js +63 -33
- package/dist/color-picker/GradientEditor.d.ts +40 -0
- package/dist/color-picker/GradientEditor.js +64 -0
- package/dist/color-picker/gradient.d.ts +25 -0
- package/dist/color-picker/gradient.js +60 -0
- package/dist/color-picker/index.d.ts +27 -7
- package/dist/color-picker/index.js +144 -40
- package/dist/color-swatch/index.d.ts +10 -0
- package/dist/color-swatch/index.js +9 -0
- package/dist/command/index.js +27 -4
- package/dist/content/index.d.ts +5 -0
- package/dist/content/index.js +15 -3
- package/dist/context-menu/index.js +1 -1
- package/dist/data-table/index.d.ts +7 -1
- package/dist/data-table/index.js +67 -16
- package/dist/data-table/model.d.ts +2 -0
- package/dist/data-table/model.js +7 -0
- package/dist/date-picker/index.d.ts +2 -0
- package/dist/date-picker/index.js +20 -21
- package/dist/day-agenda/index.d.ts +6 -1
- package/dist/day-agenda/index.js +49 -21
- package/dist/day-agenda/layout.js +65 -9
- package/dist/display/ProgressRing.js +7 -3
- package/dist/display/index.d.ts +4 -1
- package/dist/display/index.js +3 -2
- package/dist/dockable-panel/index.js +15 -13
- package/dist/drawer/index.js +11 -5
- package/dist/editable/index.d.ts +24 -2
- package/dist/editable/index.js +148 -40
- package/dist/empty/illustrations.d.ts +8 -0
- package/dist/empty/illustrations.js +78 -0
- package/dist/empty/index.d.ts +6 -1
- package/dist/empty/index.js +11 -3
- package/dist/flip/index.js +28 -8
- package/dist/form/Field.js +11 -10
- package/dist/form/Input.js +4 -1
- package/dist/form/Textarea.d.ts +3 -1
- package/dist/form/Textarea.js +9 -5
- package/dist/heatmap/index.js +4 -1
- package/dist/hotkey/index.d.ts +1 -0
- package/dist/hotkey/index.js +9 -4
- package/dist/hover-card/index.js +1 -1
- package/dist/i18n/catalog.d.ts +5 -0
- package/dist/i18n/catalog.js +289 -0
- package/dist/i18n/index.d.ts +23 -0
- package/dist/i18n/index.js +50 -0
- package/dist/image-cropper/index.js +21 -17
- package/dist/image-viewer/VideoResource.d.ts +21 -0
- package/dist/image-viewer/VideoResource.js +196 -0
- package/dist/image-viewer/index.d.ts +14 -0
- package/dist/image-viewer/index.js +141 -86
- package/dist/image-viewer/lucideGeometry.d.ts +3 -0
- package/dist/image-viewer/lucideGeometry.js +19 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/input-number/index.js +13 -5
- package/dist/internal/cache.d.ts +4 -0
- package/dist/internal/cache.js +18 -1
- package/dist/internal/iconGeometry.d.ts +2 -0
- package/dist/internal/iconGeometry.js +32 -0
- package/dist/internal/renderTokens.d.ts +4 -0
- package/dist/internal/renderTokens.js +4 -0
- package/dist/internal/stroke.d.ts +7 -0
- package/dist/internal/stroke.js +63 -7
- package/dist/internal/surfaceBudget.d.ts +20 -0
- package/dist/internal/surfaceBudget.js +69 -0
- package/dist/internal/surfaceRenderer.js +87 -28
- package/dist/internal/surfaceVisibility.d.ts +3 -0
- package/dist/internal/surfaceVisibility.js +60 -0
- package/dist/internal/textRenderer.d.ts +3 -0
- package/dist/internal/textRenderer.js +13 -5
- package/dist/internal/usePencilSurface.js +17 -1
- package/dist/internal/wash.js +4 -3
- package/dist/journey-map/index.d.ts +1 -1
- package/dist/journey-map/index.js +6 -3
- package/dist/journey-map/terrain.js +10 -5
- package/dist/keypad/index.js +4 -1
- package/dist/layout/index.js +1 -1
- package/dist/log/index.js +42 -33
- package/dist/markdown/CodeBlock.d.ts +19 -0
- package/dist/markdown/CodeBlock.js +86 -0
- package/dist/markdown/Deletion.d.ts +2 -0
- package/dist/markdown/Deletion.js +69 -0
- package/dist/markdown/icons.d.ts +14 -0
- package/dist/markdown/icons.js +16 -0
- package/dist/markdown/index.d.ts +17 -0
- package/dist/markdown/index.js +177 -0
- package/dist/node-graph/index.d.ts +3 -1
- package/dist/node-graph/index.js +63 -28
- package/dist/number-ticker/index.js +4 -1
- package/dist/number-ticker/precision.js +9 -2
- package/dist/overflow-list/index.js +9 -6
- package/dist/overlay/Overlay.js +8 -3
- package/dist/overlay/Popover.js +3 -3
- package/dist/overlay/themeBridge.js +14 -2
- package/dist/pagination/index.d.ts +10 -1
- package/dist/pagination/index.js +13 -9
- package/dist/pan-zoom/index.d.ts +2 -0
- package/dist/pan-zoom/index.js +12 -8
- package/dist/pencil.d.ts +1 -0
- package/dist/pencil.js +4 -4
- package/dist/radar/index.d.ts +2 -0
- package/dist/radar/index.js +44 -21
- package/dist/range-slider/index.js +4 -3
- package/dist/rating/index.d.ts +4 -0
- package/dist/rating/index.js +131 -24
- package/dist/resizable/index.d.ts +1 -1
- package/dist/resizable/index.js +5 -2
- package/dist/scroll-area/index.d.ts +1 -1
- package/dist/scroll-area/index.js +5 -2
- package/dist/select/MultipleSelect.js +5 -4
- package/dist/select/SearchableSelect.js +64 -7
- package/dist/select/Select.d.ts +1 -0
- package/dist/select/Select.js +6 -2
- package/dist/select/StepperSelect.d.ts +29 -0
- package/dist/select/StepperSelect.js +122 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.js +1 -0
- package/dist/select/stepperModel.d.ts +7 -0
- package/dist/select/stepperModel.js +29 -0
- package/dist/selection/Checkbox.js +4 -3
- package/dist/selection/RadioGroup.js +29 -13
- package/dist/selection/Switch.js +4 -3
- package/dist/selection/useNativeChecked.d.ts +2 -1
- package/dist/selection/useNativeChecked.js +26 -8
- package/dist/slider/index.js +50 -6
- package/dist/sortable/index.js +17 -11
- package/dist/sparkline/index.js +21 -6
- package/dist/sparkline/model.js +11 -4
- package/dist/steps/index.js +4 -1
- package/dist/styles/accordion.css +228 -0
- package/dist/styles/alert-dialog.css +139 -0
- package/dist/styles/audio-player.css +22 -0
- package/dist/styles/base.css +638 -0
- package/dist/styles/breadcrumb.css +7 -0
- package/dist/styles/calendar.css +62 -0
- package/dist/styles/carousel.css +173 -0
- package/dist/styles/choice-grid.css +226 -0
- package/dist/styles/color-picker.css +281 -0
- package/dist/styles/color-swatch.css +47 -0
- package/dist/styles/command.css +52 -0
- package/dist/styles/content.css +79 -0
- package/dist/styles/context-menu.css +35 -0
- package/dist/styles/data-table.css +31 -0
- package/dist/styles/date-picker.css +107 -0
- package/dist/styles/day-agenda.css +30 -0
- package/dist/styles/display.css +304 -0
- package/dist/styles/dockable-panel.css +110 -0
- package/dist/styles/drawer.css +133 -0
- package/dist/styles/dropdown-menu.css +68 -0
- package/dist/styles/editable.css +145 -0
- package/dist/styles/empty.css +108 -0
- package/dist/styles/flip.css +101 -0
- package/dist/styles/form.css +634 -0
- package/dist/styles/heatmap.css +256 -0
- package/dist/styles/hotkey.css +189 -0
- package/dist/styles/hover-card.css +27 -0
- package/dist/styles/image-cropper.css +205 -0
- package/dist/styles/image-viewer.css +422 -0
- package/dist/styles/input-number.css +21 -0
- package/dist/styles/journey-map.css +19 -0
- package/dist/styles/keypad.css +92 -0
- package/dist/styles/layout.css +72 -0
- package/dist/styles/leaflet.css +661 -0
- package/dist/styles/log.css +203 -0
- package/dist/styles/markdown.css +379 -0
- package/dist/styles/node-graph.css +142 -0
- package/dist/styles/number-ticker.css +83 -0
- package/dist/styles/overflow-list.css +51 -0
- package/dist/styles/overlay.css +284 -0
- package/dist/styles/pagination.css +21 -0
- package/dist/styles/pan-zoom.css +136 -0
- package/dist/styles/radar.css +1 -0
- package/dist/styles/rating.css +121 -0
- package/dist/styles/resizable.css +108 -0
- package/dist/styles/scroll-area.css +122 -0
- package/dist/styles/select.css +554 -0
- package/dist/styles/selection.css +264 -0
- package/dist/styles/slider.css +145 -0
- package/dist/styles/slot.css +109 -0
- package/dist/styles/sortable.css +159 -0
- package/dist/styles/sparkline.css +24 -0
- package/dist/styles/steps.css +27 -0
- package/dist/styles/tabs.css +79 -0
- package/dist/styles/tag-input.css +213 -0
- package/dist/styles/time-picker.css +66 -0
- package/dist/styles/timeline.css +24 -0
- package/dist/styles/toast.css +160 -0
- package/dist/styles/toggle-group.css +107 -0
- package/dist/styles/tooltip.css +78 -0
- package/dist/styles/transfer.css +148 -0
- package/dist/styles/tree-select.css +131 -0
- package/dist/styles/tree.css +59 -0
- package/dist/styles/typewriter.css +62 -0
- package/dist/styles/upload.css +60 -0
- package/dist/styles/virtual-list.css +112 -0
- package/dist/styles.css +2091 -421
- package/dist/tabs/index.d.ts +9 -0
- package/dist/tabs/index.js +69 -16
- package/dist/tabs/useOverflow.d.ts +20 -0
- package/dist/tabs/useOverflow.js +145 -0
- package/dist/tag-input/index.d.ts +7 -2
- package/dist/tag-input/index.js +81 -42
- package/dist/theme/index.d.ts +11 -0
- package/dist/theme/index.js +23 -0
- package/dist/time-picker/index.js +45 -27
- package/dist/timeline/index.js +4 -1
- package/dist/toast/index.d.ts +19 -11
- package/dist/toast/index.js +51 -8
- package/dist/toast/manager.d.ts +42 -0
- package/dist/toast/manager.js +85 -0
- package/dist/toast/useToastNavigation.d.ts +3 -0
- package/dist/toast/useToastNavigation.js +75 -0
- package/dist/tokens.css +4 -0
- package/dist/tooltip/Tooltip.js +3 -3
- package/dist/transfer/index.d.ts +5 -0
- package/dist/transfer/index.js +120 -53
- package/dist/transfer/model.d.ts +4 -0
- package/dist/transfer/model.js +37 -11
- package/dist/tree/index.d.ts +5 -1
- package/dist/tree/index.js +11 -6
- package/dist/tree-select/index.js +70 -31
- package/dist/tree-select/model.d.ts +11 -5
- package/dist/tree-select/model.js +111 -83
- package/dist/typewriter/index.js +10 -10
- package/dist/upload/index.js +50 -33
- package/dist/virtual-list/index.js +11 -6
- package/package.json +423 -3
|
@@ -1,69 +1,173 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
3
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Button } from '../Button.js';
|
|
5
|
+
import { ColorSwatch } from '../color-swatch/index.js';
|
|
3
6
|
import { Input } from '../form/Input.js';
|
|
7
|
+
import { Popover } from '../overlay/Popover.js';
|
|
4
8
|
import { PencilLine } from '../PencilLine.js';
|
|
5
9
|
import { PencilSurface } from '../PencilSurface.js';
|
|
6
10
|
import { PencilText } from '../PencilText.js';
|
|
7
11
|
import { Slider } from '../slider/index.js';
|
|
12
|
+
import { ToggleGroup } from '../toggle-group/index.js';
|
|
13
|
+
import { GradientEditor, defaultGradientLabels } from './GradientEditor.js';
|
|
14
|
+
import { colorPaintTypes, createColorGradient, formatColorPaint, normalizeColorPaint } from './gradient.js';
|
|
8
15
|
import { DEFAULT_COLOR_VALUE, colorAlphaPercent, colorRgbValue, normalizeColorValue, withColorAlpha, withColorRgb, } from './color.js';
|
|
9
|
-
const defaultLabels = {
|
|
10
|
-
palette: '
|
|
11
|
-
value: '
|
|
12
|
-
alpha: '
|
|
13
|
-
presets: '
|
|
14
|
-
invalid: '
|
|
15
|
-
};
|
|
16
|
-
function presetData(preset) {
|
|
16
|
+
const defaultLabels = (t) => ({
|
|
17
|
+
palette: t('color.palette'),
|
|
18
|
+
value: t('color.value'),
|
|
19
|
+
alpha: t('color.alpha'),
|
|
20
|
+
presets: t('color.presets'),
|
|
21
|
+
invalid: t('color.invalid'),
|
|
22
|
+
});
|
|
23
|
+
function presetData(preset, allowGradient) {
|
|
17
24
|
const input = typeof preset === 'string' ? preset : preset.value;
|
|
18
|
-
const value =
|
|
25
|
+
const value = typeof input === 'string' || allowGradient ? normalizeColorPaint(input) : null;
|
|
19
26
|
if (!value)
|
|
20
27
|
return null;
|
|
21
|
-
return { value, label: typeof preset === 'string' ?
|
|
28
|
+
return { value, label: typeof preset === 'string' ? preset : preset.label ?? formatColorPaint(value) };
|
|
22
29
|
}
|
|
23
|
-
export function ColorPicker(
|
|
30
|
+
export function ColorPicker(options) {
|
|
31
|
+
const { t } = useInkraLocale();
|
|
32
|
+
const { allowGradient = false, colorTypes = colorPaintTypes, maxStops = 8, gradientLabels, className, defaultValue = DEFAULT_COLOR_VALUE, disabled = false, form, id, label = t('color.label'), labels, name, onInvalidInput, onValueChange: _onValueChange, presets = [], presentation = 'inline', seed, showAlpha = true, value, ...props } = options;
|
|
24
33
|
const generatedId = useId();
|
|
25
34
|
const resolvedId = id ?? `inkra-color-picker-${generatedId}`;
|
|
26
35
|
const resolvedSeed = seed ?? resolvedId;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
36
|
+
const controlled = value !== undefined;
|
|
37
|
+
const normalized = (input) => typeof input === 'string' || allowGradient ? normalizeColorPaint(input) : null;
|
|
38
|
+
const controlledValue = useMemo(() => value === undefined ? null : normalized(value), [value, allowGradient]);
|
|
39
|
+
const normalizedDefault = normalized(defaultValue) ?? DEFAULT_COLOR_VALUE;
|
|
40
|
+
const defaultValueRef = useRef(normalizedDefault);
|
|
29
41
|
const [uncontrolledValue, setUncontrolledValue] = useState(normalizedDefault);
|
|
30
|
-
const currentValue = controlledValue ?? (
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
42
|
+
const currentValue = controlledValue ?? (controlled ? DEFAULT_COLOR_VALUE : uncontrolledValue);
|
|
43
|
+
const currentValueRef = useRef(currentValue);
|
|
44
|
+
const gradient = typeof currentValue === 'string' ? null : currentValue;
|
|
45
|
+
const currentColor = typeof currentValue === 'string' ? currentValue : currentValue.stops[0].color;
|
|
46
|
+
const cssValue = formatColorPaint(currentValue);
|
|
47
|
+
const currentType = gradient?.type ?? 'solid';
|
|
48
|
+
const gradientMemory = useRef(null);
|
|
49
|
+
const availableTypes = [...new Set(colorTypes.filter(type => colorPaintTypes.includes(type)))];
|
|
50
|
+
if (!availableTypes.includes(currentType))
|
|
51
|
+
availableTypes.unshift(currentType);
|
|
52
|
+
const stopLimit = Number.isFinite(maxStops) ? Math.min(64, Math.max(2, Math.floor(maxStops))) : 8;
|
|
53
|
+
const fieldsetRef = useRef(null);
|
|
54
|
+
const hiddenInputRef = useRef(null);
|
|
55
|
+
const composingRef = useRef(false);
|
|
56
|
+
const skipNextBlurRef = useRef(false);
|
|
57
|
+
const [draft, setDraft] = useState(currentColor);
|
|
58
|
+
const [open, setOpen] = useState(false);
|
|
59
|
+
const copy = { ...defaultLabels(t), ...labels };
|
|
60
|
+
const gradientCopy = { ...defaultGradientLabels(t), ...gradientLabels };
|
|
61
|
+
const parsedPresets = useMemo(() => presets.map(preset => presetData(preset, allowGradient)).filter((preset) => preset !== null), [presets, allowGradient]);
|
|
34
62
|
const inputInvalid = normalizeColorValue(draft) === null;
|
|
35
|
-
const rgbValue = colorRgbValue(
|
|
36
|
-
const alpha = colorAlphaPercent(
|
|
37
|
-
useEffect(() =>
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
const rgbValue = colorRgbValue(currentColor) ?? '#4b96aa';
|
|
64
|
+
const alpha = colorAlphaPercent(currentColor);
|
|
65
|
+
useEffect(() => { setOpen(false); }, [disabled, presentation]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
currentValueRef.current = currentValue;
|
|
68
|
+
setDraft(currentColor);
|
|
69
|
+
if (gradient)
|
|
70
|
+
gradientMemory.current = gradient;
|
|
71
|
+
}, [currentValue, currentColor]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const ownerForm = hiddenInputRef.current?.form ?? fieldsetRef.current?.form;
|
|
74
|
+
if (!ownerForm)
|
|
75
|
+
return undefined;
|
|
76
|
+
let timer;
|
|
77
|
+
const reset = (event) => {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
timer = setTimeout(() => {
|
|
80
|
+
if (event.defaultPrevented)
|
|
81
|
+
return;
|
|
82
|
+
const restored = controlled ? currentValueRef.current : defaultValueRef.current;
|
|
83
|
+
if (!controlled)
|
|
84
|
+
setUncontrolledValue(restored);
|
|
85
|
+
setDraft(typeof restored === 'string' ? restored : restored.stops[0].color);
|
|
86
|
+
gradientMemory.current = typeof restored === 'string' ? null : restored;
|
|
87
|
+
if (hiddenInputRef.current)
|
|
88
|
+
hiddenInputRef.current.value = formatColorPaint(restored);
|
|
89
|
+
composingRef.current = false;
|
|
90
|
+
skipNextBlurRef.current = false;
|
|
91
|
+
}, 0);
|
|
92
|
+
};
|
|
93
|
+
ownerForm.addEventListener('reset', reset);
|
|
94
|
+
return () => {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
ownerForm.removeEventListener('reset', reset);
|
|
97
|
+
};
|
|
98
|
+
}, [controlled, form, name]);
|
|
99
|
+
const commit = (input, source) => {
|
|
100
|
+
if (disabled)
|
|
101
|
+
return;
|
|
102
|
+
const next = normalized(input);
|
|
103
|
+
if (!next)
|
|
104
|
+
return;
|
|
105
|
+
if (JSON.stringify(next) === JSON.stringify(currentValue)) {
|
|
106
|
+
setDraft(currentColor);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!controlled) {
|
|
40
110
|
setUncontrolledValue(next);
|
|
111
|
+
setDraft(typeof next === 'string' ? next : next.stops[0].color);
|
|
112
|
+
}
|
|
41
113
|
else
|
|
42
|
-
setDraft(
|
|
43
|
-
|
|
114
|
+
setDraft(currentColor);
|
|
115
|
+
if (options.allowGradient)
|
|
116
|
+
options.onValueChange?.(next, { source });
|
|
117
|
+
else if (typeof next === 'string')
|
|
118
|
+
options.onValueChange?.(next, { source });
|
|
44
119
|
};
|
|
45
120
|
const submitDraft = () => {
|
|
121
|
+
if (disabled || composingRef.current)
|
|
122
|
+
return;
|
|
46
123
|
const normalized = normalizeColorValue(draft);
|
|
47
124
|
if (normalized)
|
|
48
125
|
commit(normalized, 'input');
|
|
49
126
|
else
|
|
50
127
|
onInvalidInput?.(draft);
|
|
51
128
|
};
|
|
52
|
-
|
|
53
|
-
|
|
129
|
+
// With no usable presets, retain the editor rather than rendering an empty control.
|
|
130
|
+
const swatchesOnly = presentation === 'swatches' && parsedPresets.length > 0;
|
|
131
|
+
const editor = _jsxs("div", { className: "inkra-color-picker__editor", "data-presentation": presentation, children: [allowGradient && !swatchesOnly && _jsx(ToggleGroup.Root, { className: "inkra-color-picker__types", "aria-label": gradientCopy.type, disabled: disabled, size: "sm", value: [currentType], onValueChange: values => {
|
|
132
|
+
const type = values[0];
|
|
133
|
+
if (!type || type === currentType)
|
|
134
|
+
return;
|
|
135
|
+
if (type === 'solid')
|
|
136
|
+
commit(currentColor, 'type');
|
|
137
|
+
else
|
|
138
|
+
commit(gradient ? { ...gradient, type } : gradientMemory.current ? { ...gradientMemory.current, type } : createColorGradient(type, currentColor), 'type');
|
|
139
|
+
}, children: availableTypes.map(type => _jsx(ToggleGroup.Item, { value: type, children: gradientCopy[type] }, type)) }), !swatchesOnly && gradient && _jsx(GradientEditor, { value: gradient, disabled: disabled, maxStops: stopLimit, labels: gradientCopy, onChange: commit, renderColorEditor: (color, onChange) => _jsx(ColorPicker, { label: gradientCopy.stopColor, labels: labels, disabled: disabled, showAlpha: showAlpha, value: color, onValueChange: onChange, seed: `${resolvedSeed}-stop` }) }), !swatchesOnly && !gradient && _jsxs(_Fragment, { children: [_jsxs("div", { className: "inkra-color-picker__main", children: [_jsxs("label", { className: "inkra-color-picker__palette", "aria-label": copy.palette, title: copy.palette, children: [_jsx("span", { className: "inkra-color-picker__checker", "aria-hidden": "true" }), _jsx("span", { className: "inkra-color-picker__current", "aria-hidden": "true", style: { backgroundColor: currentColor } }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-color-picker__palette-outline", variant: "outline", seed: `${resolvedSeed}-palette` }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-color-picker__focus", variant: "outline", seed: `${resolvedSeed}-palette-focus` }), _jsx("input", { "aria-label": copy.palette, className: "inkra-color-picker__native", disabled: disabled, type: "color", value: rgbValue, onChange: event => {
|
|
140
|
+
const next = withColorRgb(currentColor, event.currentTarget.value);
|
|
141
|
+
if (next)
|
|
142
|
+
commit(next, 'palette');
|
|
143
|
+
} })] }), _jsxs("div", { className: "inkra-color-picker__value-field", children: [_jsx("label", { className: "inkra-color-picker__label", htmlFor: `${resolvedId}-value`, children: _jsx(PencilText, { seed: `${resolvedSeed}-value-label`, children: copy.value }) }), _jsx(Input, { id: `${resolvedId}-value`, "aria-describedby": inputInvalid ? `${resolvedId}-error` : undefined, "aria-invalid": inputInvalid || undefined, autoCapitalize: "off", autoComplete: "off", className: "inkra-color-picker__input", disabled: disabled, inputMode: "text", spellCheck: false, value: draft, onBlur: () => {
|
|
144
|
+
if (skipNextBlurRef.current) {
|
|
145
|
+
skipNextBlurRef.current = false;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
submitDraft();
|
|
149
|
+
}, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { composingRef.current = false; }, onKeyDown: event => {
|
|
150
|
+
if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229)
|
|
151
|
+
return;
|
|
152
|
+
if (event.key === 'Enter') {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
skipNextBlurRef.current = true;
|
|
155
|
+
submitDraft();
|
|
156
|
+
}
|
|
157
|
+
if (event.key === 'Escape') {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
skipNextBlurRef.current = true;
|
|
160
|
+
setDraft(currentColor);
|
|
161
|
+
}
|
|
162
|
+
}, onValueChange: next => {
|
|
163
|
+
skipNextBlurRef.current = false;
|
|
164
|
+
setDraft(next);
|
|
165
|
+
} }), _jsx("span", { className: "inkra-color-picker__error", id: `${resolvedId}-error`, "aria-live": "polite", hidden: !inputInvalid, children: inputInvalid ? copy.invalid : '' })] })] }), showAlpha ? _jsxs("div", { className: "inkra-color-picker__alpha", children: [_jsxs("div", { className: "inkra-color-picker__alpha-heading", children: [_jsx("span", { id: `${resolvedId}-alpha-label`, children: _jsx(PencilText, { seed: `${resolvedSeed}-alpha-label`, children: copy.alpha }) }), _jsxs("output", { htmlFor: `${resolvedId}-alpha`, children: [alpha, "%"] })] }), _jsx(Slider, { "aria-labelledby": `${resolvedId}-alpha-label`, disabled: disabled, id: `${resolvedId}-alpha`, max: 100, min: 0, seed: `${resolvedSeed}-alpha`, value: [alpha], getValueLabel: number => `${number}%`, onValueChange: values => {
|
|
166
|
+
const next = withColorAlpha(currentColor, values[0]);
|
|
54
167
|
if (next)
|
|
55
|
-
commit(next, '
|
|
56
|
-
} })] }), _jsxs(
|
|
57
|
-
|
|
58
|
-
event.preventDefault();
|
|
59
|
-
submitDraft();
|
|
60
|
-
}
|
|
61
|
-
if (event.key === 'Escape')
|
|
62
|
-
setDraft(currentValue);
|
|
63
|
-
}, onValueChange: setDraft }), _jsx("span", { className: "inkra-color-picker__error", id: `${resolvedId}-error`, "aria-live": "polite", children: inputInvalid ? copy.invalid : '' })] })] }), showAlpha ? _jsxs("div", { className: "inkra-color-picker__alpha", children: [_jsxs("div", { className: "inkra-color-picker__alpha-heading", children: [_jsx("span", { id: `${resolvedId}-alpha-label`, children: _jsx(PencilText, { seed: `${resolvedSeed}-alpha-label`, children: copy.alpha }) }), _jsxs("output", { htmlFor: `${resolvedId}-alpha`, children: [alpha, "%"] })] }), _jsx(Slider, { "aria-labelledby": `${resolvedId}-alpha-label`, disabled: disabled, id: `${resolvedId}-alpha`, max: 100, min: 0, seed: `${resolvedSeed}-alpha`, value: [alpha], getValueLabel: number => `${number}%`, onValueChange: values => {
|
|
64
|
-
const next = withColorAlpha(currentValue, values[0]);
|
|
65
|
-
if (next)
|
|
66
|
-
commit(next, 'alpha');
|
|
67
|
-
} })] }) : null, parsedPresets.length ? _jsxs(_Fragment, { children: [_jsx(PencilLine, { className: "inkra-color-picker__divider", seed: `${resolvedSeed}-divider` }), _jsx("div", { className: "inkra-color-picker__presets", role: "group", "aria-label": copy.presets, children: parsedPresets.map((preset, index) => _jsxs("button", { type: "button", "aria-label": preset.label, "aria-pressed": preset.value === currentValue, className: "inkra-color-picker__preset", disabled: disabled, title: preset.label, onClick: () => commit(preset.value, 'preset'), children: [_jsx("span", { className: "inkra-color-picker__checker", "aria-hidden": "true" }), _jsx("span", { className: "inkra-color-picker__preset-color", "aria-hidden": "true", style: { backgroundColor: preset.value } }), _jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-color-picker__preset-outline", variant: preset.value === currentValue ? 'selection' : 'outline', seed: `${resolvedSeed}-preset-${index}` })] }, `${preset.value}-${index}`)) })] }) : null, name ? _jsx("input", { type: "hidden", name: name, value: currentValue }) : null] });
|
|
168
|
+
commit(next, 'alpha');
|
|
169
|
+
} })] }) : null] }), parsedPresets.length ? _jsxs(_Fragment, { children: [!swatchesOnly && _jsx(PencilLine, { className: "inkra-color-picker__divider", seed: `${resolvedSeed}-divider` }), _jsx("div", { className: "inkra-color-picker__presets", role: "group", "aria-label": copy.presets, children: parsedPresets.map((preset, index) => _jsx(ColorSwatch, { color: typeof preset.value === 'string' ? preset.value : preset.value.stops[0].color, background: typeof preset.value === 'string' ? undefined : formatColorPaint(preset.value), label: preset.label, selected: formatColorPaint(preset.value) === cssValue, className: "inkra-color-picker__preset", disabled: disabled, seed: `${resolvedSeed}-preset-${index}`, onClick: () => commit(preset.value, 'preset') }, `${formatColorPaint(preset.value)}-${index}`)) })] }) : null] });
|
|
170
|
+
return _jsxs("fieldset", { ...props, className: ['inkra-color-picker', className].filter(Boolean).join(' '), "data-presentation": presentation, "data-alpha": showAlpha || undefined, "data-controlled": value !== undefined || undefined, "data-invalid-value": value !== undefined && controlledValue === null || undefined, disabled: disabled, form: form, id: resolvedId, ref: fieldsetRef, children: [_jsx("legend", { className: "inkra-color-picker__legend", id: `${resolvedId}-legend`, children: typeof label === 'string' ? _jsx(PencilText, { seed: `${resolvedSeed}-legend`, children: label }) : label }), presentation === 'popover' ? _jsxs(Popover.Root, { open: open && !disabled, onOpenChange: setOpen, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { variant: "outline", className: "inkra-color-picker__trigger", disabled: disabled, "aria-label": `${copy.palette}: ${typeof currentValue === 'string' ? currentValue : gradientCopy[currentValue.type]}`, children: _jsxs("span", { className: "inkra-color-picker__trigger-content", children: [_jsxs("span", { className: "inkra-color-picker__preview", "aria-hidden": "true", children: [_jsx("span", { className: "inkra-color-picker__checker" }), _jsx("span", { className: "inkra-color-picker__current", style: { background: cssValue } })] }), _jsx(PencilText, { children: typeof currentValue === 'string' ? currentValue : gradientCopy[currentValue.type] })] }) }) }), _jsx(Popover.Portal, { children: _jsx(Popover.Content, { className: "inkra-color-picker__popover", sideOffset: 8, align: "start", "aria-label": typeof label === 'string' ? label : copy.palette, "aria-describedby": undefined, children: editor }) })] }) : editor, name ? _jsx("input", { ref: hiddenInputRef, type: "hidden", form: form, name: name, value: cssValue }) : null] });
|
|
68
171
|
}
|
|
69
172
|
export { DEFAULT_COLOR_VALUE, colorAlphaPercent, colorRgbValue, formatColorValue, normalizeColorValue, parseColorValue, withColorAlpha, withColorRgb, } from './color.js';
|
|
173
|
+
export { createColorGradient, formatColorPaint, normalizeColorPaint } from './gradient.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes, CSSProperties } from 'react';
|
|
2
|
+
export interface ColorSwatchProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'color' | 'aria-pressed'> {
|
|
3
|
+
color: string;
|
|
4
|
+
background?: CSSProperties['background'];
|
|
5
|
+
label: string;
|
|
6
|
+
selected?: boolean;
|
|
7
|
+
size?: 'sm' | 'md' | 'lg';
|
|
8
|
+
seed?: string | number;
|
|
9
|
+
}
|
|
10
|
+
export declare const ColorSwatch: import("react").ForwardRefExoticComponent<ColorSwatchProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId } from 'react';
|
|
3
|
+
import { Button } from '../Button.js';
|
|
4
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
5
|
+
export const ColorSwatch = forwardRef(function ColorSwatch({ color, background, label, selected = false, size = 'md', seed, className, ...props }, ref) {
|
|
6
|
+
const id = useId();
|
|
7
|
+
const resolvedSeed = seed ?? `inkra-color-swatch-${id}`;
|
|
8
|
+
return _jsx(Button, { title: label, ...props, "aria-label": props['aria-label'] ?? label, "aria-pressed": selected, className: ['inkra-color-swatch', className].filter(Boolean).join(' '), "data-swatch-size": size, ref: ref, seed: resolvedSeed, size: size === 'lg' ? 'icon-lg' : 'icon', variant: selected ? 'outline' : 'ghost', children: _jsx("span", { className: "inkra-color-swatch__pigment", style: { color }, "aria-hidden": "true", children: background ? _jsx("span", { className: "inkra-color-swatch__background", style: { background }, children: _jsx(PencilSurface, { as: "span", className: "inkra-color-swatch__wash", variant: "outline", seed: `${resolvedSeed}-wash` }) }) : _jsx(PencilSurface, { as: "span", className: "inkra-color-swatch__wash", variant: "wash", seed: `${resolvedSeed}-wash`, style: { '--inkra-accent': 'currentColor' } }) }) });
|
|
9
|
+
});
|
package/dist/command/index.js
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Command as Primitive } from 'cmdk';
|
|
3
|
-
import {
|
|
3
|
+
import { __iconData as searchIcon } from 'lucide-react/dist/esm/icons/search.mjs';
|
|
4
|
+
import { forwardRef, useCallback, useRef } from 'react';
|
|
4
5
|
import { PencilSurface } from '../PencilSurface.js';
|
|
5
6
|
import { PencilText } from '../PencilText.js';
|
|
6
7
|
import { PencilLine } from '../PencilLine.js';
|
|
8
|
+
import { EditablePencilText } from '../form/EditablePencilText.js';
|
|
9
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
10
|
+
const searchPaths = searchIcon.node.map(([tag, attributes]) => {
|
|
11
|
+
if (tag === 'circle') {
|
|
12
|
+
const cx = Number(attributes.cx);
|
|
13
|
+
const cy = Number(attributes.cy);
|
|
14
|
+
const r = Number(attributes.r);
|
|
15
|
+
return { d: `M${cx - r} ${cy}a${r} ${r} 0 1 0 ${r * 2} 0a${r} ${r} 0 1 0 -${r * 2} 0` };
|
|
16
|
+
}
|
|
17
|
+
return { d: attributes.d };
|
|
18
|
+
});
|
|
7
19
|
function classes(base, extra) { return [base, extra].filter(Boolean).join(' '); }
|
|
8
20
|
function copy(value) { return typeof value === 'string' ? _jsx(PencilText, { children: value }) : value; }
|
|
21
|
+
function assignRef(ref, value) {
|
|
22
|
+
if (typeof ref === 'function')
|
|
23
|
+
ref(value);
|
|
24
|
+
else if (ref)
|
|
25
|
+
ref.current = value;
|
|
26
|
+
}
|
|
9
27
|
const Root = forwardRef(function Root({ className, children, loop = true, surface = true, onKeyDown, onCompositionStartCapture, onCompositionEndCapture, ...props }, ref) {
|
|
10
28
|
const composing = useRef(false);
|
|
11
29
|
return _jsx(Primitive, { ...props, ref: ref, loop: loop, className: classes('inkra-command', className), onCompositionStartCapture: event => { composing.current = true; onCompositionStartCapture?.(event); }, onCompositionEndCapture: event => { composing.current = false; onCompositionEndCapture?.(event); }, onKeyDown: event => {
|
|
@@ -15,14 +33,19 @@ const Root = forwardRef(function Root({ className, children, loop = true, surfac
|
|
|
15
33
|
onKeyDown?.(event);
|
|
16
34
|
}, children: surface ? _jsx(PencilSurface, { variant: "paper", radius: 8, className: "inkra-command__paper", children: children }) : children });
|
|
17
35
|
});
|
|
18
|
-
const Input = forwardRef(function Input({ className, ...props }, ref) {
|
|
19
|
-
|
|
36
|
+
const Input = forwardRef(function Input({ className, defaultValue, placeholder, value, ...props }, ref) {
|
|
37
|
+
const inputRef = useRef(null);
|
|
38
|
+
const captureRef = useCallback((node) => {
|
|
39
|
+
inputRef.current = node;
|
|
40
|
+
assignRef(ref, node);
|
|
41
|
+
}, [ref]);
|
|
42
|
+
return _jsxs("div", { className: "inkra-command__input-wrap inkra-field-control-hit", children: [_jsxs("div", { className: "inkra-command__input-frame", children: [_jsx("span", { "aria-hidden": "true", className: "inkra-command__input-icon", children: _jsx(PencilIcon, { paths: searchPaths, size: 22, sourceSize: 24 }) }), _jsx(Primitive.Input, { ...props, defaultValue: defaultValue, placeholder: placeholder, value: value, ref: captureRef, className: classes('inkra-command__input', className) }), _jsx(EditablePencilText, { controlRef: inputRef, defaultValue: defaultValue, placeholder: placeholder, seed: "command-input", value: value })] }), _jsx(PencilLine, { className: "inkra-command__input-line" }), _jsx(PencilLine, { className: "inkra-command__input-focus-line", seed: "command-input-focus", weight: 2.2 })] });
|
|
20
43
|
});
|
|
21
44
|
const List = forwardRef(function List({ className, ...props }, ref) {
|
|
22
45
|
return _jsx(Primitive.List, { ...props, ref: ref, className: classes('inkra-command__list', className) });
|
|
23
46
|
});
|
|
24
47
|
const Item = forwardRef(function Item({ className, children, icon, shortcut, ...props }, ref) {
|
|
25
|
-
return _jsxs(Primitive.Item, { ...props, ref: ref, className: classes('inkra-command__item', className), children: [_jsx(PencilSurface, { as: "span", variant: "selection", "aria-hidden": "true", className: "inkra-command__highlight" }), icon != null && _jsx("span", { "aria-hidden": "true", className: "inkra-command__icon", children: icon }), _jsx("span", { className: "inkra-command__label", children: copy(children) }), shortcut != null && _jsx(Shortcut, { children: shortcut })] });
|
|
48
|
+
return _jsxs(Primitive.Item, { ...props, ref: ref, className: classes('inkra-command__item', className), children: [_jsx(PencilSurface, { as: "span", variant: "selection", "aria-hidden": "true", className: "inkra-command__highlight" }), _jsx(PencilSurface, { as: "span", variant: "outline", "aria-hidden": "true", className: "inkra-command__item-focus" }), icon != null && _jsx("span", { "aria-hidden": "true", className: "inkra-command__icon", children: icon }), _jsx("span", { className: "inkra-command__label", children: copy(children) }), shortcut != null && _jsx(Shortcut, { children: shortcut })] });
|
|
26
49
|
});
|
|
27
50
|
const Group = forwardRef(function Group({ className, heading, ...props }, ref) {
|
|
28
51
|
return _jsx(Primitive.Group, { ...props, heading: copy(heading), ref: ref, className: classes('inkra-command__group', className) });
|
package/dist/content/index.d.ts
CHANGED
|
@@ -43,6 +43,11 @@ export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
43
43
|
size?: ContentSize;
|
|
44
44
|
appearance?: 'soft' | 'outline';
|
|
45
45
|
actions?: ReactNode;
|
|
46
|
+
closable?: boolean;
|
|
47
|
+
open?: boolean;
|
|
48
|
+
defaultOpen?: boolean;
|
|
49
|
+
onOpenChange?: (open: boolean) => void;
|
|
50
|
+
closeLabel?: string;
|
|
46
51
|
}
|
|
47
52
|
export declare const Alert: {
|
|
48
53
|
Root: import("react").ForwardRefExoticComponent<AlertProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/content/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
2
3
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
3
|
-
import { createContext, useContext, forwardRef, useId } from 'react';
|
|
4
|
+
import { createContext, useContext, forwardRef, useId, useState } from 'react';
|
|
5
|
+
import { Button } from '../Button.js';
|
|
6
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
4
7
|
import { PencilSurface } from '../PencilSurface.js';
|
|
5
8
|
import { PencilText } from '../PencilText.js';
|
|
6
9
|
import { PencilLine } from '../PencilLine.js';
|
|
@@ -29,8 +32,13 @@ const CardFooter = forwardRef(function CardFooter({ className, children, divided
|
|
|
29
32
|
return _jsxs("div", { ...props, ref: ref, "data-justify": justify, "data-padding": padding, "data-orientation": orientation, className: classes('inkra-card__footer', className), children: [(divided ?? inherited) && _jsx(PencilLine, { className: "inkra-card__divider" }), children] });
|
|
30
33
|
});
|
|
31
34
|
export const Card = { Root: CardRoot, Header: CardHeader, Title: CardTitle, Description: CardDescription, Content: CardContent, Footer: CardFooter };
|
|
32
|
-
const AlertRoot = forwardRef(function AlertRoot(
|
|
35
|
+
const AlertRoot = forwardRef(function AlertRoot(inkraProps, ref) {
|
|
36
|
+
const { t } = useInkraLocale();
|
|
37
|
+
const { className, variant = 'info', icon, seed, style, children, role = 'status', size = 'md', appearance = 'soft', actions, closable = false, open, defaultOpen = true, onOpenChange, closeLabel = t('alert.dismiss'), ...props } = inkraProps;
|
|
33
38
|
const id = useId();
|
|
39
|
+
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
40
|
+
if (!(open ?? internalOpen))
|
|
41
|
+
return null;
|
|
34
42
|
const tokens = {
|
|
35
43
|
color: `var(--inkra-${variant}-foreground)`,
|
|
36
44
|
'--inkra-paper': `var(--inkra-${variant}-surface)`,
|
|
@@ -40,7 +48,11 @@ const AlertRoot = forwardRef(function AlertRoot({ className, variant = 'info', i
|
|
|
40
48
|
'--inkra-border': `var(--inkra-${variant}-foreground)`,
|
|
41
49
|
...style,
|
|
42
50
|
};
|
|
43
|
-
return _jsxs(PencilSurface, { ...props, ref: ref, role: role, className: classes('inkra-alert', className), "data-variant": variant, "data-size": size, "data-appearance": appearance, variant: appearance === 'outline' ? 'outline' : 'action', radius: 8, style: tokens, seed: seed ?? `alert-${id}`, children: [icon && _jsx("span", { className: "inkra-alert__icon", "aria-hidden": "true", children: icon }), _jsxs("div", { className: "inkra-alert__body", children: [children, actions != null && _jsx("div", { className: "inkra-alert__actions", children: actions })] })
|
|
51
|
+
return _jsxs(PencilSurface, { ...props, ref: ref, role: role, className: classes('inkra-alert', className), "data-variant": variant, "data-size": size, "data-appearance": appearance, variant: appearance === 'outline' ? 'outline' : 'action', radius: 8, style: tokens, seed: seed ?? `alert-${id}`, children: [icon && _jsx("span", { className: "inkra-alert__icon", "aria-hidden": "true", children: icon }), _jsxs("div", { className: "inkra-alert__body", children: [children, actions != null && _jsx("div", { className: "inkra-alert__actions", children: actions })] }), closable && _jsx(Button, { className: "inkra-alert__close", variant: "ghost", size: "icon", "aria-label": closeLabel, title: closeLabel, onClick: () => {
|
|
52
|
+
if (open === undefined)
|
|
53
|
+
setInternalOpen(false);
|
|
54
|
+
onOpenChange?.(false);
|
|
55
|
+
}, children: _jsx(PencilIcon, { paths: [{ d: 'M18 6 6 18M6 6l12 12' }], sourceSize: 24, size: 20 }) })] });
|
|
44
56
|
});
|
|
45
57
|
const AlertTitle = forwardRef(function AlertTitle({ className, children, ...props }, ref) {
|
|
46
58
|
return _jsx("p", { ...props, ref: ref, className: classes('inkra-alert__title', className), children: copy(children) });
|
|
@@ -223,7 +223,7 @@ export const ContextMenuSubContent = forwardRef(function ContextMenuSubContent({
|
|
|
223
223
|
props = usePortalThemeProps(props);
|
|
224
224
|
return (_jsx(Primitive.Portal, { children: _jsx(Primitive.SubContent, { ...props, className: classes('inkra-menu', 'inkra-context-menu__sub-content', className), collisionPadding: collisionPadding, ref: ref, sideOffset: sideOffset, children: _jsx(PencilSurface, { className: "inkra-menu__paper", radius: 8, seed: seed ?? `context-submenu-${id}`, variant: "paper", children: children }) }) }));
|
|
225
225
|
});
|
|
226
|
-
export const ContextMenu = Object.freeze({
|
|
226
|
+
export const ContextMenu = /* @__PURE__ */ Object.freeze({
|
|
227
227
|
Root: ContextMenuRoot,
|
|
228
228
|
Trigger: ContextMenuTrigger,
|
|
229
229
|
Portal: ContextMenuPortal,
|
|
@@ -25,6 +25,12 @@ export interface DataTableProps<TData> extends Omit<HTMLAttributes<HTMLDivElemen
|
|
|
25
25
|
defaultSorting?: SortingState;
|
|
26
26
|
onSortingChange?: OnChangeFn<SortingState>;
|
|
27
27
|
pagination?: boolean;
|
|
28
|
+
/** Supply only the current server page in data. rowCount is the known total. */
|
|
29
|
+
manualPagination?: boolean;
|
|
30
|
+
rowCount?: number;
|
|
31
|
+
/** Server owns sorting/filtering; callbacks still report the requested state. */
|
|
32
|
+
manualSorting?: boolean;
|
|
33
|
+
manualFiltering?: boolean;
|
|
28
34
|
paginationState?: PaginationState;
|
|
29
35
|
defaultPagination?: PaginationState;
|
|
30
36
|
onPaginationChange?: OnChangeFn<PaginationState>;
|
|
@@ -44,4 +50,4 @@ export interface DataTableProps<TData> extends Omit<HTMLAttributes<HTMLDivElemen
|
|
|
44
50
|
pageLabel?: (page: number, pageCount: number) => ReactNode;
|
|
45
51
|
sortStatusLabel?: (columnId: string, direction: false | 'asc' | 'desc') => string;
|
|
46
52
|
}
|
|
47
|
-
export declare function DataTable<TData>(
|
|
53
|
+
export declare function DataTable<TData>(inkraProps: DataTableProps<TData>): import("react").JSX.Element;
|
package/dist/data-table/index.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
2
3
|
import { flexRender, functionalUpdate, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
5
|
import { Button } from '../Button.js';
|
|
5
6
|
import { PencilIcon } from '../PencilIcon.js';
|
|
7
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
8
|
+
import { PencilText } from '../PencilText.js';
|
|
6
9
|
import { Input } from '../form/Input.js';
|
|
7
10
|
import { Table } from '../content/index.js';
|
|
8
11
|
import { Checkbox } from '../selection/Checkbox.js';
|
|
12
|
+
import { normalizeDataTablePagination } from './model.js';
|
|
9
13
|
const sortIcon = [{ d: 'M 8 10 L 12 6 L 16 10 M 8 14 L 12 18 L 16 14' }];
|
|
14
|
+
const sortAscendingIcon = [{ d: 'M 7 15 L 12 10 L 17 15' }];
|
|
15
|
+
const sortDescendingIcon = [{ d: 'M 7 9 L 12 14 L 17 9' }];
|
|
10
16
|
const previousIcon = [{ d: 'M 14 7 L 9 12 L 14 17' }];
|
|
11
17
|
const nextIcon = [{ d: 'M 10 7 L 15 12 L 10 17' }];
|
|
12
18
|
function classes(...values) {
|
|
@@ -15,27 +21,43 @@ function classes(...values) {
|
|
|
15
21
|
function resolve(updater, current) {
|
|
16
22
|
return functionalUpdate(updater, current);
|
|
17
23
|
}
|
|
24
|
+
function pencilCopy(value, seed) {
|
|
25
|
+
return typeof value === 'string' || typeof value === 'number'
|
|
26
|
+
? _jsx(PencilText, { seed: seed, children: String(value) })
|
|
27
|
+
: value;
|
|
28
|
+
}
|
|
18
29
|
function SelectionCheckbox(props) {
|
|
19
30
|
return _jsx(Checkbox, { className: "inkra-data-table__selection", checked: props.checked, indeterminate: props.indeterminate, disabled: props.disabled, label: _jsx("span", { className: "inkra-visually-hidden", children: props.label }), onCheckedChange: props.onChange });
|
|
20
31
|
}
|
|
21
|
-
export function DataTable(
|
|
32
|
+
export function DataTable(inkraProps) {
|
|
33
|
+
const { t } = useInkraLocale();
|
|
34
|
+
const { data, columns, getRowId, caption, density = 'comfortable', layout = 'auto', striped = false, hoverable = true, stickyHeader = false, captionSide = 'top', filterable = false, filterLabel = t('table.filter'), filterPlaceholder = t('table.filter'), globalFilter, defaultGlobalFilter = '', onGlobalFilterChange, sorting, defaultSorting = [], onSortingChange, pagination = false, manualPagination = false, rowCount, manualSorting = false, manualFiltering = false, paginationState, defaultPagination = { pageIndex: 0, pageSize: 10 }, onPaginationChange, selectable = false, rowSelection, defaultRowSelection = {}, onRowSelectionChange, getRowCanSelect, loading = false, loadingMessage = t('table.loading'), emptyMessage = t('table.empty'), selectAllLabel = t('table.selectAll'), selectRowLabel = row => t('table.selectRow', { row: row.id }), previousPageLabel = t('common.previousPage'), nextPageLabel = t('common.nextPage'), paginationLabel = t('table.pagination'), pageLabel = (page, pageCount) => t('pagination.status', { page, total: pageCount }), sortStatusLabel = (columnId, direction) => t(direction === 'asc' ? 'table.ascending' : direction === 'desc' ? 'table.descending' : 'table.unsorted', { column: columnId }), className, ...props } = inkraProps;
|
|
22
35
|
const [internalFilter, setInternalFilter] = useState(defaultGlobalFilter);
|
|
23
36
|
const [internalSorting, setInternalSorting] = useState(defaultSorting);
|
|
24
37
|
const [internalPagination, setInternalPagination] = useState(defaultPagination);
|
|
25
38
|
const [internalSelection, setInternalSelection] = useState(defaultRowSelection);
|
|
39
|
+
const paginationCorrection = useRef('');
|
|
26
40
|
const resolvedFilter = globalFilter ?? internalFilter;
|
|
27
41
|
const resolvedSorting = sorting ?? internalSorting;
|
|
28
42
|
const resolvedPagination = paginationState ?? internalPagination;
|
|
29
43
|
const resolvedSelection = rowSelection ?? internalSelection;
|
|
44
|
+
const paginationRowCount = manualPagination && pagination && rowCount !== undefined
|
|
45
|
+
? (Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : data.length)
|
|
46
|
+
: data.length;
|
|
47
|
+
const initialPagination = useMemo(() => normalizeDataTablePagination(resolvedPagination, paginationRowCount), [resolvedPagination.pageIndex, resolvedPagination.pageSize, paginationRowCount]);
|
|
30
48
|
const handleSorting = updater => {
|
|
31
49
|
if (sorting === undefined)
|
|
32
50
|
setInternalSorting(current => resolve(updater, current));
|
|
33
51
|
onSortingChange?.(updater);
|
|
34
52
|
};
|
|
35
53
|
const handlePagination = updater => {
|
|
54
|
+
const normalizedUpdater = current => {
|
|
55
|
+
const rowCount = manualPagination ? paginationRowCount : table.getPrePaginationRowModel().rows.length;
|
|
56
|
+
return normalizeDataTablePagination(resolve(updater, normalizeDataTablePagination(current, rowCount)), rowCount);
|
|
57
|
+
};
|
|
36
58
|
if (paginationState === undefined)
|
|
37
|
-
setInternalPagination(
|
|
38
|
-
onPaginationChange?.(
|
|
59
|
+
setInternalPagination(normalizedUpdater);
|
|
60
|
+
onPaginationChange?.(normalizedUpdater);
|
|
39
61
|
};
|
|
40
62
|
const handleSelection = updater => {
|
|
41
63
|
if (rowSelection === undefined)
|
|
@@ -53,39 +75,68 @@ export function DataTable({ data, columns, getRowId, caption, density = 'comfort
|
|
|
53
75
|
data,
|
|
54
76
|
columns,
|
|
55
77
|
getRowId,
|
|
78
|
+
manualPagination: pagination && manualPagination,
|
|
79
|
+
rowCount: pagination && manualPagination ? paginationRowCount : undefined,
|
|
80
|
+
manualSorting,
|
|
81
|
+
manualFiltering,
|
|
56
82
|
state: {
|
|
57
83
|
globalFilter: resolvedFilter,
|
|
58
84
|
sorting: resolvedSorting,
|
|
59
|
-
pagination:
|
|
85
|
+
pagination: initialPagination,
|
|
60
86
|
rowSelection: resolvedSelection,
|
|
61
87
|
},
|
|
62
88
|
onGlobalFilterChange: updater => handleFilter(resolve(updater, resolvedFilter)),
|
|
63
89
|
onSortingChange: handleSorting,
|
|
64
90
|
onPaginationChange: handlePagination,
|
|
65
91
|
onRowSelectionChange: handleSelection,
|
|
92
|
+
defaultColumn: {
|
|
93
|
+
header: context => pencilCopy(context.column.id, `data-table-header-${context.header.id}`),
|
|
94
|
+
cell: context => pencilCopy(context.renderValue()?.toString?.() ?? null, `data-table-cell-${context.cell.id}`),
|
|
95
|
+
},
|
|
66
96
|
enableRowSelection: selectable ? getRowCanSelect ?? true : false,
|
|
67
97
|
getCoreRowModel: getCoreRowModel(),
|
|
68
98
|
getFilteredRowModel: getFilteredRowModel(),
|
|
69
99
|
getSortedRowModel: getSortedRowModel(),
|
|
70
100
|
getPaginationRowModel: pagination ? getPaginationRowModel() : undefined,
|
|
71
101
|
});
|
|
102
|
+
const filteredRowCount = pagination && manualPagination ? paginationRowCount : table.getPrePaginationRowModel().rows.length;
|
|
103
|
+
const effectivePagination = useMemo(() => normalizeDataTablePagination(initialPagination, filteredRowCount), [initialPagination, filteredRowCount]);
|
|
104
|
+
if (table.getState().pagination.pageIndex !== effectivePagination.pageIndex
|
|
105
|
+
|| table.getState().pagination.pageSize !== effectivePagination.pageSize) {
|
|
106
|
+
table.setOptions(previous => ({
|
|
107
|
+
...previous,
|
|
108
|
+
state: { ...previous.state, pagination: effectivePagination },
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
72
111
|
const rows = table.getRowModel().rows;
|
|
73
|
-
const
|
|
74
|
-
const pageCount = Math.max(1, table.getPageCount());
|
|
112
|
+
const pageCount = filteredRowCount === 0 ? 0 : Math.ceil(filteredRowCount / effectivePagination.pageSize);
|
|
75
113
|
const visibleColumnCount = Math.max(1, table.getVisibleLeafColumns().length + (selectable ? 1 : 0));
|
|
76
114
|
const hasSelectableRows = rows.some(row => row.getCanSelect());
|
|
77
115
|
useEffect(() => {
|
|
78
116
|
if (!pagination)
|
|
79
117
|
return;
|
|
80
|
-
const
|
|
81
|
-
|
|
118
|
+
const matches = resolvedPagination.pageIndex === effectivePagination.pageIndex
|
|
119
|
+
&& resolvedPagination.pageSize === effectivePagination.pageSize;
|
|
120
|
+
if (matches) {
|
|
121
|
+
paginationCorrection.current = '';
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const signature = `${filteredRowCount}:${resolvedPagination.pageIndex}:${resolvedPagination.pageSize}->${effectivePagination.pageIndex}:${effectivePagination.pageSize}`;
|
|
125
|
+
if (paginationCorrection.current === signature)
|
|
82
126
|
return;
|
|
83
|
-
|
|
127
|
+
paginationCorrection.current = signature;
|
|
84
128
|
if (paginationState === undefined)
|
|
85
|
-
setInternalPagination(
|
|
86
|
-
onPaginationChange?.(
|
|
87
|
-
}, [filteredRowCount, onPaginationChange, pagination,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
129
|
+
setInternalPagination(effectivePagination);
|
|
130
|
+
onPaginationChange?.(effectivePagination);
|
|
131
|
+
}, [effectivePagination.pageIndex, effectivePagination.pageSize, filteredRowCount, onPaginationChange, pagination,
|
|
132
|
+
paginationState, resolvedPagination.pageIndex, resolvedPagination.pageSize]);
|
|
133
|
+
return _jsxs("div", { ...props, className: classes('inkra-data-table', className), "data-loading": loading || undefined, children: [filterable && _jsx("div", { className: "inkra-data-table__toolbar", children: _jsxs("label", { className: "inkra-data-table__filter", children: [_jsx("span", { children: pencilCopy(filterLabel, 'data-table-filter-label') }), _jsx(Input, { type: "search", value: resolvedFilter, placeholder: filterPlaceholder, onChange: event => handleFilter(event.currentTarget.value) })] }) }), _jsx(Table.Container, { className: "inkra-data-table__container", children: _jsxs(Table.Root, { density: density, layout: layout, striped: striped, hoverable: hoverable, stickyHeader: stickyHeader, captionSide: captionSide, children: [caption != null && _jsx(Table.Caption, { children: caption }), _jsx(Table.Header, { children: table.getHeaderGroups().map(headerGroup => _jsxs(Table.Row, { children: [selectable && _jsx(Table.Head, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: pagination ? table.getIsAllPageRowsSelected() : table.getIsAllRowsSelected(), indeterminate: pagination ? table.getIsSomePageRowsSelected() : table.getIsSomeRowsSelected(), label: selectAllLabel, disabled: loading || !hasSelectableRows, onChange: checked => pagination ? table.toggleAllPageRowsSelected(checked) : table.toggleAllRowsSelected(checked) }) }), headerGroup.headers.map(header => _jsx(Table.Head, { colSpan: header.colSpan, "aria-sort": header.column.getIsSorted() === 'asc' ? 'ascending' : header.column.getIsSorted() === 'desc' ? 'descending' : header.column.getCanSort() ? 'none' : undefined, children: header.isPlaceholder ? null : (() => {
|
|
134
|
+
const rendered = flexRender(header.column.columnDef.header, header.getContext());
|
|
135
|
+
const direction = header.column.getIsSorted();
|
|
136
|
+
if (!header.column.getCanSort())
|
|
137
|
+
return pencilCopy(rendered, `data-table-header-${header.id}`);
|
|
138
|
+
return _jsxs("button", { type: "button", className: "inkra-data-table__sort", "data-sorted": direction || undefined, onClick: header.column.getToggleSortingHandler(), children: [_jsx(PencilSurface, { as: "span", "aria-hidden": "true", className: "inkra-data-table__sort-focus", variant: "outline", radius: 6, seed: `data-table-sort-${header.id}-focus` }), _jsx("span", { className: "inkra-data-table__sort-label", children: pencilCopy(rendered, `data-table-sort-${header.id}`) }), _jsx(PencilIcon, { "aria-hidden": "true", className: "inkra-data-table__sort-icon", "data-direction": direction || 'none', paths: direction === 'asc' ? sortAscendingIcon : direction === 'desc' ? sortDescendingIcon : sortIcon, size: 18, sourceSize: 24, active: Boolean(direction) }), _jsx("span", { className: "inkra-visually-hidden", children: sortStatusLabel(header.column.id, direction) })] });
|
|
139
|
+
})() }, header.id))] }, headerGroup.id)) }), _jsx(Table.Body, { children: loading ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", role: "status", children: pencilCopy(loadingMessage, 'data-table-loading') }) }) })
|
|
140
|
+
: rows.length === 0 ? _jsx(Table.Row, { children: _jsx(Table.Cell, { colSpan: visibleColumnCount, children: _jsx("div", { className: "inkra-data-table__state", children: pencilCopy(emptyMessage, 'data-table-empty') }) }) })
|
|
141
|
+
: rows.map(row => _jsxs(Table.Row, { selected: row.getIsSelected(), children: [selectable && _jsx(Table.Cell, { className: "inkra-data-table__select-cell", children: _jsx(SelectionCheckbox, { checked: row.getIsSelected(), label: selectRowLabel(row), disabled: !row.getCanSelect(), onChange: checked => row.toggleSelected(checked) }) }), row.getVisibleCells().map(cell => _jsx(Table.Cell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))] }, row.id)) })] }) }), pagination && _jsxs("nav", { className: "inkra-data-table__pagination", "aria-label": paginationLabel, children: [_jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": previousPageLabel, disabled: loading || pageCount === 0 || effectivePagination.pageIndex <= 0, onClick: () => table.previousPage(), children: _jsx(PencilIcon, { className: "inkra-data-table__page-arrow", paths: previousIcon, size: 18, sourceSize: 24 }) }), _jsx("span", { "aria-live": "polite", "aria-atomic": "true", children: pencilCopy(pageLabel(pageCount === 0 ? 0 : effectivePagination.pageIndex + 1, pageCount), 'data-table-page-label') }), _jsx(Button, { size: "icon-xs", variant: "ghost", "aria-label": nextPageLabel, disabled: loading || pageCount === 0 || effectivePagination.pageIndex >= pageCount - 1, onClick: () => table.nextPage(), children: _jsx(PencilIcon, { className: "inkra-data-table__page-arrow", paths: nextIcon, size: 18, sourceSize: 24 }) })] })] });
|
|
91
142
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function normalizeDataTablePagination(state, rowCount) {
|
|
2
|
+
const pageSize = Number.isFinite(state.pageSize) ? Math.max(1, Math.floor(state.pageSize)) : 10;
|
|
3
|
+
const count = Number.isFinite(rowCount) ? Math.max(0, Math.floor(rowCount)) : 0;
|
|
4
|
+
const pageCount = count === 0 ? 0 : Math.ceil(count / pageSize);
|
|
5
|
+
const requestedIndex = Number.isFinite(state.pageIndex) ? Math.max(0, Math.floor(state.pageIndex)) : 0;
|
|
6
|
+
return { pageIndex: pageCount === 0 ? 0 : Math.min(requestedIndex, pageCount - 1), pageSize };
|
|
7
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AriaAttributes, CSSProperties, FocusEventHandler } from 'react';
|
|
2
2
|
import type { DateRange } from 'react-day-picker';
|
|
3
3
|
import type { CalendarProps } from '../calendar/index.js';
|
|
4
|
+
import type { FieldControlSize } from '../form/types.js';
|
|
4
5
|
export type DatePickerMode = 'single' | 'range';
|
|
5
6
|
export type DatePickerRange = DateRange;
|
|
6
7
|
export type DatePickerValue = Date | DatePickerRange | undefined;
|
|
@@ -23,6 +24,7 @@ export interface DatePickerProps {
|
|
|
23
24
|
onBlur?: FocusEventHandler<HTMLButtonElement>;
|
|
24
25
|
onFocus?: FocusEventHandler<HTMLButtonElement>;
|
|
25
26
|
required?: boolean;
|
|
27
|
+
size?: FieldControlSize;
|
|
26
28
|
style?: CSSProperties;
|
|
27
29
|
mode?: DatePickerMode;
|
|
28
30
|
value?: DatePickerValue;
|