@mono-lit/helper 0.0.1 → 0.0.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.
Files changed (51) hide show
  1. package/LICENSE.md +55 -55
  2. package/README.md +35 -35
  3. package/dist/data-source-controller-B_rgHCjw.js +241 -0
  4. package/dist/{data-source-controller-C50Qyfzh.js → data-source-controller-Dbb4fd8I.js} +69 -6
  5. package/dist/{data-source-options-BTpfrOZ_.js → data-source-options-CP-X62Pa.js} +2 -2
  6. package/dist/{data-source-options-CQSTZHPE.js → data-source-options-Jqj7LU3Z.js} +1 -1
  7. package/dist/{data-source-read-CUX-lgrQ.js → data-source-read-B4DGp6UQ.js} +1 -1
  8. package/dist/{export-BvlBx_GZ.js → export-BiGVyxt6.js} +2 -2
  9. package/dist/{field-icons-BoOG6KrL.js → field-icons-DELrSdqJ.js} +36 -1
  10. package/dist/index.js +7 -7
  11. package/dist/{index.node-DjvYOL-J.js → index.node-BIqEGehH.js} +5 -4
  12. package/dist/index.node.js +7 -7
  13. package/dist/{mono-button-CoVnBLav.js → mono-button-simcTQdW.js} +2 -2
  14. package/dist/{mono-button.shadow-FfP_hMH4.js → mono-button.shadow-B_rKOY9N.js} +2 -2
  15. package/dist/{mono-data-chart-ChU4Ii3p.js → mono-data-chart-CEv29_NS.js} +53 -12
  16. package/dist/{mono-data-dropdown-BAUl58kW.js → mono-data-dropdown-Wnoke6zO.js} +1 -1
  17. package/dist/{mono-data-grid-CLBK8clE.js → mono-data-grid-CjkEqxgE.js} +5 -232
  18. package/dist/{mono-data-grid-Db80FzdZ.js → mono-data-grid-YjR3n9Ar.js} +3 -230
  19. package/dist/{data-search-Bwo5bg8K.js → normalize-error-Gcgmgh5i.js} +299 -1
  20. package/dist/{reactive-BHsqVjRh.js → reactive-Di3mTMnV.js} +299 -1
  21. package/dist/src/components/card/card-core.d.ts +1 -0
  22. package/dist/src/components/chart/chart-types.d.ts +19 -0
  23. package/dist/src/components/select/select-types.d.ts +26 -0
  24. package/dist/src/components/tag-input/tag-input-types.d.ts +26 -0
  25. package/dist/src/composables/data-source-controller.d.ts +29 -0
  26. package/dist/src/composables/field-icons.d.ts +8 -0
  27. package/dist/src/utils/normalize-error.d.ts +30 -0
  28. package/dist/{tooltip-CysgRgBi.js → tooltip-DQqNpqk2.js} +62 -27
  29. package/dist/tooltip.js +1 -1
  30. package/dist/ui/alert.js +1 -1
  31. package/dist/ui/button-dropdown.js +1 -1
  32. package/dist/ui/button.js +1 -1
  33. package/dist/ui/card.js +15 -2
  34. package/dist/ui/chart.js +81 -4
  35. package/dist/ui/dropdown-table.js +2 -2
  36. package/dist/ui/index.css +181 -18
  37. package/dist/ui/modal.js +3 -2
  38. package/dist/ui/select.js +153 -19
  39. package/dist/ui/shadow/alert.js +1 -1
  40. package/dist/ui/shadow/button-dropdown.js +1 -1
  41. package/dist/ui/shadow/button.js +1 -1
  42. package/dist/ui/shadow/card.js +16 -2
  43. package/dist/ui/shadow/chart.js +133 -17
  44. package/dist/ui/shadow/dropdown-table.js +2 -2
  45. package/dist/ui/shadow/modal.js +3 -2
  46. package/dist/ui/shadow/select.js +153 -19
  47. package/dist/ui/shadow/table.js +4 -4
  48. package/dist/ui/shadow/tag-input.js +154 -26
  49. package/dist/ui/table.js +3 -3
  50. package/dist/ui/tag-input.js +153 -25
  51. package/package.json +1 -1
