@inkra/react 0.8.0 → 0.9.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 (48) hide show
  1. package/README.md +89 -0
  2. package/dist/PencilIcon.d.ts +14 -1
  3. package/dist/PencilIcon.js +88 -52
  4. package/dist/guide/index.d.ts +77 -0
  5. package/dist/guide/index.js +379 -0
  6. package/dist/i18n/catalog.d.ts +1 -1
  7. package/dist/i18n/catalog.js +6 -0
  8. package/dist/image-viewer/index.d.ts +2 -0
  9. package/dist/image-viewer/index.js +28 -28
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.js +4 -0
  12. package/dist/infinite-scroll/gesture.d.ts +19 -0
  13. package/dist/infinite-scroll/gesture.js +218 -0
  14. package/dist/infinite-scroll/index.d.ts +55 -0
  15. package/dist/infinite-scroll/index.js +543 -0
  16. package/dist/infinite-scroll/model.d.ts +33 -0
  17. package/dist/infinite-scroll/model.js +168 -0
  18. package/dist/internal/iconFill.d.ts +16 -0
  19. package/dist/internal/iconFill.js +275 -0
  20. package/dist/inventory-grid/index.js +13 -3
  21. package/dist/inventory-grid/outline.d.ts +9 -0
  22. package/dist/inventory-grid/outline.js +54 -0
  23. package/dist/loading/index.d.ts +58 -0
  24. package/dist/loading/index.js +188 -0
  25. package/dist/loading/locales.d.ts +15 -0
  26. package/dist/loading/locales.js +34 -0
  27. package/dist/loading/model.d.ts +6 -0
  28. package/dist/loading/model.js +29 -0
  29. package/dist/masonry/index.d.ts +25 -0
  30. package/dist/masonry/index.js +227 -0
  31. package/dist/masonry/layout.d.ts +35 -0
  32. package/dist/masonry/layout.js +67 -0
  33. package/dist/number-ticker/RollingValue.d.ts +6 -0
  34. package/dist/number-ticker/RollingValue.js +10 -0
  35. package/dist/number-ticker/index.d.ts +4 -0
  36. package/dist/number-ticker/index.js +55 -7
  37. package/dist/number-ticker/rolling.d.ts +35 -0
  38. package/dist/number-ticker/rolling.js +87 -0
  39. package/dist/styles/guide.css +202 -0
  40. package/dist/styles/image-viewer.css +20 -0
  41. package/dist/styles/infinite-scroll.css +171 -0
  42. package/dist/styles/inventory-grid.css +20 -8
  43. package/dist/styles/loading.css +348 -0
  44. package/dist/styles/masonry.css +68 -0
  45. package/dist/styles/number-ticker.css +46 -0
  46. package/dist/styles.css +879 -8
  47. package/dist/toast/manager.d.ts +4 -4
  48. package/package.json +26 -1
package/README.md CHANGED
@@ -17,6 +17,52 @@ Inkra components require a React client boundary. Next.js and React Server Compo
17
17
 
18
18
  ## Usage
19
19
 
