@gradio/imageeditor 0.12.14 → 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.
Files changed (130) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/Example.svelte +1 -1
  3. package/IMAGE_EDITOR_OVERVIEW.md +224 -0
  4. package/ImageEditor.stories.svelte +4 -4
  5. package/Index.svelte +35 -46
  6. package/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +56 -103
  7. package/dist/Example.svelte.d.ts +1 -1
  8. package/dist/Index.svelte +29 -33
  9. package/dist/Index.svelte.d.ts +17 -16
  10. package/dist/{shared/InteractiveImageEditor.svelte → InteractiveImageEditor.svelte} +47 -88
  11. package/dist/{shared/InteractiveImageEditor.svelte.d.ts → InteractiveImageEditor.svelte.d.ts} +13 -15
  12. package/dist/shared/Anchor.svelte +311 -0
  13. package/dist/shared/Anchor.svelte.d.ts +18 -0
  14. package/dist/shared/Controls.svelte +162 -23
  15. package/dist/shared/Controls.svelte.d.ts +7 -2
  16. package/dist/shared/{tools/IconButton.svelte → IconButton.svelte} +19 -3
  17. package/dist/shared/{tools/IconButton.svelte.d.ts → IconButton.svelte.d.ts} +4 -1
  18. package/dist/shared/ImageEditor.svelte +546 -289
  19. package/dist/shared/ImageEditor.svelte.d.ts +51 -56
  20. package/dist/shared/Layers.svelte +233 -0
  21. package/dist/shared/Layers.svelte.d.ts +35 -0
  22. package/dist/shared/Resize.svelte +218 -0
  23. package/dist/shared/Resize.svelte.d.ts +29 -0
  24. package/dist/shared/SecondaryToolbar.svelte +54 -0
  25. package/dist/shared/SecondaryToolbar.svelte.d.ts +35 -0
  26. package/dist/shared/Toolbar.svelte +337 -0
  27. package/dist/shared/Toolbar.svelte.d.ts +42 -0
  28. package/dist/shared/{tools → brush}/BrushOptions.svelte +68 -64
  29. package/dist/shared/{tools → brush}/BrushOptions.svelte.d.ts +9 -9
  30. package/dist/shared/{tools → brush}/BrushSize.svelte +2 -27
  31. package/dist/shared/{tools → brush}/ColorField.svelte +2 -1
  32. package/dist/shared/{tools → brush}/ColorField.svelte.d.ts +1 -0
  33. package/dist/shared/{tools → brush}/ColorPicker.svelte +81 -9
  34. package/dist/shared/{tools → brush}/ColorPicker.svelte.d.ts +1 -0
  35. package/dist/shared/{tools → brush}/ColorSwatch.svelte +78 -38
  36. package/dist/shared/{tools → brush}/ColorSwatch.svelte.d.ts +6 -2
  37. package/dist/shared/brush/brush-cursor.d.ts +101 -0
  38. package/dist/shared/brush/brush-cursor.js +327 -0
  39. package/dist/shared/brush/brush-textures.d.ts +71 -0
  40. package/dist/shared/brush/brush-textures.js +404 -0
  41. package/dist/shared/brush/brush-utils.d.ts +32 -0
  42. package/dist/shared/brush/brush-utils.js +59 -0
  43. package/dist/shared/brush/brush.d.ts +152 -0
  44. package/dist/shared/brush/brush.js +396 -0
  45. package/dist/shared/brush/types.d.ts +37 -0
  46. package/dist/shared/brush/types.js +1 -0
  47. package/dist/shared/core/editor.d.ts +252 -0
  48. package/dist/shared/core/editor.js +1221 -0
  49. package/dist/shared/crop/crop.d.ts +201 -0
  50. package/dist/shared/crop/crop.js +727 -0
  51. package/dist/shared/image/image.d.ts +89 -0
  52. package/dist/shared/image/image.js +185 -0
  53. package/dist/shared/resize/resize.d.ts +270 -0
  54. package/dist/shared/resize/resize.js +1413 -0
  55. package/dist/shared/types.d.ts +12 -0
  56. package/dist/shared/utils/pixi.d.ts +16 -57
  57. package/dist/shared/utils/pixi.js +15 -113
  58. package/dist/shared/zoom/zoom.d.ts +136 -0
  59. package/dist/shared/zoom/zoom.js +548 -0
  60. package/package.json +10 -9
  61. package/shared/Anchor.svelte +384 -0
  62. package/shared/Controls.svelte +194 -27
  63. package/shared/{tools/IconButton.svelte → IconButton.svelte} +21 -4
  64. package/shared/ImageEditor.svelte +682 -349
  65. package/shared/Layers.svelte +249 -0
  66. package/shared/Resize.svelte +239 -0
  67. package/shared/SecondaryToolbar.svelte +61 -0
  68. package/shared/Toolbar.svelte +386 -0
  69. package/shared/brush/BRUSH_TOOL.md +134 -0
  70. package/shared/brush/BrushOptions.svelte +200 -0
  71. package/shared/{tools → brush}/BrushSize.svelte +2 -30
  72. package/shared/{tools → brush}/ColorField.svelte +3 -1
  73. package/shared/{tools → brush}/ColorPicker.svelte +86 -9
  74. package/shared/{tools → brush}/ColorSwatch.svelte +95 -42
  75. package/shared/brush/brush-cursor.ts +408 -0
  76. package/shared/brush/brush-textures.ts +508 -0
  77. package/shared/brush/brush-utils.ts +76 -0
  78. package/shared/brush/brush.ts +506 -0
  79. package/shared/brush/types.ts +41 -0
  80. package/shared/core/EDITOR.md +182 -0
  81. package/shared/core/editor.ts +1730 -0
  82. package/shared/crop/CROP.md +139 -0
  83. package/shared/crop/crop.ts +1027 -0
  84. package/shared/image/IMAGE.md +135 -0
  85. package/shared/image/image.ts +300 -0
  86. package/shared/resize/resize.ts +1894 -0
  87. package/shared/types.ts +14 -0
  88. package/shared/utils/pixi.ts +19 -246
  89. package/shared/zoom/zoom.ts +729 -0
  90. package/dist/shared/layers/Layers.svelte +0 -231
  91. package/dist/shared/layers/Layers.svelte.d.ts +0 -18
  92. package/dist/shared/layers/utils.d.ts +0 -73
  93. package/dist/shared/layers/utils.js +0 -156
  94. package/dist/shared/tools/Brush.svelte +0 -215
  95. package/dist/shared/tools/Brush.svelte.d.ts +0 -29
  96. package/dist/shared/tools/Crop.svelte +0 -155
  97. package/dist/shared/tools/Crop.svelte.d.ts +0 -16
  98. package/dist/shared/tools/Cropper.svelte +0 -271
  99. package/dist/shared/tools/Cropper.svelte.d.ts +0 -41
  100. package/dist/shared/tools/Handle.svelte +0 -260
  101. package/dist/shared/tools/Handle.svelte.d.ts +0 -29
  102. package/dist/shared/tools/Sources.svelte +0 -257
  103. package/dist/shared/tools/Sources.svelte.d.ts +0 -34
  104. package/dist/shared/tools/Tools.svelte +0 -102
  105. package/dist/shared/tools/Tools.svelte.d.ts +0 -48
  106. package/dist/shared/tools/brush.d.ts +0 -62
  107. package/dist/shared/tools/brush.js +0 -133
  108. package/dist/shared/tools/crop.d.ts +0 -15
  109. package/dist/shared/tools/crop.js +0 -154
  110. package/dist/shared/tools/index.d.ts +0 -5
  111. package/dist/shared/tools/index.js +0 -5
  112. package/dist/shared/tools/sources.d.ts +0 -46
  113. package/dist/shared/tools/sources.js +0 -121
  114. package/dist/shared/tools/types.d.ts +0 -1
  115. package/shared/layers/Layers.svelte +0 -250
  116. package/shared/layers/utils.ts +0 -294
  117. package/shared/tools/Brush.svelte +0 -280
  118. package/shared/tools/BrushOptions.svelte +0 -180
  119. package/shared/tools/Crop.svelte +0 -189
  120. package/shared/tools/Cropper.svelte +0 -346
  121. package/shared/tools/Handle.svelte +0 -289
  122. package/shared/tools/Sources.svelte +0 -283
  123. package/shared/tools/Tools.svelte +0 -162
  124. package/shared/tools/brush.ts +0 -247
  125. package/shared/tools/crop.ts +0 -213
  126. package/shared/tools/index.ts +0 -5
  127. package/shared/tools/sources.ts +0 -195
  128. package/shared/tools/types.ts +0 -1
  129. /package/dist/shared/{tools → brush}/BrushSize.svelte.d.ts +0 -0
  130. /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 Layers from "./layers/Layers.svelte";
