@gradio/imageeditor 0.19.0 → 0.20.1
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 +32 -0
- package/Example.svelte +9 -3
- package/Index.svelte +23 -24
- package/InteractiveImageEditor.svelte +99 -61
- package/dist/Example.svelte +9 -3
- package/dist/Example.svelte.d.ts +4 -18
- package/dist/Index.svelte +23 -24
- package/dist/Index.svelte.d.ts +2 -19
- package/dist/InteractiveImageEditor.svelte +99 -61
- package/dist/InteractiveImageEditor.svelte.d.ts +15 -32
- package/dist/shared/Anchor.svelte +13 -11
- package/dist/shared/Anchor.svelte.d.ts +4 -19
- package/dist/shared/Controls.svelte +63 -39
- package/dist/shared/Controls.svelte.d.ts +13 -28
- package/dist/shared/IconButton.svelte +38 -17
- package/dist/shared/IconButton.svelte.d.ts +5 -19
- package/dist/shared/ImageEditor.svelte +235 -160
- package/dist/shared/ImageEditor.svelte.d.ts +55 -2
- package/dist/shared/Layers.svelte +43 -29
- package/dist/shared/Layers.svelte.d.ts +10 -25
- package/dist/shared/Resize.svelte +22 -16
- package/dist/shared/Resize.svelte.d.ts +6 -22
- package/dist/shared/SecondaryToolbar.svelte +34 -18
- package/dist/shared/SecondaryToolbar.svelte.d.ts +10 -25
- package/dist/shared/Toolbar.svelte +60 -76
- package/dist/shared/Toolbar.svelte.d.ts +9 -24
- package/dist/shared/brush/BrushOptions.svelte +49 -29
- package/dist/shared/brush/BrushOptions.svelte.d.ts +6 -21
- package/dist/shared/brush/BrushSize.svelte +12 -9
- package/dist/shared/brush/BrushSize.svelte.d.ts +5 -20
- package/dist/shared/brush/ColorField.svelte +21 -16
- package/dist/shared/brush/ColorField.svelte.d.ts +6 -22
- package/dist/shared/brush/ColorPicker.svelte +22 -16
- package/dist/shared/brush/ColorPicker.svelte.d.ts +4 -18
- package/dist/shared/brush/ColorSwatch.svelte +41 -21
- package/dist/shared/brush/ColorSwatch.svelte.d.ts +10 -24
- package/dist/shared/brush/brush.js +7 -8
- package/dist/shared/core/editor.d.ts +5 -0
- package/dist/shared/core/editor.js +43 -2
- package/dist/shared/resize/resize.d.ts +2 -0
- package/dist/shared/resize/resize.js +27 -18
- package/package.json +9 -9
- package/shared/Anchor.svelte +13 -11
- package/shared/Controls.svelte +63 -39
- package/shared/IconButton.svelte +38 -17
- package/shared/ImageEditor.svelte +235 -160
- package/shared/Layers.svelte +43 -29
- package/shared/Resize.svelte +22 -16
- package/shared/SecondaryToolbar.svelte +34 -18
- package/shared/Toolbar.svelte +60 -76
- package/shared/brush/BrushOptions.svelte +49 -29
- package/shared/brush/BrushSize.svelte +12 -9
- package/shared/brush/ColorField.svelte +21 -16
- package/shared/brush/ColorPicker.svelte +22 -16
- package/shared/brush/ColorSwatch.svelte +41 -21
- package/shared/brush/brush.ts +8 -10
- package/shared/core/editor.ts +49 -2
- package/shared/resize/resize.ts +28 -20
|
@@ -56,6 +56,7 @@ export interface ImageEditorContext {
|
|
|
56
56
|
resize_canvas: (width: number, height: number) => void;
|
|
57
57
|
reset: () => void;
|
|
58
58
|
set_background_image: (image: Sprite) => void;
|
|
59
|
+
request_render: () => void;
|
|
59
60
|
pad_bottom: number;
|
|
60
61
|
}
|
|
61
62
|
export declare class ImageEditor {
|
|
@@ -107,9 +108,13 @@ export declare class ImageEditor {
|
|
|
107
108
|
private overlay_graphics;
|
|
108
109
|
private pad_bottom;
|
|
109
110
|
private theme_mode;
|
|
111
|
+
private render_idle_remaining;
|
|
112
|
+
private static readonly WAKE_EVENTS;
|
|
113
|
+
private wake_render_loop_bound;
|
|
110
114
|
constructor(options: ImageEditorOptions);
|
|
111
115
|
get context(): ImageEditorContext;
|
|
112
116
|
init(): Promise<void>;
|
|
117
|
+
private wake_render_loop;
|
|
113
118
|
private setup_containers;
|
|
114
119
|
resize_canvas(width: number, height: number): void;
|
|
115
120
|
reset(): void;
|
|
@@ -16,6 +16,7 @@ const spring_config = {
|
|
|
16
16
|
stiffness: 0.45,
|
|
17
17
|
damping: 0.8
|
|
18
18
|
};
|
|
19
|
+
const RENDER_IDLE_TIMEOUT_MS = 500;
|
|
19
20
|
class EditorState {
|
|
20
21
|
state;
|
|
21
22
|
scale;
|
|
@@ -114,6 +115,15 @@ export class ImageEditor {
|
|
|
114
115
|
overlay_graphics;
|
|
115
116
|
pad_bottom;
|
|
116
117
|
theme_mode;
|
|
118
|
+
render_idle_remaining = RENDER_IDLE_TIMEOUT_MS;
|
|
119
|
+
static WAKE_EVENTS = [
|
|
120
|
+
"pointerdown",
|
|
121
|
+
"pointermove",
|
|
122
|
+
"pointerup",
|
|
123
|
+
"pointerenter",
|
|
124
|
+
"wheel"
|
|
125
|
+
];
|
|
126
|
+
wake_render_loop_bound = this.wake_render_loop.bind(this);
|
|
117
127
|
constructor(options) {
|
|
118
128
|
this.pad_bottom = options.pad_bottom || 0;
|
|
119
129
|
this.dark = options.dark || false;
|
|
@@ -178,7 +188,8 @@ export class ImageEditor {
|
|
|
178
188
|
execute_command: this.execute_command.bind(this),
|
|
179
189
|
resize_canvas: this.resize_canvas.bind(this),
|
|
180
190
|
reset: this.reset.bind(this),
|
|
181
|
-
set_background_image: this.set_background_image.bind(this)
|
|
191
|
+
set_background_image: this.set_background_image.bind(this),
|
|
192
|
+
request_render: this.wake_render_loop.bind(this)
|
|
182
193
|
};
|
|
183
194
|
}
|
|
184
195
|
async init() {
|
|
@@ -222,14 +233,17 @@ export class ImageEditor {
|
|
|
222
233
|
this.dimensions_value = dimensions;
|
|
223
234
|
this.image_container.width = dimensions.width;
|
|
224
235
|
this.image_container.height = dimensions.height;
|
|
236
|
+
this.wake_render_loop();
|
|
225
237
|
});
|
|
226
238
|
this.scale.subscribe((scale) => {
|
|
227
239
|
this.scale_value = scale;
|
|
240
|
+
this.wake_render_loop();
|
|
228
241
|
});
|
|
229
242
|
this.position.subscribe((position) => {
|
|
230
243
|
this.position_value = position;
|
|
244
|
+
this.wake_render_loop();
|
|
231
245
|
});
|
|
232
|
-
this.app.ticker.add(() => {
|
|
246
|
+
this.app.ticker.add((ticker) => {
|
|
233
247
|
this.image_container.scale.set(this.scale_value);
|
|
234
248
|
this.image_container.position.set(this.position_value.x, this.position_value.y);
|
|
235
249
|
const effective_width = this.dimensions_value.width * this.scale_value;
|
|
@@ -283,9 +297,23 @@ export class ImageEditor {
|
|
|
283
297
|
else {
|
|
284
298
|
this.overlay_graphics.clear();
|
|
285
299
|
}
|
|
300
|
+
this.render_idle_remaining -= ticker.deltaMS;
|
|
301
|
+
if (this.render_idle_remaining <= 0) {
|
|
302
|
+
ticker.stop();
|
|
303
|
+
}
|
|
286
304
|
});
|
|
305
|
+
for (const event_name of ImageEditor.WAKE_EVENTS) {
|
|
306
|
+
canvas.addEventListener(event_name, this.wake_render_loop_bound);
|
|
307
|
+
}
|
|
308
|
+
this.wake_render_loop();
|
|
287
309
|
this.ready_resolve();
|
|
288
310
|
}
|
|
311
|
+
wake_render_loop() {
|
|
312
|
+
this.render_idle_remaining = RENDER_IDLE_TIMEOUT_MS;
|
|
313
|
+
if (this.app?.ticker && !this.app.ticker.started) {
|
|
314
|
+
this.app.ticker.start();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
289
317
|
setup_containers() {
|
|
290
318
|
this.image_container = new Container({
|
|
291
319
|
eventMode: "static",
|
|
@@ -355,6 +383,7 @@ export class ImageEditor {
|
|
|
355
383
|
if (this.outline_container) {
|
|
356
384
|
this.outline_container.position.set(app_center_x, app_center_y);
|
|
357
385
|
}
|
|
386
|
+
this.wake_render_loop();
|
|
358
387
|
}
|
|
359
388
|
reset() {
|
|
360
389
|
const zoom = this.tools.get("zoom");
|
|
@@ -366,6 +395,7 @@ export class ImageEditor {
|
|
|
366
395
|
if (brush) {
|
|
367
396
|
this.set_tool("draw");
|
|
368
397
|
}
|
|
398
|
+
this.wake_render_loop();
|
|
369
399
|
}
|
|
370
400
|
async set_image_properties(properties) {
|
|
371
401
|
let hard = typeof properties.animate !== "undefined" ? !properties.animate : true;
|
|
@@ -431,12 +461,14 @@ export class ImageEditor {
|
|
|
431
461
|
for (const tool of this.tools.values()) {
|
|
432
462
|
tool.set_tool(this.current_tool, this.current_subtool);
|
|
433
463
|
}
|
|
464
|
+
this.wake_render_loop();
|
|
434
465
|
}
|
|
435
466
|
set_subtool(subtool) {
|
|
436
467
|
this.current_subtool = subtool;
|
|
437
468
|
for (const tool of this.tools.values()) {
|
|
438
469
|
tool.set_tool(this.current_tool, this.current_subtool);
|
|
439
470
|
}
|
|
471
|
+
this.wake_render_loop();
|
|
440
472
|
}
|
|
441
473
|
set_background_image(image) {
|
|
442
474
|
this.background_image = image;
|
|
@@ -556,6 +588,7 @@ export class ImageEditor {
|
|
|
556
588
|
this.event_callbacks.set(event, this.event_callbacks.get(event)?.filter((cb) => cb !== callback) || []);
|
|
557
589
|
}
|
|
558
590
|
notify(event) {
|
|
591
|
+
this.wake_render_loop();
|
|
559
592
|
for (const callback of this.event_callbacks.get(event) || []) {
|
|
560
593
|
callback();
|
|
561
594
|
}
|
|
@@ -563,6 +596,12 @@ export class ImageEditor {
|
|
|
563
596
|
destroy() {
|
|
564
597
|
if (!this.app)
|
|
565
598
|
return;
|
|
599
|
+
const canvas = this.app.canvas;
|
|
600
|
+
if (canvas) {
|
|
601
|
+
for (const event_name of ImageEditor.WAKE_EVENTS) {
|
|
602
|
+
canvas.removeEventListener(event_name, this.wake_render_loop_bound);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
566
605
|
this.app?.destroy();
|
|
567
606
|
}
|
|
568
607
|
resize(width, height) {
|
|
@@ -587,8 +626,10 @@ export class ImageEditor {
|
|
|
587
626
|
set_layer_options(layer_options) {
|
|
588
627
|
this.layer_options = layer_options;
|
|
589
628
|
this.layer_manager.set_layer_options(layer_options, this.width, this.height);
|
|
629
|
+
this.wake_render_loop();
|
|
590
630
|
}
|
|
591
631
|
toggle_layer_visibility(id) {
|
|
592
632
|
this.layer_manager.toggle_layer_visibility(id);
|
|
633
|
+
this.wake_render_loop();
|
|
593
634
|
}
|
|
594
635
|
}
|
|
@@ -61,6 +61,8 @@ export declare class ResizeTool implements Tool {
|
|
|
61
61
|
private dom_mouseup_handler;
|
|
62
62
|
private event_callbacks;
|
|
63
63
|
private last_scale;
|
|
64
|
+
private readonly handle_pointer_move_bound;
|
|
65
|
+
private readonly handle_pointer_up_bound;
|
|
64
66
|
private original_state;
|
|
65
67
|
/**
|
|
66
68
|
* @method setup
|
|
@@ -84,6 +84,8 @@ export class ResizeTool {
|
|
|
84
84
|
dom_mouseup_handler = null;
|
|
85
85
|
event_callbacks = new Map();
|
|
86
86
|
last_scale = 1;
|
|
87
|
+
handle_pointer_move_bound = this.handle_pointer_move.bind(this);
|
|
88
|
+
handle_pointer_up_bound = this.handle_pointer_up.bind(this);
|
|
87
89
|
original_state = null;
|
|
88
90
|
/**
|
|
89
91
|
* @method setup
|
|
@@ -165,7 +167,7 @@ export class ResizeTool {
|
|
|
165
167
|
* @returns {void}
|
|
166
168
|
*/
|
|
167
169
|
cleanup() {
|
|
168
|
-
if (this.resize_ui_container) {
|
|
170
|
+
if (this.resize_ui_container && this.image_editor_context?.app) {
|
|
169
171
|
this.image_editor_context.app.stage.removeChild(this.resize_ui_container);
|
|
170
172
|
}
|
|
171
173
|
this.cleanup_event_listeners();
|
|
@@ -181,16 +183,20 @@ export class ResizeTool {
|
|
|
181
183
|
this.current_tool = tool;
|
|
182
184
|
this.current_subtool = subtool;
|
|
183
185
|
if (tool === "image" && subtool === "size") {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
const context = this.image_editor_context;
|
|
187
|
+
if (!context)
|
|
188
|
+
return;
|
|
189
|
+
if (context.background_image) {
|
|
190
|
+
const storedBorderRegion = context.background_image
|
|
191
|
+
.borderRegion;
|
|
186
192
|
if (typeof storedBorderRegion === "number" && storedBorderRegion > 0) {
|
|
187
193
|
this.borderRegion = storedBorderRegion;
|
|
188
194
|
}
|
|
189
195
|
}
|
|
190
196
|
this.show_resize_ui();
|
|
191
|
-
if (
|
|
192
|
-
const bg =
|
|
193
|
-
const container =
|
|
197
|
+
if (context.background_image) {
|
|
198
|
+
const bg = context.background_image;
|
|
199
|
+
const container = context.image_container;
|
|
194
200
|
const bg_width = bg.width;
|
|
195
201
|
const bg_height = bg.height;
|
|
196
202
|
const container_bounds = container.getLocalBounds();
|
|
@@ -685,9 +691,9 @@ export class ResizeTool {
|
|
|
685
691
|
setup_event_listeners() {
|
|
686
692
|
const stage = this.image_editor_context.app.stage;
|
|
687
693
|
stage.eventMode = "static";
|
|
688
|
-
stage.on("pointermove", this.
|
|
689
|
-
stage.on("pointerup", this.
|
|
690
|
-
stage.on("pointerupoutside", this.
|
|
694
|
+
stage.on("pointermove", this.handle_pointer_move_bound);
|
|
695
|
+
stage.on("pointerup", this.handle_pointer_up_bound);
|
|
696
|
+
stage.on("pointerupoutside", this.handle_pointer_up_bound);
|
|
691
697
|
this.setup_dom_event_listeners();
|
|
692
698
|
}
|
|
693
699
|
/**
|
|
@@ -695,7 +701,10 @@ export class ResizeTool {
|
|
|
695
701
|
*/
|
|
696
702
|
setup_dom_event_listeners() {
|
|
697
703
|
this.cleanup_dom_event_listeners();
|
|
698
|
-
const
|
|
704
|
+
const context = this.image_editor_context;
|
|
705
|
+
if (!context?.app?.canvas)
|
|
706
|
+
return;
|
|
707
|
+
const canvas = context.app.canvas;
|
|
699
708
|
this.dom_mousedown_handler = (e) => {
|
|
700
709
|
if (this.current_subtool !== "size")
|
|
701
710
|
return;
|
|
@@ -780,9 +789,9 @@ export class ResizeTool {
|
|
|
780
789
|
* Cleans up DOM event listeners
|
|
781
790
|
*/
|
|
782
791
|
cleanup_dom_event_listeners() {
|
|
783
|
-
const canvas = this.image_editor_context
|
|
792
|
+
const canvas = this.image_editor_context?.app?.canvas;
|
|
784
793
|
if (this.dom_mousedown_handler) {
|
|
785
|
-
canvas
|
|
794
|
+
canvas?.removeEventListener("mousedown", this.dom_mousedown_handler);
|
|
786
795
|
this.dom_mousedown_handler = null;
|
|
787
796
|
}
|
|
788
797
|
if (this.dom_mousemove_handler) {
|
|
@@ -798,10 +807,10 @@ export class ResizeTool {
|
|
|
798
807
|
* removes event listeners for the resize tool
|
|
799
808
|
*/
|
|
800
809
|
cleanup_event_listeners() {
|
|
801
|
-
const stage = this.image_editor_context
|
|
802
|
-
stage
|
|
803
|
-
stage
|
|
804
|
-
stage
|
|
810
|
+
const stage = this.image_editor_context?.app?.stage;
|
|
811
|
+
stage?.off("pointermove", this.handle_pointer_move_bound);
|
|
812
|
+
stage?.off("pointerup", this.handle_pointer_up_bound);
|
|
813
|
+
stage?.off("pointerupoutside", this.handle_pointer_up_bound);
|
|
805
814
|
this.cleanup_dom_event_listeners();
|
|
806
815
|
}
|
|
807
816
|
/**
|
|
@@ -1197,7 +1206,7 @@ export class ResizeTool {
|
|
|
1197
1206
|
this.resize_ui_container.visible = true;
|
|
1198
1207
|
this.update_resize_ui();
|
|
1199
1208
|
}
|
|
1200
|
-
if (this.image_editor_context
|
|
1209
|
+
if (this.image_editor_context?.background_image) {
|
|
1201
1210
|
const bg = this.image_editor_context.background_image;
|
|
1202
1211
|
this.current_cursor = bg.cursor;
|
|
1203
1212
|
bg.cursor = "move";
|
|
@@ -1212,7 +1221,7 @@ export class ResizeTool {
|
|
|
1212
1221
|
if (this.resize_ui_container) {
|
|
1213
1222
|
this.resize_ui_container.visible = false;
|
|
1214
1223
|
}
|
|
1215
|
-
if (this.image_editor_context
|
|
1224
|
+
if (this.image_editor_context?.background_image) {
|
|
1216
1225
|
const bg = this.image_editor_context.background_image;
|
|
1217
1226
|
bg.cursor = this.current_cursor;
|
|
1218
1227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/imageeditor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"pixi-filters": "^6.1.4",
|
|
12
12
|
"pixi.js": "^8.14.0",
|
|
13
13
|
"tinycolor2": "^1.6.0",
|
|
14
|
-
"@gradio/atoms": "^0.
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/image": "^0.
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
19
|
-
"@gradio/utils": "^0.
|
|
20
|
-
"@gradio/
|
|
14
|
+
"@gradio/atoms": "^0.26.1",
|
|
15
|
+
"@gradio/client": "^2.4.0",
|
|
16
|
+
"@gradio/image": "^0.28.1",
|
|
17
|
+
"@gradio/icons": "^0.16.0",
|
|
18
|
+
"@gradio/statustracker": "^0.15.2",
|
|
19
|
+
"@gradio/utils": "^0.14.0",
|
|
20
|
+
"@gradio/upload": "^0.18.2"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@gradio/preview": "^0.
|
|
23
|
+
"@gradio/preview": "^0.17.0"
|
|
24
24
|
},
|
|
25
25
|
"main_changeset": true,
|
|
26
26
|
"main": "./Index.svelte",
|
package/shared/Anchor.svelte
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { spring } from "svelte/motion";
|
|
3
3
|
import { Image } from "@gradio/icons";
|
|
4
|
-
import { onMount
|
|
4
|
+
import { onMount } from "svelte";
|
|
5
5
|
|
|
6
6
|
const positions = [
|
|
7
7
|
"top-left",
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
"bottom",
|
|
15
15
|
"bottom-right"
|
|
16
16
|
] as const;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
17
|
+
let {
|
|
18
|
+
onposition
|
|
19
|
+
}: {
|
|
20
|
+
onposition?: (position: (typeof positions)[number]) => void;
|
|
21
|
+
} = $props();
|
|
20
22
|
|
|
21
23
|
const spring_opt = {
|
|
22
24
|
stiffness: 0.1,
|
|
@@ -35,8 +37,8 @@
|
|
|
35
37
|
};
|
|
36
38
|
const arrow_spring = spring<Arrow[]>([], spring_opt);
|
|
37
39
|
|
|
38
|
-
let last_i = 0;
|
|
39
|
-
let expanded = true;
|
|
40
|
+
let last_i = $state(0);
|
|
41
|
+
let expanded = $state(true);
|
|
40
42
|
const box_size = (120 - 5 * 2) / 3;
|
|
41
43
|
|
|
42
44
|
async function handle_box_hover(i: number): Promise<void> {
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
dimensions.set(init);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
let last_pos = 0;
|
|
54
|
+
let last_pos = $state(0);
|
|
53
55
|
async function handle_box_click(i: number, stagger = false): Promise<void> {
|
|
54
56
|
if (expanded && stagger) return;
|
|
55
57
|
|
|
@@ -77,7 +79,7 @@
|
|
|
77
79
|
dimensions.set([120 + 10, 120 + 10]);
|
|
78
80
|
arrow_spring.set(eventual_arrows);
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
onposition?.(positions[i]);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
function get_valid_offsets(anchorPoint: {
|
|
@@ -215,14 +217,14 @@
|
|
|
215
217
|
class="box-wrap"
|
|
216
218
|
role="grid"
|
|
217
219
|
tabindex="0"
|
|
218
|
-
|
|
220
|
+
onmouseleave={() => handle_box_click(last_pos, true)}
|
|
219
221
|
>
|
|
220
222
|
{#each { length: 9 } as _, i}
|
|
221
223
|
<button
|
|
222
224
|
class="box"
|
|
223
225
|
class:active-box={last_pos === i}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
onmouseenter={() => handle_box_hover(i)}
|
|
227
|
+
onclick={() => handle_box_click(i)}
|
|
226
228
|
>
|
|
227
229
|
<span class:active={last_pos === i} class="icon-wrap">
|
|
228
230
|
<Image />
|
package/shared/Controls.svelte
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher } from "svelte";
|
|
3
2
|
import { IconButton, IconButtonWrapper } from "@gradio/atoms";
|
|
4
3
|
import {
|
|
5
4
|
Check,
|
|
@@ -11,30 +10,45 @@
|
|
|
11
10
|
Undo,
|
|
12
11
|
Redo
|
|
13
12
|
} from "@gradio/icons";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
let {
|
|
14
|
+
can_save = false,
|
|
15
|
+
changeable = false,
|
|
16
|
+
current_zoom = 1,
|
|
17
|
+
tool,
|
|
18
|
+
min_zoom = true,
|
|
19
|
+
enable_download = false,
|
|
20
|
+
can_undo,
|
|
21
|
+
can_redo,
|
|
22
|
+
onremove_image,
|
|
23
|
+
onundo,
|
|
24
|
+
onredo,
|
|
25
|
+
onsave,
|
|
26
|
+
onzoom_in,
|
|
27
|
+
onzoom_out,
|
|
28
|
+
onset_zoom,
|
|
29
|
+
onpan,
|
|
30
|
+
ondownload
|
|
31
|
+
}: {
|
|
32
|
+
can_save?: boolean;
|
|
33
|
+
changeable?: boolean;
|
|
34
|
+
current_zoom?: number;
|
|
35
|
+
tool: string;
|
|
36
|
+
min_zoom?: boolean;
|
|
37
|
+
enable_download?: boolean;
|
|
38
|
+
can_undo: boolean;
|
|
39
|
+
can_redo: boolean;
|
|
40
|
+
onremove_image?: () => void;
|
|
41
|
+
onundo?: () => void;
|
|
42
|
+
onredo?: () => void;
|
|
43
|
+
onsave?: () => void;
|
|
44
|
+
onzoom_in?: () => void;
|
|
45
|
+
onzoom_out?: () => void;
|
|
46
|
+
onset_zoom?: (zoom: number | "fit") => void;
|
|
47
|
+
onpan?: () => void;
|
|
48
|
+
ondownload?: () => void;
|
|
49
|
+
} = $props();
|
|
50
|
+
|
|
51
|
+
let show_zoom_popup = $state(false);
|
|
38
52
|
|
|
39
53
|
function handle_zoom_click(e: MouseEvent): void {
|
|
40
54
|
e.stopPropagation();
|
|
@@ -42,7 +56,7 @@
|
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
function handle_zoom_change(zoom: number | "fit"): void {
|
|
45
|
-
|
|
59
|
+
onset_zoom?.(zoom);
|
|
46
60
|
show_zoom_popup = false;
|
|
47
61
|
}
|
|
48
62
|
|
|
@@ -52,7 +66,7 @@
|
|
|
52
66
|
}
|
|
53
67
|
}
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
let formatted_zoom = $derived(Math.round(current_zoom * 100));
|
|
56
70
|
</script>
|
|
57
71
|
|
|
58
72
|
<IconButtonWrapper>
|
|
@@ -61,7 +75,7 @@
|
|
|
61
75
|
Icon={Download}
|
|
62
76
|
label="Download"
|
|
63
77
|
onclick={(event) => {
|
|
64
|
-
|
|
78
|
+
ondownload?.();
|
|
65
79
|
event.stopPropagation();
|
|
66
80
|
}}
|
|
67
81
|
/>
|
|
@@ -72,7 +86,7 @@
|
|
|
72
86
|
label="Pan"
|
|
73
87
|
onclick={(e) => {
|
|
74
88
|
e.stopPropagation();
|
|
75
|
-
|
|
89
|
+
onpan?.();
|
|
76
90
|
}}
|
|
77
91
|
highlight={tool === "pan"}
|
|
78
92
|
size="small"
|
|
@@ -85,7 +99,7 @@
|
|
|
85
99
|
Icon={ZoomOut}
|
|
86
100
|
label="Zoom out"
|
|
87
101
|
onclick={(event) => {
|
|
88
|
-
|
|
102
|
+
onzoom_out?.();
|
|
89
103
|
event.stopPropagation();
|
|
90
104
|
}}
|
|
91
105
|
/>
|
|
@@ -93,7 +107,7 @@
|
|
|
93
107
|
Icon={ZoomIn}
|
|
94
108
|
label="Zoom in"
|
|
95
109
|
onclick={(event) => {
|
|
96
|
-
|
|
110
|
+
onzoom_in?.();
|
|
97
111
|
event.stopPropagation();
|
|
98
112
|
}}
|
|
99
113
|
/>
|
|
@@ -102,20 +116,30 @@
|
|
|
102
116
|
<span
|
|
103
117
|
role="button"
|
|
104
118
|
tabindex="0"
|
|
105
|
-
|
|
106
|
-
|
|
119
|
+
onclick={handle_zoom_click}
|
|
120
|
+
onkeydown={handle_zoom_keydown}>{formatted_zoom}%</span
|
|
107
121
|
>
|
|
108
122
|
{#if show_zoom_popup}
|
|
109
123
|
<div class="zoom-controls">
|
|
110
124
|
<ul>
|
|
111
125
|
<li>
|
|
112
|
-
<button
|
|
126
|
+
<button
|
|
127
|
+
onclick={(event) => {
|
|
128
|
+
event.stopPropagation();
|
|
129
|
+
handle_zoom_change("fit");
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
113
132
|
Fit to screen
|
|
114
133
|
</button>
|
|
115
134
|
</li>
|
|
116
135
|
{#each [0.25, 0.5, 1, 2, 4] as zoom}
|
|
117
136
|
<li>
|
|
118
|
-
<button
|
|
137
|
+
<button
|
|
138
|
+
onclick={(event) => {
|
|
139
|
+
event.stopPropagation();
|
|
140
|
+
handle_zoom_change(zoom);
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
119
143
|
{zoom * 100}%
|
|
120
144
|
</button>
|
|
121
145
|
</li>
|
|
@@ -130,7 +154,7 @@
|
|
|
130
154
|
Icon={Undo}
|
|
131
155
|
label="Undo"
|
|
132
156
|
onclick={(event) => {
|
|
133
|
-
|
|
157
|
+
onundo?.();
|
|
134
158
|
event.stopPropagation();
|
|
135
159
|
}}
|
|
136
160
|
disabled={!can_undo}
|
|
@@ -140,7 +164,7 @@
|
|
|
140
164
|
Icon={Redo}
|
|
141
165
|
label="Redo"
|
|
142
166
|
onclick={(event) => {
|
|
143
|
-
|
|
167
|
+
onredo?.();
|
|
144
168
|
event.stopPropagation();
|
|
145
169
|
}}
|
|
146
170
|
disabled={!can_redo}
|
|
@@ -152,7 +176,7 @@
|
|
|
152
176
|
Icon={Check}
|
|
153
177
|
label="Save changes"
|
|
154
178
|
onclick={(event) => {
|
|
155
|
-
|
|
179
|
+
onsave?.();
|
|
156
180
|
event.stopPropagation();
|
|
157
181
|
}}
|
|
158
182
|
color="var(--color-accent)"
|
|
@@ -163,7 +187,7 @@
|
|
|
163
187
|
Icon={Trash}
|
|
164
188
|
label="Clear canvas"
|
|
165
189
|
onclick={(event) => {
|
|
166
|
-
|
|
190
|
+
onremove_image?.();
|
|
167
191
|
event.stopPropagation();
|
|
168
192
|
}}
|
|
169
193
|
/>
|
package/shared/IconButton.svelte
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { type Component } from "svelte";
|
|
3
3
|
import type { ColorInput } from "tinycolor2";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
Icon,
|
|
7
|
+
label = "",
|
|
8
|
+
show_label = false,
|
|
9
|
+
pending = false,
|
|
10
|
+
size = "small",
|
|
11
|
+
padded = true,
|
|
12
|
+
highlight = false,
|
|
13
|
+
disabled = false,
|
|
14
|
+
hasPopup = false,
|
|
15
|
+
color = "var(--block-label-text-color)",
|
|
16
|
+
transparent = false,
|
|
17
|
+
background = "var(--background-fill-primary)",
|
|
18
|
+
offset = 0,
|
|
19
|
+
label_position = "left",
|
|
20
|
+
roundedness = "quite",
|
|
21
|
+
onclick = undefined
|
|
22
|
+
}: {
|
|
23
|
+
Icon: Component<any>;
|
|
24
|
+
label?: string;
|
|
25
|
+
show_label?: boolean;
|
|
26
|
+
pending?: boolean;
|
|
27
|
+
size?: "small" | "large" | "medium";
|
|
28
|
+
padded?: boolean;
|
|
29
|
+
highlight?: boolean;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
hasPopup?: boolean;
|
|
32
|
+
color?: string | ColorInput;
|
|
33
|
+
transparent?: boolean;
|
|
34
|
+
background?: string;
|
|
35
|
+
offset?: number;
|
|
36
|
+
label_position?: "left" | "right";
|
|
37
|
+
roundedness?: "quite" | "very";
|
|
38
|
+
onclick?: (event: MouseEvent) => void;
|
|
39
|
+
} = $props();
|
|
40
|
+
|
|
41
|
+
let _color = $derived(highlight ? "var(--color-accent)" : color.toString());
|
|
21
42
|
</script>
|
|
22
43
|
|
|
23
44
|
<button
|