@optionfactory/ful 4.0.16 → 5.0.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/dist/ful.mjs CHANGED
@@ -460,7 +460,7 @@ class HttpRequestBuilder {
460
460
  */
461
461
  headers(hs) {
462
462
  for (const [k, v] of new Headers(hs).entries()) {
463
- if (v === null || v === undefined) {
463
+ if (v == null) {
464
464
  this.#headers.delete(k);
465
465
  } else {
466
466
  this.#headers.set(k, v);
@@ -475,7 +475,7 @@ class HttpRequestBuilder {
475
475
  * @returns {HttpRequestBuilder} this builder
476
476
  */
477
477
  header(k, v) {
478
- if (v === null || v === undefined) {
478
+ if (v == null) {
479
479
  this.#headers.delete(k);
480
480
  } else {
481
481
  this.#headers.set(k, v);
@@ -489,7 +489,7 @@ class HttpRequestBuilder {
489
489
  */
490
490
  params(ps) {
491
491
  for (const [k, v] of new URLSearchParams(ps).entries()) {
492
- if (v === null || v === undefined) {
492
+ if (v == null) {
493
493
  this.#params.delete(k);
494
494
  } else {
495
495
  this.#params.set(k, v);
@@ -504,7 +504,7 @@ class HttpRequestBuilder {
504
504
  * @returns {HttpRequestBuilder} this builder
505
505
  */
506
506
  param(k, ...vs) {
507
- if (vs.length === 0 || vs[0] === null || vs[0] === undefined) {
507
+ if (vs.length === 0 || vs[0] == null) {
508
508
  this.#params.delete(k);
509
509
  return this;
510
510
  }
@@ -1135,22 +1135,6 @@ class Timing {
1135
1135
  }
1136
1136
  }
1137
1137
 
1138
- class Loaders {
1139
- static fromAttributes(el, defaultLoader, options) {
1140
- const http = registry.component("http-client");
1141
- const requestMapper = el.hasAttribute("request-mapper") ? registry.component(el.getAttribute("request-mapper")) : v => v;
1142
- const responseMapper = el.hasAttribute("response-mapper") ? registry.component(el.getAttribute("response-mapper")) : v => v;
1143
- const loaderClass = registry.component(el.getAttribute("loader") ?? defaultLoader);
1144
- return loaderClass.create({
1145
- el,
1146
- http,
1147
- requestMapper,
1148
- responseMapper,
1149
- options: options ?? {}
1150
- });
1151
- }
1152
- }
1153
-
1154
1138
  class Bindings {
1155
1139
 
1156
1140
  /**
@@ -1347,7 +1331,10 @@ class LocalFormLoader {
1347
1331
  }
1348
1332
 
1349
1333
  class FormLoader {
1350
- static create({ el, http, requestMapper, responseMapper }) {
1334
+ static create(el, conf) {
1335
+ const http = registry.component("http-client");
1336
+ const requestMapper = el.hasAttribute("request-mapper") ? registry.component(el.getAttribute("request-mapper")) : v => v;
1337
+ const responseMapper = el.hasAttribute("response-mapper") ? registry.component(el.getAttribute("response-mapper")) : v => v;
1351
1338
  const url = el.getAttribute("action");
1352
1339
  if (!url) {
1353
1340
  return new LocalFormLoader(requestMapper, responseMapper);
@@ -1379,7 +1366,7 @@ class Form extends ParsedElement {
1379
1366
  async submit() {
1380
1367
  this.spinner(true);
1381
1368
  try {
1382
- const loader = Loaders.fromAttributes(this, 'loaders:form');
1369
+ const loader = registry.component(this.getAttribute("loader") ?? 'loaders:form').create(this);
1383
1370
  const values = this.values;
1384
1371
  let request = await loader.prepare(values, this);
1385
1372
  try {
@@ -1430,7 +1417,7 @@ class Form extends ParsedElement {
1430
1417
  }
1431
1418
 
1432
1419
  class Input extends ParsedElement {
1433
- static observed = ['value', 'readonly:presence'];
1420
+ static observed = ['value', 'readonly:presence', 'required:presence'];
1434
1421
  static slots = true;
1435
1422
  static template = `
1436
1423
  <div class="form-label">
@@ -1470,6 +1457,7 @@ class Input extends ParsedElement {
1470
1457
  if (!skipObservedSetup) {
1471
1458
  this.disabled = disabled;
1472
1459
  this.readonly = observed.readonly;
1460
+ this.required = observed.required;
1473
1461
  this.value = observed.value;
1474
1462
  }
1475
1463
 
@@ -1512,6 +1500,15 @@ class Input extends ParsedElement {
1512
1500
  set disabled(d) {
1513
1501
  Attributes.toggle(this._input, 'disabled', d);
1514
1502
  }
1503
+ get required() {
1504
+ return this._input.getAttribute('aria-required') === 'true';
1505
+ }
1506
+ set required(d) {
1507
+ Attributes.set(this._input, "aria-required", d ? "true" : null);
1508
+ this.reflect(() => {
1509
+ Attributes.toggle(this, 'required', d);
1510
+ });
1511
+ }
1515
1512
  focus(options) {
1516
1513
  this._input.focus(options);
1517
1514
  }
@@ -1525,7 +1522,7 @@ class Input extends ParsedElement {
1525
1522
  this._fieldError.innerText = error;
1526
1523
  }
1527
1524
  formResetCallback() {
1528
- this.value = this.getAttribute("value");
1525
+ this.value = this.unmarshal('value', this.getAttribute("value"));
1529
1526
  }
1530
1527
  }
1531
1528
 
@@ -1569,22 +1566,22 @@ class Instant extends ParsedElement {
1569
1566
  const date = `${d.getFullYear()}-${pad(2, d.getMonth() + 1)}-${pad(2, d.getDate())}`;
1570
1567
  const time = `${pad(2, d.getHours())}:${pad(2, d.getMinutes())}:${pad(2, d.getSeconds())}.${pad(3, d.getMilliseconds())}`;
1571
1568
  return `${date}T${time}`
1572
- }
1569
+ }
1573
1570
  }
1574
1571
 
1575
1572
 
1576
1573
  class InputLocalDate extends Input {
1577
- static observed = ['value', 'readonly:presence', 'min', 'max', 'step'];
1574
+ static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
1578
1575
  _type() {
1579
1576
  return 'date';
1580
1577
  }
1581
- render(conf){
1582
- const {observed} = conf;
1578
+ render(conf) {
1579
+ const { observed } = conf;
1583
1580
  super.render(conf);
1584
1581
  this.min = observed.min;
1585
1582
  this.max = observed.max;
1586
1583
  this.step = observed.step;
1587
- }
1584
+ }
1588
1585
  get min() {
1589
1586
  const v = this._input.min;
1590
1587
  return v === '' ? null : v;
@@ -1644,12 +1641,12 @@ class InputLocalTime extends InputLocalDate {
1644
1641
 
1645
1642
 
1646
1643
  class InputInstant extends Input {
1647
- static observed = ['value', 'readonly:presence', 'min', 'max', 'step'];
1644
+ static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
1648
1645
  _type() {
1649
1646
  return 'datetime-local';
1650
1647
  }
1651
- render(conf){
1652
- const {observed} = conf;
1648
+ render(conf) {
1649
+ const { observed } = conf;
1653
1650
  super.render(conf);
1654
1651
  this.min = observed.min;
1655
1652
  this.max = observed.min;
@@ -1700,7 +1697,7 @@ class InputFile extends Input {
1700
1697
  'maxtotalsizeexceeded': "La dimensione massima complessiva dei file è di {0}"
1701
1698
  }
1702
1699
  }
1703
- static observed = ['value', 'readonly:presence', "accept:csv", 'multiple:presence', "itemlist:presence", "dropzone:presence", "maxfilesize:number", "maxtotalsize:number"];
1700
+ static observed = ['value', 'readonly:presence', 'required:presence', "accept:csv", 'multiple:presence', "itemlist:presence", "dropzone:presence", "maxfilesize:number", "maxtotalsize:number"];
1704
1701
  #accept;
1705
1702
  #items;
1706
1703
  #dropzone;
@@ -1926,17 +1923,7 @@ class RemoteLoader {
1926
1923
  #prefetch;
1927
1924
  #revision;
1928
1925
  #data;
1929
- static create({ el, http, responseMapper }) {
1930
- return new RemoteLoader({
1931
- http,
1932
- url: el.getAttribute("src"),
1933
- method: el.getAttribute("method") ?? 'POST',
1934
- responseMapper,
1935
- prefetch: el.hasAttribute("preload"),
1936
- revision: el.getAttribute("revision")
1937
- });
1938
- }
1939
- constructor({http, url, method, responseMapper, prefetch, revision}) {
1926
+ constructor({ http, url, method, responseMapper, prefetch, revision }) {
1940
1927
  this.#http = http;
1941
1928
  this.#url = url;
1942
1929
  this.#method = method;
@@ -1959,7 +1946,7 @@ class RemoteLoader {
1959
1946
  await this.#ensureFetched();
1960
1947
  return this.#data.filter(([k, v]) => (v ?? '').toLowerCase().includes(needle?.toLowerCase()));
1961
1948
  }
1962
- async reconfigureUrl(url){
1949
+ async reconfigureUrl(url) {
1963
1950
  this.#data = null;
1964
1951
  this.#url = url;
1965
1952
  }
@@ -1968,9 +1955,9 @@ class RemoteLoader {
1968
1955
  return
1969
1956
  }
1970
1957
  const storageKey = `${this.#method}@${this.#url}`;
1971
- if(this.#revision !== null){
1958
+ if (this.#revision !== null) {
1972
1959
  const data = VersionedLocalStorage.load(storageKey, this.#revision);
1973
- if(data !== undefined){
1960
+ if (data !== undefined) {
1974
1961
  this.#data = data;
1975
1962
  return;
1976
1963
  }
@@ -1978,7 +1965,7 @@ class RemoteLoader {
1978
1965
  const data = await this.#http.request(this.#method, this.#url)
1979
1966
  .fetchJson();
1980
1967
  this.#data = this.#responseMapper(data);
1981
- if(this.#revision !== null){
1968
+ if (this.#revision !== null) {
1982
1969
  VersionedLocalStorage.save(storageKey, this.#revision, this.#data);
1983
1970
  }
1984
1971
  }
@@ -1989,15 +1976,7 @@ class PartialRemoteLoader {
1989
1976
  #url;
1990
1977
  #method;
1991
1978
  #responseMapper;
1992
- static create({ el, http, responseMapper }) {
1993
- return new PartialRemoteLoader({
1994
- http,
1995
- url: el.getAttribute("src"),
1996
- method: el.getAttribute("method") ?? 'POST',
1997
- responseMapper
1998
- });
1999
- }
2000
- constructor({http, url, method, responseMapper}) {
1979
+ constructor({ http, url, method, responseMapper }) {
2001
1980
  this.#http = http;
2002
1981
  this.#url = url;
2003
1982
  this.#method = method;
@@ -2035,16 +2014,49 @@ class InMemoryLoader {
2035
2014
 
2036
2015
 
2037
2016
  class SelectLoader {
2038
- static create(conf) {
2039
- if (!conf.el.hasAttribute("src")) {
2040
- const els = Array.from(conf.options.options?.querySelectorAll('option') ?? []);
2017
+ static create(el, conf) {
2018
+ if (!el.hasAttribute("src")) {
2019
+ const els = Array.from(conf.options?.querySelectorAll('option') ?? []);
2041
2020
  const data = els.map(e => {
2042
2021
  return [e.getAttribute("value") ?? e.innerText.trim(), e.innerText.trim()];
2043
2022
  });
2044
2023
  return new InMemoryLoader(data);
2045
2024
  }
2046
- const chunked = "chunked" == conf.el.getAttribute("mode");
2047
- return chunked ? PartialRemoteLoader.create(conf) : RemoteLoader.create(conf);
2025
+ const http = registry.component("http-client");
2026
+ const responseMapper = SelectLoader.#responseMapperFrom(el);
2027
+
2028
+ if ("chunked" == el.getAttribute("mode")) {
2029
+ return new PartialRemoteLoader({
2030
+ http,
2031
+ url: el.getAttribute("src"),
2032
+ method: el.getAttribute("method") ?? 'POST',
2033
+ responseMapper
2034
+ })
2035
+ }
2036
+ return new RemoteLoader({
2037
+ http,
2038
+ url: el.getAttribute("src"),
2039
+ method: el.getAttribute("method") ?? 'POST',
2040
+ responseMapper,
2041
+ prefetch: el.hasAttribute("preload"),
2042
+ revision: el.getAttribute("revision")
2043
+ });
2044
+ }
2045
+ static #responseMapperFrom(el) {
2046
+ if (el.hasAttribute("k-expr") && el.hasAttribute("l-expr")) {
2047
+ return v => {
2048
+ const evaluator = registry.evaluator().withOverlay(v);
2049
+ return [
2050
+ evaluator.evaluateExpression(el.getAttribute("k-expr")),
2051
+ evaluator.evaluateExpression(el.getAttribute("l-expr")),
2052
+ evaluator.evaluateExpression(el.getAttribute("m-expr") ?? 'self'),
2053
+ ];
2054
+ };
2055
+ }
2056
+ if (el.hasAttribute("response-mapper")) {
2057
+ return registry.component(el.getAttribute("response-mapper"));
2058
+ }
2059
+ return v => v;
2048
2060
  }
2049
2061
  }
2050
2062
 
@@ -2079,7 +2091,7 @@ class Dropdown extends ParsedElement {
2079
2091
  if (values === undefined) {
2080
2092
  throw new Error("null data");
2081
2093
  }
2082
- this.#options = new Map(values.map((v,i) => [String(i), v]));
2094
+ this.#options = new Map(values.map((v, i) => [String(i), v]));
2083
2095
  if (values.length === 0) {
2084
2096
  const el = document.createElement('div');
2085
2097
  el.classList.add('text-center', 'py-2', 'bi', 'bi-database-slash');
@@ -2131,7 +2143,7 @@ class Dropdown extends ParsedElement {
2131
2143
  if (candidate) {
2132
2144
  selected.removeAttribute('selected');
2133
2145
  candidate.setAttribute("selected", "");
2134
- candidate.scrollIntoView({block: "nearest", behavior: "smooth"});
2146
+ candidate.scrollIntoView({ block: "nearest", behavior: "smooth" });
2135
2147
  }
2136
2148
  return;
2137
2149
  }
@@ -2140,7 +2152,7 @@ class Dropdown extends ParsedElement {
2140
2152
  }
2141
2153
 
2142
2154
  class Select extends ParsedElement {
2143
- static observed = ['value:csvm', 'readonly:presence', 'itemlist:presence']
2155
+ static observed = ['value:csvm', 'readonly:presence', "required:presence", 'itemlist:presence']
2144
2156
  static slots = true
2145
2157
  static template = `
2146
2158
  <div class="form-label">
@@ -2170,15 +2182,7 @@ class Select extends ParsedElement {
2170
2182
  <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg"></button>
2171
2183
  </ful-item>
2172
2184
  `
2173
- }
2174
- static mappers = {
2175
- "csvm": (v, name, el) => {
2176
- if (el.hasAttribute("multiple")) {
2177
- return v === null ? [] : v.split(",").map(e => e.trim()).filter(e => e)
2178
- }
2179
- return v === null || v === '' ? null : v
2180
- }
2181
- };
2185
+ }
2182
2186
  static formAssociated = true
2183
2187
  internals
2184
2188
  #loader
@@ -2196,7 +2200,8 @@ class Select extends ParsedElement {
2196
2200
  }
2197
2201
  async render({ slots, observed, disabled }) {
2198
2202
  const name = this.getAttribute("name");
2199
- this.#loader = Loaders.fromAttributes(this, 'loaders:select', { options: slots.options });
2203
+ this.#loader = registry.component(this.getAttribute("loader") ?? 'loaders:select').create(this, {options: slots.options});
2204
+
2200
2205
  this.#multiple = this.hasAttribute("multiple");
2201
2206
  await this.#loader.prefetch?.();
2202
2207
  const fragment = this.template().withOverlay({ slots, name }).render();
@@ -2208,6 +2213,7 @@ class Select extends ParsedElement {
2208
2213
  this.value = observed.value;
2209
2214
  this.disabled = disabled;
2210
2215
  this.readonly = observed.readonly;
2216
+ this.required = observed.required;
2211
2217
  this.itemlist = observed.itemlist;
2212
2218
 
2213
2219
  this.#ddmenu = fragment.querySelector('ful-dropdown');
@@ -2223,7 +2229,7 @@ class Select extends ParsedElement {
2223
2229
  if (e.target.matches('input')) {
2224
2230
  return;
2225
2231
  }
2226
- if(this.disabled || this.readonly){
2232
+ if (this.disabled || this.readonly) {
2227
2233
  return;
2228
2234
  }
2229
2235
  if (this.#ddmenu.shown) {
@@ -2238,9 +2244,9 @@ class Select extends ParsedElement {
2238
2244
  if (!e.target.closest("button")) {
2239
2245
  return;
2240
2246
  }
2241
- if(this.disabled || this.readonly){
2247
+ if (this.disabled || this.readonly) {
2242
2248
  return;
2243
- }
2249
+ }
2244
2250
  const idx = [...this.#items.children].indexOf(e.target.closest('ful-item'));
2245
2251
  if (idx === -1) {
2246
2252
  return;
@@ -2251,7 +2257,7 @@ class Select extends ParsedElement {
2251
2257
  });
2252
2258
  this.#badges.addEventListener('click', (e) => {
2253
2259
  e.stopPropagation();
2254
- if(this.disabled || this.readonly){
2260
+ if (this.disabled || this.readonly) {
2255
2261
  return;
2256
2262
  }
2257
2263
  const idx = [...this.#badges.children].indexOf(e.target);
@@ -2276,7 +2282,7 @@ class Select extends ParsedElement {
2276
2282
  });
2277
2283
  this.#input.addEventListener('keydown', e => {
2278
2284
  e.stopPropagation();
2279
- if(this.disabled || this.readonly){
2285
+ if (this.disabled || this.readonly) {
2280
2286
  return;
2281
2287
  }
2282
2288
  switch (e.code) {
@@ -2315,7 +2321,7 @@ class Select extends ParsedElement {
2315
2321
  });
2316
2322
  this.#input.addEventListener('input', e => {
2317
2323
  e.stopPropagation();
2318
- if(this.disabled || this.readonly){
2324
+ if (this.disabled || this.readonly) {
2319
2325
  return;
2320
2326
  }
2321
2327
  dload();
@@ -2338,7 +2344,7 @@ class Select extends ParsedElement {
2338
2344
  await fn(this.#loader);
2339
2345
  }
2340
2346
  #changed() {
2341
- const selection = [...this.#values.entries()].map(e => ({key: e[0], label: e[1][0], metadata: e[1].slice(1)}));
2347
+ const selection = [...this.#values.entries()].map(e => ({ key: e[0], label: e[1][0], metadata: e[1].slice(1) }));
2342
2348
  const value = this.#multiple ? selection : (selection[0] ?? null);
2343
2349
  this.dispatchEvent(new CustomEvent('change', {
2344
2350
  bubbles: true,
@@ -2360,10 +2366,10 @@ class Select extends ParsedElement {
2360
2366
  this.template('items').withOverlay({ entries: this.#values.entries() }).renderTo(this.#items);
2361
2367
  }
2362
2368
  set value(vs) {
2363
- if(vs === null){
2369
+ if (vs === null) {
2364
2370
  this.#values = new Map();
2365
2371
  this.#syncBadges();
2366
- return;
2372
+ return;
2367
2373
  }
2368
2374
  (async () => {
2369
2375
  const entries = await (this.#multiple ? this.#loader.exact(...vs) : this.#loader.exact(vs));
@@ -2384,14 +2390,14 @@ class Select extends ParsedElement {
2384
2390
  return [...this.#values.entries()][0] ?? null;
2385
2391
  }
2386
2392
  //@ts-ignore
2387
- get disabled(){
2393
+ get disabled() {
2388
2394
  return this.#input.hasAttribute('disabled');
2389
2395
  }
2390
- set disabled(d){
2396
+ set disabled(d) {
2391
2397
  Attributes.toggle(this.#input, 'disabled', d);
2392
- }
2393
- get readonly(){
2394
- return this.#input.readOnly;
2398
+ }
2399
+ get readonly() {
2400
+ return this.#input.readOnly;
2395
2401
  }
2396
2402
  set readonly(v) {
2397
2403
  this.#input.readOnly = v;
@@ -2399,6 +2405,15 @@ class Select extends ParsedElement {
2399
2405
  Attributes.toggle(this, 'readonly', v);
2400
2406
  });
2401
2407
  }
2408
+ get required() {
2409
+ return this.#input.getAttribute('aria-required') === 'true';
2410
+ }
2411
+ set required(d) {
2412
+ Attributes.set(this.#input, "aria-required", d ? "true" : null);
2413
+ this.reflect(() => {
2414
+ Attributes.toggle(this, 'required', d);
2415
+ });
2416
+ }
2402
2417
  #useItemlist;
2403
2418
  get itemlist() {
2404
2419
  return this.#useItemlist;
@@ -2424,7 +2439,7 @@ class Select extends ParsedElement {
2424
2439
  }
2425
2440
 
2426
2441
  class RadioGroup extends ParsedElement {
2427
- static observed = ['value', 'readonly:presence'];
2442
+ static observed = ['value', 'readonly:presence', "required:presence"];
2428
2443
  static slots = true;
2429
2444
  static template = `
2430
2445
  <fieldset>
@@ -2488,6 +2503,7 @@ class RadioGroup extends ParsedElement {
2488
2503
  this.#fieldset = this.firstElementChild;
2489
2504
  this.disabled = disabled;
2490
2505
  this.readonly = observed.readonly;
2506
+ this.required = observed.required;
2491
2507
  this.value = observed.value;
2492
2508
  this.#fieldError = this.querySelector('ful-field-error');
2493
2509
  this.ariaDescribedByElements = [this.#fieldError];
@@ -2528,6 +2544,15 @@ class RadioGroup extends ParsedElement {
2528
2544
  set disabled(d){
2529
2545
  Attributes.toggle(this.#fieldset, 'disabled', d);
2530
2546
  }
2547
+ get required() {
2548
+ return this.#fieldset.getAttribute('aria-required') === 'true';
2549
+ }
2550
+ set required(d) {
2551
+ Attributes.set(this.#fieldset, "aria-required", d ? "true" : null);
2552
+ this.reflect(() => {
2553
+ Attributes.toggle(this, 'required', d);
2554
+ });
2555
+ }
2531
2556
  focus(options) {
2532
2557
  this.#firstRadio.focus(options);
2533
2558
  }
@@ -2543,7 +2568,7 @@ class RadioGroup extends ParsedElement {
2543
2568
  }
2544
2569
 
2545
2570
  class Checkbox extends ParsedElement {
2546
- static observed = ['value:bool', 'readonly:presence'];
2571
+ static observed = ['value:bool', 'readonly:presence', "required:presence"];
2547
2572
  static slots = true;
2548
2573
  static template = `
2549
2574
  <div data-tpl-class="klass">
@@ -2574,6 +2599,7 @@ class Checkbox extends ParsedElement {
2574
2599
  Attributes.forward('input-', this, this.#input);
2575
2600
  this.disabled = disabled;
2576
2601
  this.readonly = observed.readonly;
2602
+ this.required = observed.required;
2577
2603
  this.value = observed.value;
2578
2604
  this.#input.addEventListener('change', (evt) => {
2579
2605
  evt.stopPropagation();
@@ -2620,6 +2646,15 @@ class Checkbox extends ParsedElement {
2620
2646
  set disabled(d) {
2621
2647
  Attributes.toggle(this.#input, 'disabled', d);
2622
2648
  }
2649
+ get required() {
2650
+ return this.#input.getAttribute('aria-required') === 'true';
2651
+ }
2652
+ set required(d) {
2653
+ Attributes.set(this.#input, "aria-required", d ? "true" : null);
2654
+ this.reflect(() => {
2655
+ Attributes.toggle(this, 'required', d);
2656
+ });
2657
+ }
2623
2658
  focus(options) {
2624
2659
  this.#input.focus(options);
2625
2660
  }
@@ -2867,7 +2902,8 @@ class RemoteTableLoader {
2867
2902
 
2868
2903
 
2869
2904
  class TableLoader {
2870
- static create({ el, http }) {
2905
+ static create(el, conf) {
2906
+ const http = registry.component("http-client");
2871
2907
  const url = el.getAttribute("src");
2872
2908
  const method = el.getAttribute("method") ?? 'GET';
2873
2909
  return new RemoteTableLoader(http, url, method);
@@ -3011,7 +3047,7 @@ class Table extends ParsedElement {
3011
3047
  this.#feedback.setAttribute("hidden", "");
3012
3048
  this.#noAutoload.setAttribute("hidden", "");
3013
3049
  try {
3014
- const loader = Loaders.fromAttributes(this, 'loaders:table');
3050
+ const loader = registry.component(this.getAttribute("loader") ?? 'loaders:table').create(this);
3015
3051
  const pageResponse = await loader.load(pageRequest, sortRequest, filterRequest);
3016
3052
  this.#latestRequest = { pageRequest, sortRequest, filterRequest };
3017
3053
  this.#update(pageRequest, sortRequest, filterRequest, pageResponse);
@@ -3048,7 +3084,7 @@ class Table extends ParsedElement {
3048
3084
  }
3049
3085
 
3050
3086
  class InstantFilter extends Input {
3051
- static observed = ['value:json', 'readonly:presence'];
3087
+ static observed = ['value:json', 'readonly:presence', 'required:presence'];
3052
3088
  static template = `
3053
3089
  <div class="form-label">
3054
3090
  <label>{{{{ slots.default }}}}</label>
@@ -3078,13 +3114,14 @@ class InstantFilter extends Input {
3078
3114
  #value1;
3079
3115
  #value2;
3080
3116
  render(conf) {
3081
- super.render({...conf, skipObservedSetup: true});
3117
+ super.render({ ...conf, skipObservedSetup: true });
3082
3118
  this.#operator = this.querySelector('[data-ref=operator]');
3083
3119
  this.#value1 = this.querySelector('[data-ref=value1]');
3084
3120
  this.#value2 = this.querySelector('[data-ref=value2]');
3085
3121
 
3086
3122
  this.disabled = conf.disabled;
3087
3123
  this.readonly = conf.observed.readonly;
3124
+ this.required = conf.observed.required;
3088
3125
  this.value = conf.observed.value;
3089
3126
 
3090
3127
  this.addEventListener('click', (evt) => {
@@ -3106,7 +3143,7 @@ class InstantFilter extends Input {
3106
3143
  return values.some(v => v === '') ? undefined : [operator, ...values.map(v => new Date(v).toISOString())];
3107
3144
  }
3108
3145
  set value(v) {
3109
- if (v === null || v === undefined) {
3146
+ if (v == null) {
3110
3147
  this.#value1.value = '';
3111
3148
  this.#value2.value = '';
3112
3149
  return;
@@ -3119,20 +3156,15 @@ class InstantFilter extends Input {
3119
3156
  set readonly(v) {
3120
3157
  this.#value2.readOnly = v;
3121
3158
  super.readonly = v;
3122
- }
3159
+ }
3123
3160
  set disabled(d) {
3124
3161
  Attributes.toggle(this.#value2, 'disabled', d);
3125
3162
  super.disabled = d;
3126
3163
  }
3127
- formResetCallback() {
3128
- const v = this.getAttribute("value");
3129
- this.value = v === null ? null : JSON.parse(v);
3130
- }
3131
-
3132
3164
  }
3133
3165
 
3134
3166
  class LocalDateFilter extends Input {
3135
- static observed = ["value:json", 'readonly:presence'];
3167
+ static observed = ["value:json", 'readonly:presence', 'required:presence'];
3136
3168
  static template = `
3137
3169
  <div class="form-label">
3138
3170
  <label>{{{{ slots.default }}}}</label>
@@ -3162,7 +3194,7 @@ class LocalDateFilter extends Input {
3162
3194
  #value1;
3163
3195
  #value2;
3164
3196
  render(conf) {
3165
- super.render({...conf, skipObservedSetup: true});
3197
+ super.render({ ...conf, skipObservedSetup: true });
3166
3198
 
3167
3199
  this.#operator = this.querySelector('[data-ref=operator]');
3168
3200
  this.#value1 = this.querySelector('[data-ref=value1]');
@@ -3170,6 +3202,7 @@ class LocalDateFilter extends Input {
3170
3202
 
3171
3203
  this.disabled = conf.disabled;
3172
3204
  this.readonly = conf.observed.readonly;
3205
+ this.required = conf.observed.required;
3173
3206
  this.value = conf.observed.value;
3174
3207
 
3175
3208
  this.addEventListener('click', (evt) => {
@@ -3190,7 +3223,7 @@ class LocalDateFilter extends Input {
3190
3223
  return values.some(v => v === '') ? undefined : [operator, ...values];
3191
3224
  }
3192
3225
  set value(v) {
3193
- if (v === null || v === undefined) {
3226
+ if (v == null) {
3194
3227
  this.#value1.value = '';
3195
3228
  this.#value2.value = '';
3196
3229
  return;
@@ -3208,14 +3241,10 @@ class LocalDateFilter extends Input {
3208
3241
  Attributes.toggle(this.#value2, 'disabled', d);
3209
3242
  super.disabled = d;
3210
3243
  }
3211
- formResetCallback() {
3212
- const v = this.getAttribute("value");
3213
- this.value = v === null ? null : JSON.parse(v);
3214
- }
3215
3244
  }
3216
3245
 
3217
3246
  class TextFilter extends Input {
3218
- static observed = ["value:json", 'readonly:presence'];
3247
+ static observed = ["value:json", 'readonly:presence', 'required:presence'];
3219
3248
  static template = `
3220
3249
  <div class="form-label">
3221
3250
  <label>{{{{ slots.default }}}}</label>
@@ -3240,13 +3269,14 @@ class TextFilter extends Input {
3240
3269
  #operator;
3241
3270
  #value;
3242
3271
  render(conf) {
3243
- super.render({...conf, skipObservedSetup: true});
3272
+ super.render({ ...conf, skipObservedSetup: true });
3244
3273
 
3245
3274
  this.#operator = this.querySelector('[data-ref=operator]');
3246
3275
  this.#value = this.querySelector('[data-ref=value]');
3247
3276
 
3248
3277
  this.disabled = conf.disabled;
3249
3278
  this.readonly = conf.observed.readonly;
3279
+ this.required = conf.observed.required;
3250
3280
  this.value = conf.observed.value;
3251
3281
 
3252
3282
  this.addEventListener('click', (evt) => {
@@ -3265,7 +3295,7 @@ class TextFilter extends Input {
3265
3295
  return this.#value.value === '' ? undefined : [operator, 'IGNORE_CASE', this.#value.value];
3266
3296
  }
3267
3297
  set value(v) {
3268
- if (v === null || v === undefined) {
3298
+ if (v == null) {
3269
3299
  this.#value.value = '';
3270
3300
  return;
3271
3301
  }
@@ -3273,10 +3303,6 @@ class TextFilter extends Input {
3273
3303
  this.#operator.setAttribute('value', operator);
3274
3304
  this.#value.value = value;
3275
3305
  }
3276
- formResetCallback() {
3277
- const v = this.getAttribute("value");
3278
- this.value = v === null ? null : JSON.parse(v);
3279
- }
3280
3306
  }
3281
3307
 
3282
3308
  class LocalizationModule {
@@ -3333,5 +3359,5 @@ class Plugin {
3333
3359
  }
3334
3360
  }
3335
3361
 
3336
- export { AsyncEvents, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Bindings, Checkbox, Dropdown, Failure, Form, FormLoader, Hex, HttpClient, HttpClientError, Input, InputFile, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, Loaders, LocalDate, LocalDateFilter, LocalStorage, LocalizationModule, MediaType, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Spinner, Table, TableSchemaParser, TextFilter, Timing, VersionedLocalStorage, VersionedSessionStorage };
3362
+ export { AsyncEvents, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Bindings, Checkbox, Dropdown, Failure, Form, FormLoader, Hex, HttpClient, HttpClientError, Input, InputFile, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, LocalDate, LocalDateFilter, LocalStorage, LocalizationModule, MediaType, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Spinner, Table, TableSchemaParser, TextFilter, Timing, VersionedLocalStorage, VersionedSessionStorage };
3337
3363
  //# sourceMappingURL=ful.mjs.map