@iyulab/flex-table 0.26.0 → 0.27.0
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
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.27.0] - 2026-08-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Shift-click range selection on the row checkbox column** (`selectable` + `selectionMode:
|
|
8
|
+
'multi'`). Clicking a row's checkbox while holding Shift now selects every row between it and
|
|
9
|
+
the last row you clicked, matching the row-selection convention used by spreadsheets and file
|
|
10
|
+
managers. A plain click still just toggles the one row it lands on, and the anchor resets to
|
|
11
|
+
whichever row you last clicked (shift or not) — the same rules as native cell selection already
|
|
12
|
+
followed elsewhere in the grid.
|
|
13
|
+
- **`selectWhere(predicate)`** — selects every currently-loaded row for which `predicate(row,
|
|
14
|
+
dataIndex)` returns true, added to whatever is already selected (call `deselectAll()` first for
|
|
15
|
+
a fresh set). Built for "the user pastes a list of keys, select the matching rows" flows: the
|
|
16
|
+
host doesn't need to reason about visual-vs-data row indices or virtualization — it gets the
|
|
17
|
+
same row objects `data` was set with.
|
|
18
|
+
- **`emptyMessage` / `noMatchingMessage`** — the text shown in the empty state (`data` is empty,
|
|
19
|
+
or every row is hidden by an active column filter) was previously hardcoded to `'No data'` /
|
|
20
|
+
`'No matching data'` with no way to override it. Any consumer localizing their UI, or wanting a
|
|
21
|
+
more specific message ("no orders yet — create one"), had to reimplement the empty state
|
|
22
|
+
themselves. Both now default to the same English strings for backward compatibility, and can be
|
|
23
|
+
overridden as string properties/attributes (`empty-message`, `no-matching-message`).
|
|
24
|
+
|
|
3
25
|
## [0.26.0] - 2026-08-20
|
|
4
26
|
|
|
5
27
|
### Added
|
|
@@ -20,4 +20,11 @@ export declare class RowSelectionState {
|
|
|
20
20
|
deselect(index: number): void;
|
|
21
21
|
selectAll(): void;
|
|
22
22
|
deselectAll(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Selects every index between from and to, inclusive, in either direction.
|
|
25
|
+
* Used for shift-click range selection anchored at the last toggled row.
|
|
26
|
+
* Single mode: only the endpoint (to) ends up selected, matching toggle()'s
|
|
27
|
+
* "one at a time" semantics — a range doesn't make sense with one slot.
|
|
28
|
+
*/
|
|
29
|
+
selectRange(from: number, to: number): void;
|
|
23
30
|
}
|
|
@@ -1113,6 +1113,14 @@ var y = class {
|
|
|
1113
1113
|
deselectAll() {
|
|
1114
1114
|
this._selected.clear();
|
|
1115
1115
|
}
|
|
1116
|
+
selectRange(e, t) {
|
|
1117
|
+
if (this._mode === "single") {
|
|
1118
|
+
this.select(t);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
let n = Math.min(e, t), r = Math.max(e, t);
|
|
1122
|
+
for (let e = n; e <= r; e++) e >= 0 && e < this._rowCount && this._selected.add(e);
|
|
1123
|
+
}
|
|
1116
1124
|
};
|
|
1117
1125
|
//#endregion
|
|
1118
1126
|
//#region src/core/sorting.ts
|
|
@@ -1556,7 +1564,7 @@ var J = {
|
|
|
1556
1564
|
lte: "≤"
|
|
1557
1565
|
}, Y = 120, X = 40, Z = 32, Q = 5, $ = class extends e {
|
|
1558
1566
|
constructor(...e) {
|
|
1559
|
-
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new y(), this._editing = new b(), this._rowSelection = new x(), this._undo = new D(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._onCommentPopupOutsideClick = () => {
|
|
1567
|
+
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = Z, this.showRowNumbers = !1, this.theme = void 0, this.maxRows = 0, this.editable = !0, this.showFilters = !1, this.showContextMenu = !1, this.frozenRows = 0, this.emptyMessage = "No data", this.noMatchingMessage = "No matching data", this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._activeCell = null, this._editingCell = null, this._sortCriteria = [], this._selection = new y(), this._editing = new b(), this._rowSelection = new x(), this._undo = new D(), this._filters = [], this._filteredIndices = [], this._sortedIndices = [], this._openFilterKey = null, this._rowSelectionVersion = 0, this._lastCheckboxRowIndex = null, this._checkboxShiftPending = !1, this._autocompleteState = null, this._headerMenu = null, this._bodyContextMenu = null, this._commentPopup = null, this._viewDirty = !0, this._isDragging = !1, this._wasDrag = !1, this._resizing = null, this._resizeCleanup = null, this._colDrag = null, this._colDragIndicatorLeft = null, this._fillDrag = null, this._rowDrag = null, this._rowDragIndicatorY = null, this._findState = null, this._columnWidths = /* @__PURE__ */ new Map(), this._hostResizeObserver = null, this._comments = /* @__PURE__ */ new Map(), this._isDragOver = !1, this._onCommentPopupOutsideClick = () => {
|
|
1560
1568
|
this._commitCommentPopup();
|
|
1561
1569
|
}, this._invalidCells = /* @__PURE__ */ new Map(), this._textFilterState = /* @__PURE__ */ new Map(), this._numberFilterState = /* @__PURE__ */ new Map(), this._dateFilterState = /* @__PURE__ */ new Map(), this._emptyFilterState = /* @__PURE__ */ new Map();
|
|
1562
1570
|
}
|
|
@@ -1627,6 +1635,15 @@ var J = {
|
|
|
1627
1635
|
deselectAll() {
|
|
1628
1636
|
this.selectable && (this._rowSelection.deselectAll(), this._rowSelectionVersion++, this._dispatchRowSelectionEvent());
|
|
1629
1637
|
}
|
|
1638
|
+
selectWhere(e) {
|
|
1639
|
+
if (this.selectable) {
|
|
1640
|
+
for (let t = 0; t < this._visibleRowCount; t++) {
|
|
1641
|
+
let n = this._toDataIndex(t), r = this.data[n];
|
|
1642
|
+
r !== void 0 && e(r, n) && this._rowSelection.select(t);
|
|
1643
|
+
}
|
|
1644
|
+
this._rowSelectionVersion++, this._dispatchRowSelectionEvent(), this.requestUpdate();
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1630
1647
|
_dispatchRowSelectionEvent() {
|
|
1631
1648
|
let { selectedIndices: e, selectedRows: t } = this.getSelectedRows();
|
|
1632
1649
|
this.dispatchEvent(new CustomEvent("selection-change", {
|
|
@@ -3794,7 +3811,7 @@ var J = {
|
|
|
3794
3811
|
for (let t = s; t < c; t++) m.push(this._renderHeaderCell(e[t], t));
|
|
3795
3812
|
let h = this._colDragIndicatorLeft == null ? "" : n`<div class="ft-drop-indicator" style="left:${this._colDragIndicatorLeft}px"></div>`;
|
|
3796
3813
|
if (this.data.length === 0 || this._visibleRowCount === 0) {
|
|
3797
|
-
let e = this.data.length === 0 ?
|
|
3814
|
+
let e = this.data.length === 0 ? this.emptyMessage : this.noMatchingMessage;
|
|
3798
3815
|
return n`
|
|
3799
3816
|
${i}
|
|
3800
3817
|
<div class="ft-header" role="row" style="width: ${o}px; height: ${a}px;">
|
|
@@ -3833,7 +3850,10 @@ var J = {
|
|
|
3833
3850
|
e.target.checked ? this._rowSelection.selectAll() : this._rowSelection.deselectAll(), this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
|
|
3834
3851
|
}
|
|
3835
3852
|
_onRowCheckboxChange(e, t) {
|
|
3836
|
-
e.stopPropagation(), this._rowSelection.toggle(t), this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
|
|
3853
|
+
e.stopPropagation(), this._checkboxShiftPending && this._lastCheckboxRowIndex !== null ? this._rowSelection.selectRange(this._lastCheckboxRowIndex, t) : this._rowSelection.toggle(t), this._lastCheckboxRowIndex = t, this._checkboxShiftPending = !1, this._rowSelectionVersion++, this._dispatchRowSelectionEvent();
|
|
3854
|
+
}
|
|
3855
|
+
_onRowCheckboxClick(e) {
|
|
3856
|
+
this._checkboxShiftPending = e.shiftKey;
|
|
3837
3857
|
}
|
|
3838
3858
|
_renderFrozenRows(e, t, i) {
|
|
3839
3859
|
let a = this._frozenRowCount;
|
|
@@ -3880,6 +3900,7 @@ var J = {
|
|
|
3880
3900
|
style="position: absolute; top: 0; left: ${m + h}px; width: 36px; height: ${u}px; z-index: 2;">
|
|
3881
3901
|
<input type="checkbox"
|
|
3882
3902
|
.checked=${p}
|
|
3903
|
+
@click=${(e) => this._onRowCheckboxClick(e)}
|
|
3883
3904
|
@change=${(t) => this._onRowCheckboxChange(t, e)}>
|
|
3884
3905
|
</div>
|
|
3885
3906
|
` : "";
|
|
@@ -4055,6 +4076,12 @@ q([a({ type: Array })], $.prototype, "columns", void 0), q([a({
|
|
|
4055
4076
|
type: Number,
|
|
4056
4077
|
attribute: "frozen-rows"
|
|
4057
4078
|
})], $.prototype, "frozenRows", void 0), q([a({
|
|
4079
|
+
type: String,
|
|
4080
|
+
attribute: "empty-message"
|
|
4081
|
+
})], $.prototype, "emptyMessage", void 0), q([a({
|
|
4082
|
+
type: String,
|
|
4083
|
+
attribute: "no-matching-message"
|
|
4084
|
+
})], $.prototype, "noMatchingMessage", void 0), q([a({
|
|
4058
4085
|
type: Boolean,
|
|
4059
4086
|
reflect: !0
|
|
4060
4087
|
})], $.prototype, "loading", void 0), q([a({
|
package/dist/flex-table.d.ts
CHANGED
|
@@ -61,6 +61,13 @@ export declare class FlexTable extends LitElement {
|
|
|
61
61
|
showContextMenu: boolean;
|
|
62
62
|
/** Number of rows to freeze at the top (always visible during vertical scroll). */
|
|
63
63
|
frozenRows: number;
|
|
64
|
+
/** Message shown when `data` is empty (no rows at all). Default: 'No data'. */
|
|
65
|
+
emptyMessage: string;
|
|
66
|
+
/**
|
|
67
|
+
* Message shown when `data` has rows but every one is hidden by the active
|
|
68
|
+
* column filters (0 visible rows, non-empty `data`). Default: 'No matching data'.
|
|
69
|
+
*/
|
|
70
|
+
noMatchingMessage: string;
|
|
64
71
|
/**
|
|
65
72
|
* True일 때 그리드 위에 로딩 오버레이를 표시하고 host에 `aria-busy="true"`를 반영한다.
|
|
66
73
|
* `useODataSource()`가 반환하는 `loading`과 자연 연동하도록 설계됨:
|
|
@@ -98,6 +105,10 @@ export declare class FlexTable extends LitElement {
|
|
|
98
105
|
private _sortedIndices;
|
|
99
106
|
private _openFilterKey;
|
|
100
107
|
private _rowSelectionVersion;
|
|
108
|
+
/** Anchor row for shift-click range selection on the row checkbox column. */
|
|
109
|
+
private _lastCheckboxRowIndex;
|
|
110
|
+
/** Set by the checkbox's own click (which carries shiftKey) just before its change event fires. */
|
|
111
|
+
private _checkboxShiftPending;
|
|
101
112
|
private _autocompleteState;
|
|
102
113
|
private _headerMenu;
|
|
103
114
|
private _bodyContextMenu;
|
|
@@ -138,6 +149,17 @@ export declare class FlexTable extends LitElement {
|
|
|
138
149
|
selectAll(): void;
|
|
139
150
|
/** Deselect all rows. */
|
|
140
151
|
deselectAll(): void;
|
|
152
|
+
/**
|
|
153
|
+
* Selects every currently-loaded row for which `predicate` returns true, in addition to
|
|
154
|
+
* whatever is already selected — call `deselectAll()` first for a fresh set. Iterates the
|
|
155
|
+
* visible (post filter/sort) view and resolves each visual row to its underlying data row,
|
|
156
|
+
* so the predicate always sees the same objects `data` was set with.
|
|
157
|
+
*
|
|
158
|
+
* Built for "paste a list of business keys, select the matching rows" flows — e.g. a user
|
|
159
|
+
* pastes order numbers and the host selects whichever loaded rows match, without the host
|
|
160
|
+
* needing to know how visual/data indices relate.
|
|
161
|
+
*/
|
|
162
|
+
selectWhere(predicate: (row: DataRow, dataIndex: number) => boolean): void;
|
|
141
163
|
private _dispatchRowSelectionEvent;
|
|
142
164
|
/**
|
|
143
165
|
* Set a filter for a column. Replaces any existing filter on the same key.
|
|
@@ -400,6 +422,12 @@ export declare class FlexTable extends LitElement {
|
|
|
400
422
|
render(): TemplateResult<1>;
|
|
401
423
|
private _onSelectAllChange;
|
|
402
424
|
private _onRowCheckboxChange;
|
|
425
|
+
/**
|
|
426
|
+
* Captures shiftKey from the checkbox's own click (MouseEvent) — the subsequent native
|
|
427
|
+
* `change` event does not carry modifier keys. Click always fires before change for a
|
|
428
|
+
* checkbox input, so this reliably primes `_onRowCheckboxChange` for the same interaction.
|
|
429
|
+
*/
|
|
430
|
+
private _onRowCheckboxClick;
|
|
403
431
|
private _renderFrozenRows;
|
|
404
432
|
private _renderFooter;
|
|
405
433
|
private _renderRow;
|
package/dist/flex-table.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "./flex-table-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./flex-table-CeT8zkEi.js";
|
|
2
2
|
export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
|
package/dist/react.js
CHANGED