@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.
Files changed (177) hide show
  1. package/README.md +186 -1
  2. package/dist/PencilIcon.js +14 -16
  3. package/dist/PencilLine.js +18 -12
  4. package/dist/PencilText.js +192 -59
  5. package/dist/alert-dialog/index.d.ts +64 -0
  6. package/dist/alert-dialog/index.js +111 -0
  7. package/dist/audio-player/index.d.ts +60 -0
  8. package/dist/audio-player/index.js +101 -0
  9. package/dist/carousel/index.d.ts +51 -0
  10. package/dist/carousel/index.js +290 -0
  11. package/dist/choice-grid/index.d.ts +38 -0
  12. package/dist/choice-grid/index.js +85 -0
  13. package/dist/color-picker/color.d.ts +15 -0
  14. package/dist/color-picker/color.js +55 -0
  15. package/dist/color-picker/index.d.ts +31 -0
  16. package/dist/color-picker/index.js +69 -0
  17. package/dist/command/index.d.ts +45 -0
  18. package/dist/command/index.js +39 -0
  19. package/dist/context-menu/index.d.ts +76 -0
  20. package/dist/context-menu/index.js +241 -0
  21. package/dist/data-table/index.d.ts +47 -0
  22. package/dist/data-table/index.js +91 -0
  23. package/dist/date-picker/index.d.ts +50 -0
  24. package/dist/date-picker/index.js +180 -0
  25. package/dist/display/ProgressRing.d.ts +6 -0
  26. package/dist/display/ProgressRing.js +146 -0
  27. package/dist/display/index.d.ts +27 -0
  28. package/dist/display/index.js +18 -2
  29. package/dist/dockable-panel/index.d.ts +41 -0
  30. package/dist/dockable-panel/index.js +253 -0
  31. package/dist/dockable-panel/model.d.ts +21 -0
  32. package/dist/dockable-panel/model.js +61 -0
  33. package/dist/drawer/index.d.ts +63 -0
  34. package/dist/drawer/index.js +88 -0
  35. package/dist/dropdown-menu/index.d.ts +1 -1
  36. package/dist/dropdown-menu/index.js +3 -2
  37. package/dist/editable/index.d.ts +53 -0
  38. package/dist/editable/index.js +162 -0
  39. package/dist/empty/index.d.ts +21 -0
  40. package/dist/empty/index.js +23 -0
  41. package/dist/flip/index.d.ts +49 -0
  42. package/dist/flip/index.js +138 -0
  43. package/dist/form/EditablePencilText.js +188 -143
  44. package/dist/form/Input.js +1 -1
  45. package/dist/form/Textarea.d.ts +12 -0
  46. package/dist/form/Textarea.js +83 -4
  47. package/dist/form/index.d.ts +1 -1
  48. package/dist/heatmap/index.d.ts +59 -0
  49. package/dist/heatmap/index.js +248 -0
  50. package/dist/hotkey/index.d.ts +37 -0
  51. package/dist/hotkey/index.js +130 -0
  52. package/dist/hotkey/model.d.ts +27 -0
  53. package/dist/hotkey/model.js +97 -0
  54. package/dist/hover-card/index.d.ts +33 -0
  55. package/dist/hover-card/index.js +141 -0
  56. package/dist/image-cropper/index.d.ts +82 -0
  57. package/dist/image-cropper/index.js +261 -0
  58. package/dist/image-cropper/model.d.ts +42 -0
  59. package/dist/image-cropper/model.js +94 -0
  60. package/dist/image-viewer/index.d.ts +79 -0
  61. package/dist/image-viewer/index.js +220 -0
  62. package/dist/image-viewer/model.d.ts +9 -0
  63. package/dist/image-viewer/model.js +18 -0
  64. package/dist/index.d.ts +47 -0
  65. package/dist/index.js +47 -0
  66. package/dist/input-number/index.d.ts +6 -0
  67. package/dist/input-number/index.js +126 -15
  68. package/dist/input-number/model.d.ts +13 -0
  69. package/dist/input-number/model.js +50 -0
  70. package/dist/internal/cache.d.ts +1 -1
  71. package/dist/internal/cache.js +1 -1
  72. package/dist/internal/renderTokens.d.ts +10 -0
  73. package/dist/internal/renderTokens.js +13 -0
  74. package/dist/internal/surfaceRenderer.d.ts +14 -6
  75. package/dist/internal/surfaceRenderer.js +15 -12
  76. package/dist/internal/textRenderer.d.ts +1 -0
  77. package/dist/internal/textRenderer.js +33 -7
  78. package/dist/internal/usePencilSurface.js +16 -8
  79. package/dist/internal/useSelectionDrawing.js +15 -11
  80. package/dist/journey-map/MapScale.js +3 -1
  81. package/dist/journey-map/index.js +3 -1
  82. package/dist/keypad/index.d.ts +23 -0
  83. package/dist/keypad/index.js +63 -0
  84. package/dist/keypad/model.d.ts +20 -0
  85. package/dist/keypad/model.js +33 -0
  86. package/dist/layout/index.d.ts +63 -0
  87. package/dist/layout/index.js +62 -0
  88. package/dist/log/index.d.ts +45 -0
  89. package/dist/log/index.js +159 -0
  90. package/dist/log/model.d.ts +16 -0
  91. package/dist/log/model.js +13 -0
  92. package/dist/node-graph/index.d.ts +123 -0
  93. package/dist/node-graph/index.js +304 -0
  94. package/dist/number-ticker/index.d.ts +31 -0
  95. package/dist/number-ticker/index.js +135 -0
  96. package/dist/number-ticker/model.d.ts +41 -0
  97. package/dist/number-ticker/model.js +163 -0
  98. package/dist/number-ticker/precision.d.ts +7 -0
  99. package/dist/number-ticker/precision.js +134 -0
  100. package/dist/overflow-list/index.d.ts +39 -0
  101. package/dist/overflow-list/index.js +230 -0
  102. package/dist/overflow-list/layout.d.ts +19 -0
  103. package/dist/overflow-list/layout.js +44 -0
  104. package/dist/overlay/Overlay.d.ts +1 -1
  105. package/dist/overlay/Overlay.js +4 -2
  106. package/dist/overlay/Popover.d.ts +1 -1
  107. package/dist/overlay/Popover.js +3 -2
  108. package/dist/overlay/themeBridge.d.ts +12 -1
  109. package/dist/overlay/themeBridge.js +27 -1
  110. package/dist/pagination/index.d.ts +23 -0
  111. package/dist/pagination/index.js +41 -0
  112. package/dist/pagination/model.d.ts +4 -0
  113. package/dist/pagination/model.js +42 -0
  114. package/dist/pan-zoom/index.d.ts +71 -0
  115. package/dist/pan-zoom/index.js +175 -0
  116. package/dist/radar/index.d.ts +56 -0
  117. package/dist/radar/index.js +220 -0
  118. package/dist/range-slider/index.d.ts +16 -0
  119. package/dist/range-slider/index.js +22 -0
  120. package/dist/rating/index.d.ts +28 -0
  121. package/dist/rating/index.js +127 -0
  122. package/dist/resizable/index.d.ts +32 -0
  123. package/dist/resizable/index.js +50 -0
  124. package/dist/scroll-area/index.d.ts +36 -0
  125. package/dist/scroll-area/index.js +44 -0
  126. package/dist/segmented-progress/SegmentedProgress.d.ts +17 -0
  127. package/dist/segmented-progress/SegmentedProgress.js +42 -0
  128. package/dist/slider/index.js +1 -1
  129. package/dist/slot/index.d.ts +24 -0
  130. package/dist/slot/index.js +29 -0
  131. package/dist/sortable/index.d.ts +50 -0
  132. package/dist/sortable/index.js +283 -0
  133. package/dist/sortable/model.d.ts +30 -0
  134. package/dist/sortable/model.js +64 -0
  135. package/dist/sparkline/index.d.ts +19 -0
  136. package/dist/sparkline/index.js +240 -0
  137. package/dist/sparkline/model.d.ts +16 -0
  138. package/dist/sparkline/model.js +28 -0
  139. package/dist/steps/index.d.ts +16 -0
  140. package/dist/steps/index.js +37 -0
  141. package/dist/styles.css +4957 -269
  142. package/dist/tag-input/index.d.ts +69 -0
  143. package/dist/tag-input/index.js +325 -0
  144. package/dist/tag-input/model.d.ts +14 -0
  145. package/dist/tag-input/model.js +38 -0
  146. package/dist/time-picker/index.d.ts +15 -0
  147. package/dist/time-picker/index.js +168 -0
  148. package/dist/timeline/index.d.ts +14 -0
  149. package/dist/timeline/index.js +16 -0
  150. package/dist/toast/index.d.ts +27 -0
  151. package/dist/toast/index.js +36 -0
  152. package/dist/toggle-group/index.d.ts +17 -0
  153. package/dist/toggle-group/index.js +26 -0
  154. package/dist/tokens.css +10 -9
  155. package/dist/tooltip/Tooltip.d.ts +1 -1
  156. package/dist/tooltip/Tooltip.js +3 -2
  157. package/dist/transfer/index.d.ts +64 -0
  158. package/dist/transfer/index.js +174 -0
  159. package/dist/transfer/model.d.ts +12 -0
  160. package/dist/transfer/model.js +33 -0
  161. package/dist/tree/index.d.ts +39 -0
  162. package/dist/tree/index.js +36 -0
  163. package/dist/tree-select/index.d.ts +59 -0
  164. package/dist/tree-select/index.js +142 -0
  165. package/dist/tree-select/model.d.ts +31 -0
  166. package/dist/tree-select/model.js +135 -0
  167. package/dist/typewriter/index.d.ts +21 -0
  168. package/dist/typewriter/index.js +101 -0
  169. package/dist/typewriter/model.d.ts +7 -0
  170. package/dist/typewriter/model.js +45 -0
  171. package/dist/upload/index.d.ts +59 -0
  172. package/dist/upload/index.js +230 -0
  173. package/dist/virtual-list/index.d.ts +44 -0
  174. package/dist/virtual-list/index.js +129 -0
  175. package/dist/virtual-list/model.d.ts +7 -0
  176. package/dist/virtual-list/model.js +27 -0
  177. package/package.json +23 -3
