@gradio/imageeditor 0.19.0 → 0.20.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 +21 -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 +8 -8
- 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
package/shared/Layers.svelte
CHANGED
|
@@ -10,45 +10,53 @@
|
|
|
10
10
|
VisibilityOff
|
|
11
11
|
} from "@gradio/icons";
|
|
12
12
|
import { IconButton } from "@gradio/atoms";
|
|
13
|
-
import { createEventDispatcher } from "svelte";
|
|
14
13
|
import type { Writable } from "svelte/store";
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
let {
|
|
16
|
+
layers,
|
|
17
|
+
enable_additional_layers = true,
|
|
18
|
+
enable_layers = true,
|
|
19
|
+
show_layers = false,
|
|
20
|
+
onnew_layer,
|
|
21
|
+
onchange_layer,
|
|
22
|
+
onmove_layer,
|
|
23
|
+
ondelete_layer,
|
|
24
|
+
ontoggle_layer_visibility
|
|
25
|
+
}: {
|
|
26
|
+
layers: Writable<{
|
|
27
|
+
active_layer: string;
|
|
28
|
+
layers: {
|
|
29
|
+
name: string;
|
|
30
|
+
id: string;
|
|
31
|
+
user_created: boolean;
|
|
32
|
+
visible: boolean;
|
|
33
|
+
}[];
|
|
34
|
+
}>;
|
|
35
|
+
enable_additional_layers?: boolean;
|
|
36
|
+
enable_layers?: boolean;
|
|
37
|
+
show_layers?: boolean;
|
|
38
|
+
onnew_layer?: () => void;
|
|
39
|
+
onchange_layer?: (id: string) => void;
|
|
40
|
+
onmove_layer?: (value: { id: string; direction: "up" | "down" }) => void;
|
|
41
|
+
ondelete_layer?: (id: string) => void;
|
|
42
|
+
ontoggle_layer_visibility?: (id: string) => void;
|
|
43
|
+
} = $props();
|
|
36
44
|
|
|
37
45
|
function new_layer(): void {
|
|
38
|
-
|
|
46
|
+
onnew_layer?.();
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
function change_layer(id: string): void {
|
|
42
|
-
|
|
50
|
+
onchange_layer?.(id);
|
|
43
51
|
show_layers = false;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
function move_layer(id: string, direction: "up" | "down"): void {
|
|
47
|
-
|
|
55
|
+
onmove_layer?.({ id, direction });
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
function delete_layer(id: string): void {
|
|
51
|
-
|
|
59
|
+
ondelete_layer?.(id);
|
|
52
60
|
}
|
|
53
61
|
</script>
|
|
54
62
|
|
|
@@ -61,7 +69,10 @@
|
|
|
61
69
|
<button
|
|
62
70
|
class="layer-title-button"
|
|
63
71
|
aria-label="Show Layers"
|
|
64
|
-
|
|
72
|
+
onclick={(event) => {
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
show_layers = !show_layers;
|
|
75
|
+
}}
|
|
65
76
|
>
|
|
66
77
|
{show_layers
|
|
67
78
|
? "Layers"
|
|
@@ -78,7 +89,7 @@
|
|
|
78
89
|
size="small"
|
|
79
90
|
onclick={(e) => {
|
|
80
91
|
e.stopPropagation();
|
|
81
|
-
|
|
92
|
+
ontoggle_layer_visibility?.(id);
|
|
82
93
|
}}
|
|
83
94
|
/>
|
|
84
95
|
{:else}
|
|
@@ -87,14 +98,17 @@
|
|
|
87
98
|
size="small"
|
|
88
99
|
onclick={(e) => {
|
|
89
100
|
e.stopPropagation();
|
|
90
|
-
|
|
101
|
+
ontoggle_layer_visibility?.(id);
|
|
91
102
|
}}
|
|
92
103
|
/>
|
|
93
104
|
{/if}
|
|
94
105
|
<button
|
|
95
106
|
class:selected_layer={$layers.active_layer === id}
|
|
96
107
|
aria-label={`layer-${i + 1}`}
|
|
97
|
-
|
|
108
|
+
onclick={(event) => {
|
|
109
|
+
event.stopPropagation();
|
|
110
|
+
change_layer(id);
|
|
111
|
+
}}>{name}</button
|
|
98
112
|
>
|
|
99
113
|
{#if $layers.layers.length > 1}
|
|
100
114
|
<div>
|
package/shared/Resize.svelte
CHANGED
|
@@ -12,24 +12,30 @@
|
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<script lang="ts">
|
|
15
|
-
import { createEventDispatcher } from "svelte";
|
|
16
15
|
import type { Spring } from "svelte/motion";
|
|
17
16
|
import Anchor from "./Anchor.svelte";
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
let {
|
|
19
|
+
dimensions,
|
|
20
|
+
onchange
|
|
21
|
+
}: {
|
|
22
|
+
dimensions: Spring<{ width: number; height: number }>;
|
|
23
|
+
onchange?: (value: {
|
|
24
|
+
anchor: Position;
|
|
25
|
+
scale: boolean;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
}) => void;
|
|
29
|
+
} = $props();
|
|
30
|
+
|
|
31
|
+
let selected_anchor = $state<Position>("center");
|
|
32
|
+
let scale = $state(false);
|
|
33
|
+
|
|
34
|
+
let new_width = $state($dimensions?.width);
|
|
35
|
+
let new_height = $state($dimensions?.height);
|
|
30
36
|
|
|
31
37
|
function handle_click(): void {
|
|
32
|
-
|
|
38
|
+
onchange?.({
|
|
33
39
|
anchor: selected_anchor,
|
|
34
40
|
scale,
|
|
35
41
|
width: new_width,
|
|
@@ -44,7 +50,7 @@
|
|
|
44
50
|
|
|
45
51
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
46
52
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
47
|
-
<div class="wrap"
|
|
53
|
+
<div class="wrap" onclick={(event) => event.stopPropagation()}>
|
|
48
54
|
<div class="size-wrap">
|
|
49
55
|
<div class="size-input">
|
|
50
56
|
<label for="width">Width</label><input
|
|
@@ -86,9 +92,9 @@
|
|
|
86
92
|
</div>
|
|
87
93
|
</div>
|
|
88
94
|
<div class="anchor-wrap">
|
|
89
|
-
<Anchor
|
|
95
|
+
<Anchor onposition={(position) => set_anchor(position)} />
|
|
90
96
|
</div>
|
|
91
|
-
<button class="apply-button"
|
|
97
|
+
<button class="apply-button" onclick={() => handle_click()}
|
|
92
98
|
>Resize Canvas</button
|
|
93
99
|
>
|
|
94
100
|
</div>
|
|
@@ -2,19 +2,35 @@
|
|
|
2
2
|
import Layers from "./Layers.svelte";
|
|
3
3
|
import type { Writable } from "svelte/store";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
let {
|
|
6
|
+
layers,
|
|
7
|
+
enable_additional_layers = true,
|
|
8
|
+
enable_layers = true,
|
|
9
|
+
show_layers = false,
|
|
10
|
+
onnew_layer,
|
|
11
|
+
onchange_layer,
|
|
12
|
+
onmove_layer,
|
|
13
|
+
ondelete_layer,
|
|
14
|
+
ontoggle_layer_visibility
|
|
15
|
+
}: {
|
|
16
|
+
layers: Writable<{
|
|
17
|
+
active_layer: string;
|
|
18
|
+
layers: {
|
|
19
|
+
name: string;
|
|
20
|
+
id: string;
|
|
21
|
+
user_created: boolean;
|
|
22
|
+
visible: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
}>;
|
|
25
|
+
enable_additional_layers?: boolean;
|
|
26
|
+
enable_layers?: boolean;
|
|
27
|
+
show_layers?: boolean;
|
|
28
|
+
onnew_layer?: () => void;
|
|
29
|
+
onchange_layer?: (id: string) => void;
|
|
30
|
+
onmove_layer?: (value: { id: string; direction: "up" | "down" }) => void;
|
|
31
|
+
ondelete_layer?: (id: string) => void;
|
|
32
|
+
ontoggle_layer_visibility?: (id: string) => void;
|
|
33
|
+
} = $props();
|
|
18
34
|
</script>
|
|
19
35
|
|
|
20
36
|
<div class="toolbar-wrap-wrap">
|
|
@@ -25,11 +41,11 @@
|
|
|
25
41
|
{enable_additional_layers}
|
|
26
42
|
{enable_layers}
|
|
27
43
|
{show_layers}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
{onnew_layer}
|
|
45
|
+
{onchange_layer}
|
|
46
|
+
{onmove_layer}
|
|
47
|
+
{ondelete_layer}
|
|
48
|
+
{ontoggle_layer_visibility}
|
|
33
49
|
/>
|
|
34
50
|
{/if}
|
|
35
51
|
</div>
|
package/shared/Toolbar.svelte
CHANGED
|
@@ -12,17 +12,16 @@
|
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<script lang="ts">
|
|
15
|
-
import { createEventDispatcher } from "svelte";
|
|
16
15
|
import IconButton from "./IconButton.svelte";
|
|
17
16
|
import {
|
|
18
17
|
Image,
|
|
19
18
|
Brush,
|
|
19
|
+
BrushSize,
|
|
20
20
|
Erase,
|
|
21
21
|
Crop,
|
|
22
22
|
Upload,
|
|
23
23
|
ImagePaste,
|
|
24
24
|
Webcam,
|
|
25
|
-
Circle,
|
|
26
25
|
Resize,
|
|
27
26
|
ColorPickerSolid
|
|
28
27
|
} from "@gradio/icons";
|
|
@@ -32,66 +31,46 @@
|
|
|
32
31
|
type Brush as BrushType,
|
|
33
32
|
type Eraser as EraserType
|
|
34
33
|
} from "./brush/types";
|
|
35
|
-
import tinycolor from "tinycolor2";
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
let {
|
|
36
|
+
tool = "image",
|
|
37
|
+
subtool = null,
|
|
38
|
+
background = false,
|
|
39
|
+
brush_options,
|
|
40
|
+
selected_size = $bindable(25),
|
|
41
|
+
eraser_options,
|
|
42
|
+
selected_eraser_size = $bindable(25),
|
|
43
|
+
selected_color = $bindable(),
|
|
44
|
+
selected_opacity = $bindable(1),
|
|
45
|
+
preview = $bindable(),
|
|
46
|
+
show_brush_color = false,
|
|
47
|
+
show_brush_size = false,
|
|
48
|
+
show_eraser_size = false,
|
|
49
|
+
sources,
|
|
50
|
+
transforms,
|
|
51
|
+
ontool_change,
|
|
52
|
+
onsubtool_change
|
|
53
|
+
}: {
|
|
54
|
+
tool?: Tool;
|
|
55
|
+
subtool?: Subtool;
|
|
56
|
+
background?: boolean;
|
|
57
|
+
brush_options: BrushType | false;
|
|
58
|
+
selected_size?: number;
|
|
59
|
+
eraser_options: EraserType | false;
|
|
60
|
+
selected_eraser_size?: number;
|
|
61
|
+
selected_color?: any;
|
|
62
|
+
selected_opacity?: number;
|
|
63
|
+
preview?: boolean;
|
|
64
|
+
show_brush_color?: boolean;
|
|
65
|
+
show_brush_size?: boolean;
|
|
66
|
+
show_eraser_size?: boolean;
|
|
67
|
+
sources: Source[];
|
|
68
|
+
transforms: Transform[];
|
|
69
|
+
ontool_change?: (value: { tool: Tool }) => void;
|
|
70
|
+
onsubtool_change?: (value: { tool: Tool; subtool: Subtool }) => void;
|
|
71
|
+
} = $props();
|
|
39
72
|
|
|
40
|
-
|
|
41
|
-
export let brush_options: BrushType | false;
|
|
42
|
-
export let selected_size =
|
|
43
|
-
brush_options && typeof brush_options.default_size === "number"
|
|
44
|
-
? brush_options.default_size
|
|
45
|
-
: 25;
|
|
46
|
-
export let eraser_options: EraserType | false;
|
|
47
|
-
export let selected_eraser_size =
|
|
48
|
-
eraser_options && typeof eraser_options.default_size === "number"
|
|
49
|
-
? eraser_options.default_size
|
|
50
|
-
: 25;
|
|
51
|
-
|
|
52
|
-
// Handle default_color including potential color-opacity tuple
|
|
53
|
-
export let selected_color =
|
|
54
|
-
brush_options &&
|
|
55
|
-
(() => {
|
|
56
|
-
const default_color = brush_options.default_color;
|
|
57
|
-
if (Array.isArray(default_color)) {
|
|
58
|
-
return default_color[0];
|
|
59
|
-
}
|
|
60
|
-
return default_color;
|
|
61
|
-
})();
|
|
62
|
-
|
|
63
|
-
// Set default opacity based on default_color if it's a tuple
|
|
64
|
-
export let selected_opacity =
|
|
65
|
-
brush_options &&
|
|
66
|
-
(() => {
|
|
67
|
-
const default_color = brush_options.default_color;
|
|
68
|
-
if (Array.isArray(default_color)) {
|
|
69
|
-
return default_color[1];
|
|
70
|
-
}
|
|
71
|
-
// Check if color string has opacity
|
|
72
|
-
const color = tinycolor(default_color);
|
|
73
|
-
if (color.getAlpha() < 1) {
|
|
74
|
-
return color.getAlpha();
|
|
75
|
-
}
|
|
76
|
-
return 1;
|
|
77
|
-
})();
|
|
78
|
-
|
|
79
|
-
export let preview = false;
|
|
80
|
-
export let show_brush_color = false;
|
|
81
|
-
export let show_brush_size = false;
|
|
82
|
-
export let show_eraser_size = false;
|
|
83
|
-
export let sources: Source[];
|
|
84
|
-
export let transforms: Transform[];
|
|
85
|
-
let recent_colors: string[] = [];
|
|
86
|
-
|
|
87
|
-
let enable_layers = true;
|
|
88
|
-
const dispatch = createEventDispatcher<{
|
|
89
|
-
tool_change: { tool: Tool };
|
|
90
|
-
subtool_change: {
|
|
91
|
-
tool: Tool;
|
|
92
|
-
subtool: Subtool;
|
|
93
|
-
};
|
|
94
|
-
}>();
|
|
73
|
+
let recent_colors = $state<string[]>([]);
|
|
95
74
|
|
|
96
75
|
/**
|
|
97
76
|
* Handles tool click events
|
|
@@ -99,7 +78,7 @@
|
|
|
99
78
|
*/
|
|
100
79
|
function handle_tool_click(e: Event, _tool: Tool): void {
|
|
101
80
|
e.stopPropagation();
|
|
102
|
-
|
|
81
|
+
ontool_change?.({ tool: _tool });
|
|
103
82
|
}
|
|
104
83
|
|
|
105
84
|
/**
|
|
@@ -110,23 +89,28 @@
|
|
|
110
89
|
function handle_subtool_click(e: Event, _subtool: typeof subtool): void {
|
|
111
90
|
e.stopPropagation();
|
|
112
91
|
|
|
113
|
-
|
|
92
|
+
onsubtool_change?.({ tool, subtool: _subtool });
|
|
114
93
|
}
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
95
|
+
$effect(() => {
|
|
96
|
+
show_brush_size = tool === "draw" && subtool === "size";
|
|
97
|
+
show_brush_color = tool === "draw" && subtool === "color";
|
|
98
|
+
show_eraser_size = tool === "erase" && subtool === "size";
|
|
99
|
+
});
|
|
119
100
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
101
|
+
let can_crop = $derived(transforms.includes("crop"));
|
|
102
|
+
let can_resize = $derived(transforms.includes("resize"));
|
|
103
|
+
let can_upload = $derived(sources.includes("upload"));
|
|
104
|
+
let can_webcam = $derived(sources.includes("webcam"));
|
|
105
|
+
let can_paste = $derived(sources.includes("clipboard"));
|
|
106
|
+
let can_edit_image = $derived(
|
|
107
|
+
sources.length > 0 || (background && transforms.length > 0)
|
|
108
|
+
);
|
|
125
109
|
</script>
|
|
126
110
|
|
|
127
|
-
<div class="toolbar-wrap">
|
|
111
|
+
<div class="toolbar-wrap" onclick={(e) => e.stopPropagation()}>
|
|
128
112
|
<div class="half-container">
|
|
129
|
-
{#if
|
|
113
|
+
{#if can_edit_image}
|
|
130
114
|
<IconButton
|
|
131
115
|
Icon={Image}
|
|
132
116
|
label="Image"
|
|
@@ -244,7 +228,7 @@
|
|
|
244
228
|
offset={0}
|
|
245
229
|
/>
|
|
246
230
|
<IconButton
|
|
247
|
-
Icon={
|
|
231
|
+
Icon={BrushSize}
|
|
248
232
|
label="Brush Size"
|
|
249
233
|
onclick={(e) => handle_subtool_click(e, "size")}
|
|
250
234
|
highlight={subtool === "size"}
|
|
@@ -265,7 +249,7 @@
|
|
|
265
249
|
bind:selected_color
|
|
266
250
|
bind:selected_opacity
|
|
267
251
|
bind:preview
|
|
268
|
-
|
|
252
|
+
onclick_outside={(e) => {
|
|
269
253
|
e.stopPropagation();
|
|
270
254
|
preview = false;
|
|
271
255
|
show_brush_color = false;
|
|
@@ -279,7 +263,7 @@
|
|
|
279
263
|
|
|
280
264
|
{#if tool === "erase" && eraser_options}
|
|
281
265
|
<IconButton
|
|
282
|
-
Icon={
|
|
266
|
+
Icon={BrushSize}
|
|
283
267
|
label="Eraser Size"
|
|
284
268
|
onclick={(e) => handle_subtool_click(e, "size")}
|
|
285
269
|
highlight={subtool === "size"}
|
|
@@ -298,7 +282,7 @@
|
|
|
298
282
|
bind:selected_color
|
|
299
283
|
bind:selected_opacity
|
|
300
284
|
bind:preview
|
|
301
|
-
|
|
285
|
+
onclick_outside={(e) => {
|
|
302
286
|
e.stopPropagation();
|
|
303
287
|
preview = false;
|
|
304
288
|
show_eraser_size = false;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher, tick } from "svelte";
|
|
3
2
|
import { click_outside } from "../utils/events";
|
|
4
3
|
|
|
5
4
|
import { type Brush, type ColorTuple } from "./types";
|
|
@@ -9,23 +8,35 @@
|
|
|
9
8
|
import BrushSize from "./BrushSize.svelte";
|
|
10
9
|
import type { ColorInput } from "tinycolor2";
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
let {
|
|
12
|
+
colors,
|
|
13
|
+
selected_color = $bindable(),
|
|
14
|
+
color_mode = undefined,
|
|
15
|
+
recent_colors = [],
|
|
16
|
+
selected_size = $bindable(),
|
|
17
|
+
selected_opacity = $bindable(),
|
|
18
|
+
show_swatch,
|
|
19
|
+
show_size,
|
|
20
|
+
mode = "brush",
|
|
21
|
+
preview = $bindable(),
|
|
22
|
+
onclick_outside = () => {}
|
|
23
|
+
}: {
|
|
24
|
+
colors: (ColorInput | ColorTuple)[];
|
|
25
|
+
selected_color: any;
|
|
26
|
+
color_mode?: Brush["color_mode"];
|
|
27
|
+
recent_colors?: (ColorInput | ColorTuple)[];
|
|
28
|
+
selected_size: number;
|
|
29
|
+
selected_opacity: number;
|
|
30
|
+
show_swatch: boolean;
|
|
31
|
+
show_size: boolean;
|
|
32
|
+
mode?: "brush" | "eraser";
|
|
33
|
+
preview?: boolean;
|
|
34
|
+
onclick_outside?: (event: MouseEvent) => void;
|
|
35
|
+
} = $props();
|
|
36
|
+
|
|
37
|
+
let color_picker = $state(false);
|
|
38
|
+
let current_mode = $state<"hex" | "rgb" | "hsl">("hex");
|
|
39
|
+
let editing_index = $state<number | null>(null);
|
|
29
40
|
|
|
30
41
|
function handle_color_selection(
|
|
31
42
|
{
|
|
@@ -61,9 +72,11 @@
|
|
|
61
72
|
recent_colors[editing_index] = color;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
$effect(() => {
|
|
76
|
+
handle_color_change(selected_color);
|
|
77
|
+
});
|
|
78
|
+
let width = $state(0);
|
|
79
|
+
let height = $state(0);
|
|
67
80
|
|
|
68
81
|
function debounce(
|
|
69
82
|
func: (...args: any[]) => void,
|
|
@@ -76,7 +89,6 @@
|
|
|
76
89
|
};
|
|
77
90
|
}
|
|
78
91
|
|
|
79
|
-
export let preview = false;
|
|
80
92
|
function handle_preview(): void {
|
|
81
93
|
if (!preview) {
|
|
82
94
|
preview = true;
|
|
@@ -92,7 +104,15 @@
|
|
|
92
104
|
|
|
93
105
|
const debounced_close_preview = debounce(handle_close_preview, 1000);
|
|
94
106
|
|
|
95
|
-
|
|
107
|
+
$effect(() => {
|
|
108
|
+
if (!show_size) {
|
|
109
|
+
preview = false;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
selected_size;
|
|
114
|
+
handle_preview();
|
|
115
|
+
});
|
|
96
116
|
|
|
97
117
|
function handle_select(color: string): void {
|
|
98
118
|
selected_color = color;
|
|
@@ -124,7 +144,7 @@
|
|
|
124
144
|
<div
|
|
125
145
|
class="wrap"
|
|
126
146
|
class:padded={!color_picker}
|
|
127
|
-
use:click_outside={
|
|
147
|
+
use:click_outside={onclick_outside}
|
|
128
148
|
class:color_picker
|
|
129
149
|
class:size_picker={show_size && mode === "brush"}
|
|
130
150
|
class:eraser_picker={mode === "eraser"}
|
|
@@ -138,8 +158,8 @@
|
|
|
138
158
|
<ColorField
|
|
139
159
|
bind:current_mode
|
|
140
160
|
color={selected_color}
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
onclose={() => (color_picker = false)}
|
|
162
|
+
onselected={(detail) => handle_select(detail)}
|
|
143
163
|
/>
|
|
144
164
|
{/if}
|
|
145
165
|
{/if}
|
|
@@ -147,12 +167,12 @@
|
|
|
147
167
|
<ColorSwatch
|
|
148
168
|
bind:color_picker
|
|
149
169
|
{colors}
|
|
150
|
-
|
|
151
|
-
|
|
170
|
+
onselect={(detail) => handle_color_selection(detail, "core")}
|
|
171
|
+
onedit={(detail) => handle_color_selection(detail, "user")}
|
|
152
172
|
user_colors={color_mode === "defaults" ? recent_colors : null}
|
|
153
173
|
{selected_color}
|
|
154
174
|
{current_mode}
|
|
155
|
-
|
|
175
|
+
onadd_color={handle_add_color}
|
|
156
176
|
/>
|
|
157
177
|
{/if}
|
|
158
178
|
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { click_outside } from "../utils/events";
|
|
3
|
-
import { createEventDispatcher } from "svelte";
|
|
4
3
|
import { BrushSize } from "@gradio/icons";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
let {
|
|
6
|
+
selected_size = $bindable(),
|
|
7
|
+
min,
|
|
8
|
+
max,
|
|
9
|
+
onclick_outside = () => {}
|
|
10
|
+
}: {
|
|
11
|
+
selected_size: number;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
onclick_outside?: (event: MouseEvent) => void;
|
|
15
|
+
} = $props();
|
|
13
16
|
</script>
|
|
14
17
|
|
|
15
|
-
<div class="wrap" use:click_outside={
|
|
18
|
+
<div class="wrap" use:click_outside={onclick_outside}>
|
|
16
19
|
<span>
|
|
17
20
|
<BrushSize />
|
|
18
21
|
</span>
|