package/LICENSE.md CHANGED
@@ -1,55 +1,55 @@
1
- # MIT License
2
-
3
- Copyright (c) 2026 YCEJ9999
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
-
23
- ---
24
-
25
- ## Third-party software
26
-
27
- The MIT License above covers only the code written for this package. It does
28
- **not** cover third-party packages these libraries depend on or work with. Each of
29
- those keeps its own license.
30
-
31
- ### DevExtreme
32
-
33
- **DevExtreme** is a product of **Developer Express Inc. ("DevExpress")** and is
34
- licensed by DevExpress under its own terms, not by the copyright holder of this
35
- package. `@mono-lit/helper` does not include or depend on DevExtreme. It can work with
36
- DevExtreme data sources that your application supplies (for example from
37
- `@mono-lit/devextreme`). If you use DevExtreme with it:
38
-
39
- - this package does **not** provide, sublicense, transfer or grant any DevExtreme
40
- license;
41
- - you are responsible for obtaining a valid DevExtreme license from DevExpress and
42
- for complying with its End User License Agreement;
43
- - DevExpress's license terms, not this file, govern your use of DevExtreme.
44
-
45
- Official DevExpress licensing information:
46
-
47
- - DevExtreme licensing: https://js.devexpress.com/Licensing/
48
- - DevExtreme EULA: https://js.devexpress.com/EULAs/DevExtremeComplete/
49
-
50
- For setting up your license key, see
51
- [../devextreme/README.md](../devextreme/README.md).
52
-
53
- "DevExpress" and "DevExtreme" are trademarks or registered trademarks of
54
- Developer Express Inc. This project is not affiliated with or endorsed by
55
- DevExpress.
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 YCEJ9999
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ ## Third-party software
26
+
27
+ The MIT License above covers only the code written for this package. It does
28
+ **not** cover third-party packages these libraries depend on or work with. Each of
29
+ those keeps its own license.
30
+
31
+ ### DevExtreme
32
+
33
+ **DevExtreme** is a product of **Developer Express Inc. ("DevExpress")** and is
34
+ licensed by DevExpress under its own terms, not by the copyright holder of this
35
+ package. `@mono-lit/helper` does not include or depend on DevExtreme. It can work with
36
+ DevExtreme data sources that your application supplies (for example from
37
+ `@mono-lit/devextreme`). If you use DevExtreme with it:
38
+
39
+ - this package does **not** provide, sublicense, transfer or grant any DevExtreme
40
+ license;
41
+ - you are responsible for obtaining a valid DevExtreme license from DevExpress and
42
+ for complying with its End User License Agreement;
43
+ - DevExpress's license terms, not this file, govern your use of DevExtreme.
44
+
45
+ Official DevExpress licensing information:
46
+
47
+ - DevExtreme licensing: https://js.devexpress.com/Licensing/
48
+ - DevExtreme EULA: https://js.devexpress.com/EULAs/DevExtremeComplete/
49
+
50
+ For setting up your license key, see
51
+ [../devextreme/README.md](../devextreme/README.md).
52
+
53
+ "DevExpress" and "DevExtreme" are trademarks or registered trademarks of
54
+ Developer Express Inc. This project is not affiliated with or endorsed by
55
+ DevExpress.
package/README.md CHANGED
@@ -1,35 +1,35 @@
1
- # @mono-lit/helper
2
-
3
- Lit web components (tables, forms, inputs, dialogs, charts, …) for Vue and Nuxt. Every component ships a light-DOM build (`@mono-lit/helper/ui/<name>`) and a shadow-DOM build (`@mono-lit/helper/ui/shadow/<name>`).
4
-
5
- ## Installation
6
-
7
- **From npmjs (public):**
8
-
9
- ```bash
10
- pnpm add @mono-lit/helper
11
- ```
12
-
13
- **From the Netlify registry:** add this to your project's `.npmrc`, then run the same `pnpm add`:
14
-
15
- ```ini
16
- @mono-lit:registry=https://mono-libs.netlify.app/npm/
17
- //mono-libs.netlify.app/npm/:_authToken=<REGISTRY_DOWNLOAD_TOKEN>
18
- ```
19
-
20
- Optional peers are only needed for the features that use them: `chart.js` (chart), `suneditor` (rich-text editor), `exceljs` / `handlebars` (table export), plus `@floating-ui/dom`, `vue`, `vite` and `@nuxt/kit` for their integrations.
21
-
22
- ## Usage
23
-
24
- ```ts
25
- import '@mono-lit/helper/index.css'
26
- import '@mono-lit/helper/ui/button'
27
- ```
28
-
29
- ```html
30
- <mono-button>Save</mono-button>
31
- ```
32
-
33
- ## License
34
-
35
- MIT. See the [repository](https://github.com/mono-lit/core) for full documentation.
1
+ # @mono-lit/helper
2
+
3
+ Lit web components (tables, forms, inputs, dialogs, charts, …) for Vue and Nuxt. Every component ships a light-DOM build (`@mono-lit/helper/ui/<name>`) and a shadow-DOM build (`@mono-lit/helper/ui/shadow/<name>`).
4
+
5
+ ## Installation
6
+
7
+ **From npmjs (public):**
8
+
9
+ ```bash
10
+ pnpm add @mono-lit/helper
11
+ ```
12
+
13
+ **From the Netlify registry:** add this to your project's `.npmrc`, then run the same `pnpm add`:
14
+
15
+ ```ini
16
+ @mono-lit:registry=https://mono-libs.netlify.app/npm/
17
+ //mono-libs.netlify.app/npm/:_authToken=<REGISTRY_DOWNLOAD_TOKEN>
18
+ ```
19
+
20
+ Optional peers are only needed for the features that use them: `chart.js` (chart), `suneditor` (rich-text editor), `exceljs` / `handlebars` (table export), plus `@floating-ui/dom`, `vue`, `vite` and `@nuxt/kit` for their integrations.
21
+
22
+ ## Usage
23
+
24
+ ```ts
25
+ import '@mono-lit/helper/index.css'
26
+ import '@mono-lit/helper/ui/button'
27
+ ```
28
+
29
+ ```html
30
+ <mono-button>Save</mono-button>
31
+ ```
32
+
33
+ ## License
34
+
35
+ MIT. See the [repository](https://github.com/mono-lit/core) for full documentation.
@@ -0,0 +1,241 @@
1
+ import { n as describeError, o as isCanceled } from "./normalize-error-Gcgmgh5i.js";
2
+ //#region src/composables/data-source-controller.ts
3
+ /**
4
+ * Encapsulates the generic DataSource + incremental-loading logic shared by
5
+ * `mono-select`, `mono-tag-input`, etc. The host owns the rendering and any
6
+ * scroll/height ergonomics; this controller owns binding, event wiring, page
7
+ * accumulation, and array-mode chunking.
8
+ *
9
+ * Every state mutation calls `host.requestUpdate()` so the host re-renders.
10
+ */
11
+ var DataSourceController = class {
12
+ constructor(host, opts) {
13
+ this._dsItems = [];
14
+ this._dsLoading = false;
15
+ this._loadingMore = false;
16
+ this._isLastPage = false;
17
+ this._error = null;
18
+ this._hasLoaded = false;
19
+ this._retrying = false;
20
+ this._internalLoad = false;
21
+ this._bound = null;
22
+ this._onChanged = () => {
23
+ if (this._internalLoad) return;
24
+ this._markLoaded();
25
+ if (this.opts.getLoadMoreMode()) {
26
+ const ds = this._bound;
27
+ this._dsItems = ds ? [...ds.items?.() ?? []] : [];
28
+ this._isLastPage = ds?.isLastPage?.() ?? false;
29
+ this._dsLoading = ds?.isLoading?.() ?? false;
30
+ this._changed();
31
+ return;
32
+ }
33
+ this._sync();
34
+ };
35
+ this._onLoadingChanged = () => {
36
+ this._dsLoading = this._bound?.isLoading?.() ?? false;
37
+ this._changed();
38
+ };
39
+ this._onLoadError = (err) => {
40
+ this._dsLoading = this._bound?.isLoading?.() ?? false;
41
+ this.setError(err ?? /* @__PURE__ */ new Error("Request failed"));
42
+ };
43
+ this.host = host;
44
+ this.opts = opts;
45
+ this._visibleCount = opts.getPageSize();
46
+ host.addController(this);
47
+ }
48
+ hostConnected() {
49
+ const ds = this.opts.getDataSource();
50
+ if (ds && this._bound !== ds) this.bind(ds);
51
+ }
52
+ hostDisconnected() {
53
+ this._detach();
54
+ }
55
+ /** Attach/detach listeners for a (possibly new) DataSource and seed state. */
56
+ bind(ds) {
57
+ if (this._bound === ds) return;
58
+ this._detach();
59
+ this._bound = ds;
60
+ this._error = null;
61
+ this._retrying = false;
62
+ this._hasLoaded = !!ds && (ds.items?.() ?? []).length > 0;
63
+ if (!ds) {
64
+ this._dsItems = [];
65
+ this._dsLoading = false;
66
+ this._isLastPage = false;
67
+ this._changed();
68
+ return;
69
+ }
70
+ ds.on("changed", this._onChanged);
71
+ ds.on("loadingChanged", this._onLoadingChanged);
72
+ ds.on("loadError", this._onLoadError);
73
+ this._sync();
74
+ this.maybeImmediateLoad();
75
+ }
76
+ maybeImmediateLoad() {
77
+ const ds = this._bound;
78
+ if (!ds || !this.opts.getImmediate()) return;
79
+ const hasItems = (ds.items?.() ?? []).length > 0;
80
+ const loading = ds.isLoading?.() ?? false;
81
+ if (hasItems || loading) return;
82
+ this._loadFirstPage();
83
+ }
84
+ /** Call when the load-more mode changes; refreshes paging assumptions. */
85
+ onLoadMoreModeChange() {
86
+ if (!this.opts.getLoadMoreMode()) return;
87
+ this._bound?.paginate?.(true);
88
+ this._isLastPage = this._bound?.isLastPage?.() ?? false;
89
+ this._changed();
90
+ }
91
+ /** Reset array-mode paging to the first chunk. */
92
+ resetVisible() {
93
+ this._visibleCount = this.opts.getPageSize();
94
+ this._changed();
95
+ }
96
+ /** Load/reveal the next page and append it to the accumulated list. */
97
+ async loadMore() {
98
+ if (!this.opts.getLoadMoreMode() || this.atLastPage) return;
99
+ if (this.isArrayLoadMore) {
100
+ this._visibleCount = Math.min(this.opts.getItems().length, this._visibleCount + this.opts.getPageSize());
101
+ this._changed();
102
+ return;
103
+ }
104
+ const ds = this._bound;
105
+ if (!ds || this._loadingMore || this._dsLoading) return;
106
+ if (typeof ds.pageIndex !== "function") return;
107
+ const current = ds.pageIndex() ?? 0;
108
+ this._internalLoad = true;
109
+ this._loadingMore = true;
110
+ this._changed();
111
+ try {
112
+ ds.paginate?.(true);
113
+ ds.pageIndex(current + 1);
114
+ await Promise.resolve(ds.load());
115
+ this._dsItems = [...this._dsItems, ...ds.items?.() ?? []];
116
+ this._isLastPage = ds.isLastPage?.() ?? false;
117
+ this._error = null;
118
+ } catch (err) {
119
+ ds.pageIndex(current);
120
+ this.setError(err);
121
+ } finally {
122
+ this._loadingMore = false;
123
+ this._internalLoad = false;
124
+ this._changed();
125
+ }
126
+ }
127
+ /**
128
+ * Re-run the request after a failure. Reloads the source as it stands, so a
129
+ * failed search is retried with the same query. While it runs the field is
130
+ * {@link busy} again.
131
+ *
132
+ * `load` replaces the plain `ds.load()` — a host whose failed request was a
133
+ * search passes one that clears the query on the source and reloads.
134
+ */
135
+ async retry(load) {
136
+ if (!this._bound) return;
137
+ this._error = null;
138
+ this._retrying = true;
139
+ this._changed();
140
+ try {
141
+ await this._loadFirstPage(load);
142
+ } finally {
143
+ this._retrying = false;
144
+ this._changed();
145
+ }
146
+ }
147
+ /**
148
+ * Record a failure from a request the host drove itself (e.g. a server
149
+ * select-all drain). Cancelled loads are ignored.
150
+ */
151
+ setError(raw) {
152
+ if (isCanceled(raw)) return;
153
+ this._error = describeError(raw);
154
+ this._changed();
155
+ }
156
+ /** Raw source array: accumulated DataSource items, or the plain `items`. */
157
+ get sourceItems() {
158
+ return this._bound ? this._dsItems : this.opts.getItems();
159
+ }
160
+ /** Rows to actually render (array-mode reveals a slice; else everything). */
161
+ get visibleItems() {
162
+ if (this.isArrayLoadMore) return this.opts.getItems().slice(0, this._visibleCount);
163
+ return this.sourceItems;
164
+ }
165
+ get loading() {
166
+ return this._dsLoading;
167
+ }
168
+ get loadingMore() {
169
+ return this._loadingMore;
170
+ }
171
+ /**
172
+ * The field must be locked: the FIRST load (or a retry) is in flight. A
173
+ * search / filter reload after the source has answered once does not count —
174
+ * the person is typing into the field and it must stay usable.
175
+ */
176
+ get busy() {
177
+ return !!this._bound && (this._dsLoading && !this._hasLoaded || this._retrying);
178
+ }
179
+ /** The last load failure, described for a person; null once a load succeeds. */
180
+ get error() {
181
+ return this._error;
182
+ }
183
+ /** load-more is driving a plain `items` array (no DataSource bound). */
184
+ get isArrayLoadMore() {
185
+ return !this.opts.getDataSource() && !!this.opts.getLoadMoreMode();
186
+ }
187
+ /** Whether there's nothing left to load/reveal. */
188
+ get atLastPage() {
189
+ if (this.isArrayLoadMore) return this._visibleCount >= this.opts.getItems().length;
190
+ return this._isLastPage;
191
+ }
192
+ /** A load answered: the source is usable and any earlier failure is over. */
193
+ _markLoaded() {
194
+ this._hasLoaded = true;
195
+ this._error = null;
196
+ }
197
+ _sync() {
198
+ const ds = this._bound;
199
+ this._dsItems = ds ? [...ds.items?.() ?? []] : [];
200
+ this._dsLoading = ds?.isLoading?.() ?? false;
201
+ this._isLastPage = ds?.isLastPage?.() ?? false;
202
+ this._changed();
203
+ }
204
+ /** Load the first page, replacing the accumulated list. */
205
+ async _loadFirstPage(load) {
206
+ const ds = this._bound;
207
+ if (!ds) return;
208
+ this._internalLoad = true;
209
+ this._dsLoading = true;
210
+ this._changed();
211
+ try {
212
+ if (this.opts.getLoadMoreMode()) {
213
+ ds.paginate?.(true);
214
+ ds.pageIndex?.(0);
215
+ }
216
+ await Promise.resolve(load ? load() : ds.load());
217
+ this._dsItems = [...ds.items?.() ?? []];
218
+ this._isLastPage = ds.isLastPage?.() ?? false;
219
+ this._markLoaded();
220
+ } catch (err) {
221
+ this.setError(err);
222
+ } finally {
223
+ this._dsLoading = ds.isLoading?.() ?? false;
224
+ this._internalLoad = false;
225
+ this._changed();
226
+ }
227
+ }
228
+ _detach() {
229
+ const ds = this._bound;
230
+ if (!ds) return;
231
+ ds.off("changed", this._onChanged);
232
+ ds.off("loadingChanged", this._onLoadingChanged);
233
+ ds.off("loadError", this._onLoadError);
234
+ this._bound = null;
235
+ }
236
+ _changed() {
237
+ this.host.requestUpdate();
238
+ }
239
+ };
240
+ //#endregion
241
+ export { DataSourceController as t };
@@ -1,3 +1,4 @@
1
+ import { a as describeError, l as isCanceled } from "./reactive-Di3mTMnV.js";
1
2
  //#region src/composables/data-source-controller.ts
2
3
  /**
3
4
  * Encapsulates the generic DataSource + incremental-loading logic shared by
@@ -13,10 +14,14 @@ var DataSourceController = class {
13
14
  this._dsLoading = false;
14
15
  this._loadingMore = false;
15
16
  this._isLastPage = false;
17
+ this._error = null;
18
+ this._hasLoaded = false;
19
+ this._retrying = false;
16
20
  this._internalLoad = false;
17
21
  this._bound = null;
18
22
  this._onChanged = () => {
19
23
  if (this._internalLoad) return;
24
+ this._markLoaded();
20
25
  if (this.opts.getLoadMoreMode()) {
21
26
  const ds = this._bound;
22
27
  this._dsItems = ds ? [...ds.items?.() ?? []] : [];
@@ -31,6 +36,10 @@ var DataSourceController = class {
31
36
  this._dsLoading = this._bound?.isLoading?.() ?? false;
32
37
  this._changed();
33
38
  };
39
+ this._onLoadError = (err) => {
40
+ this._dsLoading = this._bound?.isLoading?.() ?? false;
41
+ this.setError(err ?? /* @__PURE__ */ new Error("Request failed"));
42
+ };
34
43
  this.host = host;