@@ -0,0 +1,240 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useMemo, useRef, useState } from 'react';
3
+ import { PencilText } from '../PencilText.js';
4
+ import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
5
+ import { effectiveDpr } from '../internal/cache.js';
6
+ import { observeCanvasRecovery } from '../internal/recovery.js';
7
+ import { seedToNumber } from '../internal/random.js';
8
+ import { pencilStroke } from '../internal/stroke.js';
9
+ import { drawPencilWash } from '../internal/wash.js';
10
+ import { pencilRenderDefaults, readRenderToken } from '../internal/renderTokens.js';
11
+ import { createSparklineGeometry, normalizeSparklineData, sparklineDomain } from './model.js';
12
+ function defaultLabel(value, index) {
13
+ return value === null ? `Point ${index + 1}: missing` : `Point ${index + 1}: ${value}`;
14
+ }
15
+ function resolveCssColor(host, style, token, fallback) {
16
+ const value = style.getPropertyValue(token).trim();
17
+ if (!value || !value.includes('var('))
18
+ return value || fallback;
19
+ const probe = document.createElement('span');
20
+ probe.style.color = `var(${token})`;
21
+ host.appendChild(probe);
22
+ const resolved = getComputedStyle(probe).color;
23
+ probe.remove();
24
+ return resolved || fallback;
25
+ }
26
+ export const Sparkline = forwardRef(function Sparkline({ data, type = 'line', width = 180, height = 56, min, max, label = 'Sparkline', showPoints = true, showArea = true, showSummary = true, getLabel = defaultLabel, className, ...props }, ref) {
27
+ const canvasRef = useRef(null);
28
+ const interactionCanvasRef = useRef(null);
29
+ const [pencilReady, setPencilReady] = useState(false);
30
+ const [interactionReady, setInteractionReady] = useState(false);
31
+ const points = useMemo(() => normalizeSparklineData(data), [data]);
32
+ const values = useMemo(() => points.flatMap(point => point.value === null ? [] : [point.value]), [points]);
33
+ const domain = useMemo(() => sparklineDomain(points), [points]);
34
+ const boundedPoints = useMemo(() => points.map(point => ({
35
+ ...point,
36
+ value: point.value === null ? null : Math.min(max ?? Infinity, Math.max(min ?? -Infinity, point.value)),
37
+ })), [max, min, points]);
38
+ const geometry = useMemo(() => createSparklineGeometry(boundedPoints, width, height), [boundedPoints, height, width]);
39
+ const [activeIndex, setActiveIndex] = useState(values.length ? [...points].reverse().find(point => point.value !== null)?.index ?? null : null);
40
+ const activePoint = activeIndex === null ? null : points[activeIndex] ?? null;
41
+ const effectiveShowPoints = showPoints || values.length === 1;
42
+ const summary = values.length === 0 ? 'No data' : `Current ${values[values.length - 1]}, min ${Math.min(...values)}, max ${Math.max(...values)}`;
43
+ const onKeyDown = (event) => {
44
+ if (!points.length || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key))
45
+ return;
46
+ event.preventDefault();
47
+ const current = activeIndex ?? 0;
48
+ const next = event.key === 'Home' ? 0 : event.key === 'End' ? points.length - 1 : Math.min(points.length - 1, Math.max(0, current + (event.key === 'ArrowRight' ? 1 : -1)));
49
+ setActiveIndex(next);
50
+ };
51
+ const classes = ['inkra-sparkline', `inkra-sparkline--${type}`, className].filter(Boolean).join(' ');
52
+ useBrowserLayoutEffect(() => {
53
+ const canvas = canvasRef.current;
54
+ if (!canvas || typeof window === 'undefined')
55
+ return undefined;
56
+ let frame = 0;
57
+ let disposed = false;
58
+ const paint = () => {
59
+ frame = 0;
60
+ if (disposed)
61
+ return;
62
+ const cssWidth = canvas.clientWidth;
63
+ const cssHeight = canvas.clientHeight;
64
+ const clear = () => { canvas.width = 1; canvas.height = 1; setPencilReady(false); };
65
+ if (!values.length || cssWidth < 4 || cssHeight < 4) {
66
+ clear();
67
+ return;
68
+ }
69
+ const dpr = effectiveDpr(2);
70
+ const context = canvas.getContext('2d');
71
+ if (!context || context.isContextLost?.()) {
72
+ clear();
73
+ return;
74
+ }
75
+ const style = getComputedStyle(canvas);
76
+ const lineColor = resolveCssColor(canvas, style, '--inkra-accent-strong', style.color);
77
+ const washColor = resolveCssColor(canvas, style, '--inkra-accent', lineColor);
78
+ const strokeWidth = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
79
+ const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, 4);
80
+ const washStrength = readRenderToken(style, '--inkra-wash-strength', pencilRenderDefaults.washStrength, 0, 1) / 0.48;
81
+ const washSpacing = readRenderToken(style, '--inkra-wash-spacing', pencilRenderDefaults.washSpacing, 1, 6);
82
+ const washRoughness = readRenderToken(style, '--inkra-wash-roughness', pencilRenderDefaults.washRoughness, 0, 6);
83
+ const washAngle = readRenderToken(style, '--inkra-wash-angle', pencilRenderDefaults.washAngle, -85, 85);
84
+ canvas.width = Math.ceil(cssWidth * dpr);
85
+ canvas.height = Math.ceil(cssHeight * dpr);
86
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
87
+ context.clearRect(0, 0, cssWidth, cssHeight);
88
+ const scaleX = cssWidth / width;
89
+ const scaleY = cssHeight / height;
90
+ const segments = [];
91
+ let segment = [];
92
+ boundedPoints.forEach(point => {
93
+ if (point.value === null) {
94
+ if (segment.length)
95
+ segments.push(segment);
96
+ segment = [];
97
+ return;
98
+ }
99
+ segment.push([geometry.x(point.index) * scaleX, geometry.y(point.value) * scaleY]);
100
+ });
101
+ if (segment.length)
102
+ segments.push(segment);
103
+ const seed = seedToNumber(`sparkline-${type}-${data.length}`);
104
+ if (type === 'line' && segments.some(points => points.length > 1)) {
105
+ if (showArea && typeof Path2D !== 'undefined' && geometry.areaPath) {
106
+ context.save();
107
+ context.scale(scaleX, scaleY);
108
+ context.clip(new Path2D(geometry.areaPath));
109
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
110
+ drawPencilWash(context, { x: 0, y: 0, width: cssWidth, height: cssHeight }, washColor, seed + 17, { strength: washStrength * 0.22, spacing: washSpacing, roughness: washRoughness, angle: washAngle, inset: 2, radius: 2, dpr });
111
+ context.restore();
112
+ }
113
+ segments.forEach((points, index) => {
114
+ if (points.length > 1)
115
+ pencilStroke(context, points, lineColor, strokeWidth, seed + index * 47, roughness, 2, false, { variation: 0.82, breaks: 0.25, grain: 0.62, gain: 2.1, step: 0.8 });
116
+ });
117
+ }
118
+ else if (type === 'bar') {
119
+ const baseline = geometry.y(0) * scaleY;
120
+ const barWidth = Math.max(2, cssWidth / Math.max(16, points.length * 2));
121
+ boundedPoints.forEach(point => {
122
+ if (point.value === null)
123
+ return;
124
+ const y = geometry.y(point.value) * scaleY;
125
+ drawPencilWash(context, { x: geometry.x(point.index) * scaleX - barWidth / 2, y: Math.min(y, baseline), width: barWidth, height: Math.max(2, Math.abs(baseline - y)) }, washColor, seed + point.index * 31, { strength: washStrength * 0.34, spacing: washSpacing, roughness: washRoughness, angle: washAngle, inset: 1, radius: 1, dpr });
126
+ pencilStroke(context, [[geometry.x(point.index) * scaleX - barWidth / 2, Math.min(y, baseline)], [geometry.x(point.index) * scaleX + barWidth / 2, Math.min(y, baseline)]], lineColor, strokeWidth * 0.55, seed + point.index * 43, roughness, 1, false, { variation: 0.8, breaks: 0.2, grain: 0.65, step: 0.8 });
127
+ });
128
+ }
129
+ if (!disposed)
130
+ setPencilReady(true);
131
+ };
132
+ const schedule = () => {
133
+ if (frame)
134
+ return;
135
+ frame = requestAnimationFrame(() => {
136
+ try {
137
+ paint();
138
+ }
139
+ catch {
140
+ canvas.width = 1;
141
+ canvas.height = 1;
142
+ setPencilReady(false);
143
+ }
144
+ });
145
+ };
146
+ const resize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(schedule);
147
+ resize?.observe(canvas);
148
+ const media = window.matchMedia('(prefers-color-scheme: dark)');
149
+ media.addEventListener('change', schedule);
150
+ const inheritedStyle = new MutationObserver(schedule);
151
+ for (let element = canvas; element; element = element.parentElement)
152
+ inheritedStyle.observe(element, { attributes: true, attributeFilter: ['style', 'class', 'data-theme', 'data-inkra-theme'] });
153
+ const stopRecovery = observeCanvasRecovery(() => { setPencilReady(false); schedule(); });
154
+ try {
155
+ paint();
156
+ }
157
+ catch {
158
+ canvas.width = 1;
159
+ canvas.height = 1;
160
+ setPencilReady(false);
161
+ }
162
+ return () => { disposed = true; setPencilReady(false); resize?.disconnect(); inheritedStyle.disconnect(); stopRecovery(); media.removeEventListener('change', schedule); cancelAnimationFrame(frame); };
163
+ }, [boundedPoints, data.length, geometry, height, showArea, type, values.length, width]);
164
+ useBrowserLayoutEffect(() => {
165
+ const canvas = interactionCanvasRef.current;
166
+ if (!canvas || typeof window === 'undefined')
167
+ return undefined;
168
+ let frame = 0;
169
+ const paint = () => {
170
+ frame = 0;
171
+ const cssWidth = canvas.clientWidth;
172
+ const cssHeight = canvas.clientHeight;
173
+ const context = canvas.getContext('2d');
174
+ const clearInteraction = () => { canvas.width = 1; canvas.height = 1; setInteractionReady(false); };
175
+ if (!context || cssWidth < 4 || cssHeight < 4 || !values.length) {
176
+ clearInteraction();
177
+ return;
178
+ }
179
+ const dpr = effectiveDpr(2);
180
+ const style = getComputedStyle(canvas);
181
+ const lineColor = resolveCssColor(canvas, style, '--inkra-accent-strong', style.color);
182
+ const strokeWidth = readRenderToken(style, '--inkra-stroke-width', pencilRenderDefaults.strokeWidth, 0.3, 3);
183
+ const roughness = readRenderToken(style, '--inkra-stroke-amplitude', pencilRenderDefaults.strokeRoughness, 0, 4);
184
+ canvas.width = Math.ceil(cssWidth * dpr);
185
+ canvas.height = Math.ceil(cssHeight * dpr);
186
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
187
+ context.clearRect(0, 0, cssWidth, cssHeight);
188
+ const scaleX = cssWidth / width;
189
+ const scaleY = cssHeight / height;
190
+ const seed = seedToNumber(`sparkline-interaction-${type}-${data.length}`);
191
+ if (type === 'line' && effectiveShowPoints)
192
+ boundedPoints.forEach(point => {
193
+ if (point.value === null)
194
+ return;
195
+ const x = geometry.x(point.index) * scaleX;
196
+ const y = geometry.y(point.value) * scaleY;
197
+ pencilStroke(context, [[x - 2.8, y], [x, y - 2.8], [x + 2.8, y], [x, y + 2.8], [x - 2.8, y]], lineColor, strokeWidth * 0.7, seed + point.index * 61, roughness, 1, true, { variation: 0.75, breaks: 0.12, grain: 0.62, gain: 1.8, step: 0.8 });
198
+ });
199
+ if (activePoint && activePoint.value !== null) {
200
+ const x = geometry.x(activePoint.index) * scaleX;
201
+ pencilStroke(context, [[x, 2], [x, cssHeight - 2]], lineColor, strokeWidth * 0.18, seed + 401, roughness, 1, false, { variation: 0.55, breaks: 0.45, grain: 0.5, gain: 1.1, step: 1.2 });
202
+ }
203
+ setInteractionReady(true);
204
+ };
205
+ const schedule = () => { if (!frame)
206
+ frame = requestAnimationFrame(() => { try {
207
+ paint();
208
+ }
209
+ catch {
210
+ canvas.width = 1;
211
+ canvas.height = 1;
212
+ setInteractionReady(false);
213
+ } }); };
214
+ const resize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(schedule);
215
+ resize?.observe(canvas);
216
+ const media = window.matchMedia('(prefers-color-scheme: dark)');
217
+ media.addEventListener('change', schedule);
218
+ const inheritedStyle = new MutationObserver(schedule);
219
+ for (let element = canvas; element; element = element.parentElement)
220
+ inheritedStyle.observe(element, { attributes: true, attributeFilter: ['style', 'class', 'data-theme', 'data-inkra-theme'] });
221
+ const stopRecovery = observeCanvasRecovery(() => { setInteractionReady(false); schedule(); });
222
+ try {
223
+ paint();
224
+ }
225
+ catch {
226
+ canvas.width = 1;
227
+ canvas.height = 1;
228
+ setInteractionReady(false);
229
+ }
230
+ return () => { setInteractionReady(false); resize?.disconnect(); inheritedStyle.disconnect(); stopRecovery(); media.removeEventListener('change', schedule); cancelAnimationFrame(frame); };
231
+ }, [activeIndex, activePoint, boundedPoints, data.length, effectiveShowPoints, geometry, height, type, values.length, width]);
232
+ return _jsxs("div", { ...props, ref: ref, className: `${classes}${pencilReady ? ' inkra-sparkline--pencil-ready' : ''}${interactionReady ? ' inkra-sparkline--interaction-ready' : ''}`, "data-empty": values.length === 0 || undefined, "data-show-points": effectiveShowPoints || undefined, children: [_jsxs("svg", { className: "inkra-sparkline__svg", viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": label, tabIndex: 0, onKeyDown: onKeyDown, children: [showArea && type === 'line' && geometry.areaPath && _jsx("path", { className: "inkra-sparkline__area", d: geometry.areaPath, "aria-hidden": "true" }), type === 'line' && geometry.linePath && _jsx("path", { className: "inkra-sparkline__line", d: geometry.linePath, "aria-hidden": "true" }), type === 'bar' && boundedPoints.map(point => {
233
+ if (point.value === null)
234
+ return null;
235
+ const baseline = geometry.y(0);
236
+ const valueY = geometry.y(point.value);
237
+ return _jsx("rect", { className: "inkra-sparkline__bar", x: geometry.x(point.index) - Math.max(2, width / Math.max(16, points.length * 2)) / 2, width: Math.max(2, width / Math.max(16, points.length * 2)), y: Math.min(baseline, valueY), height: Math.max(1, Math.abs(baseline - valueY)), "data-index": point.index, "data-active": activeIndex === point.index || undefined, onMouseEnter: () => setActiveIndex(point.index) }, point.index);
238
+ }), (showPoints || values.length === 1) && type === 'line' && boundedPoints.map(point => point.value === null ? null : _jsx("circle", { className: "inkra-sparkline__point", cx: geometry.x(point.index), cy: geometry.y(point.value), r: activeIndex === point.index ? 3.5 : 2, "data-active": activeIndex === point.index || undefined, onMouseEnter: () => setActiveIndex(point.index) }, point.index)), activePoint && activePoint.value !== null && _jsx("line", { className: "inkra-sparkline__guide", x1: geometry.x(activePoint.index), x2: geometry.x(activePoint.index), y1: 4, y2: height - 4, "aria-hidden": "true" })] }), _jsx("canvas", { ref: canvasRef, className: "inkra-sparkline__pencil inkra-sparkline__pencil--static", style: { aspectRatio: `${width} / ${height}` }, "aria-hidden": "true" }), _jsx("canvas", { ref: interactionCanvasRef, className: "inkra-sparkline__pencil inkra-sparkline__pencil--interaction", style: { aspectRatio: `${width} / ${height}` }, "aria-hidden": "true" }), activePoint && _jsx("div", { className: "inkra-sparkline__tooltip", "aria-hidden": "true", children: _jsx(PencilText, { children: getLabel(activePoint.value, activePoint.index) }) }), showSummary && _jsxs("p", { className: "inkra-sparkline__summary", children: [_jsxs("span", { className: "visually-hidden", children: [label, ": "] }), _jsx(PencilText, { seed: `sparkline-summary-${label}-${summary}`, children: summary })] }), _jsx("div", { className: "inkra-sparkline__data", "aria-label": `${label} data`, children: points.map(point => _jsx("span", { children: getLabel(point.value, point.index) }, point.index)) }), _jsxs("span", { className: "visually-hidden", children: ["Domain ", domain[0], " to ", domain[1]] })] });
239
+ });
240
+ export { createSparklineGeometry, normalizeSparklineData, sparklineDomain } from './model.js';
@@ -0,0 +1,16 @@
1
+ export type SparklineDatum = number | null | undefined;
2
+ export type SparklinePoint = {
3
+ index: number;
4
+ value: number | null;
5
+ };
6
+ export type SparklineScale = (value: number | {
7
+ valueOf(): number;
8
+ }) => number;
9
+ export declare function normalizeSparklineData(data: readonly SparklineDatum[]): SparklinePoint[];
10
+ export declare function sparklineDomain(points: readonly SparklinePoint[]): [number, number];
11
+ export declare function createSparklineGeometry(points: readonly SparklinePoint[], width: number, height: number, padding?: number): {
12
+ x: SparklineScale;
13
+ y: SparklineScale;
14
+ linePath: string;
15
+ areaPath: string;
16
+ };
@@ -0,0 +1,28 @@
1
+ import { scaleLinear } from 'd3-scale';
2
+ import { area, line } from 'd3-shape';
3
+ export function normalizeSparklineData(data) {
4
+ return data.map((value, index) => ({ index, value: typeof value === 'number' && Number.isFinite(value) ? value : null }));
5
+ }
6
+ export function sparklineDomain(points) {
7
+ const values = points.flatMap(point => point.value === null ? [] : [point.value]);
8
+ if (values.length === 0)
9
+ return [0, 1];
10
+ const min = Math.min(...values);
11
+ const max = Math.max(...values);
12
+ if (min === max) {
13
+ const pad = Math.abs(min) * 0.12 || 1;
14
+ return [min - pad, max + pad];
15
+ }
16
+ const pad = (max - min) * 0.08;
17
+ return [min - pad, max + pad];
18
+ }
19
+ export function createSparklineGeometry(points, width, height, padding = 4) {
20
+ const innerWidth = Math.max(1, width - padding * 2);
21
+ const innerHeight = Math.max(1, height - padding * 2);
22
+ const x = scaleLinear().domain([0, Math.max(1, points.length - 1)]).range([padding, padding + innerWidth]);
23
+ const y = scaleLinear().domain(sparklineDomain(points)).range([padding + innerHeight, padding]);
24
+ const defined = (point) => point.value !== null;
25
+ const linePath = line().defined(defined).x(point => x(point.index)).y(point => y(point.value));
26
+ const areaPath = area().defined(defined).x(point => x(point.index)).y0(padding + innerHeight).y1(point => y(point.value));
27
+ return { x, y, linePath: linePath(points) ?? '', areaPath: areaPath(points) ?? '' };
28
+ }
@@ -0,0 +1,16 @@
1
+ import { type OlHTMLAttributes, type ReactNode } from 'react';
2
+ export type StepStatus = 'upcoming' | 'current' | 'complete' | 'error';
3
+ export interface StepItem {
4
+ id: string;
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ status?: StepStatus;
8
+ disabled?: boolean;
9
+ }
10
+ export interface StepsProps extends Omit<OlHTMLAttributes<HTMLOListElement>, 'children'> {
11
+ current?: number;
12
+ items: readonly StepItem[];
13
+ onStepChange?: (index: number, item: StepItem) => void;
14
+ orientation?: 'horizontal' | 'vertical';
15
+ }
16
+ export declare const Steps: import("react").ForwardRefExoticComponent<StepsProps & import("react").RefAttributes<HTMLOListElement>>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { PencilIcon } from '../PencilIcon.js';
4
+ import { PencilLine } from '../PencilLine.js';
5
+ import { PencilSurface } from '../PencilSurface.js';
6
+ import { PencilText } from '../PencilText.js';
7
+ const markerPaths = {
8
+ upcoming: [{ d: 'M16 6a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z' }],
9
+ current: [{ d: 'M16 6a10 10 0 1 0 0 20 10 10 0 0 0 0-20Z' }, { d: 'M16 12a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z', mode: 'wash' }],
10
+ complete: [{ d: 'M6 17l6 6L27 8' }],
11
+ error: [{ d: 'M16 6a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM16 10v8M16 22v1' }],
12
+ };
13
+ const copy = (value) => typeof value === 'string' ? _jsx(PencilText, { children: value }) : value;
14
+ function resolvedStatus(item, index, current) {
15
+ if (item.status)
16
+ return item.status;
17
+ if (index < current)
18
+ return 'complete';
19
+ if (index === current)
20
+ return 'current';
21
+ return 'upcoming';
22
+ }
23
+ export const Steps = forwardRef(function Steps({ 'aria-label': ariaLabel = 'Progress', className, current = 0, items, onStepChange, orientation = 'horizontal', ...props }, ref) {
24
+ return _jsx("ol", { ...props, ref: ref, "aria-label": ariaLabel, className: ['inkra-steps', className].filter(Boolean).join(' '), "data-orientation": orientation, children: items.map((item, index) => {
25
+ const status = resolvedStatus(item, index, current);
26
+ const interactive = onStepChange !== undefined;
27
+ const content = _jsxs(_Fragment, { children: [status === 'current' && _jsx(PencilSurface, { "aria-hidden": "true", className: "inkra-steps__selection", variant: "selection", seed: `step-${item.id}` }), _jsx("span", { className: "inkra-steps__marker", "aria-hidden": "true", children: _jsx(PencilIcon, { paths: markerPaths[status], size: 28, sourceSize: 32, active: status === 'current' }) }), _jsxs("span", { className: "inkra-steps__copy", children: [_jsx("span", { className: "inkra-steps__title", children: copy(item.title) }), item.description != null && _jsx("span", { className: "inkra-steps__description", children: copy(item.description) })] })] });
28
+ const shared = {
29
+ className: 'inkra-steps__control',
30
+ 'aria-current': status === 'current' ? 'step' : undefined,
31
+ 'aria-disabled': item.disabled || undefined,
32
+ };
33
+ return _jsxs("li", { className: "inkra-steps__item", "data-status": status, "data-disabled": item.disabled || undefined, children: [interactive
34
+ ? _jsx("button", { ...shared, type: "button", disabled: item.disabled, onClick: () => onStepChange(index, item), children: content })
35
+ : _jsx("div", { ...shared, children: content }), index < items.length - 1 && _jsx(PencilLine, { className: "inkra-steps__connector", orientation: orientation === 'horizontal' ? 'horizontal' : 'vertical', seed: `step-line-${item.id}` })] }, item.id);
36
+ }) });
37
+ });