@gradio/imageslider 0.2.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.
@@ -0,0 +1,201 @@
1
+ <script>import Slider from "./Slider.svelte";
2
+ import ImageEl from "./ImageEl.svelte";
3
+ import {
4
+ BlockLabel,
5
+ Empty,
6
+ IconButton,
7
+ IconButtonWrapper,
8
+ FullscreenButton
9
+ } from "@gradio/atoms";
10
+ import { Image, Download, Undo, Clear } from "@gradio/icons";
11
+ import {} from "@gradio/client";
12
+ import { DownloadLink } from "@gradio/wasm/svelte";
13
+ import { ZoomableImage } from "./zoom";
14
+ import { onMount } from "svelte";
15
+ import { tweened } from "svelte/motion";
16
+ import { createEventDispatcher } from "svelte";
17
+ export let value = [null, null];
18
+ export let label = void 0;
19
+ export let show_download_button = true;
20
+ export let show_label;
21
+ export let i18n;
22
+ export let position;
23
+ export let layer_images = true;
24
+ export let show_single = false;
25
+ export let slider_color;
26
+ export let show_fullscreen_button = true;
27
+ export let el_width = 0;
28
+ export let max_height;
29
+ export let interactive = true;
30
+ const dispatch = createEventDispatcher();
31
+ let img;
32
+ let slider_wrap;
33
+ let image_container;
34
+ let transform = tweened(
35
+ { x: 0, y: 0, z: 1 },
36
+ {
37
+ duration: 75
38
+ }
39
+ );
40
+ let parent_el;
41
+ $:
42
+ coords_at_viewport = get_coords_at_viewport(
43
+ position,
44
+ viewport_width,
45
+ image_size.width,
46
+ image_size.left,
47
+ $transform.x,
48
+ $transform.z
49
+ );
50
+ $:
51
+ style = layer_images ? `clip-path: inset(0 0 0 ${coords_at_viewport * 100}%)` : "";
52
+ function get_coords_at_viewport(viewport_percent_x, viewportWidth, image_width, img_offset_x, tx, scale) {
53
+ const px_relative_to_image = viewport_percent_x * image_width;
54
+ const pixel_position = px_relative_to_image + img_offset_x;
55
+ const normalised_position = (pixel_position - tx) / scale;
56
+ const percent_position = normalised_position / viewportWidth;
57
+ return percent_position;
58
+ }
59
+ let img_width = 0;
60
+ let viewport_width = 0;
61
+ let zoomable_image = null;
62
+ let observer = null;
63
+ function init_image(img2, slider_wrap2) {
64
+ if (!img2 || !slider_wrap2)
65
+ return;
66
+ zoomable_image?.destroy();
67
+ observer?.disconnect();
68
+ img_width = img2?.getBoundingClientRect().width || 0;
69
+ viewport_width = slider_wrap2?.getBoundingClientRect().width || 0;
70
+ zoomable_image = new ZoomableImage(slider_wrap2, img2);
71
+ zoomable_image.subscribe(({ x, y, scale }) => {
72
+ transform.set({ x, y, z: scale });
73
+ });
74
+ observer = new ResizeObserver((entries) => {
75
+ for (const entry of entries) {
76
+ if (entry.target === slider_wrap2) {
77
+ viewport_width = entry.contentRect.width;
78
+ }
79
+ if (entry.target === img2) {
80
+ img_width = entry.contentRect.width;
81
+ }
82
+ }
83
+ });
84
+ observer.observe(slider_wrap2);
85
+ observer.observe(img2);
86
+ }
87
+ $:
88
+ init_image(img, slider_wrap);
89
+ onMount(() => {
90
+ return () => {
91
+ zoomable_image?.destroy();
92
+ observer?.disconnect();
93
+ };
94
+ });
95
+ let is_full_screen = false;
96
+ let slider_wrap_parent;
97
+ let image_size = { top: 0, left: 0, width: 0, height: 0 };
98
+ function handle_image_load(event) {
99
+ image_size = event.detail;
100
+ }
101
+ </script>
102
+
103
+ <BlockLabel {show_label} Icon={Image} label={label || i18n("image.image")} />
104
+ {#if (value === null || value[0] === null || value[1] === null) && !show_single}
105
+ <Empty unpadded_box={true} size="large"><Image /></Empty>
106
+ {:else}
107
+ <div class="image-container" bind:this={image_container}>
108
+ <IconButtonWrapper>
109
+ <IconButton
110
+ Icon={Undo}
111
+ label={i18n("common.undo")}
112
+ disabled={$transform.z === 1}
113
+ on:click={() => zoomable_image?.reset_zoom()}
114
+ />
115
+ {#if show_fullscreen_button}
116
+ <FullscreenButton container={image_container} bind:is_full_screen />
117
+ {/if}
118
+
119
+ {#if show_download_button}
120
+ <DownloadLink
121
+ href={value[1]?.url}
122
+ download={value[1]?.orig_name || "image"}
123
+ >
124
+ <IconButton Icon={Download} label={i18n("common.download")} />
125
+ </DownloadLink>
126
+ {/if}
127
+ {#if interactive}
128
+ <IconButton
129
+ Icon={Clear}
130
+ label="Remove Image"
131
+ on:click={(event) => {
132
+ value = [null, null];
133
+ dispatch("clear");
134
+ event.stopPropagation();
135
+ }}
136
+ />
137
+ {/if}
138
+ </IconButtonWrapper>
139
+ <div
140
+ class="slider-wrap"
141
+ bind:this={slider_wrap_parent}
142
+ bind:clientWidth={el_width}
143
+ class:limit_height={!is_full_screen}
144
+ >
145
+ <Slider
146
+ bind:position
147
+ {slider_color}
148
+ bind:el={slider_wrap}
149
+ bind:parent_el
150
+ {image_size}
151
+ >
152
+ <ImageEl
153
+ src={value?.[0]?.url}
154
+ alt=""
155
+ loading="lazy"
156
+ bind:img_el={img}
157
+ variant="preview"
158
+ transform="translate({$transform.x}px, {$transform.y}px) scale({$transform.z})"
159
+ fullscreen={is_full_screen}
160
+ {max_height}
161
+ on:load={handle_image_load}
162
+ />
163
+ <ImageEl
164
+ variant="preview"
165
+ fixed={layer_images}
166
+ hidden={!value?.[1]?.url}
167
+ src={value?.[1]?.url}
168
+ alt=""
169
+ loading="lazy"
170
+ {style}
171
+ transform="translate({$transform.x}px, {$transform.y}px) scale({$transform.z})"
172
+ fullscreen={is_full_screen}
173
+ {max_height}
174
+ on:load={handle_image_load}
175
+ />
176
+ </Slider>
177
+ </div>
178
+ </div>
179
+ {/if}
180
+
181
+ <style>
182
+ .slider-wrap {
183
+ user-select: none;
184
+ height: 100%;
185
+ width: 100%;
186
+ position: relative;
187
+ display: flex;
188
+ align-items: center;
189
+ justify-content: center;
190
+ }
191
+
192
+ .limit_height :global(img) {
193
+ max-height: 500px;
194
+ }
195
+
196
+ .image-container {
197
+ height: 100%;
198
+ position: relative;
199
+ min-width: var(--size-20);
200
+ }
201
+ </style>
@@ -0,0 +1,32 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type FileData } from "@gradio/client";
3
+ import type { I18nFormatter } from "@gradio/utils";
4
+ declare const __propDef: {
5
+ props: {
6
+ value?: [null | FileData, null | FileData] | undefined;
7
+ label?: string | undefined;
8
+ show_download_button?: boolean | undefined;
9
+ show_label: boolean;
10
+ i18n: I18nFormatter;
11
+ position: number;
12
+ layer_images?: boolean | undefined;
13
+ show_single?: boolean | undefined;
14
+ slider_color: string;
15
+ show_fullscreen_button?: boolean | undefined;
16
+ el_width?: number | undefined;
17
+ max_height: number;
18
+ interactive?: boolean | undefined;
19
+ };
20
+ events: {
21
+ clear: CustomEvent<void>;
22
+ } & {
23
+ [evt: string]: CustomEvent<any>;
24
+ };
25
+ slots: {};
26
+ };
27
+ export type SliderPreviewProps = typeof __propDef.props;
28
+ export type SliderPreviewEvents = typeof __propDef.events;
29
+ export type SliderPreviewSlots = typeof __propDef.slots;
30
+ export default class SliderPreview extends SvelteComponent<SliderPreviewProps, SliderPreviewEvents, SliderPreviewSlots> {
31
+ }
32
+ export {};
@@ -0,0 +1,41 @@
1
+ <svelte:options accessors={true} />
2
+
3
+ <script>import Image from "./Image.svelte";
4
+ import {} from "@gradio/client";
5
+ export let value = [null, null];
6
+ export let upload;
7
+ export let stream_handler;
8
+ export let label;
9
+ export let show_label;
10
+ export let i18n;
11
+ export let root;
12
+ export let upload_count = 1;
13
+ export let dragging;
14
+ export let max_height;
15
+ export let max_file_size = null;
16
+ </script>
17
+
18
+ <Image
19
+ slider_color="var(--border-color-primary)"
20
+ position={0.5}
21
+ bind:value
22
+ bind:dragging
23
+ {root}
24
+ on:edit
25
+ on:clear
26
+ on:stream
27
+ on:drag={({ detail }) => (dragging = detail)}
28
+ on:upload
29
+ on:select
30
+ on:share
31
+ {label}
32
+ {show_label}
33
+ {upload_count}
34
+ {stream_handler}
35
+ {upload}
36
+ {max_file_size}
37
+ {max_height}
38
+ {i18n}
39
+ >
40
+ <slot />
41
+ </Image>
@@ -0,0 +1,71 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { I18nFormatter } from "@gradio/utils";
3
+ import { type Client } from "@gradio/client";
4
+ import type { FileData } from "@gradio/client";
5
+ declare const __propDef: {
6
+ props: {
7
+ value?: [FileData | null, FileData | null] | undefined;
8
+ upload: Client["upload"];
9
+ stream_handler: Client["stream"];
10
+ label: string;
11
+ show_label: boolean;
12
+ i18n: I18nFormatter;
13
+ root: string;
14
+ upload_count?: number | undefined;
15
+ dragging: boolean;
16
+ max_height: number;
17
+ max_file_size?: (number | null) | undefined;
18
+ };
19
+ events: {
20
+ edit: CustomEvent<undefined>;
21
+ clear: CustomEvent<undefined>;
22
+ stream: CustomEvent<string | null>;
23
+ upload: CustomEvent<[any, any]>;
24
+ select: CustomEvent<import("@gradio/utils").SelectData>;
25
+ share: CustomEvent<any>;
26
+ } & {
27
+ [evt: string]: CustomEvent<any>;
28
+ };
29
+ slots: {
30
+ default: {};
31
+ };
32
+ };
33
+ export type SliderUploadProps = typeof __propDef.props;
34
+ export type SliderUploadEvents = typeof __propDef.events;
35
+ export type SliderUploadSlots = typeof __propDef.slots;
36
+ export default class SliderUpload extends SvelteComponent<SliderUploadProps, SliderUploadEvents, SliderUploadSlots> {
37
+ get value(): [any, any] | undefined;
38
+ /**accessor*/
39
+ set value(_: [any, any] | undefined);
40
+ get upload(): Client;
41
+ /**accessor*/
42
+ set upload(_: Client);
43
+ get stream_handler(): Client;
44
+ /**accessor*/
45
+ set stream_handler(_: Client);
46
+ get label(): string;
47
+ /**accessor*/
48
+ set label(_: string);
49
+ get show_label(): boolean;
50
+ /**accessor*/
51
+ set show_label(_: boolean);
52
+ get i18n(): any;
53
+ /**accessor*/
54
+ set i18n(_: any);
55
+ get root(): string;
56
+ /**accessor*/
57
+ set root(_: string);
58
+ get upload_count(): number | undefined;
59
+ /**accessor*/
60
+ set upload_count(_: number | undefined);
61
+ get dragging(): boolean;
62
+ /**accessor*/
63
+ set dragging(_: boolean);
64
+ get max_height(): number;
65
+ /**accessor*/
66
+ set max_height(_: number);
67
+ get max_file_size(): number | null | undefined;
68
+ /**accessor*/
69
+ set max_file_size(_: number | null | undefined);
70
+ }
71
+ export {};
@@ -0,0 +1,69 @@
1
+ export declare class ZoomableImage {
2
+ container: HTMLDivElement;
3
+ image: HTMLImageElement;
4
+ scale: number;
5
+ offsetX: number;
6
+ offsetY: number;
7
+ isDragging: boolean;
8
+ lastX: number;
9
+ lastY: number;
10
+ initial_left_padding: number;
11
+ initial_top_padding: number;
12
+ initial_width: number;
13
+ initial_height: number;
14
+ subscribers: (({ x, y, scale }: {
15
+ x: number;
16
+ y: number;
17
+ scale: number;
18
+ }) => void)[];
19
+ handleImageLoad: () => void;
20
+ real_image_size: {
21
+ top: number;
22
+ left: number;
23
+ width: number;
24
+ height: number;
25
+ };
26
+ last_touch_distance: number;
27
+ constructor(container: HTMLDivElement, image: HTMLImageElement);
28
+ handleResize(): void;
29
+ init(): void;
30
+ reset_zoom(): void;
31
+ handleMouseDown(e: MouseEvent): void;
32
+ handleMouseMove(e: MouseEvent): void;
33
+ handleMouseUp(): void;
34
+ handleWheel(e: WheelEvent): Promise<void>;
35
+ compute_new_position({ position, scale, anchor_position }: {
36
+ position: number;
37
+ scale: number;
38
+ anchor_position: number;
39
+ }): number;
40
+ compute_new_offset({ cursor_position, current_offset, new_scale, old_scale }: {
41
+ cursor_position: number;
42
+ current_offset: number;
43
+ new_scale: number;
44
+ old_scale: number;
45
+ }): number;
46
+ constrain_to_bounds(pan?: boolean): void;
47
+ updateTransform(): void;
48
+ destroy(): void;
49
+ subscribe(cb: ({ x, y, scale }: {
50
+ x: number;
51
+ y: number;
52
+ scale: number;
53
+ }) => void): void;
54
+ unsubscribe(cb: ({ x, y, scale }: {
55
+ x: number;
56
+ y: number;
57
+ scale: number;
58
+ }) => void): void;
59
+ notify({ x, y, scale }: {
60
+ x: number;
61
+ y: number;
62
+ scale: number;
63
+ }): void;
64
+ handleTouchStart(e: TouchEvent): void;
65
+ get_image_size(img: HTMLImageElement | null): void;
66
+ handleTouchMove(e: TouchEvent): void;
67
+ handleTouchEnd(e: TouchEvent): void;
68
+ calculate_position(screen_coord: number, image_anchor: number, axis: "x" | "y"): number;
69
+ }