@iyulab/flex-table 0.48.3 → 0.48.5

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,39 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.48.5] - 2026-10-06
4
+
5
+ ### Fixed
6
+
7
+ - **The form controls the grid draws are named.** Cell editors (text, number, select, date and the
8
+ autocomplete input) had no name and are now named by the column header; the autocomplete editor
9
+ is a combobox whose suggestions are a listbox of options, with the highlighted one announced. In
10
+ the filter dropdown — now a group named "Filter {column}" — the match-mode select, the filter text,
11
+ each numeric condition's operator and value, the condition join and the blank-cells select are
12
+ named (the blank-cells label sat next to its select without being tied to it). The find and
13
+ replace boxes were named only by their placeholder. New locale keys: `findInput`,
14
+ `replaceInput`, `filterFor`, `filterMatchMode`, `filterText`, `conditionOperator`,
15
+ `conditionValue`, `conditionJoin`.
16
+
17
+ ## [0.48.4] - 2026-10-06
18
+
19
+ ### Fixed
20
+
21
+ - **The find panel's controls are named.** The previous, next and close buttons held only `◀`, `▶`
22
+ and `✕`, and the two option checkboxes were labelled `Aa` and `[ ]`, so assistive technology read
23
+ symbols. They are now named from the locale ("Previous match", "Next match", "Close find", "Match
24
+ case", "Whole cell") and the buttons declare their keys with `aria-keyshortcuts`; the tooltips keep
25
+ showing the key. The no-match count ("0 results") was a fixed English string and now comes from
26
+ the locale too (new key `findNoResults`).
27
+ - **Row selection has a keyboard path, and the row checkboxes are no longer Tab stops.** With
28
+ `selectable`, every rendered row checkbox was in the Tab order and none had a name, so Shift+Tab
29
+ from the control after the grid landed on an unnamed checkbox, and selecting rows from the
30
+ keyboard meant tabbing through them. Shift+Space now selects (and deselects) the active cell's row
31
+ — Space alone still types into an editable cell — and the checkboxes are named ("Select row",
32
+ "Select all rows"; new keys `selectRow`, `selectAllRows`) and kept out of the Tab order.
33
+ - The locale values of `findPrevious`, `findNext` and `closeFind` no longer embed the key in
34
+ parentheses — the key is added to the tooltip separately. An app that registered its own wording
35
+ for these keys keeps it.
36
+
3
37
  ## [0.48.3] - 2026-10-06
4
38
 
5
39
  ### Fixed
package/README.md CHANGED
@@ -435,6 +435,7 @@ package dependency; CSS custom properties are resolved at render time, not impor
435
435
  | Home / End | Row start/end |
436
436
  | Ctrl+Home / Ctrl+End | Table start/end |
437
437
  | Shift+Arrow / Shift+Click | Extend selection range |
438
+ | Shift+Space | Select / deselect the active cell's row (when `selectable` — the row checkboxes are not Tab stops) |
438
439
  | Ctrl+C / Ctrl+X | Copy/Cut selection as TSV |
439
440
  | Ctrl+V | Paste TSV data |
440
441
  | Ctrl+D / Ctrl+R | Fill down / fill right |
@@ -1110,14 +1110,23 @@ O.register("en", {
1110
1110
  fromPlaceholder: "From",
1111
1111
  toPlaceholder: "To",
1112
1112
  findPlaceholder: "Find...",
1113
- findPrevious: "Previous (Shift+Enter)",
1114
- findNext: "Next (Enter)",
1113
+ findPrevious: "Previous match",
1114
+ findNext: "Next match",
1115
1115
  matchCase: "Match case",
1116
1116
  wholeCell: "Whole cell",
1117
- closeFind: "Close (Escape)",
1117
+ closeFind: "Close find",
1118
+ findNoResults: "No results",
1118
1119
  replaceWithPlaceholder: "Replace with...",
1119
1120
  replace: "Replace",
1120
1121
  replaceAll: "Replace all",
1122
+ findInput: "Find",
1123
+ replaceInput: "Replace with",
1124
+ filterFor: "Filter {header}",
1125
+ filterMatchMode: "Match",
1126
+ filterText: "Filter text",
1127
+ conditionOperator: "Condition {n}",
1128
+ conditionValue: "Condition {n} value",
1129
+ conditionJoin: "Combine conditions",
1121
1130
  sortAscending: "Sort ascending",
1122
1131
  sortDescending: "Sort descending",
1123
1132
  filter: "Filter…",
@@ -1133,6 +1142,8 @@ O.register("en", {
1133
1142
  insertRowAbove: "Insert row above",
1134
1143
  insertRowBelow: "Insert row below",
1135
1144
  deleteRow: "Delete row",
1145
+ selectRow: "Select row",
1146
+ selectAllRows: "Select all rows",
1136
1147
  filterByThisValue: "Filter by this value",
1137
1148
  addComment: "Add Comment",
1138
1149
  editComment: "Edit Comment",
@@ -1167,14 +1178,23 @@ O.register("en", {
1167
1178
  fromPlaceholder: "시작",
1168
1179
  toPlaceholder: "끝",
1169
1180
  findPlaceholder: "찾기...",
1170
- findPrevious: "이전 (Shift+Enter)",
1171
- findNext: "다음 (Enter)",
1181
+ findPrevious: "이전 일치",
1182
+ findNext: "다음 일치",
1172
1183
  matchCase: "대/소문자 구분",
1173
1184
  wholeCell: "셀 전체 일치",
1174
- closeFind: "닫기 (Escape)",
1185
+ closeFind: "찾기 닫기",
1186
+ findNoResults: "결과 없음",
1175
1187
  replaceWithPlaceholder: "바꿀 내용...",
1176
1188
  replace: "바꾸기",
1177
1189
  replaceAll: "모두 바꾸기",
1190
+ findInput: "찾을 내용",
1191
+ replaceInput: "바꿀 내용",
1192
+ filterFor: "{header} 필터",
1193
+ filterMatchMode: "일치 방식",
1194
+ filterText: "필터 텍스트",
1195
+ conditionOperator: "조건 {n}",
1196
+ conditionValue: "조건 {n} 값",
1197
+ conditionJoin: "조건 결합",
1178
1198
  sortAscending: "오름차순 정렬",
1179
1199
  sortDescending: "내림차순 정렬",
1180
1200
  filter: "필터…",
@@ -1190,6 +1210,8 @@ O.register("en", {
1190
1210
  insertRowAbove: "위에 행 삽입",
1191
1211
  insertRowBelow: "아래에 행 삽입",
1192
1212
  deleteRow: "행 삭제",
1213
+ selectRow: "행 선택",
1214
+ selectAllRows: "모든 행 선택",
1193
1215
  filterByThisValue: "이 값으로 필터",
1194
1216
  addComment: "메모 추가",
1195
1217
  editComment: "메모 편집",
@@ -2874,6 +2896,10 @@ var K = {
2874
2896
  return;
2875
2897
  }
2876
2898
  if (!this._handleCtrlKey(e) && !this._handleAltKey(e, t)) {
2899
+ if (e.key === " " && e.shiftKey && this.selectable && this._activeCell && !e.ctrlKey && !e.metaKey && !e.altKey) {
2900
+ e.preventDefault(), this._rowSelection.toggle(this._activeCell.row), this._lastCheckboxRowIndex = this._activeCell.row, this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
2901
+ return;
2902
+ }
2877
2903
  if (this._activeCell && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2878
2904
  let e = t[this._activeCell.col];
2879
2905
  e && this._isCellEditable(e) && this._startEdit();
@@ -3517,7 +3543,8 @@ var K = {
3517
3543
  _renderFilterDropdown(e) {
3518
3544
  let t = e.type ?? "text";
3519
3545
  return i`
3520
- <div class="ft-filter-dropdown" @click=${(e) => e.stopPropagation()}>
3546
+ <div class="ft-filter-dropdown" role="group" aria-label=${k("filterFor", { header: e.label })}
3547
+ @click=${(e) => e.stopPropagation()}>
3521
3548
  ${t === "boolean" ? this._renderBooleanFilter(e) : t === "number" ? this._renderNumberFilter(e) : t === "date" || t === "datetime" ? this._renderDateFilter(e) : this._renderTextFilter(e)}
3522
3549
  <div class="ft-filter-actions">
3523
3550
  <button class="ft-filter-clear" @click=${() => this._clearColumnFilter(e.key)}>${k("clear")}</button>
@@ -3554,7 +3581,7 @@ var K = {
3554
3581
  return i`
3555
3582
  <div class="ft-filter-empty-row">
3556
3583
  <label>${k("blankCells")}</label>
3557
- <select class="ft-filter-mode-select"
3584
+ <select class="ft-filter-mode-select" aria-label=${k("blankCells")}
3558
3585
  .value=${n}
3559
3586
  @change=${(n) => {
3560
3587
  let r = n.target.value;
@@ -3626,7 +3653,7 @@ var K = {
3626
3653
  };
3627
3654
  return i`
3628
3655
  <div class="ft-filter-mode-row">
3629
- <select class="ft-filter-mode-select"
3656
+ <select class="ft-filter-mode-select" aria-label=${k("filterMatchMode")}
3630
3657
  .value=${t.mode}
3631
3658
  @change=${(t) => {
3632
3659
  let n = t.target.value, r = this._textFilterState.get(e.key) ?? {
@@ -3647,7 +3674,7 @@ var K = {
3647
3674
  <option value="wildcard">${k("wildcard")}</option>
3648
3675
  </select>
3649
3676
  </div>
3650
- <input class="ft-filter-input" type="text" placeholder=${k("searchPlaceholder")}
3677
+ <input class="ft-filter-input" type="text" placeholder=${k("searchPlaceholder")} aria-label=${k("filterText")}
3651
3678
  .value=${t.value}
3652
3679
  @input=${(t) => {
3653
3680
  let n = t.target.value, r = this._textFilterState.get(e.key) ?? {
@@ -3684,7 +3711,7 @@ var K = {
3684
3711
  let n = (this._numberFilterState.get(e) ?? this._defaultNumState())[t];
3685
3712
  return i`
3686
3713
  <div class="ft-num-cond-row">
3687
- <select class="ft-filter-mode-select ft-num-op-select"
3714
+ <select class="ft-filter-mode-select ft-num-op-select" aria-label=${k("conditionOperator", { n: t === "cond1" ? 1 : 2 })}
3688
3715
  .value=${n.op}
3689
3716
  @change=${(n) => {
3690
3717
  let r = n.target.value, i = this._numberFilterState.get(e) ?? this._defaultNumState();
@@ -3699,6 +3726,7 @@ var K = {
3699
3726
  ${Object.keys(K).map((e) => i`<option value=${e}>${K[e]}</option>`)}
3700
3727
  </select>
3701
3728
  <input class="ft-filter-input ft-num-cond-input" type="text" inputmode="decimal" placeholder=${k("valuePlaceholder")}
3729
+ aria-label=${k("conditionValue", { n: t === "cond1" ? 1 : 2 })}
3702
3730
  .value=${n.text ?? (n.value == null ? "" : N(n.value))}
3703
3731
  aria-invalid=${n.text && n.value == null ? "true" : "false"}
3704
3732
  @input=${(n) => {
@@ -3720,7 +3748,7 @@ var K = {
3720
3748
  return i`
3721
3749
  ${this._renderNumCondRow(e.key, "cond1")}
3722
3750
  <div class="ft-filter-mode-row">
3723
- <select class="ft-filter-mode-select"
3751
+ <select class="ft-filter-mode-select" aria-label=${k("conditionJoin")}
3724
3752
  .value=${t.join}
3725
3753
  @change=${(t) => {
3726
3754
  let n = t.target.value, r = this._numberFilterState.get(e.key) ?? this._defaultNumState();
@@ -3785,7 +3813,7 @@ var K = {
3785
3813
  }
3786
3814
  _renderBooleanFilter(e) {
3787
3815
  return i`
3788
- <select class="ft-filter-input"
3816
+ <select class="ft-filter-input" aria-label=${k("filterFor", { header: e.label })}
3789
3817
  @change=${(t) => {
3790
3818
  let n = t.target.value;
3791
3819
  n === "all" ? this.removeFilter(e.key) : this.setFilter(e.key, (e) => !!e == (n === "true"));
@@ -4215,25 +4243,26 @@ var K = {
4215
4243
  e.key === "Enter" && !_(e) && e.target instanceof HTMLInputElement && e.target.classList.contains("ft-find-input") && (e.shiftKey ? this._findPrev() : this._findNext());
4216
4244
  }}>
4217
4245
  <div class="ft-find-row">
4218
- <input class="ft-find-input" type="text" placeholder=${k("findPlaceholder")}
4246
+ <input class="ft-find-input" type="text" placeholder=${k("findPlaceholder")} aria-label=${k("findInput")}
4219
4247
  .value=${t}
4220
4248
  @input=${(e) => {
4221
4249
  this._findState.query = e.target.value, this._findSearch();
4222
4250
  }}>
4223
- <span class="ft-find-count">${c > 0 ? `${l}/${c}` : t ? "0 results" : ""}</span>
4224
- <button @click=${() => this._findPrev()} title=${k("findPrevious")}>◀</button>
4225
- <button @click=${() => this._findNext()} title=${k("findNext")}>▶</button>
4226
- <label title=${k("matchCase")}><input type="checkbox" ?checked=${r} @change=${(e) => {
4251
+ <span class="ft-find-count">${c > 0 ? `${l}/${c}` : t ? k("findNoResults") : ""}</span>
4252
+ <!-- 글리프는 이름이 아니다(«◀» 는 «black left-pointing triangle» 로 읽힌다) — 이름은 aria-label, 키는 aria-keyshortcuts -->
4253
+ <button @click=${() => this._findPrev()} aria-label=${k("findPrevious")} aria-keyshortcuts="Shift+Enter" title=${`${k("findPrevious")} (Shift+Enter)`}>◀</button>
4254
+ <button @click=${() => this._findNext()} aria-label=${k("findNext")} aria-keyshortcuts="Enter" title=${`${k("findNext")} (Enter)`}>▶</button>
4255
+ <label title=${k("matchCase")}><input type="checkbox" aria-label=${k("matchCase")} ?checked=${r} @change=${(e) => {
4227
4256
  this._findState.matchCase = e.target.checked, this._findSearch();
4228
4257
  }}> Aa</label>
4229
- <label title=${k("wholeCell")}><input type="checkbox" ?checked=${a} @change=${(e) => {
4258
+ <label title=${k("wholeCell")}><input type="checkbox" aria-label=${k("wholeCell")} ?checked=${a} @change=${(e) => {
4230
4259
  this._findState.wholeCell = e.target.checked, this._findSearch();
4231
4260
  }}> [ ]</label>
4232
- <button @click=${() => this._closeFindPanel()} title=${k("closeFind")}>✕</button>
4261
+ <button @click=${() => this._closeFindPanel()} aria-label=${k("closeFind")} aria-keyshortcuts="Escape" title=${`${k("closeFind")} (Escape)`}>✕</button>
4233
4262
  </div>
4234
4263
  ${e === "replace" ? i`
4235
4264
  <div class="ft-find-row">
4236
- <input class="ft-find-replace-input" type="text" placeholder=${k("replaceWithPlaceholder")}
4265
+ <input class="ft-find-replace-input" type="text" placeholder=${k("replaceWithPlaceholder")} aria-label=${k("replaceInput")}
4237
4266
  .value=${n}
4238
4267
  @input=${(e) => {
4239
4268
  this._findState.replaceWith = e.target.value;
@@ -4254,7 +4283,7 @@ var K = {
4254
4283
  style="position: absolute; top: 0; left: ${d + u}px; width: ${this._checkboxColWidth}px; height: ${r}px; z-index: 4;">
4255
4284
  ${this._rowSelection.mode === "multi" ? i`
4256
4285
  <!-- 칸 전체가 누르는 자리다(라벨) — 체크 상자는 16px 그대로. -->
4257
- <label class="ft-checkbox-hit"><input type="checkbox"
4286
+ <label class="ft-checkbox-hit"><input type="checkbox" tabindex="-1" aria-label=${k("selectAllRows")}
4258
4287
  .checked=${this._rowSelection.isAllSelected}
4259
4288
  .indeterminate=${this._rowSelection.isSomeSelected}
4260
4289
  @change=${this._onSelectAllChange}></label>
@@ -4354,7 +4383,9 @@ var K = {
4354
4383
  let o = this.visibleColumns, s = this._toDataIndex(e), c = this.data[s], l = a ?? e * this.rowHeight, u = this.rowHeight, d = this._totalRowWidth, f = e % 2 == 0 ? "ft-row-even" : "ft-row-odd", p = this.selectable && this._rowSelection.isSelected(e), m = this._scrollLeft, h = 0, g = this.selectable ? i`
4355
4384
  <div class="ft-checkbox-cell"
4356
4385
  style="position: absolute; top: 0; left: ${m + h}px; width: ${this._checkboxColWidth}px; height: ${u}px; z-index: 2;">
4357
- <label class="ft-checkbox-hit"><input type="checkbox"
4386
+ <!-- 행 체크박스는 Tab 정지점이 아니다 — 그리드(호스트)가 하나의 정지점이고 행 선택은 Shift+Space 다.
4387
+ 렌더된 행마다 정지점이면 표 하나를 지나는 데 Tab 이 행 수만큼 든다. -->
4388
+ <label class="ft-checkbox-hit"><input type="checkbox" tabindex="-1" aria-label=${k("selectRow")}
4358
4389
  .checked=${p}
4359
4390
  @click=${(e) => this._onRowCheckboxClick(e)}
4360
4391
  @change=${(t) => this._onRowCheckboxChange(t, e)}></label>
@@ -4461,7 +4492,7 @@ var K = {
4461
4492
  if (t.editor) return t.editor(e[t.key], e, t);
4462
4493
  let n = e[t.key], r = n == null ? "" : String(n);
4463
4494
  if (t.type === "number") return i`
4464
- <input class="ft-editor ft-editor-number" type="text" inputmode="decimal"
4495
+ <input class="ft-editor ft-editor-number" type="text" inputmode="decimal" aria-label=${t.label}
4465
4496
  .value=${typeof n == "number" ? N(n) : r}
4466
4497
  @keydown=${this._onEditorKeyDown}
4467
4498
  @blur=${() => this._commitEdit()}>
@@ -4469,7 +4500,7 @@ var K = {
4469
4500
  if (t.type === "date" || t.type === "datetime") {
4470
4501
  let e = t.type === "datetime";
4471
4502
  return i`
4472
- <u-date-picker class="ft-editor ft-editor-${t.type}" size="sm"
4503
+ <u-date-picker class="ft-editor ft-editor-${t.type}" size="sm" aria-label=${t.label}
4473
4504
  mode=${e ? "datetime" : "date"} ?confirm=${e}
4474
4505
  .value=${Qe(n, e)}
4475
4506
  @keydown=${this._onEditorKeyDown}
@@ -4480,7 +4511,7 @@ var K = {
4480
4511
  if (t.type === "select" && t.options && t.options.length > 0) {
4481
4512
  let e = t.options, r = typeof e[0] == "string";
4482
4513
  return i`
4483
- <select class="ft-editor"
4514
+ <select class="ft-editor" aria-label=${t.label}
4484
4515
  @keydown=${this._onEditorKeyDown}
4485
4516
  @blur=${() => this._commitEdit()}
4486
4517
  @change=${() => this._commitEdit()}>
@@ -4491,15 +4522,20 @@ var K = {
4491
4522
  if (t.autocomplete) {
4492
4523
  let e = this._autocompleteState?.candidates ?? [], n = this._autocompleteState?.activeIndex ?? -1;
4493
4524
  return i`
4494
- <input class="ft-editor" type="text"
4525
+ <input class="ft-editor" type="text" aria-label=${t.label}
4526
+ role="combobox" aria-autocomplete="list"
4527
+ aria-expanded=${e.length > 0 ? "true" : "false"}
4528
+ aria-controls="ft-autocomplete-list"
4529
+ aria-activedescendant=${n >= 0 && e.length > 0 ? `ft-autocomplete-${n}` : a}
4495
4530
  .value=${r}
4496
4531
  @input=${(e) => this._onAutocompleteInput(e, t)}
4497
4532
  @keydown=${this._onEditorKeyDown}
4498
4533
  @blur=${() => this._commitEdit()}>
4499
4534
  ${e.length > 0 ? i`
4500
- <div class="ft-autocomplete-dropdown">
4535
+ <div class="ft-autocomplete-dropdown" id="ft-autocomplete-list" role="listbox" aria-label=${t.label}>
4501
4536
  ${e.map((e, t) => i`
4502
4537
  <div class="ft-autocomplete-item ${t === n ? "ft-autocomplete-active" : ""}"
4538
+ id=${`ft-autocomplete-${t}`} role="option" aria-selected=${t === n ? "true" : "false"}
4503
4539
  @mousedown=${(t) => {
4504
4540
  t.preventDefault(), this._selectAutocompleteCandidate(e);
4505
4541
  }}>
@@ -4511,7 +4547,7 @@ var K = {
4511
4547
  `;
4512
4548
  }
4513
4549
  return i`
4514
- <input class="ft-editor" type="text"
4550
+ <input class="ft-editor" type="text" aria-label=${t.label}
4515
4551
  .value=${r}
4516
4552
  @keydown=${this._onEditorKeyDown}
4517
4553
  @blur=${() => this._commitEdit()}>
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-CLw2o8sr.js";
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-CL29tbBZ.js";
2
2
  export { o as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, r as flexTableLocale, a as renderCell };
package/dist/locale.d.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  * (아이콘 자리의 글리프)는 어느 로케일에서도 같은 것을 뜻하며, 번역하면 오히려 읽히지 않는다.
26
26
  * 그 판단은 «번역하지 않기로 한 것»이지 «빠뜨린 것»이 아니다.
27
27
  */
28
- export type FlexTableMessageKey = 'showHiddenColumns' | 'columnMenu' | 'cellActions' | 'noColumnsDefined' | 'addCommentPlaceholder' | 'cancel' | 'save' | 'clear' | 'blankCells' | 'emptyOnly' | 'nonEmptyOnly' | 'blankAll' | 'all' | 'contains' | 'startsWith' | 'endsWith' | 'wildcard' | 'searchPlaceholder' | 'valuePlaceholder' | 'fromPlaceholder' | 'toPlaceholder' | 'findPlaceholder' | 'findPrevious' | 'findNext' | 'matchCase' | 'wholeCell' | 'closeFind' | 'replaceWithPlaceholder' | 'replace' | 'replaceAll' | 'sortAscending' | 'sortDescending' | 'filter' | 'clearFilter' | 'hideColumn' | 'showColumn' | 'autoFitWidth' | 'wider' | 'narrower' | 'columnMenuFor' | 'columnMenuRegion' | 'copy' | 'insertRowAbove' | 'insertRowBelow' | 'deleteRow' | 'filterByThisValue' | 'addComment' | 'editComment' | 'deleteComment' | 'booleanTrue' | 'booleanFalse' | 'dropFileToImport' | 'notANumber' | 'notADate' | 'notADateTime' | 'notInList';
28
+ export type FlexTableMessageKey = 'showHiddenColumns' | 'columnMenu' | 'cellActions' | 'noColumnsDefined' | 'addCommentPlaceholder' | 'cancel' | 'save' | 'clear' | 'blankCells' | 'emptyOnly' | 'nonEmptyOnly' | 'blankAll' | 'all' | 'contains' | 'startsWith' | 'endsWith' | 'wildcard' | 'searchPlaceholder' | 'valuePlaceholder' | 'fromPlaceholder' | 'toPlaceholder' | 'findPlaceholder' | 'findPrevious' | 'findNext' | 'matchCase' | 'wholeCell' | 'closeFind' | 'findNoResults' | 'replaceWithPlaceholder' | 'replace' | 'replaceAll' | 'findInput' | 'replaceInput' | 'filterFor' | 'filterMatchMode' | 'filterText' | 'conditionOperator' | 'conditionValue' | 'conditionJoin' | 'sortAscending' | 'sortDescending' | 'filter' | 'clearFilter' | 'hideColumn' | 'showColumn' | 'autoFitWidth' | 'wider' | 'narrower' | 'columnMenuFor' | 'columnMenuRegion' | 'copy' | 'insertRowAbove' | 'insertRowBelow' | 'deleteRow' | 'selectRow' | 'selectAllRows' | 'filterByThisValue' | 'addComment' | 'editComment' | 'deleteComment' | 'booleanTrue' | 'booleanFalse' | 'dropFileToImport' | 'notANumber' | 'notADate' | 'notADateTime' | 'notInList';
29
29
  /** 이 패키지의 chrome 문자열 묶음. 소비자가 `register()` 로 언어를 더하거나 문구를 덮을 수 있다. */
30
30
  export declare const flexTableLocale: import("@iyulab/components/dist/utilities/Locale.js").LocaleNamespace<FlexTableMessageKey>;
31
31
  /** 짧은 조회 별칭 — 렌더 코드가 읽히게 유지한다. */
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./flex-table-CLw2o8sr.js";
1
+ import { t as e } from "./flex-table-CL29tbBZ.js";
2
2
  import { i as t, t as n } from "./query-Dw4iO0ir.js";
3
3
  import { t as r } from "./view-DiLr-TdY.js";
4
4
  import i, { useCallback as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.48.3",
3
+ "version": "0.48.5",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",
@@ -71,6 +71,7 @@ flex-table {
71
71
  | Arrows / Tab / Shift+Tab | Move between cells |
72
72
  | Home / End, Ctrl+Home / Ctrl+End | Row start/end, table start/end |
73
73
  | Shift+Arrow, Shift+Click | Extend range selection |
74
+ | Shift+Space | Select / deselect the active cell's row (`selectable`; row checkboxes are not Tab stops) |
74
75
  | Ctrl+Click header | Select column |
75
76
  | Enter / F2 | Edit (Enter on a non-editable cell fires `row-activate`) |
76
77
  | Typing a printable character | Start editing an editable cell |