@iyulab/flex-table 0.53.0 → 0.54.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 +8 -0
- package/README.md +3 -3
- package/dist/{flex-table-DUAMhXCS.js → flex-table-DlvIBu4z.js} +9 -5
- package/dist/flex-table.d.ts +8 -0
- package/dist/flex-table.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/skills/iyulab-flex-table/SKILL.md +1 -1
- package/skills/iyulab-flex-table/references/api.md +1 -0
- package/skills/iyulab-flex-table/references/react.md +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.54.0] - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`error`** property — the last load failure (`{ message }`, so a data source's `SourceError` goes in as is). While
|
|
8
|
+
set, the grid shows `error.message` as an alert where the rows or the empty state would be; a failed query used to
|
|
9
|
+
look like "No data" unless the app drew its own message beside the grid.
|
|
10
|
+
|
|
3
11
|
## [0.53.0] - 2026-10-07
|
|
4
12
|
|
|
5
13
|
### Added
|
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ guarantee about a *constrained* host. `height-model.browser.test.ts` pins both s
|
|
|
113
113
|
| `footerData` | `footer-data` | `Record<string, string \| TemplateResult> \| null` | `null` | Footer/summary row data (keys match column keys) |
|
|
114
114
|
| `emptyMessage` | `empty-message` | `string` | `'No data'` | Shown when `data` is empty |
|
|
115
115
|
| `noMatchingMessage` | `no-matching-message` | `string` | `'No matching data'` | Shown when `data` has rows but every one is hidden by an active column filter |
|
|
116
|
+
| `error` | — | `{ message: string } \| null` | `null` | The last load failure. While set, `error.message` is shown as an alert where the rows or the empty state would be, so a failed query does not look like "no data". Pass a data source's `error` as is |
|
|
116
117
|
| `stylesheets` | — | `CSSStyleSheet[]` | `[]` | Constructable stylesheets adopted into the shadow root alongside the grid's own styles — the escape hatch for styling content a `render` function inserts, since document CSS doesn't cross the shadow boundary. Reassigning swaps the previous set, it doesn't accumulate |
|
|
117
118
|
|
|
118
119
|
### Read-only Properties
|
|
@@ -816,7 +817,7 @@ The hook returns:
|
|
|
816
817
|
|---|---|
|
|
817
818
|
| `data` / `totalCount` | Current page rows and the server's total (`@odata.count`). When the server pages its response (`@odata.nextLink`, e.g. a page size smaller than `pageSize`), the hook follows the link until the page is filled; a link outside the request's origin, or one that returns to a page already read, is reported through `error` instead of showing a short page |
|
|
818
819
|
| `loading` | A request is in flight, or none has answered yet (true from the first render until the first response settles, and while `enabled: false`) |
|
|
819
|
-
| `error` | The last failed request, or `null`: `{ message, status?, code?, details?, body? }`. **
|
|
820
|
+
| `error` | The last failed request, or `null`: `{ message, status?, code?, details?, body? }`. **Pass it to the table** (`error={source.error}`) or render `error.message` yourself — a failed request otherwise leaves the grid silently empty. Branch on `status` (HTTP), `code` (the server's rejection code, OData `error.code`) and `details` (OData `error.details`); `body` is the parsed response (or its text). A failure with no response — a network error, or a `@odata.nextLink` the hook refused — has a `message` only |
|
|
820
821
|
| `page` / `setPage` | Zero-based page index |
|
|
821
822
|
| `sortCriteria` / `onSortChange` | Bind `onSortChange` to the table's `sort-change` event |
|
|
822
823
|
| `search` / `setSearch` | Current search term and its setter (resets to page 0) |
|
|
@@ -888,8 +889,7 @@ class OrdersPage extends LitElement {
|
|
|
888
889
|
render() {
|
|
889
890
|
const { data, loading, error } = this.orders.state;
|
|
890
891
|
return html`
|
|
891
|
-
|
|
892
|
-
<flex-table data-mode="server" .data=${data} .loading=${loading}
|
|
892
|
+
<flex-table data-mode="server" .data=${data} .loading=${loading} .error=${error}
|
|
893
893
|
@sort-change=${(e: CustomEvent) => this.orders.source.setSort(e.detail.criteria)}></flex-table>`;
|
|
894
894
|
}
|
|
895
895
|
}
|
|
@@ -392,6 +392,10 @@ var S = i`
|
|
|
392
392
|
color: var(--ft-empty-color);
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
.ft-error {
|
|
396
|
+
color: var(--ft-invalid-color);
|
|
397
|
+
}
|
|
398
|
+
|
|
395
399
|
.ft-has-comment {
|
|
396
400
|
position: relative;
|
|
397
401
|
}
|
|
@@ -1752,7 +1756,7 @@ var K = {
|
|
|
1752
1756
|
lte: "≤"
|
|
1753
1757
|
}, q = 120, J = 40, Y = 20, X = 32, Z = 5, Xe = 200, Q = class extends r {
|
|
1754
1758
|
constructor(...e) {
|
|
1755
|
-
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = X, this._targetFloor = 0, 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.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._headerCol = null, this._focusedCellEl = null, this._editingCell = null, this._sortCriteria = [], this._selection = new oe(), this._editing = new se(), this._rowSelection = new k(), this._undo = new A(), 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._wasHeaderDrag = !1, this._wasRowDrag = !1, 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._warnedUnconstrained = !1, this._onHostFocus = (e) => {
|
|
1759
|
+
super(...e), this.columns = [], this._data = [], this._inUndoRedo = !1, this.clearUndoOnDataChange = !1, this.clearSelectionOnDataChange = !1, this._rowHeightFromCss = X, this._targetFloor = 0, 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.error = null, this.loading = !1, this.importEnabled = !1, this.selectable = !1, this.dataMode = "client", this.footerData = null, this.stylesheets = [], this._scrollTop = 0, this._scrollLeft = 0, this._viewportHeight = 0, this._viewportWidth = 0, this._colLeftOffsets = [], this._totalRowWidth = 0, this._baseStyleSheets = [], this._activeCell = null, this._headerCol = null, this._focusedCellEl = null, this._editingCell = null, this._sortCriteria = [], this._selection = new oe(), this._editing = new se(), this._rowSelection = new k(), this._undo = new A(), 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._wasHeaderDrag = !1, this._wasRowDrag = !1, 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._warnedUnconstrained = !1, this._onHostFocus = (e) => {
|
|
1756
1760
|
e.composedPath()[0] !== this || this._activeCell || this._headerCol !== null || this.visibleColumns.length !== 0 && this.matches(":focus-visible") && (this._visibleRowCount > 0 ? (this._selection.setActive(0, 0), this._syncActiveCell()) : this._headerCol = 0);
|
|
1757
1761
|
}, this._onFocusIn = (e) => {
|
|
1758
1762
|
let t = e.composedPath()[0];
|
|
@@ -4153,8 +4157,8 @@ var K = {
|
|
|
4153
4157
|
for (let e of u) h.push(this._renderHeaderCell(t[e], e));
|
|
4154
4158
|
for (let e = c; e < l; e++) h.push(this._renderHeaderCell(t[e], e));
|
|
4155
4159
|
let g = this._colDragIndicatorLeft == null ? "" : a`<div class="ft-drop-indicator" style="left:${this._colDragIndicatorLeft}px"></div>`;
|
|
4156
|
-
if (this.data.length === 0 || this._visibleRowCount === 0) {
|
|
4157
|
-
let e = this.data.length === 0 ? this.emptyMessage : this.noMatchingMessage
|
|
4160
|
+
if (this.error || this.data.length === 0 || this._visibleRowCount === 0) {
|
|
4161
|
+
let e = this.error ? a`<div class="ft-empty ft-error" role="alert">${this.error.message}</div>` : a`<div class="ft-empty">${this.data.length === 0 ? this.emptyMessage : this.noMatchingMessage}</div>`;
|
|
4158
4162
|
return a`
|
|
4159
4163
|
${r}
|
|
4160
4164
|
<div class="ft-header" role="row" aria-rowindex="1" style="width: ${s}px; height: ${i}px;">
|
|
@@ -4162,7 +4166,7 @@ var K = {
|
|
|
4162
4166
|
${h}
|
|
4163
4167
|
${g}
|
|
4164
4168
|
</div>
|
|
4165
|
-
|
|
4169
|
+
${e}
|
|
4166
4170
|
`;
|
|
4167
4171
|
}
|
|
4168
4172
|
let { start: _, end: v } = this.visibleRange, y = this._frozenRowCount, b = [];
|
|
@@ -4449,7 +4453,7 @@ G([c({ type: Array })], Q.prototype, "columns", void 0), G([c({
|
|
|
4449
4453
|
})], Q.prototype, "emptyMessage", void 0), G([c({
|
|
4450
4454
|
type: String,
|
|
4451
4455
|
attribute: "no-matching-message"
|
|
4452
|
-
})], Q.prototype, "noMatchingMessage", void 0), G([c({
|
|
4456
|
+
})], Q.prototype, "noMatchingMessage", void 0), G([c({ attribute: !1 })], Q.prototype, "error", void 0), G([c({
|
|
4453
4457
|
type: Boolean,
|
|
4454
4458
|
reflect: !0
|
|
4455
4459
|
})], Q.prototype, "loading", void 0), G([c({
|
package/dist/flex-table.d.ts
CHANGED
|
@@ -72,6 +72,14 @@ export declare class FlexTable extends LitElement {
|
|
|
72
72
|
* column filters (0 visible rows, non-empty `data`). Default: 'No matching data'.
|
|
73
73
|
*/
|
|
74
74
|
noMatchingMessage: string;
|
|
75
|
+
/**
|
|
76
|
+
* The last load failure, or `null`. While set, the grid shows `error.message` in an alert where rows or the empty
|
|
77
|
+
* state would be — a failed query otherwise looks like "no data". Takes a data source's `error` as is
|
|
78
|
+
* (`useODataSource`/`createODataSource`, a `SourceError`): any `{ message }`.
|
|
79
|
+
*/
|
|
80
|
+
error: {
|
|
81
|
+
message: string;
|
|
82
|
+
} | null;
|
|
75
83
|
/**
|
|
76
84
|
* True일 때 그리드 위에 로딩 오버레이를 표시하고 host에 `aria-busy="true"`를 반영한다.
|
|
77
85
|
* `useODataSource()`가 반환하는 `loading`과 자연 연동하도록 설계됨:
|
package/dist/flex-table.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, t as a } from "./flex-table-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, t as a } from "./flex-table-DlvIBu4z.js";
|
|
2
2
|
import { t as o } from "./locale-B1SiVDS1.js";
|
|
3
3
|
export { a as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, o as flexTableLocale, r as renderCell };
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./flex-table-
|
|
1
|
+
import { t as e } from "./flex-table-DlvIBu4z.js";
|
|
2
2
|
import { a as t, t as n } from "./source-CwbD-6ea.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, useState as c, useSyncExternalStore as l } from "react";
|
package/package.json
CHANGED
|
@@ -126,7 +126,7 @@ Server paging from OData or an in-memory array uses the same binding — see `re
|
|
|
126
126
|
- `--ft-row-height` is read once at first render; change row height later via `rowHeight` / `row-height`.
|
|
127
127
|
- Styles for elements returned by `render` must be passed through the `stylesheets` property
|
|
128
128
|
(constructable `CSSStyleSheet[]`); document CSS does not cross the shadow boundary.
|
|
129
|
-
- Always
|
|
129
|
+
- Always show `error` from `useODataSource` — pass it to the table (`error={source.error}`); a failed request otherwise leaves the grid empty. Branch on `error.status` / `error.code` rather than parsing the message.
|
|
130
130
|
|
|
131
131
|
## References
|
|
132
132
|
|
|
@@ -23,6 +23,7 @@ All names below are members of the `FlexTable` class (`<flex-table>`).
|
|
|
23
23
|
| `footerData` | `footer-data` | `Record<string, string \| TemplateResult> \| null` | `null` | Summary row keyed by column key |
|
|
24
24
|
| `emptyMessage` | `empty-message` | `string` | `'No data'` | |
|
|
25
25
|
| `noMatchingMessage` | `no-matching-message` | `string` | `'No matching data'` | All rows hidden by filters |
|
|
26
|
+
| `error` | — | `{ message: string } \| null` | `null` | Load failure shown as an alert instead of rows / empty state (a source's `error` as is) |
|
|
26
27
|
| `loading` | `loading` | `boolean` | `false` | Loading overlay + `aria-busy` |
|
|
27
28
|
| `importEnabled` | `import-enabled` | `boolean` | `false` | Drag-and-drop `.xlsx` / `.csv` import |
|
|
28
29
|
| `clearUndoOnDataChange` | `clear-undo-on-data-change` | `boolean` | `false` | Replacing `data` clears undo history |
|
|
@@ -71,12 +71,12 @@ function Orders() {
|
|
|
71
71
|
return (
|
|
72
72
|
<>
|
|
73
73
|
<input value={source.search} onChange={(e) => source.setSearch(e.target.value)} />
|
|
74
|
-
{source.error && <p role="alert">{source.error.message}</p>}
|
|
75
74
|
<FlexTableReact<Order>
|
|
76
75
|
dataMode="server"
|
|
77
76
|
columns={columns}
|
|
78
77
|
data={source.data}
|
|
79
78
|
loading={source.loading}
|
|
79
|
+
error={source.error}
|
|
80
80
|
onSortChange={source.onSortChange}
|
|
81
81
|
clearSelectionOnDataChange
|
|
82
82
|
/>
|
|
@@ -109,7 +109,7 @@ function Orders() {
|
|
|
109
109
|
|---|---|
|
|
110
110
|
| `data`, `totalCount` | Current page and `@odata.count`; `@odata.nextLink` is followed to fill a page |
|
|
111
111
|
| `loading` | Request in flight, or no answer yet (true from the first render until the first response settles, and while `enabled: false`) — so `!loading && totalCount === 0` means "no results" |
|
|
112
|
-
| `error` | `SourceError \| null` — `{ message, status?, code?, details?, body?, cause? }`; render `error.message
|
|
112
|
+
| `error` | `SourceError \| null` — `{ message, status?, code?, details?, body?, cause? }`; pass it to the table (`error`) or render `error.message`; branch on `status` / `code` (OData `error.code`). `message` is the server's sentence when it sent one; otherwise this package's, in the `flexTableLocale` language — `requestFailed` (`Request failed ({status})`) when a response came back, `networkFailed` when none did (the transport's exception is in `cause`) |
|
|
113
113
|
| `page`, `setPage` | Zero-based |
|
|
114
114
|
| `sortCriteria`, `onSortChange` | Pass `onSortChange` to the table's `sort-change` (resets to page 0) |
|
|
115
115
|
| `search`, `setSearch` | Literal text (resets to page 0) |
|
|
@@ -170,8 +170,7 @@ class OrdersPage extends LitElement {
|
|
|
170
170
|
private orders = new ODataSourceController<Order>(this, '/api/orders', { pageSize: 20 });
|
|
171
171
|
render() {
|
|
172
172
|
const { data, loading, error } = this.orders.state;
|
|
173
|
-
return html
|
|
174
|
-
<flex-table data-mode="server" .data=${data} .loading=${loading}
|
|
173
|
+
return html`<flex-table data-mode="server" .data=${data} .loading=${loading} .error=${error}
|
|
175
174
|
@sort-change=${(e: CustomEvent) => this.orders.source.setSort(e.detail.criteria)}></flex-table>`;
|
|
176
175
|
}
|
|
177
176
|
}
|