@gradio/dataframe 0.18.0 → 0.18.2

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,27 @@
1
1
  # @gradio/dataframe
2
2
 
3
+ ## 0.18.2
4
+
5
+ ### Fixes
6
+
7
+ - [#11496](https://github.com/gradio-app/gradio/pull/11496) [`ee0da48`](https://github.com/gradio-app/gradio/commit/ee0da481446da5ffd79151a457cd3847db645bfb) - Prevent deletion of values in static columns. Thanks @hannahblair!
8
+
9
+ ## 0.18.1
10
+
11
+ ### Fixes
12
+
13
+ - [#11436](https://github.com/gradio-app/gradio/pull/11436) [`f49b8da`](https://github.com/gradio-app/gradio/commit/f49b8da1c062dbd81acb8e7fdcdc9a09a272d02f) - Multi-Line DataFrame Editing. Thanks @deckar01!
14
+
15
+ ### Dependency updates
16
+
17
+ - @gradio/atoms@0.16.3
18
+ - @gradio/statustracker@0.10.14
19
+ - @gradio/upload@0.16.10
20
+ - @gradio/client@1.15.5
21
+ - @gradio/markdown-code@0.5.0
22
+ - @gradio/button@0.5.6
23
+ - @gradio/checkbox@0.4.25
24
+
3
25
  ## 0.18.0
4
26
 
5
27
  ### Features
@@ -23,7 +23,6 @@ export let on_select_column = null;
23
23
  export let on_select_row = null;
24
24
  export let el;
25
25
  const dispatch = createEventDispatcher();
26
- let is_expanded = false;
27
26
  function truncate_text(text, max_length = null, is_image = false) {
28
27
  if (is_image)
29
28
  return String(text);
@@ -35,7 +34,7 @@ function truncate_text(text, max_length = null, is_image = false) {
35
34
  return str.slice(0, max_length) + "...";
36
35
  }
37
36
  $:
38
- should_truncate = !edit && !is_expanded && max_chars !== null && max_chars > 0;
37
+ should_truncate = !edit && max_chars !== null && max_chars > 0;
39
38
  $:
40
39
  display_content = editable ? value : display_value !== null ? display_value : value;
41
40
  $:
@@ -53,18 +52,8 @@ function handle_blur(event) {
53
52
  });
54
53
  }
55
54
  function handle_keydown(event) {
56
- if (event.key === "Enter") {
57
- if (!header) {
58
- is_expanded = !is_expanded;
59
- }
60
- }
61
55
  dispatch("keydown", event);
62
56
  }
63
- function handle_click() {
64
- if (!edit && !header) {
65
- is_expanded = !is_expanded;
66
- }
67
- }
68
57
  function handle_bool_change(new_value) {
69
58
  value = new_value.toString();
70
59
  dispatch("blur", {
@@ -81,10 +70,9 @@ function handle_bool_change(new_value) {
81
70
  </script>
82
71
 
83
72
  {#if edit && datatype !== "bool"}
84
- <input
73
+ <textarea
85
74
  readonly={is_static}
86
75
  aria-readonly={is_static}
87
- role="textbox"
88
76
  aria-label={is_static ? "Cell is read-only" : "Edit cell"}
89
77
  bind:this={el}
90
78
  bind:value
@@ -92,7 +80,6 @@ function handle_bool_change(new_value) {
92
80
  tabindex="-1"
93
81
  on:blur={handle_blur}
94
82
  on:mousedown|stopPropagation
95
- on:mouseup|stopPropagation
96
83
  on:click|stopPropagation
97
84
  use:use_focus
98
85
  on:keydown={handle_keydown}
@@ -108,18 +95,17 @@ function handle_bool_change(new_value) {
108
95
  {:else}
109
96
  <span
110
97
  class:dragging={is_dragging}
111
- on:click={handle_click}
112
98
  on:keydown={handle_keydown}
113
99
  tabindex="0"
114
100
  role="button"
115
101
  class:edit
116
- class:expanded={is_expanded}
102
+ class:expanded={edit}
117
103
  class:multiline={header}
118
104
  on:focus|preventDefault
119
105
  style={styling}
120
106
  data-editable={editable}
121
107
  data-max-chars={max_chars}
122
- data-expanded={is_expanded}
108
+ data-expanded={edit}
123
109
  placeholder=" "
124
110
  class:text={datatype === "str"}
125
111
  class:wrap={wrap_text}
@@ -167,7 +153,7 @@ function handle_bool_change(new_value) {
167
153
  cursor: crosshair !important;
168
154
  }
169
155
 
170
- input {
156
+ textarea {
171
157
  position: absolute;
172
158
  flex: 1 1 0%;
173
159
  transform: translateX(-0.1px);
@@ -176,6 +162,16 @@ function handle_bool_change(new_value) {
176
162
  background: transparent;
177
163
  cursor: text;
178
164
  width: calc(100% - var(--size-2));
165
+ resize: none;
166
+ height: 100%;
167
+ padding-left: 0;
168
+ font-size: inherit;
169
+ font-weight: inherit;
170
+ line-height: var(--line-lg);
171
+ }
172
+
173
+ textarea:focus {
174
+ outline: none;
179
175
  }
180
176
 
181
177
  span {
@@ -227,7 +223,7 @@ function handle_bool_change(new_value) {
227
223
  object-fit: contain;
228
224
  }
229
225
 
230
- input:read-only {
226
+ textarea:read-only {
231
227
  cursor: not-allowed;
232
228
  }
233
229
 
@@ -25,11 +25,10 @@ declare const __propDef: {
25
25
  coords: [number, number];
26
26
  on_select_column?: (((col: number) => void) | null) | undefined;
27
27
  on_select_row?: (((row: number) => void) | null) | undefined;
28
- el: HTMLInputElement | null;
28
+ el: HTMLTextAreaElement | null;
29
29
  };
30
30
  events: {
31
31
  mousedown: MouseEvent;
32
- mouseup: MouseEvent;
33
32
  click: MouseEvent;
34
33
  focus: FocusEvent;
35
34
  blur: CustomEvent<{
@@ -73,7 +73,8 @@ const df_ctx = create_dataframe_context({
73
73
  wrap,
74
74
  max_height,
75
75
  column_widths,
76
- max_chars
76
+ max_chars,
77
+ static_columns
77
78
  });
78
79
  const { state: df_state, actions: df_actions } = df_ctx;
79
80
  $:
@@ -40,7 +40,7 @@ declare const __propDef: {
40
40
  components?: Record<string, any> | undefined;
41
41
  el: {
42
42
  cell: HTMLTableCellElement | null;
43
- input: HTMLInputElement | null;
43
+ input: HTMLTextAreaElement | null;
44
44
  };
45
45
  handle_select_column: (col: number) => void;
46
46
  handle_select_row: (row: number) => void;
@@ -33,7 +33,7 @@ declare const __propDef: {
33
33
  max_chars: number | undefined;
34
34
  editable: boolean;
35
35
  i18n: I18nFormatter;
36
- el: HTMLInputElement | null;
36
+ el: HTMLTextAreaElement | null;
37
37
  is_static: boolean;
38
38
  col_count: [number, "fixed" | "dynamic"];
39
39
  };
@@ -18,6 +18,7 @@ interface DataFrameState {
18
18
  max_height: number;
19
19
  column_widths: string[];
20
20
  max_chars?: number;
21
+ static_columns?: (string | number)[];
21
22
  };
22
23
  current_search_query: string | null;
23
24
  sort_state: {
@@ -165,7 +166,7 @@ export interface DataFrameContext {
165
166
  styling?: string[][] | null;
166
167
  els?: Record<string, {
167
168
  cell: HTMLTableCellElement | null;
168
- input: HTMLInputElement | null;
169
+ input: HTMLTextAreaElement | null;
169
170
  }>;
170
171
  parent_element?: HTMLElement;
171
172
  get_data_at?: (row: number, col: number) => string | number;
@@ -13,6 +13,6 @@ export type TableData = TableCell[][];
13
13
  export type CountConfig = [number, "fixed" | "dynamic"];
14
14
  export type ElementRefs = Record<string, {
15
15
  cell: null | HTMLTableCellElement;
16
- input: null | HTMLInputElement;
16
+ input: null | HTMLTextAreaElement;
17
17
  }>;
18
18
  export type DataBinding = Record<string, TableCell>;
@@ -1,11 +1,11 @@
1
1
  import type { Headers, HeadersWithIDs } from "../utils";
2
2
  export declare function make_headers(_head: Headers, col_count: [number, "fixed" | "dynamic"], els: Record<string, {
3
3
  cell: null | HTMLTableCellElement;
4
- input: null | HTMLInputElement;
4
+ input: null | HTMLTextAreaElement;
5
5
  }>, make_id: () => string): HeadersWithIDs;
6
6
  export declare function process_data(values: (string | number)[][], els: Record<string, {
7
7
  cell: null | HTMLTableCellElement;
8
- input: null | HTMLInputElement;
8
+ input: null | HTMLTextAreaElement;
9
9
  }>, data_binding: Record<string, any>, make_id: () => string, display_value?: string[][] | null): {
10
10
  id: string;
11
11
  value: string | number;
@@ -59,6 +59,7 @@ function handle_header_navigation(event, ctx) {
59
59
  }
60
60
  return false;
61
61
  }
62
+ // eslint-disable-next-line complexity
62
63
  function handle_delete_operation(event, ctx) {
63
64
  if (!ctx.data || !ctx.headers || !ctx.els || !ctx.dispatch)
64
65
  return false;
@@ -69,6 +70,10 @@ function handle_delete_operation(event, ctx) {
69
70
  return false;
70
71
  const editing = state.ui_state.editing;
71
72
  const selected_cells = state.ui_state.selected_cells;
73
+ const static_columns = state.config.static_columns || [];
74
+ if (selected_cells.some(([_, col]) => static_columns.includes(col))) {
75
+ return false;
76
+ }
72
77
  if (editing) {
73
78
  const [row, col] = editing;
74
79
  const input_el = ctx.els[ctx.data[row][col].id]?.input;
@@ -129,8 +134,10 @@ async function handle_enter_key(event, ctx, i, j) {
129
134
  const state = get(ctx.state);
130
135
  if (!state.config.editable)
131
136
  return false;
132
- event.preventDefault();
133
137
  const editing = state.ui_state.editing;
138
+ if (editing && event.shiftKey)
139
+ return false;
140
+ event.preventDefault();
134
141
  if (editing && dequal(editing, [i, j])) {
135
142
  const cell_id = ctx.data[i][j].id;
136
143
  const input_el = ctx.els[cell_id]?.input;
@@ -138,6 +145,8 @@ async function handle_enter_key(event, ctx, i, j) {
138
145
  await save_cell_value(input_el.value, ctx, i, j);
139
146
  }
140
147
  ctx.actions.set_editing(false);
148
+ await tick();
149
+ ctx.parent_element?.focus();
141
150
  }
142
151
  else {
143
152
  ctx.actions.set_editing([i, j]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/dataframe",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -17,15 +17,15 @@
17
17
  "dompurify": "^3.0.3",
18
18
  "katex": "^0.16.7",
19
19
  "marked": "^12.0.0",
20
- "@gradio/atoms": "^0.16.2",
21
- "@gradio/checkbox": "^0.4.24",
22
- "@gradio/client": "^1.15.4",
23
- "@gradio/button": "^0.5.5",
24
- "@gradio/statustracker": "^0.10.13",
20
+ "@gradio/atoms": "^0.16.3",
21
+ "@gradio/checkbox": "^0.4.25",
22
+ "@gradio/button": "^0.5.6",
23
+ "@gradio/client": "^1.15.5",
25
24
  "@gradio/icons": "^0.12.0",
26
- "@gradio/upload": "^0.16.9",
25
+ "@gradio/statustracker": "^0.10.14",
26
+ "@gradio/markdown-code": "^0.5.0",
27
27
  "@gradio/utils": "^0.10.2",
28
- "@gradio/markdown-code": "^0.4.4"
28
+ "@gradio/upload": "^0.16.10"
29
29
  },
30
30
  "exports": {
31
31
  ".": {
@@ -44,7 +44,7 @@
44
44
  "svelte": "^4.0.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@gradio/preview": "^0.13.2"
47
+ "@gradio/preview": "^0.14.0"
48
48
  },
49
49
  "repository": {
50
50
  "type": "git",
@@ -36,15 +36,13 @@
36
36
  export let coords: [number, number];
37
37
  export let on_select_column: ((col: number) => void) | null = null;
38
38
  export let on_select_row: ((row: number) => void) | null = null;
39
- export let el: HTMLInputElement | null;
39
+ export let el: HTMLTextAreaElement | null;
40
40
 
41
41
  const dispatch = createEventDispatcher<{
42
42
  blur: { blur_event: FocusEvent; coords: [number, number] };
43
43
  keydown: KeyboardEvent;
44
44
  }>();
45
45
 
46
- let is_expanded = false;
47
-
48
46
  function truncate_text(
49
47
  text: string | number,
50
48
  max_length: number | null = null,
@@ -57,8 +55,7 @@
57
55
  return str.slice(0, max_length) + "...";
58
56
  }
59
57
 
60
- $: should_truncate =
61
- !edit && !is_expanded && max_chars !== null && max_chars > 0;
58
+ $: should_truncate = !edit && max_chars !== null && max_chars > 0;
62
59
 
63
60
  $: display_content = editable
64
61
  ? value
@@ -70,7 +67,7 @@
70
67
  ? truncate_text(display_content, max_chars, datatype === "image")
71
68
  : display_content;
72
69
 
73
- function use_focus(node: HTMLInputElement): any {
70
+ function use_focus(node: HTMLTextAreaElement): any {
74
71
  requestAnimationFrame(() => {
75
72
  node.focus();
76
73
  });
@@ -86,20 +83,9 @@
86
83
  }
87
84
 
88
85
  function handle_keydown(event: KeyboardEvent): void {
89
- if (event.key === "Enter") {
90
- if (!header) {
91
- is_expanded = !is_expanded;
92
- }
93
- }
94
86
  dispatch("keydown", event);
95
87
  }
96
88
 
97
- function handle_click(): void {
98
- if (!edit && !header) {
99
- is_expanded = !is_expanded;
100
- }
101
- }
102
-
103
89
  function handle_bool_change(new_value: boolean): void {
104
90
  value = new_value.toString();
105
91
  dispatch("blur", {
@@ -116,10 +102,9 @@
116
102
  </script>
117
103
 
118
104
  {#if edit && datatype !== "bool"}
119
- <input
105
+ <textarea
120
106
  readonly={is_static}
121
107
  aria-readonly={is_static}
122
- role="textbox"
123
108
  aria-label={is_static ? "Cell is read-only" : "Edit cell"}
124
109
  bind:this={el}
125
110
  bind:value
@@ -127,7 +112,6 @@
127
112
  tabindex="-1"
128
113
  on:blur={handle_blur}
129
114
  on:mousedown|stopPropagation
130
- on:mouseup|stopPropagation
131
115
  on:click|stopPropagation
132
116
  use:use_focus
133
117
  on:keydown={handle_keydown}
@@ -143,18 +127,17 @@
143
127
  {:else}
144
128
  <span
145
129
  class:dragging={is_dragging}
146
- on:click={handle_click}
147
130
  on:keydown={handle_keydown}
148
131
  tabindex="0"
149
132
  role="button"
150
133
  class:edit
151
- class:expanded={is_expanded}
134
+ class:expanded={edit}
152
135
  class:multiline={header}
153
136
  on:focus|preventDefault
154
137
  style={styling}
155
138
  data-editable={editable}
156
139
  data-max-chars={max_chars}
157
- data-expanded={is_expanded}
140
+ data-expanded={edit}
158
141
  placeholder=" "
159
142
  class:text={datatype === "str"}
160
143
  class:wrap={wrap_text}
@@ -202,7 +185,7 @@
202
185
  cursor: crosshair !important;
203
186
  }
204
187
 
205
- input {
188
+ textarea {
206
189
  position: absolute;
207
190
  flex: 1 1 0%;
208
191
  transform: translateX(-0.1px);
@@ -211,6 +194,16 @@
211
194
  background: transparent;
212
195
  cursor: text;
213
196
  width: calc(100% - var(--size-2));
197
+ resize: none;
198
+ height: 100%;
199
+ padding-left: 0;
200
+ font-size: inherit;
201
+ font-weight: inherit;
202
+ line-height: var(--line-lg);
203
+ }
204
+
205
+ textarea:focus {
206
+ outline: none;
214
207
  }
215
208
 
216
209
  span {
@@ -262,7 +255,7 @@
262
255
  object-fit: contain;
263
256
  }
264
257
 
265
- input:read-only {
258
+ textarea:read-only {
266
259
  cursor: not-allowed;
267
260
  }
268
261
 
@@ -92,7 +92,8 @@
92
92
  wrap,
93
93
  max_height,
94
94
  column_widths,
95
- max_chars
95
+ max_chars,
96
+ static_columns
96
97
  });
97
98
 
98
99
  const { state: df_state, actions: df_actions } = df_ctx;
@@ -165,7 +166,7 @@
165
166
 
166
167
  let els: Record<
167
168
  string,
168
- { cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
169
+ { cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
169
170
  > = {};
170
171
  let data_binding: Record<string, (typeof data)[0][0]> = {};
171
172
  let _headers = make_headers(headers, col_count, els, make_id);
@@ -59,7 +59,7 @@
59
59
  export let components: Record<string, any> = {};
60
60
  export let el: {
61
61
  cell: HTMLTableCellElement | null;
62
- input: HTMLInputElement | null;
62
+ input: HTMLTextAreaElement | null;
63
63
  };
64
64
  export let handle_select_column: (col: number) => void;
65
65
  export let handle_select_row: (row: number) => void;
@@ -36,7 +36,7 @@
36
36
  export let max_chars: number | undefined;
37
37
  export let editable: boolean;
38
38
  export let i18n: I18nFormatter;
39
- export let el: HTMLInputElement | null;
39
+ export let el: HTMLTextAreaElement | null;
40
40
  export let is_static: boolean;
41
41
  export let col_count: [number, "fixed" | "dynamic"];
42
42
 
@@ -21,7 +21,7 @@
21
21
  let input_value = "";
22
22
 
23
23
  function handle_search_input(e: Event): void {
24
- const target = e.target as HTMLInputElement;
24
+ const target = e.target as HTMLTextAreaElement;
25
25
  input_value = target.value;
26
26
  const new_query = input_value || null;
27
27
  if (current_search_query !== new_query) {
@@ -30,6 +30,7 @@ interface DataFrameState {
30
30
  max_height: number;
31
31
  column_widths: string[];
32
32
  max_chars?: number;
33
+ static_columns?: (string | number)[];
33
34
  };
34
35
  current_search_query: string | null;
35
36
  sort_state: {
@@ -179,7 +180,7 @@ export interface DataFrameContext {
179
180
  styling?: string[][] | null;
180
181
  els?: Record<
181
182
  string,
182
- { cell: HTMLTableCellElement | null; input: HTMLInputElement | null }
183
+ { cell: HTMLTableCellElement | null; input: HTMLTextAreaElement | null }
183
184
  >;
184
185
  parent_element?: HTMLElement;
185
186
  get_data_at?: (row: number, col: number) => string | number;
package/shared/types.ts CHANGED
@@ -22,7 +22,7 @@ export type ElementRefs = Record<
22
22
  string,
23
23
  {
24
24
  cell: null | HTMLTableCellElement;
25
- input: null | HTMLInputElement;
25
+ input: null | HTMLTextAreaElement;
26
26
  }
27
27
  >;
28
28
 
@@ -5,7 +5,7 @@ export function make_headers(
5
5
  col_count: [number, "fixed" | "dynamic"],
6
6
  els: Record<
7
7
  string,
8
- { cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
8
+ { cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
9
9
  >,
10
10
  make_id: () => string
11
11
  ): HeadersWithIDs {
@@ -38,7 +38,7 @@ export function process_data(
38
38
  values: (string | number)[][],
39
39
  els: Record<
40
40
  string,
41
- { cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
41
+ { cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
42
42
  >,
43
43
  data_binding: Record<string, any>,
44
44
  make_id: () => string,
@@ -88,6 +88,7 @@ function handle_header_navigation(
88
88
  return false;
89
89
  }
90
90
 
91
+ // eslint-disable-next-line complexity
91
92
  function handle_delete_operation(
92
93
  event: KeyboardEvent,
93
94
  ctx: DataFrameContext
@@ -101,6 +102,11 @@ function handle_delete_operation(
101
102
  const editing = state.ui_state.editing;
102
103
  const selected_cells = state.ui_state.selected_cells;
103
104
 
105
+ const static_columns = state.config.static_columns || [];
106
+ if (selected_cells.some(([_, col]) => static_columns.includes(col))) {
107
+ return false;
108
+ }
109
+
104
110
  if (editing) {
105
111
  const [row, col] = editing;
106
112
  const input_el = ctx.els[ctx.data[row][col].id]?.input;
@@ -180,9 +186,10 @@ async function handle_enter_key(
180
186
  const state = get(ctx.state);
181
187
  if (!state.config.editable) return false;
182
188
 
183
- event.preventDefault();
184
-
185
189
  const editing = state.ui_state.editing;
190
+ if (editing && event.shiftKey) return false;
191
+
192
+ event.preventDefault();
186
193
 
187
194
  if (editing && dequal(editing, [i, j])) {
188
195
  const cell_id = ctx.data[i][j].id;
@@ -191,6 +198,8 @@ async function handle_enter_key(
191
198
  await save_cell_value(input_el.value, ctx, i, j);
192
199
  }
193
200
  ctx.actions.set_editing(false);
201
+ await tick();
202
+ ctx.parent_element?.focus();
194
203
  } else {
195
204
  ctx.actions.set_editing([i, j]);
196
205
  }