@inkra/react 0.1.0 → 0.3.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 +186 -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 +111 -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 +45 -0
- package/dist/command/index.js +39 -0
- package/dist/context-menu/index.d.ts +76 -0
- package/dist/context-menu/index.js +241 -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 +88 -0
- package/dist/dropdown-menu/index.d.ts +1 -1
- package/dist/dropdown-menu/index.js +3 -2
- 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 +188 -143
- package/dist/form/Input.js +1 -1
- 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/input-number/index.d.ts +6 -0
- package/dist/input-number/index.js +126 -15
- package/dist/input-number/model.d.ts +13 -0
- package/dist/input-number/model.js +50 -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 +31 -0
- package/dist/number-ticker/index.js +135 -0
- package/dist/number-ticker/model.d.ts +41 -0
- package/dist/number-ticker/model.js +163 -0
- package/dist/number-ticker/precision.d.ts +7 -0
- package/dist/number-ticker/precision.js +134 -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/overlay/Overlay.d.ts +1 -1
- package/dist/overlay/Overlay.js +4 -2
- package/dist/overlay/Popover.d.ts +1 -1
- package/dist/overlay/Popover.js +3 -2
- package/dist/overlay/themeBridge.d.ts +12 -1
- package/dist/overlay/themeBridge.js +27 -1
- 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 +56 -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 +283 -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 +4957 -269
- 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/tooltip/Tooltip.d.ts +1 -1
- package/dist/tooltip/Tooltip.js +3 -2
- 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 +129 -0
- package/dist/virtual-list/model.d.ts +7 -0
- package/dist/virtual-list/model.js +27 -0
- package/package.json +23 -3
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { EdgeLabelRenderer, Handle, Panel, Position, ReactFlow, ReactFlowProvider, addEdge as addFlowEdge, applyEdgeChanges as applyFlowEdgeChanges, applyNodeChanges as applyFlowNodeChanges, getBezierPath, useReactFlow, useViewport, } from '@xyflow/react';
|
|
3
|
+
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
5
|
+
import { PencilSurface } from '../PencilSurface.js';
|
|
6
|
+
import { PencilText } from '../PencilText.js';
|
|
7
|
+
import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost, canvasIsUsable, effectiveDpr, } from '../internal/cache.js';
|
|
8
|
+
import { observeCanvasRecovery } from '../internal/recovery.js';
|
|
9
|
+
import { seedToNumber } from '../internal/random.js';
|
|
10
|
+
import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
|
|
11
|
+
import { pencilStroke } from '../internal/stroke.js';
|
|
12
|
+
const positionMap = {
|
|
13
|
+
top: Position.Top,
|
|
14
|
+
right: Position.Right,
|
|
15
|
+
bottom: Position.Bottom,
|
|
16
|
+
left: Position.Left,
|
|
17
|
+
};
|
|
18
|
+
const zoomInPaths = [
|
|
19
|
+
{ d: 'M 12 5 L 12 19' },
|
|
20
|
+
{ d: 'M 5 12 L 19 12' },
|
|
21
|
+
];
|
|
22
|
+
const zoomOutPaths = [{ d: 'M 5 12 L 19 12' }];
|
|
23
|
+
const fitViewPaths = [
|
|
24
|
+
{ d: 'M 9 5 L 5 5 L 5 9' },
|
|
25
|
+
{ d: 'M 15 5 L 19 5 L 19 9' },
|
|
26
|
+
{ d: 'M 5 15 L 5 19 L 9 19' },
|
|
27
|
+
{ d: 'M 19 15 L 19 19 L 15 19' },
|
|
28
|
+
];
|
|
29
|
+
const edgeCanvasCache = new BoundedCache(96, 24 * 1024 * 1024);
|
|
30
|
+
const edgeCanvasPadding = 12;
|
|
31
|
+
const edgeCanvasMaxDimension = 1024;
|
|
32
|
+
const edgeCanvasMaxPixels = 512 * 512;
|
|
33
|
+
const edgeCanvasMinScale = 0.5;
|
|
34
|
+
const edgeMaxSampleSegments = 1024;
|
|
35
|
+
function readableLabel(value, fallback) {
|
|
36
|
+
return typeof value === 'string' || typeof value === 'number' ? String(value) : fallback;
|
|
37
|
+
}
|
|
38
|
+
const NodeGraphPort = forwardRef(function NodeGraphPort({ id, type, position = type === 'source' ? 'right' : 'left', label, connectable = true, className, style, ...props }, ref) {
|
|
39
|
+
const { zoom } = useViewport();
|
|
40
|
+
const portStyle = {
|
|
41
|
+
...style,
|
|
42
|
+
'--inkra-node-graph-port-scale': 1 / Math.max(zoom, 0.01),
|
|
43
|
+
};
|
|
44
|
+
return _jsx(Handle, { ...props, ref: ref, id: id, type: type, position: positionMap[position], isConnectable: connectable, "aria-label": label, className: ['inkra-node-graph__port', className].filter(Boolean).join(' '), "data-port-position": position, style: portStyle, children: _jsxs("span", { "aria-hidden": "true", className: "inkra-node-graph__port-visual", children: [_jsx(PencilSurface, { as: "span", className: "inkra-node-graph__port-feedback", radius: 8, seed: `node-graph-port-${type}-${id}-feedback`, variant: "quiet" }), _jsx(PencilSurface, { as: "span", className: "inkra-node-graph__port-mark", radius: 8, seed: `node-graph-port-${type}-${id}`, variant: "outline" })] }) });
|
|
45
|
+
});
|
|
46
|
+
const NodeGraphNode = forwardRef(function NodeGraphNode({ selected = false, dragging = false, readOnly = false, label, description, ports = [], children, className, seed = 'node-graph-node', ...props }, ref) {
|
|
47
|
+
return _jsxs("div", { ...props, ref: ref, className: ['inkra-node-graph__node-shell', className].filter(Boolean).join(' '), "data-selected": selected || undefined, "data-dragging": dragging || undefined, "data-read-only": readOnly || undefined, children: [_jsxs(PencilSurface, { className: "inkra-node-graph__node-paper", variant: "paper", radius: 8, seed: seed, children: [selected && _jsx(PencilSurface, { "aria-hidden": "true", className: "inkra-node-graph__node-selection", variant: "selection", seed: `${seed}-selection` }), _jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-node-graph__node-focus", radius: 8, seed: `${seed}-focus`, variant: "outline" }), _jsxs("div", { className: "inkra-node-graph__node-content", children: [label != null && _jsx("div", { className: "inkra-node-graph__node-label", children: typeof label === 'string' ? _jsx(PencilText, { children: label }) : label }), description != null && _jsx("div", { className: "inkra-node-graph__node-description", children: description }), children != null && _jsx("div", { className: "inkra-node-graph__node-body", children: children })] })] }), ports.map(port => _jsx(NodeGraphPort, { ...port, connectable: !readOnly && port.connectable !== false }, `${port.type}-${port.id}`))] });
|
|
48
|
+
});
|
|
49
|
+
const DefaultNodeRenderer = memo(function DefaultNodeRenderer({ id, data, selected, dragging }) {
|
|
50
|
+
const originalData = data.__inkraData ?? data;
|
|
51
|
+
const custom = data.__inkraRenderNode?.({
|
|
52
|
+
id,
|
|
53
|
+
data: originalData,
|
|
54
|
+
selected,
|
|
55
|
+
dragging,
|
|
56
|
+
readOnly: Boolean(data.__inkraReadOnly),
|
|
57
|
+
});
|
|
58
|
+
if (custom != null)
|
|
59
|
+
return custom;
|
|
60
|
+
return _jsx(NodeGraphNode, { label: originalData.label, description: originalData.description, ports: originalData.ports, selected: selected, dragging: dragging, readOnly: Boolean(data.__inkraReadOnly), seed: `node-graph-${id}`, children: originalData.content });
|
|
61
|
+
});
|
|
62
|
+
function boundsEqual(previous, next) {
|
|
63
|
+
return previous != null
|
|
64
|
+
&& Math.abs(previous.x - next.x) < 0.01
|
|
65
|
+
&& Math.abs(previous.y - next.y) < 0.01
|
|
66
|
+
&& Math.abs(previous.width - next.width) < 0.01
|
|
67
|
+
&& Math.abs(previous.height - next.height) < 0.01;
|
|
68
|
+
}
|
|
69
|
+
function edgeCanvasBudget(bounds, requestedScale) {
|
|
70
|
+
const cssPixels = bounds.width * bounds.height;
|
|
71
|
+
if (!Number.isFinite(cssPixels) || cssPixels <= 0)
|
|
72
|
+
return null;
|
|
73
|
+
const scale = Math.min(requestedScale, edgeCanvasMaxDimension / bounds.width, edgeCanvasMaxDimension / bounds.height, Math.sqrt(edgeCanvasMaxPixels / cssPixels));
|
|
74
|
+
if (!Number.isFinite(scale) || scale < edgeCanvasMinScale)
|
|
75
|
+
return null;
|
|
76
|
+
const width = Math.max(1, Math.floor(bounds.width * scale));
|
|
77
|
+
const height = Math.max(1, Math.floor(bounds.height * scale));
|
|
78
|
+
if (width > edgeCanvasMaxDimension || height > edgeCanvasMaxDimension || width * height > edgeCanvasMaxPixels)
|
|
79
|
+
return null;
|
|
80
|
+
return { width, height, scaleX: width / bounds.width, scaleY: height / bounds.height };
|
|
81
|
+
}
|
|
82
|
+
function sampleSvgPath(path, bounds) {
|
|
83
|
+
const total = path.getTotalLength();
|
|
84
|
+
if (!Number.isFinite(total) || total <= 0)
|
|
85
|
+
return [];
|
|
86
|
+
const count = Math.min(edgeMaxSampleSegments, Math.max(2, Math.ceil(total / 1.4)));
|
|
87
|
+
return Array.from({ length: count + 1 }, (_, index) => {
|
|
88
|
+
const point = path.getPointAtLength(total * index / count);
|
|
89
|
+
return [point.x - bounds.x, point.y - bounds.y];
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function PencilEdgeCanvas({ id, path, className, selected = false }) {
|
|
93
|
+
const pathRef = useRef(null);
|
|
94
|
+
const canvasRef = useRef(null);
|
|
95
|
+
const [bounds, setBounds] = useState(null);
|
|
96
|
+
const [ready, setReady] = useState(false);
|
|
97
|
+
const budget = useMemo(() => bounds ? edgeCanvasBudget(bounds, effectiveDpr(2)) : null, [bounds]);
|
|
98
|
+
const measure = useCallback(() => {
|
|
99
|
+
const element = pathRef.current;
|
|
100
|
+
if (!element)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
const box = element.getBBox();
|
|
104
|
+
const next = {
|
|
105
|
+
x: box.x - edgeCanvasPadding,
|
|
106
|
+
y: box.y - edgeCanvasPadding,
|
|
107
|
+
width: Math.max(1, box.width + edgeCanvasPadding * 2),
|
|
108
|
+
height: Math.max(1, box.height + edgeCanvasPadding * 2),
|
|
109
|
+
};
|
|
110
|
+
setBounds(previous => boundsEqual(previous, next) ? previous : next);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
setBounds(null);
|
|
114
|
+
}
|
|
115
|
+
}, []);
|
|
116
|
+
useLayoutEffect(() => {
|
|
117
|
+
measure();
|
|
118
|
+
}, [measure, path]);
|
|
119
|
+
useLayoutEffect(() => {
|
|
120
|
+
setReady(false);
|
|
121
|
+
}, [budget, path]);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const canvas = canvasRef.current;
|
|
124
|
+
const pathElement = pathRef.current;
|
|
125
|
+
if (!canvas || !pathElement || !bounds || !budget)
|
|
126
|
+
return undefined;
|
|
127
|
+
let disposed = false;
|
|
128
|
+
let frame = 0;
|
|
129
|
+
let lastPaint = '';
|
|
130
|
+
const paint = () => {
|
|
131
|
+
frame = 0;
|
|
132
|
+
if (disposed)
|
|
133
|
+
return;
|
|
134
|
+
try {
|
|
135
|
+
const points = sampleSvgPath(pathElement, bounds);
|
|
136
|
+
if (points.length < 2)
|
|
137
|
+
throw new Error('NodeGraph edge has no drawable path');
|
|
138
|
+
const style = getComputedStyle(canvas);
|
|
139
|
+
const width = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
|
|
140
|
+
const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, maxStrokeRoughness);
|
|
141
|
+
const numericSeed = seedToNumber(`node-graph-edge-${id}`);
|
|
142
|
+
const key = JSON.stringify([
|
|
143
|
+
PENCIL_RENDERER_VERSION, path, bounds, style.color, width, roughness, numericSeed, budget, selected,
|
|
144
|
+
]);
|
|
145
|
+
if (key === lastPaint && canvasIsUsable(canvas))
|
|
146
|
+
return;
|
|
147
|
+
let image = edgeCanvasCache.get(key);
|
|
148
|
+
if (image && !canvasIsUsable(image)) {
|
|
149
|
+
edgeCanvasCache.delete(key);
|
|
150
|
+
image = undefined;
|
|
151
|
+
}
|
|
152
|
+
if (!image) {
|
|
153
|
+
image = document.createElement('canvas');
|
|
154
|
+
image.width = budget.width;
|
|
155
|
+
image.height = budget.height;
|
|
156
|
+
const context = image.getContext('2d');
|
|
157
|
+
if (!context)
|
|
158
|
+
throw new Error('NodeGraph edge canvas unavailable');
|
|
159
|
+
context.scale(budget.scaleX, budget.scaleY);
|
|
160
|
+
pencilStroke(context, points, style.color, width * (selected ? 1.15 : 0.92), numericSeed, roughness, 3, false, {
|
|
161
|
+
variation: 0.82,
|
|
162
|
+
breaks: selected ? 0.16 : 0.25,
|
|
163
|
+
grain: 0.7,
|
|
164
|
+
gain: selected ? 2.5 : 2.1,
|
|
165
|
+
step: 0.65,
|
|
166
|
+
taperLength: 5,
|
|
167
|
+
edgeGrain: true,
|
|
168
|
+
});
|
|
169
|
+
edgeCanvasCache.set(key, image, canvasByteCost(image));
|
|
170
|
+
}
|
|
171
|
+
const context = canvas.getContext('2d');
|
|
172
|
+
if (!context || context.isContextLost?.())
|
|
173
|
+
throw new Error('NodeGraph edge canvas unavailable');
|
|
174
|
+
canvas.width = image.width;
|
|
175
|
+
canvas.height = image.height;
|
|
176
|
+
context.drawImage(image, 0, 0);
|
|
177
|
+
lastPaint = key;
|
|
178
|
+
setReady(true);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
lastPaint = '';
|
|
182
|
+
canvas.width = 0;
|
|
183
|
+
canvas.height = 0;
|
|
184
|
+
setReady(false);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const schedule = () => {
|
|
188
|
+
if (!frame)
|
|
189
|
+
frame = requestAnimationFrame(paint);
|
|
190
|
+
};
|
|
191
|
+
const inheritedStyle = new MutationObserver(schedule);
|
|
192
|
+
for (let element = canvas; element; element = element.parentElement) {
|
|
193
|
+
inheritedStyle.observe(element, { attributes: true, attributeFilter: ['style', 'class', 'data-theme', 'data-inkra-theme'] });
|
|
194
|
+
}
|
|
195
|
+
const scheme = matchMedia('(prefers-color-scheme: dark)');
|
|
196
|
+
scheme.addEventListener('change', schedule);
|
|
197
|
+
const stopRecovery = observeCanvasRecovery(() => { lastPaint = ''; schedule(); });
|
|
198
|
+
paint();
|
|
199
|
+
return () => {
|
|
200
|
+
disposed = true;
|
|
201
|
+
inheritedStyle.disconnect();
|
|
202
|
+
scheme.removeEventListener('change', schedule);
|
|
203
|
+
stopRecovery();
|
|
204
|
+
cancelAnimationFrame(frame);
|
|
205
|
+
};
|
|
206
|
+
}, [bounds, budget, id, path, selected]);
|
|
207
|
+
return _jsxs("g", { className: ['inkra-node-graph__edge-pencil', className].filter(Boolean).join(' '), "data-pencil-ready": ready && budget ? true : undefined, "data-selected": selected || undefined, children: [_jsx("path", { ref: pathRef, d: path, className: "inkra-node-graph__edge-measure", fill: "none" }), _jsx("path", { d: path, className: "inkra-node-graph__edge-fallback", fill: "none" }), bounds && budget && _jsx("foreignObject", { "aria-hidden": "true", className: "inkra-node-graph__edge-foreign-object", height: bounds.height, pointerEvents: "none", width: bounds.width, x: bounds.x, y: bounds.y, children: _jsx("canvas", { ref: canvasRef, className: "inkra-node-graph__edge-canvas", style: { height: bounds.height, width: bounds.width } }) })] });
|
|
208
|
+
}
|
|
209
|
+
const NodeGraphEdge = memo(function NodeGraphEdge({ id, path, labelX, labelY, selected, interactionWidth = 24, label, className, }) {
|
|
210
|
+
return _jsxs(_Fragment, { children: [interactionWidth > 0 && _jsx("path", { d: path, className: "inkra-node-graph__edge-hit", fill: "none", stroke: "transparent", strokeWidth: interactionWidth }), _jsx(PencilEdgeCanvas, { id: id, path: path, className: className, selected: selected }), label != null && Number.isFinite(labelX) && Number.isFinite(labelY) && _jsx(EdgeLabelRenderer, { children: _jsx(PencilSurface, { className: "inkra-node-graph__edge-label nodrag nopan", variant: "paper", radius: 6, seed: `edge-label-${id}`, style: { transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` }, children: label }) })] });
|
|
211
|
+
});
|
|
212
|
+
function DefaultEdgeRenderer({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, selected = false, interactionWidth, data, }) {
|
|
213
|
+
const [path, labelX, labelY] = getBezierPath({
|
|
214
|
+
sourceX,
|
|
215
|
+
sourceY,
|
|
216
|
+
targetX,
|
|
217
|
+
targetY,
|
|
218
|
+
sourcePosition,
|
|
219
|
+
targetPosition,
|
|
220
|
+
});
|
|
221
|
+
const edge = data?.__inkraEdge;
|
|
222
|
+
const custom = edge && data?.__inkraRenderEdge?.({ id, edge, selected, path, labelX, labelY });
|
|
223
|
+
if (custom != null)
|
|
224
|
+
return _jsxs(_Fragment, { children: [_jsx("path", { d: path, className: "inkra-node-graph__edge-hit", fill: "none", stroke: "transparent", strokeWidth: interactionWidth ?? 24 }), custom] });
|
|
225
|
+
const label = edge == null ? undefined : data?.__inkraRenderEdgeLabel?.(edge) ?? edge.label;
|
|
226
|
+
return _jsx(NodeGraphEdge, { id: id, interactionWidth: interactionWidth, label: label, labelX: labelX, labelY: labelY, path: path, selected: selected });
|
|
227
|
+
}
|
|
228
|
+
const nodeTypes = { inkra: DefaultNodeRenderer };
|
|
229
|
+
const edgeTypes = { inkra: DefaultEdgeRenderer };
|
|
230
|
+
function NodeGraphConnectionLine({ fromX, fromY, toX, toY, fromPosition, toPosition }) {
|
|
231
|
+
const [path] = getBezierPath({
|
|
232
|
+
sourceX: fromX,
|
|
233
|
+
sourceY: fromY,
|
|
234
|
+
sourcePosition: fromPosition,
|
|
235
|
+
targetX: toX,
|
|
236
|
+
targetY: toY,
|
|
237
|
+
targetPosition: toPosition,
|
|
238
|
+
});
|
|
239
|
+
return _jsx(PencilEdgeCanvas, { id: "connection-preview", path: path, className: "inkra-node-graph__connection-line" });
|
|
240
|
+
}
|
|
241
|
+
function NodeGraphControls({ className, fitViewOptions, zoomInLabel = 'Zoom in', zoomOutLabel = 'Zoom out', fitViewLabel = 'Fit graph to view', ...props }) {
|
|
242
|
+
const flow = useReactFlow();
|
|
243
|
+
const actions = [
|
|
244
|
+
{ label: zoomInLabel, paths: zoomInPaths, run: () => flow.zoomIn() },
|
|
245
|
+
{ label: zoomOutLabel, paths: zoomOutPaths, run: () => flow.zoomOut() },
|
|
246
|
+
{ label: fitViewLabel, paths: fitViewPaths, run: () => flow.fitView(fitViewOptions) },
|
|
247
|
+
];
|
|
248
|
+
return _jsx("div", { ...props, className: ['inkra-node-graph__controls', 'nopan', className].filter(Boolean).join(' '), children: actions.map(action => _jsxs(PencilSurface, { as: "button", "aria-label": action.label, className: "inkra-node-graph__control", "data-tooltip": action.label, onClick: () => void action.run(), variant: "paper", children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-node-graph__control-focus", radius: 6, seed: `node-graph-${action.label}-focus`, variant: "outline" }), _jsx(PencilIcon, { decorative: true, paths: action.paths, size: 22, sourceSize: 24, seed: `node-graph-${action.label}` })] }, action.label)) });
|
|
249
|
+
}
|
|
250
|
+
function SsrFallback({ nodes, emptyState, label, }) {
|
|
251
|
+
return _jsx("div", { className: "inkra-node-graph__ssr", role: "group", "aria-label": label, children: nodes.length === 0 ? _jsx("div", { className: "inkra-node-graph__empty", children: emptyState }) : _jsx("ul", { children: nodes.map(node => _jsx("li", { children: readableLabel(node.data.label, node.id) }, node.id)) }) });
|
|
252
|
+
}
|
|
253
|
+
function NodeGraphRootInner({ nodes, edges, onNodesChange, onEdgesChange, onConnect, isValidConnection, renderNode, renderEdge, renderEdgeLabel, readOnly = false, emptyState = 'No nodes yet', controls = true, controlLabels, fitView = false, fitViewOptions, defaultViewport = { x: 0, y: 0, zoom: 1 }, viewport, onViewportChange, minZoom = 0.25, maxZoom = 2, panOnDrag = true, zoomOnScroll = true, zoomOnPinch = true, connectOnClick = true, onlyRenderVisibleElements = false, className, style, 'aria-label': ariaLabel = 'Node graph', ...props }, forwardedRef) {
|
|
254
|
+
const [mounted, setMounted] = useState(false);
|
|
255
|
+
const instanceRef = useRef(null);
|
|
256
|
+
useEffect(() => setMounted(true), []);
|
|
257
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
258
|
+
fitView: options => instanceRef.current?.fitView(options) ?? Promise.resolve(false),
|
|
259
|
+
zoomIn: () => instanceRef.current?.zoomIn() ?? Promise.resolve(false),
|
|
260
|
+
zoomOut: () => instanceRef.current?.zoomOut() ?? Promise.resolve(false),
|
|
261
|
+
getViewport: () => instanceRef.current?.getViewport() ?? defaultViewport,
|
|
262
|
+
}), [defaultViewport]);
|
|
263
|
+
const internalNodes = useMemo(() => nodes.map(node => ({
|
|
264
|
+
...node,
|
|
265
|
+
type: 'inkra',
|
|
266
|
+
ariaLabel: node.ariaLabel ?? readableLabel(node.data.label, node.id),
|
|
267
|
+
data: {
|
|
268
|
+
__inkraRenderNode: renderNode,
|
|
269
|
+
__inkraReadOnly: readOnly,
|
|
270
|
+
__inkraData: node.data,
|
|
271
|
+
},
|
|
272
|
+
})), [nodes, readOnly, renderNode]);
|
|
273
|
+
const internalEdges = useMemo(() => edges.map(edge => ({
|
|
274
|
+
...edge,
|
|
275
|
+
type: 'inkra',
|
|
276
|
+
ariaLabel: edge.ariaLabel ?? `Edge from ${edge.source} to ${edge.target}`,
|
|
277
|
+
label: undefined,
|
|
278
|
+
data: {
|
|
279
|
+
...edge.data,
|
|
280
|
+
__inkraEdge: edge,
|
|
281
|
+
__inkraRenderEdge: renderEdge,
|
|
282
|
+
__inkraRenderEdgeLabel: renderEdgeLabel,
|
|
283
|
+
},
|
|
284
|
+
})), [edges, renderEdge, renderEdgeLabel]);
|
|
285
|
+
const rootStyle = { minHeight: 320, ...style };
|
|
286
|
+
return _jsx("div", { ...props, "aria-label": ariaLabel, className: ['inkra-node-graph', className].filter(Boolean).join(' '), "data-empty": nodes.length === 0 || undefined, "data-read-only": readOnly || undefined, style: rootStyle, children: !mounted ? _jsx(SsrFallback, { nodes: nodes, emptyState: emptyState, label: ariaLabel }) : _jsxs(ReactFlowProvider, { children: [_jsx(ReactFlow, { "aria-label": ariaLabel, nodes: internalNodes, edges: internalEdges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onInit: instance => { instanceRef.current = instance; }, onNodesChange: changes => onNodesChange?.(changes), onEdgesChange: changes => onEdgesChange?.(changes), onConnect: readOnly ? undefined : onConnect, isValidConnection: isValidConnection, nodesDraggable: !readOnly, nodesConnectable: !readOnly, elementsSelectable: !readOnly, edgesReconnectable: !readOnly, nodesFocusable: true, edgesFocusable: true, deleteKeyCode: readOnly ? null : ['Backspace', 'Delete'], fitView: fitView, fitViewOptions: fitViewOptions, defaultViewport: defaultViewport, viewport: viewport, onViewportChange: onViewportChange, minZoom: minZoom, maxZoom: maxZoom, panOnDrag: panOnDrag, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, connectOnClick: connectOnClick && !readOnly, connectionLineComponent: NodeGraphConnectionLine, onlyRenderVisibleElements: onlyRenderVisibleElements, noDragClassName: "nodrag", noPanClassName: "nopan", children: controls && _jsx(Panel, { position: "bottom-left", children: _jsx(NodeGraphControls, { fitViewLabel: controlLabels?.fitView, fitViewOptions: fitViewOptions, zoomInLabel: controlLabels?.zoomIn, zoomOutLabel: controlLabels?.zoomOut }) }) }), nodes.length === 0 && _jsx("div", { className: "inkra-node-graph__empty", role: "status", children: emptyState })] }) });
|
|
287
|
+
}
|
|
288
|
+
const NodeGraphRoot = forwardRef(NodeGraphRootInner);
|
|
289
|
+
export function applyNodeGraphNodeChanges(changes, nodes) {
|
|
290
|
+
return applyFlowNodeChanges(changes, nodes);
|
|
291
|
+
}
|
|
292
|
+
export function applyNodeGraphEdgeChanges(changes, edges) {
|
|
293
|
+
return applyFlowEdgeChanges(changes, edges);
|
|
294
|
+
}
|
|
295
|
+
export function addNodeGraphEdge(connection, edges) {
|
|
296
|
+
return addFlowEdge(connection, edges);
|
|
297
|
+
}
|
|
298
|
+
export const NodeGraph = {
|
|
299
|
+
Root: NodeGraphRoot,
|
|
300
|
+
Node: NodeGraphNode,
|
|
301
|
+
Port: NodeGraphPort,
|
|
302
|
+
Edge: NodeGraphEdge,
|
|
303
|
+
Controls: NodeGraphControls,
|
|
304
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
2
|
+
import type { NumberTickerCompact, NumberTickerCompactOptions, NumberTickerFormatOptions, NumberTickerLocale, NumberTickerValue } from './model.js';
|
|
3
|
+
export interface NumberTickerRenderDetails {
|
|
4
|
+
animating: boolean;
|
|
5
|
+
formattedValue: string;
|
|
6
|
+
}
|
|
7
|
+
export interface NumberTickerDeltaDetails {
|
|
8
|
+
direction: 'increase' | 'decrease';
|
|
9
|
+
formattedDelta: string;
|
|
10
|
+
}
|
|
11
|
+
export interface NumberTickerProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children' | 'prefix'> {
|
|
12
|
+
value: NumberTickerValue;
|
|
13
|
+
locale?: NumberTickerLocale;
|
|
14
|
+
formatOptions?: NumberTickerFormatOptions;
|
|
15
|
+
compact?: NumberTickerCompact;
|
|
16
|
+
compactOptions?: NumberTickerCompactOptions;
|
|
17
|
+
prefix?: ReactNode;
|
|
18
|
+
suffix?: ReactNode;
|
|
19
|
+
renderValue?: (value: NumberTickerValue, details: NumberTickerRenderDetails) => ReactNode;
|
|
20
|
+
getValueLabel?: (value: NumberTickerValue, formattedValue: string) => string;
|
|
21
|
+
animate?: boolean;
|
|
22
|
+
duration?: number;
|
|
23
|
+
easing?: (progress: number) => number;
|
|
24
|
+
showDelta?: boolean;
|
|
25
|
+
deltaDuration?: number;
|
|
26
|
+
deltaFormatOptions?: NumberTickerFormatOptions;
|
|
27
|
+
renderDelta?: (delta: NumberTickerValue, details: NumberTickerDeltaDetails) => ReactNode;
|
|
28
|
+
}
|
|
29
|
+
export declare const NumberTicker: import("react").ForwardRefExoticComponent<NumberTickerProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
30
|
+
export { createNumberFormatter, easeOutCubic, formatTickerDelta, formatTickerValue, interpolateTickerValue, numberTickerUnits, } from './model.js';
|
|
31
|
+
export type { NumberTickerCompact, NumberTickerCompactOptions, NumberTickerFormatOptions, NumberTickerLocale, NumberTickerUnit, NumberTickerValue } from './model.js';
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { PencilText } from '../PencilText.js';
|
|
4
|
+
import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
|
|
5
|
+
import { canAnimateTickerValue, createNumberFormatter, createTickerFormatter, easeOutCubic, formatDerivedTickerDelta, formatTickerValue, interpolateTickerValue, tickerValueChanged, subtractTickerValues, tickerValueKey, } from './model.js';
|
|
6
|
+
import { ExactDecimal } from './precision.js';
|
|
7
|
+
function usePrefersReducedMotion() {
|
|
8
|
+
const [reduced, setReduced] = useState(false);
|
|
9
|
+
useBrowserLayoutEffect(() => {
|
|
10
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
|
|
11
|
+
return undefined;
|
|
12
|
+
const query = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
13
|
+
const update = () => setReduced(query.matches);
|
|
14
|
+
update();
|
|
15
|
+
query.addEventListener('change', update);
|
|
16
|
+
return () => query.removeEventListener('change', update);
|
|
17
|
+
}, []);
|
|
18
|
+
return reduced;
|
|
19
|
+
}
|
|
20
|
+
function lettering(content, seed) {
|
|
21
|
+
return typeof content === 'string' ? _jsx(PencilText, { seed: seed, children: content }) : content;
|
|
22
|
+
}
|
|
23
|
+
function accessibleAffix(content) {
|
|
24
|
+
return typeof content === 'string' || typeof content === 'number' ? String(content) : '';
|
|
25
|
+
}
|
|
26
|
+
export const NumberTicker = forwardRef(function NumberTicker({ animate = true, className, compact = false, compactOptions, deltaDuration = 1600, deltaFormatOptions, duration = 420, easing = easeOutCubic, formatOptions, getValueLabel, locale, prefix, renderDelta, renderValue, showDelta = false, suffix, value, ...props }, forwardedRef) {
|
|
27
|
+
const rootRef = useRef(null);
|
|
28
|
+
const frameRef = useRef(null);
|
|
29
|
+
const deltaTimerRef = useRef(null);
|
|
30
|
+
const displayedRef = useRef(value);
|
|
31
|
+
const targetRef = useRef(value);
|
|
32
|
+
const [displayedValue, setDisplayedValue] = useState(value);
|
|
33
|
+
const [announcedValue, setAnnouncedValue] = useState(value);
|
|
34
|
+
const [animating, setAnimating] = useState(false);
|
|
35
|
+
const [delta, setDelta] = useState(null);
|
|
36
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
37
|
+
const localeKey = Array.isArray(locale) ? locale.join('\u0000') : locale;
|
|
38
|
+
const formatOptionsKey = JSON.stringify(formatOptions ?? {});
|
|
39
|
+
const compactOptionsKey = tickerValueKey(compactOptions ?? {});
|
|
40
|
+
const deltaFormatOptionsKey = JSON.stringify(deltaFormatOptions ?? formatOptions ?? {});
|
|
41
|
+
const formatter = useMemo(() => createTickerFormatter(locale, formatOptions, compact, compactOptions), [localeKey, formatOptionsKey, compact, compactOptionsKey]);
|
|
42
|
+
const deltaFormatter = useMemo(() => createTickerFormatter(locale, deltaFormatOptions ?? formatOptions, compact, compactOptions), [localeKey, deltaFormatOptionsKey, compact, compactOptionsKey]);
|
|
43
|
+
const announcementFormatter = useMemo(() => createNumberFormatter(locale, compact ? { ...formatOptions, notation: 'standard' } : formatOptions), [localeKey, formatOptionsKey, compact]);
|
|
44
|
+
useImperativeHandle(forwardedRef, () => rootRef.current);
|
|
45
|
+
useBrowserLayoutEffect(() => {
|
|
46
|
+
const previousTarget = targetRef.current;
|
|
47
|
+
targetRef.current = value;
|
|
48
|
+
if (showDelta && tickerValueChanged(previousTarget, value)) {
|
|
49
|
+
const nextDelta = subtractTickerValues(value, previousTarget);
|
|
50
|
+
setDelta(nextDelta);
|
|
51
|
+
if (deltaTimerRef.current !== null)
|
|
52
|
+
clearTimeout(deltaTimerRef.current);
|
|
53
|
+
if (deltaDuration > 0 && Number.isFinite(deltaDuration)) {
|
|
54
|
+
deltaTimerRef.current = setTimeout(() => {
|
|
55
|
+
deltaTimerRef.current = null;
|
|
56
|
+
setDelta(null);
|
|
57
|
+
}, deltaDuration);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (!showDelta) {
|
|
61
|
+
setDelta(null);
|
|
62
|
+
}
|
|
63
|
+
if (frameRef.current !== null) {
|
|
64
|
+
cancelAnimationFrame(frameRef.current);
|
|
65
|
+
frameRef.current = null;
|
|
66
|
+
}
|
|
67
|
+
const reduceImmediately = reducedMotion
|
|
68
|
+
|| (typeof window !== 'undefined'
|
|
69
|
+
&& typeof window.matchMedia === 'function'
|
|
70
|
+
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
71
|
+
const from = displayedRef.current;
|
|
72
|
+
if (!animate || reduceImmediately || !canAnimateTickerValue(from, value, duration)) {
|
|
73
|
+
displayedRef.current = value;
|
|
74
|
+
setDisplayedValue(value);
|
|
75
|
+
setAnnouncedValue(value);
|
|
76
|
+
setAnimating(false);
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
let startTime = null;
|
|
80
|
+
setAnimating(true);
|
|
81
|
+
const tick = (time) => {
|
|
82
|
+
if (startTime === null)
|
|
83
|
+
startTime = time;
|
|
84
|
+
const progress = Math.min(1, Math.max(0, (time - startTime) / duration));
|
|
85
|
+
const next = interpolateTickerValue(from, value, easing(progress));
|
|
86
|
+
displayedRef.current = next;
|
|
87
|
+
setDisplayedValue(next);
|
|
88
|
+
if (progress < 1) {
|
|
89
|
+
frameRef.current = requestAnimationFrame(tick);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
frameRef.current = null;
|
|
93
|
+
displayedRef.current = value;
|
|
94
|
+
setDisplayedValue(value);
|
|
95
|
+
setAnnouncedValue(value);
|
|
96
|
+
setAnimating(false);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
frameRef.current = requestAnimationFrame(tick);
|
|
100
|
+
return () => {
|
|
101
|
+
if (frameRef.current !== null) {
|
|
102
|
+
cancelAnimationFrame(frameRef.current);
|
|
103
|
+
frameRef.current = null;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}, [animate, deltaDuration, duration, easing, reducedMotion, showDelta, value]);
|
|
107
|
+
useEffect(() => () => {
|
|
108
|
+
if (frameRef.current !== null)
|
|
109
|
+
cancelAnimationFrame(frameRef.current);
|
|
110
|
+
if (deltaTimerRef.current !== null)
|
|
111
|
+
clearTimeout(deltaTimerRef.current);
|
|
112
|
+
}, []);
|
|
113
|
+
const formattedValue = formatTickerValue(formatter, displayedValue);
|
|
114
|
+
const finalFormattedValue = formatTickerValue(formatter, value);
|
|
115
|
+
const announcedFormattedValue = formatTickerValue(announcementFormatter, announcedValue);
|
|
116
|
+
const defaultAccessibleLabel = [accessibleAffix(prefix), announcedFormattedValue, accessibleAffix(suffix)]
|
|
117
|
+
.filter(Boolean).join(' ');
|
|
118
|
+
const accessibleLabel = getValueLabel?.(announcedValue, announcedFormattedValue) ?? defaultAccessibleLabel;
|
|
119
|
+
const valueContent = renderValue?.(displayedValue, { animating, formattedValue })
|
|
120
|
+
?? (animating
|
|
121
|
+
? _jsx("span", { className: "inkra-number-ticker__native-value", children: formattedValue })
|
|
122
|
+
: _jsx(PencilText, { seed: `number-ticker-${finalFormattedValue}`, children: finalFormattedValue }));
|
|
123
|
+
const deltaContent = delta === null ? null : (() => {
|
|
124
|
+
const formattedDelta = formatDerivedTickerDelta(deltaFormatter, delta);
|
|
125
|
+
const details = {
|
|
126
|
+
direction: new ExactDecimal(String(delta)).lt(0) ? 'decrease' : 'increase',
|
|
127
|
+
formattedDelta,
|
|
128
|
+
};
|
|
129
|
+
return renderDelta?.(delta, details) ?? _jsx(PencilText, { seed: `number-ticker-delta-${formattedDelta}`, children: formattedDelta });
|
|
130
|
+
})();
|
|
131
|
+
const classes = ['inkra-number-ticker', animating && 'inkra-number-ticker--animating', className]
|
|
132
|
+
.filter(Boolean).join(' ');
|
|
133
|
+
return _jsxs("span", { ...props, className: classes, ref: rootRef, "data-animating": animating || undefined, children: [_jsxs("span", { "aria-hidden": "true", className: "inkra-number-ticker__visual", children: [prefix !== undefined && _jsx("span", { className: "inkra-number-ticker__prefix", children: lettering(prefix, 'number-ticker-prefix') }), _jsx("span", { className: "inkra-number-ticker__value", children: valueContent }), suffix !== undefined && _jsx("span", { className: "inkra-number-ticker__suffix", children: lettering(suffix, 'number-ticker-suffix') }), deltaContent !== null && _jsx("span", { className: "inkra-number-ticker__delta", "data-direction": new ExactDecimal(String(delta)).lt(0) ? 'decrease' : 'increase', children: deltaContent })] }), _jsx("span", { className: "inkra-number-ticker__announcement", "aria-atomic": "true", "aria-live": "polite", children: accessibleLabel })] });
|
|
134
|
+
});
|
|
135
|
+
export { createNumberFormatter, easeOutCubic, formatTickerDelta, formatTickerValue, interpolateTickerValue, numberTickerUnits, } from './model.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { tickerDecimal } from './precision.js';
|
|
2
|
+
import type { NumberTickerValue } from './precision.js';
|
|
3
|
+
export { subtractTickerValues, tickerValueKey } from './precision.js';
|
|
4
|
+
export type { NumberTickerValue } from './precision.js';
|
|
5
|
+
export type NumberTickerLocale = string | readonly string[];
|
|
6
|
+
export type NumberTickerCompact = boolean | 'en' | 'zh';
|
|
7
|
+
export interface NumberTickerFormatOptions extends Intl.NumberFormatOptions {
|
|
8
|
+
roundingMode?: 'ceil' | 'floor' | 'expand' | 'trunc' | 'halfCeil' | 'halfFloor' | 'halfExpand' | 'halfTrunc' | 'halfEven';
|
|
9
|
+
}
|
|
10
|
+
export interface NumberTickerUnit {
|
|
11
|
+
factor: NumberTickerValue;
|
|
12
|
+
label: string;
|
|
13
|
+
/** Absolute display magnitude required to enter this unit. */
|
|
14
|
+
threshold?: NumberTickerValue;
|
|
15
|
+
}
|
|
16
|
+
export interface NumberTickerCompactOptions {
|
|
17
|
+
units?: readonly NumberTickerUnit[];
|
|
18
|
+
minUnit?: string;
|
|
19
|
+
maxUnit?: string;
|
|
20
|
+
separator?: string;
|
|
21
|
+
promotionThreshold?: NumberTickerValue;
|
|
22
|
+
promoteAfterRounding?: boolean;
|
|
23
|
+
overflow?: 'unit' | 'scientific';
|
|
24
|
+
}
|
|
25
|
+
export declare const numberTickerUnits: Readonly<Record<'en' | 'zh', readonly NumberTickerUnit[]>>;
|
|
26
|
+
type TickerFormatter = {
|
|
27
|
+
format(value: NumberTickerValue): string;
|
|
28
|
+
formatToParts(value: NumberTickerValue): Intl.NumberFormatPart[];
|
|
29
|
+
formatDecimal(value: ReturnType<typeof tickerDecimal>): Intl.NumberFormatPart[];
|
|
30
|
+
};
|
|
31
|
+
export declare function createNumberFormatter(locale?: NumberTickerLocale, options?: NumberTickerFormatOptions): Intl.NumberFormat;
|
|
32
|
+
export declare function createTickerFormatter(locale?: NumberTickerLocale, options?: NumberTickerFormatOptions, compact?: NumberTickerCompact, compactOptions?: NumberTickerCompactOptions): TickerFormatter;
|
|
33
|
+
export declare function formatTickerValue(formatter: TickerFormatter | Intl.NumberFormat, value: NumberTickerValue): string;
|
|
34
|
+
export declare function formatTickerDelta(formatter: TickerFormatter | Intl.NumberFormat, delta: NumberTickerValue): string;
|
|
35
|
+
export declare function formatDerivedTickerDelta(formatter: TickerFormatter, delta: NumberTickerValue): string;
|
|
36
|
+
export declare function interpolateTickerValue(from: number, to: number, progress: number): number;
|
|
37
|
+
export declare function interpolateTickerValue(from: bigint, to: bigint, progress: number): bigint;
|
|
38
|
+
export declare function interpolateTickerValue(from: NumberTickerValue, to: NumberTickerValue, progress: number): NumberTickerValue;
|
|
39
|
+
export declare function tickerValueChanged(previous: NumberTickerValue, next: NumberTickerValue): boolean;
|
|
40
|
+
export declare function canAnimateTickerValue(from: NumberTickerValue, to: NumberTickerValue, duration: number): boolean;
|
|
41
|
+
export declare function easeOutCubic(progress: number): number;
|