@gradio/imageeditor 0.12.13 → 0.13.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/CHANGELOG.md +23 -0
- package/Example.svelte +1 -1
- package/IMAGE_EDITOR_OVERVIEW.md +224 -0
- package/ImageEditor.stories.svelte +4 -4
- package/Index.svelte +35 -46
- package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +56 -103
- package/dist/Example.svelte.d.ts +1 -1
- package/dist/Index.svelte +29 -33
- package/dist/Index.svelte.d.ts +17 -16
- package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +47 -88
- package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +13 -15
- package/dist/shared/Anchor.svelte +311 -0
- package/dist/shared/Anchor.svelte.d.ts +18 -0
- package/dist/shared/Controls.svelte +162 -23
- package/dist/shared/Controls.svelte.d.ts +7 -2
- package/dist/shared/{tools/IconButton.svelte → IconButton.svelte} +19 -3
- package/dist/shared/{tools/IconButton.svelte.d.ts → IconButton.svelte.d.ts} +4 -1
- package/dist/shared/ImageEditor.svelte +546 -289
- package/dist/shared/ImageEditor.svelte.d.ts +51 -56
- package/dist/shared/Layers.svelte +233 -0
- package/dist/shared/Layers.svelte.d.ts +35 -0
- package/dist/shared/Resize.svelte +218 -0
- package/dist/shared/Resize.svelte.d.ts +29 -0
- package/dist/shared/SecondaryToolbar.svelte +54 -0
- package/dist/shared/SecondaryToolbar.svelte.d.ts +35 -0
- package/dist/shared/Toolbar.svelte +337 -0
- package/dist/shared/Toolbar.svelte.d.ts +42 -0
- package/dist/shared/{tools → brush}/BrushOptions.svelte +68 -64
- package/dist/shared/{tools → brush}/BrushOptions.svelte.d.ts +9 -9
- package/dist/shared/{tools → brush}/BrushSize.svelte +2 -27
- package/dist/shared/{tools → brush}/ColorField.svelte +2 -1
- package/dist/shared/{tools → brush}/ColorField.svelte.d.ts +1 -0
- package/dist/shared/{tools → brush}/ColorPicker.svelte +81 -9
- package/dist/shared/{tools → brush}/ColorPicker.svelte.d.ts +1 -0
- package/dist/shared/{tools → brush}/ColorSwatch.svelte +78 -38
- package/dist/shared/{tools → brush}/ColorSwatch.svelte.d.ts +6 -2
- package/dist/shared/brush/brush-cursor.d.ts +101 -0
- package/dist/shared/brush/brush-cursor.js +327 -0
- package/dist/shared/brush/brush-textures.d.ts +71 -0
- package/dist/shared/brush/brush-textures.js +404 -0
- package/dist/shared/brush/brush-utils.d.ts +32 -0
- package/dist/shared/brush/brush-utils.js +59 -0
- package/dist/shared/brush/brush.d.ts +152 -0
- package/dist/shared/brush/brush.js +396 -0
- package/dist/shared/brush/types.d.ts +37 -0
- package/dist/shared/brush/types.js +1 -0
- package/dist/shared/core/editor.d.ts +252 -0
- package/dist/shared/core/editor.js +1221 -0
- package/dist/shared/crop/crop.d.ts +201 -0
- package/dist/shared/crop/crop.js +727 -0
- package/dist/shared/image/image.d.ts +89 -0
- package/dist/shared/image/image.js +185 -0
- package/dist/shared/resize/resize.d.ts +270 -0
- package/dist/shared/resize/resize.js +1413 -0
- package/dist/shared/types.d.ts +12 -0
- package/dist/shared/utils/pixi.d.ts +16 -57
- package/dist/shared/utils/pixi.js +15 -113
- package/dist/shared/zoom/zoom.d.ts +136 -0
- package/dist/shared/zoom/zoom.js +548 -0
- package/package.json +11 -10
- package/shared/Anchor.svelte +384 -0
- package/shared/Controls.svelte +194 -27
- package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
- package/shared/ImageEditor.svelte +682 -349
- package/shared/Layers.svelte +249 -0
- package/shared/Resize.svelte +239 -0
- package/shared/SecondaryToolbar.svelte +61 -0
- package/shared/Toolbar.svelte +386 -0
- package/shared/brush/BRUSH_TOOL.md +134 -0
- package/shared/brush/BrushOptions.svelte +200 -0
- package/shared/{tools → brush}/BrushSize.svelte +2 -30
- package/shared/{tools → brush}/ColorField.svelte +3 -1
- package/shared/{tools → brush}/ColorPicker.svelte +86 -9
- package/shared/{tools → brush}/ColorSwatch.svelte +95 -42
- package/shared/brush/brush-cursor.ts +408 -0
- package/shared/brush/brush-textures.ts +508 -0
- package/shared/brush/brush-utils.ts +76 -0
- package/shared/brush/brush.ts +506 -0
- package/shared/brush/types.ts +41 -0
- package/shared/core/EDITOR.md +182 -0
- package/shared/core/editor.ts +1730 -0
- package/shared/crop/CROP.md +139 -0
- package/shared/crop/crop.ts +1027 -0
- package/shared/image/IMAGE.md +135 -0
- package/shared/image/image.ts +300 -0
- package/shared/resize/resize.ts +1894 -0
- package/shared/types.ts +14 -0
- package/shared/utils/pixi.ts +19 -246
- package/shared/zoom/zoom.ts +729 -0
- package/dist/shared/layers/Layers.svelte +0 -231
- package/dist/shared/layers/Layers.svelte.d.ts +0 -18
- package/dist/shared/layers/utils.d.ts +0 -73
- package/dist/shared/layers/utils.js +0 -156
- package/dist/shared/tools/Brush.svelte +0 -215
- package/dist/shared/tools/Brush.svelte.d.ts +0 -29
- package/dist/shared/tools/Crop.svelte +0 -155
- package/dist/shared/tools/Crop.svelte.d.ts +0 -16
- package/dist/shared/tools/Cropper.svelte +0 -271
- package/dist/shared/tools/Cropper.svelte.d.ts +0 -41
- package/dist/shared/tools/Handle.svelte +0 -260
- package/dist/shared/tools/Handle.svelte.d.ts +0 -29
- package/dist/shared/tools/Sources.svelte +0 -257
- package/dist/shared/tools/Sources.svelte.d.ts +0 -34
- package/dist/shared/tools/Tools.svelte +0 -102
- package/dist/shared/tools/Tools.svelte.d.ts +0 -48
- package/dist/shared/tools/brush.d.ts +0 -62
- package/dist/shared/tools/brush.js +0 -133
- package/dist/shared/tools/crop.d.ts +0 -15
- package/dist/shared/tools/crop.js +0 -154
- package/dist/shared/tools/index.d.ts +0 -5
- package/dist/shared/tools/index.js +0 -5
- package/dist/shared/tools/sources.d.ts +0 -46
- package/dist/shared/tools/sources.js +0 -121
- package/dist/shared/tools/types.d.ts +0 -1
- package/shared/layers/Layers.svelte +0 -250
- package/shared/layers/utils.ts +0 -294
- package/shared/tools/Brush.svelte +0 -280
- package/shared/tools/BrushOptions.svelte +0 -180
- package/shared/tools/Crop.svelte +0 -189
- package/shared/tools/Cropper.svelte +0 -346
- package/shared/tools/Handle.svelte +0 -289
- package/shared/tools/Sources.svelte +0 -283
- package/shared/tools/Tools.svelte +0 -162
- package/shared/tools/brush.ts +0 -247
- package/shared/tools/crop.ts +0 -213
- package/shared/tools/index.ts +0 -5
- package/shared/tools/sources.ts +0 -195
- package/shared/tools/types.ts +0 -1
- /package/dist/shared/{tools → brush}/BrushSize.svelte.d.ts +0 -0
- /package/dist/shared/{tools/types.js → types.js} +0 -0
|
@@ -3,45 +3,40 @@
|
|
|
3
3
|
<script>import { createEventDispatcher } from "svelte";
|
|
4
4
|
import {} from "@gradio/utils";
|
|
5
5
|
import { prepare_files } from "@gradio/client";
|
|
6
|
-
import {} from "./utils/commands";
|
|
7
|
-
import ImageEditor from "./ImageEditor.svelte";
|
|
8
|
-
import
|
|
9
|
-
import {} from "./
|
|
10
|
-
import {} from "./tools/Brush.svelte";
|
|
11
|
-
import { Tools, Crop, Brush, Sources } from "./tools";
|
|
6
|
+
import {} from "./shared/utils/commands";
|
|
7
|
+
import ImageEditor from "./shared/ImageEditor.svelte";
|
|
8
|
+
import {} from "./shared/brush/types";
|
|
9
|
+
import {} from "./shared/Toolbar.svelte";
|
|
12
10
|
import { BlockLabel } from "@gradio/atoms";
|
|
13
11
|
import { Image as ImageIcon } from "@gradio/icons";
|
|
14
|
-
import { inject } from "./utils/parse_placeholder";
|
|
12
|
+
import { inject } from "./shared/utils/parse_placeholder";
|
|
13
|
+
import {
|
|
14
|
+
} from "./shared/types";
|
|
15
15
|
export let brush;
|
|
16
16
|
export let eraser;
|
|
17
17
|
export let sources;
|
|
18
|
-
export let crop_size = null;
|
|
19
18
|
export let i18n;
|
|
20
19
|
export let root;
|
|
21
20
|
export let label = void 0;
|
|
22
21
|
export let show_label;
|
|
23
22
|
export let changeable = false;
|
|
24
|
-
export let value = {
|
|
25
|
-
background: null,
|
|
26
|
-
layers: [],
|
|
27
|
-
composite: null
|
|
28
|
-
};
|
|
29
|
-
export let transforms = ["crop"];
|
|
30
23
|
export let layers;
|
|
24
|
+
export let composite;
|
|
25
|
+
export let background;
|
|
26
|
+
export let layer_options;
|
|
27
|
+
export let transforms;
|
|
31
28
|
export let accept_blobs;
|
|
32
|
-
export let status = "complete";
|
|
33
29
|
export let canvas_size;
|
|
34
30
|
export let fixed_canvas = false;
|
|
35
31
|
export let realtime;
|
|
36
32
|
export let upload;
|
|
37
|
-
export let
|
|
38
|
-
export let dragging;
|
|
33
|
+
export let is_dragging;
|
|
39
34
|
export let placeholder = void 0;
|
|
40
|
-
export let
|
|
41
|
-
export let height;
|
|
35
|
+
export let border_region;
|
|
42
36
|
export let full_history = null;
|
|
43
37
|
const dispatch = createEventDispatcher();
|
|
44
38
|
let editor;
|
|
39
|
+
let has_drawn = false;
|
|
45
40
|
function is_not_null(o) {
|
|
46
41
|
return !!o;
|
|
47
42
|
}
|
|
@@ -49,7 +44,7 @@ function is_file_data(o) {
|
|
|
49
44
|
return !!o;
|
|
50
45
|
}
|
|
51
46
|
$:
|
|
52
|
-
if (
|
|
47
|
+
if (background_image)
|
|
53
48
|
dispatch("upload");
|
|
54
49
|
export async function get_data() {
|
|
55
50
|
let blobs;
|
|
@@ -58,45 +53,41 @@ export async function get_data() {
|
|
|
58
53
|
} catch (e) {
|
|
59
54
|
return { background: null, layers: [], composite: null };
|
|
60
55
|
}
|
|
61
|
-
const
|
|
56
|
+
const bg = blobs.background ? upload(
|
|
62
57
|
await prepare_files([new File([blobs.background], "background.png")]),
|
|
63
58
|
root
|
|
64
59
|
) : Promise.resolve(null);
|
|
65
60
|
const layers2 = blobs.layers.filter(is_not_null).map(
|
|
66
61
|
async (blob, i) => upload(await prepare_files([new File([blob], `layer_${i}.png`)]), root)
|
|
67
62
|
);
|
|
68
|
-
const
|
|
63
|
+
const composite2 = blobs.composite ? upload(
|
|
69
64
|
await prepare_files([new File([blobs.composite], "composite.png")]),
|
|
70
65
|
root
|
|
71
66
|
) : Promise.resolve(null);
|
|
72
|
-
const [
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
const [background2, composite_, ...layers_] = await Promise.all([
|
|
68
|
+
bg,
|
|
69
|
+
composite2,
|
|
75
70
|
...layers2
|
|
76
71
|
]);
|
|
77
72
|
return {
|
|
78
|
-
background: Array.isArray(
|
|
73
|
+
background: Array.isArray(background2) ? background2[0] : background2,
|
|
79
74
|
layers: layers_.flatMap((layer) => Array.isArray(layer) ? layer : [layer]).filter(is_file_data),
|
|
80
75
|
composite: Array.isArray(composite_) ? composite_[0] : composite_
|
|
81
76
|
};
|
|
82
77
|
}
|
|
83
|
-
function handle_value(
|
|
78
|
+
function handle_value(value) {
|
|
84
79
|
if (!editor)
|
|
85
80
|
return;
|
|
86
|
-
if (
|
|
81
|
+
if (value == null) {
|
|
87
82
|
editor.handle_remove();
|
|
88
83
|
dispatch("receive_null");
|
|
89
84
|
}
|
|
90
85
|
}
|
|
91
86
|
$:
|
|
92
|
-
handle_value(
|
|
93
|
-
|
|
94
|
-
crop_constraint = crop_size;
|
|
95
|
-
let bg = false;
|
|
87
|
+
handle_value({ layers, composite, background });
|
|
88
|
+
let background_image = false;
|
|
96
89
|
let history = false;
|
|
97
90
|
export let image_id = null;
|
|
98
|
-
$:
|
|
99
|
-
editor && editor.set_tool && (sources && sources.length ? editor.set_tool("bg") : editor.set_tool("draw"));
|
|
100
91
|
function nextframe() {
|
|
101
92
|
return new Promise((resolve) => setTimeout(() => resolve(), 30));
|
|
102
93
|
}
|
|
@@ -155,13 +146,9 @@ async function handle_change(e) {
|
|
|
155
146
|
handle_change(e);
|
|
156
147
|
}
|
|
157
148
|
}
|
|
158
|
-
let active_mode = null;
|
|
159
|
-
let editor_height = height - 100;
|
|
160
|
-
let _dynamic_height;
|
|
161
|
-
$:
|
|
162
|
-
dynamic_height = _dynamic_height;
|
|
163
149
|
$:
|
|
164
150
|
[heading, paragraph] = placeholder ? inject(placeholder) : [false, false];
|
|
151
|
+
let current_tool;
|
|
165
152
|
</script>
|
|
166
153
|
|
|
167
154
|
<BlockLabel
|
|
@@ -170,60 +157,34 @@ $:
|
|
|
170
157
|
label={label || i18n("image.image")}
|
|
171
158
|
/>
|
|
172
159
|
<ImageEditor
|
|
160
|
+
{transforms}
|
|
161
|
+
{composite}
|
|
162
|
+
{layers}
|
|
163
|
+
{background}
|
|
173
164
|
on:history
|
|
174
165
|
{canvas_size}
|
|
175
|
-
crop_size={Array.isArray(crop_size) ? crop_size : undefined}
|
|
176
166
|
bind:this={editor}
|
|
177
|
-
bind:height={editor_height}
|
|
178
|
-
bind:canvas_height={_dynamic_height}
|
|
179
|
-
parent_height={height}
|
|
180
167
|
{changeable}
|
|
181
168
|
on:save
|
|
182
169
|
on:change={handle_change}
|
|
183
170
|
on:clear={() => dispatch("clear")}
|
|
184
|
-
bind:history
|
|
185
|
-
bind:bg
|
|
186
171
|
{sources}
|
|
187
|
-
crop_constraint={!!crop_constraint}
|
|
188
172
|
{full_history}
|
|
173
|
+
bind:background_image
|
|
174
|
+
bind:current_tool
|
|
175
|
+
brush_options={brush}
|
|
176
|
+
eraser_options={eraser}
|
|
177
|
+
{fixed_canvas}
|
|
178
|
+
{border_region}
|
|
179
|
+
{layer_options}
|
|
180
|
+
{i18n}
|
|
181
|
+
{root}
|
|
182
|
+
{upload}
|
|
183
|
+
bind:is_dragging
|
|
184
|
+
bind:has_drawn
|
|
189
185
|
>
|
|
190
|
-
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
<Sources
|
|
194
|
-
bind:dragging
|
|
195
|
-
{i18n}
|
|
196
|
-
{root}
|
|
197
|
-
{sources}
|
|
198
|
-
{upload}
|
|
199
|
-
{stream_handler}
|
|
200
|
-
{canvas_size}
|
|
201
|
-
bind:bg
|
|
202
|
-
bind:active_mode
|
|
203
|
-
background_file={value?.background || value?.composite || null}
|
|
204
|
-
{fixed_canvas}
|
|
205
|
-
></Sources>
|
|
206
|
-
|
|
207
|
-
{#if transforms.includes("crop")}
|
|
208
|
-
<Crop {crop_constraint} />
|
|
209
|
-
{/if}
|
|
210
|
-
{#if brush}
|
|
211
|
-
<Brush
|
|
212
|
-
color_mode={brush.color_mode}
|
|
213
|
-
default_color={brush.default_color}
|
|
214
|
-
default_size={brush.default_size}
|
|
215
|
-
colors={brush.colors}
|
|
216
|
-
mode="draw"
|
|
217
|
-
/>
|
|
218
|
-
{/if}
|
|
219
|
-
|
|
220
|
-
{#if brush && eraser}
|
|
221
|
-
<Brush default_size={eraser.default_size} mode="erase" />
|
|
222
|
-
{/if}
|
|
223
|
-
</Tools>
|
|
224
|
-
|
|
225
|
-
{#if !bg && !history && active_mode !== "webcam" && status !== "error"}
|
|
226
|
-
<div class="empty wrap" style:height={`${editor_height}px`}>
|
|
186
|
+
{#if !background_image && current_tool === "image" && !has_drawn}
|
|
187
|
+
<div class="empty wrap">
|
|
227
188
|
{#if sources && sources.length}
|
|
228
189
|
{#if heading || paragraph}
|
|
229
190
|
{#if heading}
|
|
@@ -268,10 +229,10 @@ $:
|
|
|
268
229
|
left: 0;
|
|
269
230
|
right: 0;
|
|
270
231
|
margin: auto;
|
|
271
|
-
z-index: var(--layer-
|
|
232
|
+
z-index: var(--layer-1);
|
|
272
233
|
text-align: center;
|
|
273
|
-
color: var(--
|
|
274
|
-
|
|
234
|
+
color: var(--color-grey-500) !important;
|
|
235
|
+
cursor: pointer;
|
|
275
236
|
}
|
|
276
237
|
|
|
277
238
|
.wrap {
|
|
@@ -279,10 +240,8 @@ $:
|
|
|
279
240
|
flex-direction: column;
|
|
280
241
|
justify-content: center;
|
|
281
242
|
align-items: center;
|
|
282
|
-
color: var(--block-label-text-color);
|
|
283
243
|
line-height: var(--line-md);
|
|
284
244
|
font-size: var(--text-md);
|
|
285
|
-
pointer-events: none;
|
|
286
245
|
}
|
|
287
246
|
|
|
288
247
|
.or {
|
package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts}
RENAMED
|
@@ -11,34 +11,32 @@ export interface ImageBlobs {
|
|
|
11
11
|
}
|
|
12
12
|
import { type I18nFormatter } from "@gradio/utils";
|
|
13
13
|
import { type FileData, type Client } from "@gradio/client";
|
|
14
|
-
import { type CommandNode } from "./utils/commands";
|
|
15
|
-
import { type Brush as IBrush } from "./
|
|
16
|
-
import { type
|
|
14
|
+
import { type CommandNode } from "./shared/utils/commands";
|
|
15
|
+
import { type Brush as IBrush, type Eraser } from "./shared/brush/types";
|
|
16
|
+
import { type LayerOptions, type Transform, type Source } from "./shared/types";
|
|
17
17
|
declare const __propDef: {
|
|
18
18
|
props: {
|
|
19
|
-
brush: IBrush
|
|
20
|
-
eraser: Eraser
|
|
21
|
-
sources:
|
|
22
|
-
crop_size?: ([number, number] | `${string}:${string}` | null) | undefined;
|
|
19
|
+
brush: IBrush;
|
|
20
|
+
eraser: Eraser;
|
|
21
|
+
sources: Source[];
|
|
23
22
|
i18n: I18nFormatter;
|
|
24
23
|
root: string;
|
|
25
24
|
label?: string | undefined;
|
|
26
25
|
show_label: boolean;
|
|
27
26
|
changeable?: boolean | undefined;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
layers: FileData[];
|
|
28
|
+
composite: FileData | null;
|
|
29
|
+
background: FileData | null;
|
|
30
|
+
layer_options: LayerOptions;
|
|
31
|
+
transforms: Transform[];
|
|
31
32
|
accept_blobs: (a: any) => void;
|
|
32
|
-
status?: ("pending" | "complete" | "error" | "generating" | "streaming") | undefined;
|
|
33
33
|
canvas_size: [number, number];
|
|
34
34
|
fixed_canvas?: boolean | undefined;
|
|
35
35
|
realtime: boolean;
|
|
36
36
|
upload: Client["upload"];
|
|
37
|
-
|
|
38
|
-
dragging: boolean;
|
|
37
|
+
is_dragging: boolean;
|
|
39
38
|
placeholder?: string | undefined;
|
|
40
|
-
|
|
41
|
-
height: any;
|
|
39
|
+
border_region: number;
|
|
42
40
|
full_history?: (CommandNode | null) | undefined;
|
|
43
41
|
get_data?: (() => Promise<ImageBlobs>) | undefined;
|
|
44
42
|
image_id?: (null | string) | undefined;
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
<script>import { spring } from "svelte/motion";
|
|
2
|
+
import { Image } from "@gradio/icons";
|
|
3
|
+
import { onMount, createEventDispatcher } from "svelte";
|
|
4
|
+
const positions = [
|
|
5
|
+
"top-left",
|
|
6
|
+
"top",
|
|
7
|
+
"top-right",
|
|
8
|
+
"left",
|
|
9
|
+
"center",
|
|
10
|
+
"right",
|
|
11
|
+
"bottom-left",
|
|
12
|
+
"bottom",
|
|
13
|
+
"bottom-right"
|
|
14
|
+
];
|
|
15
|
+
const dispatch = createEventDispatcher();
|
|
16
|
+
const spring_opt = {
|
|
17
|
+
stiffness: 0.1,
|
|
18
|
+
precision: 0.5
|
|
19
|
+
};
|
|
20
|
+
const pos = spring([5, 5], spring_opt);
|
|
21
|
+
const init = [(120 - 10) / 3, (120 - 10) / 3];
|
|
22
|
+
const dimensions = spring(init, spring_opt);
|
|
23
|
+
const arrow_spring = spring([], spring_opt);
|
|
24
|
+
let last_i = 0;
|
|
25
|
+
let expanded = true;
|
|
26
|
+
const box_size = (120 - 5 * 2) / 3;
|
|
27
|
+
async function handle_box_hover(i) {
|
|
28
|
+
expanded = false;
|
|
29
|
+
last_i = i;
|
|
30
|
+
const y = Math.floor(i / 3);
|
|
31
|
+
const x = i % 3;
|
|
32
|
+
pos.set([x * box_size + 5 * x + 5, y * box_size + 5 * y + 5]);
|
|
33
|
+
dimensions.set(init);
|
|
34
|
+
}
|
|
35
|
+
let last_pos = 0;
|
|
36
|
+
async function handle_box_click(i, stagger = false) {
|
|
37
|
+
if (expanded && stagger)
|
|
38
|
+
return;
|
|
39
|
+
const y = Math.floor(i / 3);
|
|
40
|
+
const x = i % 3;
|
|
41
|
+
const [initial_arrows, eventual_arrows] = get_valid_offsets({
|
|
42
|
+
x,
|
|
43
|
+
y
|
|
44
|
+
});
|
|
45
|
+
if (stagger && last_i !== i) {
|
|
46
|
+
await Promise.all([
|
|
47
|
+
pos.set([x * box_size + 5 * x + 5, y * box_size + 5 * y + 5]),
|
|
48
|
+
arrow_spring.set(initial_arrows, { hard: true }),
|
|
49
|
+
dimensions.set(init)
|
|
50
|
+
]);
|
|
51
|
+
} else {
|
|
52
|
+
await arrow_spring.set(initial_arrows, { hard: true });
|
|
53
|
+
}
|
|
54
|
+
last_pos = i;
|
|
55
|
+
expanded = true;
|
|
56
|
+
pos.set([0, 0]);
|
|
57
|
+
dimensions.set([120 + 10, 120 + 10]);
|
|
58
|
+
arrow_spring.set(eventual_arrows);
|
|
59
|
+
dispatch("position", positions[i]);
|
|
60
|
+
}
|
|
61
|
+
function get_valid_offsets(anchorPoint) {
|
|
62
|
+
const destination_points = [
|
|
63
|
+
{ x: 0, y: 0 },
|
|
64
|
+
{ x: 1, y: 0 },
|
|
65
|
+
{ x: 2, y: 0 },
|
|
66
|
+
{ x: 0, y: 1 },
|
|
67
|
+
{ x: 1, y: 1 },
|
|
68
|
+
{ x: 2, y: 1 },
|
|
69
|
+
{ x: 0, y: 2 },
|
|
70
|
+
{ x: 1, y: 2 },
|
|
71
|
+
{ x: 2, y: 2 }
|
|
72
|
+
];
|
|
73
|
+
const offsets = destination_points.map((dest) => ({
|
|
74
|
+
x: dest.x - anchorPoint.x,
|
|
75
|
+
y: dest.y - anchorPoint.y,
|
|
76
|
+
dest
|
|
77
|
+
})).filter((offset) => !(offset.x === 0 && offset.y === 0));
|
|
78
|
+
const normalized_offsets = offsets.map((offset) => {
|
|
79
|
+
if (offset.x === 0 || offset.y === 0) {
|
|
80
|
+
const magnitude = Math.max(Math.abs(offset.x), Math.abs(offset.y));
|
|
81
|
+
return {
|
|
82
|
+
magnitude,
|
|
83
|
+
x: offset.x / magnitude,
|
|
84
|
+
y: offset.y / magnitude,
|
|
85
|
+
dest: offset.dest,
|
|
86
|
+
original: offset,
|
|
87
|
+
type: "cardinal"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (Math.abs(offset.x) === Math.abs(offset.y)) {
|
|
91
|
+
const magnitude = Math.abs(offset.x);
|
|
92
|
+
return {
|
|
93
|
+
magnitude,
|
|
94
|
+
x: offset.x / magnitude,
|
|
95
|
+
y: offset.y / magnitude,
|
|
96
|
+
dest: offset.dest,
|
|
97
|
+
original: offset,
|
|
98
|
+
type: "ordinal"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}).filter((offset) => offset !== null);
|
|
103
|
+
const directions = /* @__PURE__ */ new Map();
|
|
104
|
+
normalized_offsets.forEach((offset) => {
|
|
105
|
+
if (directions.has(`${offset.x}-${offset.y}`)) {
|
|
106
|
+
directions.get(`${offset.x}-${offset.y}`)?.push(offset);
|
|
107
|
+
} else {
|
|
108
|
+
directions.set(`${offset.x}-${offset.y}`, [offset]);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const unique_directions = Array.from(directions.values()).map(
|
|
112
|
+
(direction) => {
|
|
113
|
+
return direction.sort((a, b) => b.magnitude - a.magnitude)[0];
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
const eventual_arrows = unique_directions.map((arrow) => ({
|
|
117
|
+
x: arrow.dest.x * box_size + 14 * arrow.dest.x + 14 + 10 + (arrow.type === "ordinal" ? 0 : 12 * arrow.x),
|
|
118
|
+
y: arrow.dest.y * box_size + 14 * arrow.dest.y + 14 + 10 + (arrow.type === "ordinal" ? 0 : 12 * arrow.y),
|
|
119
|
+
x_dir: arrow.x,
|
|
120
|
+
y_dir: arrow.y,
|
|
121
|
+
rotation: Math.atan2(arrow.original.y, arrow.original.x) * (180 / Math.PI) + 180,
|
|
122
|
+
type: arrow.type === "ordinal" ? 1 : 2
|
|
123
|
+
}));
|
|
124
|
+
const initial_arrows = eventual_arrows.map((arrow) => ({
|
|
125
|
+
...arrow,
|
|
126
|
+
x: anchorPoint.x * box_size + 14 * anchorPoint.x + // gaps
|
|
127
|
+
14 + // padding
|
|
128
|
+
10 + // arrow head
|
|
129
|
+
arrow.x_dir * box_size / 2 + arrow.x_dir * (arrow.type === 1 ? 0 : 10),
|
|
130
|
+
// offset based on direction
|
|
131
|
+
y: anchorPoint.y * box_size + 14 * anchorPoint.y + 14 + 10 + arrow.y_dir * box_size / 2 + arrow.y_dir * (arrow.type === 1 ? 0 : 10)
|
|
132
|
+
}));
|
|
133
|
+
return [initial_arrows, eventual_arrows];
|
|
134
|
+
}
|
|
135
|
+
onMount(() => {
|
|
136
|
+
handle_box_click(0);
|
|
137
|
+
});
|
|
138
|
+
</script>
|
|
139
|
+
|
|
140
|
+
<div class="wrap" class:expanded>
|
|
141
|
+
<div
|
|
142
|
+
class="box-wrap"
|
|
143
|
+
role="grid"
|
|
144
|
+
tabindex="0"
|
|
145
|
+
on:mouseleave={() => handle_box_click(last_pos, true)}
|
|
146
|
+
>
|
|
147
|
+
{#each { length: 9 } as _, i}
|
|
148
|
+
<button
|
|
149
|
+
class="box"
|
|
150
|
+
class:active-box={last_pos === i}
|
|
151
|
+
on:mouseenter={() => handle_box_hover(i)}
|
|
152
|
+
on:click={() => handle_box_click(i)}
|
|
153
|
+
>
|
|
154
|
+
<span class:active={last_pos === i} class="icon-wrap">
|
|
155
|
+
<Image />
|
|
156
|
+
</span>
|
|
157
|
+
</button>
|
|
158
|
+
{/each}
|
|
159
|
+
</div>
|
|
160
|
+
<div
|
|
161
|
+
class="expanding-box"
|
|
162
|
+
style:width="{$dimensions[0]}px"
|
|
163
|
+
style:height="{$dimensions[1]}px"
|
|
164
|
+
style:transform="translate({$pos[0]}px, {$pos[1]}px)"
|
|
165
|
+
></div>
|
|
166
|
+
<svg viewBox="0 0 150 150">
|
|
167
|
+
{#each $arrow_spring as arrow}
|
|
168
|
+
<line
|
|
169
|
+
x1={(last_pos % 3) * box_size +
|
|
170
|
+
14 * (last_pos % 3) +
|
|
171
|
+
14 +
|
|
172
|
+
10 +
|
|
173
|
+
(arrow.x_dir * box_size) / 2 +
|
|
174
|
+
arrow.x_dir * (arrow.type === 1 ? 0 : 10)}
|
|
175
|
+
x2={arrow.x}
|
|
176
|
+
y1={Math.floor(last_pos / 3) * box_size +
|
|
177
|
+
14 * Math.floor(last_pos / 3) +
|
|
178
|
+
14 +
|
|
179
|
+
10 +
|
|
180
|
+
(arrow.y_dir * box_size) / 2 +
|
|
181
|
+
arrow.y_dir * (arrow.type === 1 ? 0 : 10)}
|
|
182
|
+
y2={arrow.y}
|
|
183
|
+
/>
|
|
184
|
+
{/each}
|
|
185
|
+
{#each $arrow_spring as arrow}
|
|
186
|
+
<g style:transform="translate(-75px, -75px)">
|
|
187
|
+
<g style:transform="translate({arrow.x}px, {arrow.y}px)">
|
|
188
|
+
<path
|
|
189
|
+
d="M 70,75 80,69 80,81 Z"
|
|
190
|
+
style:transform="rotate({arrow.rotation}deg)"
|
|
191
|
+
style:transform-origin="75 75"
|
|
192
|
+
/>
|
|
193
|
+
</g>
|
|
194
|
+
</g>
|
|
195
|
+
{/each}
|
|
196
|
+
</svg>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<style>
|
|
200
|
+
.wrap {
|
|
201
|
+
position: relative;
|
|
202
|
+
margin: 10px 0;
|
|
203
|
+
height: 130px;
|
|
204
|
+
width: 130px;
|
|
205
|
+
padding: 5px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.box-wrap {
|
|
209
|
+
width: 100%;
|
|
210
|
+
height: 100%;
|
|
211
|
+
display: grid;
|
|
212
|
+
grid-template-columns: repeat(3, 1fr);
|
|
213
|
+
grid-template-rows: repeat(3, 1fr);
|
|
214
|
+
gap: 5px;
|
|
215
|
+
|
|
216
|
+
background-color: var(--block-background-fill);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.box {
|
|
220
|
+
cursor: pointer;
|
|
221
|
+
border: none;
|
|
222
|
+
border: #ccc dashed 1px;
|
|
223
|
+
border-radius: 4px;
|
|
224
|
+
transition: 0.1s;
|
|
225
|
+
display: flex;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
align-items: center;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.box::after {
|
|
231
|
+
background: none !important;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.expanding-box {
|
|
235
|
+
--mod: 3;
|
|
236
|
+
--border: 2;
|
|
237
|
+
border: orange solid 2px;
|
|
238
|
+
border-radius: 4px;
|
|
239
|
+
position: absolute;
|
|
240
|
+
top: 0px;
|
|
241
|
+
left: 0px;
|
|
242
|
+
width: 100%;
|
|
243
|
+
height: 100%;
|
|
244
|
+
pointer-events: none;
|
|
245
|
+
|
|
246
|
+
width: calc(100% / var(--mod));
|
|
247
|
+
height: calc(100% / var(--mod));
|
|
248
|
+
transform-origin: 50% 0;
|
|
249
|
+
box-sizing: border-box;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
svg {
|
|
253
|
+
position: absolute;
|
|
254
|
+
top: 0px;
|
|
255
|
+
left: 0px;
|
|
256
|
+
width: 100%;
|
|
257
|
+
height: 100%;
|
|
258
|
+
|
|
259
|
+
pointer-events: none;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.icon-wrap {
|
|
263
|
+
opacity: 0.2;
|
|
264
|
+
height: 50%;
|
|
265
|
+
width: 50%;
|
|
266
|
+
|
|
267
|
+
display: flex;
|
|
268
|
+
justify-content: center;
|
|
269
|
+
align-items: center;
|
|
270
|
+
border-color: #ccc;
|
|
271
|
+
transition: 0.1s;
|
|
272
|
+
padding-top: 1px;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.wrap.expanded .icon-wrap {
|
|
276
|
+
opacity: 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.wrap.expanded .box {
|
|
280
|
+
border-color: #fff;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.icon-wrap.active {
|
|
284
|
+
opacity: 1 !important;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
svg {
|
|
288
|
+
opacity: 0;
|
|
289
|
+
padding: 5px;
|
|
290
|
+
transition: 0.1s;
|
|
291
|
+
}
|
|
292
|
+
.expanded svg {
|
|
293
|
+
opacity: 1;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
svg line {
|
|
297
|
+
stroke: #999;
|
|
298
|
+
stroke-width: 3px;
|
|
299
|
+
stroke-linecap: round;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
svg path {
|
|
303
|
+
stroke-width: 3;
|
|
304
|
+
stroke: #999;
|
|
305
|
+
stroke-linejoin: round;
|
|
306
|
+
stroke-linecap: round;
|
|
307
|
+
fill: #999;
|
|
308
|
+
/* transition: 0.5s; */
|
|
309
|
+
transform-origin: 75px 75px;
|
|
310
|
+
}
|
|
311
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
[x: string]: never;
|
|
5
|
+
};
|
|
6
|
+
events: {
|
|
7
|
+
position: CustomEvent<"center" | "left" | "right" | "top-left" | "top" | "top-right" | "bottom-left" | "bottom" | "bottom-right">;
|
|
8
|
+
} & {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
};
|
|
11
|
+
slots: {};
|
|
12
|
+
};
|
|
13
|
+
export type AnchorProps = typeof __propDef.props;
|
|
14
|
+
export type AnchorEvents = typeof __propDef.events;
|
|
15
|
+
export type AnchorSlots = typeof __propDef.slots;
|
|
16
|
+
export default class Anchor extends SvelteComponent<AnchorProps, AnchorEvents, AnchorSlots> {
|
|
17
|
+
}
|
|
18
|
+
export {};
|