@pixerate/editor-svelte 0.39.0 → 0.41.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/dist/vector/VectorCanvas.svelte +26 -1
- package/dist/vector/VectorCanvas.svelte.d.ts.map +1 -1
- package/dist/vector/VectorEnvelopePanel.svelte +179 -0
- package/dist/vector/VectorEnvelopePanel.svelte.d.ts +15 -0
- package/dist/vector/VectorEnvelopePanel.svelte.d.ts.map +1 -0
- package/dist/vector/VectorGradientAnnotator.svelte +39 -0
- package/dist/vector/VectorGradientAnnotator.svelte.d.ts +10 -0
- package/dist/vector/VectorGradientAnnotator.svelte.d.ts.map +1 -0
- package/dist/vector/VectorGradientDefs.svelte +21 -9
- package/dist/vector/VectorGradientDefs.svelte.d.ts +3 -2
- package/dist/vector/VectorGradientDefs.svelte.d.ts.map +1 -1
- package/dist/vector/VectorGradientEditor.svelte +166 -0
- package/dist/vector/VectorGradientEditor.svelte.d.ts +9 -0
- package/dist/vector/VectorGradientEditor.svelte.d.ts.map +1 -0
- package/dist/vector/VectorStylePanel.svelte +4 -0
- package/dist/vector/VectorStylePanel.svelte.d.ts.map +1 -1
- package/dist/vector/VectorToolbar.svelte +28 -1
- package/dist/vector/VectorToolbar.svelte.d.ts +2 -0
- package/dist/vector/VectorToolbar.svelte.d.ts.map +1 -1
- package/dist/vector/index.d.ts +3 -0
- package/dist/vector/index.d.ts.map +1 -1
- package/dist/vector/index.js +3 -0
- package/package.json +2 -2
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
type VectorEditorController
|
|
17
17
|
} from '@pixerate/editor/vector';
|
|
18
18
|
import VectorGradientDefs from './VectorGradientDefs.svelte';
|
|
19
|
+
import VectorGradientAnnotator from './VectorGradientAnnotator.svelte';
|
|
19
20
|
import VectorSceneNodes from './VectorSceneNodes.svelte';
|
|
20
21
|
import VectorWidthPointEditor from './VectorWidthPointEditor.svelte';
|
|
21
22
|
import { vectorStateOf } from './subscribe.svelte.js';
|
|
@@ -188,7 +189,7 @@
|
|
|
188
189
|
<path d={overlay.penPreview} stroke={accentColor} stroke-dasharray="4 3" />
|
|
189
190
|
{/if}
|
|
190
191
|
{#if box}
|
|
191
|
-
<g data-vector-selection="" transform={box.transform || undefined}>
|
|
192
|
+
<g data-vector-selection="" data-free-distort={box.freeDistort || undefined} transform={box.transform || undefined}>
|
|
192
193
|
<rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />
|
|
193
194
|
<line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />
|
|
194
195
|
{#each box.handles as h (h.handle)}
|
|
@@ -200,6 +201,29 @@
|
|
|
200
201
|
{/each}
|
|
201
202
|
</g>
|
|
202
203
|
{/if}
|
|
204
|
+
{#if overlay.distort}
|
|
205
|
+
<g data-vector-distort={overlay.distort.mode}>
|
|
206
|
+
<path d={overlay.distort.d} stroke={accentColor} />
|
|
207
|
+
{#each overlay.distort.corners as c, i (i)}
|
|
208
|
+
<rect x={c.x - hs / 2} y={c.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />
|
|
209
|
+
{/each}
|
|
210
|
+
</g>
|
|
211
|
+
{/if}
|
|
212
|
+
{#if overlay.envelope}
|
|
213
|
+
<g data-vector-envelope-mesh={overlay.envelope.nodeId}>
|
|
214
|
+
<path data-vector-envelope-grid="" d={overlay.envelope.grid} stroke={accentColor} stroke-opacity="0.8" />
|
|
215
|
+
{#each overlay.envelope.handles as h (h.index)}
|
|
216
|
+
<g data-vector-envelope-handle={h.index}>
|
|
217
|
+
<line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} stroke-opacity="0.6" />
|
|
218
|
+
<circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered || h.active ? 1 : 0)} fill={h.active ? accentColor : THEME.handleFill} stroke={accentColor} />
|
|
219
|
+
</g>
|
|
220
|
+
{/each}
|
|
221
|
+
{#each overlay.envelope.anchors as a (a.index)}
|
|
222
|
+
{@const size = as + (a.hovered || a.active ? 2 : 0)}
|
|
223
|
+
<rect data-vector-envelope-anchor={a.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={a.active ? accentColor : THEME.handleFill} stroke={accentColor} />
|
|
224
|
+
{/each}
|
|
225
|
+
</g>
|
|
226
|
+
{/if}
|
|
203
227
|
{#each overlay.handles as h (handleKey(h))}
|
|
204
228
|
<g data-vector-handle={h.ref.handle}>
|
|
205
229
|
<line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />
|
|
@@ -247,6 +271,7 @@
|
|
|
247
271
|
{#each overlay.blend.anchors as a, i (i)}
|
|
248
272
|
<rect data-vector-blend-anchor={i} x={a.x - as / 2} y={a.y - as / 2} width={as} height={as} fill={a.hovered ? accentColor : THEME.handleFill} stroke={accentColor} />
|
|
249
273
|
{/each}
|
|
274
|
+
{#if overlay.gradient}<VectorGradientAnnotator annotator={overlay.gradient} {accentColor} />{/if}
|
|
250
275
|
{#if overlay.widthSpot}
|
|
251
276
|
<g data-vector-width-spot="">
|
|
252
277
|
<line x1={overlay.widthSpot.left.x} y1={overlay.widthSpot.left.y} x2={overlay.widthSpot.right.x} y2={overlay.widthSpot.right.y} stroke={accentColor} stroke-dasharray="3 2" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorCanvas.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,EAQH,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"VectorCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorCanvas.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,EAQH,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AAQjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA8RH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { VECTOR_ENVELOPE_STYLE_OPTIONS, getVectorEnvelopePanel, type VectorEditorController, type VectorEnvelopeParams } from '@pixerate/editor/vector';
|
|
3
|
+
import { vectorStateOf } from './subscribe.svelte.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Envelope distortion of the selection: Make with Warp, Mesh or Top Object, and for one selected
|
|
7
|
+
* object with an envelope its warp preset, bend, horizontal and vertical distortion, orientation
|
|
8
|
+
* and mesh rows and columns, plus Release and Expand. Edit the mesh on the canvas with the
|
|
9
|
+
* Envelope tool. Slider drags are one undo step each.
|
|
10
|
+
*/
|
|
11
|
+
interface Props {
|
|
12
|
+
controller: VectorEditorController;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { controller, class: className = '' }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const store = vectorStateOf(() => controller);
|
|
19
|
+
const view = $derived(store.current);
|
|
20
|
+
const panel = $derived(getVectorEnvelopePanel(view));
|
|
21
|
+
const env = $derived(panel?.envelope ?? null);
|
|
22
|
+
// The mesh size Make with Mesh uses (an envelope's own size once it has one).
|
|
23
|
+
let size = $state({ rows: 2, columns: 2 });
|
|
24
|
+
const rows = $derived(env?.params.rows ?? size.rows);
|
|
25
|
+
const columns = $derived(env?.params.columns ?? size.columns);
|
|
26
|
+
|
|
27
|
+
const buttonClass =
|
|
28
|
+
'inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40';
|
|
29
|
+
const numberClass = 'h-7 w-14 rounded border border-border bg-background px-1.5 text-xs';
|
|
30
|
+
const selectClass = 'h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs';
|
|
31
|
+
const labelClass = 'w-full truncate text-[11px] text-muted-foreground';
|
|
32
|
+
|
|
33
|
+
const SLIDERS: { key: 'bend' | 'horizontal' | 'vertical'; label: string }[] = [
|
|
34
|
+
{ key: 'bend', label: 'Bend %' },
|
|
35
|
+
{ key: 'horizontal', label: 'Horizontal distortion %' },
|
|
36
|
+
{ key: 'vertical', label: 'Vertical distortion %' }
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const end = () => controller.endMergedEdit();
|
|
40
|
+
function set(patch: Partial<VectorEnvelopeParams>, merge = false) {
|
|
41
|
+
controller.envelope(patch, { ids: panel?.nodeId ? [panel.nodeId] : undefined, merge });
|
|
42
|
+
}
|
|
43
|
+
function setSlider(key: 'bend' | 'horizontal' | 'vertical', raw: string) {
|
|
44
|
+
const n = parseFloat(raw);
|
|
45
|
+
if (Number.isFinite(n)) set({ [key]: n }, true);
|
|
46
|
+
}
|
|
47
|
+
function setCount(key: 'rows' | 'columns', raw: string) {
|
|
48
|
+
const n = Math.round(parseFloat(raw));
|
|
49
|
+
if (!Number.isFinite(n) || n < 1 || n > 16) return;
|
|
50
|
+
if (env) set({ [key]: n });
|
|
51
|
+
else size = { ...size, [key]: n };
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
{#if panel}
|
|
56
|
+
<div class="space-y-2 {className}" data-vector-envelope="" aria-label="Envelope">
|
|
57
|
+
<div class="flex flex-wrap items-center gap-1">
|
|
58
|
+
<span class="w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">Envelope</span>
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
data-vector-envelope-action="warp"
|
|
62
|
+
class={buttonClass}
|
|
63
|
+
title="Make with Warp"
|
|
64
|
+
aria-label="Make with Warp"
|
|
65
|
+
disabled={!panel.canMake || !!env}
|
|
66
|
+
onclick={() => set({ style: 'arc', bend: 50 })}
|
|
67
|
+
>
|
|
68
|
+
Warp
|
|
69
|
+
</button>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
data-vector-envelope-action="mesh"
|
|
73
|
+
class={buttonClass}
|
|
74
|
+
title="Make with Mesh"
|
|
75
|
+
aria-label="Make with Mesh"
|
|
76
|
+
disabled={!panel.canMake || !!env}
|
|
77
|
+
onclick={() => set({ style: 'mesh', rows: size.rows, columns: size.columns })}
|
|
78
|
+
>
|
|
79
|
+
Mesh
|
|
80
|
+
</button>
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
data-vector-envelope-action="top"
|
|
84
|
+
class={buttonClass}
|
|
85
|
+
title="Make with Top Object"
|
|
86
|
+
aria-label="Make with Top Object"
|
|
87
|
+
disabled={!panel.canMakeWithTopObject}
|
|
88
|
+
onclick={() => controller.makeEnvelopeWithTopObject({ rows: size.rows, columns: size.columns })}
|
|
89
|
+
>
|
|
90
|
+
Top
|
|
91
|
+
</button>
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
data-vector-envelope-action="release"
|
|
95
|
+
class={buttonClass}
|
|
96
|
+
title="Release Envelope"
|
|
97
|
+
aria-label="Release Envelope"
|
|
98
|
+
disabled={!panel.canRelease}
|
|
99
|
+
onclick={() => controller.releaseEnvelope()}
|
|
100
|
+
>
|
|
101
|
+
Release
|
|
102
|
+
</button>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
data-vector-envelope-action="expand"
|
|
106
|
+
class={buttonClass}
|
|
107
|
+
title="Expand Envelope"
|
|
108
|
+
aria-label="Expand Envelope"
|
|
109
|
+
disabled={!panel.canRelease}
|
|
110
|
+
onclick={() => controller.expandAppearance()}
|
|
111
|
+
>
|
|
112
|
+
Expand
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
{#if env}
|
|
116
|
+
<label class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground" data-vector-envelope-param="style">
|
|
117
|
+
<span class={labelClass}>Warp</span>
|
|
118
|
+
<select
|
|
119
|
+
aria-label="Envelope warp"
|
|
120
|
+
class={selectClass}
|
|
121
|
+
value={env.params.style}
|
|
122
|
+
onchange={(e) => set({ style: e.currentTarget.value as VectorEnvelopeParams['style'] })}
|
|
123
|
+
>
|
|
124
|
+
{#each VECTOR_ENVELOPE_STYLE_OPTIONS as o (o.value)}<option value={o.value}>{o.label}</option>{/each}
|
|
125
|
+
</select>
|
|
126
|
+
</label>
|
|
127
|
+
{#if env.params.style !== 'mesh'}
|
|
128
|
+
{#each SLIDERS as s (s.key)}
|
|
129
|
+
<label class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground" data-vector-envelope-param={s.key}>
|
|
130
|
+
<span class={labelClass}>{s.label}</span>
|
|
131
|
+
<input
|
|
132
|
+
type="range"
|
|
133
|
+
min="-100"
|
|
134
|
+
max="100"
|
|
135
|
+
step="1"
|
|
136
|
+
aria-label="Envelope {s.label} slider"
|
|
137
|
+
class="h-4 min-w-0 flex-1 accent-primary"
|
|
138
|
+
value={env.params[s.key]}
|
|
139
|
+
oninput={(e) => setSlider(s.key, e.currentTarget.value)}
|
|
140
|
+
onpointerup={end}
|
|
141
|
+
onkeyup={end}
|
|
142
|
+
onblur={end}
|
|
143
|
+
/>
|
|
144
|
+
<input
|
|
145
|
+
type="number"
|
|
146
|
+
min="-100"
|
|
147
|
+
max="100"
|
|
148
|
+
step="1"
|
|
149
|
+
aria-label="Envelope {s.label}"
|
|
150
|
+
class={numberClass}
|
|
151
|
+
value={env.params[s.key]}
|
|
152
|
+
oninput={(e) => setSlider(s.key, e.currentTarget.value)}
|
|
153
|
+
onblur={end}
|
|
154
|
+
/>
|
|
155
|
+
</label>
|
|
156
|
+
{/each}
|
|
157
|
+
<label class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground" data-vector-envelope-param="orientation">
|
|
158
|
+
<span class={labelClass}>Orientation</span>
|
|
159
|
+
<select
|
|
160
|
+
aria-label="Envelope orientation"
|
|
161
|
+
class={selectClass}
|
|
162
|
+
value={env.params.orientation}
|
|
163
|
+
onchange={(e) => set({ orientation: e.currentTarget.value as 'horizontal' | 'vertical' })}
|
|
164
|
+
>
|
|
165
|
+
<option value="horizontal">Horizontal</option>
|
|
166
|
+
<option value="vertical">Vertical</option>
|
|
167
|
+
</select>
|
|
168
|
+
</label>
|
|
169
|
+
{/if}
|
|
170
|
+
{/if}
|
|
171
|
+
<div class="flex items-center gap-2 text-xs text-foreground" data-vector-envelope-param="mesh">
|
|
172
|
+
<span class="min-w-0 flex-1 truncate text-[11px] text-muted-foreground">Mesh rows × columns</span>
|
|
173
|
+
<input type="number" min="1" max="16" step="1" aria-label="Envelope mesh rows" class={numberClass} value={rows} oninput={(e) => setCount('rows', e.currentTarget.value)} />
|
|
174
|
+
<input type="number" min="1" max="16" step="1" aria-label="Envelope mesh columns" class={numberClass} value={columns} oninput={(e) => setCount('columns', e.currentTarget.value)} />
|
|
175
|
+
</div>
|
|
176
|
+
{#if panel.count > 1 && !panel.canMakeWithTopObject}<p class="text-[11px] text-muted-foreground">Select one object to edit its envelope.</p>{/if}
|
|
177
|
+
{#if env}<p class="text-[11px] text-muted-foreground">Edit the mesh with the Envelope tool (U).</p>{/if}
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type VectorEditorController } from '@pixerate/editor/vector';
|
|
2
|
+
/**
|
|
3
|
+
* Envelope distortion of the selection: Make with Warp, Mesh or Top Object, and for one selected
|
|
4
|
+
* object with an envelope its warp preset, bend, horizontal and vertical distortion, orientation
|
|
5
|
+
* and mesh rows and columns, plus Release and Expand. Edit the mesh on the canvas with the
|
|
6
|
+
* Envelope tool. Slider drags are one undo step each.
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
controller: VectorEditorController;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const VectorEnvelopePanel: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type VectorEnvelopePanel = ReturnType<typeof VectorEnvelopePanel>;
|
|
14
|
+
export default VectorEnvelopePanel;
|
|
15
|
+
//# sourceMappingURL=VectorEnvelopePanel.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VectorEnvelopePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorEnvelopePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyD,KAAK,sBAAsB,EAA6B,MAAM,yBAAyB,CAAC;AAItJ;;;;;GAKG;AACH,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqGH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { VECTOR_OVERLAY_THEME as THEME, vectorDiamondPath, type VectorRenderGradientAnnotator } from '@pixerate/editor/vector';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
/** The gradient tool's annotator (screen space) from `getVectorOverlayRenderData`. */
|
|
6
|
+
annotator: VectorRenderGradientAnnotator;
|
|
7
|
+
accentColor?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let { annotator: g, accentColor = THEME.accent }: Props = $props();
|
|
11
|
+
const r = THEME.handleRadius + 1.5;
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<g data-vector-gradient-annotator={g.type}>
|
|
15
|
+
{#if g.extent}<path data-vector-gradient-extent="" d={g.extent} fill="none" stroke={accentColor} stroke-dasharray="4 3" />{/if}
|
|
16
|
+
<line x1={g.start.x} y1={g.start.y} x2={g.end.x} y2={g.end.y} stroke="#ffffff" stroke-width="3" />
|
|
17
|
+
<line data-vector-gradient-bar="" x1={g.start.x} y1={g.start.y} x2={g.end.x} y2={g.end.y} stroke={accentColor} />
|
|
18
|
+
{#if g.aspect}<line x1={g.start.x} y1={g.start.y} x2={g.aspect.x} y2={g.aspect.y} stroke={accentColor} stroke-dasharray="2 2" />{/if}
|
|
19
|
+
<circle data-vector-gradient-handle="start" cx={g.start.x} cy={g.start.y} r={r + (g.start.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} stroke-width={g.start.hovered ? 2 : 1} />
|
|
20
|
+
<rect data-vector-gradient-handle={g.end.handle} x={g.end.x - r} y={g.end.y - r} width={2 * r} height={2 * r} fill={THEME.handleFill} stroke={accentColor} stroke-width={g.end.hovered ? 2 : 1} />
|
|
21
|
+
{#if g.aspect}
|
|
22
|
+
<circle data-vector-gradient-handle="aspect" cx={g.aspect.x} cy={g.aspect.y} r={THEME.handleRadius + (g.aspect.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} stroke-width={g.aspect.hovered ? 2 : 1} />
|
|
23
|
+
{/if}
|
|
24
|
+
{#if g.focal}
|
|
25
|
+
<g data-vector-gradient-handle="focal">
|
|
26
|
+
<circle cx={g.focal.x} cy={g.focal.y} r={THEME.handleRadius - 1 + (g.focal.hovered ? 1 : 0)} fill={accentColor} stroke={THEME.handleFill} />
|
|
27
|
+
</g>
|
|
28
|
+
{/if}
|
|
29
|
+
{#each g.midpoints as m (m.index)}
|
|
30
|
+
<path data-vector-gradient-midpoint={m.index} d={vectorDiamondPath(m.x, m.y, 4 + (m.hovered ? 1 : 0))} fill={THEME.handleFill} stroke={accentColor} />
|
|
31
|
+
{/each}
|
|
32
|
+
{#each g.stops as s (s.index)}
|
|
33
|
+
<g data-vector-gradient-stop={s.index} data-selected={s.selected || undefined} opacity={g.removing && s.selected ? 0.35 : 1}>
|
|
34
|
+
<line x1={s.barX} y1={s.barY} x2={s.x} y2={s.y} stroke={accentColor} />
|
|
35
|
+
<rect x={s.x - 5} y={s.y - 5} width="10" height="10" rx="2" fill={s.color} fill-opacity={s.opacity} stroke={s.selected ? accentColor : '#ffffff'} stroke-width={s.selected || s.hovered ? 2 : 1} />
|
|
36
|
+
<rect x={s.x - 6.5} y={s.y - 6.5} width="13" height="13" rx="3" fill="none" stroke={s.selected ? '#ffffff' : accentColor} />
|
|
37
|
+
</g>
|
|
38
|
+
{/each}
|
|
39
|
+
</g>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type VectorRenderGradientAnnotator } from '@pixerate/editor/vector';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** The gradient tool's annotator (screen space) from `getVectorOverlayRenderData`. */
|
|
4
|
+
annotator: VectorRenderGradientAnnotator;
|
|
5
|
+
accentColor?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const VectorGradientAnnotator: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type VectorGradientAnnotator = ReturnType<typeof VectorGradientAnnotator>;
|
|
9
|
+
export default VectorGradientAnnotator;
|
|
10
|
+
//# sourceMappingURL=VectorGradientAnnotator.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VectorGradientAnnotator.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorGradientAnnotator.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoD,KAAK,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAG7H,UAAU,KAAK;IACb,sFAAsF;IACtF,SAAS,EAAE,6BAA6B,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsCH,QAAA,MAAM,uBAAuB,2CAAwC,CAAC;AACtE,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC1E,eAAe,uBAAuB,CAAC"}
|
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { SvgGradientDef } from '@pixerate/editor/vector';
|
|
2
|
+
import type { SvgGradientDef, VectorPaintDef } from '@pixerate/editor/vector';
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
|
-
|
|
5
|
+
/** Paint servers: gradient descriptors and patterns of gradient tiles (conic, diamond, stroke modes). */
|
|
6
|
+
defs: readonly VectorPaintDef[];
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
let { defs }: Props = $props();
|
|
9
10
|
</script>
|
|
10
11
|
|
|
12
|
+
{#snippet gradient(def: SvgGradientDef)}
|
|
13
|
+
{#if def.type === 'radial'}
|
|
14
|
+
<radialGradient id={def.id} {...def.attrs}>
|
|
15
|
+
{#each def.stops as s, i (i)}<stop offset={s.offset} stop-color={s.stopColor} stop-opacity={s.stopOpacity} />{/each}
|
|
16
|
+
</radialGradient>
|
|
17
|
+
{:else}
|
|
18
|
+
<linearGradient id={def.id} {...def.attrs}>
|
|
19
|
+
{#each def.stops as s, i (i)}<stop offset={s.offset} stop-color={s.stopColor} stop-opacity={s.stopOpacity} />{/each}
|
|
20
|
+
</linearGradient>
|
|
21
|
+
{/if}
|
|
22
|
+
{/snippet}
|
|
23
|
+
|
|
11
24
|
{#if defs.length}
|
|
12
25
|
<defs>
|
|
13
26
|
{#each defs as def (def.id)}
|
|
14
|
-
{#if def.type === '
|
|
15
|
-
<
|
|
16
|
-
{#each def.
|
|
17
|
-
|
|
27
|
+
{#if def.type === 'pattern'}
|
|
28
|
+
<pattern id={def.id} {...def.attrs}>
|
|
29
|
+
{#each def.gradients as g (g.id)}{@render gradient(g)}{/each}
|
|
30
|
+
{#each def.tiles as t, i (i)}<path d={t.d} fill={t.fill} />{/each}
|
|
31
|
+
</pattern>
|
|
18
32
|
{:else}
|
|
19
|
-
|
|
20
|
-
{#each def.stops as s, i (i)}<stop offset={s.offset} stop-color={s.stopColor} stop-opacity={s.stopOpacity} />{/each}
|
|
21
|
-
</linearGradient>
|
|
33
|
+
{@render gradient(def)}
|
|
22
34
|
{/if}
|
|
23
35
|
{/each}
|
|
24
36
|
</defs>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { VectorPaintDef } from '@pixerate/editor/vector';
|
|
2
2
|
interface Props {
|
|
3
|
-
|
|
3
|
+
/** Paint servers: gradient descriptors and patterns of gradient tiles (conic, diamond, stroke modes). */
|
|
4
|
+
defs: readonly VectorPaintDef[];
|
|
4
5
|
}
|
|
5
6
|
declare const VectorGradientDefs: import("svelte").Component<Props, {}, "">;
|
|
6
7
|
type VectorGradientDefs = ReturnType<typeof VectorGradientDefs>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorGradientDefs.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorGradientDefs.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"VectorGradientDefs.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorGradientDefs.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG5E,UAAU,KAAK;IACb,yGAAyG;IACzG,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;CACjC;AAqCH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
VECTOR_GRADIENT_TYPES,
|
|
4
|
+
VECTOR_GRADIENT_SPREADS,
|
|
5
|
+
VECTOR_GRADIENT_INTERPOLATIONS,
|
|
6
|
+
VECTOR_STROKE_GRADIENT_MODES,
|
|
7
|
+
VECTOR_GRADIENT_TYPE_LABELS,
|
|
8
|
+
VECTOR_GRADIENT_SPREAD_LABELS,
|
|
9
|
+
VECTOR_GRADIENT_INTERPOLATION_LABELS,
|
|
10
|
+
VECTOR_STROKE_GRADIENT_MODE_LABELS,
|
|
11
|
+
getVectorGradientEditor,
|
|
12
|
+
vectorPaintToHex,
|
|
13
|
+
type VectorEditorController,
|
|
14
|
+
type VectorGradientInterpolation,
|
|
15
|
+
type VectorGradientSpread,
|
|
16
|
+
type VectorGradientType,
|
|
17
|
+
type VectorStrokeGradientMode
|
|
18
|
+
} from '@pixerate/editor/vector';
|
|
19
|
+
import { vectorStateOf } from './subscribe.svelte.js';
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
controller: VectorEditorController;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gradient editor for the selection's fill or stroke: style (or solid), the stop ramp (click it
|
|
28
|
+
* to add a stop, pick a stop to edit its colour, opacity, position and midpoint), interpolation
|
|
29
|
+
* space, smoothness, spread, how it lies on a stroke, and reverse.
|
|
30
|
+
*/
|
|
31
|
+
let { controller, class: className = '' }: Props = $props();
|
|
32
|
+
|
|
33
|
+
const store = vectorStateOf(() => controller);
|
|
34
|
+
const ed = $derived(getVectorGradientEditor(store.current));
|
|
35
|
+
const selectClass = 'h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs';
|
|
36
|
+
const numberClass = 'h-7 w-14 rounded border border-border bg-background px-1.5 text-xs';
|
|
37
|
+
const buttonClass =
|
|
38
|
+
'inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground';
|
|
39
|
+
const percent = (v: number) => Math.round(v * 1000) / 10;
|
|
40
|
+
const end = () => controller.endMergedEdit();
|
|
41
|
+
|
|
42
|
+
function setType(value: string) {
|
|
43
|
+
if (!ed) return;
|
|
44
|
+
if (value === 'solid') {
|
|
45
|
+
const first = ed.stops[0];
|
|
46
|
+
controller.setStyle({ [ed.target]: vectorPaintToHex(first?.color) ?? first?.color ?? '#000000' }, undefined, { label: 'Solid Colour' });
|
|
47
|
+
} else controller.setGradient({ type: value as VectorGradientType });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function numberOf(e: Event & { currentTarget: HTMLInputElement }, fn: (n: number) => void) {
|
|
51
|
+
const n = parseFloat(e.currentTarget.value);
|
|
52
|
+
if (Number.isFinite(n)) fn(n);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function addAt(e: MouseEvent & { currentTarget: HTMLElement }) {
|
|
56
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
57
|
+
controller.addGradientStop(r.width > 0 ? Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) : 0.5);
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
{#if ed}
|
|
62
|
+
{@const sel = ed.selected}
|
|
63
|
+
<div class="space-y-1.5 {className}" data-vector-gradient-editor={ed.target} aria-label="Gradient">
|
|
64
|
+
<div class="flex items-center gap-1.5">
|
|
65
|
+
<span class="w-16 text-xs text-foreground">Gradient</span>
|
|
66
|
+
{#each ['fill', 'stroke'] as const as t (t)}
|
|
67
|
+
<button type="button" class={buttonClass} aria-pressed={ed.target === t} onclick={() => controller.setGradientTarget(t)}>{t === 'fill' ? 'Fill' : 'Stroke'}</button>
|
|
68
|
+
{/each}
|
|
69
|
+
{#if ed.mixed}<span class="text-[11px] text-muted-foreground">Mixed</span>{/if}
|
|
70
|
+
</div>
|
|
71
|
+
<div class="flex items-center gap-1.5">
|
|
72
|
+
<span class="w-16 text-xs text-foreground">Type</span>
|
|
73
|
+
<select aria-label="Gradient type" class={selectClass} value={ed.type ?? 'solid'} onchange={(e) => setType(e.currentTarget.value)}>
|
|
74
|
+
<option value="solid">Solid</option>
|
|
75
|
+
{#each VECTOR_GRADIENT_TYPES as t (t)}<option value={t}>{VECTOR_GRADIENT_TYPE_LABELS[t]}</option>{/each}
|
|
76
|
+
</select>
|
|
77
|
+
<button type="button" class={buttonClass} aria-label="Reverse gradient" title="Reverse gradient" disabled={!ed.gradient} onclick={() => controller.reverseGradient()}>⇄</button>
|
|
78
|
+
</div>
|
|
79
|
+
{#if ed.gradient}
|
|
80
|
+
<div class="relative h-8" data-vector-gradient-ramp="">
|
|
81
|
+
<div
|
|
82
|
+
role="button"
|
|
83
|
+
tabindex="-1"
|
|
84
|
+
aria-label="Add gradient stop"
|
|
85
|
+
class="absolute inset-x-1.5 top-0 h-4 cursor-copy rounded border border-border"
|
|
86
|
+
style="background-image: {ed.preview}, repeating-conic-gradient(#ddd 0% 25%, #fff 0% 50%); background-size: auto, 8px 8px"
|
|
87
|
+
onclick={addAt}
|
|
88
|
+
onkeydown={() => {}}
|
|
89
|
+
></div>
|
|
90
|
+
{#each ed.stops as s (s.index)}
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
data-vector-gradient-stop={s.index}
|
|
94
|
+
aria-label="Gradient stop {s.index + 1} at {percent(s.offset)}%"
|
|
95
|
+
aria-pressed={s.selected}
|
|
96
|
+
class="absolute top-4 h-3.5 w-3 -translate-x-1/2 rounded-sm border border-border aria-pressed:ring-2 aria-pressed:ring-primary"
|
|
97
|
+
style="left: calc(6px + (100% - 12px) * {s.offset}); background: {s.color}; opacity: {0.4 + 0.6 * (s.opacity ?? 1)}"
|
|
98
|
+
onclick={() => controller.selectGradientStop(s.selected ? null : s.index)}
|
|
99
|
+
></button>
|
|
100
|
+
{/each}
|
|
101
|
+
</div>
|
|
102
|
+
{#if sel}
|
|
103
|
+
<div class="flex flex-wrap items-center gap-1.5" data-vector-gradient-selected-stop={sel.index}>
|
|
104
|
+
<input
|
|
105
|
+
type="color"
|
|
106
|
+
aria-label="Stop colour"
|
|
107
|
+
class="h-7 w-9 cursor-pointer rounded border border-border bg-background"
|
|
108
|
+
value={vectorPaintToHex(sel.color) ?? '#000000'}
|
|
109
|
+
oninput={(e) => controller.setGradientStop(sel.index, { color: e.currentTarget.value }, { merge: `gradient-stop-color-${sel.index}` })}
|
|
110
|
+
onblur={end}
|
|
111
|
+
/>
|
|
112
|
+
<label class="flex items-center gap-1 text-[11px] text-muted-foreground">
|
|
113
|
+
Opacity
|
|
114
|
+
<input type="number" min="0" max="100" step="5" aria-label="Stop opacity" class={numberClass} value={percent(sel.opacity ?? 1)} oninput={(e) => numberOf(e, (n) => controller.setGradientStop(sel.index, { opacity: n / 100 }))} />
|
|
115
|
+
</label>
|
|
116
|
+
<label class="flex items-center gap-1 text-[11px] text-muted-foreground">
|
|
117
|
+
At
|
|
118
|
+
<input type="number" min="0" max="100" step="1" aria-label="Stop position" class={numberClass} value={percent(sel.offset)} oninput={(e) => numberOf(e, (n) => controller.setGradientStop(sel.index, { offset: n / 100 }))} />
|
|
119
|
+
</label>
|
|
120
|
+
{#if !sel.last}
|
|
121
|
+
<label class="flex items-center gap-1 text-[11px] text-muted-foreground">
|
|
122
|
+
Midpoint
|
|
123
|
+
<input type="number" min="5" max="95" step="1" aria-label="Stop midpoint" class={numberClass} value={percent(sel.midpoint ?? 0.5)} oninput={(e) => numberOf(e, (n) => controller.setGradientStop(sel.index, { midpoint: n / 100 }))} />
|
|
124
|
+
</label>
|
|
125
|
+
{/if}
|
|
126
|
+
<button type="button" class={buttonClass} aria-label="Delete stop" title="Delete stop" disabled={ed.stops.length <= 2} onclick={() => controller.removeGradientStop(sel.index)}>✕</button>
|
|
127
|
+
</div>
|
|
128
|
+
{/if}
|
|
129
|
+
<label class="flex items-center gap-1.5 text-xs text-foreground">
|
|
130
|
+
<span class="w-16">Colours</span>
|
|
131
|
+
<select aria-label="Gradient interpolation" class={selectClass} value={ed.interpolation} onchange={(e) => controller.setGradient({ interpolation: e.currentTarget.value as VectorGradientInterpolation })}>
|
|
132
|
+
{#each VECTOR_GRADIENT_INTERPOLATIONS as i (i)}<option value={i}>{VECTOR_GRADIENT_INTERPOLATION_LABELS[i]}</option>{/each}
|
|
133
|
+
</select>
|
|
134
|
+
</label>
|
|
135
|
+
<label class="flex items-center gap-1.5 text-xs text-foreground">
|
|
136
|
+
<span class="w-16">Smooth</span>
|
|
137
|
+
<input
|
|
138
|
+
type="range"
|
|
139
|
+
min="0"
|
|
140
|
+
max="1"
|
|
141
|
+
step="0.05"
|
|
142
|
+
aria-label="Gradient smoothness"
|
|
143
|
+
class="h-4 flex-1 accent-primary"
|
|
144
|
+
value={ed.smoothness}
|
|
145
|
+
oninput={(e) => controller.setGradient({ smoothness: parseFloat(e.currentTarget.value) }, { merge: 'gradient-smoothness' })}
|
|
146
|
+
onpointerup={end}
|
|
147
|
+
onblur={end}
|
|
148
|
+
/>
|
|
149
|
+
</label>
|
|
150
|
+
<label class="flex items-center gap-1.5 text-xs text-foreground">
|
|
151
|
+
<span class="w-16">Spread</span>
|
|
152
|
+
<select aria-label="Gradient spread" class={selectClass} value={ed.spread} onchange={(e) => controller.setGradient({ spread: e.currentTarget.value as VectorGradientSpread })}>
|
|
153
|
+
{#each VECTOR_GRADIENT_SPREADS as sp (sp)}<option value={sp}>{VECTOR_GRADIENT_SPREAD_LABELS[sp]}</option>{/each}
|
|
154
|
+
</select>
|
|
155
|
+
</label>
|
|
156
|
+
{#if ed.target === 'stroke'}
|
|
157
|
+
<label class="flex items-center gap-1.5 text-xs text-foreground">
|
|
158
|
+
<span class="w-16">On stroke</span>
|
|
159
|
+
<select aria-label="Gradient on stroke" class={selectClass} value={ed.strokeMode} onchange={(e) => controller.setGradient({ strokeMode: e.currentTarget.value as VectorStrokeGradientMode })}>
|
|
160
|
+
{#each VECTOR_STROKE_GRADIENT_MODES as m (m)}<option value={m}>{VECTOR_STROKE_GRADIENT_MODE_LABELS[m]}</option>{/each}
|
|
161
|
+
</select>
|
|
162
|
+
</label>
|
|
163
|
+
{/if}
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VectorEditorController } from '@pixerate/editor/vector';
|
|
2
|
+
interface Props {
|
|
3
|
+
controller: VectorEditorController;
|
|
4
|
+
class?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const VectorGradientEditor: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type VectorGradientEditor = ReturnType<typeof VectorGradientEditor>;
|
|
8
|
+
export default VectorGradientEditor;
|
|
9
|
+
//# sourceMappingURL=VectorGradientEditor.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VectorGradientEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorGradientEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAWH,KAAK,sBAAsB,EAK5B,MAAM,yBAAyB,CAAC;AAIjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoHH,QAAA,MAAM,oBAAoB,2CAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { vectorStateOf } from './subscribe.svelte.js';
|
|
19
19
|
import { vectorButtonClass } from './styles.js';
|
|
20
20
|
import VectorEffectsPanel from './VectorEffectsPanel.svelte';
|
|
21
|
+
import VectorEnvelopePanel from './VectorEnvelopePanel.svelte';
|
|
22
|
+
import VectorGradientEditor from './VectorGradientEditor.svelte';
|
|
21
23
|
|
|
22
24
|
interface Props {
|
|
23
25
|
controller: VectorEditorController;
|
|
@@ -103,6 +105,7 @@
|
|
|
103
105
|
{#if style?.mixed[key]}<span class="text-[11px] text-muted-foreground">Mixed</span>{/if}
|
|
104
106
|
</div>
|
|
105
107
|
{/each}
|
|
108
|
+
{#if style}<VectorGradientEditor {controller} />{/if}
|
|
106
109
|
<label class="flex items-center gap-2 text-xs text-foreground">
|
|
107
110
|
<span class="w-16">Width</span>
|
|
108
111
|
<input
|
|
@@ -219,5 +222,6 @@
|
|
|
219
222
|
/>
|
|
220
223
|
</label>
|
|
221
224
|
<VectorEffectsPanel {controller} />
|
|
225
|
+
<VectorEnvelopePanel {controller} />
|
|
222
226
|
{/if}
|
|
223
227
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorStylePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorStylePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAYH,KAAK,sBAAsB,EAG5B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"VectorStylePanel.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorStylePanel.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAYH,KAAK,sBAAsB,EAG5B,MAAM,yBAAyB,CAAC;AAQjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+JH,QAAA,MAAM,gBAAgB,2CAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -36,10 +36,22 @@
|
|
|
36
36
|
* spine, Replace Spine, Expand and Release (default `true`).
|
|
37
37
|
*/
|
|
38
38
|
showBlend?: boolean;
|
|
39
|
+
/** The Free Distort toggle of the selection box (default `true`). */
|
|
40
|
+
showDistort?: boolean;
|
|
39
41
|
class?: string;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
let {
|
|
44
|
+
let {
|
|
45
|
+
controller,
|
|
46
|
+
tools = VECTOR_TOOLS,
|
|
47
|
+
showHistory = true,
|
|
48
|
+
showArrange = true,
|
|
49
|
+
showSnap = true,
|
|
50
|
+
showPathEdit = true,
|
|
51
|
+
showBlend = true,
|
|
52
|
+
showDistort = true,
|
|
53
|
+
class: className = ''
|
|
54
|
+
}: Props = $props();
|
|
43
55
|
|
|
44
56
|
const snapTitle = 'Snap to objects';
|
|
45
57
|
const modKey = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform ?? '') ? '⌘' : 'Ctrl';
|
|
@@ -191,6 +203,21 @@
|
|
|
191
203
|
{/if}
|
|
192
204
|
</div>
|
|
193
205
|
{/if}
|
|
206
|
+
{#if showDistort}
|
|
207
|
+
<div class="ml-1 flex items-center gap-1" role="group" aria-label="Distort">
|
|
208
|
+
<button
|
|
209
|
+
type="button"
|
|
210
|
+
data-vector-free-distort=""
|
|
211
|
+
class={vectorButtonClass}
|
|
212
|
+
aria-pressed={view.freeDistort}
|
|
213
|
+
title="Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+{modKey}: symmetric perspective; {modKey}-drag a side to skew)"
|
|
214
|
+
aria-label="Free Distort"
|
|
215
|
+
onclick={() => controller.setFreeDistort(!view.freeDistort)}
|
|
216
|
+
>
|
|
217
|
+
Distort
|
|
218
|
+
</button>
|
|
219
|
+
</div>
|
|
220
|
+
{/if}
|
|
194
221
|
{#if showBlend}
|
|
195
222
|
<VectorBlendControls {controller} />
|
|
196
223
|
{/if}
|
|
@@ -20,6 +20,8 @@ interface Props {
|
|
|
20
20
|
* spine, Replace Spine, Expand and Release (default `true`).
|
|
21
21
|
*/
|
|
22
22
|
showBlend?: boolean;
|
|
23
|
+
/** The Free Distort toggle of the selection box (default `true`). */
|
|
24
|
+
showDistort?: boolean;
|
|
23
25
|
class?: string;
|
|
24
26
|
}
|
|
25
27
|
declare const VectorToolbar: import("svelte").Component<Props, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EASH,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAMjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"VectorToolbar.svelte.d.ts","sourceRoot":"","sources":["../../src/vector/VectorToolbar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EASH,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AAMjC,UAAU,KAAK;IACb,UAAU,EAAE,sBAAsB,CAAC;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAiJH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
package/dist/vector/index.d.ts
CHANGED
|
@@ -7,8 +7,11 @@ export { default as VectorWidthPointEditor } from "./VectorWidthPointEditor.svel
|
|
|
7
7
|
export { default as VectorToolbar } from "./VectorToolbar.svelte";
|
|
8
8
|
export { default as VectorStylePanel } from "./VectorStylePanel.svelte";
|
|
9
9
|
export { default as VectorEffectsPanel } from "./VectorEffectsPanel.svelte";
|
|
10
|
+
export { default as VectorEnvelopePanel } from "./VectorEnvelopePanel.svelte";
|
|
10
11
|
export { default as VectorSceneNodes } from "./VectorSceneNodes.svelte";
|
|
11
12
|
export { default as VectorGradientDefs } from "./VectorGradientDefs.svelte";
|
|
13
|
+
export { default as VectorGradientAnnotator } from "./VectorGradientAnnotator.svelte";
|
|
14
|
+
export { default as VectorGradientEditor } from "./VectorGradientEditor.svelte";
|
|
12
15
|
export * from "./createVectorEditor.svelte.js";
|
|
13
16
|
export { vectorStateOf } from "./subscribe.svelte.js";
|
|
14
17
|
export * from "./styles.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,qBAAqB,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAChF,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC7E,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/vector/index.js
CHANGED
|
@@ -4,8 +4,11 @@ export { default as VectorWidthPointEditor } from "./VectorWidthPointEditor.svel
|
|
|
4
4
|
export { default as VectorToolbar } from "./VectorToolbar.svelte";
|
|
5
5
|
export { default as VectorStylePanel } from "./VectorStylePanel.svelte";
|
|
6
6
|
export { default as VectorEffectsPanel } from "./VectorEffectsPanel.svelte";
|
|
7
|
+
export { default as VectorEnvelopePanel } from "./VectorEnvelopePanel.svelte";
|
|
7
8
|
export { default as VectorSceneNodes } from "./VectorSceneNodes.svelte";
|
|
8
9
|
export { default as VectorGradientDefs } from "./VectorGradientDefs.svelte";
|
|
10
|
+
export { default as VectorGradientAnnotator } from "./VectorGradientAnnotator.svelte";
|
|
11
|
+
export { default as VectorGradientEditor } from "./VectorGradientEditor.svelte";
|
|
9
12
|
export * from "./createVectorEditor.svelte.js";
|
|
10
13
|
export { vectorStateOf } from "./subscribe.svelte.js";
|
|
11
14
|
export * from "./styles.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"@tiptap/extension-bubble-menu": "^2.11.5",
|
|
108
108
|
"@tiptap/extension-placeholder": "^2.11.5",
|
|
109
109
|
"@tiptap/starter-kit": "^2.11.5",
|
|
110
|
-
"@pixerate/editor": "0.
|
|
110
|
+
"@pixerate/editor": "0.44.0"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
113
|
"@dagrejs/dagre": "^1.1.4",
|