@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,6 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useInkraLocale, resolveInkraLocale } from '../i18n/index.js';
|
|
3
|
+
import { calendarLocales } from '../calendar/locales.js';
|
|
2
4
|
import { forwardRef, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
import { Button } from '../Button.js';
|
|
5
|
+
import { Button, ButtonGroup } from '../Button.js';
|
|
4
6
|
import { Calendar } from '../calendar/index.js';
|
|
5
7
|
import { mergeAriaIds, useFieldContext } from '../form/context.js';
|
|
6
8
|
import { useResolvedControlProps } from '../form/controlProps.js';
|
|
@@ -35,24 +37,19 @@ function initialForMode(mode, value) {
|
|
|
35
37
|
return validDate(value) ? value : undefined;
|
|
36
38
|
return isRange(value) ? value : undefined;
|
|
37
39
|
}
|
|
38
|
-
function defaultCopy(
|
|
39
|
-
const chinese = locale.toLowerCase().startsWith('zh');
|
|
40
|
+
function defaultCopy(t) {
|
|
40
41
|
return {
|
|
41
|
-
calendar:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
placeholder: chinese ? '选择日期' : 'Choose a date',
|
|
46
|
-
previous: chinese ? '上个月' : 'Previous month',
|
|
47
|
-
rangePlaceholder: chinese ? '选择日期范围' : 'Choose a date range',
|
|
48
|
-
selected: chinese ? '已选择' : 'selected',
|
|
49
|
-
today: chinese ? '今天' : 'today',
|
|
42
|
+
calendar: t('datePicker.calendar'), clear: t('datePicker.clear'),
|
|
43
|
+
next: t('datePicker.next'), open: t('datePicker.open'),
|
|
44
|
+
placeholder: t('datePicker.placeholder'), previous: t('datePicker.previous'),
|
|
45
|
+
rangePlaceholder: t('datePicker.range'), selected: t('datePicker.selected'), today: t('datePicker.today'),
|
|
50
46
|
};
|
|
51
47
|
}
|
|
52
48
|
export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef) {
|
|
53
49
|
const valueControlled = Object.prototype.hasOwnProperty.call(allProps, 'value');
|
|
54
50
|
const openControlled = Object.prototype.hasOwnProperty.call(allProps, 'open');
|
|
55
|
-
const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, autoFocus, calendarLabel, calendarProps = {}, className, clearable = true, clearButtonLabel, defaultOpen = false, defaultValue, dir, disabled = false, endName, form, fullWidth = true, id, invalid = false, locale
|
|
51
|
+
const { 'aria-describedby': ariaDescribedBy, 'aria-errormessage': ariaErrorMessage, 'aria-invalid': ariaInvalid, 'aria-label': ariaLabel, autoFocus, calendarLabel, calendarProps = {}, className, clearable = true, clearButtonLabel, defaultOpen = false, defaultValue, dir, disabled = false, endName, form, fullWidth = true, id, invalid = false, locale: localeProp, mode = 'single', name, onBlur, onFocus, onOpenChange, onValueChange, open, openButtonLabel, placeholder, rangeSeparator = ' – ', readOnly = false, required = false, size = 'md', style, value, } = allProps;
|
|
52
|
+
const { locale, t } = useInkraLocale(localeProp);
|
|
56
53
|
const generatedId = useId();
|
|
57
54
|
const field = useFieldContext();
|
|
58
55
|
const triggerRef = useRef(null);
|
|
@@ -71,7 +68,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
|
|
|
71
68
|
'aria-errormessage': ariaErrorMessage,
|
|
72
69
|
'aria-invalid': ariaInvalid,
|
|
73
70
|
});
|
|
74
|
-
const copy = defaultCopy(
|
|
71
|
+
const copy = defaultCopy(t);
|
|
75
72
|
const currentValue = initialForMode(mode, valueControlled ? value : uncontrolledValue);
|
|
76
73
|
const currentOpen = openControlled ? Boolean(open) : uncontrolledOpen;
|
|
77
74
|
const singleValue = validDate(currentValue) ? currentValue : undefined;
|
|
@@ -85,6 +82,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
|
|
|
85
82
|
const captionFormatter = useMemo(() => new Intl.DateTimeFormat(locale, {
|
|
86
83
|
month: 'long', year: 'numeric',
|
|
87
84
|
}), [locale]);
|
|
85
|
+
const dayFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
88
86
|
const weekdayFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: 'short' }), [locale]);
|
|
89
87
|
const displayValue = singleValue
|
|
90
88
|
? formatter.format(singleValue)
|
|
@@ -128,7 +126,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
|
|
|
128
126
|
const { disabled: disabledDays, formatters, labels, defaultMonth, ...restCalendarProps } = calendarProps;
|
|
129
127
|
const localizedFormatters = {
|
|
130
128
|
formatCaption: date => captionFormatter.format(date),
|
|
131
|
-
formatDay: date =>
|
|
129
|
+
formatDay: date => dayFormatter.format(date.getDate()),
|
|
132
130
|
formatWeekdayName: date => weekdayFormatter.format(date),
|
|
133
131
|
...formatters,
|
|
134
132
|
};
|
|
@@ -143,6 +141,7 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
|
|
|
143
141
|
...labels,
|
|
144
142
|
};
|
|
145
143
|
const sharedCalendarProps = {
|
|
144
|
+
locale: calendarLocales[resolveInkraLocale(locale)],
|
|
146
145
|
...restCalendarProps,
|
|
147
146
|
appearance: 'picker',
|
|
148
147
|
defaultMonth: defaultMonth ?? firstDate,
|
|
@@ -150,11 +149,11 @@ export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef)
|
|
|
150
149
|
formatters: localizedFormatters,
|
|
151
150
|
labels: localizedLabels,
|
|
152
151
|
};
|
|
153
|
-
return (_jsxs(Popover.Root, { open: currentOpen, onOpenChange: updateOpen, children: [_jsxs("span", { className: classes('inkra-date-picker', className), "data-disabled": state.disabled || undefined, "data-full-width": fullWidth || undefined, "data-has-value": hasValue || undefined, "data-invalid": state.invalid || constraintInvalid || undefined, "data-mode": mode, "data-readonly": state.readOnly || undefined, dir: dir, style: style, children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { "aria-describedby": mergeAriaIds(state.describedBy, accessibleValueId), "aria-errormessage": state.errorMessage, "aria-invalid": constraintInvalid || state.ariaInvalid, "aria-label": ariaLabel ?? (openButtonLabel ? `${openButtonLabel}: ${displayValue}` : undefined), "aria-required": state.required || undefined, autoFocus: autoFocus, className: "inkra-date-picker__trigger", disabled: state.disabled, form: form, fullWidth: fullWidth, id: triggerId, onBlur: onBlur, onFocus: onFocus, ref: triggerRef, startIcon: _jsx(PencilIcon, { paths: calendarPaths, sourceSize: 24, size: 22 }),
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
return (_jsxs(Popover.Root, { open: currentOpen, onOpenChange: updateOpen, children: [_jsxs("span", { className: classes('inkra-date-picker', className), "data-disabled": state.disabled || undefined, "data-full-width": fullWidth || undefined, "data-has-value": hasValue || undefined, "data-invalid": state.invalid || constraintInvalid || undefined, "data-mode": mode, "data-readonly": state.readOnly || undefined, "data-size": size, dir: dir, style: style, children: [_jsxs(ButtonGroup, { "aria-label": copy.calendar, className: "inkra-date-picker__control", "data-size": size, fullWidth: fullWidth, size: size, variant: "outline", children: [_jsx(Popover.Trigger, { asChild: true, children: _jsx(Button, { "aria-describedby": mergeAriaIds(state.describedBy, accessibleValueId), "aria-errormessage": state.errorMessage, "aria-invalid": constraintInvalid || state.ariaInvalid, "aria-label": ariaLabel ?? (openButtonLabel ? `${openButtonLabel}: ${displayValue}` : undefined), "aria-required": state.required || undefined, autoFocus: autoFocus, className: "inkra-date-picker__trigger", disabled: state.disabled, form: form, fullWidth: fullWidth, id: triggerId, onBlur: onBlur, onFocus: onFocus, ref: triggerRef, startIcon: _jsx(PencilIcon, { paths: calendarPaths, sourceSize: 24, size: 22 }), children: _jsx("span", { className: "inkra-date-picker__value", "data-placeholder": !hasValue || undefined, children: displayValue }) }) }), clearable ? (_jsx(Button, { "aria-hidden": !hasValue || undefined, "aria-label": clearButtonLabel ?? copy.clear, className: "inkra-date-picker__clear", disabled: !hasValue || state.disabled || state.readOnly, onPointerDown: event => event.preventDefault(), onClick: () => {
|
|
153
|
+
commit(undefined, 'clear');
|
|
154
|
+
updateOpen(false);
|
|
155
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
156
|
+
}, size: size === 'sm' ? 'icon-sm' : size === 'lg' ? 'icon-lg' : 'icon', tabIndex: hasValue ? 0 : -1, variant: "ghost", children: _jsx(PencilIcon, { paths: clearPaths, sourceSize: 24, size: 22 }) })) : null] }), accessibleValueId ? _jsx("span", { className: "inkra-date-picker__accessible-value", id: accessibleValueId, children: displayValue }) : null, name || state.required ? (mode === 'range' ? _jsxs(_Fragment, { children: [_jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.from) }), _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: endName ?? (name ? `${name}End` : undefined), onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(rangeValue?.to) })] }) : _jsx("input", { "aria-hidden": "true", className: "inkra-date-picker__form-field", disabled: state.disabled, form: form, name: name, onChange: () => undefined, onInvalid: event => { event.preventDefault(); setConstraintInvalid(true); triggerRef.current?.focus(); }, readOnly: state.readOnly, required: state.required, tabIndex: -1, type: "text", value: datePickerLocalValue(singleValue) })) : null] }), _jsx(Popover.Portal, { children: _jsxs(Popover.Content, { align: "start", "aria-label": calendarLabel ?? copy.calendar, className: "inkra-date-picker__popover", collisionPadding: 12, onOpenAutoFocus: event => {
|
|
158
157
|
event.preventDefault();
|
|
159
158
|
requestAnimationFrame(() => {
|
|
160
159
|
requestAnimationFrame(() => {
|
|
@@ -13,6 +13,7 @@ export interface AgendaEvent {
|
|
|
13
13
|
}
|
|
14
14
|
export interface DayAgendaProps extends Omit<HTMLAttributes<HTMLElement>, 'onSelect'> {
|
|
15
15
|
date: string;
|
|
16
|
+
/** Immutable snapshots: replace the events array and changed event records when editing. */
|
|
16
17
|
events: readonly AgendaEvent[];
|
|
17
18
|
selectedEvent?: string;
|
|
18
19
|
onEventSelect?: (event: AgendaEvent) => void;
|
|
@@ -32,6 +33,10 @@ export interface DayAgendaProps extends Omit<HTMLAttributes<HTMLElement>, 'onSel
|
|
|
32
33
|
previous: string;
|
|
33
34
|
next: string;
|
|
34
35
|
};
|
|
36
|
+
/** Opt-in initial summary. Layout uses every event and all can be revealed. */
|
|
37
|
+
maxVisibleEvents?: number;
|
|
38
|
+
showAllEventsLabel?: (count: number) => string;
|
|
39
|
+
showFewerEventsLabel?: string;
|
|
35
40
|
}
|
|
36
41
|
export declare function agendaTime(minute: number, hourCycle?: 'h12' | 'h23', locale?: string): string;
|
|
37
42
|
export declare function agendaInterval(event: AgendaEvent): {
|
|
@@ -40,4 +45,4 @@ export declare function agendaInterval(event: AgendaEvent): {
|
|
|
40
45
|
before: boolean;
|
|
41
46
|
after: boolean;
|
|
42
47
|
} | null;
|
|
43
|
-
export declare function DayAgenda(
|
|
48
|
+
export declare function DayAgenda(inkraProps: DayAgendaProps): import("react").JSX.Element;
|
package/dist/day-agenda/index.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useId } from 'react';
|
|
2
|
+
import { useId, useMemo, useState } from 'react';
|
|
3
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
3
4
|
import { TravelIcon } from '../internal/travelIcons.js';
|
|
4
5
|
import { Button } from '../Button.js';
|
|
5
6
|
import { createAgendaScale, layoutAgendaCards } from './layout.js';
|
|
6
7
|
import { PencilSurface } from '../PencilSurface.js';
|
|
7
8
|
import { PencilLine } from '../PencilLine.js';
|
|
8
9
|
import { PencilText } from '../PencilText.js';
|
|
9
|
-
|
|
10
|
+
function formatAgendaMinute(minute, formatter) {
|
|
10
11
|
const value = Math.max(0, Math.min(1440, Math.floor(minute)));
|
|
11
|
-
if (
|
|
12
|
+
if (formatter) {
|
|
12
13
|
const time = new Date(Date.UTC(2000, 0, 1, 0, value));
|
|
13
|
-
return
|
|
14
|
+
return formatter.format(time) + (value === 1440 ? ' +1' : '');
|
|
14
15
|
}
|
|
15
16
|
return value === 1440 ? '00:00 +1' : `${String(Math.floor(value / 60)).padStart(2, '0')}:${String(value % 60).padStart(2, '0')}`;
|
|
16
17
|
}
|
|
18
|
+
export function agendaTime(minute, hourCycle = 'h23', locale = 'en') {
|
|
19
|
+
return formatAgendaMinute(minute, hourCycle === 'h12'
|
|
20
|
+
? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', hour: 'numeric', minute: '2-digit', hourCycle })
|
|
21
|
+
: undefined);
|
|
22
|
+
}
|
|
17
23
|
export function agendaInterval(event) {
|
|
18
24
|
if (event.startMinute === undefined || !Number.isFinite(event.startMinute))
|
|
19
25
|
return null;
|
|
@@ -24,24 +30,46 @@ export function agendaInterval(event) {
|
|
|
24
30
|
return null;
|
|
25
31
|
return { start: Math.max(0, event.startMinute), end: Math.min(1440, end ?? event.startMinute), before: event.startMinute < 0, after: (end ?? 0) > 1440 };
|
|
26
32
|
}
|
|
27
|
-
export function DayAgenda(
|
|
33
|
+
export function DayAgenda(inkraProps) {
|
|
34
|
+
const { locale, t } = useInkraLocale(inkraProps.locale);
|
|
35
|
+
const { date, events, selectedEvent, onEventSelect, locale: _locale, timeZoneLabel, emptyLabel = t('agenda.empty'), untimedLabel = t('agenda.pending'), showOverview = true, hourCycle = 'h23', density = 'comfortable', showDescriptions = true, divided = true, disabled = false, onPreviousDay, onNextDay, navigationLabels = { previous: t('agenda.previous'), next: t('agenda.next') }, maxVisibleEvents, showAllEventsLabel = count => t('agenda.all', { count }), showFewerEventsLabel = t('agenda.fewer'), className, ...props } = inkraProps;
|
|
28
36
|
const id = useId();
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
const [expandedDate, setExpandedDate] = useState();
|
|
38
|
+
const displayLimit = maxVisibleEvents !== undefined && Number.isFinite(maxVisibleEvents) ? Math.max(0, Math.floor(maxVisibleEvents)) : Infinity;
|
|
39
|
+
const expanded = expandedDate === date;
|
|
40
|
+
const title = useMemo(() => {
|
|
41
|
+
const validDate = /^\d{4}-\d{2}-\d{2}$/.test(date) ? new Date(`${date}T12:00:00Z`) : new Date(NaN);
|
|
42
|
+
return Number.isFinite(validDate.getTime()) ? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', month: 'long', day: 'numeric', weekday: 'short' }).format(validDate) : date;
|
|
43
|
+
}, [date, locale]);
|
|
44
|
+
const timeFormatter = useMemo(() => hourCycle === 'h12'
|
|
45
|
+
? new Intl.DateTimeFormat(locale, { timeZone: 'UTC', hour: 'numeric', minute: '2-digit', hourCycle })
|
|
46
|
+
: undefined, [hourCycle, locale]);
|
|
47
|
+
const { ordered, pending, scale, cards, ticks } = useMemo(() => {
|
|
48
|
+
const records = events.map(event => ({ event, interval: agendaInterval(event) }))
|
|
49
|
+
.sort((a, b) => (a.interval?.start ?? Infinity) - (b.interval?.start ?? Infinity));
|
|
50
|
+
const ordered = records.map(row => row.event);
|
|
51
|
+
const pending = records.filter(row => !row.interval).map(row => row.event);
|
|
52
|
+
const timed = records.flatMap(({ event, interval }) => interval ? [{ ...interval, id: event.id, event }] : []);
|
|
53
|
+
const intervals = timed.filter(row => row.end > row.start);
|
|
54
|
+
const points = timed.filter(row => row.end === row.start);
|
|
55
|
+
const scale = createAgendaScale(intervals, points.map(row => row.start), density === 'compact');
|
|
56
|
+
const formatTime = (minute) => formatAgendaMinute(minute, timeFormatter);
|
|
57
|
+
const cards = layoutAgendaCards(timed, scale).sort((a, b) => a.start - b.start).map(row => ({
|
|
58
|
+
...row,
|
|
59
|
+
time: `${row.before ? '\u2190 ' : ''}${formatTime(row.start)}${row.event.endMinute !== undefined ? ` - ${formatTime(row.end)}` : ''}${row.after ? ' \u2192' : ''}`,
|
|
60
|
+
}));
|
|
61
|
+
const ticks = Array.from({ length: 25 }, (_, hour) => hour * 60)
|
|
62
|
+
.filter((minute, index, list) => index === 0 || index === 24 || scale.position(minute) - scale.position(list[index - 1]) >= 25)
|
|
63
|
+
.map(minute => ({ minute, label: formatTime(minute) }));
|
|
64
|
+
return { ordered, pending, scale, cards, ticks };
|
|
65
|
+
}, [density, events, timeFormatter]);
|
|
66
|
+
const shownIds = useMemo(() => expanded || displayLimit >= ordered.length ? null
|
|
67
|
+
: new Set([...ordered.slice(0, displayLimit).map(event => event.id), ...(selectedEvent ? [selectedEvent] : [])]), [expanded, displayLimit, ordered, selectedEvent]);
|
|
68
|
+
const visibleCards = shownIds === null ? cards : cards.filter(row => shownIds.has(row.id));
|
|
69
|
+
const visiblePending = shownIds === null ? pending : pending.filter(event => shownIds.has(event.id));
|
|
42
70
|
const renderCard = (event, time) => _jsxs(PencilSurface, { variant: "quiet", radius: 6, className: "inkra-agenda__card", "data-selected": selectedEvent === event.id, style: { '--inkra-accent-soft': event.color ?? (event.kind === 'stay' ? '#c8c38f' : event.kind === 'travel' ? '#87bbc0' : '#a7c7ad') }, children: [onEventSelect && _jsx("button", { type: "button", className: "inkra-agenda__hit", disabled: disabled || event.disabled, "aria-label": `${time}, ${event.title}`, "aria-pressed": selectedEvent === event.id, onClick: () => onEventSelect(event) }), _jsxs("div", { className: "inkra-agenda__card-copy", children: [_jsxs("span", { className: "inkra-agenda__card-title", title: event.title, children: [event.icon && _jsx("span", { "aria-hidden": "true", children: event.icon }), _jsx(PencilText, { ellipsis: true, children: event.title })] }), showDescriptions && event.description && _jsx("span", { className: "inkra-agenda__description", children: event.description })] })] });
|
|
43
|
-
return _jsxs("section", { ...props, "data-density": density, "data-hour-cycle": hourCycle, className: ['inkra-agenda', className].filter(Boolean).join(' '), "aria-labelledby": props['aria-labelledby'] ?? id, children: [_jsxs("header", { className: "inkra-agenda__heading", children: [_jsx(PencilText, { as: "h3", id: id, children: title }), timeZoneLabel && _jsx(PencilText, { children: timeZoneLabel })] }), _jsx(PencilLine, {}), ordered.length === 0 ? _jsx(PencilText, { as: "p", children: emptyLabel }) : _jsxs(_Fragment, { children: [_jsx("div", { className: "inkra-agenda__timeline", "data-overview": showOverview, children: _jsxs("div", { className: "inkra-agenda__scale", style: { height: scale.height + 24 }, children: [ticks.map(minute => _jsxs("div", { className: "inkra-agenda__tick", style: { top: scale.position(minute) + 12 }, children: [_jsx("span", { children: _jsx(PencilText, { children:
|
|
44
|
-
const time =
|
|
71
|
+
return _jsxs("section", { ...props, "data-density": density, "data-hour-cycle": hourCycle, className: ['inkra-agenda', className].filter(Boolean).join(' '), "aria-labelledby": props['aria-labelledby'] ?? id, children: [_jsxs("header", { className: "inkra-agenda__heading", children: [_jsx(PencilText, { as: "h3", id: id, children: title }), timeZoneLabel && _jsx(PencilText, { children: timeZoneLabel })] }), _jsx(PencilLine, {}), ordered.length === 0 ? _jsx(PencilText, { as: "p", children: emptyLabel }) : _jsxs(_Fragment, { children: [_jsx("div", { className: "inkra-agenda__timeline", "data-overview": showOverview, children: _jsxs("div", { className: "inkra-agenda__scale", style: { height: scale.height + 24 }, children: [ticks.map(({ minute, label }) => _jsxs("div", { className: "inkra-agenda__tick", style: { top: scale.position(minute) + 12 }, children: [_jsx("span", { children: _jsx(PencilText, { children: label }) }), divided && _jsx(PencilLine, {})] }, minute)), _jsx("ol", { className: "inkra-agenda__lanes", children: visibleCards.map(row => {
|
|
72
|
+
const time = row.time;
|
|
45
73
|
return _jsxs("li", { "data-kind": row.event.kind, style: { top: row.top + 14, height: Math.max(44, row.height - 4), insetInlineStart: `${row.lane / row.lanes * 100}%`, width: `${100 / row.lanes}%` }, children: [renderCard(row.event, time), _jsx("span", { className: "inkra-agenda__accessible-time", children: time })] }, row.id);
|
|
46
|
-
}) })] }) }),
|
|
74
|
+
}) })] }) }), visiblePending.length > 0 && _jsxs("div", { className: "inkra-agenda__pending", children: [_jsx(PencilText, { children: untimedLabel }), visiblePending.map(event => _jsx("div", { children: renderCard(event, untimedLabel) }, event.id))] }), ordered.length > displayLimit && _jsx(Button, { size: "sm", variant: "ghost", "aria-expanded": expanded, onClick: () => setExpandedDate(expanded ? undefined : date), children: expanded ? showFewerEventsLabel : showAllEventsLabel(ordered.length) })] }), (onPreviousDay || onNextDay) && _jsxs("footer", { className: "inkra-agenda__navigation", children: [_jsx(PencilLine, {}), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.previous, title: navigationLabels.previous, disabled: !onPreviousDay, onClick: onPreviousDay, children: _jsx(TravelIcon, { name: "previous" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": navigationLabels.next, title: navigationLabels.next, disabled: !onNextDay, onClick: onNextDay, children: _jsx(TravelIcon, { name: "next" }) })] })] });
|
|
47
75
|
}
|
|
@@ -1,16 +1,56 @@
|
|
|
1
|
+
function heapPush(heap, value, less) {
|
|
2
|
+
let index = heap.length;
|
|
3
|
+
heap.push(value);
|
|
4
|
+
while (index > 0) {
|
|
5
|
+
const parent = (index - 1) >>> 1;
|
|
6
|
+
if (!less(value, heap[parent]))
|
|
7
|
+
break;
|
|
8
|
+
heap[index] = heap[parent];
|
|
9
|
+
index = parent;
|
|
10
|
+
}
|
|
11
|
+
heap[index] = value;
|
|
12
|
+
}
|
|
13
|
+
function heapPop(heap, less) {
|
|
14
|
+
const first = heap[0];
|
|
15
|
+
const last = heap.pop();
|
|
16
|
+
if (heap.length) {
|
|
17
|
+
let index = 0;
|
|
18
|
+
while (index * 2 + 1 < heap.length) {
|
|
19
|
+
let child = index * 2 + 1;
|
|
20
|
+
if (child + 1 < heap.length && less(heap[child + 1], heap[child]))
|
|
21
|
+
child++;
|
|
22
|
+
if (!less(heap[child], last))
|
|
23
|
+
break;
|
|
24
|
+
heap[index] = heap[child];
|
|
25
|
+
index = child;
|
|
26
|
+
}
|
|
27
|
+
heap[index] = last;
|
|
28
|
+
}
|
|
29
|
+
return first;
|
|
30
|
+
}
|
|
31
|
+
const lowerLane = (left, right) => left < right;
|
|
32
|
+
const earlierEnd = (left, right) => left.end < right.end || (left.end === right.end && left.lane < right.lane);
|
|
1
33
|
export function layoutAgendaIntervals(intervals) {
|
|
2
34
|
const sorted = [...intervals].sort((a, b) => a.start - b.start || a.id.localeCompare(b.id));
|
|
3
35
|
const result = [];
|
|
4
|
-
let group = [], groupEnd = -Infinity,
|
|
5
|
-
|
|
6
|
-
|
|
36
|
+
let group = [], groupEnd = -Infinity, laneCount = 0;
|
|
37
|
+
let occupied = [], available = [];
|
|
38
|
+
const flush = () => {
|
|
39
|
+
for (const item of group)
|
|
40
|
+
result.push({ ...item, lanes: laneCount });
|
|
41
|
+
group = [];
|
|
42
|
+
occupied = [];
|
|
43
|
+
available = [];
|
|
44
|
+
laneCount = 0;
|
|
45
|
+
};
|
|
7
46
|
for (const item of sorted) {
|
|
8
47
|
if (item.start >= groupEnd)
|
|
9
48
|
flush();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
49
|
+
while (occupied.length && occupied[0].end <= item.start) {
|
|
50
|
+
heapPush(available, heapPop(occupied, earlierEnd).lane, lowerLane);
|
|
51
|
+
}
|
|
52
|
+
const lane = available.length ? heapPop(available, lowerLane) : laneCount++;
|
|
53
|
+
heapPush(occupied, { end: item.end, lane }, earlierEnd);
|
|
14
54
|
group.push({ ...item, lane });
|
|
15
55
|
groupEnd = Math.max(groupEnd, item.end);
|
|
16
56
|
}
|
|
@@ -23,6 +63,10 @@ export function createAgendaScale(intervals, pointTimes, compact) {
|
|
|
23
63
|
const boundaries = [...new Set([0, 1440, ...pointTimes, ...intervals.flatMap(row => [row.start, row.end])])].sort((a, b) => a - b);
|
|
24
64
|
const before = new Map(), after = new Map();
|
|
25
65
|
const segments = [];
|
|
66
|
+
// Boundaries already contain every endpoint, so the furthest active end is
|
|
67
|
+
// enough to decide occupancy without rescanning every event for every segment.
|
|
68
|
+
const ordered = [...intervals].sort((a, b) => a.start - b.start);
|
|
69
|
+
let intervalIndex = 0, occupiedUntil = -Infinity;
|
|
26
70
|
let height = 0;
|
|
27
71
|
for (let index = 0; index < boundaries.length; index++) {
|
|
28
72
|
const time = boundaries[index];
|
|
@@ -33,7 +77,11 @@ export function createAgendaScale(intervals, pointTimes, compact) {
|
|
|
33
77
|
const next = boundaries[index + 1];
|
|
34
78
|
if (next === undefined)
|
|
35
79
|
continue;
|
|
36
|
-
|
|
80
|
+
while (intervalIndex < ordered.length && ordered[intervalIndex].start < next) {
|
|
81
|
+
occupiedUntil = Math.max(occupiedUntil, ordered[intervalIndex].end);
|
|
82
|
+
intervalIndex++;
|
|
83
|
+
}
|
|
84
|
+
const occupied = occupiedUntil > time;
|
|
37
85
|
const size = Math.max(occupied ? rowHeight : 16, (next - time) / 60 * 32);
|
|
38
86
|
segments.push({ start: time, end: next, top: height, size });
|
|
39
87
|
height += size;
|
|
@@ -43,7 +91,15 @@ export function createAgendaScale(intervals, pointTimes, compact) {
|
|
|
43
91
|
const exact = (edge === 'after' ? after : before).get(time);
|
|
44
92
|
if (exact !== undefined)
|
|
45
93
|
return exact;
|
|
46
|
-
|
|
94
|
+
let low = 0, high = segments.length - 1;
|
|
95
|
+
while (low <= high) {
|
|
96
|
+
const middle = (low + high) >>> 1;
|
|
97
|
+
if (segments[middle].start <= time)
|
|
98
|
+
low = middle + 1;
|
|
99
|
+
else
|
|
100
|
+
high = middle - 1;
|
|
101
|
+
}
|
|
102
|
+
const segment = high >= 0 && time <= segments[high].end ? segments[high] : undefined;
|
|
47
103
|
return segment ? segment.top + (time - segment.start) / (segment.end - segment.start) * segment.size : 0;
|
|
48
104
|
};
|
|
49
105
|
return { position, height, rowHeight };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef } from 'react';
|
|
3
|
-
import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr } from '../internal/cache.js';
|
|
3
|
+
import { BoundedCache, PENCIL_RENDERER_VERSION, boundedCanvasSize, canvasByteCost, canvasIsUsable, effectiveDpr } from '../internal/cache.js';
|
|
4
4
|
import { observeCanvasRecovery } from '../internal/recovery.js';
|
|
5
5
|
import { seedToNumber } from '../internal/random.js';
|
|
6
6
|
import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
|
|
@@ -84,6 +84,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
|
|
|
84
84
|
const weight = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
|
|
85
85
|
const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, maxStrokeRoughness);
|
|
86
86
|
const dpr = effectiveDpr(2);
|
|
87
|
+
const size = boundedCanvasSize(width, width, dpr);
|
|
87
88
|
const key = JSON.stringify([PENCIL_RENDERER_VERSION, diameter, thickness, width, dpr, style.color, weight, roughness, seed]);
|
|
88
89
|
if (key === lastPaint && canvasIsUsable(canvas))
|
|
89
90
|
return;
|
|
@@ -94,7 +95,9 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
|
|
|
94
95
|
}
|
|
95
96
|
if (!image) {
|
|
96
97
|
image = document.createElement('canvas');
|
|
97
|
-
image.width =
|
|
98
|
+
image.width = size.width;
|
|
99
|
+
image.height = size.height;
|
|
100
|
+
image.addEventListener('contextlost', () => ringCache.delete(key), { once: true });
|
|
98
101
|
const context = image.getContext('2d');
|
|
99
102
|
if (!context)
|
|
100
103
|
throw new Error('Canvas unavailable');
|
|
@@ -116,6 +119,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
|
|
|
116
119
|
lastPaint = key;
|
|
117
120
|
}
|
|
118
121
|
catch {
|
|
122
|
+
canvas.width = canvas.height = 0;
|
|
119
123
|
delete canvas.dataset.ready;
|
|
120
124
|
lastPaint = '';
|
|
121
125
|
}
|
|
@@ -131,7 +135,7 @@ export function ProgressRing({ diameter, thickness, percent, seed }) {
|
|
|
131
135
|
const scheme = matchMedia('(prefers-color-scheme: dark)');
|
|
132
136
|
scheme.addEventListener('change', paint);
|
|
133
137
|
window.addEventListener('resize', paint);
|
|
134
|
-
const stopRecovery = observeCanvasRecovery(paint);
|
|
138
|
+
const stopRecovery = observeCanvasRecovery(() => { lastPaint = ''; paint(); });
|
|
135
139
|
paint();
|
|
136
140
|
return () => {
|
|
137
141
|
observer.disconnect();
|
package/dist/display/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HTMLAttributes, ReactNode } from 'react';
|
|
1
|
+
import type { CSSProperties, HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import { type ProgressPartialSegment, type ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
|
|
3
3
|
export type { ProgressPartialSegment, ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
|
|
4
4
|
export type BadgeVariant = 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
@@ -22,6 +22,9 @@ export declare const Separator: import("react").ForwardRefExoticComponent<Separa
|
|
|
22
22
|
export interface SkeletonProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
23
23
|
seed?: string | number;
|
|
24
24
|
radius?: number;
|
|
25
|
+
width?: CSSProperties['width'];
|
|
26
|
+
height?: CSSProperties['height'];
|
|
27
|
+
animation?: 'none' | 'pulse';
|
|
25
28
|
}
|
|
26
29
|
export declare const Skeleton: import("react").ForwardRefExoticComponent<SkeletonProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
27
30
|
export interface ProgressRenderState {
|
package/dist/display/index.js
CHANGED
|
@@ -30,8 +30,9 @@ export const Separator = forwardRef(function Separator({ className, decorative =
|
|
|
30
30
|
: { 'aria-orientation': orientation, role: 'separator' };
|
|
31
31
|
return (_jsx("div", { ...props, ...semanticProps, className: ['inkra-separator', className].filter(Boolean).join(' '), "data-orientation": orientation, ref: ref, children: _jsx(PencilLine, { className: "inkra-separator__line", orientation: orientation, seed: seed ?? `inkra-separator-${orientation}`, weight: Number.isFinite(weight) ? Math.max(0.3, Math.min(2, weight)) : 0.9 }) }));
|
|
32
32
|
});
|
|
33
|
-
export const Skeleton = forwardRef(function Skeleton({ className, seed = 'inkra-skeleton', radius, ...props }, ref) {
|
|
34
|
-
|
|
33
|
+
export const Skeleton = forwardRef(function Skeleton({ className, seed = 'inkra-skeleton', radius = 7, width, height, animation = 'none', style, ...props }, ref) {
|
|
34
|
+
const resolvedRadius = Number.isFinite(radius) ? Math.max(0, Math.min(radius, 8)) : 7;
|
|
35
|
+
return (_jsx(PencilSurface, { ...props, "aria-hidden": "true", as: "div", className: ['inkra-skeleton', className].filter(Boolean).join(' '), "data-animation": animation, style: { width, height, borderRadius: resolvedRadius, ...style }, ref: ref, seed: seed, radius: resolvedRadius, variant: "selection" }));
|
|
35
36
|
});
|
|
36
37
|
function normalizeMax(max) {
|
|
37
38
|
return typeof max === 'number' && Number.isFinite(max) && max > 0 ? max : 100;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
2
3
|
import { DndContext, MouseSensor, TouchSensor, useDraggable, useSensor, useSensors, } from '@dnd-kit/core';
|
|
3
4
|
import { createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
4
5
|
import { Button } from '../Button.js';
|
|
@@ -43,7 +44,7 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
|
|
|
43
44
|
return () => observer.disconnect();
|
|
44
45
|
}, []);
|
|
45
46
|
const activate = useCallback((id) => {
|
|
46
|
-
setOrder(current => promoteDockablePanel(current, id));
|
|
47
|
+
setOrder(current => current[current.length - 1] === id ? current : promoteDockablePanel(current, id));
|
|
47
48
|
if (activeId === undefined)
|
|
48
49
|
setUncontrolledActiveId(id);
|
|
49
50
|
if (resolvedActiveId !== id)
|
|
@@ -51,7 +52,7 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
|
|
|
51
52
|
}, [activeId, onActiveChange, resolvedActiveId]);
|
|
52
53
|
useEffect(() => {
|
|
53
54
|
if (activeId != null)
|
|
54
|
-
setOrder(current => promoteDockablePanel(current, activeId));
|
|
55
|
+
setOrder(current => current[current.length - 1] === activeId ? current : promoteDockablePanel(current, activeId));
|
|
55
56
|
}, [activeId]);
|
|
56
57
|
const register = useCallback((id) => {
|
|
57
58
|
setOrder(current => current.includes(id) ? current : [...current, id]);
|
|
@@ -72,18 +73,18 @@ export const DockablePanelWorkspace = forwardRef(function DockablePanelWorkspace
|
|
|
72
73
|
titleBarVisibility,
|
|
73
74
|
width: size.width,
|
|
74
75
|
}), [boundaryPadding, size.height, size.width, titleBarVisibility]);
|
|
76
|
+
const layers = useMemo(() => new Map(order.map((id, index) => [id, index])), [order]);
|
|
75
77
|
const context = useMemo(() => ({
|
|
76
78
|
activeId: resolvedActiveId,
|
|
77
79
|
activate,
|
|
78
80
|
bounds,
|
|
79
81
|
commitDrag,
|
|
80
82
|
layer(id) {
|
|
81
|
-
|
|
82
|
-
return baseZIndex + (resolvedActiveId === id ? order.length : Math.max(0, index));
|
|
83
|
+
return baseZIndex + (resolvedActiveId === id ? order.length : (layers.get(id) ?? 0));
|
|
83
84
|
},
|
|
84
85
|
register,
|
|
85
86
|
snapThreshold,
|
|
86
|
-
}), [activate, baseZIndex, bounds, commitDrag, order, register, resolvedActiveId, snapThreshold]);
|
|
87
|
+
}), [activate, baseZIndex, bounds, commitDrag, layers, order.length, register, resolvedActiveId, snapThreshold]);
|
|
87
88
|
function handleDragEnd(event) {
|
|
88
89
|
const id = String(event.active.id);
|
|
89
90
|
dragCallbacks.current.get(id)?.({ x: event.delta.x, y: event.delta.y });
|
|
@@ -96,13 +97,13 @@ const resetPaths = [
|
|
|
96
97
|
{ d: 'M4 12a8 8 0 1 0 2.34-5.66L4 8.68' },
|
|
97
98
|
{ d: 'M4 4v4.68h4.68' },
|
|
98
99
|
];
|
|
99
|
-
const defaultLabels = {
|
|
100
|
-
move: '
|
|
101
|
-
collapse: '
|
|
102
|
-
expand: '
|
|
103
|
-
reset: '
|
|
104
|
-
keyboardInstructions: '
|
|
105
|
-
};
|
|
100
|
+
const defaultLabels = (t) => ({
|
|
101
|
+
move: t('dockable.move'),
|
|
102
|
+
collapse: t('dockable.collapse'),
|
|
103
|
+
expand: t('dockable.expand'),
|
|
104
|
+
reset: t('dockable.reset'),
|
|
105
|
+
keyboardInstructions: t('dockable.instructions'),
|
|
106
|
+
});
|
|
106
107
|
function renderTitle(title) {
|
|
107
108
|
return typeof title === 'string' ? _jsx(PencilText, { children: title }) : title;
|
|
108
109
|
}
|
|
@@ -110,6 +111,7 @@ function isInteractiveTarget(target) {
|
|
|
110
111
|
return target instanceof Element && Boolean(target.closest('button, a, input, select, textarea, [contenteditable="true"], [data-dockable-panel-no-drag]'));
|
|
111
112
|
}
|
|
112
113
|
export const DockablePanel = forwardRef(function DockablePanel({ children, className, collapsed, collapsible = true, defaultCollapsed = false, defaultPosition = { x: 8, y: 8 }, headerActions, id, keyboardStep = 8, labels: labelOverrides, movable = true, onCollapsedChange, onFocusCapture, onPointerDown, onPositionChange, position, style, title, ...props }, forwardedRef) {
|
|
114
|
+
const { t } = useInkraLocale();
|
|
113
115
|
const workspace = useContext(WorkspaceContext);
|
|
114
116
|
if (!workspace)
|
|
115
117
|
throw new Error('DockablePanel must be rendered inside DockablePanelWorkspace.');
|
|
@@ -125,7 +127,7 @@ export const DockablePanel = forwardRef(function DockablePanel({ children, class
|
|
|
125
127
|
const resolvedCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
126
128
|
const positionRef = useRef(resolvedPosition);
|
|
127
129
|
positionRef.current = resolvedPosition;
|
|
128
|
-
const labels = { ...defaultLabels, ...labelOverrides };
|
|
130
|
+
const labels = { ...defaultLabels(t), ...labelOverrides };
|
|
129
131
|
const { activate, bounds, commitDrag, layer, register, snapThreshold, } = workspace;
|
|
130
132
|
const { attributes, isDragging, listeners, setActivatorNodeRef, setNodeRef, transform } = useDraggable({
|
|
131
133
|
id,
|
package/dist/drawer/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useInkraLocale } from '../i18n/index.js';
|
|
2
3
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
4
|
import { forwardRef, useId } from 'react';
|
|
4
5
|
import { __iconData as closeIcon } from 'lucide-react/dist/esm/icons/x.mjs';
|
|
@@ -40,7 +41,7 @@ export const DrawerContent = forwardRef(function DrawerContent({ children, class
|
|
|
40
41
|
const themedProps = usePortalThemeProps({ ...props, style });
|
|
41
42
|
const mergedStyle = { ...themedProps.style, '--inkra-drawer-size': drawerSize(size) };
|
|
42
43
|
const edgeOrientation = direction === 'left' || direction === 'right' ? 'vertical' : 'horizontal';
|
|
43
|
-
return (
|
|
44
|
+
return (_jsxs(DialogPrimitive.Content, { ...themedProps, className: classes('inkra-drawer__content', className), "data-direction": direction, ref: ref, style: mergedStyle, children: [_jsx(PencilSurface, { "aria-hidden": "true", className: "inkra-drawer__focus-ring", radius: 0, seed: `drawer-focus-${id}`, variant: "outline" }), _jsxs(PencilSurface, { className: "inkra-drawer__surface", radius: 0, seed: seed ?? `inkra-drawer-${id}`, variant: "full", children: [_jsx(PencilLine, { className: "inkra-drawer__edge", orientation: edgeOrientation, seed: `drawer-edge-${id}` }), _jsx("div", { className: "inkra-drawer__layout", children: children })] })] }));
|
|
44
45
|
});
|
|
45
46
|
export const DrawerTitle = forwardRef(function DrawerTitle({ children, className, seed, ...props }, ref) {
|
|
46
47
|
const id = useId();
|
|
@@ -53,7 +54,9 @@ export const DrawerDescription = forwardRef(function DrawerDescription({ childre
|
|
|
53
54
|
export const DrawerClose = forwardRef(function DrawerClose(props, ref) {
|
|
54
55
|
return _jsx(DialogPrimitive.Close, { ...props, ref: ref });
|
|
55
56
|
});
|
|
56
|
-
export const DrawerCloseButton = forwardRef(function DrawerCloseButton(
|
|
57
|
+
export const DrawerCloseButton = forwardRef(function DrawerCloseButton(inkraProps, ref) {
|
|
58
|
+
const { t } = useInkraLocale();
|
|
59
|
+
const { className, label = t('common.close'), onFocus, ...props } = inkraProps;
|
|
57
60
|
return _jsx(Tooltip.Provider, { children: _jsxs(Tooltip.Root, { children: [_jsx(Tooltip.Trigger, { asChild: true, children: _jsx(DialogPrimitive.Close, { asChild: true, children: _jsx(Button, { ...props, "aria-label": label, className: classes('inkra-drawer__close-button', className), ref: ref, size: "icon", type: "button", variant: "outline", onFocus: event => {
|
|
58
61
|
onFocus?.(event);
|
|
59
62
|
const dialog = event.currentTarget.closest('[role="dialog"]');
|
|
@@ -61,9 +64,12 @@ export const DrawerCloseButton = forwardRef(function DrawerCloseButton({ classNa
|
|
|
61
64
|
event.preventDefault();
|
|
62
65
|
}, children: _jsx(PencilIcon, { decorative: true, paths: closePaths, size: 30, sourceSize: 24 }) }) }) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Content, { align: "end", side: "bottom", children: label }) })] }) });
|
|
63
66
|
});
|
|
64
|
-
export const DrawerHeader = forwardRef(function DrawerHeader(
|
|
67
|
+
export const DrawerHeader = forwardRef(function DrawerHeader(inkraProps, ref) {
|
|
68
|
+
const { t } = useInkraLocale();
|
|
69
|
+
const { action, children, className, closeLabel = t('common.close'), divided = true, showClose = true, ...props } = inkraProps;
|
|
65
70
|
const id = useId();
|
|
66
|
-
|
|
71
|
+
const hasActions = Boolean(action || showClose);
|
|
72
|
+
return _jsxs("div", { ...props, className: classes('inkra-drawer__header', className), "data-actions": hasActions || undefined, "data-custom-action": action ? true : undefined, "data-divided": divided || undefined, ref: ref, children: [_jsx("div", { className: "inkra-drawer__heading", children: children }), hasActions ? _jsxs("div", { className: "inkra-drawer__header-actions", children: [action, showClose ? _jsx(DrawerCloseButton, { label: closeLabel }) : null] }) : null, divided ? _jsx(PencilLine, { className: "inkra-drawer__divider", seed: `drawer-header-${id}` }) : null] });
|
|
67
73
|
});
|
|
68
74
|
export const DrawerBody = forwardRef(function DrawerBody({ className, ...props }, ref) {
|
|
69
75
|
return _jsx("div", { ...props, className: classes('inkra-drawer__body', className), ref: ref });
|
|
@@ -72,7 +78,7 @@ export const DrawerFooter = forwardRef(function DrawerFooter({ align = 'end', ch
|
|
|
72
78
|
const id = useId();
|
|
73
79
|
return _jsxs("div", { ...props, className: classes('inkra-drawer__footer', className), "data-align": align, "data-divided": divided || undefined, ref: ref, children: [divided ? _jsx(PencilLine, { className: "inkra-drawer__divider", seed: `drawer-footer-${id}` }) : null, children] });
|
|
74
80
|
});
|
|
75
|
-
export const Drawer = Object.freeze({
|
|
81
|
+
export const Drawer = /* @__PURE__ */ Object.freeze({
|
|
76
82
|
Root: DrawerRoot,
|
|
77
83
|
Trigger: DrawerTrigger,
|
|
78
84
|
Portal: DrawerPortal,
|
package/dist/editable/index.d.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
import type { HTMLAttributes, KeyboardEvent, ReactNode } from 'react';
|
|
1
|
+
import type { FocusEvent, HTMLAttributes, KeyboardEvent, ReactNode } from 'react';
|
|
2
|
+
import type { SearchableSelectOption } from '../select/index.js';
|
|
2
3
|
import type { InputProps } from '../form/Input.js';
|
|
3
4
|
import type { TextareaProps } from '../form/Textarea.js';
|
|
4
5
|
export type EditableBlurBehavior = 'ignore' | 'save' | 'cancel';
|
|
6
|
+
export type EditableEditorType = 'input' | 'textarea' | 'select';
|
|
7
|
+
export interface EditableSelectProps {
|
|
8
|
+
items: readonly SearchableSelectOption[];
|
|
9
|
+
mode?: 'dropdown' | 'searchable' | 'stepper';
|
|
10
|
+
size?: 'sm' | 'md' | 'lg';
|
|
11
|
+
dir?: 'ltr' | 'rtl';
|
|
12
|
+
searchPlaceholder?: string;
|
|
13
|
+
emptyContent?: ReactNode;
|
|
14
|
+
clearable?: boolean;
|
|
15
|
+
clearLabel?: string;
|
|
16
|
+
openLabel?: string;
|
|
17
|
+
previousLabel?: string;
|
|
18
|
+
nextLabel?: string;
|
|
19
|
+
loop?: boolean;
|
|
20
|
+
}
|
|
5
21
|
export type EditableSaveResult = boolean | string | void | null | undefined;
|
|
6
22
|
export type EditableSaveHandler = (value: string, previousValue: string) => EditableSaveResult | Promise<EditableSaveResult>;
|
|
7
23
|
export type EditableValidateHandler = (value: string, previousValue: string) => EditableSaveResult | Promise<EditableSaveResult>;
|
|
@@ -9,12 +25,16 @@ export interface EditableEditorProps {
|
|
|
9
25
|
value: string;
|
|
10
26
|
onValueChange: (value: string) => void;
|
|
11
27
|
onKeyDown: (event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
|
12
|
-
onBlur: () => void;
|
|
28
|
+
onBlur: (event: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
|
13
29
|
disabled?: boolean;
|
|
30
|
+
readOnly?: boolean;
|
|
14
31
|
invalid?: boolean;
|
|
15
32
|
error?: string;
|
|
16
33
|
autoFocus?: boolean;
|
|
17
34
|
'aria-label'?: string;
|
|
35
|
+
'aria-describedby'?: string;
|
|
36
|
+
'aria-errormessage'?: string;
|
|
37
|
+
'aria-invalid'?: boolean;
|
|
18
38
|
}
|
|
19
39
|
export type EditableEditor = ReactNode | ((props: EditableEditorProps) => ReactNode);
|
|
20
40
|
export interface EditableProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onChange'> {
|
|
@@ -29,6 +49,8 @@ export interface EditableProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
|
|
|
29
49
|
onCancel?: (value: string) => void;
|
|
30
50
|
display?: ReactNode | ((value: string) => ReactNode);
|
|
31
51
|
editor?: EditableEditor;
|
|
52
|
+
editorType?: EditableEditorType;
|
|
53
|
+
selectProps?: EditableSelectProps;
|
|
32
54
|
multiline?: boolean;
|
|
33
55
|
inputProps?: Omit<InputProps, 'value' | 'defaultValue' | 'onValueChange' | 'onChange'>;
|
|
34
56
|
textareaProps?: Omit<TextareaProps, 'value' | 'defaultValue' | 'onChange'>;
|