@gradio/file 0.11.3 → 0.12.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 CHANGED
@@ -1,5 +1,35 @@
1
1
  # @gradio/file
2
2
 
3
+ ## 0.12.1
4
+
5
+ ### Dependency updates
6
+
7
+ - @gradio/atoms@0.13.1
8
+ - @gradio/statustracker@0.10.1
9
+ - @gradio/client@1.10.0
10
+ - @gradio/icons@0.10.0
11
+ - @gradio/upload@0.14.5
12
+
13
+ ## 0.12.0
14
+
15
+ ### Features
16
+
17
+ - [#10210](https://github.com/gradio-app/gradio/pull/10210) [`13a83e5`](https://github.com/gradio-app/gradio/commit/13a83e5001e29594990d61569887cb8562744964) - Allow reordering files in gr.File. Thanks @hannahblair!
18
+
19
+ ### Fixes
20
+
21
+ - [#10209](https://github.com/gradio-app/gradio/pull/10209) [`2700d18`](https://github.com/gradio-app/gradio/commit/2700d1898bc8eeb3fd9f65ce77be17c2614a388f) - Ensure the `height` param in `gr.File` works as expected. Thanks @hannahblair!
22
+
23
+ ### Dependency updates
24
+
25
+ - @gradio/atoms@0.13.0
26
+ - @gradio/utils@0.10.0
27
+ - @gradio/upload@0.14.4
28
+ - @gradio/client@1.9.0
29
+ - @gradio/icons@0.9.0
30
+ - @gradio/statustracker@0.10.0
31
+ - @gradio/wasm@0.16.0
32
+
3
33
  ## 0.11.3
4
34
 
5
35
  ### Dependency updates
@@ -1,11 +1,11 @@
1
1
  <script>
2
2
  import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
3
- import FilePreview from "./shared/FilePreview.svelte";
3
+ import File from "./Index.svelte";
4
4
  </script>
5
5
 
6
6
  <Meta
7
7
  title="Components/File"
8
- component={FilePreview}
8
+ component={File}
9
9
  argTypes={{
10
10
  value: {
11
11
  control: "text",
@@ -17,7 +17,7 @@
17
17
  />
18
18
 
19
19
  <Template let:args>
20
- <FilePreview {...args} />
20
+ <File {...args} />
21
21
  </Template>
22
22
 
23
23
  <Story
@@ -34,14 +34,31 @@
34
34
  }}
35
35
  />
36
36
  <Story
37
- name="Multiple files, with height set to 150px"
37
+ name="Multiple files, with height set to 150px and reordering enabled"
38
38
  args={{
39
- value: Array(10).fill({
40
- path: "cheetah.jpg",
41
- orig_name: "cheetah.jpg",
42
- url: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
43
- size: 10000
44
- }),
45
- height: 150
39
+ value: [
40
+ {
41
+ path: "cheetah.jpg",
42
+ orig_name: "cheetah.jpgz",
43
+ url: "https://raw.githubusercontent.com/gradio-app/gradio/main/test/test_files/bus.png",
44
+ size: 10000
45
+ },
46
+ {
47
+ path: "cheetah.jpgs",
48
+ orig_name: "cheetah.jpg",
49
+ url: "https://gradio-builds.s3.amazonaws.com/demo-files/ghepardo-primo-piano.jpg",
50
+ size: 10000
51
+ }
52
+ ],
53
+ height: 150,
54
+ allow_reordering: true
55
+ }}
56
+ />
57
+ <Story
58
+ name="File upload with height set to 400px"
59
+ args={{
60
+ interactive: true,
61
+ value: null,
62
+ height: 400
46
63
  }}
47
64
  />
package/Index.svelte CHANGED
@@ -46,6 +46,7 @@
46
46
  export let file_count: "single" | "multiple" | "directory";
47
47
  export let file_types: string[] = ["file"];
48
48
  export let input_ready: boolean;
49
+ export let allow_reordering = false;
49
50
  let uploading = false;
50
51
  $: input_ready = !uploading;
51
52
 
@@ -103,6 +104,7 @@
103
104
  selectable={_selectable}
104
105
  {root}
105
106
  {height}
107
+ {allow_reordering}
106
108
  bind:uploading
107
109
  max_file_size={gradio.max_file_size}
108
110
  on:change={({ detail }) => {
package/dist/Index.svelte CHANGED
@@ -28,6 +28,7 @@ export let gradio;
28
28
  export let file_count;
29
29
  export let file_types = ["file"];
30
30
  export let input_ready;
31
+ export let allow_reordering = false;
31
32
  let uploading = false;
32
33
  $:
33
34
  input_ready = !uploading;
@@ -85,6 +86,7 @@ let pending_upload = false;
85
86
  selectable={_selectable}
86
87
  {root}
87
88
  {height}
89
+ {allow_reordering}
88
90
  bind:uploading
89
91
  max_file_size={gradio.max_file_size}
90
92
  on:change={({ detail }) => {
@@ -35,6 +35,7 @@ declare const __propDef: {
35
35
  file_count: "single" | "multiple" | "directory";
36
36
  file_types?: string[] | undefined;
37
37
  input_ready: boolean;
38
+ allow_reordering?: boolean | undefined;
38
39
  };
39
40
  events: {
40
41
  [evt: string]: CustomEvent<any>;
@@ -117,4 +118,7 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
117
118
  get input_ready(): boolean;
118
119
  /**accessor*/
119
120
  set input_ready(_: boolean);
121
+ get allow_reordering(): boolean | undefined;
122
+ /**accessor*/
123
+ set allow_reordering(_: boolean | undefined);
120
124
  }
@@ -6,6 +6,51 @@ export let value;
6
6
  export let selectable = false;
7
7
  export let height = void 0;
8
8
  export let i18n;
9
+ export let allow_reordering = false;
10
+ let dragging_index = null;
11
+ let drop_target_index = null;
12
+ function handle_drag_start(event, index) {
13
+ dragging_index = index;
14
+ if (event.dataTransfer) {
15
+ event.dataTransfer.effectAllowed = "move";
16
+ event.dataTransfer.setData("text/plain", index.toString());
17
+ }
18
+ }
19
+ function handle_drag_over(event, index) {
20
+ event.preventDefault();
21
+ if (index === normalized_files.length - 1) {
22
+ const rect = event.currentTarget.getBoundingClientRect();
23
+ const midY = rect.top + rect.height / 2;
24
+ drop_target_index = event.clientY > midY ? normalized_files.length : index;
25
+ } else {
26
+ drop_target_index = index;
27
+ }
28
+ if (event.dataTransfer) {
29
+ event.dataTransfer.dropEffect = "move";
30
+ }
31
+ }
32
+ function handle_drag_end(event) {
33
+ if (!event.dataTransfer?.dropEffect || event.dataTransfer.dropEffect === "none") {
34
+ dragging_index = null;
35
+ drop_target_index = null;
36
+ }
37
+ }
38
+ function handle_drop(event, index) {
39
+ event.preventDefault();
40
+ if (dragging_index === null || dragging_index === index)
41
+ return;
42
+ const files = Array.isArray(value) ? [...value] : [value];
43
+ const [removed] = files.splice(dragging_index, 1);
44
+ files.splice(
45
+ drop_target_index === normalized_files.length ? normalized_files.length : index,
46
+ 0,
47
+ removed
48
+ );
49
+ const new_value = Array.isArray(value) ? files : files[0];
50
+ dispatch("change", new_value);
51
+ dragging_index = null;
52
+ drop_target_index = null;
53
+ }
9
54
  function split_filename(filename) {
10
55
  const last_dot = filename.lastIndexOf(".");
11
56
  if (last_dot === -1) {
@@ -45,19 +90,42 @@ const is_browser = typeof window !== "undefined";
45
90
 
46
91
  <div
47
92
  class="file-preview-holder"
48
- style="max-height: {typeof height === undefined ? 'auto' : height + 'px'};"
93
+ style:max-height={height
94
+ ? typeof height === "number"
95
+ ? height + "px"
96
+ : height
97
+ : "auto"}
49
98
  >
50
99
  <table class="file-preview">
51
100
  <tbody>
52
- {#each normalized_files as file, i (file)}
101
+ {#each normalized_files as file, i (file.url)}
53
102
  <tr
54
103
  class="file"
55
104
  class:selectable
105
+ class:dragging={dragging_index === i}
106
+ class:drop-target={drop_target_index === i ||
107
+ (i === normalized_files.length - 1 &&
108
+ drop_target_index === normalized_files.length)}
109
+ data-drop-target={drop_target_index === normalized_files.length &&
110
+ i === normalized_files.length - 1
111
+ ? "after"
112
+ : drop_target_index === i + 1
113
+ ? "after"
114
+ : "before"}
115
+ draggable={allow_reordering && normalized_files.length > 1}
56
116
  on:click={(event) => {
57
117
  handle_row_click(event, i);
58
118
  }}
119
+ on:dragstart={(event) => handle_drag_start(event, i)}
120
+ on:dragenter|preventDefault
121
+ on:dragover={(event) => handle_drag_over(event, i)}
122
+ on:drop={(event) => handle_drop(event, i)}
123
+ on:dragend={handle_drag_end}
59
124
  >
60
125
  <td class="filename" aria-label={file.orig_name}>
126
+ {#if allow_reordering && normalized_files.length > 1}
127
+ <span class="drag-handle">⋮⋮</span>
128
+ {/if}
61
129
  <span class="stem">{file.filename_stem}</span>
62
130
  <span class="ext">{file.filename_ext}</span>
63
131
  </td>
@@ -183,4 +251,30 @@ const is_browser = typeof window !== "undefined";
183
251
  tbody > tr:nth-child(odd) {
184
252
  background: var(--table-odd-background-fill);
185
253
  }
254
+
255
+ .drag-handle {
256
+ cursor: grab;
257
+ color: var(--body-text-color-subdued);
258
+ padding-right: var(--size-2);
259
+ user-select: none;
260
+ }
261
+
262
+ .dragging {
263
+ opacity: 0.5;
264
+ cursor: grabbing;
265
+ }
266
+
267
+ .drop-target {
268
+ border-top: 2px solid var(--color-accent);
269
+ }
270
+
271
+ tr:last-child.drop-target[data-drop-target="before"] {
272
+ border-top: 2px solid var(--color-accent);
273
+ border-bottom: none;
274
+ }
275
+
276
+ tr:last-child.drop-target[data-drop-target="after"] {
277
+ border-top: none;
278
+ border-bottom: 2px solid var(--color-accent);
279
+ }
186
280
  </style>
@@ -5,10 +5,12 @@ declare const __propDef: {
5
5
  props: {
6
6
  value: FileData | FileData[];
7
7
  selectable?: boolean | undefined;
8
- height?: number | undefined;
8
+ height?: number | string | undefined;
9
9
  i18n: I18nFormatter;
10
+ allow_reordering?: boolean | undefined;
10
11
  };
11
12
  events: {
13
+ dragenter: DragEvent;
12
14
  select: CustomEvent<SelectData>;
13
15
  change: CustomEvent<any>;
14
16
  delete: CustomEvent<FileData>;
@@ -16,6 +16,7 @@ export let max_file_size = null;
16
16
  export let upload;
17
17
  export let stream_handler;
18
18
  export let uploading = false;
19
+ export let allow_reordering = false;
19
20
  async function handle_upload({
20
21
  detail
21
22
  }) {
@@ -81,6 +82,7 @@ $:
81
82
  {height}
82
83
  on:change
83
84
  on:delete
85
+ {allow_reordering}
84
86
  />
85
87
  {:else}
86
88
  <Upload
@@ -94,6 +96,7 @@ $:
94
96
  on:error
95
97
  {stream_handler}
96
98
  {upload}
99
+ {height}
97
100
  >
98
101
  <slot />
99
102
  </Upload>
@@ -16,6 +16,7 @@ declare const __propDef: {
16
16
  upload: Client["upload"];
17
17
  stream_handler: Client["stream"];
18
18
  uploading?: boolean | undefined;
19
+ allow_reordering?: boolean | undefined;
19
20
  };
20
21
  events: {
21
22
  error: CustomEvent<any>;
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@gradio/file",
3
- "version": "0.11.3",
3
+ "version": "0.12.1",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
7
7
  "license": "ISC",
8
8
  "private": false,
9
9
  "dependencies": {
10
- "@gradio/atoms": "^0.12.0",
11
- "@gradio/client": "^1.8.0",
12
- "@gradio/statustracker": "^0.9.7",
13
- "@gradio/icons": "^0.8.1",
14
- "@gradio/upload": "^0.14.3",
15
- "@gradio/wasm": "^0.15.0",
16
- "@gradio/utils": "^0.9.0"
10
+ "@gradio/atoms": "^0.13.1",
11
+ "@gradio/client": "^1.10.0",
12
+ "@gradio/icons": "^0.10.0",
13
+ "@gradio/statustracker": "^0.10.1",
14
+ "@gradio/upload": "^0.14.5",
15
+ "@gradio/utils": "^0.10.0",
16
+ "@gradio/wasm": "^0.16.0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@gradio/preview": "^0.13.0"
@@ -13,8 +13,66 @@
13
13
  }>();
14
14
  export let value: FileData | FileData[];
15
15
  export let selectable = false;
16
- export let height: number | undefined = undefined;
16
+ export let height: number | string | undefined = undefined;
17
17
  export let i18n: I18nFormatter;
18
+ export let allow_reordering = false;
19
+
20
+ let dragging_index: number | null = null;
21
+ let drop_target_index: number | null = null;
22
+
23
+ function handle_drag_start(event: DragEvent, index: number): void {
24
+ dragging_index = index;
25
+ if (event.dataTransfer) {
26
+ event.dataTransfer.effectAllowed = "move";
27
+ event.dataTransfer.setData("text/plain", index.toString());
28
+ }
29
+ }
30
+
31
+ function handle_drag_over(event: DragEvent, index: number): void {
32
+ event.preventDefault();
33
+ if (index === normalized_files.length - 1) {
34
+ const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
35
+ const midY = rect.top + rect.height / 2;
36
+ drop_target_index =
37
+ event.clientY > midY ? normalized_files.length : index;
38
+ } else {
39
+ drop_target_index = index;
40
+ }
41
+ if (event.dataTransfer) {
42
+ event.dataTransfer.dropEffect = "move";
43
+ }
44
+ }
45
+
46
+ function handle_drag_end(event: DragEvent): void {
47
+ if (
48
+ !event.dataTransfer?.dropEffect ||
49
+ event.dataTransfer.dropEffect === "none"
50
+ ) {
51
+ dragging_index = null;
52
+ drop_target_index = null;
53
+ }
54
+ }
55
+
56
+ function handle_drop(event: DragEvent, index: number): void {
57
+ event.preventDefault();
58
+ if (dragging_index === null || dragging_index === index) return;
59
+
60
+ const files = Array.isArray(value) ? [...value] : [value];
61
+ const [removed] = files.splice(dragging_index, 1);
62
+ files.splice(
63
+ drop_target_index === normalized_files.length
64
+ ? normalized_files.length
65
+ : index,
66
+ 0,
67
+ removed
68
+ );
69
+
70
+ const new_value = Array.isArray(value) ? files : files[0];
71
+ dispatch("change", new_value);
72
+
73
+ dragging_index = null;
74
+ drop_target_index = null;
75
+ }
18
76
 
19
77
  function split_filename(filename: string): [string, string] {
20
78
  const last_dot = filename.lastIndexOf(".");
@@ -66,19 +124,42 @@
66
124
 
67
125
  <div
68
126
  class="file-preview-holder"
69
- style="max-height: {typeof height === undefined ? 'auto' : height + 'px'};"
127
+ style:max-height={height
128
+ ? typeof height === "number"
129
+ ? height + "px"
130
+ : height
131
+ : "auto"}
70
132
  >
71
133
  <table class="file-preview">
72
134
  <tbody>
73
- {#each normalized_files as file, i (file)}
135
+ {#each normalized_files as file, i (file.url)}
74
136
  <tr
75
137
  class="file"
76
138
  class:selectable
139
+ class:dragging={dragging_index === i}
140
+ class:drop-target={drop_target_index === i ||
141
+ (i === normalized_files.length - 1 &&
142
+ drop_target_index === normalized_files.length)}
143
+ data-drop-target={drop_target_index === normalized_files.length &&
144
+ i === normalized_files.length - 1
145
+ ? "after"
146
+ : drop_target_index === i + 1
147
+ ? "after"
148
+ : "before"}
149
+ draggable={allow_reordering && normalized_files.length > 1}
77
150
  on:click={(event) => {
78
151
  handle_row_click(event, i);
79
152
  }}
153
+ on:dragstart={(event) => handle_drag_start(event, i)}
154
+ on:dragenter|preventDefault
155
+ on:dragover={(event) => handle_drag_over(event, i)}
156
+ on:drop={(event) => handle_drop(event, i)}
157
+ on:dragend={handle_drag_end}
80
158
  >
81
159
  <td class="filename" aria-label={file.orig_name}>
160
+ {#if allow_reordering && normalized_files.length > 1}
161
+ <span class="drag-handle">⋮⋮</span>
162
+ {/if}
82
163
  <span class="stem">{file.filename_stem}</span>
83
164
  <span class="ext">{file.filename_ext}</span>
84
165
  </td>
@@ -204,4 +285,30 @@
204
285
  tbody > tr:nth-child(odd) {
205
286
  background: var(--table-odd-background-fill);
206
287
  }
288
+
289
+ .drag-handle {
290
+ cursor: grab;
291
+ color: var(--body-text-color-subdued);
292
+ padding-right: var(--size-2);
293
+ user-select: none;
294
+ }
295
+
296
+ .dragging {
297
+ opacity: 0.5;
298
+ cursor: grabbing;
299
+ }
300
+
301
+ .drop-target {
302
+ border-top: 2px solid var(--color-accent);
303
+ }
304
+
305
+ tr:last-child.drop-target[data-drop-target="before"] {
306
+ border-top: 2px solid var(--color-accent);
307
+ border-bottom: none;
308
+ }
309
+
310
+ tr:last-child.drop-target[data-drop-target="after"] {
311
+ border-top: none;
312
+ border-bottom: 2px solid var(--color-accent);
313
+ }
207
314
  </style>
@@ -22,6 +22,7 @@
22
22
  export let upload: Client["upload"];
23
23
  export let stream_handler: Client["stream"];
24
24
  export let uploading = false;
25
+ export let allow_reordering = false;
25
26
 
26
27
  async function handle_upload({
27
28
  detail
@@ -97,6 +98,7 @@
97
98
  {height}
98
99
  on:change
99
100
  on:delete
101
+ {allow_reordering}
100
102
  />
101
103
  {:else}
102
104
  <Upload
@@ -110,6 +112,7 @@
110
112
  on:error
111
113
  {stream_handler}
112
114
  {upload}
115
+ {height}
113
116
  >
114
117
  <slot />
115
118
  </Upload>