35
44
  this.opts = opts;
36
45
  this._visibleCount = opts.getPageSize();
@@ -48,6 +57,9 @@ var DataSourceController = class {
48
57
  if (this._bound === ds) return;
49
58
  this._detach();
50
59
  this._bound = ds;
60
+ this._error = null;
61
+ this._retrying = false;
62
+ this._hasLoaded = !!ds && (ds.items?.() ?? []).length > 0;
51
63
  if (!ds) {
52
64
  this._dsItems = [];
53
65
  this._dsLoading = false;
@@ -57,7 +69,7 @@ var DataSourceController = class {
57
69
  }
58
70
  ds.on("changed", this._onChanged);
59
71
  ds.on("loadingChanged", this._onLoadingChanged);
60
- ds.on("loadError", this._onLoadingChanged);
72
+ ds.on("loadError", this._onLoadError);
61
73
  this._sync();
62
74
  this.maybeImmediateLoad();
63
75
  }
@@ -102,14 +114,45 @@ var DataSourceController = class {
102
114
  await Promise.resolve(ds.load());
103
115
  this._dsItems = [...this._dsItems, ...ds.items?.() ?? []];
104
116
  this._isLastPage = ds.isLastPage?.() ?? false;
105
- } catch {
117
+ this._error = null;
118
+ } catch (err) {
106
119
  ds.pageIndex(current);
120
+ this.setError(err);
107
121
  } finally {
108
122
  this._loadingMore = false;
109
123
  this._internalLoad = false;
110
124
  this._changed();
111
125
  }
112
126
  }
