@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/Example.svelte +9 -3
  3. package/Index.svelte +23 -24
  4. package/InteractiveImageEditor.svelte +99 -61
  5. package/dist/Example.svelte +9 -3
  6. package/dist/Example.svelte.d.ts +4 -18
  7. package/dist/Index.svelte +23 -24
  8. package/dist/Index.svelte.d.ts +2 -19
  9. package/dist/InteractiveImageEditor.svelte +99 -61
  10. package/dist/InteractiveImageEditor.svelte.d.ts +15 -32
  11. package/dist/shared/Anchor.svelte +13 -11
  12. package/dist/shared/Anchor.svelte.d.ts +4 -19
  13. package/dist/shared/Controls.svelte +63 -39
  14. package/dist/shared/Controls.svelte.d.ts +13 -28
  15. package/dist/shared/IconButton.svelte +38 -17
  16. package/dist/shared/IconButton.svelte.d.ts +5 -19
  17. package/dist/shared/ImageEditor.svelte +235 -160
  18. package/dist/shared/ImageEditor.svelte.d.ts +55 -2
  19. package/dist/shared/Layers.svelte +43 -29
  20. package/dist/shared/Layers.svelte.d.ts +10 -25
  21. package/dist/shared/Resize.svelte +22 -16
  22. package/dist/shared/Resize.svelte.d.ts +6 -22
  23. package/dist/shared/SecondaryToolbar.svelte +34 -18
  24. package/dist/shared/SecondaryToolbar.svelte.d.ts +10 -25
  25. package/dist/shared/Toolbar.svelte +60 -76
  26. package/dist/shared/Toolbar.svelte.d.ts +9 -24
  27. package/dist/shared/brush/BrushOptions.svelte +49 -29
  28. package/dist/shared/brush/BrushOptions.svelte.d.ts +6 -21
  29. package/dist/shared/brush/BrushSize.svelte +12 -9
  30. package/dist/shared/brush/BrushSize.svelte.d.ts +5 -20
  31. package/dist/shared/brush/ColorField.svelte +21 -16
  32. package/dist/shared/brush/ColorField.svelte.d.ts +6 -22
  33. package/dist/shared/brush/ColorPicker.svelte +22 -16
  34. package/dist/shared/brush/ColorPicker.svelte.d.ts +4 -18
  35. package/dist/shared/brush/ColorSwatch.svelte +41 -21
  36. package/dist/shared/brush/ColorSwatch.svelte.d.ts +10 -24
  37. package/dist/shared/brush/brush.js +7 -8
  38. package/dist/shared/core/editor.d.ts +5 -0
  39. package/dist/shared/core/editor.js +43 -2
  40. package/dist/shared/resize/resize.d.ts +2 -0
  41. package/dist/shared/resize/resize.js +27 -18
  42. package/package.json +9 -9
  43. package/shared/Anchor.svelte +13 -11
  44. package/shared/Controls.svelte +63 -39
  45. package/shared/IconButton.svelte +38 -17
  46. package/shared/ImageEditor.svelte +235 -160
  47. package/shared/Layers.svelte +43 -29
  48. package/shared/Resize.svelte +22 -16
  49. package/shared/SecondaryToolbar.svelte +34 -18
  50. package/shared/Toolbar.svelte +60 -76
  51. package/shared/brush/BrushOptions.svelte +49 -29
  52. package/shared/brush/BrushSize.svelte +12 -9
  53. package/shared/brush/ColorField.svelte +21 -16
  54. package/shared/brush/ColorPicker.svelte +22 -16
  55. package/shared/brush/ColorSwatch.svelte +41 -21
  56. package/shared/brush/brush.ts +8 -10
  57. package/shared/core/editor.ts +49 -2
  58. package/shared/resize/resize.ts +28 -20
@@ -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
- const dispatch = createEventDispatcher<{
17
- new_layer: void;
18
- change_layer: string;
19
- move_layer: { id: string; direction: "up" | "down" };
20
- delete_layer: string;
21
- toggle_layer_visibility: string;
22
- }>();
23
-
24
- export let layers: Writable<{
25
- active_layer: string;
26
- layers: {
27
- name: string;
28
- id: string;
29
- user_created: boolean;
30
- visible: boolean;
31
- }[];
32
- }>;
33
- export let enable_additional_layers = true;
34
- export let enable_layers = true;
35
- export let show_layers = false;
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
- dispatch("new_layer");
46
+ onnew_layer?.();
39
47
  }
40
48
 