20
+ ### Guided And Asynchronous Experiences
21
+
22
+ `Guide.Root`, `Guide.Trigger` and `Guide.Step` compose a targeted walkthrough.
23
+ `showSkip` exposes skipping; `onSkip` is separate from `onFinish`, so leaving
24
+ early never reports completion. Controlled `open` and `step`, target resolution,
25
+ focus restoration and localized labels are available.
26
+
27
+ `Loading` supports `inline`, `region` and `fullscreen` carriers with six layout
28
+ presets: `brand-center`, `scene-bottom`, `intel`, `staged`, `edge` and `skeleton`.
29
+ Supply real progress or `null` for indeterminate progress. The application owns
30
+ `status` and the cancel/retry/continue actions. Fullscreen blocking and scroll
31
+ locking can be configured independently; the edge preset is non-blocking by
32
+ default. Backgrounds, logos, stages, six logical content slots and rotating
33
+ ReactNode tips share the same component. Tips support manual navigation, pause,
34
+ controlled indices and sequential or non-repeating random rotation.
35
+
36
+ `InfiniteScroll` loads at `top`, `bottom`, `left` or `right` boundaries. Configure
37
+ automatic boundary loading and touch pull-and-release separately per direction;
38
+ buttons remain available for keyboard use. `onLoad` receives an abort signal,
39
+ direction and source. Stable item identities support anchored prepends, while
40
+ errors, retry, exhausted directions and in-flight requests remain explicit.
41
+
42
+ `Masonry` arranges measured items in shortest-column or sequential order while
43
+ retaining DOM order. It supports adaptive or fixed columns, independent gaps,
44
+ RTL and dynamic item sizes. Supply unique stable keys, image alternatives and
45
+ your own data fetching, optionally through `InfiniteScroll`:
46
+
47
+ ```tsx
48
+ <InfiniteScroll
49
+ directions="bottom"
50
+ hasMore={hasMore}
51
+ onLoad={({ signal }) => fetchNextPage(signal)}
52
+ style={{ maxHeight: 640, overflow: 'auto' }}
53
+ >
54
+ <Masonry
55
+ items={items}
56
+ getItemKey={item => item.id}
57
+ minColumnWidth={220}
58
+ renderItem={({ item }) => <article>{item.content}</article>}
59
+ />
60
+ </InfiniteScroll>
61
+ ```
62
+
63
+ These APIs are included in this source candidate. Local browser checks are not
64
+ real-device certification or a claim that this candidate has been published.
65
+
20
66
  Import the component styles once near the application entry point:
21
67
 
22
68
  ```tsx
@@ -435,6 +481,22 @@ motion and SSR show deterministic final values. Intermediate frames are visual;
435
481
  the polite announcement updates only at the final value. Use `getValueLabel`
436
482
  when units or custom rendering need a complete accessible phrase.
437
483
 
484
+ Use `animation="roll"` for vertical, place-aligned digit reels. Integers align
485
+ from the right and fractions from the decimal point; carry, multiplication,
486
+ shrinking values and sign changes expand or collapse the affected columns.
487
+ Grouping, signs, currency and compact units remain formatted text, not digit
488
+ reels. `rollDirection="auto"` rolls up for increasing values and down for
489
+ decreasing values; `up` or `down` fixes the direction. `duration` and `easing`
490
+ apply to both modes. The default `animation="count"` retains value interpolation.
491
+ `renderValue` takes precedence over the built-in reels. Changing the formatting
492
+ or animation mode settles immediately, as do disabled/reduced motion, invalid
493
+ durations, and transitions with more than 128 formatted tokens. Exact strings
494
+ and bigint values retain their original precision in either mode.
495
+
496
+ ```tsx
497
+ <NumberTicker value={amount} animation="roll" rollDirection="auto" duration={650} />
498
+ ```
499
+
438
500
  Set `compact` to follow `locale` (Chinese units for `zh`, English units otherwise),
439
501
  or use `compact="en"` / `compact="zh"` to select a unit table explicitly.
440
502
  English uses this library's short-scale suffix convention: K, M, B, T, Qa, Qi,
@@ -546,6 +608,33 @@ The matching inherited tokens are `--inkra-icon-stroke-width`,
546
608
  and `--inkra-icon-break-count`. Finite explicit props override those tokens.
547
609
  See `docs/pencil-contract.md` in the workspace for the material contract.
548
610
 
611
+ Icons support `variant="outline"` (default) and `variant="filled"`. Filled icons
612
+ keep holes and internal marks transparent, join overlapping silhouettes and do
613
+ not close open arrows or checks into polygons. Set `fillColor` independently of
614
+ the contour's CSS `color`; it defaults to `currentColor` and accepts CSS variables.
615
+ The silhouette stays continuous while stroke controls affect its exterior.
616
+
617
+ ```tsx
618
+ <PencilIcon paths={folderPaths} variant="filled" fillColor="var(--folder-color)"
619
+ sourceSize={24} size={32} style={{ color: 'var(--inkra-ink)' }} />
620
+ ```
621
+
622
+ Supply `filledPaths` when a shape needs a dedicated filled silhouette. Each path
623
+ can use `mode: 'fill'` with an optional `color` for independently colored regions,
624
+ or `mode: 'cutout'` for transparent details. Closed cutouts remove an area; open
625
+ cutouts remove a stroke without implicitly closing it. Explicit fills accept
626
+ `fillRule: 'nonzero' | 'evenodd'` (default `nonzero`). These authored regions do
627
+ not add their own contour stroke. For lighter colored-pencil regions, use
628
+ `mode: 'wash'` with the same optional `color` and `fillRule`: paper remains visible
629
+ between pigment strokes, with a light pencil edge in the icon's CSS `color`.
630
+ Dense `fill` regions are unchanged.
631
+ Explicit `mode: 'stroke'` keeps a line symbol
632
+ even when its path closes; omitted mode detects filled silhouettes automatically.
633
+ An empty `filledPaths` intentionally hides the
634
+ base shape in filled mode. `badge` accepts the same three props and inherits the
635
+ base variant and fill color unless overridden. Intensity applies once to the
636
+ completed image, including its fills, exterior strokes and badge.
637
+
549
638
  `PencilLine` is an aria-hidden canvas decoration using the same pressure, grain, breaks, and repeated-pass renderer as surfaces and icons. It defaults to horizontal and supports a native vertical stroke without rotating a horizontal bitmap:
550
639
 
551
640
  ```tsx