127
+ /**
128
+ * Re-run the request after a failure. Reloads the source as it stands, so a
129
+ * failed search is retried with the same query. While it runs the field is
130
+ * {@link busy} again.
131
+ *
132
+ * `load` replaces the plain `ds.load()` — a host whose failed request was a
133
+ * search passes one that clears the query on the source and reloads.
134
+ */
135
+ async retry(load) {
136
+ if (!this._bound) return;
137
+ this._error = null;
138
+ this._retrying = true;
139
+ this._changed();
140
+ try {
141
+ await this._loadFirstPage(load);
142
+ } finally {
143
+ this._retrying = false;
144
+ this._changed();
145
+ }
146
+ }
147
+ /**
148
+ * Record a failure from a request the host drove itself (e.g. a server
149
+ * select-all drain). Cancelled loads are ignored.
150
+ */
151
+ setError(raw) {
152
+ if (isCanceled(raw)) return;
153
+ this._error = describeError(raw);
154
+ this._changed();
155
+ }
113
156
  /** Raw source array: accumulated DataSource items, or the plain `items`. */
114
157
  get sourceItems() {
115
158
  return this._bound ? this._dsItems : this.opts.getItems();
@@ -125,6 +168,18 @@ var DataSourceController = class {
125
168
  get loadingMore() {
126
169
  return this._loadingMore;
127
170
  }
171
+ /**
172
+ * The field must be locked: the FIRST load (or a retry) is in flight. A
173
+ * search / filter reload after the source has answered once does not count —
174
+ * the person is typing into the field and it must stay usable.
175
+ */
176
+ get busy() {
177
+ return !!this._bound && (this._dsLoading && !this._hasLoaded || this._retrying);
178
+ }
179
+ /** The last load failure, described for a person; null once a load succeeds. */
180
+ get error() {
181
+ return this._error;
182
+ }
128
183
  /** load-more is driving a plain `items` array (no DataSource bound). */
129
184
  get isArrayLoadMore() {
130
185
  return !this.opts.getDataSource() && !!this.opts.getLoadMoreMode();
@@ -134,6 +189,11 @@ var DataSourceController = class {
134
189
  if (this.isArrayLoadMore) return this._visibleCount >= this.opts.getItems().length;
135
190
  return this._isLastPage;
136
191
  }
192
+ /** A load answered: the source is usable and any earlier failure is over. */
193
+ _markLoaded() {
194
+ this._hasLoaded = true;
195
+ this._error = null;
196
+ }
137
197
  _sync() {
138
198
  const ds = this._bound;
139
199
  this._dsItems = ds ? [...ds.items?.() ?? []] : [];
@@ -142,7 +202,7 @@ var DataSourceController = class {
142
202
  this._changed();
143
203
  }
144
204
  /** Load the first page, replacing the accumulated list. */
145
- async _loadFirstPage() {
205
+ async _loadFirstPage(load) {
146
206
  const ds = this._bound;
147
207
  if (!ds) return;
148
208
  this._internalLoad = true;
@@ -153,10 +213,13 @@ var DataSourceController = class {
153
213
  ds.paginate?.(true);
154
214
  ds.pageIndex?.(0);
155
215
  }
156
- await Promise.resolve(ds.load());
216
+ await Promise.resolve(load ? load() : ds.load());
157
217
  this._dsItems = [...ds.items?.() ?? []];
158
218
  this._isLastPage = ds.isLastPage?.() ?? false;
159
- } catch {} finally {
219
+ this._markLoaded();
220
+ } catch (err) {
221
+ this.setError(err);
222
+ } finally {
160
223
  this._dsLoading = ds.isLoading?.() ?? false;
161
224
  this._internalLoad = false;
162
225
  this._changed();
@@ -167,7 +230,7 @@ var DataSourceController = class {
167
230
  if (!ds) return;
168
231
  ds.off("changed", this._onChanged);
169
232
  ds.off("loadingChanged", this._onLoadingChanged);
170
- ds.off("loadError", this._onLoadingChanged);
233
+ ds.off("loadError", this._onLoadError);
171
234
  this._bound = null;
172
235
  }
173
236
  _changed() {
@@ -1,6 +1,6 @@
1
1
  import { n as isPath, r as mergePatch, t as getFieldValue } from "./field-path-C92eGLg3.js";
2
- import { b as andPredicates, o as mergeSearchFields, t as resolveMaybeReactive, x as compileFilterPredicate, y as andFilters } from "./reactive-BHsqVjRh.js";
3
- import { i as searchFilterOf, s as flattenLeaves, t as isDataSourceLike } from "./data-source-read-CUX-lgrQ.js";
2
+ import { A as compileFilterPredicate, O as andFilters, h as mergeSearchFields, k as andPredicates, t as resolveMaybeReactive } from "./reactive-Di3mTMnV.js";
3
+ import { i as searchFilterOf, s as flattenLeaves, t as isDataSourceLike } from "./data-source-read-B4DGp6UQ.js";
4
4
  import { s as odataStringToArray, t as arrayToODataString } from "./filter-odata-C1vSGZaY.js";
5
5
  //#region src/components/table/array-source.ts
6
6
  /** Normalise a possibly-grouped input array to flat leaf rows when grouping. */
@@ -1,7 +1,7 @@
1
1
  import { n as isPath, r as mergePatch, t as getFieldValue } from "./field-path-C92eGLg3.js";
2
2
  import { i as searchFilterOf, s as flattenLeaves, t as isDataSourceLike } from "./data-source-read-DL88aH8t.js";
3
3
  import { n as andPredicates, r as compileFilterPredicate, t as andFilters } from "./filter-eval-DIfU2EAn.js";
4
- import { o as mergeSearchFields } from "./data-search-Bwo5bg8K.js";
4
+ import { h as mergeSearchFields } from "./normalize-error-Gcgmgh5i.js";
5
5
  import { t as resolveMaybeReactive } from "./reactive-D3EERqgO.js";
6
6
  import { s as odataStringToArray, t as arrayToODataString } from "./filter-odata-C1vSGZaY.js";
7
7
  //#region src/components/table/array-source.ts
@@ -1,5 +1,5 @@
1
1
  import { c as toODataClause, n as isPath, t as getFieldValue } from "./field-path-C92eGLg3.js";
2
- import { n as unwrapReactive, y as andFilters } from "./reactive-BHsqVjRh.js";
2
+ import { O as andFilters, n as unwrapReactive } from "./reactive-Di3mTMnV.js";
3
3
  //#region src/components/table/grouping.ts
4
4
  /**
5
5
  * Client-side grouping helpers shared by {@link monoArraySource} and
@@ -1,6 +1,6 @@
1
1
  import { a as stripDirectives, d as __toESM, i as escapeCellText, n as parseMarkdown, r as encodeDirective, u as __commonJSMin } from "./markdown-DfqrfOJN.js";
2
- import { n as unwrapReactive } from "./reactive-BHsqVjRh.js";
3
- import { n as isReadableSource, r as readAllRows } from "./data-source-read-CUX-lgrQ.js";
2
+ import { n as unwrapReactive } from "./reactive-Di3mTMnV.js";
3
+ import { n as isReadableSource, r as readAllRows } from "./data-source-read-B4DGp6UQ.js";
4
4
  //#region src/export/download.ts
5
5
  /**
6
6
  * Browser download helper. Kept separate (and DOM-guarded) so the report engine
@@ -47,5 +47,40 @@ function chevronIcon(dir) {
47
47
  <path fill-rule="evenodd" d=${dir === -1 ? "M12.79 5.23a.75.75 0 0 1-.02 1.06L9.06 10l3.71 3.71a.75.75 0 1 1-1.06 1.06l-4.24-4.24a.75.75 0 0 1 0-1.06l4.24-4.24a.75.75 0 0 1 1.08 0z" : "M7.21 14.77a.75.75 0 0 1 .02-1.06L10.94 10 7.23 6.29a.75.75 0 1 1 1.06-1.06l4.24 4.24a.75.75 0 0 1 0 1.06l-4.24 4.24a.75.75 0 0 1-1.08 0z"} clip-rule="evenodd" />
48
48
  </svg>`;
49
49
  }
50
+ /**
51
+ * Busy arc for a field whose data source is still answering. Spun by the
52
+ * component's own CSS (`[mono-ds-spinner] > svg`) so a reduced-motion rule can
53
+ * stop it.
54
+ */
55
+ function spinnerIcon() {
56
+ return html`<svg
57
+ mono-icon
58
+ viewBox="0 0 24 24"
59
+ width="16"
60
+ height="16"
61
+ fill="none"
62
+ stroke="currentColor"
63
+ stroke-width="2.5"
64
+ stroke-linecap="round"
65
+ aria-hidden="true"
66
+ >
67
+ <path d="M12 3a9 9 0 1 0 9 9" />
68
+ </svg>`;
69
+ }
70
+ /** ↻ — the retry affordance a field shows after its data source failed. */
71
+ function refreshIcon() {
72
+ return html`<svg
73
+ mono-icon
74
+ viewBox="0 0 24 24"
75
+ width="16"
76
+ height="16"
77
+ fill="currentColor"
78
+ aria-hidden="true"
79
+ >
80
+ <path
81
+ d="M17.65 6.35A8 8 0 1 0 19.73 14h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"
82
+ />
83
+ </svg>`;
84
+ }
50
85
  //#endregion
51
- export { chevronIcon as n, closeIcon as r, caretIcon as t };
86
+ export { spinnerIcon as a, refreshIcon as i, chevronIcon as n, closeIcon as r, caretIcon as t };