@inkra/react 0.2.0 → 0.4.3
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 +101 -7
- 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.d.ts +1 -1
- package/dist/alert-dialog/index.js +18 -6
- package/dist/audio-player/index.d.ts +2 -0
- package/dist/audio-player/index.js +126 -31
- package/dist/calendar/index.d.ts +4 -1
- package/dist/calendar/index.js +31 -12
- package/dist/carousel/index.js +13 -6
- package/dist/choice-grid/index.d.ts +6 -1
- package/dist/choice-grid/index.js +61 -33
- package/dist/color-picker/GradientEditor.d.ts +39 -0
- package/dist/color-picker/GradientEditor.js +63 -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 +134 -32
- package/dist/color-swatch/index.d.ts +10 -0
- package/dist/color-swatch/index.js +9 -0
- package/dist/command/index.d.ts +24 -2
- package/dist/command/index.js +29 -6
- package/dist/content/index.d.ts +5 -0
- package/dist/content/index.js +12 -3
- package/dist/context-menu/index.d.ts +2 -2
- package/dist/context-menu/index.js +4 -3
- package/dist/data-table/index.d.ts +7 -1
- package/dist/data-table/index.js +64 -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 +10 -9
- package/dist/day-agenda/index.d.ts +6 -1
- package/dist/day-agenda/index.js +46 -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 +5 -5
- package/dist/drawer/index.d.ts +1 -1
- package/dist/drawer/index.js +9 -6
- package/dist/dropdown-menu/index.d.ts +1 -1
- package/dist/dropdown-menu/index.js +3 -2
- package/dist/editable/index.d.ts +24 -2
- package/dist/editable/index.js +142 -37
- 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/EditablePencilText.js +34 -0
- package/dist/form/Field.js +11 -10
- package/dist/form/Input.js +1 -1
- package/dist/form/Textarea.d.ts +3 -1
- package/dist/form/Textarea.js +6 -4
- package/dist/hotkey/index.d.ts +1 -0
- package/dist/hotkey/index.js +6 -4
- package/dist/hover-card/index.js +1 -1
- package/dist/image-cropper/index.js +4 -2
- package/dist/image-viewer/VideoResource.d.ts +21 -0
- package/dist/image-viewer/VideoResource.js +194 -0
- package/dist/image-viewer/index.d.ts +14 -0
- package/dist/image-viewer/index.js +118 -65
- package/dist/image-viewer/lucideGeometry.d.ts +3 -0
- package/dist/image-viewer/lucideGeometry.js +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/input-number/index.d.ts +6 -0
- package/dist/input-number/index.js +131 -15
- package/dist/input-number/model.d.ts +13 -0
- package/dist/input-number/model.js +50 -0
- 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 +2 -0
- package/dist/internal/textRenderer.js +3 -3
- package/dist/internal/usePencilSurface.js +17 -1
- package/dist/internal/wash.js +4 -3
- package/dist/journey-map/terrain.js +10 -5
- package/dist/layout/index.js +1 -1
- package/dist/log/index.js +29 -22
- 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 +175 -0
- package/dist/node-graph/index.d.ts +2 -0
- package/dist/node-graph/index.js +49 -25
- package/dist/number-ticker/index.d.ts +11 -9
- package/dist/number-ticker/index.js +14 -11
- package/dist/number-ticker/model.d.ts +38 -5
- package/dist/number-ticker/model.js +139 -10
- package/dist/number-ticker/precision.d.ts +7 -0
- package/dist/number-ticker/precision.js +141 -0
- package/dist/overflow-list/index.js +5 -2
- package/dist/overlay/Overlay.d.ts +1 -1
- package/dist/overlay/Overlay.js +5 -3
- package/dist/overlay/Popover.d.ts +1 -1
- package/dist/overlay/Popover.js +6 -5
- package/dist/overlay/themeBridge.d.ts +12 -1
- package/dist/overlay/themeBridge.js +41 -3
- package/dist/pagination/index.d.ts +10 -1
- package/dist/pagination/index.js +6 -4
- package/dist/pan-zoom/index.d.ts +2 -0
- package/dist/pan-zoom/index.js +7 -6
- package/dist/pencil.d.ts +1 -0
- package/dist/pencil.js +4 -4
- package/dist/radar/index.d.ts +4 -0
- package/dist/radar/index.js +41 -21
- package/dist/rating/index.d.ts +4 -0
- package/dist/rating/index.js +128 -24
- package/dist/resizable/index.js +1 -1
- 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 +61 -6
- package/dist/select/Select.d.ts +1 -0
- package/dist/select/Select.js +3 -1
- package/dist/select/StepperSelect.d.ts +29 -0
- package/dist/select/StepperSelect.js +119 -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 +15 -6
- package/dist/sparkline/index.js +17 -2
- package/dist/sparkline/model.js +11 -4
- 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 +557 -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 +2241 -443
- package/dist/tabs/index.d.ts +9 -0
- package/dist/tabs/index.js +66 -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 +70 -26
- package/dist/time-picker/index.js +38 -20
- package/dist/toast/index.d.ts +19 -11
- package/dist/toast/index.js +48 -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.d.ts +1 -1
- package/dist/tooltip/Tooltip.js +6 -5
- package/dist/transfer/index.d.ts +5 -0
- package/dist/transfer/index.js +93 -29
- 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 +5 -5
- package/dist/tree-select/index.js +67 -30
- 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 +30 -16
- package/dist/virtual-list/index.js +10 -7
- package/package.json +416 -5
|
@@ -1,29 +1,158 @@
|
|
|
1
|
+
import { ExactDecimal, formatExactParts, tickerDecimal } from './precision.js';
|
|
2
|
+
export { subtractTickerValues, tickerValueKey } from './precision.js';
|
|
3
|
+
export const numberTickerUnits = {
|
|
4
|
+
en: ['K', 'M', 'B', 'T', 'Qa', 'Qi', 'Sx', 'Sp', 'Oc', 'No', 'Dc']
|
|
5
|
+
.map((label, index) => ({ label, factor: Number(`1e${(index + 1) * 3}`) })),
|
|
6
|
+
zh: [{ label: '千', factor: 1e3 }, ...['万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祇', '那由他', '不可思议', '无量大数']
|
|
7
|
+
.map((label, index) => ({ label, factor: Number(`1e${(index + 1) * 4}`) }))],
|
|
8
|
+
};
|
|
1
9
|
export function createNumberFormatter(locale = 'en-US', options) {
|
|
2
|
-
|
|
10
|
+
const formatter = new Intl.NumberFormat(locale, options);
|
|
11
|
+
if (options?.roundingMode && options.roundingMode !== 'halfExpand' && !('roundingMode' in formatter.resolvedOptions())) {
|
|
12
|
+
throw new RangeError('NumberTicker roundingMode requires Intl.NumberFormat rounding support');
|
|
13
|
+
}
|
|
14
|
+
return formatter;
|
|
15
|
+
}
|
|
16
|
+
export function createTickerFormatter(locale = 'en-US', options, compact = false, compactOptions = {}) {
|
|
17
|
+
if (!compact || options?.notation === 'scientific' || options?.notation === 'engineering') {
|
|
18
|
+
const native = createNumberFormatter(locale, options);
|
|
19
|
+
const formatToParts = (value) => formatExactParts(native, value);
|
|
20
|
+
return { formatToParts, formatDecimal: value => formatExactParts(native, value), format: value => formatToParts(value).map(part => part.value).join('') };
|
|
21
|
+
}
|
|
22
|
+
const precision = {
|
|
23
|
+
minimumFractionDigits: 0,
|
|
24
|
+
maximumFractionDigits: Math.max(1, options?.minimumFractionDigits ?? 0),
|
|
25
|
+
...options,
|
|
26
|
+
notation: 'standard',
|
|
27
|
+
};
|
|
28
|
+
const formatter = createNumberFormatter(locale, precision);
|
|
29
|
+
const language = compact === true
|
|
30
|
+
? (formatter.resolvedOptions().locale.startsWith('zh') ? 'zh' : 'en')
|
|
31
|
+
: compact;
|
|
32
|
+
const multiplier = tickerDecimal(compactOptions.promotionThreshold ?? 1);
|
|
33
|
+
if (!multiplier.gt(0) || !multiplier.isFinite())
|
|
34
|
+
throw new RangeError('promotionThreshold must be positive and finite');
|
|
35
|
+
const supplied = [...(compactOptions.units ?? numberTickerUnits[language])]
|
|
36
|
+
.map(unit => ({ ...unit, factor: tickerDecimal(unit.factor), threshold: unit.threshold === undefined ? undefined : tickerDecimal(unit.threshold) }))
|
|
37
|
+
.sort((a, b) => a.factor.cmp(b.factor));
|
|
38
|
+
const labels = new Set();
|
|
39
|
+
let previousFactor = tickerDecimal(1);
|
|
40
|
+
let previousThreshold = tickerDecimal(0);
|
|
41
|
+
for (const unit of supplied) {
|
|
42
|
+
const threshold = unit.threshold ?? unit.factor.times(multiplier);
|
|
43
|
+
if (!unit.label.trim() || labels.has(unit.label) || !unit.factor.isFinite() || unit.factor.lte(previousFactor)
|
|
44
|
+
|| !threshold.isFinite() || threshold.lte(previousThreshold)) {
|
|
45
|
+
throw new RangeError('Compact units need unique nonempty labels, increasing finite factors > 1 and positive increasing thresholds');
|
|
46
|
+
}
|
|
47
|
+
labels.add(unit.label);
|
|
48
|
+
previousFactor = unit.factor;
|
|
49
|
+
previousThreshold = threshold;
|
|
50
|
+
}
|
|
51
|
+
const units = [{ factor: tickerDecimal(1), label: '', threshold: undefined }, ...supplied];
|
|
52
|
+
const indexFor = (label, fallback) => {
|
|
53
|
+
if (label === undefined)
|
|
54
|
+
return fallback;
|
|
55
|
+
const index = units.findIndex(unit => unit.label === label);
|
|
56
|
+
if (index < 0)
|
|
57
|
+
throw new RangeError(`Unknown compact unit: ${label}`);
|
|
58
|
+
return index;
|
|
59
|
+
};
|
|
60
|
+
const minIndex = indexFor(compactOptions.minUnit, 0);
|
|
61
|
+
const maxIndex = indexFor(compactOptions.maxUnit, units.length - 1);
|
|
62
|
+
if (minIndex > maxIndex)
|
|
63
|
+
throw new RangeError('minUnit must not exceed maxUnit');
|
|
64
|
+
const thresholdFor = (index) => units[index].threshold ?? units[index].factor.times(multiplier);
|
|
65
|
+
const last = units[maxIndex];
|
|
66
|
+
const overflowThreshold = maxIndex + 1 < units.length ? thresholdFor(maxIndex + 1)
|
|
67
|
+
: thresholdFor(maxIndex).times(maxIndex > 0 ? last.factor.div(units[maxIndex - 1].factor) : 1000);
|
|
68
|
+
const overflow = compactOptions.overflow ?? (compactOptions.maxUnit !== undefined ? 'unit' : 'scientific');
|
|
69
|
+
const scientific = createNumberFormatter(locale, { ...precision, notation: 'scientific' });
|
|
70
|
+
const rounding = createNumberFormatter('en-US', {
|
|
71
|
+
...precision, style: 'decimal', numberingSystem: 'latn', useGrouping: false,
|
|
72
|
+
});
|
|
73
|
+
const percentScale = options?.style === 'percent' ? 100 : 1;
|
|
74
|
+
const formatDecimal = (exact) => {
|
|
75
|
+
if (!exact.isFinite())
|
|
76
|
+
return formatExactParts(formatter, exact);
|
|
77
|
+
let index = minIndex;
|
|
78
|
+
const reaches = (threshold) => {
|
|
79
|
+
if (compactOptions.promoteAfterRounding === false)
|
|
80
|
+
return exact.abs().times(percentScale).gte(threshold);
|
|
81
|
+
const scaled = exact.div(units[index].factor).times(percentScale);
|
|
82
|
+
const rounded = formatExactParts(rounding, scaled).map(part => part.value).join('');
|
|
83
|
+
return new ExactDecimal(rounded).abs().times(units[index].factor).gte(threshold);
|
|
84
|
+
};
|
|
85
|
+
while (index < maxIndex) {
|
|
86
|
+
if (!reaches(thresholdFor(index + 1)))
|
|
87
|
+
break;
|
|
88
|
+
index++;
|
|
89
|
+
}
|
|
90
|
+
if (index === maxIndex && overflow === 'scientific' && reaches(overflowThreshold))
|
|
91
|
+
return formatExactParts(scientific, exact);
|
|
92
|
+
const { factor, label } = units[index];
|
|
93
|
+
const parts = formatExactParts(formatter, exact.div(factor));
|
|
94
|
+
if (label) {
|
|
95
|
+
let insertion = parts.length;
|
|
96
|
+
while (insertion > 0 && !['integer', 'fraction'].includes(parts[insertion - 1].type))
|
|
97
|
+
insertion--;
|
|
98
|
+
const separator = compactOptions.separator ?? ' ';
|
|
99
|
+
parts.splice(insertion, 0, ...(separator ? [{ type: 'literal', value: separator }] : []), { type: 'compact', value: label });
|
|
100
|
+
}
|
|
101
|
+
return parts;
|
|
102
|
+
};
|
|
103
|
+
const formatToParts = (value) => formatDecimal(tickerDecimal(value));
|
|
104
|
+
return { formatToParts, formatDecimal, format: value => formatToParts(value).map(part => part.value).join('') };
|
|
3
105
|
}
|
|
4
106
|
export function formatTickerValue(formatter, value) {
|
|
5
|
-
return formatter.format(value);
|
|
107
|
+
return formatter instanceof Intl.NumberFormat ? formatExactParts(formatter, value).map(part => part.value).join('') : formatter.format(value);
|
|
6
108
|
}
|
|
7
109
|
export function formatTickerDelta(formatter, delta) {
|
|
8
|
-
const
|
|
9
|
-
|
|
110
|
+
const exact = tickerDecimal(delta);
|
|
111
|
+
return formatDecimalDelta(formatter, exact);
|
|
112
|
+
}
|
|
113
|
+
// Deltas are derived from validated inputs and can legitimately exceed input bounds.
|
|
114
|
+
export function formatDerivedTickerDelta(formatter, delta) {
|
|
115
|
+
return formatDecimalDelta(formatter, new ExactDecimal(String(delta)));
|
|
116
|
+
}
|
|
117
|
+
function formatDecimalDelta(formatter, exact) {
|
|
118
|
+
const sign = exact.gt(0) ? '+' : exact.lt(0) ? '\u2212' : '';
|
|
119
|
+
const magnitude = (formatter instanceof Intl.NumberFormat ? formatExactParts(formatter, exact) : formatter.formatDecimal(exact))
|
|
10
120
|
.filter(part => part.type !== 'plusSign' && part.type !== 'minusSign')
|
|
121
|
+
.filter(part => part.type !== 'literal' || !/^[()]$/.test(part.value))
|
|
11
122
|
.map(part => part.value)
|
|
12
123
|
.join('');
|
|
13
124
|
return `${sign}${magnitude}`;
|
|
14
125
|
}
|
|
15
126
|
export function interpolateTickerValue(from, to, progress) {
|
|
16
|
-
if (!
|
|
127
|
+
if (!tickerDecimal(from).isFinite() || !tickerDecimal(to).isFinite())
|
|
17
128
|
return to;
|
|
18
|
-
const boundedProgress = Math.min(1, Math.max(0, progress));
|
|
19
|
-
|
|
129
|
+
const boundedProgress = Number.isFinite(progress) ? Math.min(1, Math.max(0, progress)) : 1;
|
|
130
|
+
if (boundedProgress === 0)
|
|
131
|
+
return from;
|
|
132
|
+
if (boundedProgress === 1)
|
|
133
|
+
return to;
|
|
134
|
+
if (typeof from === 'number' && typeof to === 'number') {
|
|
135
|
+
const distance = to - from;
|
|
136
|
+
return Number.isFinite(distance) ? from + distance * boundedProgress
|
|
137
|
+
: from * (1 - boundedProgress) + to * boundedProgress;
|
|
138
|
+
}
|
|
139
|
+
const start = tickerDecimal(from);
|
|
140
|
+
const next = start.plus(tickerDecimal(to).minus(start).times(String(boundedProgress)));
|
|
141
|
+
return typeof from === 'bigint' && typeof to === 'bigint' ? BigInt(next.trunc().toFixed(0)) : next.toString();
|
|
20
142
|
}
|
|
21
143
|
export function tickerValueChanged(previous, next) {
|
|
22
|
-
|
|
144
|
+
if (Object.is(previous, next))
|
|
145
|
+
return false;
|
|
146
|
+
const before = tickerDecimal(previous);
|
|
147
|
+
const after = tickerDecimal(next);
|
|
148
|
+
return !before.eq(after) || (before.isZero() && before.isNegative() !== after.isNegative());
|
|
23
149
|
}
|
|
24
150
|
export function canAnimateTickerValue(from, to, duration) {
|
|
25
|
-
|
|
26
|
-
|
|
151
|
+
const start = tickerDecimal(from);
|
|
152
|
+
const end = tickerDecimal(to);
|
|
153
|
+
return start.isFinite() && end.isFinite()
|
|
154
|
+
&& typeof from === typeof to
|
|
155
|
+
&& (typeof to === 'number' || (Math.max(start.sd(), end.sd(), Math.abs(start.e - end.e)) <= 512))
|
|
27
156
|
&& Number.isFinite(duration)
|
|
28
157
|
&& duration > 0
|
|
29
158
|
&& tickerValueChanged(from, to);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Decimal } from 'decimal.js';
|
|
2
|
+
export type NumberTickerValue = number | bigint | string;
|
|
3
|
+
export declare const ExactDecimal: typeof Decimal;
|
|
4
|
+
export declare function tickerDecimal(value: NumberTickerValue): Decimal;
|
|
5
|
+
export declare function tickerValueKey(value: unknown): string;
|
|
6
|
+
export declare function formatExactParts(formatter: Intl.NumberFormat, value: NumberTickerValue | Decimal): Intl.NumberFormatPart[];
|
|
7
|
+
export declare function subtractTickerValues(next: NumberTickerValue, previous: NumberTickerValue): NumberTickerValue | null;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Decimal } from 'decimal.js';
|
|
2
|
+
// Isolated arithmetic context; never change the application's Decimal defaults.
|
|
3
|
+
// Covers the full span of the bounded input exponents and significant digits.
|
|
4
|
+
export const ExactDecimal = Decimal.clone({ precision: 30050, toExpNeg: -7, toExpPos: 21 });
|
|
5
|
+
const decimalPattern = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
6
|
+
const maxDigits = 10000;
|
|
7
|
+
export function tickerDecimal(value) {
|
|
8
|
+
const source = typeof value === 'number' && Object.is(value, -0) ? '-0' : String(value).trim();
|
|
9
|
+
if (typeof value !== 'number') {
|
|
10
|
+
const [coefficient, exponent = '0'] = source.split(/[eE]/);
|
|
11
|
+
if (!decimalPattern.test(source) || coefficient.replace(/\D/g, '').length > maxDigits
|
|
12
|
+
|| exponent.length > 7 || Math.abs(Number(exponent)) > maxDigits) {
|
|
13
|
+
throw new RangeError('NumberTicker requires a decimal value with at most 10000 digits and an exponent between -10000 and 10000');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const decimal = new ExactDecimal(source);
|
|
17
|
+
if (decimal.isFinite() && Math.abs(decimal.e) > maxDigits) {
|
|
18
|
+
throw new RangeError('NumberTicker decimal magnitude exceeds the supported exponent range');
|
|
19
|
+
}
|
|
20
|
+
return decimal;
|
|
21
|
+
}
|
|
22
|
+
export function tickerValueKey(value) {
|
|
23
|
+
return JSON.stringify(value, (_key, item) => typeof item === 'bigint' ? { $bigint: String(item) } : item);
|
|
24
|
+
}
|
|
25
|
+
const roundingModes = {
|
|
26
|
+
ceil: Decimal.ROUND_CEIL, floor: Decimal.ROUND_FLOOR,
|
|
27
|
+
expand: Decimal.ROUND_UP, trunc: Decimal.ROUND_DOWN,
|
|
28
|
+
halfCeil: Decimal.ROUND_HALF_CEIL, halfFloor: Decimal.ROUND_HALF_FLOOR,
|
|
29
|
+
halfExpand: Decimal.ROUND_HALF_UP, halfTrunc: Decimal.ROUND_HALF_DOWN,
|
|
30
|
+
halfEven: Decimal.ROUND_HALF_EVEN,
|
|
31
|
+
};
|
|
32
|
+
function roundedDecimal(value, options) {
|
|
33
|
+
const extra = options;
|
|
34
|
+
const mode = roundingModes[extra.roundingMode ?? 'halfExpand'];
|
|
35
|
+
const maxFraction = options.maximumFractionDigits ?? 0;
|
|
36
|
+
const increment = new ExactDecimal(extra.roundingIncrement ?? 1).times(new ExactDecimal(10).pow(-maxFraction));
|
|
37
|
+
const fraction = value.div(increment).toDecimalPlaces(0, mode).times(increment);
|
|
38
|
+
const significant = options.maximumSignificantDigits === undefined ? null : value.toSignificantDigits(options.maximumSignificantDigits, mode);
|
|
39
|
+
const significantPlace = value.e - (options.maximumSignificantDigits ?? 1) + 1;
|
|
40
|
+
const useSignificant = significant !== null && (extra.roundingPriority === 'morePrecision'
|
|
41
|
+
? significantPlace < -maxFraction : extra.roundingPriority === 'lessPrecision'
|
|
42
|
+
? significantPlace >= -maxFraction : true);
|
|
43
|
+
const rounded = useSignificant ? significant : fraction;
|
|
44
|
+
const decimals = extra.trailingZeroDisplay === 'stripIfInteger' && rounded.isInteger() ? 0
|
|
45
|
+
: useSignificant ? Math.max(0, (options.minimumSignificantDigits ?? 1) - rounded.e - 1)
|
|
46
|
+
: options.minimumFractionDigits ?? 0;
|
|
47
|
+
return { value: rounded, decimals };
|
|
48
|
+
}
|
|
49
|
+
let exactDecimalStringsSupported;
|
|
50
|
+
function supportsExactDecimalStrings() {
|
|
51
|
+
if (exactDecimalStringsSupported === undefined) {
|
|
52
|
+
const probe = new Intl.NumberFormat('en-US', { useGrouping: false });
|
|
53
|
+
exactDecimalStringsSupported = probe.format('9007199254740993') === '9007199254740993';
|
|
54
|
+
}
|
|
55
|
+
return exactDecimalStringsSupported;
|
|
56
|
+
}
|
|
57
|
+
// Intl accepts exact decimal strings in modern engines, but some engines still
|
|
58
|
+
// turn strings beyond Number.MAX_VALUE into Infinity. BigInt bypasses that path.
|
|
59
|
+
export function formatExactParts(formatter, value) {
|
|
60
|
+
if (typeof value === 'number')
|
|
61
|
+
return formatter.formatToParts(value);
|
|
62
|
+
const decimal = Decimal.isDecimal(value) ? value : tickerDecimal(value);
|
|
63
|
+
if (!decimal.isFinite())
|
|
64
|
+
return formatter.formatToParts(decimal.toNumber());
|
|
65
|
+
if (decimal.isZero())
|
|
66
|
+
return formatter.formatToParts(decimal.isNegative() ? -0 : 0);
|
|
67
|
+
if (decimal.isInteger())
|
|
68
|
+
return formatter.formatToParts(BigInt(decimal.toFixed(0)));
|
|
69
|
+
if (decimal.e < 308) {
|
|
70
|
+
const exactFormat = formatter.formatToParts;
|
|
71
|
+
if (!supportsExactDecimalStrings()) {
|
|
72
|
+
throw new RangeError('NumberTicker decimal strings require exact Intl.NumberFormat string support');
|
|
73
|
+
}
|
|
74
|
+
return exactFormat.call(formatter, decimal.toString());
|
|
75
|
+
}
|
|
76
|
+
const options = formatter.resolvedOptions();
|
|
77
|
+
// Directed rounding still observes tiny tails at huge magnitudes. Round the
|
|
78
|
+
// mantissa before converting to BigInt, then let Intl supply locale notation.
|
|
79
|
+
if (options.notation === 'scientific' || options.notation === 'engineering') {
|
|
80
|
+
const display = options.style === 'percent' ? decimal.times(100) : decimal;
|
|
81
|
+
const exponent = options.notation === 'engineering' ? Math.floor(display.e / 3) * 3 : display.e;
|
|
82
|
+
const factor = new ExactDecimal(10).pow(exponent);
|
|
83
|
+
const rounded = roundedDecimal(display.div(factor), options).value.times(factor);
|
|
84
|
+
const originalScale = options.style === 'percent' ? rounded.div(100) : rounded;
|
|
85
|
+
return formatter.formatToParts(BigInt(originalScale.toFixed(0)));
|
|
86
|
+
}
|
|
87
|
+
let scaled = options.style === 'percent' ? decimal.times(100) : decimal;
|
|
88
|
+
if (options.notation === 'compact') {
|
|
89
|
+
const probe = new Intl.NumberFormat(options.locale, { ...options, style: 'decimal', useGrouping: false });
|
|
90
|
+
const digits = probe.formatToParts(BigInt(new ExactDecimal(10).pow(scaled.e).toFixed(0)))
|
|
91
|
+
.filter(part => part.type === 'integer').map(part => Array.from(part.value).length).reduce((sum, length) => sum + length, 0);
|
|
92
|
+
scaled = scaled.div(new ExactDecimal(10).pow(scaled.e - digits + 1));
|
|
93
|
+
}
|
|
94
|
+
const rounded = roundedDecimal(scaled, options);
|
|
95
|
+
const [integer, fraction = ''] = rounded.value.abs().toFixed().split('.');
|
|
96
|
+
const fractionalDigits = fraction.padEnd(rounded.decimals, '0');
|
|
97
|
+
const plain = new Intl.NumberFormat(options.locale, {
|
|
98
|
+
numberingSystem: options.numberingSystem, minimumIntegerDigits: options.minimumIntegerDigits,
|
|
99
|
+
useGrouping: options.useGrouping, maximumFractionDigits: 0,
|
|
100
|
+
});
|
|
101
|
+
const numericParts = plain.formatToParts(BigInt(integer));
|
|
102
|
+
if (fractionalDigits) {
|
|
103
|
+
const digits = new Intl.NumberFormat(options.locale, { numberingSystem: options.numberingSystem, useGrouping: false });
|
|
104
|
+
const symbols = Array.from({ length: 10 }, (_, digit) => digits.format(digit));
|
|
105
|
+
numericParts.push({ type: 'decimal', value: digits.formatToParts(1.1).find(part => part.type === 'decimal').value }, { type: 'fraction', value: fractionalDigits.replace(/\d/g, digit => symbols[Number(digit)]) });
|
|
106
|
+
}
|
|
107
|
+
const pattern = formatter.formatToParts(BigInt(decimal.trunc().toFixed(0)));
|
|
108
|
+
// Long unit/currency names depend on the displayed fraction as well as the
|
|
109
|
+
// integer ending. Preserve plural operands without passing an overflowing
|
|
110
|
+
// decimal string to Intl (CLDR cardinal rules inspect at most six end digits).
|
|
111
|
+
const pluralParts = options.style === 'unit' || (options.style === 'currency' && options.currencyDisplay === 'name')
|
|
112
|
+
? formatExactParts(new Intl.NumberFormat(options.locale, {
|
|
113
|
+
numberingSystem: options.numberingSystem, style: options.style,
|
|
114
|
+
unit: options.unit, unitDisplay: options.unitDisplay,
|
|
115
|
+
currency: options.currency, currencyDisplay: options.currencyDisplay,
|
|
116
|
+
minimumFractionDigits: fractionalDigits.length, maximumFractionDigits: fractionalDigits.length,
|
|
117
|
+
}), `${BigInt(integer) > 1000000n ? 1000000n + BigInt(integer) % 1000000n : BigInt(integer)}${fractionalDigits ? `.${fractionalDigits}` : ''}`)
|
|
118
|
+
: undefined;
|
|
119
|
+
let inserted = false;
|
|
120
|
+
return pattern.flatMap(part => {
|
|
121
|
+
if (part.type === 'unit' || part.type === 'currency') {
|
|
122
|
+
return [pluralParts?.find(candidate => candidate.type === part.type) ?? part];
|
|
123
|
+
}
|
|
124
|
+
if (!['integer', 'group', 'decimal', 'fraction'].includes(part.type))
|
|
125
|
+
return [part];
|
|
126
|
+
if (inserted)
|
|
127
|
+
return [];
|
|
128
|
+
inserted = true;
|
|
129
|
+
return numericParts;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export function subtractTickerValues(next, previous) {
|
|
133
|
+
const to = tickerDecimal(next);
|
|
134
|
+
const from = tickerDecimal(previous);
|
|
135
|
+
if (!to.isFinite() || !from.isFinite())
|
|
136
|
+
return null;
|
|
137
|
+
if (typeof next === 'number' && typeof previous === 'number')
|
|
138
|
+
return next - previous;
|
|
139
|
+
const difference = to.minus(from);
|
|
140
|
+
return typeof next === 'bigint' && typeof previous === 'bigint' ? BigInt(difference.toFixed(0)) : difference.toString();
|
|
141
|
+
}
|
|
@@ -157,6 +157,9 @@ function OverflowListInner({ 'aria-label': ariaLabel, className, gap, getOverflo
|
|
|
157
157
|
widthCache.current.clear();
|
|
158
158
|
setLayout({ signature, visibleKeys: new Set(allKeys), needsOverflow: false, measuring: true });
|
|
159
159
|
}, [allKeys, layout.signature, signature]);
|
|
160
|
+
// Keep observer targets in sync when a resize reveals or hides items without
|
|
161
|
+
// changing whether an overflow trigger is needed. Memoized visibleEntries
|
|
162
|
+
// leaves the observer intact during unrelated parent updates.
|
|
160
163
|
useEffect(() => {
|
|
161
164
|
const root = rootRef.current;
|
|
162
165
|
if (!root)
|
|
@@ -177,7 +180,7 @@ function OverflowListInner({ 'aria-label': ariaLabel, className, gap, getOverflo
|
|
|
177
180
|
frameRef.current = null;
|
|
178
181
|
}
|
|
179
182
|
};
|
|
180
|
-
}, [effectiveLayout.measuring, effectiveLayout.needsOverflow, scheduleLayout, signature]);
|
|
183
|
+
}, [effectiveLayout.measuring, effectiveLayout.needsOverflow, scheduleLayout, signature, visibleEntries]);
|
|
181
184
|
useEffect(() => {
|
|
182
185
|
const fonts = typeof document === 'undefined' ? undefined : document.fonts;
|
|
183
186
|
if (!fonts?.addEventListener)
|
|
@@ -198,7 +201,7 @@ function OverflowListInner({ 'aria-label': ariaLabel, className, gap, getOverflo
|
|
|
198
201
|
useEffect(() => {
|
|
199
202
|
if (effectiveLayout.measuring)
|
|
200
203
|
return;
|
|
201
|
-
const changeKey =
|
|
204
|
+
const changeKey = JSON.stringify([signature, visibleEntries.map(entry => entry.key)]);
|
|
202
205
|
if (lastChangeRef.current === changeKey)
|
|
203
206
|
return;
|
|
204
207
|
lastChangeRef.current = changeKey;
|
|
@@ -7,7 +7,7 @@ export declare function OverlayRoot(props: OverlayRootProps): import("react").JS
|
|
|
7
7
|
export type OverlayTriggerProps = DialogPrimitive.DialogTriggerProps;
|
|
8
8
|
export declare const OverlayTrigger: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
9
9
|
export type OverlayPortalProps = DialogPrimitive.DialogPortalProps;
|
|
10
|
-
export declare function OverlayPortal({ children, ...props }: OverlayPortalProps):
|
|
10
|
+
export declare function OverlayPortal({ children, ...props }: OverlayPortalProps): ReactNode;
|
|
11
11
|
export type OverlayBackdropProps = DialogPrimitive.DialogOverlayProps;
|
|
12
12
|
export declare const OverlayBackdrop: import("react").ForwardRefExoticComponent<DialogPrimitive.DialogOverlayProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
13
13
|
export interface OverlayContentProps extends DialogPrimitive.DialogContentProps {
|
package/dist/overlay/Overlay.js
CHANGED
|
@@ -8,7 +8,7 @@ import { PencilLine } from '../PencilLine.js';
|
|
|
8
8
|
import { PencilSurface } from '../PencilSurface.js';
|
|
9
9
|
import { PencilText } from '../PencilText.js';
|
|
10
10
|
import { Tooltip } from '../tooltip/Tooltip.js';
|
|
11
|
-
import {
|
|
11
|
+
import { mapPortalChildren, PortalThemeProvider, usePortalThemeProps, usePortalTriggerRef, } from './themeBridge.js';
|
|
12
12
|
function classes(...values) {
|
|
13
13
|
return values.filter(Boolean).join(' ');
|
|
14
14
|
}
|
|
@@ -20,13 +20,15 @@ export const OverlayTrigger = forwardRef(function OverlayTrigger(props, forwarde
|
|
|
20
20
|
return _jsx(DialogPrimitive.Trigger, { ...props, ref: ref });
|
|
21
21
|
});
|
|
22
22
|
export function OverlayPortal({ children, ...props }) {
|
|
23
|
-
return (_jsx(DialogPrimitive.Portal, { ...props, children:
|
|
23
|
+
return mapPortalChildren(children, child => _jsx(DialogPrimitive.Portal, { ...props, children: child }));
|
|
24
24
|
}
|
|
25
25
|
export const OverlayBackdrop = forwardRef(function OverlayBackdrop({ className, ...props }, ref) {
|
|
26
|
+
props = usePortalThemeProps(props);
|
|
26
27
|
return (_jsx(DialogPrimitive.Overlay, { ...props, className: classes('inkra-overlay__backdrop', className), ref: ref }));
|
|
27
28
|
});
|
|
28
29
|
export const OverlayContent = forwardRef(function OverlayContent({ children, className, layout = 'plain', seed, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
29
30
|
const id = useId();
|
|
31
|
+
props = usePortalThemeProps(props);
|
|
30
32
|
return (_jsx(DialogPrimitive.Content, { ...props, className: classes('inkra-overlay__content', className), "data-layout": layout, ref: ref, children: _jsx(PencilSurface, { className: classes('inkra-overlay__surface', surfaceClassName), radius: surfaceRadius, seed: seed ?? `inkra-overlay-${id}`, variant: surfaceVariant, children: _jsx("div", { className: "inkra-overlay__content-body", children: children }) }) }));
|
|
31
33
|
});
|
|
32
34
|
export const OverlayTitle = forwardRef(function OverlayTitle({ children, className, seed, ...props }, ref) {
|
|
@@ -61,7 +63,7 @@ export const OverlayFooter = forwardRef(function OverlayFooter({ children, class
|
|
|
61
63
|
const id = useId();
|
|
62
64
|
return _jsxs("div", { ...props, ref: ref, className: classes('inkra-overlay__footer', className), "data-divided": divided || undefined, "data-align": align, children: [divided && _jsx(PencilLine, { className: "inkra-overlay__section-divider", seed: `overlay-footer-${id}` }), children] });
|
|
63
65
|
});
|
|
64
|
-
export const Overlay = Object.freeze({
|
|
66
|
+
export const Overlay = /* @__PURE__ */ Object.freeze({
|
|
65
67
|
Root: OverlayRoot,
|
|
66
68
|
Trigger: OverlayTrigger,
|
|
67
69
|
Portal: OverlayPortal,
|
|
@@ -8,7 +8,7 @@ export declare const PopoverAnchor: import("react").ForwardRefExoticComponent<Po
|
|
|
8
8
|
export type PopoverTriggerProps = PopoverPrimitive.PopoverTriggerProps;
|
|
9
9
|
export declare const PopoverTrigger: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
10
10
|
export type PopoverPortalProps = PopoverPrimitive.PopoverPortalProps;
|
|
11
|
-
export declare function PopoverPortal({ children, ...props }: PopoverPortalProps): import("react").
|
|
11
|
+
export declare function PopoverPortal({ children, ...props }: PopoverPortalProps): import("react").ReactNode;
|
|
12
12
|
export interface PopoverContentProps extends PopoverPrimitive.PopoverContentProps {
|
|
13
13
|
arrowProps?: PopoverArrowProps;
|
|
14
14
|
seed?: string | number;
|
package/dist/overlay/Popover.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
|
3
3
|
import { forwardRef, useId } from 'react';
|
|
4
4
|
import { PencilSurface } from '../PencilSurface.js';
|
|
5
5
|
import { PencilText } from '../PencilText.js';
|
|
6
|
-
import {
|
|
6
|
+
import { mapPortalChildren, PortalThemeProvider, usePortalThemeProps, usePortalTriggerRef, } from './themeBridge.js';
|
|
7
7
|
function classes(...values) {
|
|
8
8
|
return values.filter(Boolean).join(' ');
|
|
9
9
|
}
|
|
@@ -16,11 +16,12 @@ export const PopoverTrigger = forwardRef(function PopoverTrigger(props, forwarde
|
|
|
16
16
|
return _jsx(PopoverPrimitive.Trigger, { ...props, ref: ref });
|
|
17
17
|
});
|
|
18
18
|
export function PopoverPortal({ children, ...props }) {
|
|
19
|
-
return (_jsx(PopoverPrimitive.Portal, { ...props, children:
|
|
19
|
+
return mapPortalChildren(children, child => _jsx(PopoverPrimitive.Portal, { ...props, children: child }));
|
|
20
20
|
}
|
|
21
|
-
export const PopoverContent = forwardRef(function PopoverContent({ arrowProps, children, className, seed, showArrow = false, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
21
|
+
export const PopoverContent = forwardRef(function PopoverContent({ arrowProps, children, className, collisionPadding = 12, seed, showArrow = false, surfaceClassName, surfaceRadius = 8, surfaceVariant = 'paper', ...props }, ref) {
|
|
22
22
|
const id = useId();
|
|
23
|
-
|
|
23
|
+
props = usePortalThemeProps(props);
|
|
24
|
+
return (_jsxs(PopoverPrimitive.Content, { ...props, className: classes('inkra-popover__content', className), collisionPadding: collisionPadding, ref: ref, children: [_jsx(PencilSurface, { className: classes('inkra-popover__surface', surfaceClassName), radius: surfaceRadius, seed: seed ?? `inkra-popover-${id}`, variant: surfaceVariant, children: _jsx("div", { className: "inkra-popover__content-body", children: children }) }), showArrow ? (_jsx(PopoverPrimitive.Arrow, { ...arrowProps, className: classes('inkra-popover__arrow', arrowProps?.className) })) : null] }));
|
|
24
25
|
});
|
|
25
26
|
export const PopoverTitle = forwardRef(function PopoverTitle({ children, className, seed, ...props }, ref) {
|
|
26
27
|
const id = useId();
|
|
@@ -33,7 +34,7 @@ export const PopoverDescription = forwardRef(function PopoverDescription({ child
|
|
|
33
34
|
export const PopoverClose = forwardRef(function PopoverClose(props, ref) {
|
|
34
35
|
return _jsx(PopoverPrimitive.Close, { ...props, ref: ref });
|
|
35
36
|
});
|
|
36
|
-
export const Popover = Object.freeze({
|
|
37
|
+
export const Popover = /* @__PURE__ */ Object.freeze({
|
|
37
38
|
Root: PopoverRoot,
|
|
38
39
|
Anchor: PopoverAnchor,
|
|
39
40
|
Trigger: PopoverTrigger,
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import type { ForwardedRef, ReactNode } from 'react';
|
|
1
|
+
import type { CSSProperties, ForwardedRef, HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
type InkraStyle = CSSProperties & Record<`--inkra-${string}`, string>;
|
|
2
3
|
export declare function PortalThemeProvider({ children }: {
|
|
3
4
|
children: ReactNode;
|
|
4
5
|
}): import("react").JSX.Element;
|
|
5
6
|
export declare function usePortalTriggerRef<Element extends HTMLElement>(forwardedRef: ForwardedRef<Element>): (node: Element | null) => void;
|
|
7
|
+
export declare function mapPortalChildren(children: ReactNode, render: (child: ReactNode) => ReactNode): ReactNode;
|
|
8
|
+
export declare function usePortalThemeProps<Props extends HTMLAttributes<HTMLElement>>(props: Props): {
|
|
9
|
+
'data-inkra-theme': string | undefined;
|
|
10
|
+
'data-theme': string | undefined;
|
|
11
|
+
dir: "ltr" | "rtl";
|
|
12
|
+
lang: string | undefined;
|
|
13
|
+
} & Props & {
|
|
14
|
+
style: InkraStyle;
|
|
15
|
+
};
|
|
6
16
|
export declare const PortalThemeBridge: import("react").ForwardRefExoticComponent<{
|
|
7
17
|
children: ReactNode;
|
|
8
18
|
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
19
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, forwardRef, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
2
|
+
import { Children, Fragment, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
3
3
|
const emptySnapshot = {
|
|
4
4
|
direction: 'ltr',
|
|
5
5
|
style: {},
|
|
@@ -45,6 +45,14 @@ function readThemeSnapshot(node, forcedColors = isForcedColorsActive()) {
|
|
|
45
45
|
style,
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
function sameThemeSnapshot(left, right) {
|
|
49
|
+
if (left.theme !== right.theme || left.inkraTheme !== right.inkraTheme
|
|
50
|
+
|| left.direction !== right.direction || left.language !== right.language)
|
|
51
|
+
return false;
|
|
52
|
+
const entries = Object.entries(left.style);
|
|
53
|
+
return entries.length === Object.keys(right.style).length
|
|
54
|
+
&& entries.every(([property, value]) => right.style[property] === value);
|
|
55
|
+
}
|
|
48
56
|
export function PortalThemeProvider({ children }) {
|
|
49
57
|
const parentDepth = useContext(PortalLayerDepthContext);
|
|
50
58
|
const depth = parentDepth + 1;
|
|
@@ -52,12 +60,16 @@ export function PortalThemeProvider({ children }) {
|
|
|
52
60
|
const [snapshot, setSnapshot] = useState(emptySnapshot);
|
|
53
61
|
const captureTrigger = useCallback((node) => {
|
|
54
62
|
setTrigger(node);
|
|
55
|
-
|
|
63
|
+
const next = readThemeSnapshot(node);
|
|
64
|
+
setSnapshot(current => sameThemeSnapshot(current, next) ? current : next);
|
|
56
65
|
}, []);
|
|
57
66
|
useEffect(() => {
|
|
58
67
|
if (!trigger || typeof MutationObserver === 'undefined')
|
|
59
68
|
return undefined;
|
|
60
|
-
const refresh = () =>
|
|
69
|
+
const refresh = () => {
|
|
70
|
+
const next = readThemeSnapshot(trigger);
|
|
71
|
+
setSnapshot(current => sameThemeSnapshot(current, next) ? current : next);
|
|
72
|
+
};
|
|
61
73
|
const observer = new MutationObserver(refresh);
|
|
62
74
|
for (let target = trigger; target; target = target.parentElement) {
|
|
63
75
|
observer.observe(target, {
|
|
@@ -86,6 +98,32 @@ export function usePortalTriggerRef(forwardedRef) {
|
|
|
86
98
|
captureTrigger?.(node);
|
|
87
99
|
}, [captureTrigger, forwardedRef]);
|
|
88
100
|
}
|
|
101
|
+
// Presence must receive each animated DOM node, not a display:contents wrapper.
|
|
102
|
+
export function mapPortalChildren(children, render) {
|
|
103
|
+
return Children.map(children, child => {
|
|
104
|
+
if (isValidElement(child) && child.type === Fragment) {
|
|
105
|
+
return mapPortalChildren(child.props.children, render);
|
|
106
|
+
}
|
|
107
|
+
return child == null ? null : render(child);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export function usePortalThemeProps(props) {
|
|
111
|
+
const context = useContext(PortalThemeContext);
|
|
112
|
+
const depth = context?.depth ?? 0;
|
|
113
|
+
const snapshot = context?.snapshot ?? emptySnapshot;
|
|
114
|
+
return {
|
|
115
|
+
'data-inkra-theme': snapshot.inkraTheme,
|
|
116
|
+
'data-theme': snapshot.theme,
|
|
117
|
+
dir: snapshot.direction,
|
|
118
|
+
lang: snapshot.language,
|
|
119
|
+
...props,
|
|
120
|
+
style: {
|
|
121
|
+
...snapshot.style,
|
|
122
|
+
'--inkra-floating-layer-offset': `${depth * 20}`,
|
|
123
|
+
...props.style,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
89
127
|
export const PortalThemeBridge = forwardRef(function PortalThemeBridge({ children }, ref) {
|
|
90
128
|
const context = useContext(PortalThemeContext);
|
|
91
129
|
const depth = context?.depth ?? 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HTMLAttributes } from 'react';
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
export interface PaginationLabels {
|
|
3
3
|
nav: string;
|
|
4
4
|
first: string;
|
|
@@ -16,6 +16,15 @@ export interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, 'chil
|
|
|
16
16
|
siblingCount?: number;
|
|
17
17
|
boundaryCount?: number;
|
|
18
18
|
showFirstLast?: boolean;
|
|
19
|
+
showPreviousNext?: boolean;
|
|
20
|
+
hideOnSinglePage?: boolean;
|
|
21
|
+
simple?: boolean;
|
|
22
|
+
/** Non-interactive content inside each numbered page button. */
|
|
23
|
+
renderPage?: (page: number, state: {
|
|
24
|
+
isCurrent: boolean;
|
|
25
|
+
}) => ReactNode;
|
|
26
|
+
/** Non-interactive current/total content in simple mode. */
|
|
27
|
+
renderSummary?: (page: number, totalPages: number) => ReactNode;
|
|
19
28
|
disabled?: boolean;
|
|
20
29
|
size?: 'sm' | 'md' | 'lg';
|
|
21
30
|
labels?: Partial<PaginationLabels>;
|
package/dist/pagination/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const defaultLabels = {
|
|
|
9
9
|
nav: 'Pagination', first: 'First page', previous: 'Previous page', next: 'Next page', last: 'Last page',
|
|
10
10
|
page: page => `Page ${page}`, ellipsis: 'More pages',
|
|
11
11
|
};
|
|
12
|
-
export const Pagination = forwardRef(function Pagination({ totalPages, page, defaultPage = 1, onPageChange, siblingCount = 1, boundaryCount = 1, showFirstLast = false, disabled = false, size = 'md', labels, className, ...props }, ref) {
|
|
12
|
+
export const Pagination = forwardRef(function Pagination({ totalPages, page, defaultPage = 1, onPageChange, siblingCount = 1, boundaryCount = 1, showFirstLast = false, showPreviousNext = true, hideOnSinglePage = false, simple = false, renderPage, renderSummary, disabled = false, size = 'md', labels, className, ...props }, ref) {
|
|
13
13
|
const total = normalizeTotal(totalPages);
|
|
14
14
|
const [internalPage, setInternalPage] = useState(() => clampPage(defaultPage, total));
|
|
15
15
|
const current = clampPage(page ?? internalPage, total);
|
|
@@ -35,7 +35,9 @@ export const Pagination = forwardRef(function Pagination({ totalPages, page, def
|
|
|
35
35
|
paths.push({ d: 'M19 6v12' });
|
|
36
36
|
return _jsx("li", { children: _jsx(Button, { variant: "ghost", size: size === 'md' ? 'icon' : `icon-${size}`, "aria-label": text[kind], title: text[kind], disabled: disabled || unavailable, onClick: () => select(target), children: _jsx(PencilIcon, { className: "inkra-pagination__arrow", paths: paths, size: 20, sourceSize: 24 }) }) }, kind);
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (hideOnSinglePage && total <= 1)
|
|
39
|
+
return null;
|
|
40
|
+
return _jsx("nav", { "aria-label": text.nav, ...props, ref: ref, className: ['inkra-pagination', className].filter(Boolean).join(' '), "data-empty": !total || undefined, "data-simple": simple || undefined, children: _jsxs("ul", { className: "inkra-pagination__list", children: [showFirstLast && arrow('first', 1, current <= 1), showPreviousNext && arrow('previous', current - 1, current <= 1), simple ? _jsx("li", { className: "inkra-pagination__summary", "aria-live": "polite", "aria-atomic": "true", children: renderSummary ? renderSummary(current, total) : _jsx(PencilText, { children: `${current} / ${total}` }) }) : getPaginationEntries(total, current, siblingCount, boundaryCount).map(entry => typeof entry === 'number'
|
|
41
|
+
? _jsx("li", { className: "inkra-pagination__page-item", children: _jsxs(Button, { size: size, variant: "ghost", className: "inkra-pagination__page", "aria-label": text.page(entry), "aria-current": entry === current ? 'page' : undefined, disabled: disabled, onClick: () => select(entry), children: [entry === current && _jsx(PencilSurface, { as: "span", "aria-hidden": "true", variant: "selection", className: "inkra-pagination__selection", seed: `page-${entry}` }), renderPage ? renderPage(entry, { isCurrent: entry === current }) : _jsx(PencilText, { children: String(entry) })] }) }, entry)
|
|
42
|
+
: _jsxs("li", { className: "inkra-pagination__ellipsis", children: [_jsx("span", { className: "inkra-pagination__sr-only", children: text.ellipsis }), _jsx(PencilText, { "aria-hidden": "true", children: "..." })] }, entry)), showPreviousNext && arrow('next', current + 1, !total || current >= total), showFirstLast && arrow('last', total, !total || current >= total)] }) });
|
|
41
43
|
});
|
package/dist/pan-zoom/index.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface PanZoomProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chil
|
|
|
48
48
|
minScale?: number;
|
|
49
49
|
maxScale?: number;
|
|
50
50
|
wheel?: PanZoomWheelMode;
|
|
51
|
+
/** Scale increment per wheel delta unit. Must be finite and positive. */
|
|
52
|
+
wheelStep?: number;
|
|
51
53
|
pinch?: boolean;
|
|
52
54
|
pan?: boolean;
|
|
53
55
|
keyboard?: boolean;
|