@gradio/imageeditor 0.12.14 → 0.14.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 +36 -0
- package/Example.svelte +1 -1
- package/IMAGE_EDITOR_OVERVIEW.md +224 -0
- package/ImageEditor.stories.svelte +13 -5
- package/Index.svelte +46 -45
- package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +65 -103
- package/README.md +2 -0
- package/dist/Example.svelte.d.ts +1 -1
- package/dist/Index.svelte +35 -33
- package/dist/Index.svelte.d.ts +25 -16
- package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +54 -88
- package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +17 -15
- package/dist/shared/Anchor.svelte +311 -0
- package/dist/shared/Anchor.svelte.d.ts +18 -0
- package/dist/shared/Controls.svelte +169 -18
- package/dist/shared/Controls.svelte.d.ts +9 -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 +572 -289
- package/dist/shared/ImageEditor.svelte.d.ts +54 -56
- package/dist/shared/Layers.svelte +260 -0
- package/dist/shared/Layers.svelte.d.ts +37 -0
- package/dist/shared/Resize.svelte +218 -0
- package/dist/shared/Resize.svelte.d.ts +29 -0
- package/dist/shared/SecondaryToolbar.svelte +55 -0
- package/dist/shared/SecondaryToolbar.svelte.d.ts +37 -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 +65 -0
- package/dist/shared/brush/brush-textures.js +385 -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 +397 -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 +261 -0
- package/dist/shared/core/editor.js +1264 -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 +183 -0
- package/dist/shared/resize/resize.d.ts +270 -0
- package/dist/shared/resize/resize.js +1413 -0
- package/dist/shared/types.d.ts +16 -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 +8 -7
- package/shared/Anchor.svelte +384 -0
- package/shared/Controls.svelte +178 -37
- package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
- package/shared/ImageEditor.svelte +712 -350
- package/shared/Layers.svelte +282 -0
- package/shared/Resize.svelte +239 -0
- package/shared/SecondaryToolbar.svelte +67 -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 +485 -0
- package/shared/brush/brush-utils.ts +76 -0
- package/shared/brush/brush.ts +510 -0
- package/shared/brush/types.ts +41 -0
- package/shared/core/EDITOR.md +182 -0
- package/shared/core/editor.ts +1788 -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 +298 -0
- package/shared/resize/resize.ts +1894 -0
- package/shared/types.ts +19 -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,43 @@
|
|
|
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
|
|
25
|
-
background: null,
|
|
26
|
-
layers: [],
|
|
27
|
-
composite: null
|
|
28
|
-
};
|
|
29
|
-
export let transforms = ["crop"];
|
|
23
|
+
export let theme_mode;
|
|
30
24
|
export let layers;
|
|
25
|
+
export let composite;
|
|
26
|
+
export let background;
|
|
27
|
+
export let layer_options;
|
|
28
|
+
export let transforms;
|
|
31
29
|
export let accept_blobs;
|
|
32
|
-
export let status = "complete";
|
|
33
30
|
export let canvas_size;
|
|
34
31
|
export let fixed_canvas = false;
|
|
35
32
|
export let realtime;
|
|
36
33
|
export let upload;
|
|
37
|
-
export let
|
|
38
|
-
export let dragging;
|
|
34
|
+
export let is_dragging;
|
|
39
35
|
export let placeholder = void 0;
|
|
40
|
-
export let
|
|
41
|
-
export let height;
|
|
36
|
+
export let border_region;
|
|
42
37
|
export let full_history = null;
|
|
38
|
+
export let webcam_options;
|
|
39
|
+
export let show_download_button = false;
|
|
43
40
|
const dispatch = createEventDispatcher();
|
|
44
41
|
let editor;
|
|
42
|
+
let has_drawn = false;
|
|
45
43
|
function is_not_null(o) {
|
|
46
44
|
return !!o;
|
|
47
45
|
}
|
|
@@ -49,7 +47,7 @@ function is_file_data(o) {
|
|
|
49
47
|
return !!o;
|
|
50
48
|
}
|
|
51
49
|
$:
|
|
52
|
-
if (
|
|
50
|
+
if (background_image)
|
|
53
51
|
dispatch("upload");
|
|
54
52
|
export async function get_data() {
|
|
55
53
|
let blobs;
|
|
@@ -58,45 +56,41 @@ export async function get_data() {
|
|
|
58
56
|
} catch (e) {
|
|
59
57
|
return { background: null, layers: [], composite: null };
|
|
60
58
|
}
|
|
61
|
-
const
|
|
59
|
+
const bg = blobs.background ? upload(
|
|
62
60
|
await prepare_files([new File([blobs.background], "background.png")]),
|
|
63
61
|
root
|
|
64
62
|
) : Promise.resolve(null);
|
|
65
63
|
const layers2 = blobs.layers.filter(is_not_null).map(
|
|
66
64
|
async (blob, i) => upload(await prepare_files([new File([blob], `layer_${i}.png`)]), root)
|
|
67
65
|
);
|
|
68
|
-
const
|
|
66
|
+
const composite2 = blobs.composite ? upload(
|
|
69
67
|
await prepare_files([new File([blobs.composite], "composite.png")]),
|
|
70
68
|
root
|
|
71
69
|
) : Promise.resolve(null);
|
|
72
|
-
const [
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
const [background2, composite_, ...layers_] = await Promise.all([
|
|
71
|
+
bg,
|
|
72
|
+
composite2,
|
|
75
73
|
...layers2
|
|
76
74
|
]);
|
|
77
75
|
return {
|
|
78
|
-
background: Array.isArray(
|
|
76
|
+
background: Array.isArray(background2) ? background2[0] : background2,
|
|
79
77
|
layers: layers_.flatMap((layer) => Array.isArray(layer) ? layer : [layer]).filter(is_file_data),
|
|
80
78
|
composite: Array.isArray(composite_) ? composite_[0] : composite_
|
|
81
79
|
};
|
|
82
80
|
}
|
|
83
|
-
function handle_value(
|
|
81
|
+
function handle_value(value) {
|
|
84
82
|
if (!editor)
|
|
85
83
|
return;
|
|
86
|
-
if (
|
|
84
|
+
if (value == null) {
|
|
87
85
|
editor.handle_remove();
|
|
88
86
|
dispatch("receive_null");
|
|
89
87
|
}
|
|
90
88
|
}
|
|
91
89
|
$:
|
|
92
|
-
handle_value(
|
|
93
|
-
|
|
94
|
-
crop_constraint = crop_size;
|
|
95
|
-
let bg = false;
|
|
90
|
+
handle_value({ layers, composite, background });
|
|
91
|
+
let background_image = false;
|
|
96
92
|
let history = false;
|
|
97
93
|
export let image_id = null;
|
|
98
|
-
$:
|
|
99
|
-
editor && editor.set_tool && (sources && sources.length ? editor.set_tool("bg") : editor.set_tool("draw"));
|
|
100
94
|
function nextframe() {
|
|
101
95
|
return new Promise((resolve) => setTimeout(() => resolve(), 30));
|
|
102
96
|
}
|
|
@@ -155,13 +149,9 @@ async function handle_change(e) {
|
|
|
155
149
|
handle_change(e);
|
|
156
150
|
}
|
|
157
151
|
}
|
|
158
|
-
let active_mode = null;
|
|
159
|
-
let editor_height = height - 100;
|
|
160
|
-
let _dynamic_height;
|
|
161
|
-
$:
|
|
162
|
-
dynamic_height = _dynamic_height;
|
|
163
152
|
$:
|
|
164
153
|
[heading, paragraph] = placeholder ? inject(placeholder) : [false, false];
|
|
154
|
+
let current_tool;
|
|
165
155
|
</script>
|
|
166
156
|
|
|
167
157
|
<BlockLabel
|
|
@@ -170,60 +160,38 @@ $:
|
|
|
170
160
|
label={label || i18n("image.image")}
|
|
171
161
|
/>
|
|
172
162
|
<ImageEditor
|
|
163
|
+
{transforms}
|
|
164
|
+
{composite}
|
|
165
|
+
{layers}
|
|
166
|
+
{background}
|
|
173
167
|
on:history
|
|
174
168
|
{canvas_size}
|
|
175
|
-
crop_size={Array.isArray(crop_size) ? crop_size : undefined}
|
|
176
169
|
bind:this={editor}
|
|
177
|
-
bind:height={editor_height}
|
|
178
|
-
bind:canvas_height={_dynamic_height}
|
|
179
|
-
parent_height={height}
|
|
180
170
|
{changeable}
|
|
181
171
|
on:save
|
|
182
172
|
on:change={handle_change}
|
|
183
173
|
on:clear={() => dispatch("clear")}
|
|
184
|
-
|
|
185
|
-
bind:bg
|
|
174
|
+
on:download_error
|
|
186
175
|
{sources}
|
|
187
|
-
crop_constraint={!!crop_constraint}
|
|
188
176
|
{full_history}
|
|
177
|
+
bind:background_image
|
|
178
|
+
bind:current_tool
|
|
179
|
+
brush_options={brush}
|
|
180
|
+
eraser_options={eraser}
|
|
181
|
+
{fixed_canvas}
|
|
182
|
+
{border_region}
|
|
183
|
+
{layer_options}
|
|
184
|
+
{i18n}
|
|
185
|
+
{root}
|
|
186
|
+
{upload}
|
|
187
|
+
bind:is_dragging
|
|
188
|
+
bind:has_drawn
|
|
189
|
+
{webcam_options}
|
|
190
|
+
{show_download_button}
|
|
191
|
+
{theme_mode}
|
|
189
192
|
>
|
|
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`}>
|
|
193
|
+
{#if !background_image && current_tool === "image" && !has_drawn}
|
|
194
|
+
<div class="empty wrap">
|
|
227
195
|
{#if sources && sources.length}
|
|
228
196
|
{#if heading || paragraph}
|
|
229
197
|
{#if heading}
|
|
@@ -268,10 +236,10 @@ $:
|
|
|
268
236
|
left: 0;
|
|
269
237
|
right: 0;
|
|
270
238
|
margin: auto;
|
|
271
|
-
z-index: var(--layer-
|
|
239
|
+
z-index: var(--layer-1);
|
|
272
240
|
text-align: center;
|
|
273
|
-
color: var(--
|
|
274
|
-
|
|
241
|
+
color: var(--color-grey-500) !important;
|
|
242
|
+
cursor: pointer;
|
|
275
243
|
}
|
|
276
244
|
|
|
277
245
|
.wrap {
|
|
@@ -279,10 +247,8 @@ $:
|
|
|
279
247
|
flex-direction: column;
|
|
280
248
|
justify-content: center;
|
|
281
249
|
align-items: center;
|
|
282
|
-
color: var(--block-label-text-color);
|
|
283
250
|
line-height: var(--line-md);
|
|
284
251
|
font-size: var(--text-md);
|
|
285
|
-
pointer-events: none;
|
|
286
252
|
}
|
|
287
253
|
|
|
288
254
|
.or {
|
package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts}
RENAMED
|
@@ -11,41 +11,43 @@ 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, type WebcamOptions } 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
|
+
theme_mode: "dark" | "light";
|
|
28
|
+
layers: FileData[];
|
|
29
|
+
composite: FileData | null;
|
|
30
|
+
background: FileData | null;
|
|
31
|
+
layer_options: LayerOptions;
|
|
32
|
+
transforms: Transform[];
|
|
31
33
|
accept_blobs: (a: any) => void;
|
|
32
|
-
status?: ("pending" | "complete" | "error" | "generating" | "streaming") | undefined;
|
|
33
34
|
canvas_size: [number, number];
|
|
34
35
|
fixed_canvas?: boolean | undefined;
|
|
35
36
|
realtime: boolean;
|
|
36
37
|
upload: Client["upload"];
|
|
37
|
-
|
|
38
|
-
dragging: boolean;
|
|
38
|
+
is_dragging: boolean;
|
|
39
39
|
placeholder?: string | undefined;
|
|
40
|
-
|
|
41
|
-
height: any;
|
|
40
|
+
border_region: number;
|
|
42
41
|
full_history?: (CommandNode | null) | undefined;
|
|
42
|
+
webcam_options: WebcamOptions;
|
|
43
|
+
show_download_button?: boolean | undefined;
|
|
43
44
|
get_data?: (() => Promise<ImageBlobs>) | undefined;
|
|
44
45
|
image_id?: (null | string) | undefined;
|
|
45
46
|
};
|
|
46
47
|
events: {
|
|
47
48
|
history: CustomEvent<import("svelte/store").Writable<CommandNode>>;
|
|
48
49
|
save: CustomEvent<void>;
|
|
50
|
+
download_error: CustomEvent<string>;
|
|
49
51
|
clear?: CustomEvent<undefined> | undefined;
|
|
50
52
|
upload?: CustomEvent<undefined> | undefined;
|
|
51
53
|
change?: CustomEvent<undefined> | 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 {};
|