@vueye-table/headless 3.0.0-beta.1 → 3.0.0-beta.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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { injectDataGrid, injectDataTable, provideDataGrid, provideDataTable, useDataGrid, useVirtualColumns, useVirtualRows } from "@vueye-table/vue";
1
+ import { injectDataGrid, injectDataTable, provideDataGrid, provideDataTable, useDataGrid, useTableLocale, useVirtualColumns, useVirtualRows } from "@vueye-table/vue";
2
2
  import { Fragment, computed, defineComponent, h, inject, nextTick, onMounted, onScopeDispose, provide, ref, shallowRef, useId, watch } from "vue";
3
3
  import { formatValue, getRowItemKey, gridCommand } from "@vueye-table/core";
4
4
 
@@ -32,6 +32,13 @@ const rowProp = {
32
32
  function flag(value) {
33
33
  return value ? "" : void 0;
34
34
  }
35
+ /**
36
+ * The direction an element is laid out in, read from the nearest `dir` attribute, so keyboard
37
+ * arrows can follow what the reader sees. Without one, left to right.
38
+ */
39
+ function directionOf(element) {
40
+ return element?.closest("[dir]")?.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
41
+ }
35
42
 
36
43
  //#endregion
37
44
  //#region src/controls.ts
@@ -55,10 +62,11 @@ const DataTableSelectAll = defineComponent({
55
62
  slots: Object,
56
63
  props: { label: {
57
64
  type: String,
58
- default: "Select all rows"
65
+ default: void 0
59
66
  } },
60
67
  setup(props, { slots }) {
61
68
  const table = injectDataTable("<DataTableSelectAll>");
69
+ const locale = useTableLocale();
62
70
  return () => {
63
71
  const state = table.allSelection;
64
72
  const toggle = () => table.toggleAll();
@@ -68,7 +76,7 @@ const DataTableSelectAll = defineComponent({
68
76
  }) ?? checkbox({
69
77
  checked: state === "all",
70
78
  indeterminate: state === "some",
71
- label: props.label,
79
+ label: props.label ?? locale().messages.selectAllRows,
72
80
  disabled: table.selectionMode !== "multiple" || table.rows.length === 0,
73
81
  onChange: toggle
74
82
  });
@@ -81,13 +89,19 @@ const DataTableSelectRow = defineComponent({
81
89
  slots: Object,
82
90
  props: {
83
91
  row: rowProp,
92
+ /** Defaults to the locale's "Select row 3", the row's place in the table as shown. */
84
93
  label: {
85
94
  type: String,
86
- default: "Select row"
95
+ default: void 0
87
96
  }
88
97
  },
89
98
  setup(props, { slots }) {
90
99
  const table = injectDataTable("<DataTableSelectRow>");
100
+ const locale = useTableLocale();
101
+ const position = () => {
102
+ const index = table.rows.indexOf(props.row);
103
+ return index < 0 ? props.row.index + 1 : Math.max(1, table.pageStart) + index;
104
+ };
91
105
  return () => {
92
106
  const selected = table.tree ? props.row.selection === "all" : table.isSelected(props.row.key);
93
107
  const indeterminate = table.tree && props.row.selection === "some";
@@ -99,7 +113,7 @@ const DataTableSelectRow = defineComponent({
99
113
  }) ?? checkbox({
100
114
  checked: selected,
101
115
  indeterminate,
102
- label: props.label,
116
+ label: props.label ?? locale().messages.selectRow({ position: locale().count(position()) }),
103
117
  disabled: table.selectionMode === "none",
104
118
  onChange: toggle
105
119
  });
@@ -119,7 +133,7 @@ const DataTableSearch = defineComponent({
119
133
  },
120
134
  label: {
121
135
  type: String,
122
- default: "Search"
136
+ default: void 0
123
137
  },
124
138
  placeholder: {
125
139
  type: String,
@@ -128,13 +142,14 @@ const DataTableSearch = defineComponent({
128
142
  },
129
143
  setup(props) {
130
144
  const table = injectDataTable("<DataTableSearch>");
145
+ const locale = useTableLocale();
131
146
  let timer;
132
147
  onScopeDispose(() => clearTimeout(timer));
133
148
  return () => h("input", {
134
149
  type: "search",
135
150
  value: table.state.search,
136
151
  placeholder: props.placeholder,
137
- "aria-label": props.label,
152
+ "aria-label": props.label ?? locale().messages.search,
138
153
  onInput: (event) => {
139
154
  const { value } = event.target;
140
155
  clearTimeout(timer);
@@ -155,12 +170,14 @@ const DataTablePagination = defineComponent({
155
170
  as: asProp("nav"),
156
171
  label: {
157
172
  type: String,
158
- default: "Pagination"
173
+ default: void 0
159
174
  }
160
175
  },
161
176
  setup(props, { slots }) {
162
177
  const table = injectDataTable("<DataTablePagination>");
178
+ const locale = useTableLocale();
163
179
  return () => {
180
+ const { messages, count } = locale();
164
181
  const slotProps = {
165
182
  page: table.page,
166
183
  pageCount: table.pageCount,
@@ -179,14 +196,14 @@ const DataTablePagination = defineComponent({
179
196
  "data-current": flag(current),
180
197
  onClick
181
198
  }, content);
182
- return h(props.as, { "aria-label": props.label }, slots.default?.(slotProps) ?? [
183
- button("Previous page", "‹", !table.canPreviousPage, table.previousPage),
199
+ return h(props.as, { "aria-label": props.label ?? messages.pagination }, slots.default?.(slotProps) ?? [
200
+ button(messages.previousPage, "‹", !table.canPreviousPage, table.previousPage),
184
201
  ...table.pageItems.map((item) => item.type === "gap" ? h("span", {
185
202
  key: item.key,
186
203
  "aria-hidden": "true",
187
204
  "data-gap": ""
188
- }, "…") : button(`Page ${item.page}`, String(item.page), false, () => table.goToPage(item.page), item.current)),
189
- button("Next page", "›", !table.canNextPage, table.nextPage)
205
+ }, "…") : button(messages.page(count(item.page)), count(item.page).text, false, () => table.goToPage(item.page), item.current)),
206
+ button(messages.nextPage, "›", !table.canNextPage, table.nextPage)
190
207
  ]);
191
208
  };
192
209
  }
@@ -206,22 +223,23 @@ const DataTablePageSize = defineComponent({
206
223
  },
207
224
  label: {
208
225
  type: String,
209
- default: "Rows per page"
226
+ default: void 0
210
227
  }
211
228
  },
212
229
  setup(props) {
213
230
  const table = injectDataTable("<DataTablePageSize>");
231
+ const locale = useTableLocale();
214
232
  return () => {
215
233
  const sizes = props.options.includes(table.pageSize) ? props.options : [...props.options, table.pageSize].toSorted((left, right) => left - right);
216
234
  return h("select", {
217
235
  value: table.pageSize,
218
- "aria-label": props.label,
236
+ "aria-label": props.label ?? locale().messages.rowsPerPage,
219
237
  onChange: (event) => table.setPageSize(Number(event.target.value))
220
238
  }, sizes.map((size) => h("option", {
221
239
  key: size,
222
240
  value: size,
223
241
  selected: size === table.pageSize
224
- }, String(size))));
242
+ }, locale().count(size).text)));
225
243
  };
226
244
  }
227
245
  });
@@ -233,11 +251,12 @@ const DataTableColumnVisibility = defineComponent({
233
251
  as: asProp("fieldset"),
234
252
  legend: {
235
253
  type: String,
236
- default: "Columns"
254
+ default: void 0
237
255
  }
238
256
  },
239
257
  setup(props, { slots }) {
240
258
  const table = injectDataTable("<DataTableColumnVisibility>");
259
+ const locale = useTableLocale();
241
260
  return () => {
242
261
  const hidden = new Set(table.state.hiddenColumns);
243
262
  const columns = table.allColumns.map((column) => ({
@@ -247,7 +266,7 @@ const DataTableColumnVisibility = defineComponent({
247
266
  return h(props.as, slots.default?.({
248
267
  columns,
249
268
  toggle: table.toggleColumn
250
- }) ?? [props.as === "fieldset" ? h("legend", props.legend) : null, ...columns.map(({ column, visible }) => h("label", {
269
+ }) ?? [props.as === "fieldset" ? h("legend", props.legend ?? locale().messages.columns) : null, ...columns.map(({ column, visible }) => h("label", {
251
270
  key: column.id,
252
271
  "data-column": column.id
253
272
  }, [h("input", {
@@ -265,7 +284,9 @@ const DataTableStatus = defineComponent({
265
284
  props: { as: asProp("p") },
266
285
  setup(props, { slots }) {
267
286
  const table = injectDataTable("<DataTableStatus>");
287
+ const locale = useTableLocale();
268
288
  return () => {
289
+ const { messages, count } = locale();
269
290
  const slotProps = {
270
291
  start: table.pageStart,
271
292
  end: table.pageEnd,
@@ -281,12 +302,23 @@ const DataTableStatus = defineComponent({
281
302
  loadingChildrenCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "loading").length : 0,
282
303
  childErrorCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "error").length : 0
283
304
  };
284
- const count = `Loaded ${table.loadedRowCount.toLocaleString()} ${table.loadedRowCount === 1 ? "row" : "rows"}`;
285
- const text = table.loadingMode ? table.loadState === "error" ? `${count}. Could not load rows. Retry loading.` : table.loadState === "aborted" ? `${count}. Loading stopped.` : table.loadState === "loading" || table.loadState === "streaming" ? `${count}…` : `${count}${table.loadState === "done" ? ". All rows loaded." : "."}` : table.rowCount === 0 ? "No rows" : `${table.pageStart}–${table.pageEnd} of ${table.rowCount} rows` + (table.selectedCount > 0 ? `, ${table.selectedCount} selected` : "");
305
+ const text = table.loadingMode ? messages.loadedStatus({
306
+ loaded: count(table.loadedRowCount),
307
+ state: table.loadState
308
+ }) : table.rowCount === 0 ? messages.noRows : messages.rangeStatus({
309
+ start: count(table.pageStart),
310
+ end: count(table.pageEnd),
311
+ total: count(table.rowCount),
312
+ selected: count(table.selectedCount)
313
+ });
286
314
  return h(props.as, {
287
315
  role: "status",
288
316
  "aria-live": "polite"
289
- }, slots.default?.(slotProps) ?? text + (slotProps.loadingChildrenCount ? " Loading children…" : "") + (slotProps.childErrorCount ? " Could not load children. Retry children in the affected row." : ""));
317
+ }, slots.default?.(slotProps) ?? [
318
+ text,
319
+ slotProps.loadingChildrenCount ? messages.loadingChildren : "",
320
+ slotProps.childErrorCount ? messages.childrenError : ""
321
+ ].filter((part) => part !== "").join(" "));
290
322
  };
291
323
  }
292
324
  });
@@ -294,7 +326,7 @@ const DataTableStatus = defineComponent({
294
326
  //#endregion
295
327
  //#region src/loading.ts
296
328
  /** Keep this visual sentinel out of the live region and logical data-row count. */
297
- function loadSentinel(table, colspan) {
329
+ function loadSentinel(table, colspan, locale) {
298
330
  return table.loadingMode === "cursor" && table.loadedRowCount > 0 && (table.loadState === "loading" || table.loadState === "streaming") ? h("tr", {
299
331
  key: "vt-load-sentinel",
300
332
  "data-load-sentinel": "",
@@ -302,7 +334,7 @@ function loadSentinel(table, colspan) {
302
334
  }, [h("td", {
303
335
  colspan: Math.max(1, colspan),
304
336
  class: "vt-load-sentinel"
305
- }, "Loading more rows…")]) : void 0;
337
+ }, locale?.messages.loadingMoreRows ?? "Loading more rows…")]) : void 0;
306
338
  }
307
339
  /** A keyboard-accessible alternative to automatic virtual-end loading, with error recovery. */
308
340
  const DataTableLoadMore = defineComponent({
@@ -310,29 +342,30 @@ const DataTableLoadMore = defineComponent({
310
342
  props: {
311
343
  label: {
312
344
  type: String,
313
- default: "Load more rows"
345
+ default: void 0
314
346
  },
315
347
  retryLabel: {
316
348
  type: String,
317
- default: "Retry loading"
349
+ default: void 0
318
350
  },
319
351
  loadingLabel: {
320
352
  type: String,
321
- default: "Loading rows…"
353
+ default: void 0
322
354
  }
323
355
  },
324
356
  setup(props) {
325
357
  const table = injectDataTable("<DataTableLoadMore>");
358
+ const locale = useTableLocale();
326
359
  return () => table.loadState === "error" ? h("button", {
327
360
  type: "button",
328
361
  onClick: () => table.retry()
329
- }, props.retryLabel) : table.loadingMode === "cursor" && table.loadState !== "done" ? h("button", {
362
+ }, props.retryLabel ?? locale().messages.retryLoading) : table.loadingMode === "cursor" && table.loadState !== "done" ? h("button", {
330
363
  type: "button",
331
364
  disabled: !table.canLoadMore,
332
365
  onClick: () => {
333
366
  table.loadNext();
334
367
  }
335
- }, table.loadState === "loading" || table.loadState === "streaming" ? props.loadingLabel : props.label) : null;
368
+ }, table.loadState === "loading" || table.loadState === "streaming" ? props.loadingLabel ?? locale().messages.loadingRows : props.label ?? locale().messages.loadMoreRows) : null;
336
369
  }
337
370
  });
338
371
 
@@ -375,6 +408,7 @@ const DataCellEditor = defineComponent({
375
408
  name: "DataCellEditor",
376
409
  props: cellEditorProps,
377
410
  setup(props) {
411
+ const locale = useTableLocale();
378
412
  const field = ref();
379
413
  const query = ref("");
380
414
  const options = props.spec.options ?? [];
@@ -418,9 +452,10 @@ const DataCellEditor = defineComponent({
418
452
  };
419
453
  return () => {
420
454
  const { editor, spec } = props;
455
+ const { messages, count } = locale();
421
456
  const attrs = {
422
457
  ...editor.attrs,
423
- "aria-label": `Edit ${editor.column.header}`,
458
+ "aria-label": messages.editCell(editor.column.header),
424
459
  "data-editor": "",
425
460
  disabled: editor.pending,
426
461
  onKeydown: keydown
@@ -437,7 +472,7 @@ const DataCellEditor = defineComponent({
437
472
  ref: field,
438
473
  type: "search",
439
474
  value: query.value,
440
- "aria-label": `Search ${editor.column.header} options`,
475
+ "aria-label": messages.searchOptions(editor.column.header),
441
476
  disabled: editor.pending,
442
477
  onInput: (event) => {
443
478
  query.value = event.target.value;
@@ -460,11 +495,11 @@ const DataCellEditor = defineComponent({
460
495
  }, shown.map(({ value, index }) => h("option", {
461
496
  key: index,
462
497
  value: index
463
- }, String(value ?? "(empty)")))),
498
+ }, value === null || value === void 0 ? messages.emptyOption : String(value)))),
464
499
  h("span", {
465
500
  role: "status",
466
501
  "data-editor-hint": ""
467
- }, matches.value.length === 0 ? "No matching options. Change your search." : matches.value.length > 50 ? "Showing the first 50 matches. Refine your search." : `${matches.value.length} options`)
502
+ }, matches.value.length === 0 ? messages.noMatchingOptions : matches.value.length > 50 ? messages.firstMatchesOnly(count(50)) : messages.optionCount(count(matches.value.length)))
468
503
  ]);
469
504
  }
470
505
  return h("input", {
@@ -825,7 +860,14 @@ function treeKeydown(event, context, virtual) {
825
860
  const index = table.rows.findIndex((row) => getRowItemKey(row.key) === key);
826
861
  const row = table.rows[index];
827
862
  if (!row) return;
828
- const command = gridCommand(event, {
863
+ const command = gridCommand({
864
+ key: event.key,
865
+ shiftKey: event.shiftKey,
866
+ ctrlKey: event.ctrlKey,
867
+ metaKey: event.metaKey,
868
+ altKey: event.altKey,
869
+ direction: directionOf(target)
870
+ }, {
829
871
  canExpand: row.canExpand,
830
872
  expanded: row.isExpanded
831
873
  });
@@ -853,11 +895,11 @@ const expandToggleProps = {
853
895
  },
854
896
  expandLabel: {
855
897
  type: String,
856
- default: "Expand row"
898
+ default: void 0
857
899
  },
858
900
  collapseLabel: {
859
901
  type: String,
860
- default: "Collapse row"
902
+ default: void 0
861
903
  }
862
904
  };
863
905
  const treeCellProps = {
@@ -872,13 +914,15 @@ const DataTableExpandToggle = defineComponent({
872
914
  const table = injectDataTable("<DataTableExpandToggle>");
873
915
  const context = injectHierarchy();
874
916
  const grid = injectDataGrid();
917
+ const locale = useTableLocale();
918
+ const label = () => props.row.isExpanded ? props.collapseLabel ?? locale().messages.collapseRow : props.expandLabel ?? locale().messages.expandRow;
875
919
  return () => props.row.canExpand ? h("button", {
876
920
  type: "button",
877
921
  "data-expand-toggle": "",
878
922
  tabindex: table.tree ? -1 : void 0,
879
923
  "aria-expanded": String(props.row.isExpanded),
880
924
  "aria-controls": props.controls ?? (!table.tree ? itemId(context, props.row, true) : props.row.isExpanded ? table.rows.filter((row) => row.parentKey === props.row.key).map((row) => itemId(context, row)).filter(Boolean).join(" ") || void 0 : void 0),
881
- "aria-label": props.row.isExpanded ? props.collapseLabel : props.expandLabel,
925
+ "aria-label": label(),
882
926
  onMousedown: (event) => event.stopPropagation(),
883
927
  onDblclick: (event) => event.stopPropagation(),
884
928
  onClick: (event) => {
@@ -893,7 +937,7 @@ const DataTableExpandToggle = defineComponent({
893
937
  }, slots.default?.({
894
938
  row: props.row,
895
939
  expanded: props.row.isExpanded
896
- }) ?? (props.row.isExpanded ? props.collapseLabel : props.expandLabel)) : null;
940
+ }) ?? label()) : null;
897
941
  }
898
942
  });
899
943
  const DataTableTreeCell = defineComponent({
@@ -904,6 +948,7 @@ const DataTableTreeCell = defineComponent({
904
948
  const table = injectDataTable("<DataTableTreeCell>");
905
949
  const context = injectHierarchy();
906
950
  const grid = injectDataGrid();
951
+ const locale = useTableLocale();
907
952
  return () => h(props.as, {
908
953
  class: "vt-tree-cell",
909
954
  "data-tree-cell": "",
@@ -911,8 +956,8 @@ const DataTableTreeCell = defineComponent({
911
956
  }, [
912
957
  h("span", { class: "vt-tree-toggle" }, slots.toggle?.({ row: props.row }) ?? h(DataTableExpandToggle, { row: props.row })),
913
958
  h("span", { class: "vt-tree-label" }, [slots.default?.()]),
914
- props.row.childStatus === "loading" ? h("span", { class: "vt-tree-loading" }, "Loading children…") : null,
915
- props.row.childStatus === "error" ? h("span", { class: "vt-tree-error" }, ["Could not load children. ", h("button", {
959
+ props.row.childStatus === "loading" ? h("span", { class: "vt-tree-loading" }, locale().messages.loadingChildren) : null,
960
+ props.row.childStatus === "error" ? h("span", { class: "vt-tree-error" }, [`${locale().messages.childrenError} `, h("button", {
916
961
  type: "button",
917
962
  class: "vt-tree-retry",
918
963
  onMousedown: (event) => event.stopPropagation(),
@@ -929,7 +974,7 @@ const DataTableTreeCell = defineComponent({
929
974
  else element?.ownerDocument.getElementById(itemId(context, props.row) ?? "")?.focus();
930
975
  });
931
976
  }
932
- }, "Retry children")]) : null
977
+ }, locale().messages.retryChildren)]) : null
933
978
  ]);
934
979
  }
935
980
  });
@@ -1155,13 +1200,14 @@ const DataTableSortButton = defineComponent({
1155
1200
  },
1156
1201
  setup(props, { slots }) {
1157
1202
  const table = injectDataTable("<DataTableSortButton>");
1203
+ const locale = useTableLocale();
1158
1204
  return () => {
1159
1205
  const sort = table.getSort(props.column.id);
1160
1206
  const next = !sort ? "ascending" : sort.direction === "asc" ? "descending" : "unsorted";
1161
1207
  return h(props.as, {
1162
1208
  type: props.as === "button" ? "button" : void 0,
1163
1209
  "data-sort": sort?.direction,
1164
- "aria-label": `${props.column.header}, sort ${next}`,
1210
+ "aria-label": locale().messages.sortBy(props.column.header, next),
1165
1211
  onClick: (event) => table.toggleSort(props.column.id, { multi: event.shiftKey })
1166
1212
  }, [slots.default?.({ sort }) ?? props.column.header, slots.indicator?.({ sort })]);
1167
1213
  };
@@ -1184,6 +1230,7 @@ const DataTableBody = defineComponent({
1184
1230
  },
1185
1231
  setup(props, { slots }) {
1186
1232
  const table = injectDataTable("<DataTableBody>");
1233
+ const locale = useTableLocale();
1187
1234
  const virtual = injectVirtual();
1188
1235
  const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length);
1189
1236
  return () => {
@@ -1197,7 +1244,7 @@ const DataTableBody = defineComponent({
1197
1244
  }) : details.render(item.row, item.rowIndex));
1198
1245
  const visibleDetails = new Set(table.renderItems.filter((item) => item.kind === "detail").map((item) => item.row.key));
1199
1246
  const retained = slots.default ? [] : details.retained(visibleDetails);
1200
- const sentinel = loadSentinel(table, props.colspan ?? table.columns.length);
1247
+ const sentinel = loadSentinel(table, props.colspan ?? table.columns.length, locale());
1201
1248
  return h(props.as, [
1202
1249
  ...Array.isArray(content) ? content : content ? [content] : [],
1203
1250
  ...retained,
@@ -1593,6 +1640,7 @@ const DataTableRowActions = defineComponent({
1593
1640
  },
1594
1641
  setup(props) {
1595
1642
  const table = injectDataTable("<DataTableRowActions>");
1643
+ const locale = useTableLocale();
1596
1644
  const context = inject(editingKey, void 0);
1597
1645
  const id = useEditorId();
1598
1646
  const element = shallowRef();
@@ -1601,31 +1649,33 @@ const DataTableRowActions = defineComponent({
1601
1649
  if (previous && !active && !context?.draft && target?.closest("tr")?.contains(target.ownerDocument.activeElement)) nextTick(() => target.querySelector("button[data-edit-row]")?.focus());
1602
1650
  });
1603
1651
  return () => {
1652
+ const { messages } = locale();
1653
+ const rowName = String(props.row.key);
1604
1654
  const draft = context?.draft?.rowKey === props.row.key ? context.draft : void 0;
1605
1655
  const issues = [...new Map(draft?.issues.map((problem) => [problem.message, problem])).values()];
1606
- const button = (label, action, disabled = false) => h("button", {
1656
+ const button = (label, action, disabled = false, role) => h("button", {
1607
1657
  type: "button",
1608
1658
  disabled,
1609
- "aria-label": `${label}, row ${String(props.row.key)}`,
1610
- "data-edit-row": label === "Edit row" ? "" : void 0,
1611
- ...label === "Save row" ? issueAttrs(id, issues, !!draft?.pending) : {},
1659
+ "aria-label": messages.actionOnRow(label, rowName),
1660
+ "data-edit-row": role === "edit" ? "" : void 0,
1661
+ ...role === "save" ? issueAttrs(id, issues, !!draft?.pending) : {},
1612
1662
  onClick: action
1613
1663
  }, label);
1614
1664
  return h("div", {
1615
1665
  ref: element,
1616
1666
  "data-row-actions": "",
1617
1667
  role: "group",
1618
- "aria-label": `Row ${String(props.row.key)} actions`,
1668
+ "aria-label": messages.rowActionsFor(rowName),
1619
1669
  "aria-busy": draft?.pending ? "true" : void 0
1620
1670
  }, [
1621
- ...context?.mode === "row" && props.editable ? draft ? [button("Save row", () => context.save(), draft.pending), button("Cancel", () => context.cancel(props.row.key), draft.pending)] : [button("Edit row", () => context.start(props.row))] : [],
1671
+ ...context?.mode === "row" && props.editable ? draft ? [button(messages.saveRow, () => context.save(), draft.pending, "save"), button(messages.cancel, () => context.cancel(props.row.key), draft.pending)] : [button(messages.editRow, () => context.start(props.row), false, "edit")] : [],
1622
1672
  h("details", { onToggle: (event) => {
1623
1673
  const menu = event.currentTarget;
1624
1674
  if (menu.open) nextTick(() => menu.scrollIntoView?.({
1625
1675
  block: "nearest",
1626
1676
  inline: "nearest"
1627
1677
  }));
1628
- } }, [h("summary", { "aria-label": `Actions for row ${String(props.row.key)}` }, "Actions"), h("div", { "data-row-menu": "" }, [button("Revert row", () => table.revert([props.row.key]), !props.row.isDirty || !!draft), props.removable ? button("Remove row", () => table.removeRows([props.row.key]), !!draft) : null])]),
1678
+ } }, [h("summary", { "aria-label": messages.actionsForRow(rowName) }, messages.actions), h("div", { "data-row-menu": "" }, [button(messages.revertRow, () => table.revert([props.row.key]), !props.row.isDirty || !!draft), props.removable ? button(messages.removeRow, () => table.removeRows([props.row.key]), !!draft) : null])]),
1629
1679
  editorIssues(id, issues, !!draft?.pending)
1630
1680
  ]);
1631
1681
  };
@@ -1739,7 +1789,14 @@ const DataGridRoot = defineComponent({
1739
1789
  event.preventDefault();
1740
1790
  return;
1741
1791
  }
1742
- if (grid.handleKey(event)) event.preventDefault();
1792
+ if (grid.handleKey({
1793
+ key: event.key,
1794
+ shiftKey: event.shiftKey,
1795
+ ctrlKey: event.ctrlKey,
1796
+ metaKey: event.metaKey,
1797
+ altKey: event.altKey,
1798
+ direction: directionOf(event.currentTarget)
1799
+ })) event.preventDefault();
1743
1800
  };
1744
1801
  const onCopy = (event, cut = false) => {
1745
1802
  if (grid.editor || !grid.range || !event.clipboardData) return;