@iyulab/flex-table 0.48.2 → 0.48.4

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,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.48.4] - 2026-10-06
4
+
5
+ ### Fixed
6
+
7
+ - **The find panel's controls are named.** The previous, next and close buttons held only `◀`, `▶`
8
+ and `✕`, and the two option checkboxes were labelled `Aa` and `[ ]`, so assistive technology read
9
+ symbols. They are now named from the locale ("Previous match", "Next match", "Close find", "Match
10
+ case", "Whole cell") and the buttons declare their keys with `aria-keyshortcuts`; the tooltips keep
11
+ showing the key. The no-match count ("0 results") was a fixed English string and now comes from
12
+ the locale too (new key `findNoResults`).
13
+ - **Row selection has a keyboard path, and the row checkboxes are no longer Tab stops.** With
14
+ `selectable`, every rendered row checkbox was in the Tab order and none had a name, so Shift+Tab
15
+ from the control after the grid landed on an unnamed checkbox, and selecting rows from the
16
+ keyboard meant tabbing through them. Shift+Space now selects (and deselects) the active cell's row
17
+ — Space alone still types into an editable cell — and the checkboxes are named ("Select row",
18
+ "Select all rows"; new keys `selectRow`, `selectAllRows`) and kept out of the Tab order.
19
+ - The locale values of `findPrevious`, `findNext` and `closeFind` no longer embed the key in
20
+ parentheses — the key is added to the tooltip separately. An app that registered its own wording
21
+ for these keys keeps it.
22
+
23
+ ## [0.48.3] - 2026-10-06
24
+
25
+ ### Fixed
26
+
27
+ - **Enter and Space on a column menu button open the menu while a cell is active.** The grid read
28
+ the key as its own and started editing the active cell instead. Keys pressed on a control inside
29
+ the grid — a column menu button, a row checkbox, a button a cell renders — are now that control's;
30
+ the grid's cell keys apply while the grid itself has focus. Ctrl/Cmd shortcuts still apply.
31
+
3
32
  ## [0.48.2] - 2026-10-06
4
33
 
5
34
  ### 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,11 +1110,12 @@ 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",
@@ -1133,6 +1134,8 @@ O.register("en", {
1133
1134
  insertRowAbove: "Insert row above",
1134
1135
  insertRowBelow: "Insert row below",
1135
1136
  deleteRow: "Delete row",
1137
+ selectRow: "Select row",
1138
+ selectAllRows: "Select all rows",
1136
1139
  filterByThisValue: "Filter by this value",
1137
1140
  addComment: "Add Comment",
1138
1141
  editComment: "Edit Comment",
@@ -1167,11 +1170,12 @@ O.register("en", {
1167
1170
  fromPlaceholder: "시작",
1168
1171
  toPlaceholder: "끝",
1169
1172
  findPlaceholder: "찾기...",
1170
- findPrevious: "이전 (Shift+Enter)",
1171
- findNext: "다음 (Enter)",
1173
+ findPrevious: "이전 일치",
1174
+ findNext: "다음 일치",
1172
1175
  matchCase: "대/소문자 구분",
1173
1176
  wholeCell: "셀 전체 일치",
1174
- closeFind: "닫기 (Escape)",
1177
+ closeFind: "찾기 닫기",
1178
+ findNoResults: "결과 없음",
1175
1179
  replaceWithPlaceholder: "바꿀 내용...",
1176
1180
  replace: "바꾸기",
1177
1181
  replaceAll: "모두 바꾸기",
@@ -1190,6 +1194,8 @@ O.register("en", {
1190
1194
  insertRowAbove: "위에 행 삽입",
1191
1195
  insertRowBelow: "아래에 행 삽입",
1192
1196
  deleteRow: "행 삭제",
1197
+ selectRow: "행 선택",
1198
+ selectAllRows: "모든 행 선택",
1193
1199
  filterByThisValue: "이 값으로 필터",
1194
1200
  addComment: "메모 추가",
1195
1201
  editComment: "메모 편집",
@@ -2847,7 +2853,7 @@ var K = {
2847
2853
  "y"
2848
2854
  ].includes(t) && this._handleCtrlKey(e)) return;
2849
2855
  }
2850
- if (t.length !== 0 && this._visibleRowCount !== 0) {
2856
+ if (t.length !== 0 && this._visibleRowCount !== 0 && (e.composedPath()[0] === this || e.ctrlKey || e.metaKey)) {
2851
2857
  if ((e.key === "Enter" || e.key === "F2") && this._activeCell && !_(e)) {
2852
2858
  e.preventDefault();
2853
2859
  let n = t[this._activeCell.col];
@@ -2874,6 +2880,10 @@ var K = {
2874
2880
  return;
2875
2881
  }
2876
2882
  if (!this._handleCtrlKey(e) && !this._handleAltKey(e, t)) {
2883
+ if (e.key === " " && e.shiftKey && this.selectable && this._activeCell && !e.ctrlKey && !e.metaKey && !e.altKey) {
2884
+ e.preventDefault(), this._rowSelection.toggle(this._activeCell.row), this._lastCheckboxRowIndex = this._activeCell.row, this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
2885
+ return;
2886
+ }
2877
2887
  if (this._activeCell && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
2878
2888
  let e = t[this._activeCell.col];
2879
2889
  e && this._isCellEditable(e) && this._startEdit();
@@ -4220,16 +4230,17 @@ var K = {
4220
4230
  @input=${(e) => {
4221
4231
  this._findState.query = e.target.value, this._findSearch();
4222
4232
  }}>
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) => {
4233
+ <span class="ft-find-count">${c > 0 ? `${l}/${c}` : t ? k("findNoResults") : ""}</span>
4234
+ <!-- 글리프는 이름이 아니다(«◀» 는 «black left-pointing triangle» 로 읽힌다) — 이름은 aria-label, 키는 aria-keyshortcuts -->
4235
+ <button @click=${() => this._findPrev()} aria-label=${k("findPrevious")} aria-keyshortcuts="Shift+Enter" title=${`${k("findPrevious")} (Shift+Enter)`}>◀</button>
4236
+ <button @click=${() => this._findNext()} aria-label=${k("findNext")} aria-keyshortcuts="Enter" title=${`${k("findNext")} (Enter)`}>▶</button>
4237
+ <label title=${k("matchCase")}><input type="checkbox" aria-label=${k("matchCase")} ?checked=${r} @change=${(e) => {
4227
4238
  this._findState.matchCase = e.target.checked, this._findSearch();
4228
4239
  }}> Aa</label>
4229
- <label title=${k("wholeCell")}><input type="checkbox" ?checked=${a} @change=${(e) => {
4240
+ <label title=${k("wholeCell")}><input type="checkbox" aria-label=${k("wholeCell")} ?checked=${a} @change=${(e) => {
4230
4241
  this._findState.wholeCell = e.target.checked, this._findSearch();
4231
4242
  }}> [ ]</label>
4232
- <button @click=${() => this._closeFindPanel()} title=${k("closeFind")}>✕</button>
4243
+ <button @click=${() => this._closeFindPanel()} aria-label=${k("closeFind")} aria-keyshortcuts="Escape" title=${`${k("closeFind")} (Escape)`}>✕</button>
4233
4244
  </div>
4234
4245
  ${e === "replace" ? i`
4235
4246
  <div class="ft-find-row">
@@ -4254,7 +4265,7 @@ var K = {
4254
4265
  style="position: absolute; top: 0; left: ${d + u}px; width: ${this._checkboxColWidth}px; height: ${r}px; z-index: 4;">
4255
4266
  ${this._rowSelection.mode === "multi" ? i`
4256
4267
  <!-- 칸 전체가 누르는 자리다(라벨) — 체크 상자는 16px 그대로. -->
4257
- <label class="ft-checkbox-hit"><input type="checkbox"
4268
+ <label class="ft-checkbox-hit"><input type="checkbox" tabindex="-1" aria-label=${k("selectAllRows")}
4258
4269
  .checked=${this._rowSelection.isAllSelected}
4259
4270
  .indeterminate=${this._rowSelection.isSomeSelected}
4260
4271
  @change=${this._onSelectAllChange}></label>
@@ -4354,7 +4365,9 @@ var K = {
4354
4365
  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
4366
  <div class="ft-checkbox-cell"
4356
4367
  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"
4368
+ <!-- 행 체크박스는 Tab 정지점이 아니다 — 그리드(호스트)가 하나의 정지점이고 행 선택은 Shift+Space 다.
4369
+ 렌더된 행마다 정지점이면 표 하나를 지나는 데 Tab 이 행 수만큼 든다. -->
4370
+ <label class="ft-checkbox-hit"><input type="checkbox" tabindex="-1" aria-label=${k("selectRow")}
4358
4371
  .checked=${p}
4359
4372
  @click=${(e) => this._onRowCheckboxClick(e)}
4360
4373
  @change=${(t) => this._onRowCheckboxChange(t, e)}></label>
@@ -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-CDMcn3Nj.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-Do-AzEj5.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' | '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-CDMcn3Nj.js";
1
+ import { t as e } from "./flex-table-Do-AzEj5.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.2",
3
+ "version": "0.48.4",
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 |