@@ -1,10 +1,17 @@
1
1
  import type { CanvasHTMLAttributes } from 'react';
2
2
  export interface PencilIconPath {
3
3
  d: string;
4
- mode?: 'stroke' | 'wash' | 'scuff';
4
+ /** Filled icons: stroke stays a line, wash exposes paper, fill is dense; omitted mode detects closed silhouettes. */
5
+ mode?: 'stroke' | 'wash' | 'scuff' | 'fill' | 'cutout';
6
+ /** Optional pigment for an explicit fill region, including CSS custom properties. */
7
+ color?: string;
8
+ fillRule?: CanvasFillRule;
5
9
  }
6
10
  export interface PencilIconBadge {
7
11
  paths: readonly PencilIconPath[];
12
+ filledPaths?: readonly PencilIconPath[];
13
+ variant?: 'outline' | 'filled';
14
+ fillColor?: string;
8
15
  sourceSize?: number;
9
16
  position?: 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
10
17
  /** Relative canvas size, clamped to 0.35-0.65. Defaults to 0.55. */
@@ -12,6 +19,12 @@ export interface PencilIconBadge {
12
19
  }
13
20
  export interface PencilIconProps extends Omit<CanvasHTMLAttributes<HTMLCanvasElement>, 'children' | 'height' | 'width'> {
14
21
  paths: readonly PencilIconPath[];
22
+ /** Filled icons retain open symbols and use negative space for interior details. */
23
+ variant?: 'outline' | 'filled';
24
+ /** Optional authored geometry for shapes whose filled silhouette differs from the outline. */
25
+ filledPaths?: readonly PencilIconPath[];
26
+ /** Body pigment, independent of the CSS color used for the contour. Defaults to currentColor. */
27
+ fillColor?: string;
15
28
  badge?: PencilIconBadge;
16
29
  seed?: string | number;
17
30
  size?: number;
@@ -6,6 +6,7 @@ import { seedToNumber } from './internal/random.js';
6
6
  import { pencilStroke } from './internal/stroke.js';
7
7
  import { maxStrokeRoughness, pencilRenderDefaults, readRenderToken } from './internal/renderTokens.js';
8
8
  import { cachedPathParts } from './internal/iconGeometry.js';
9
+ import { paintFilledIcon } from './internal/iconFill.js';
9
10
  const imageCache = new BoundedCache(128, 24 * 1024 * 1024);
10
11
  function drawFallbackPath(context, data, color, mode, weightScale = 1) {
11
12
  try {
@@ -47,56 +48,62 @@ function renderIcon(paths, options) {
47
48
  const offset = (40 - sourceSize) / 2;
48
49
  context.translate(offset, offset);
49
50
  let sampledPoints = 0;
50
- paths.forEach((path, index) => {
51
- if (path.d.length > 65536)
52
- return;
53
- const mode = path.mode ?? 'stroke';
54
- const pathSeed = seed + index * 137;
55
- if (mode === 'wash') {
51
+ if (options.variant === 'filled') {
52
+ paintFilledIcon(context, paths, { color, fillColor: options.fillColor ?? color, sourceSize, seed, weightScale, roughnessScale, breaks, breakCount, pressureVariation, coordinateScale });
53
+ }
54
+ else
55
+ paths.forEach((path, index) => {
56
+ if (path.d.length > 65536)
57
+ return;
58
+ const mode = path.mode ?? 'stroke';
59
+ if (mode === 'cutout')
60
+ return;
61
+ const pathSeed = seed + index * 137;
62
+ if (mode === 'wash' || mode === 'fill') {
63
+ try {
64
+ context.save();
65
+ context.clip(new Path2D(path.d));
66
+ context.globalAlpha = active ? 0.76 : 0.34;
67
+ for (let y = sourceSize * 0.18; y < sourceSize; y += 1.6) {
68
+ pencilStroke(context, [[sourceSize * 0.15, y + 3], [sourceSize * 0.85, y - 3]], color, 1.1 * weightScale / (pencilRenderDefaults.strokeWidth / 1.2), pathSeed + Math.round(y * 17), 0.25, 2, false, { variation: Math.min(1, 0.85 * variationScale), fitPressure: true, breaks, breakCount, breakScale: coordinateScale, continuousBreaks: true, grain: 0.7, gain: 2.2, step: 0.35 });
69
+ }
70
+ context.restore();
71
+ }
72
+ catch {
73
+ drawFallbackPath(context, path.d, color, mode, weightScale);
74
+ }
75
+ return;
76
+ }
56
77
  try {
57
- context.save();
58
- context.clip(new Path2D(path.d));
59
- context.globalAlpha = active ? 0.76 : 0.34;
60
- for (let y = sourceSize * 0.18; y < sourceSize; y += 1.6) {
61
- pencilStroke(context, [[sourceSize * 0.15, y + 3], [sourceSize * 0.85, y - 3]], color, 1.1 * weightScale / (pencilRenderDefaults.strokeWidth / 1.2), pathSeed + Math.round(y * 17), 0.25, 2, false, { variation: Math.min(1, 0.85 * variationScale), fitPressure: true, breaks, breakCount, breakScale: coordinateScale, continuousBreaks: true, grain: 0.7, gain: 2.2, step: 0.35 });
78
+ if (sampledPoints >= 65536) {
79
+ drawFallbackPath(context, path.d, color, mode, weightScale);
80
+ return;
81
+ }
82
+ const parts = cachedPathParts(path.d);
83
+ sampledPoints += parts.reduce((total, part) => total + part.length, 0);
84
+ if (sampledPoints > 65536) {
85
+ drawFallbackPath(context, path.d, color, mode, weightScale);
86
+ return;
87
+ }
88
+ for (const points of parts) {
89
+ pencilStroke(context, points, color, (mode === 'scuff' ? 0.55 : 1.65) * weightScale, pathSeed, 0.3 * roughnessScale, mode === 'scuff' ? 1 : 3, false, {
90
+ variation: pressureVariation,
91
+ fitPressure: true,
92
+ breaks,
93
+ breakCount,
94
+ breakScale: coordinateScale,
95
+ continuousBreaks: true,
96
+ grain: 0.68,
97
+ gain: mode === 'scuff' ? 1.5 : 3,
98
+ step: 0.35,
99
+ taperLength: 0.65,
100
+ });
62
101
  }
63
- context.restore();
64
102
  }
65
103
  catch {
66
104
  drawFallbackPath(context, path.d, color, mode, weightScale);
67
105
  }
68
- return;
69
- }
70
- try {
71
- if (sampledPoints >= 65536) {
72
- drawFallbackPath(context, path.d, color, mode, weightScale);
73
- return;
74
- }
75
- const parts = cachedPathParts(path.d);
76
- sampledPoints += parts.reduce((total, part) => total + part.length, 0);
77
- if (sampledPoints > 65536) {
78
- drawFallbackPath(context, path.d, color, mode, weightScale);
79
- return;
80
- }
81
- for (const points of parts) {
82
- pencilStroke(context, points, color, (mode === 'scuff' ? 0.55 : 1.65) * weightScale, pathSeed, 0.3 * roughnessScale, mode === 'scuff' ? 1 : 3, false, {
83
- variation: pressureVariation,
84
- fitPressure: true,
85
- breaks,
86
- breakCount,
87
- breakScale: coordinateScale,
88
- continuousBreaks: true,
89
- grain: 0.68,
90
- gain: mode === 'scuff' ? 1.5 : 3,
91
- step: 0.35,
92
- taperLength: 0.65,
93
- });
94
- }
95
- }
96
- catch {
97
- drawFallbackPath(context, path.d, color, mode, weightScale);
98
- }
99
- });
106
+ });
100
107
  if (active) {
101
108
  pencilStroke(context, [[sourceSize * 0.2, sourceSize - 2], [sourceSize * 0.5, sourceSize - 1.5], [sourceSize * 0.8, sourceSize - 2.2]], color, 1.1 * weightScale, seed + 977, 0.25 * roughnessScale, 3, false, { variation: 0.8 * variationScale, fitPressure: true, breaks, breakCount, breakScale: coordinateScale, continuousBreaks: true, grain: 0.7, gain: 2.8, step: 0.35 });
102
109
  }
@@ -109,8 +116,10 @@ function renderIcon(paths, options) {
109
116
  const extent = 40 * scale;
110
117
  const x = onLeft ? 0 : 40 - extent;
111
118
  const y = position.startsWith('top') ? 0 : 40 - extent;
112
- const badgeImage = renderIcon(badge.paths, {
119
+ const badgeVariant = badge.variant ?? options.variant;
120
+ const badgeImage = renderIcon(badgeVariant === 'filled' ? badge.filledPaths ?? badge.paths : badge.paths, {
113
121
  seed: seed + 1543, size: size * scale, sourceSize: badgeSourceSize, active: false, color, dpr,
122
+ variant: badgeVariant, fillColor: badge.fillColor ?? options.fillColor,
114
123
  weightScale: weightScale / scale, roughnessScale, breaks, breakCount, pressureVariation,
115
124
  });
116
125
  const mask = document.createElement('canvas');
@@ -140,17 +149,18 @@ function renderIcon(paths, options) {
140
149
  }
141
150
  return canvas;
142
151
  }
143
- export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge, className = '', decorative = true, paths, seed, size = 40, sourceSize = 32, strokeWidth, intensity, breaks, breakCount, pressureVariation, ...props }, forwardedRef) {
152
+ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge, className = '', decorative = true, paths, variant = 'outline', filledPaths, fillColor, seed, size = 40, sourceSize = 32, strokeWidth, intensity, breaks, breakCount, pressureVariation, ...props }, forwardedRef) {
144
153
  const canvasRef = useRef(null);
145
154
  const lastPaintRef = useRef('');
146
155
  useImperativeHandle(forwardedRef, () => canvasRef.current);
147
156
  // Compare values, not caller object identity. Serializing each render also
148
157
  // detects in-place edits; the effect receives a stable snapshot of that value.
149
- const geometryKey = JSON.stringify(paths);
158
+ const originalGeometryKey = JSON.stringify(paths);
159
+ const geometryKey = variant === 'filled' && filledPaths ? JSON.stringify(filledPaths) : originalGeometryKey;
150
160
  const badgeKey = badge ? JSON.stringify(badge) : '';
151
161
  const stablePaths = useMemo(() => JSON.parse(geometryKey), [geometryKey]);
152
162
  const stableBadge = useMemo(() => badgeKey ? JSON.parse(badgeKey) : undefined, [badgeKey]);
153
- const resolvedSeed = seedToNumber(seed ?? geometryKey);
163
+ const resolvedSeed = seedToNumber(seed ?? originalGeometryKey);
154
164
  useEffect(() => {
155
165
  const canvas = canvasRef.current;
156
166
  if (!canvas)
@@ -162,6 +172,28 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
162
172
  const dpr = effectiveDpr(3);
163
173
  const computed = getComputedStyle(canvas);
164
174
  const color = computed.color;
175
+ const resolveColor = (value) => {
176
+ if (!value || value === 'currentColor')
177
+ return color;
178
+ const probe = document.createElement('span');
179
+ probe.style.color = value;
180
+ // Canvas fallback children inherit its own color and custom properties, not just its parent's.
181
+ canvas.appendChild(probe);
182
+ try {
183
+ return getComputedStyle(probe).color || color;
184
+ }
185
+ finally {
186
+ probe.remove();
187
+ }
188
+ };
189
+ const resolvedFill = variant === 'filled' ? resolveColor(fillColor) : color;
190
+ const resolvePaths = (items) => items.map(path => path.color ? { ...path, color: resolveColor(path.color) } : path);
191
+ const renderedPaths = variant === 'filled' ? resolvePaths(stablePaths) : stablePaths;
192
+ const renderedBadge = stableBadge ? { ...stableBadge,
193
+ fillColor: resolveColor(stableBadge.fillColor ?? fillColor),
194
+ paths: resolvePaths(stableBadge.paths),
195
+ filledPaths: stableBadge.filledPaths ? resolvePaths(stableBadge.filledPaths) : undefined,
196
+ } : undefined;
165
197
  const rtl = computed.direction === 'rtl';
166
198
  const iconWeight = readRenderToken(computed, '--inkra-icon-stroke-width', pencilRenderDefaults.iconStrokeWidth, 0.3, 3);
167
199
  const weightScale = (typeof strokeWidth === 'number' && Number.isFinite(strokeWidth) ? Math.min(3, Math.max(0.3, strokeWidth)) : iconWeight) / 1.2;
@@ -180,9 +212,10 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
180
212
  : readRenderToken(computed, '--inkra-icon-break-count', pencilRenderDefaults.iconBreakCount, 0, 12));
181
213
  const key = JSON.stringify([
182
214
  PENCIL_RENDERER_VERSION,
183
- 'icon-path-pressure-gaps-v4',
215
+ 'icon-path-paper-wash-v8',
184
216
  geometryKey,
185
217
  badgeKey,
218
+ variant, resolvedFill, renderedPaths, renderedBadge,
186
219
  rtl,
187
220
  resolvedSeed,
188
221
  size,
@@ -202,7 +235,7 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
202
235
  image = undefined;
203
236
  }
204
237
  if (!image) {
205
- image = renderIcon(stablePaths, { seed: resolvedSeed, size, sourceSize, active, color, dpr, badge: stableBadge, rtl, weightScale, roughnessScale, breaks: strokeBreaks, breakCount: strokeBreakCount, pressureVariation: strokeVariation });
238
+ image = renderIcon(renderedPaths, { seed: resolvedSeed, size, sourceSize, active, color, dpr, variant, fillColor: resolvedFill, badge: renderedBadge, rtl, weightScale, roughnessScale, breaks: strokeBreaks, breakCount: strokeBreakCount, pressureVariation: strokeVariation });
206
239
  imageCache.set(key, image, canvasByteCost(image));
207
240
  }
208
241
  canvas.width = image.width;
@@ -236,7 +269,9 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
236
269
  inheritedColor.observe(element, { attributes: true, attributeFilter: ['style', 'class', 'dir', 'data-theme', 'data-inkra-theme'] });
237
270
  }
238
271
  const colorScheme = window.matchMedia('(prefers-color-scheme: dark)');
272
+ const forcedColors = window.matchMedia('(forced-colors: active)');
239
273
  colorScheme.addEventListener('change', paint);
274
+ forcedColors.addEventListener('change', paint);
240
275
  const stopRecovery = observeCanvasRecovery(() => { lastPaintRef.current = ''; paint(); });
241
276
  paint();
242
277
  return () => {
@@ -245,10 +280,11 @@ export const PencilIcon = forwardRef(function PencilIcon({ active = false, badge
245
280
  stopRecovery();
246
281
  inheritedColor.disconnect();
247
282
  colorScheme.removeEventListener('change', paint);
283
+ forcedColors.removeEventListener('change', paint);
248
284
  };
249
- }, [active, stableBadge, badgeKey, geometryKey, stablePaths, resolvedSeed, size, sourceSize, strokeWidth, intensity, breaks, breakCount, pressureVariation]);
285
+ }, [active, stableBadge, badgeKey, geometryKey, stablePaths, resolvedSeed, size, sourceSize, strokeWidth, intensity, breaks, breakCount, pressureVariation, variant, fillColor]);
250
286
  const accessibilityProps = decorative
251
287
  ? { 'aria-hidden': true }
252
288
  : { role: props.role ?? 'img' };
253
- return (_jsx("canvas", { ...props, ...accessibilityProps, className: ['inkra-pencil-icon', className].filter(Boolean).join(' '), "data-active": active || undefined, "data-composite": Boolean(badge?.paths.length) || undefined, "data-renderer": "inkra-pressure-pencil", ref: canvasRef, style: { width: size, height: size, ...props.style } }));
289
+ return (_jsx("canvas", { ...props, ...accessibilityProps, className: ['inkra-pencil-icon', className].filter(Boolean).join(' '), "data-active": active || undefined, "data-icon-variant": variant, "data-composite": Boolean(badge?.paths.length) || undefined, "data-renderer": "inkra-pressure-pencil", ref: canvasRef, style: { width: size, height: size, ...props.style } }));
254
290
  });
@@ -0,0 +1,77 @@
1
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
2
+ import type { ReactNode, RefObject } from 'react';
3
+ type GuideSide = 'top' | 'right' | 'bottom' | 'left';
4
+ type GuideAlign = 'start' | 'center' | 'end';
5
+ export type GuideSkipPlacement = 'panel' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
6
+ export type GuideSkipAppearance = 'icon' | 'text';
7
+ export type GuideTarget = string | HTMLElement | RefObject<HTMLElement | null> | (() => HTMLElement | null);
8
+ export interface GuideStepProps {
9
+ /** Stable identifier reported by callbacks. */
10
+ id?: string;
11
+ /** A selector, element, ref, or resolver. Missing targets fall back to the viewport. */
12
+ target: GuideTarget;
13
+ title: string;
14
+ description?: ReactNode;
15
+ children?: ReactNode;
16
+ side?: GuideSide;
17
+ align?: GuideAlign;
18
+ sideOffset?: number;
19
+ collisionPadding?: number;
20
+ }
21
+ export declare function GuideStep(_props: GuideStepProps): null;
22
+ export declare namespace GuideStep {
23
+ var displayName: string;
24
+ }
25
+ export type GuideOpenChangeReason = 'trigger' | 'close' | 'escape' | 'finish' | 'skip';
26
+ export interface GuideOpenChangeDetails {
27
+ reason: GuideOpenChangeReason;
28
+ step: number;
29
+ stepId?: string;
30
+ }
31
+ export interface GuideStepChangeDetails {
32
+ reason: 'next' | 'previous';
33
+ previousStep: number;
34
+ stepId?: string;
35
+ }
36
+ export interface GuideLabels {
37
+ previous: string;
38
+ next: string;
39
+ finish: string;
40
+ skip: string;
41
+ close: string;
42
+ /** Supports {current} and {total}. */
43
+ progress: string;
44
+ }
45
+ export interface GuideRootProps {
46
+ children?: ReactNode;
47
+ open?: boolean;
48
+ defaultOpen?: boolean;
49
+ onOpenChange?: (open: boolean, details: GuideOpenChangeDetails) => void;
50
+ step?: number;
51
+ defaultStep?: number;
52
+ onStepChange?: (step: number, details: GuideStepChangeDetails) => void;
53
+ onFinish?: () => void;
54
+ onSkip?: () => void;
55
+ showSkip?: boolean;
56
+ skipPlacement?: GuideSkipPlacement;
57
+ skipAppearance?: GuideSkipAppearance;
58
+ showClose?: boolean;
59
+ closeOnEscape?: boolean;
60
+ scrollIntoView?: boolean | ScrollIntoViewOptions;
61
+ restoreFocus?: boolean;
62
+ highlightPadding?: number;
63
+ highlightRadius?: number;
64
+ locale?: string;
65
+ labels?: Partial<GuideLabels>;
66
+ portalContainer?: HTMLElement | null;
67
+ className?: string;
68
+ }
69
+ export type GuideTriggerProps = PopoverPrimitive.PopoverTriggerProps;
70
+ export declare const GuideTrigger: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
71
+ export declare function GuideRoot({ children, open, defaultOpen, onOpenChange, step, defaultStep, onStepChange, onFinish, onSkip, showSkip, skipPlacement, skipAppearance, showClose, closeOnEscape, scrollIntoView, restoreFocus, highlightPadding, highlightRadius, locale, labels, portalContainer, className, }: GuideRootProps): import("react").JSX.Element;
72
+ export declare const Guide: Readonly<{
73
+ Root: typeof GuideRoot;
74
+ Trigger: import("react").ForwardRefExoticComponent<PopoverPrimitive.PopoverTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
75
+ Step: typeof GuideStep;
76
+ }>;
77
+ export {};