41
49
  function change_layer(id: string): void {
42
- dispatch("change_layer", id);
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
- dispatch("move_layer", { id, direction });
55
+ onmove_layer?.({ id, direction });
48
56
  }
49
57
 
50
58
  function delete_layer(id: string): void {
51
- dispatch("delete_layer", id);
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
- on:click|stopPropagation={() => (show_layers = !show_layers)}
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
- dispatch("toggle_layer_visibility", id);
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
- dispatch("toggle_layer_visibility", id);
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
- on:click|stopPropagation={() => change_layer(id)}>{name}</button
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>
@@ -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
- export let dimensions: Spring<{ width: number; height: number }>;
20
-
21
- const dispatch = createEventDispatcher<{
22
- change: { anchor: Position; scale: boolean; width: number; height: number };
23
- }>();
24
-
25
- let selected_anchor: Position = "center";
26
- let scale = false;
27
-
28
- let new_width = $dimensions?.width;
29
- let new_height = $dimensions?.height;
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
- dispatch("change", {
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" on:click|stopPropagation>
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 on:position={(e) => set_anchor(e.detail)} />
95
+ <Anchor onposition={(position) => set_anchor(position)} />
90
96
  </div>
91
- <button class="apply-button" on:click={() => handle_click()}
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
- export let layers: Writable<{
6
- active_layer: string;
7
- layers: {
8
- name: string;
9
- id: string;
10
- user_created: boolean;
11
- visible: boolean;
12
- }[];
13
- }>;
14
-
15
- export let enable_additional_layers = true;
16
- export let enable_layers = true;
17
- export let show_layers = false;
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
- on:new_layer
29
- on:change_layer
30
- on:move_layer
31
- on:delete_layer
32
- on:toggle_layer_visibility
44
+ {onnew_layer}
45
+ {onchange_layer}
46
+ {onmove_layer}
47
+ {ondelete_layer}
48
+ {ontoggle_layer_visibility}
33
49
  />
34
50
  {/if}
35
51
  </div>
@@ -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
- export let tool: Tool = "image";
38
- export let subtool: Subtool = null;
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
- export let background = false;
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
- dispatch("tool_change", { tool: _tool });
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
- dispatch("subtool_change", { tool, subtool: _subtool });
92
+ onsubtool_change?.({ tool, subtool: _subtool });
114
93
  }
115
94
 
116
- $: show_brush_size = tool === "draw" && subtool === "size";
117
- $: show_brush_color = tool === "draw" && subtool === "color";
118
- $: show_eraser_size = tool === "erase" && subtool === "size";
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
- $: can_crop = transforms.includes("crop");
121
- $: can_resize = transforms.includes("resize");
122
- $: can_upload = sources.includes("upload");
123
- $: can_webcam = sources.includes("webcam");
124
- $: can_paste = sources.includes("clipboard");
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 sources.length > 0}
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={Circle}
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
- on:click_outside={(e) => {
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={Circle}
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
- on:click_outside={(e) => {
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
- export let colors: (ColorInput | ColorTuple)[];
13
- export let selected_color: any;
14
- export let color_mode: Brush["color_mode"] | undefined = undefined;
15
- export let recent_colors: (ColorInput | ColorTuple)[] = [];
16
- export let selected_size: number;
17
- export let selected_opacity: number;
18
- export let show_swatch: boolean;
19
- export let show_size: boolean;
20
- export let mode: "brush" | "eraser" = "brush";
21
-
22
- let color_picker = false;
23
- let current_mode: "hex" | "rgb" | "hsl" = "hex";
24
- let editing_index: number | null = null;
25
-
26
- const dispatch = createEventDispatcher<{
27
- click_outside: void;
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
- $: handle_color_change(selected_color);
65
- let width = 0;
66
- let height = 0;
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
- $: (selected_size, selected_color, handle_preview());
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={() => dispatch("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
- on:close={() => (color_picker = false)}
142
- on:selected={({ detail }) => handle_select(detail)}
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
- on:select={({ detail }) => handle_color_selection(detail, "core")}
151
- on:edit={({ detail }) => handle_color_selection(detail, "user")}
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
- on:add_color={handle_add_color}
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
- export let selected_size: number;
7
- export let min: number;
8
- export let max: number;
9
-
10
- const dispatch = createEventDispatcher<{
11
- click_outside: void;
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={() => dispatch("click_outside")}>
18
+ <div class="wrap" use:click_outside={onclick_outside}>
16
19
  <span>
17
20
  <BrushSize />
18
21
  </span>