@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.
- package/README.md +89 -0
- package/dist/PencilIcon.d.ts +14 -1
- package/dist/PencilIcon.js +88 -52
- package/dist/guide/index.d.ts +77 -0
- package/dist/guide/index.js +379 -0
- package/dist/i18n/catalog.d.ts +1 -1
- package/dist/i18n/catalog.js +6 -0
- package/dist/image-viewer/index.d.ts +2 -0
- package/dist/image-viewer/index.js +28 -28
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/infinite-scroll/gesture.d.ts +19 -0
- package/dist/infinite-scroll/gesture.js +218 -0
- package/dist/infinite-scroll/index.d.ts +55 -0
- package/dist/infinite-scroll/index.js +543 -0
- package/dist/infinite-scroll/model.d.ts +33 -0
- package/dist/infinite-scroll/model.js +168 -0
- package/dist/internal/iconFill.d.ts +16 -0
- package/dist/internal/iconFill.js +275 -0
- package/dist/inventory-grid/index.js +13 -3
- package/dist/inventory-grid/outline.d.ts +9 -0
- package/dist/inventory-grid/outline.js +54 -0
- package/dist/loading/index.d.ts +58 -0
- package/dist/loading/index.js +188 -0
- package/dist/loading/locales.d.ts +15 -0
- package/dist/loading/locales.js +34 -0
- package/dist/loading/model.d.ts +6 -0
- package/dist/loading/model.js +29 -0
- package/dist/masonry/index.d.ts +25 -0
- package/dist/masonry/index.js +227 -0
- package/dist/masonry/layout.d.ts +35 -0
- package/dist/masonry/layout.js +67 -0
- package/dist/number-ticker/RollingValue.d.ts +6 -0
- package/dist/number-ticker/RollingValue.js +10 -0
- package/dist/number-ticker/index.d.ts +4 -0
- package/dist/number-ticker/index.js +55 -7
- package/dist/number-ticker/rolling.d.ts +35 -0
- package/dist/number-ticker/rolling.js +87 -0
- package/dist/styles/guide.css +202 -0
- package/dist/styles/image-viewer.css +20 -0
- package/dist/styles/infinite-scroll.css +171 -0
- package/dist/styles/inventory-grid.css +20 -8
- package/dist/styles/loading.css +348 -0
- package/dist/styles/masonry.css +68 -0
- package/dist/styles/number-ticker.css +46 -0
- package/dist/styles.css +879 -8
- package/dist/toast/manager.d.ts +4 -4
- 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
|
package/dist/PencilIcon.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import type { CanvasHTMLAttributes } from 'react';
|
|
2
2
|
export interface PencilIconPath {
|
|
3
3
|
d: string;
|
|
4
|
-
|
|
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;
|
package/dist/PencilIcon.js
CHANGED
|
@@ -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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ??
|
|
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-
|
|
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(
|
|
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 {};
|