@inkra/react 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +213 -2
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/Yozai-OFL.txt +94 -0
- package/dist/Button.d.ts +36 -0
- package/dist/Button.js +89 -0
- package/dist/PencilIcon.d.ts +22 -0
- package/dist/PencilIcon.js +212 -0
- package/dist/PencilLine.d.ts +10 -0
- package/dist/PencilLine.js +85 -0
- package/dist/PencilSurface.d.ts +19 -0
- package/dist/PencilSurface.js +19 -0
- package/dist/PencilText.d.ts +16 -0
- package/dist/PencilText.js +205 -0
- package/dist/accordion/Accordion.d.ts +27 -0
- package/dist/accordion/Accordion.js +40 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/assets/ui-hand-yozai.woff2 +0 -0
- package/dist/breadcrumb/index.d.ts +16 -0
- package/dist/breadcrumb/index.js +25 -0
- package/dist/calendar/index.d.ts +21 -0
- package/dist/calendar/index.js +41 -0
- package/dist/content/index.d.ts +106 -0
- package/dist/content/index.js +80 -0
- package/dist/day-agenda/index.d.ts +43 -0
- package/dist/day-agenda/index.js +47 -0
- package/dist/day-agenda/layout.d.ts +20 -0
- package/dist/day-agenda/layout.js +60 -0
- package/dist/display/index.d.ts +32 -0
- package/dist/display/index.js +59 -0
- package/dist/dropdown-menu/index.d.ts +34 -0
- package/dist/dropdown-menu/index.js +40 -0
- package/dist/form/EditablePencilText.d.ts +13 -0
- package/dist/form/EditablePencilText.js +312 -0
- package/dist/form/Field.d.ts +13 -0
- package/dist/form/Field.js +26 -0
- package/dist/form/Input.d.ts +27 -0
- package/dist/form/Input.js +97 -0
- package/dist/form/InputAction.d.ts +7 -0
- package/dist/form/InputAction.js +24 -0
- package/dist/form/Textarea.d.ts +5 -0
- package/dist/form/Textarea.js +23 -0
- package/dist/form/context.d.ts +12 -0
- package/dist/form/context.js +10 -0
- package/dist/form/controlProps.d.ts +23 -0
- package/dist/form/controlProps.js +20 -0
- package/dist/form/index.d.ts +7 -0
- package/dist/form/index.js +3 -0
- package/dist/form/types.d.ts +6 -0
- package/dist/form/types.js +1 -0
- package/dist/images/layers-2x.png +0 -0
- package/dist/images/layers.png +0 -0
- package/dist/images/marker-icon.png +0 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +22 -0
- package/dist/input-number/index.d.ts +16 -0
- package/dist/input-number/index.js +77 -0
- package/dist/internal/InputActionContext.d.ts +1 -0
- package/dist/internal/InputActionContext.js +3 -0
- package/dist/internal/cache.d.ts +14 -0
- package/dist/internal/cache.js +54 -0
- package/dist/internal/line.d.ts +4 -0
- package/dist/internal/line.js +21 -0
- package/dist/internal/paper.d.ts +1 -0
- package/dist/internal/paper.js +65 -0
- package/dist/internal/random.d.ts +3 -0
- package/dist/internal/random.js +30 -0
- package/dist/internal/recovery.d.ts +1 -0
- package/dist/internal/recovery.js +52 -0
- package/dist/internal/stroke.d.ts +24 -0
- package/dist/internal/stroke.js +144 -0
- package/dist/internal/surfaceRenderer.d.ts +33 -0
- package/dist/internal/surfaceRenderer.js +175 -0
- package/dist/internal/textQueue.d.ts +1 -0
- package/dist/internal/textQueue.js +37 -0
- package/dist/internal/textRenderer.d.ts +27 -0
- package/dist/internal/textRenderer.js +213 -0
- package/dist/internal/travelIcons.d.ts +24 -0
- package/dist/internal/travelIcons.js +14 -0
- package/dist/internal/useBrowserLayoutEffect.d.ts +2 -0
- package/dist/internal/useBrowserLayoutEffect.js +3 -0
- package/dist/internal/usePencilSurface.d.ts +3 -0
- package/dist/internal/usePencilSurface.js +108 -0
- package/dist/internal/useSelectionDrawing.d.ts +2 -0
- package/dist/internal/useSelectionDrawing.js +86 -0
- package/dist/internal/wash.d.ts +16 -0
- package/dist/internal/wash.js +149 -0
- package/dist/journey-map/MapScale.d.ts +4 -0
- package/dist/journey-map/MapScale.js +52 -0
- package/dist/journey-map/index.d.ts +39 -0
- package/dist/journey-map/index.js +195 -0
- package/dist/journey-map/terrain.d.ts +8 -0
- package/dist/journey-map/terrain.js +127 -0
- package/dist/overlay/Overlay.d.ts +65 -0
- package/dist/overlay/Overlay.js +77 -0
- package/dist/overlay/Popover.d.ts +43 -0
- package/dist/overlay/Popover.js +45 -0
- package/dist/overlay/index.d.ts +4 -0
- package/dist/overlay/index.js +2 -0
- package/dist/overlay/themeBridge.d.ts +8 -0
- package/dist/overlay/themeBridge.js +98 -0
- package/dist/pencil.d.ts +10 -0
- package/dist/pencil.js +39 -0
- package/dist/select/MultipleSelect.d.ts +44 -0
- package/dist/select/MultipleSelect.js +124 -0
- package/dist/select/SearchableSelect.d.ts +45 -0
- package/dist/select/SearchableSelect.js +50 -0
- package/dist/select/Select.d.ts +109 -0
- package/dist/select/Select.js +205 -0
- package/dist/select/index.d.ts +4 -0
- package/dist/select/index.js +2 -0
- package/dist/selection/Checkbox.d.ts +9 -0
- package/dist/selection/Checkbox.js +37 -0
- package/dist/selection/RadioGroup.d.ts +23 -0
- package/dist/selection/RadioGroup.js +82 -0
- package/dist/selection/Switch.d.ts +8 -0
- package/dist/selection/Switch.js +36 -0
- package/dist/selection/index.d.ts +6 -0
- package/dist/selection/index.js +3 -0
- package/dist/selection/useNativeChecked.d.ts +13 -0
- package/dist/selection/useNativeChecked.js +27 -0
- package/dist/slider/index.d.ts +11 -0
- package/dist/slider/index.js +45 -0
- package/dist/styles.css +3663 -0
- package/dist/tabs/index.d.ts +22 -0
- package/dist/tabs/index.js +42 -0
- package/dist/tokens.css +355 -0
- package/dist/tooltip/Tooltip.d.ts +24 -0
- package/dist/tooltip/Tooltip.js +32 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/package.json +100 -4
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
function hash(index, seed) {
|
|
2
|
+
let value = Math.imul(index ^ seed, 0x45d9f3b);
|
|
3
|
+
value = Math.imul(value ^ (value >>> 16), 0x45d9f3b);
|
|
4
|
+
return ((value ^ (value >>> 16)) >>> 0) / 4294967296;
|
|
5
|
+
}
|
|
6
|
+
function noise(at, seed) {
|
|
7
|
+
const cell = Math.floor(at);
|
|
8
|
+
const progress = at - cell;
|
|
9
|
+
const blend = progress * progress * (3 - 2 * progress);
|
|
10
|
+
return hash(cell, seed) * (1 - blend) + hash(cell + 1, seed) * blend;
|
|
11
|
+
}
|
|
12
|
+
const clamp = (value) => Math.max(0, Math.min(1, value));
|
|
13
|
+
export function strokeProfile(at, total, seed, settings = {}) {
|
|
14
|
+
const variation = settings.variation ?? 1;
|
|
15
|
+
const breaks = settings.breaks ?? 0.8;
|
|
16
|
+
const grainValue = settings.grain ?? 0.7;
|
|
17
|
+
const force = clamp(0.12 + noise(at / 19, seed + 17) * 0.97
|
|
18
|
+
+ (noise(at / 5.3, seed + 39) - 0.5) * 0.3);
|
|
19
|
+
const pressure = clamp(0.72 * (1 - variation) + force * variation);
|
|
20
|
+
let contact = 1;
|
|
21
|
+
const cell = Math.floor(at / 37);
|
|
22
|
+
for (let index = cell - 1; index <= cell + 1; index += 1) {
|
|
23
|
+
if (hash(index, seed + 277) > breaks * 0.9)
|
|
24
|
+
continue;
|
|
25
|
+
const middle = index * 37 + 5 + hash(index, seed + 283) * 27;
|
|
26
|
+
const halfGap = (1.1 + hash(index, seed + 293) * 3.6) * breaks;
|
|
27
|
+
contact = Math.min(contact, clamp((Math.abs(at - middle) - halfGap) / 2.1));
|
|
28
|
+
}
|
|
29
|
+
const tooth = noise(at * 1.6, seed + 401);
|
|
30
|
+
const deposit = clamp((tooth - grainValue * 0.22) / Math.max(0.2, 1 - grainValue * 0.22));
|
|
31
|
+
const taper = 0.2 + 0.8 * clamp(Math.min(at, total - at) / (settings.taperLength ?? 3));
|
|
32
|
+
return { pressure, contact, deposit, taper };
|
|
33
|
+
}
|
|
34
|
+
export function pencilStroke(context, points, color, width, seed, amplitude, passes = 2, closed = false, settings = {}) {
|
|
35
|
+
if (points.length < 2)
|
|
36
|
+
return;
|
|
37
|
+
const scale = Math.max(0.4, settings.scale ?? 1);
|
|
38
|
+
const line = points.map(point => [...point]);
|
|
39
|
+
const lastPoint = line[line.length - 1];
|
|
40
|
+
if (closed && Math.hypot(line[0][0] - lastPoint[0], line[0][1] - lastPoint[1]) > 0.01) {
|
|
41
|
+
line.push(line[0]);
|
|
42
|
+
}
|
|
43
|
+
const segments = [];
|
|
44
|
+
let total = 0;
|
|
45
|
+
for (let index = 1; index < line.length; index += 1) {
|
|
46
|
+
const a = line[index - 1];
|
|
47
|
+
const b = line[index];
|
|
48
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1]) / scale;
|
|
49
|
+
if (length < 0.0001)
|
|
50
|
+
continue;
|
|
51
|
+
segments.push({ a, b, start: total, length });
|
|
52
|
+
total += length;
|
|
53
|
+
}
|
|
54
|
+
if (!total)
|
|
55
|
+
return;
|
|
56
|
+
const distanceOffset = settings.distanceOffset ?? 0;
|
|
57
|
+
const totalDistance = settings.totalDistance ?? distanceOffset + total;
|
|
58
|
+
const step = settings.step ?? (settings.detail === 'fill' ? 2.1 : 1.05);
|
|
59
|
+
const samples = [];
|
|
60
|
+
let segmentIndex = 0;
|
|
61
|
+
for (let at = 0;; at = Math.min(total, at + step)) {
|
|
62
|
+
while (segmentIndex < segments.length - 1
|
|
63
|
+
&& at > segments[segmentIndex].start + segments[segmentIndex].length)
|
|
64
|
+
segmentIndex += 1;
|
|
65
|
+
const segment = segments[segmentIndex];
|
|
66
|
+
const progress = clamp((at - segment.start) / segment.length);
|
|
67
|
+
const normalX = -(segment.b[1] - segment.a[1]) / (segment.length * scale);
|
|
68
|
+
const normalY = (segment.b[0] - segment.a[0]) / (segment.length * scale);
|
|
69
|
+
const globalAt = distanceOffset + at;
|
|
70
|
+
const offset = (noise(globalAt / 8, seed + 503) - 0.5) * amplitude * Math.sqrt(scale);
|
|
71
|
+
samples.push({
|
|
72
|
+
at: globalAt,
|
|
73
|
+
x: segment.a[0] + (segment.b[0] - segment.a[0]) * progress + normalX * offset,
|
|
74
|
+
y: segment.a[1] + (segment.b[1] - segment.a[1]) * progress + normalY * offset,
|
|
75
|
+
normalX,
|
|
76
|
+
normalY,
|
|
77
|
+
...strokeProfile(globalAt, totalDistance, seed, settings),
|
|
78
|
+
});
|
|
79
|
+
if (at === total)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
const inheritedAlpha = context.globalAlpha;
|
|
83
|
+
const material = 0.65 + hash(0, seed + 619) * 0.65;
|
|
84
|
+
const viewport = settings.viewport;
|
|
85
|
+
const filaments = Math.max(0, settings.filaments ?? 3);
|
|
86
|
+
context.save();
|
|
87
|
+
context.strokeStyle = color;
|
|
88
|
+
context.lineCap = 'round';
|
|
89
|
+
for (let pass = 0; pass < passes; pass += 1) {
|
|
90
|
+
for (let index = 1; index < samples.length; index += 1) {
|
|
91
|
+
const a = samples[index - 1];
|
|
92
|
+
const b = samples[index];
|
|
93
|
+
if (viewport && ((a.x < -8 && b.x < -8) || (a.x > viewport[0] + 8 && b.x > viewport[0] + 8)
|
|
94
|
+
|| (a.y < (settings.viewportTop ?? 36) && b.y < (settings.viewportTop ?? 36))
|
|
95
|
+
|| (a.y > viewport[1] + 8 && b.y > viewport[1] + 8)))
|
|
96
|
+
continue;
|
|
97
|
+
const contact = Math.min(a.contact, b.contact);
|
|
98
|
+
if (!contact || (pass > 0 && noise(a.at / 11, seed + pass * 127) < 0.48))
|
|
99
|
+
continue;
|
|
100
|
+
const pressure = (a.pressure + b.pressure) / 2;
|
|
101
|
+
const deposit = 0.12 + 0.88 * (a.deposit + b.deposit) / 2;
|
|
102
|
+
const opacity = Math.pow(pressure, 1.4) * contact * deposit * material * a.taper;
|
|
103
|
+
if (opacity < 0.012)
|
|
104
|
+
continue;
|
|
105
|
+
const nib = width * (0.3 + pressure * 1.65) * Math.pow(scale, 0.32);
|
|
106
|
+
const drift = pass ? (noise(a.at / 6, seed + pass * 107) - 0.5) * width * 1.1 : 0;
|
|
107
|
+
context.globalAlpha = Math.min(1, inheritedAlpha * opacity * (pass ? 0.56 : 1.2)
|
|
108
|
+
* (settings.gain ?? 1));
|
|
109
|
+
context.lineWidth = Math.max(0.18, nib * (pass ? 0.4 : 1));
|
|
110
|
+
const baseAlpha = context.globalAlpha;
|
|
111
|
+
const grainValue = settings.grain ?? 0.7;
|
|
112
|
+
context.globalAlpha = baseAlpha * (1 - grainValue * 0.78);
|
|
113
|
+
context.beginPath();
|
|
114
|
+
context.moveTo(a.x + a.normalX * drift, a.y + a.normalY * drift);
|
|
115
|
+
context.lineTo(b.x + b.normalX * drift, b.y + b.normalY * drift);
|
|
116
|
+
context.stroke();
|
|
117
|
+
if (grainValue > 0.05) {
|
|
118
|
+
for (let filament = 0; filament < filaments; filament += 1) {
|
|
119
|
+
const tooth = noise(a.at * 2.7, seed + filament * 191 + pass * 317);
|
|
120
|
+
if (tooth < 0.28)
|
|
121
|
+
continue;
|
|
122
|
+
const offset = drift + (filament - (filaments - 1) / 2) * nib * 0.3;
|
|
123
|
+
context.globalAlpha = baseAlpha * grainValue * tooth;
|
|
124
|
+
context.lineWidth = Math.max(0.15, nib * (0.12 + tooth * 0.17));
|
|
125
|
+
context.beginPath();
|
|
126
|
+
context.moveTo(a.x + a.normalX * offset, a.y + a.normalY * offset);
|
|
127
|
+
context.lineTo(b.x + b.normalX * offset, b.y + b.normalY * offset);
|
|
128
|
+
context.stroke();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (pass === 0 && settings.detail !== 'fill' && settings.edgeGrain !== false
|
|
132
|
+
&& a.deposit > 0.52 && grainValue > 0.05) {
|
|
133
|
+
const toothOffset = (noise(a.at * 2, seed + 727) - 0.5) * nib * 1.9;
|
|
134
|
+
context.lineWidth = 0.18 + pressure * 0.24;
|
|
135
|
+
context.globalAlpha = baseAlpha * 0.55;
|
|
136
|
+
context.beginPath();
|
|
137
|
+
context.moveTo(a.x + a.normalX * toothOffset, a.y + a.normalY * toothOffset);
|
|
138
|
+
context.lineTo(b.x + b.normalX * toothOffset, b.y + b.normalY * toothOffset);
|
|
139
|
+
context.stroke();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
context.restore();
|
|
144
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type PencilSurfaceVariant = 'paper' | 'action' | 'quiet' | 'badge' | 'outline' | 'danger' | 'ghost' | 'selection' | 'wash' | 'full';
|
|
2
|
+
export interface PencilSurfaceTheme {
|
|
3
|
+
paper: string;
|
|
4
|
+
dark: boolean;
|
|
5
|
+
ink: string;
|
|
6
|
+
wash: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const PENCIL_ACTION_WASH: Readonly<{
|
|
9
|
+
lightStrength: 0.2;
|
|
10
|
+
darkStrength: 0.12;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const PENCIL_QUIET_WASH: Readonly<{
|
|
13
|
+
lightStrength: 0.12;
|
|
14
|
+
darkStrength: 0.08;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const PENCIL_FILL_WASH: Readonly<{
|
|
17
|
+
angle: -35;
|
|
18
|
+
spacing: 1.8;
|
|
19
|
+
roughness: 3.5;
|
|
20
|
+
inset: 2;
|
|
21
|
+
edgeReach: 0;
|
|
22
|
+
}>;
|
|
23
|
+
export declare const PENCIL_SELECTION_WASH: Readonly<{
|
|
24
|
+
lightStrength: 0.2;
|
|
25
|
+
darkStrength: 0.12;
|
|
26
|
+
radius: 8;
|
|
27
|
+
angle: -35;
|
|
28
|
+
spacing: 1.8;
|
|
29
|
+
roughness: 3.5;
|
|
30
|
+
inset: 2;
|
|
31
|
+
edgeReach: 0;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function drawPencilSurface(canvas: HTMLCanvasElement, borderCanvas: HTMLCanvasElement | null, width: number, height: number, variant: PencilSurfaceVariant, seed: number, dpr: number, theme: PencilSurfaceTheme, radius?: number): Array<readonly [number, number]> | undefined;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { pencilPaper } from './paper.js';
|
|
2
|
+
import { pencilStroke } from './stroke.js';
|
|
3
|
+
import { drawPencilWash } from './wash.js';
|
|
4
|
+
export const PENCIL_ACTION_WASH = Object.freeze({
|
|
5
|
+
lightStrength: 0.2,
|
|
6
|
+
darkStrength: 0.12,
|
|
7
|
+
});
|
|
8
|
+
export const PENCIL_QUIET_WASH = Object.freeze({
|
|
9
|
+
lightStrength: 0.12,
|
|
10
|
+
darkStrength: 0.08,
|
|
11
|
+
});
|
|
12
|
+
const surfaceStyles = {
|
|
13
|
+
paper: { radius: 8, width: 1.2, passes: 3 },
|
|
14
|
+
action: {
|
|
15
|
+
radius: 7,
|
|
16
|
+
width: 1.35,
|
|
17
|
+
passes: 3,
|
|
18
|
+
themeStrength: { light: PENCIL_ACTION_WASH.lightStrength, dark: PENCIL_ACTION_WASH.darkStrength },
|
|
19
|
+
},
|
|
20
|
+
quiet: {
|
|
21
|
+
radius: 7,
|
|
22
|
+
width: 0.75,
|
|
23
|
+
passes: 2,
|
|
24
|
+
themeStrength: { light: PENCIL_QUIET_WASH.lightStrength, dark: PENCIL_QUIET_WASH.darkStrength },
|
|
25
|
+
},
|
|
26
|
+
badge: { radius: 6, width: 0.9, passes: 2, strength: 0.25 },
|
|
27
|
+
outline: { radius: 7, width: 1.2, passes: 3 },
|
|
28
|
+
danger: { radius: 7, width: 0.75, passes: 2, strength: 0.18 },
|
|
29
|
+
};
|
|
30
|
+
export const PENCIL_FILL_WASH = Object.freeze({
|
|
31
|
+
angle: -35,
|
|
32
|
+
spacing: 1.8,
|
|
33
|
+
roughness: 3.5,
|
|
34
|
+
inset: 2,
|
|
35
|
+
edgeReach: 0,
|
|
36
|
+
});
|
|
37
|
+
export const PENCIL_SELECTION_WASH = Object.freeze({
|
|
38
|
+
...PENCIL_FILL_WASH,
|
|
39
|
+
lightStrength: PENCIL_ACTION_WASH.lightStrength,
|
|
40
|
+
darkStrength: PENCIL_ACTION_WASH.darkStrength,
|
|
41
|
+
radius: 8,
|
|
42
|
+
});
|
|
43
|
+
function outline(width, height, radius, seed) {
|
|
44
|
+
const left = 2.5;
|
|
45
|
+
const top = 2.5;
|
|
46
|
+
const right = width - 2.5;
|
|
47
|
+
const bottom = height - 2.5;
|
|
48
|
+
const points = [];
|
|
49
|
+
for (let corner = 0; corner < 4; corner += 1) {
|
|
50
|
+
const variation = radius === 0 ? 0 : ((seed >>> (corner * 4)) & 7) * 0.12;
|
|
51
|
+
const resolvedRadius = Math.min(radius + variation, (right - left) / 2, (bottom - top) / 2);
|
|
52
|
+
const centerX = corner < 2 ? right - resolvedRadius : left + resolvedRadius;
|
|
53
|
+
const centerY = corner === 0 || corner === 3 ? top + resolvedRadius : bottom - resolvedRadius;
|
|
54
|
+
for (let step = 0; step <= 6; step += 1) {
|
|
55
|
+
const angle = (corner - 1 + step / 6) * Math.PI / 2;
|
|
56
|
+
points.push([
|
|
57
|
+
centerX + Math.cos(angle) * resolvedRadius,
|
|
58
|
+
centerY + Math.sin(angle) * resolvedRadius,
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return points;
|
|
63
|
+
}
|
|
64
|
+
function paperPattern(context, color, dark, dpr) {
|
|
65
|
+
const pattern = context.createPattern(pencilPaper(color, dark, dpr), 'repeat');
|
|
66
|
+
if (!pattern)
|
|
67
|
+
return color;
|
|
68
|
+
if (typeof pattern.setTransform === 'function' && typeof DOMMatrix !== 'undefined') {
|
|
69
|
+
pattern.setTransform(new DOMMatrix().scale(1 / dpr));
|
|
70
|
+
}
|
|
71
|
+
return pattern;
|
|
72
|
+
}
|
|
73
|
+
export function drawPencilSurface(canvas, borderCanvas, width, height, variant, seed, dpr, theme, radius) {
|
|
74
|
+
const bleed = variant === 'wash' ? 12 : 0;
|
|
75
|
+
canvas.width = Math.ceil((width + bleed * 2) * dpr);
|
|
76
|
+
canvas.height = Math.ceil((height + bleed * 2) * dpr);
|
|
77
|
+
const context = canvas.getContext('2d');
|
|
78
|
+
if (!context)
|
|
79
|
+
throw new Error('Canvas 2D is unavailable');
|
|
80
|
+
context.setTransform(canvas.width / (width + bleed * 2), 0, 0, canvas.height / (height + bleed * 2), 0, 0);
|
|
81
|
+
if (variant === 'wash') {
|
|
82
|
+
if (borderCanvas) {
|
|
83
|
+
borderCanvas.width = canvas.width;
|
|
84
|
+
borderCanvas.height = canvas.height;
|
|
85
|
+
}
|
|
86
|
+
context.translate(bleed, bleed);
|
|
87
|
+
drawPencilWash(context, { x: 0, y: 0, width, height }, theme.wash, seed, {
|
|
88
|
+
strength: 1,
|
|
89
|
+
spacing: 1.8,
|
|
90
|
+
roughness: 3.5,
|
|
91
|
+
inset: 2,
|
|
92
|
+
radius: 8,
|
|
93
|
+
edgeReach: 8,
|
|
94
|
+
dpr,
|
|
95
|
+
});
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (variant === 'selection') {
|
|
99
|
+
if (borderCanvas) {
|
|
100
|
+
borderCanvas.width = canvas.width;
|
|
101
|
+
borderCanvas.height = canvas.height;
|
|
102
|
+
}
|
|
103
|
+
drawPencilWash(context, { x: 0, y: 0, width, height }, theme.wash, seed, {
|
|
104
|
+
strength: theme.dark ? PENCIL_SELECTION_WASH.darkStrength : PENCIL_SELECTION_WASH.lightStrength,
|
|
105
|
+
spacing: PENCIL_SELECTION_WASH.spacing,
|
|
106
|
+
roughness: PENCIL_SELECTION_WASH.roughness,
|
|
107
|
+
inset: PENCIL_SELECTION_WASH.inset,
|
|
108
|
+
radius: PENCIL_SELECTION_WASH.radius,
|
|
109
|
+
edgeReach: PENCIL_SELECTION_WASH.edgeReach,
|
|
110
|
+
angle: PENCIL_SELECTION_WASH.angle,
|
|
111
|
+
dpr,
|
|
112
|
+
});
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
if (variant === 'ghost') {
|
|
116
|
+
if (borderCanvas) {
|
|
117
|
+
borderCanvas.width = canvas.width;
|
|
118
|
+
borderCanvas.height = canvas.height;
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
if (variant === 'full') {
|
|
123
|
+
if (borderCanvas) {
|
|
124
|
+
borderCanvas.width = canvas.width;
|
|
125
|
+
borderCanvas.height = canvas.height;
|
|
126
|
+
}
|
|
127
|
+
context.fillStyle = paperPattern(context, theme.paper, theme.dark, dpr);
|
|
128
|
+
context.fillRect(0, 0, width, height);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
const style = surfaceStyles[variant];
|
|
132
|
+
const strength = style.themeStrength
|
|
133
|
+
? (theme.dark ? style.themeStrength.dark : style.themeStrength.light)
|
|
134
|
+
: style.strength;
|
|
135
|
+
const resolvedRadius = radius !== undefined && Number.isFinite(radius) ? Math.max(0, Math.min(radius, 8)) : style.radius;
|
|
136
|
+
const points = outline(width, height, resolvedRadius, seed);
|
|
137
|
+
if (variant !== 'outline') {
|
|
138
|
+
context.save();
|
|
139
|
+
context.beginPath();
|
|
140
|
+
context.moveTo(...points[0]);
|
|
141
|
+
for (const point of points.slice(1))
|
|
142
|
+
context.lineTo(...point);
|
|
143
|
+
context.closePath();
|
|
144
|
+
context.clip();
|
|
145
|
+
context.fillStyle = paperPattern(context, theme.paper, theme.dark, dpr);
|
|
146
|
+
context.fillRect(0, 0, width, height);
|
|
147
|
+
if (strength) {
|
|
148
|
+
drawPencilWash(context, { x: 0, y: 0, width, height }, theme.wash, seed, {
|
|
149
|
+
...PENCIL_FILL_WASH,
|
|
150
|
+
strength,
|
|
151
|
+
radius: resolvedRadius,
|
|
152
|
+
dpr,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
context.restore();
|
|
156
|
+
}
|
|
157
|
+
let borderContext = context;
|
|
158
|
+
if (borderCanvas) {
|
|
159
|
+
borderCanvas.width = canvas.width;
|
|
160
|
+
borderCanvas.height = canvas.height;
|
|
161
|
+
const separateContext = borderCanvas.getContext('2d');
|
|
162
|
+
if (separateContext) {
|
|
163
|
+
separateContext.setTransform(canvas.width / width, 0, 0, canvas.height / height, 0, 0);
|
|
164
|
+
borderContext = separateContext;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
pencilStroke(borderContext, points, theme.ink, style.width, seed, 0.6, style.passes, true, {
|
|
168
|
+
variation: 0.82,
|
|
169
|
+
breaks: variant === 'paper' ? 0.2 : 0.25,
|
|
170
|
+
grain: 0.7,
|
|
171
|
+
gain: variant === 'action' ? 2.5 : 2.1,
|
|
172
|
+
step: 0.8,
|
|
173
|
+
});
|
|
174
|
+
return points;
|
|
175
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function queueTextPaint(job: () => void): () => void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const jobs = new Set();
|
|
2
|
+
let pending = null;
|
|
3
|
+
function schedule() {
|
|
4
|
+
if (pending !== null || !jobs.size)
|
|
5
|
+
return;
|
|
6
|
+
const run = (deadline) => {
|
|
7
|
+
pending = null;
|
|
8
|
+
const start = performance.now();
|
|
9
|
+
do {
|
|
10
|
+
const job = jobs.values().next().value;
|
|
11
|
+
if (!job)
|
|
12
|
+
break;
|
|
13
|
+
jobs.delete(job);
|
|
14
|
+
job();
|
|
15
|
+
} while (jobs.size && performance.now() - start < 6 && (deadline?.timeRemaining() ?? 3) > 2);
|
|
16
|
+
schedule();
|
|
17
|
+
};
|
|
18
|
+
const idleWindow = window;
|
|
19
|
+
pending = idleWindow.requestIdleCallback
|
|
20
|
+
? idleWindow.requestIdleCallback(run, { timeout: 300 })
|
|
21
|
+
: window.setTimeout(run, 16);
|
|
22
|
+
}
|
|
23
|
+
export function queueTextPaint(job) {
|
|
24
|
+
jobs.add(job);
|
|
25
|
+
schedule();
|
|
26
|
+
return () => {
|
|
27
|
+
jobs.delete(job);
|
|
28
|
+
if (!jobs.size && pending !== null) {
|
|
29
|
+
const idleWindow = window;
|
|
30
|
+
if (idleWindow.cancelIdleCallback)
|
|
31
|
+
idleWindow.cancelIdleCallback(pending);
|
|
32
|
+
else
|
|
33
|
+
window.clearTimeout(pending);
|
|
34
|
+
pending = null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface PencilLetteringSettings {
|
|
2
|
+
variation: number;
|
|
3
|
+
breaks: number;
|
|
4
|
+
outline: number;
|
|
5
|
+
fill: number;
|
|
6
|
+
grain: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PencilTextSprite {
|
|
9
|
+
ink: HTMLCanvasElement;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
advance: number;
|
|
13
|
+
ascent: number;
|
|
14
|
+
descent: number;
|
|
15
|
+
padding: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const pencilLettering: PencilLetteringSettings;
|
|
18
|
+
export declare const pencilHandwriting = "\"Trip Map Hand\",\"HanziPen SC\",\"Hannotate SC\",\"Kaiti SC\",\"STKaiti\",cursive";
|
|
19
|
+
export declare function renderPencilText(text: string, options: {
|
|
20
|
+
size: number;
|
|
21
|
+
family?: string;
|
|
22
|
+
color: string;
|
|
23
|
+
seed: number;
|
|
24
|
+
dpr: number;
|
|
25
|
+
settings?: PencilLetteringSettings;
|
|
26
|
+
cache?: boolean;
|
|
27
|
+
}): PencilTextSprite;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { BoundedCache, PENCIL_RENDERER_VERSION, canvasByteCost } from './cache.js';
|
|
2
|
+
import { seededRandom } from './random.js';
|
|
3
|
+
export const pencilLettering = Object.freeze({
|
|
4
|
+
variation: 0.52,
|
|
5
|
+
breaks: 0.18,
|
|
6
|
+
outline: 0.96,
|
|
7
|
+
fill: 0.34,
|
|
8
|
+
grain: 0.45,
|
|
9
|
+
});
|
|
10
|
+
export const pencilHandwriting = '"Trip Map Hand","HanziPen SC","Hannotate SC","Kaiti SC","STKaiti",cursive';
|
|
11
|
+
const TEXT_PIXEL_LIMIT = 524288;
|
|
12
|
+
const textCache = new BoundedCache(160, 16 * 1024 * 1024);
|
|
13
|
+
const clamp = (value) => Math.max(0, Math.min(1, value));
|
|
14
|
+
function hash(x, y, seed) {
|
|
15
|
+
let value = Math.imul(x ^ seed, 374761393) ^ Math.imul(y, 668265263);
|
|
16
|
+
value = Math.imul(value ^ (value >>> 13), 1274126177);
|
|
17
|
+
return ((value ^ (value >>> 16)) >>> 0) / 4294967296;
|
|
18
|
+
}
|
|
19
|
+
function field(x, y, seed) {
|
|
20
|
+
const integerX = Math.floor(x);
|
|
21
|
+
const integerY = Math.floor(y);
|
|
22
|
+
const fractionX = x - integerX;
|
|
23
|
+
const fractionY = y - integerY;
|
|
24
|
+
const smoothX = fractionX * fractionX * (3 - 2 * fractionX);
|
|
25
|
+
const smoothY = fractionY * fractionY * (3 - 2 * fractionY);
|
|
26
|
+
return (hash(integerX, integerY, seed) * (1 - smoothX)
|
|
27
|
+
+ hash(integerX + 1, integerY, seed) * smoothX) * (1 - smoothY)
|
|
28
|
+
+ (hash(integerX, integerY + 1, seed) * (1 - smoothX)
|
|
29
|
+
+ hash(integerX + 1, integerY + 1, seed) * smoothX) * smoothY;
|
|
30
|
+
}
|
|
31
|
+
function distanceTo(mask, width, height, inside) {
|
|
32
|
+
const distance = new Float32Array(width * height);
|
|
33
|
+
for (let index = 0; index < distance.length; index += 1) {
|
|
34
|
+
distance[index] = (mask[index * 4 + 3] > 100) === inside ? 0 : 1000;
|
|
35
|
+
}
|
|
36
|
+
const relax = (index, neighbor, cost) => {
|
|
37
|
+
distance[index] = Math.min(distance[index], distance[neighbor] + cost);
|
|
38
|
+
};
|
|
39
|
+
for (let y = 0; y < height; y += 1) {
|
|
40
|
+
for (let x = 0; x < width; x += 1) {
|
|
41
|
+
const index = y * width + x;
|
|
42
|
+
if (x)
|
|
43
|
+
relax(index, index - 1, 1);
|
|
44
|
+
if (y) {
|
|
45
|
+
relax(index, index - width, 1);
|
|
46
|
+
if (x)
|
|
47
|
+
relax(index, index - width - 1, 1.414);
|
|
48
|
+
if (x + 1 < width)
|
|
49
|
+
relax(index, index - width + 1, 1.414);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (let y = height - 1; y >= 0; y -= 1) {
|
|
54
|
+
for (let x = width - 1; x >= 0; x -= 1) {
|
|
55
|
+
const index = y * width + x;
|
|
56
|
+
if (x + 1 < width)
|
|
57
|
+
relax(index, index + 1, 1);
|
|
58
|
+
if (y + 1 < height) {
|
|
59
|
+
relax(index, index + width, 1);
|
|
60
|
+
if (x)
|
|
61
|
+
relax(index, index + width - 1, 1.414);
|
|
62
|
+
if (x + 1 < width)
|
|
63
|
+
relax(index, index + width + 1, 1.414);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return distance;
|
|
68
|
+
}
|
|
69
|
+
export function renderPencilText(text, options) {
|
|
70
|
+
const { size, color, seed, dpr } = options;
|
|
71
|
+
const family = options.family ?? pencilHandwriting;
|
|
72
|
+
const settings = options.settings ?? pencilLettering;
|
|
73
|
+
const cache = options.cache ?? true;
|
|
74
|
+
if (text.length > 256 || !Number.isFinite(size) || size <= 0 || size > 128) {
|
|
75
|
+
throw new RangeError('Pencil text exceeds the bounded renderer input');
|
|
76
|
+
}
|
|
77
|
+
const resolution = Math.max(2, Math.min(4, Math.ceil(dpr * 2)));
|
|
78
|
+
const font = `400 ${size}px ${family}`;
|
|
79
|
+
const fontReady = document.fonts?.check(font, text) ?? false;
|
|
80
|
+
const key = cache ? JSON.stringify([
|
|
81
|
+
PENCIL_RENDERER_VERSION,
|
|
82
|
+
text,
|
|
83
|
+
size,
|
|
84
|
+
family,
|
|
85
|
+
color,
|
|
86
|
+
seed,
|
|
87
|
+
dpr,
|
|
88
|
+
resolution,
|
|
89
|
+
settings,
|
|
90
|
+
fontReady,
|
|
91
|
+
]) : '';
|
|
92
|
+
if (cache) {
|
|
93
|
+
const cached = textCache.get(key);
|
|
94
|
+
if (cached)
|
|
95
|
+
return cached;
|
|
96
|
+
}
|
|
97
|
+
const mask = document.createElement('canvas');
|
|
98
|
+
const measure = mask.getContext('2d', { willReadFrequently: true });
|
|
99
|
+
if (!measure)
|
|
100
|
+
throw new Error('Canvas 2D is unavailable');
|
|
101
|
+
measure.fillStyle = color;
|
|
102
|
+
measure.fillRect(0, 0, 1, 1);
|
|
103
|
+
const rgb = measure.getImageData(0, 0, 1, 1).data;
|
|
104
|
+
measure.font = font;
|
|
105
|
+
measure.fontKerning = 'none';
|
|
106
|
+
const characters = Array.from(text);
|
|
107
|
+
const widths = characters.map(character => measure.measureText(character).width);
|
|
108
|
+
const advance = Math.ceil(widths.reduce((sum, width) => sum + width, 0));
|
|
109
|
+
const padding = 5;
|
|
110
|
+
const ascent = Math.ceil(size * 1.1);
|
|
111
|
+
const descent = Math.ceil(size * 0.35);
|
|
112
|
+
const width = advance + padding * 2;
|
|
113
|
+
const height = ascent + descent + padding * 2;
|
|
114
|
+
if (width * height * resolution ** 2 > TEXT_PIXEL_LIMIT) {
|
|
115
|
+
throw new RangeError('Pencil text exceeds the bounded pixel budget');
|
|
116
|
+
}
|
|
117
|
+
mask.width = width * resolution;
|
|
118
|
+
mask.height = height * resolution;
|
|
119
|
+
const context = mask.getContext('2d', { willReadFrequently: true });
|
|
120
|
+
if (!context)
|
|
121
|
+
throw new Error('Canvas 2D is unavailable');
|
|
122
|
+
context.scale(resolution, resolution);
|
|
123
|
+
context.font = font;
|
|
124
|
+
context.fontKerning = 'none';
|
|
125
|
+
context.fillStyle = color;
|
|
126
|
+
context.strokeStyle = color;
|
|
127
|
+
context.lineWidth = size >= 16 ? 0.48 : 0.24;
|
|
128
|
+
const random = seededRandom(seed);
|
|
129
|
+
let x = padding;
|
|
130
|
+
characters.forEach((character, index) => {
|
|
131
|
+
const center = x + widths[index] / 2;
|
|
132
|
+
const baseline = padding + ascent + (random() - 0.5) * 0.55;
|
|
133
|
+
context.save();
|
|
134
|
+
context.translate(center, baseline);
|
|
135
|
+
context.rotate((random() - 0.5) * 0.035);
|
|
136
|
+
context.globalAlpha = 1;
|
|
137
|
+
context.fillText(character, -widths[index] / 2, 0);
|
|
138
|
+
context.strokeText(character, -widths[index] / 2, 0);
|
|
139
|
+
context.restore();
|
|
140
|
+
x += widths[index];
|
|
141
|
+
});
|
|
142
|
+
const image = context.getImageData(0, 0, mask.width, mask.height);
|
|
143
|
+
const shape = new Uint8ClampedArray(image.data);
|
|
144
|
+
const outside = distanceTo(shape, mask.width, mask.height, true);
|
|
145
|
+
const inside = distanceTo(shape, mask.width, mask.height, false);
|
|
146
|
+
const cuts = [];
|
|
147
|
+
let characterLeft = padding;
|
|
148
|
+
for (let character = 0; character < characters.length; character += 1) {
|
|
149
|
+
for (let attempt = 0, found = 0; attempt < 80 && found < 2; attempt += 1) {
|
|
150
|
+
const centerX = characterLeft + random() * widths[character];
|
|
151
|
+
const centerY = padding + ascent - size + random() * size;
|
|
152
|
+
const pixel = Math.floor(centerY * resolution) * mask.width + Math.floor(centerX * resolution);
|
|
153
|
+
if (shape[pixel * 4 + 3] < 130)
|
|
154
|
+
continue;
|
|
155
|
+
const angle = random() * Math.PI;
|
|
156
|
+
cuts.push({
|
|
157
|
+
x: centerX,
|
|
158
|
+
y: centerY,
|
|
159
|
+
cosine: Math.cos(angle),
|
|
160
|
+
sine: Math.sin(angle),
|
|
161
|
+
long: 1.1 + random() * 0.7,
|
|
162
|
+
short: 0.22 + random() * 0.2,
|
|
163
|
+
});
|
|
164
|
+
found += 1;
|
|
165
|
+
}
|
|
166
|
+
characterLeft += widths[character];
|
|
167
|
+
}
|
|
168
|
+
const columnCuts = Array.from({ length: mask.width }, (_, column) => cuts.filter(cut => Math.abs(column / resolution - cut.x)
|
|
169
|
+
<= Math.hypot(cut.cosine * cut.long, cut.sine * cut.short) * 1.55));
|
|
170
|
+
for (let y = 0; y < mask.height; y += 1) {
|
|
171
|
+
for (let xIndex = 0; xIndex < mask.width; xIndex += 1) {
|
|
172
|
+
const index = (y * mask.width + xIndex) * 4;
|
|
173
|
+
const pixel = y * mask.width + xIndex;
|
|
174
|
+
if (outside[pixel] > resolution * 1.1) {
|
|
175
|
+
image.data[index + 3] = 0;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const pixelX = xIndex / resolution;
|
|
179
|
+
const pixelY = y / resolution;
|
|
180
|
+
const force = field(pixelX / 2.7, pixelY / 2.4, seed + 31);
|
|
181
|
+
const pressure = 1 - settings.variation * 0.94
|
|
182
|
+
* (1 - clamp((force - 0.23) / 0.46) ** 0.85);
|
|
183
|
+
const paperTooth = field(pixelX * 2.3, pixelY * 2.1, seed + 113);
|
|
184
|
+
const tooth = 1 - settings.grain * 0.76
|
|
185
|
+
* (1 - clamp((paperTooth - 0.2) / 0.53));
|
|
186
|
+
let contact = 1;
|
|
187
|
+
for (const cut of columnCuts[xIndex]) {
|
|
188
|
+
const deltaX = pixelX - cut.x;
|
|
189
|
+
const deltaY = pixelY - cut.y;
|
|
190
|
+
const radius = Math.hypot((deltaX * cut.cosine + deltaY * cut.sine) / cut.long, (-deltaX * cut.sine + deltaY * cut.cosine) / cut.short);
|
|
191
|
+
contact = Math.min(contact, clamp((radius - 0.85) / 0.7));
|
|
192
|
+
}
|
|
193
|
+
const distance = shape[index + 3] > 100
|
|
194
|
+
? -(inside[pixel] - 0.5) / resolution
|
|
195
|
+
: (outside[pixel] - 0.5) / resolution;
|
|
196
|
+
const edgeWidth = 0.34 + settings.outline * 0.5;
|
|
197
|
+
const edgeWobble = (field(pixelX * 0.9, pixelY * 0.9, seed + 209) - 0.5)
|
|
198
|
+
* 0.32 * settings.outline;
|
|
199
|
+
const rim = clamp(1 - Math.abs(distance + edgeWobble) / edgeWidth);
|
|
200
|
+
const core = (shape[index + 3] / 255) * (0.28 + 0.64 * settings.fill);
|
|
201
|
+
const lift = 1 - settings.breaks * 1.75 * (1 - contact);
|
|
202
|
+
const pigment = clamp(Math.max(core, rim * (0.48 + 0.52 * settings.outline))
|
|
203
|
+
* pressure * tooth * Math.max(0.08, lift) * 1.38);
|
|
204
|
+
image.data[index] = rgb[0];
|
|
205
|
+
image.data[index + 1] = rgb[1];
|
|
206
|
+
image.data[index + 2] = rgb[2];
|
|
207
|
+
image.data[index + 3] = Math.round(255 * pigment);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
context.putImageData(image, 0, 0);
|
|
211
|
+
const sprite = { ink: mask, width, height, advance, ascent, descent, padding };
|
|
212
|
+
return cache ? textCache.set(key, sprite, canvasByteCost(mask)) : sprite;
|
|
213
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare const paths: {
|
|
2
|
+
plus: {
|
|
3
|
+
d: string;
|
|
4
|
+
}[];
|
|
5
|
+
minus: {
|
|
6
|
+
d: string;
|
|
7
|
+
}[];
|
|
8
|
+
reset: {
|
|
9
|
+
d: string;
|
|
10
|
+
}[];
|
|
11
|
+
previous: {
|
|
12
|
+
d: string;
|
|
13
|
+
}[];
|
|
14
|
+
next: {
|
|
15
|
+
d: string;
|
|
16
|
+
}[];
|
|
17
|
+
info: {
|
|
18
|
+
d: string;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
export declare function TravelIcon({ name }: {
|
|
22
|
+
name: keyof typeof paths;
|
|
23
|
+
}): import("react").JSX.Element;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PencilIcon } from '../PencilIcon.js';
|
|
3
|
+
// Lucide ISC geometry rendered through the same pencil renderer as the trip tools.
|
|
4
|
+
const paths = {
|
|
5
|
+
plus: [{ d: 'M5 12h14' }, { d: 'M12 5v14' }],
|
|
6
|
+
minus: [{ d: 'M5 12h14' }],
|
|
7
|
+
reset: [{ d: 'M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8' }, { d: 'M3 3v5h5' }],
|
|
8
|
+
previous: [{ d: 'm12 19-7-7 7-7' }, { d: 'M19 12H5' }],
|
|
9
|
+
next: [{ d: 'M5 12h14' }, { d: 'm12 5 7 7-7 7' }],
|
|
10
|
+
info: [{ d: 'M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0' }, { d: 'M12 16v-4' }, { d: 'M12 8h.01' }],
|
|
11
|
+
};
|
|
12
|
+
export function TravelIcon({ name }) {
|
|
13
|
+
return _jsx(PencilIcon, { paths: paths[name], sourceSize: 24, size: 30 });
|
|
14
|
+
}
|