@inkra/react 0.1.0 → 0.2.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 +104 -1
- package/dist/PencilIcon.js +14 -16
- package/dist/PencilLine.js +18 -12
- package/dist/PencilText.js +192 -59
- package/dist/alert-dialog/index.d.ts +64 -0
- package/dist/alert-dialog/index.js +109 -0
- package/dist/audio-player/index.d.ts +60 -0
- package/dist/audio-player/index.js +101 -0
- package/dist/carousel/index.d.ts +51 -0
- package/dist/carousel/index.js +290 -0
- package/dist/choice-grid/index.d.ts +38 -0
- package/dist/choice-grid/index.js +85 -0
- package/dist/color-picker/color.d.ts +15 -0
- package/dist/color-picker/color.js +55 -0
- package/dist/color-picker/index.d.ts +31 -0
- package/dist/color-picker/index.js +69 -0
- package/dist/command/index.d.ts +23 -0
- package/dist/command/index.js +39 -0
- package/dist/context-menu/index.d.ts +76 -0
- package/dist/context-menu/index.js +240 -0
- package/dist/data-table/index.d.ts +47 -0
- package/dist/data-table/index.js +91 -0
- package/dist/date-picker/index.d.ts +50 -0
- package/dist/date-picker/index.js +180 -0
- package/dist/display/ProgressRing.d.ts +6 -0
- package/dist/display/ProgressRing.js +146 -0
- package/dist/display/index.d.ts +27 -0
- package/dist/display/index.js +18 -2
- package/dist/dockable-panel/index.d.ts +41 -0
- package/dist/dockable-panel/index.js +253 -0
- package/dist/dockable-panel/model.d.ts +21 -0
- package/dist/dockable-panel/model.js +61 -0
- package/dist/drawer/index.d.ts +63 -0
- package/dist/drawer/index.js +86 -0
- package/dist/editable/index.d.ts +53 -0
- package/dist/editable/index.js +162 -0
- package/dist/empty/index.d.ts +21 -0
- package/dist/empty/index.js +23 -0
- package/dist/flip/index.d.ts +49 -0
- package/dist/flip/index.js +138 -0
- package/dist/form/EditablePencilText.js +157 -146
- package/dist/form/Textarea.d.ts +12 -0
- package/dist/form/Textarea.js +83 -4
- package/dist/form/index.d.ts +1 -1
- package/dist/heatmap/index.d.ts +59 -0
- package/dist/heatmap/index.js +248 -0
- package/dist/hotkey/index.d.ts +37 -0
- package/dist/hotkey/index.js +130 -0
- package/dist/hotkey/model.d.ts +27 -0
- package/dist/hotkey/model.js +97 -0
- package/dist/hover-card/index.d.ts +33 -0
- package/dist/hover-card/index.js +141 -0
- package/dist/image-cropper/index.d.ts +82 -0
- package/dist/image-cropper/index.js +261 -0
- package/dist/image-cropper/model.d.ts +42 -0
- package/dist/image-cropper/model.js +94 -0
- package/dist/image-viewer/index.d.ts +79 -0
- package/dist/image-viewer/index.js +220 -0
- package/dist/image-viewer/model.d.ts +9 -0
- package/dist/image-viewer/model.js +18 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +47 -0
- package/dist/internal/cache.d.ts +1 -1
- package/dist/internal/cache.js +1 -1
- package/dist/internal/renderTokens.d.ts +10 -0
- package/dist/internal/renderTokens.js +13 -0
- package/dist/internal/surfaceRenderer.d.ts +14 -6
- package/dist/internal/surfaceRenderer.js +15 -12
- package/dist/internal/textRenderer.d.ts +1 -0
- package/dist/internal/textRenderer.js +33 -7
- package/dist/internal/usePencilSurface.js +16 -8
- package/dist/internal/useSelectionDrawing.js +15 -11
- package/dist/journey-map/MapScale.js +3 -1
- package/dist/journey-map/index.js +3 -1
- package/dist/keypad/index.d.ts +23 -0
- package/dist/keypad/index.js +63 -0
- package/dist/keypad/model.d.ts +20 -0
- package/dist/keypad/model.js +33 -0
- package/dist/layout/index.d.ts +63 -0
- package/dist/layout/index.js +62 -0
- package/dist/log/index.d.ts +45 -0
- package/dist/log/index.js +159 -0
- package/dist/log/model.d.ts +16 -0
- package/dist/log/model.js +13 -0
- package/dist/node-graph/index.d.ts +123 -0
- package/dist/node-graph/index.js +304 -0
- package/dist/number-ticker/index.d.ts +29 -0
- package/dist/number-ticker/index.js +132 -0
- package/dist/number-ticker/model.d.ts +8 -0
- package/dist/number-ticker/model.js +34 -0
- package/dist/overflow-list/index.d.ts +39 -0
- package/dist/overflow-list/index.js +230 -0
- package/dist/overflow-list/layout.d.ts +19 -0
- package/dist/overflow-list/layout.js +44 -0
- package/dist/pagination/index.d.ts +23 -0
- package/dist/pagination/index.js +41 -0
- package/dist/pagination/model.d.ts +4 -0
- package/dist/pagination/model.js +42 -0
- package/dist/pan-zoom/index.d.ts +71 -0
- package/dist/pan-zoom/index.js +175 -0
- package/dist/radar/index.d.ts +54 -0
- package/dist/radar/index.js +220 -0
- package/dist/range-slider/index.d.ts +16 -0
- package/dist/range-slider/index.js +22 -0
- package/dist/rating/index.d.ts +28 -0
- package/dist/rating/index.js +127 -0
- package/dist/resizable/index.d.ts +32 -0
- package/dist/resizable/index.js +50 -0
- package/dist/scroll-area/index.d.ts +36 -0
- package/dist/scroll-area/index.js +44 -0
- package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
- package/dist/segmented-progress/SegmentedProgress.js +42 -0
- package/dist/slider/index.js +1 -1
- package/dist/slot/index.d.ts +24 -0
- package/dist/slot/index.js +29 -0
- package/dist/sortable/index.d.ts +50 -0
- package/dist/sortable/index.js +278 -0
- package/dist/sortable/model.d.ts +30 -0
- package/dist/sortable/model.js +64 -0
- package/dist/sparkline/index.d.ts +19 -0
- package/dist/sparkline/index.js +240 -0
- package/dist/sparkline/model.d.ts +16 -0
- package/dist/sparkline/model.js +28 -0
- package/dist/steps/index.d.ts +16 -0
- package/dist/steps/index.js +37 -0
- package/dist/styles.css +4850 -287
- package/dist/tag-input/index.d.ts +69 -0
- package/dist/tag-input/index.js +325 -0
- package/dist/tag-input/model.d.ts +14 -0
- package/dist/tag-input/model.js +38 -0
- package/dist/time-picker/index.d.ts +15 -0
- package/dist/time-picker/index.js +168 -0
- package/dist/timeline/index.d.ts +14 -0
- package/dist/timeline/index.js +16 -0
- package/dist/toast/index.d.ts +27 -0
- package/dist/toast/index.js +36 -0
- package/dist/toggle-group/index.d.ts +17 -0
- package/dist/toggle-group/index.js +26 -0
- package/dist/tokens.css +10 -9
- package/dist/transfer/index.d.ts +64 -0
- package/dist/transfer/index.js +174 -0
- package/dist/transfer/model.d.ts +12 -0
- package/dist/transfer/model.js +33 -0
- package/dist/tree/index.d.ts +39 -0
- package/dist/tree/index.js +36 -0
- package/dist/tree-select/index.d.ts +59 -0
- package/dist/tree-select/index.js +142 -0
- package/dist/tree-select/model.d.ts +31 -0
- package/dist/tree-select/model.js +135 -0
- package/dist/typewriter/index.d.ts +21 -0
- package/dist/typewriter/index.js +101 -0
- package/dist/typewriter/model.d.ts +7 -0
- package/dist/typewriter/model.js +45 -0
- package/dist/upload/index.d.ts +59 -0
- package/dist/upload/index.js +230 -0
- package/dist/virtual-list/index.d.ts +44 -0
- package/dist/virtual-list/index.js +128 -0
- package/dist/virtual-list/model.d.ts +7 -0
- package/dist/virtual-list/model.js +27 -0
- package/package.json +22 -3
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AriaAttributes, CSSProperties, FocusEventHandler } from 'react';
|
|
2
|
+
import type { DateRange } from 'react-day-picker';
|
|
3
|
+
import type { CalendarProps } from '../calendar/index.js';
|
|
4
|
+
export type DatePickerMode = 'single' | 'range';
|
|
5
|
+
export type DatePickerRange = DateRange;
|
|
6
|
+
export type DatePickerValue = Date | DatePickerRange | undefined;
|
|
7
|
+
export type DatePickerChangeReason = 'select' | 'clear';
|
|
8
|
+
export interface DatePickerChangeDetails {
|
|
9
|
+
reason: DatePickerChangeReason;
|
|
10
|
+
}
|
|
11
|
+
export type DatePickerCalendarProps = Omit<CalendarProps, 'appearance' | 'mode' | 'onSelect' | 'required' | 'selected'>;
|
|
12
|
+
export interface DatePickerProps {
|
|
13
|
+
'aria-describedby'?: string;
|
|
14
|
+
'aria-errormessage'?: string;
|
|
15
|
+
'aria-invalid'?: AriaAttributes['aria-invalid'];
|
|
16
|
+
'aria-label'?: string;
|
|
17
|
+
autoFocus?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
dir?: 'ltr' | 'rtl';
|
|
20
|
+
form?: string;
|
|
21
|
+
id?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
onBlur?: FocusEventHandler<HTMLButtonElement>;
|
|
24
|
+
onFocus?: FocusEventHandler<HTMLButtonElement>;
|
|
25
|
+
required?: boolean;
|
|
26
|
+
style?: CSSProperties;
|
|
27
|
+
mode?: DatePickerMode;
|
|
28
|
+
value?: DatePickerValue;
|
|
29
|
+
defaultValue?: DatePickerValue;
|
|
30
|
+
onValueChange?: (value: DatePickerValue, details: DatePickerChangeDetails) => void;
|
|
31
|
+
open?: boolean;
|
|
32
|
+
defaultOpen?: boolean;
|
|
33
|
+
onOpenChange?: (open: boolean) => void;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
readOnly?: boolean;
|
|
36
|
+
invalid?: boolean;
|
|
37
|
+
locale?: string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
rangeSeparator?: string;
|
|
40
|
+
endName?: string;
|
|
41
|
+
clearable?: boolean;
|
|
42
|
+
clearButtonLabel?: string;
|
|
43
|
+
calendarLabel?: string;
|
|
44
|
+
openButtonLabel?: string;
|
|
45
|
+
fullWidth?: boolean;
|
|
46
|
+
calendarProps?: DatePickerCalendarProps;
|
|
47
|
+
}
|
|
48
|
+
export declare function datePickerLocalValue(value: Date | undefined): string;
|
|
49
|
+
export declare const DatePicker: import("react").ForwardRefExoticComponent<DatePickerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
50
|
+
export type DatePickerAriaInvalid = AriaAttributes['aria-invalid'];
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { Button } from '../Button.js';
|
|
4
|
+
import { Calendar } from '../calendar/index.js';
|
|
5
|
+
import { mergeAriaIds, useFieldContext } from '../form/context.js';
|
|
6
|
+
import { useResolvedControlProps } from '../form/controlProps.js';
|
|
7
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
8
|
+
import { PencilLine } from '../PencilLine.js';
|
|
9
|
+
import { Popover } from '../overlay/Popover.js';
|
|
10
|
+
const calendarPaths = [
|
|
11
|
+
{ d: 'M8 2v4' },
|
|
12
|
+
{ d: 'M16 2v4' },
|
|
13
|
+
{ d: 'M3 10h18' },
|
|
14
|
+
{ d: 'M5 4h14a2 2 0 0 1 2 2v14H3V6a2 2 0 0 1 2-2Z' },
|
|
15
|
+
];
|
|
16
|
+
const clearPaths = [{ d: 'M18 6 6 18' }, { d: 'm6 6 12 12' }];
|
|
17
|
+
const classes = (...values) => values.filter(Boolean).join(' ');
|
|
18
|
+
function validDate(value) {
|
|
19
|
+
return value instanceof Date && Number.isFinite(value.getTime());
|
|
20
|
+
}
|
|
21
|
+
function isRange(value) {
|
|
22
|
+
return value != null && !validDate(value) && ('from' in value || 'to' in value);
|
|
23
|
+
}
|
|
24
|
+
export function datePickerLocalValue(value) {
|
|
25
|
+
if (!validDate(value))
|
|
26
|
+
return '';
|
|
27
|
+
return [
|
|
28
|
+
String(value.getFullYear()).padStart(4, '0'),
|
|
29
|
+
String(value.getMonth() + 1).padStart(2, '0'),
|
|
30
|
+
String(value.getDate()).padStart(2, '0'),
|
|
31
|
+
].join('-');
|
|
32
|
+
}
|
|
33
|
+
function initialForMode(mode, value) {
|
|
34
|
+
if (mode === 'single')
|
|
35
|
+
return validDate(value) ? value : undefined;
|
|
36
|
+
return isRange(value) ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
function defaultCopy(locale) {
|
|
39
|
+
const chinese = locale.toLowerCase().startsWith('zh');
|
|
40
|
+
return {
|
|
41
|
+
calendar: chinese ? '选择日期' : 'Choose date',
|
|
42
|
+
clear: chinese ? '清空日期' : 'Clear date',
|
|
43
|
+
next: chinese ? '下个月' : 'Next month',
|
|
44
|
+
open: chinese ? '打开日期选择器' : 'Open date picker',
|
|
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',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export const DatePicker = forwardRef(function DatePicker(allProps, forwardedRef) {
|
|
53
|
+
const valueControlled = Object.prototype.hasOwnProperty.call(allProps, 'value');
|
|
54
|
+
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 = 'en-US', mode = 'single', name, onBlur, onFocus, onOpenChange, onValueChange, open, openButtonLabel, placeholder, rangeSeparator = ' – ', readOnly = false, required = false, style, value, } = allProps;
|
|
56
|
+
const generatedId = useId();
|
|
57
|
+
const field = useFieldContext();
|
|
58
|
+
const triggerRef = useRef(null);
|
|
59
|
+
const contentRef = useRef(null);
|
|
60
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(() => initialForMode(mode, defaultValue));
|
|
61
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
62
|
+
const [constraintInvalid, setConstraintInvalid] = useState(false);
|
|
63
|
+
useImperativeHandle(forwardedRef, () => triggerRef.current);
|
|
64
|
+
const state = useResolvedControlProps({
|
|
65
|
+
id,
|
|
66
|
+
required,
|
|
67
|
+
disabled,
|
|
68
|
+
readOnly,
|
|
69
|
+
invalid,
|
|
70
|
+
'aria-describedby': ariaDescribedBy,
|
|
71
|
+
'aria-errormessage': ariaErrorMessage,
|
|
72
|
+
'aria-invalid': ariaInvalid,
|
|
73
|
+
});
|
|
74
|
+
const copy = defaultCopy(locale);
|
|
75
|
+
const currentValue = initialForMode(mode, valueControlled ? value : uncontrolledValue);
|
|
76
|
+
const currentOpen = openControlled ? Boolean(open) : uncontrolledOpen;
|
|
77
|
+
const singleValue = validDate(currentValue) ? currentValue : undefined;
|
|
78
|
+
const rangeValue = isRange(currentValue) ? currentValue : undefined;
|
|
79
|
+
const firstDate = singleValue ?? rangeValue?.from ?? rangeValue?.to;
|
|
80
|
+
const hasValue = Boolean(singleValue || rangeValue?.from || rangeValue?.to);
|
|
81
|
+
const formatter = useMemo(() => new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }), [locale]);
|
|
82
|
+
const fullFormatter = useMemo(() => new Intl.DateTimeFormat(locale, {
|
|
83
|
+
day: 'numeric', month: 'long', weekday: 'long', year: 'numeric',
|
|
84
|
+
}), [locale]);
|
|
85
|
+
const captionFormatter = useMemo(() => new Intl.DateTimeFormat(locale, {
|
|
86
|
+
month: 'long', year: 'numeric',
|
|
87
|
+
}), [locale]);
|
|
88
|
+
const weekdayFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { weekday: 'short' }), [locale]);
|
|
89
|
+
const displayValue = singleValue
|
|
90
|
+
? formatter.format(singleValue)
|
|
91
|
+
: rangeValue?.from
|
|
92
|
+
? `${formatter.format(rangeValue.from)}${rangeSeparator}${rangeValue.to ? formatter.format(rangeValue.to) : '…'}`
|
|
93
|
+
: placeholder ?? (mode === 'range' ? copy.rangePlaceholder : copy.placeholder);
|
|
94
|
+
const triggerId = state.id ?? `inkra-date-picker-${generatedId}`;
|
|
95
|
+
const accessibleValueId = field ? `${triggerId}-value` : undefined;
|
|
96
|
+
const updateOpen = (nextOpen) => {
|
|
97
|
+
if (!openControlled)
|
|
98
|
+
setUncontrolledOpen(nextOpen);
|
|
99
|
+
onOpenChange?.(nextOpen);
|
|
100
|
+
};
|
|
101
|
+
const commit = (nextValue, reason) => {
|
|
102
|
+
if (!valueControlled)
|
|
103
|
+
setUncontrolledValue(initialForMode(mode, nextValue));
|
|
104
|
+
setConstraintInvalid(false);
|
|
105
|
+
onValueChange?.(initialForMode(mode, nextValue), { reason });
|
|
106
|
+
};
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
const trigger = triggerRef.current;
|
|
109
|
+
if (!trigger || valueControlled)
|
|
110
|
+
return;
|
|
111
|
+
let timer;
|
|
112
|
+
const reset = (event) => {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
timer = setTimeout(() => {
|
|
115
|
+
if (!event.defaultPrevented) {
|
|
116
|
+
setUncontrolledValue(initialForMode(mode, defaultValue));
|
|
117
|
+
setUncontrolledOpen(false);
|
|
118
|
+
setConstraintInvalid(false);
|
|
119
|
+
}
|
|
120
|
+
}, 0);
|
|
121
|
+
};
|
|
122
|
+
trigger.form?.addEventListener('reset', reset);
|
|
123
|
+
return () => {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
trigger.form?.removeEventListener('reset', reset);
|
|
126
|
+
};
|
|
127
|
+
}, [defaultValue, form, mode, valueControlled]);
|
|
128
|
+
const { disabled: disabledDays, formatters, labels, defaultMonth, ...restCalendarProps } = calendarProps;
|
|
129
|
+
const localizedFormatters = {
|
|
130
|
+
formatCaption: date => captionFormatter.format(date),
|
|
131
|
+
formatDay: date => new Intl.NumberFormat(locale).format(date.getDate()),
|
|
132
|
+
formatWeekdayName: date => weekdayFormatter.format(date),
|
|
133
|
+
...formatters,
|
|
134
|
+
};
|
|
135
|
+
const localizedLabels = {
|
|
136
|
+
labelDayButton: (date, modifiers) => [
|
|
137
|
+
fullFormatter.format(date),
|
|
138
|
+
modifiers.today ? copy.today : '',
|
|
139
|
+
modifiers.selected ? copy.selected : '',
|
|
140
|
+
].filter(Boolean).join(', '),
|
|
141
|
+
labelNext: () => copy.next,
|
|
142
|
+
labelPrevious: () => copy.previous,
|
|
143
|
+
...labels,
|
|
144
|
+
};
|
|
145
|
+
const sharedCalendarProps = {
|
|
146
|
+
...restCalendarProps,
|
|
147
|
+
appearance: 'picker',
|
|
148
|
+
defaultMonth: defaultMonth ?? firstDate,
|
|
149
|
+
disabled: state.readOnly ? true : disabledDays,
|
|
150
|
+
formatters: localizedFormatters,
|
|
151
|
+
labels: localizedLabels,
|
|
152
|
+
};
|
|
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 }), variant: "outline", children: _jsx("span", { className: "inkra-date-picker__value", "data-placeholder": !hasValue || undefined, children: displayValue }) }) }), accessibleValueId ? _jsx("span", { className: "inkra-date-picker__accessible-value", id: accessibleValueId, children: displayValue }) : null, clearable ? (_jsx(Button, { "aria-hidden": !hasValue || undefined, "aria-label": clearButtonLabel ?? copy.clear, className: "inkra-date-picker__clear", disabled: !hasValue || state.disabled || state.readOnly, onClick: () => {
|
|
154
|
+
commit(undefined, 'clear');
|
|
155
|
+
updateOpen(false);
|
|
156
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
157
|
+
}, size: "icon", tabIndex: hasValue ? 0 : -1, variant: "ghost", children: _jsx(PencilIcon, { paths: clearPaths, sourceSize: 24, size: 22 }) })) : 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
|
+
event.preventDefault();
|
|
159
|
+
requestAnimationFrame(() => {
|
|
160
|
+
requestAnimationFrame(() => {
|
|
161
|
+
const content = contentRef.current;
|
|
162
|
+
const selected = content?.querySelector('.rdp-selected .rdp-day_button:not(:disabled)');
|
|
163
|
+
const today = content?.querySelector('.rdp-today .rdp-day_button:not(:disabled)');
|
|
164
|
+
const available = content?.querySelector('.rdp-day_button:not(:disabled)');
|
|
165
|
+
(selected ?? today ?? available ?? content)?.focus({ preventScroll: true });
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}, ref: contentRef, sideOffset: 8, children: [_jsx(PencilLine, { className: "inkra-date-picker__heading-line", seed: `date-picker-${generatedId}-line` }), mode === 'single' ? (_jsx(Calendar, { ...sharedCalendarProps, mode: "single", selected: singleValue, onSelect: nextValue => {
|
|
169
|
+
if (state.readOnly)
|
|
170
|
+
return;
|
|
171
|
+
commit(nextValue, 'select');
|
|
172
|
+
updateOpen(false);
|
|
173
|
+
} })) : (_jsx(Calendar, { ...sharedCalendarProps, excludeDisabled: true, mode: "range", selected: rangeValue, onSelect: nextValue => {
|
|
174
|
+
if (state.readOnly)
|
|
175
|
+
return;
|
|
176
|
+
commit(nextValue, 'select');
|
|
177
|
+
if (nextValue?.from && nextValue.to)
|
|
178
|
+
updateOpen(false);
|
|
179
|
+
} }))] }) })] }));
|
|
180
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr } from '../internal/cache.js';
|
|
4
|
+
import { observeCanvasRecovery } from '../internal/recovery.js';
|
|
5
|
+
import { seedToNumber } from '../internal/random.js';
|
|
6
|
+
import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
|
|
7
|
+
import { pencilStroke } from '../internal/stroke.js';
|
|
8
|
+
const ringCache = new BoundedCache(48, 8 * 1024 * 1024);
|
|
9
|
+
function smoothstep(value) {
|
|
10
|
+
return value * value * (3 - 2 * value);
|
|
11
|
+
}
|
|
12
|
+
function arcClip(diameter, radius, thickness, percent) {
|
|
13
|
+
const progress = Math.max(0, Math.min(100, percent ?? 28));
|
|
14
|
+
if (progress === 0)
|
|
15
|
+
return 'inset(50%)';
|
|
16
|
+
if (progress === 100)
|
|
17
|
+
return 'none';
|
|
18
|
+
const radians = progress / 100 * Math.PI * 2;
|
|
19
|
+
const arcLength = radius * radians;
|
|
20
|
+
const taperLength = Math.min(arcLength * 0.28, Math.max(7, thickness * 2.4));
|
|
21
|
+
const steps = Math.max(8, Math.ceil(progress * 0.9));
|
|
22
|
+
const outer = [];
|
|
23
|
+
const inner = [];
|
|
24
|
+
let start;
|
|
25
|
+
let end;
|
|
26
|
+
const point = (angle, distance) => `${((diameter / 2 + Math.cos(angle) * distance) / diameter * 100).toFixed(3)}% ${((diameter / 2 + Math.sin(angle) * distance) / diameter * 100).toFixed(3)}%`;
|
|
27
|
+
for (let index = 0; index <= steps; index += 1) {
|
|
28
|
+
const ratio = index / steps;
|
|
29
|
+
const distanceFromEnd = Math.min(ratio, 1 - ratio) * arcLength;
|
|
30
|
+
const taper = 0.12 + smoothstep(Math.min(1, distanceFromEnd / taperLength)) * 0.88;
|
|
31
|
+
// Leave enough room for the global stroke-width token while narrowing only
|
|
32
|
+
// the exposed ends. The pencil texture itself still determines the edge.
|
|
33
|
+
const halfWidth = thickness * 1.55 * taper;
|
|
34
|
+
const angle = ratio * radians - Math.PI / 2;
|
|
35
|
+
if (index === 0)
|
|
36
|
+
start = { angle, halfWidth };
|
|
37
|
+
if (index === steps)
|
|
38
|
+
end = { angle, halfWidth };
|
|
39
|
+
outer.push(point(angle, radius + halfWidth));
|
|
40
|
+
inner.push(point(angle, Math.max(0, radius - halfWidth)));
|
|
41
|
+
}
|
|
42
|
+
const endCap = Array.from({ length: 5 }, (_, index) => {
|
|
43
|
+
const capAngle = Math.PI / 2 - index / 4 * Math.PI;
|
|
44
|
+
const tangent = end.angle + Math.PI / 2;
|
|
45
|
+
const x = diameter / 2 + Math.cos(end.angle) * radius
|
|
46
|
+
+ Math.cos(end.angle) * Math.sin(capAngle) * end.halfWidth
|
|
47
|
+
+ Math.cos(tangent) * Math.cos(capAngle) * end.halfWidth;
|
|
48
|
+
const y = diameter / 2 + Math.sin(end.angle) * radius
|
|
49
|
+
+ Math.sin(end.angle) * Math.sin(capAngle) * end.halfWidth
|
|
50
|
+
+ Math.sin(tangent) * Math.cos(capAngle) * end.halfWidth;
|
|
51
|
+
return `${(x / diameter * 100).toFixed(3)}% ${(y / diameter * 100).toFixed(3)}%`;
|
|
52
|
+
});
|
|
53
|
+
const startCap = Array.from({ length: 5 }, (_, index) => {
|
|
54
|
+
const capAngle = -Math.PI / 2 + index / 4 * Math.PI;
|
|
55
|
+
const tangent = start.angle + Math.PI / 2;
|
|
56
|
+
const x = diameter / 2 + Math.cos(start.angle) * radius
|
|
57
|
+
+ Math.cos(start.angle) * Math.sin(capAngle) * start.halfWidth
|
|
58
|
+
- Math.cos(tangent) * Math.cos(capAngle) * start.halfWidth;
|
|
59
|
+
const y = diameter / 2 + Math.sin(start.angle) * radius
|
|
60
|
+
+ Math.sin(start.angle) * Math.sin(capAngle) * start.halfWidth
|
|
61
|
+
- Math.sin(tangent) * Math.cos(capAngle) * start.halfWidth;
|
|
62
|
+
return `${(x / diameter * 100).toFixed(3)}% ${(y / diameter * 100).toFixed(3)}%`;
|
|
63
|
+
});
|
|
64
|
+
return `polygon(${outer.join(',')},${endCap.join(',')},${inner.reverse().join(',')},${startCap.join(',')})`;
|
|
65
|
+
}
|
|
66
|
+
export function ProgressRing({ diameter, thickness, percent, seed }) {
|
|
67
|
+
const canvasRef = useRef(null);
|
|
68
|
+
const radius = diameter / 2 - thickness - 4;
|
|
69
|
+
const clipPath = useMemo(() => arcClip(diameter, radius, thickness, percent), [diameter, percent, radius, thickness]);
|
|
70
|
+
// Paint a stable full ring once; CSS reveals the current arc without repainting its texture.
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const canvas = canvasRef.current;
|
|
73
|
+
if (!canvas)
|
|
74
|
+
return;
|
|
75
|
+
let lastPaint = '';
|
|
76
|
+
const paint = () => {
|
|
77
|
+
try {
|
|
78
|
+
if (!CSS.supports('clip-path', 'polygon(0 0, 100% 0, 100% 100%)'))
|
|
79
|
+
return;
|
|
80
|
+
const width = canvas.clientWidth;
|
|
81
|
+
if (!width)
|
|
82
|
+
return;
|
|
83
|
+
const style = getComputedStyle(canvas);
|
|
84
|
+
const weight = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
|
|
85
|
+
const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, maxStrokeRoughness);
|
|
86
|
+
const dpr = effectiveDpr(2);
|
|
87
|
+
const key = JSON.stringify([PENCIL_RENDERER_VERSION, diameter, thickness, width, dpr, style.color, weight, roughness, seed]);
|
|
88
|
+
if (key === lastPaint && canvasIsUsable(canvas))
|
|
89
|
+
return;
|
|
90
|
+
let image = ringCache.get(key);
|
|
91
|
+
if (image && !canvasIsUsable(image)) {
|
|
92
|
+
ringCache.delete(key);
|
|
93
|
+
image = undefined;
|
|
94
|
+
}
|
|
95
|
+
if (!image) {
|
|
96
|
+
image = document.createElement('canvas');
|
|
97
|
+
image.width = image.height = Math.ceil(width * dpr);
|
|
98
|
+
const context = image.getContext('2d');
|
|
99
|
+
if (!context)
|
|
100
|
+
throw new Error('Canvas unavailable');
|
|
101
|
+
context.scale(image.width / diameter, image.height / diameter);
|
|
102
|
+
const points = Array.from({ length: 361 }, (_, index) => {
|
|
103
|
+
const angle = index * Math.PI / 180 - Math.PI / 2;
|
|
104
|
+
return [diameter / 2 + Math.cos(angle) * radius, diameter / 2 + Math.sin(angle) * radius];
|
|
105
|
+
});
|
|
106
|
+
pencilStroke(context, points, style.color, thickness * weight / 2.15, seedToNumber(seed), roughness * 0.55, 2, true, { variation: 0.86, breaks: 0.32, grain: 0.84, gain: 1.35, step: 0.55, edgeGrain: true });
|
|
107
|
+
ringCache.set(key, image, canvasByteCost(image));
|
|
108
|
+
}
|
|
109
|
+
const context = canvas.getContext('2d');
|
|
110
|
+
if (!context || context.isContextLost?.())
|
|
111
|
+
throw new Error('Canvas unavailable');
|
|
112
|
+
canvas.width = image.width;
|
|
113
|
+
canvas.height = image.height;
|
|
114
|
+
context.drawImage(image, 0, 0);
|
|
115
|
+
canvas.dataset.ready = 'true';
|
|
116
|
+
lastPaint = key;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
delete canvas.dataset.ready;
|
|
120
|
+
lastPaint = '';
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const lost = () => { delete canvas.dataset.ready; lastPaint = ''; };
|
|
124
|
+
canvas.addEventListener('contextlost', lost);
|
|
125
|
+
const observer = new MutationObserver(paint);
|
|
126
|
+
for (let node = canvas; node; node = node.parentElement) {
|
|
127
|
+
observer.observe(node, { attributes: true, attributeFilter: ['style', 'class', 'dir', 'data-theme', 'data-inkra-theme'] });
|
|
128
|
+
}
|
|
129
|
+
const resize = new ResizeObserver(paint);
|
|
130
|
+
resize.observe(canvas);
|
|
131
|
+
const scheme = matchMedia('(prefers-color-scheme: dark)');
|
|
132
|
+
scheme.addEventListener('change', paint);
|
|
133
|
+
window.addEventListener('resize', paint);
|
|
134
|
+
const stopRecovery = observeCanvasRecovery(paint);
|
|
135
|
+
paint();
|
|
136
|
+
return () => {
|
|
137
|
+
observer.disconnect();
|
|
138
|
+
resize.disconnect();
|
|
139
|
+
scheme.removeEventListener('change', paint);
|
|
140
|
+
window.removeEventListener('resize', paint);
|
|
141
|
+
canvas.removeEventListener('contextlost', lost);
|
|
142
|
+
stopRecovery();
|
|
143
|
+
};
|
|
144
|
+
}, [diameter, radius, seed, thickness]);
|
|
145
|
+
return _jsx("span", { className: "inkra-progress__ring", "aria-hidden": "true", children: _jsx("span", { className: "inkra-progress__ring-rotation", children: _jsxs("span", { className: "inkra-progress__ring-direction", children: [_jsx("canvas", { ref: canvasRef, className: "inkra-progress__ring-ink", style: { clipPath } }), _jsx("svg", { className: "inkra-progress__ring-fallback", viewBox: `0 0 ${diameter} ${diameter}`, focusable: "false", children: _jsx("circle", { cx: diameter / 2, cy: diameter / 2, r: radius, fill: "none", stroke: "currentColor", strokeWidth: thickness, pathLength: 100, strokeDasharray: "100", strokeDashoffset: 100 - (percent ?? 28), strokeLinecap: "round", transform: `rotate(-90 ${diameter / 2} ${diameter / 2})` }) })] }) }) });
|
|
146
|
+
}
|
package/dist/display/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { type ProgressPartialSegment, type ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
|
|
3
|
+
export type { ProgressPartialSegment, ProgressThreshold } from '../segmented-progress/SegmentedProgress.js';
|
|
2
4
|
export type BadgeVariant = 'neutral' | 'accent' | 'success' | 'warning' | 'danger' | 'info';
|
|
3
5
|
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
|
4
6
|
children: ReactNode;
|
|
@@ -22,11 +24,36 @@ export interface SkeletonProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chi
|
|
|
22
24
|
radius?: number;
|
|
23
25
|
}
|
|
24
26
|
export declare const Skeleton: import("react").ForwardRefExoticComponent<SkeletonProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
27
|
+
export interface ProgressRenderState {
|
|
28
|
+
value: number | null;
|
|
29
|
+
max: number;
|
|
30
|
+
percent: number | null;
|
|
31
|
+
}
|
|
32
|
+
export type ProgressContentPosition = 'center' | 'top' | 'bottom' | 'start' | 'end';
|
|
25
33
|
export interface ProgressProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
26
34
|
value: number | null;
|
|
27
35
|
max?: number;
|
|
28
36
|
label?: ReactNode;
|
|
29
37
|
seed?: string | number;
|
|
30
38
|
size?: 'sm' | 'md' | 'lg';
|
|
39
|
+
shape?: 'linear' | 'circular' | 'segmented';
|
|
40
|
+
/** Circular diameter in CSS pixels, clamped to 32-512. */
|
|
41
|
+
diameter?: number;
|
|
42
|
+
/** Circular stroke width, clamped to 1-diameter/8. */
|
|
43
|
+
thickness?: number;
|
|
44
|
+
/** Segmented cell count, clamped to 1-64. */
|
|
45
|
+
segmentCount?: number;
|
|
46
|
+
/** Segmented gap in CSS pixels, clamped to 0-24. */
|
|
47
|
+
segmentGap?: number;
|
|
48
|
+
/** How the active partial cell is represented. */
|
|
49
|
+
partialSegment?: ProgressPartialSegment;
|
|
50
|
+
/** Pigment ranges expressed in the same value units as max. */
|
|
51
|
+
thresholds?: readonly ProgressThreshold[];
|
|
52
|
+
/** Per-cell pigment overrides by zero-based segment index. */
|
|
53
|
+
segmentColors?: readonly string[];
|
|
54
|
+
/** Defaults to center for circular, end otherwise. Center maps to end for non-circular shapes. */
|
|
55
|
+
contentPosition?: ProgressContentPosition;
|
|
56
|
+
/** Non-interactive content; render state contains normalized values. */
|
|
57
|
+
children?: ReactNode | ((state: ProgressRenderState) => ReactNode);
|
|
31
58
|
}
|
|
32
59
|
export declare const Progress: import("react").ForwardRefExoticComponent<ProgressProps & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/display/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { forwardRef, useId } from 'react';
|
|
|
3
3
|
import { PencilLine } from '../PencilLine.js';
|
|
4
4
|
import { PencilSurface } from '../PencilSurface.js';
|
|
5
5
|
import { PencilText } from '../PencilText.js';
|
|
6
|
+
import { SegmentedProgress, } from '../segmented-progress/SegmentedProgress.js';
|
|
7
|
+
import { ProgressRing } from './ProgressRing.js';
|
|
6
8
|
const badgeTokens = {
|
|
7
9
|
neutral: { foreground: 'ink-muted', pigment: 'surface-muted' },
|
|
8
10
|
accent: { foreground: 'accent-strong', pigment: 'accent-soft' },
|
|
@@ -41,7 +43,7 @@ function normalizeValue(value, max) {
|
|
|
41
43
|
return 0;
|
|
42
44
|
return Math.min(max, Math.max(0, value));
|
|
43
45
|
}
|
|
44
|
-
export const Progress = forwardRef(function Progress({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, label, max, seed, style, value, size = 'md', ...props }, ref) {
|
|
46
|
+
export const Progress = forwardRef(function Progress({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, className, label, max, seed, style, value, size = 'md', shape = 'linear', diameter, thickness, segmentCount, segmentGap, partialSegment = 'fractional', thresholds, segmentColors, contentPosition, children, ...props }, ref) {
|
|
45
47
|
const generatedId = useId();
|
|
46
48
|
const labelId = `inkra-progress-${generatedId}-label`;
|
|
47
49
|
const resolvedMax = normalizeMax(max);
|
|
@@ -51,9 +53,23 @@ export const Progress = forwardRef(function Progress({ 'aria-label': ariaLabel,
|
|
|
51
53
|
? labelId
|
|
52
54
|
: ariaLabelledBy;
|
|
53
55
|
const resolvedSeed = seed ?? `inkra-progress-${generatedId}`;
|
|
56
|
+
const resolvedDiameter = typeof diameter === 'number' && Number.isFinite(diameter)
|
|
57
|
+
? Math.max(32, Math.min(512, diameter)) : { sm: 64, md: 96, lg: 128 }[size];
|
|
58
|
+
const resolvedThickness = Math.min(resolvedDiameter / 8, Math.max(1, typeof thickness === 'number' && Number.isFinite(thickness) ? thickness : { sm: 4, md: 6, lg: 8 }[size]));
|
|
59
|
+
const resolvedSegmentCount = typeof segmentCount === 'number' && Number.isFinite(segmentCount)
|
|
60
|
+
? Math.max(1, Math.min(64, Math.round(segmentCount))) : 10;
|
|
61
|
+
const resolvedSegmentGap = typeof segmentGap === 'number' && Number.isFinite(segmentGap)
|
|
62
|
+
? Math.max(0, Math.min(24, segmentGap)) : { sm: 2, md: 3, lg: 4 }[size];
|
|
63
|
+
const position = contentPosition ?? (shape === 'circular' ? 'center' : 'end');
|
|
64
|
+
const content = typeof children === 'function' ? children({ value: resolvedValue, max: resolvedMax, percent }) : children;
|
|
54
65
|
const resolvedStyle = {
|
|
55
66
|
...style,
|
|
56
67
|
'--inkra-progress-value': `${percent ?? 36}%`,
|
|
68
|
+
...(shape === 'circular' ? {
|
|
69
|
+
'--inkra-progress-angle': `${(percent ?? 28) * 3.6}deg`,
|
|
70
|
+
'--inkra-progress-diameter': `${resolvedDiameter}px`,
|
|
71
|
+
'--inkra-progress-content-inset': `${(resolvedThickness * 2 + 8) / resolvedDiameter * 100}%`,
|
|
72
|
+
} : {}),
|
|
57
73
|
};
|
|
58
|
-
return (_jsxs("div", { ...props, "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, "aria-valuemax": resolvedMax, "aria-valuemin": 0, "aria-valuenow": resolvedValue ?? undefined, className: ['inkra-progress', className].filter(Boolean).join(' '), "data-state": resolvedValue === null ? 'indeterminate' : 'determinate', "data-value": resolvedValue ?? undefined, "data-size": size, ref: ref, role: "progressbar", style: resolvedStyle, children: [label == null ? null : (typeof label === 'string' ? (_jsx(PencilText, { as: "span", className: "inkra-progress__label", id: labelId, seed: `${resolvedSeed}-label`, children: label })) : _jsx("span", { className: "inkra-progress__label", id: labelId, children: label })), _jsxs("span", { "aria-hidden": "true", className: "inkra-progress__track", children: [_jsx(PencilSurface, { as: "span", className: "inkra-progress__outline", radius: 6, seed: `${resolvedSeed}-outline`, variant: "outline" }), _jsx("span", { className: "inkra-progress__fill-clip", children: _jsx(PencilSurface, { as: "span", className: "inkra-progress__fill", radius: 6, seed: `${resolvedSeed}-fill`, variant: "selection" }) })] })] }));
|
|
74
|
+
return (_jsxs("div", { ...props, "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, "aria-valuemax": resolvedMax, "aria-valuemin": 0, "aria-valuenow": resolvedValue ?? undefined, className: ['inkra-progress', className].filter(Boolean).join(' '), "data-state": resolvedValue === null ? 'indeterminate' : 'determinate', "data-value": resolvedValue ?? undefined, "data-size": size, "data-shape": shape, ref: ref, role: "progressbar", style: resolvedStyle, children: [label == null ? null : (typeof label === 'string' ? (_jsx(PencilText, { as: "span", className: "inkra-progress__label", id: labelId, seed: `${resolvedSeed}-label`, children: label })) : _jsx("span", { className: "inkra-progress__label", id: labelId, children: label })), _jsxs("div", { className: "inkra-progress__body", "data-content-position": content == null ? undefined : shape !== 'circular' && position === 'center' ? 'end' : position, children: [shape === 'circular' ? (_jsx(ProgressRing, { diameter: resolvedDiameter, thickness: resolvedThickness, percent: percent, seed: `${resolvedSeed}-ring` })) : shape === 'segmented' ? (_jsx(SegmentedProgress, { count: resolvedSegmentCount, gap: resolvedSegmentGap, max: resolvedMax, partialSegment: partialSegment, percent: percent, seed: `${resolvedSeed}-segments`, segmentColors: segmentColors, thresholds: thresholds })) : (_jsxs("span", { "aria-hidden": "true", className: "inkra-progress__track", children: [_jsx(PencilSurface, { as: "span", className: "inkra-progress__outline", radius: 6, seed: `${resolvedSeed}-outline`, variant: "outline" }), _jsx("span", { className: "inkra-progress__fill-clip", children: _jsx(PencilSurface, { as: "span", className: "inkra-progress__fill", radius: 6, seed: `${resolvedSeed}-fill`, variant: "selection" }) })] })), content != null && _jsx("div", { className: "inkra-progress__content", children: typeof content === 'string' || typeof content === 'number' ? _jsx(PencilText, { children: String(content) }) : content })] })] }));
|
|
59
75
|
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import type { DockablePanelPosition, DockablePanelSnapEdge } from './model.js';
|
|
3
|
+
export type { DockablePanelBounds, DockablePanelPlacement, DockablePanelPosition, DockablePanelSize, DockablePanelSnapEdge, } from './model.js';
|
|
4
|
+
export type DockablePanelPositionReason = 'drag' | 'keyboard' | 'reset' | 'viewport';
|
|
5
|
+
export interface DockablePanelPositionChangeDetails {
|
|
6
|
+
reason: DockablePanelPositionReason;
|
|
7
|
+
snappedEdges: readonly DockablePanelSnapEdge[];
|
|
8
|
+
}
|
|
9
|
+
export interface DockablePanelLabels {
|
|
10
|
+
move?: string;
|
|
11
|
+
collapse?: string;
|
|
12
|
+
expand?: string;
|
|
13
|
+
reset?: string;
|
|
14
|
+
keyboardInstructions?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface DockablePanelWorkspaceProps extends HTMLAttributes<HTMLDivElement> {
|
|
17
|
+
activeId?: string | null;
|
|
18
|
+
defaultActiveId?: string | null;
|
|
19
|
+
onActiveChange?: (id: string) => void;
|
|
20
|
+
baseZIndex?: number;
|
|
21
|
+
boundaryPadding?: number;
|
|
22
|
+
snapThreshold?: number;
|
|
23
|
+
titleBarVisibility?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const DockablePanelWorkspace: import("react").ForwardRefExoticComponent<DockablePanelWorkspaceProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
26
|
+
export interface DockablePanelProps extends Omit<HTMLAttributes<HTMLElement>, 'onChange' | 'title'> {
|
|
27
|
+
id: string;
|
|
28
|
+
title: ReactNode;
|
|
29
|
+
position?: DockablePanelPosition;
|
|
30
|
+
defaultPosition?: DockablePanelPosition;
|
|
31
|
+
onPositionChange?: (position: DockablePanelPosition, details: DockablePanelPositionChangeDetails) => void;
|
|
32
|
+
collapsed?: boolean;
|
|
33
|
+
defaultCollapsed?: boolean;
|
|
34
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
35
|
+
collapsible?: boolean;
|
|
36
|
+
movable?: boolean;
|
|
37
|
+
keyboardStep?: number;
|
|
38
|
+
headerActions?: ReactNode;
|
|
39
|
+
labels?: DockablePanelLabels;
|
|
40
|
+
}
|
|
41
|
+
export declare const DockablePanel: import("react").ForwardRefExoticComponent<DockablePanelProps & import("react").RefAttributes<HTMLElement>>;
|