9
- import {} from "./tools/Brush.svelte";
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 stream_handler;
38
- export let dragging;
33
+ export let is_dragging;
39
34
  export let placeholder = void 0;
40
- export let dynamic_height = void 0;
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 (bg)
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 bg2 = blobs.background ? upload(
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 composite = blobs.composite ? upload(
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 [background, composite_, ...layers_] = await Promise.all([
73
- bg2,
74
- composite,
67
+ const [background2, composite_, ...layers_] = await Promise.all([
68
+ bg,
69
+ composite2,
75
70
  ...layers2
76
71
  ]);
77
72
  return {
78
- background: Array.isArray(background) ? background[0] : background,
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(value2) {
78
+ function handle_value(value) {
84
79
  if (!editor)
85
80
  return;
86
- if (value2 == null) {
81
+ if (value == null) {
87
82
  editor.handle_remove();
88
83
  dispatch("receive_null");
89
84
  }
90
85
  }
91
86
  $:
92
- handle_value(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
- <Tools {i18n}>
191
- <Layers layer_files={value?.layers || null} enable_layers={layers} />
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-top);
232
+ z-index: var(--layer-1);
272
233
  text-align: center;
273
- color: var(--body-text-color);
274
- top: var(--size-8);
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 {
@@ -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 "./tools/Brush.svelte";
16
- import { type Eraser } from "./tools/Brush.svelte";
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 | null;
20
- eraser: Eraser | null;
21
- sources: ("clipboard" | "webcam" | "upload")[];
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
- value?: (EditorData | null) | undefined;
29
- transforms?: "crop"[] | undefined;
30
- layers: boolean;
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
- stream_handler: Client["stream"];
38
- dragging: boolean;
37
+ is_dragging: boolean;
39
38
  placeholder?: string | undefined;
40
- dynamic_height?: number | undefined;
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 {};