@optionfactory/ful 4.0.16 → 5.0.1

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