@optionfactory/ful 1.0.12 → 1.0.14

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
@@ -1387,7 +1387,9 @@ var ful = (function (exports, ftl) {
1387
1387
  this.spinner(false);
1388
1388
  }
1389
1389
  }
1390
-
1390
+ reset(){
1391
+ this.form.reset();
1392
+ }
1391
1393
  spinner(spin) {
1392
1394
  this.querySelectorAll('ful-spinner').forEach(el => {
1393
1395
  const hel = /** @type HTMLElement */ (el);
@@ -1413,16 +1415,16 @@ var ful = (function (exports, ftl) {
1413
1415
  static observed = ['value', 'readonly:presence'];
1414
1416
  static slots = true;
1415
1417
  static template = `
1416
- <label data-tpl-for="id" class="form-label">{{{{ slots.default }}}}</label>
1418
+ <label class="form-label">{{{{ slots.default }}}}</label>
1417
1419
  <div class="input-group">
1418
1420
  <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
1419
1421
  {{{{ slots.before }}}}
1420
- <input data-tpl-if="type != 'textarea'" class="form-control" data-tpl-id="id" data-tpl-type="type" placeholder=" " data-tpl-aria-describedby="fieldErrorId" form="">
1421
- <textarea data-tpl-if="type == 'textarea'" class="form-control" data-tpl-id="id" placeholder=" " data-tpl-aria-describedby="fieldErrorId" form=""></textarea>
1422
+ <input data-tpl-if="type != 'textarea'" class="form-control" data-tpl-type="type" placeholder=" " form="">
1423
+ <textarea data-tpl-if="type == 'textarea'" class="form-control" placeholder=" " form=""></textarea>
1422
1424
  {{{{ slots.after }}}}
1423
1425
  <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
1424
1426
  </div>
1425
- <ful-field-error data-tpl-id="fieldErrorId"></ful-field-error>
1427
+ <ful-field-error></ful-field-error>
1426
1428
  `;
1427
1429
  static formAssociated = true;
1428
1430
  #input;
@@ -1430,15 +1432,18 @@ var ful = (function (exports, ftl) {
1430
1432
  constructor() {
1431
1433
  super();
1432
1434
  this.internals = this.attachInternals();
1433
- this.internals.role = 'textbox';
1435
+ this.internals.role = 'presentation';
1434
1436
  }
1435
- render({ slots }) {
1436
- const id = ftl.Attributes.uid('ful-input');
1437
- const fieldErrorId = `${id}-error`;
1437
+ render({ slots, observed, disabled }) {
1438
1438
  const type = this.getAttribute("type") ?? 'text';
1439
- const fragment = this.template().withOverlay({ id, type, fieldErrorId, slots }).render();
1439
+ const fragment = this.template().withOverlay({ type, slots }).render();
1440
1440
  this.#input = fragment.querySelector("input,textarea");
1441
+
1441
1442
  ftl.Attributes.forward('input-', this, this.#input);
1443
+ this.disabled = disabled;
1444
+ this.readonly = observed.readonly;
1445
+ this.value = observed.value;
1446
+
1442
1447
  this.#input.addEventListener('change', (evt) => {
1443
1448
  evt.stopPropagation();
1444
1449
  this.dispatchEvent(new CustomEvent('change', {
@@ -1449,7 +1454,11 @@ var ful = (function (exports, ftl) {
1449
1454
  }
1450
1455
  }));
1451
1456
  });
1457
+ const label = fragment.querySelector('label');
1458
+ label.addEventListener('click', () => this.focus());
1452
1459
  this.#fieldError = fragment.querySelector('ful-field-error');
1460
+ this.#input.ariaDescribedByElements = [this.#fieldError];
1461
+ this.#input.ariaLabelledByElements = [label];
1453
1462
  this.replaceChildren(fragment);
1454
1463
  }
1455
1464
  get value() {
@@ -1464,6 +1473,12 @@ var ful = (function (exports, ftl) {
1464
1473
  set readonly(v) {
1465
1474
  this.#input.readOnly = v;
1466
1475
  }
1476
+ get disabled(){
1477
+ return this.#input.hasAttribute('disabled');
1478
+ }
1479
+ set disabled(d){
1480
+ ftl.Attributes.toggle(this.#input, 'disabled', d);
1481
+ }
1467
1482
  focus(options) {
1468
1483
  this.#input.focus(options);
1469
1484
  }
@@ -1476,6 +1491,9 @@ var ful = (function (exports, ftl) {
1476
1491
  this.internals.setValidity({ customError: true }, " ");
1477
1492
  this.#fieldError.innerText = error;
1478
1493
  }
1494
+ formResetCallback(){
1495
+ this.value = this.getAttribute("value");
1496
+ }
1479
1497
  }
1480
1498
 
1481
1499
  class CompleteSelectLoader {
@@ -1565,11 +1583,9 @@ var ful = (function (exports, ftl) {
1565
1583
  this.#data = data;
1566
1584
  }
1567
1585
  async exact(...keys) {
1568
- await timing.sleep(500);
1569
1586
  return this.#data.filter(([k, v]) => keys.includes(k));
1570
1587
  }
1571
1588
  async load(needle) {
1572
- await timing.sleep(500);
1573
1589
  return this.#data.filter(([k, v]) => v.includes(needle?.toLowerCase()));
1574
1590
  }
1575
1591
  }
@@ -1678,22 +1694,22 @@ var ful = (function (exports, ftl) {
1678
1694
  }
1679
1695
 
1680
1696
  class Select extends ftl.ParsedElement {
1681
- static observed = ['value:csvm']
1697
+ static observed = ['value:csvm', 'readonly:presence']
1682
1698
  static slots = true
1683
1699
  static template = `
1684
- <label data-tpl-for="id" class="form-label">{{{{ slots.default }}}}</label>
1700
+ <label class="form-label">{{{{ slots.default }}}}</label>
1685
1701
  <div class="input-group flex-nowrap" tabindex="-1">
1686
1702
  <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
1687
1703
  {{{{ slots.before }}}}
1688
1704
  <div class="ful-select-input">
1689
1705
  <badges></badges>
1690
- <input data-tpl-id="id" data-tpl-ariadesribed-by="fieldErrorId" type="text" form="">
1706
+ <input type="text" form="">
1691
1707
  </div>
1692
1708
  {{{{ slots.after }}}}
1693
1709
  <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
1694
1710
  </div>
1695
1711
  <ful-dropdown hidden></ful-dropdown>
1696
- <ful-field-error data-tpl-id="fieldErrorId"></ful-field-error>
1712
+ <ful-field-error></ful-field-error>
1697
1713
  `;
1698
1714
  static mappers = {
1699
1715
  "csvm": (v, name, el) => {
@@ -1715,20 +1731,27 @@ var ful = (function (exports, ftl) {
1715
1731
  constructor() {
1716
1732
  super();
1717
1733
  this.internals = this.attachInternals();
1718
- this.internals.role = 'combobox';
1734
+ this.internals.role = 'presentation';
1719
1735
  }
1720
- async render({ slots, observed }) {
1736
+ async render({ slots, observed, disabled }) {
1721
1737
  const name = this.getAttribute("name");
1722
- const id = ftl.Attributes.uid('ful-select');
1723
- const fieldErrorId = id + "-error";
1724
1738
  this.#loader = Loaders.fromAttributes(this, 'loaders:select', { options: slots.options });
1725
1739
  await this.#loader.prefetch?.();
1726
- const fragment = this.template().withOverlay({ slots, name, id, fieldErrorId }).render();
1740
+ const fragment = this.template().withOverlay({ slots, name }).render();
1727
1741
  this.#input = fragment.querySelector('input');
1728
1742
  this.#badges = fragment.querySelector('badges');
1743
+
1744
+ this.value = observed.value;
1745
+ this.disabled = disabled;
1746
+ this.readonly = observed.readonly;
1747
+
1729
1748
  this.#ddmenu = fragment.querySelector('ful-dropdown');
1730
1749
  this.#multiple = this.hasAttribute("multiple");
1750
+ const label = fragment.querySelector('label');
1751
+ label.addEventListener('click', () => this.focus());
1731
1752
  this.#fieldError = fragment.querySelector('ful-field-error');
1753
+ this.#input.ariaDescribedByElements = [this.#fieldError];
1754
+ this.#input.ariaLabelledByElements = [label];
1732
1755
 
1733
1756
  const self = this;
1734
1757
  const [dload, abortdload] = timing.debounce(400, () => self.#ddmenu.show(() => self.#loader.load(self.#input.value)));
@@ -1822,6 +1845,11 @@ var ful = (function (exports, ftl) {
1822
1845
  this.#badges.append(...badges);
1823
1846
  }
1824
1847
  set value(value) {
1848
+ if(value === null){
1849
+ this.#values = new Map();
1850
+ this.#syncBadges();
1851
+ return;
1852
+ }
1825
1853
  (async () => {
1826
1854
  const entries = await (this.#multiple ? this.#loader.exact(...value) : this.#loader.exact(value));
1827
1855
  this.#values = new Map(entries);
@@ -1849,10 +1877,10 @@ var ful = (function (exports, ftl) {
1849
1877
  }
1850
1878
 
1851
1879
  class RadioGroup extends ftl.ParsedElement {
1852
- static observed = ['value'];
1880
+ static observed = ['value', 'readonly:presence'];
1853
1881
  static slots = true;
1854
1882
  static template = `
1855
- <fieldset data-tpl-aria-describedby="fieldErrorId">
1883
+ <fieldset>
1856
1884
  <legend class="form-label">
1857
1885
  {{{{ slots.default }}}}
1858
1886
  </legend>
@@ -1867,13 +1895,14 @@ var ful = (function (exports, ftl) {
1867
1895
  </label>
1868
1896
  </div>
1869
1897
  </section>
1870
- <ful-field-error data-tpl-id="fieldErrorId"></ful-field-error>
1898
+ <ful-field-error></ful-field-error>
1871
1899
  <footer data-tpl-if="slots.footer">
1872
1900
  {{{{ slots.footer }}}}
1873
1901
  </footer>
1874
1902
  </fieldset>
1875
1903
  `;
1876
1904
  static formAssociated = true;
1905
+ #fieldset;
1877
1906
  #fieldError;
1878
1907
  #firstRadio;
1879
1908
  #booleanType;
@@ -1882,7 +1911,7 @@ var ful = (function (exports, ftl) {
1882
1911
  this.internals = this.attachInternals();
1883
1912
  this.internals.role = 'radiogroup';
1884
1913
  }
1885
- render({ slots }) {
1914
+ render({ slots, observed, disabled }) {
1886
1915
  const name = this.getAttribute('name') ?? ftl.Attributes.uid('ful-radiogroup');
1887
1916
  const radioEls = Array.from(slots.default.querySelectorAll('ful-radio'));
1888
1917
  const inputsAndLabels = radioEls.map(el => {
@@ -1908,9 +1937,13 @@ var ful = (function (exports, ftl) {
1908
1937
  });
1909
1938
 
1910
1939
  radioEls.forEach(el => el.remove());
1911
- const fieldErrorId = ftl.Attributes.uid("ful-error");
1912
- this.template().withOverlay({ name, fieldErrorId, slots, inputsAndLabels }).renderTo(this);
1940
+ this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
1941
+ this.#fieldset = this.firstElementChild;
1942
+ this.disabled = disabled;
1943
+ this.readonly = observed.readonly;
1944
+ this.value = observed.value;
1913
1945
  this.#fieldError = this.querySelector('ful-field-error');
1946
+ this.ariaDescribedByElements = [this.#fieldError];
1914
1947
  this.#firstRadio = this.querySelector('input[type=radio]');
1915
1948
  this.#booleanType = this.getAttribute('type') === 'boolean';
1916
1949
  }
@@ -1931,7 +1964,19 @@ var ful = (function (exports, ftl) {
1931
1964
  if (el) {
1932
1965
  el.checked = true;
1933
1966
  }
1967
+ }
1968
+ get readonly(){
1969
+ return this.#fieldset.inert;
1970
+ }
1971
+ set readonly(v) {
1972
+ this.#fieldset.inert = v;
1973
+ }
1974
+ get disabled(){
1975
+ return this.#fieldset.hasAttribute('disabled');
1934
1976
  }
1977
+ set disabled(d){
1978
+ ftl.Attributes.toggle(this.#fieldset, 'disabled', d);
1979
+ }
1935
1980
  focus(options) {
1936
1981
  this.#firstRadio.focus(options);
1937
1982
  }
@@ -1947,34 +1992,35 @@ var ful = (function (exports, ftl) {
1947
1992
  }
1948
1993
 
1949
1994
  class Checkbox extends ftl.ParsedElement {
1950
- static observed = ['value:bool'];
1995
+ static observed = ['value:bool', 'readonly:presence'];
1951
1996
  static slots = true;
1952
1997
  static template = `
1953
1998
  <div data-tpl-class="klass">
1954
1999
  <div class="input-container">
1955
- <input data-tpl-id="id" class="form-check-input" type="checkbox" role="switch" form="" placeholder=" " data-tpl-aria-describedby="fieldErrorId">
2000
+ <input class="form-check-input" type="checkbox" role="switch" form="" placeholder=" ">
1956
2001
  </div>
1957
- <label data-tpl-for="id" class="form-check-label">{{{{ slots.default }}}}</label>
2002
+ <label class="form-check-label">{{{{ slots.default }}}}</label>
1958
2003
  </div>
1959
- <ful-field-error data-tpl-if="fieldErrorId"></ful-field-error>
2004
+ <ful-field-error></ful-field-error>
1960
2005
  `;
2006
+ #container;
1961
2007
  #input;
1962
2008
  #fieldError;
1963
2009
  static formAssociated = true;
1964
2010
  constructor() {
1965
2011
  super();
1966
2012
  this.internals = this.attachInternals();
1967
- this.internals.role = 'checkbox';
2013
+ this.internals.role = 'presentation';
1968
2014
  }
1969
- render({ slots }) {
1970
- const id = ftl.Attributes.uid("ful-checkbox");
1971
- const fieldErrorId = id + "-error";
2015
+ render({ slots, observed, disabled }) {
1972
2016
  const klass = this.getAttribute('type') == 'switch' ? "form-check form-switch" : "form-check";
1973
- this.internals.role = this.getAttribute('type') == 'switch' ? 'switch' : 'checkbox';
1974
- const fragment = this.template().withOverlay({ slots, klass, id, fieldErrorId }).render();
2017
+ const fragment = this.template().withOverlay({ slots, klass }).render();
2018
+ this.#container = fragment.firstElementChild;
1975
2019
  this.#input = fragment.querySelector("input");
1976
2020
  ftl.Attributes.forward('input-', this, this.#input);
1977
- this.#fieldError = fragment.querySelector('ful-field-error');
2021
+ this.disabled = disabled;
2022
+ this.readonly = observed.readonly;
2023
+ this.value = observed.value;
1978
2024
  this.#input.addEventListener('change', (evt) => {
1979
2025
  evt.stopPropagation();
1980
2026
  this.dispatchEvent(new CustomEvent('change', {
@@ -1984,7 +2030,18 @@ var ful = (function (exports, ftl) {
1984
2030
  value: this.value
1985
2031
  }
1986
2032
  }));
1987
- });
2033
+ });
2034
+ const label = fragment.querySelector('label');
2035
+ label.addEventListener('click', () => {
2036
+ this.focus();
2037
+ if (this.disabled || this.readonly) {
2038
+ return;
2039
+ }
2040
+ this.value = !this.value;
2041
+ });
2042
+ this.#fieldError = fragment.querySelector('ful-field-error');
2043
+ this.#input.ariaDescribedByElements = [this.#fieldError];
2044
+ this.#input.ariaLabelledByElements = [label];
1988
2045
  this.replaceChildren(fragment);
1989
2046
  }
1990
2047
  get value() {
@@ -1993,6 +2050,18 @@ var ful = (function (exports, ftl) {
1993
2050
  set value(value) {
1994
2051
  this.#input.checked = value;
1995
2052
  }
2053
+ get readonly(){
2054
+ return this.#container.inert;
2055
+ }
2056
+ set readonly(v) {
2057
+ this.#container.inert = v;
2058
+ }
2059
+ get disabled() {
2060
+ return this.#input.hasAttribute('disabled');
2061
+ }
2062
+ set disabled(d) {
2063
+ ftl.Attributes.toggle(this.#input, 'disabled', d);
2064
+ }
1996
2065
  focus(options) {
1997
2066
  this.#input.focus(options);
1998
2067
  }
@@ -2216,48 +2285,50 @@ var ful = (function (exports, ftl) {
2216
2285
  <ful-form data-tpl-if="slots.filters">
2217
2286
  {{{{ slots.filters }}}}
2218
2287
  </ful-form>
2219
- <table class="table">
2220
- <caption data-tpl-if="slots.caption">{{{{ slots.caption }}}}</caption>
2221
- <thead>
2222
- <tr>
2223
- <th data-tpl-each="schema" scope="col" data-tpl-class="title.classes">
2224
- {{{{ title.fragment }}}}
2225
- <ful-sorter data-tpl-if="sorter || order" data-tpl-sorter="sorter" data-tpl-order="order"></ful-sorter>
2226
- </th>
2227
- </tr>
2228
- </thead>
2229
- <tbody></tbody>
2230
- <tbody data-ref="no-autoload">
2231
- <tr>
2232
- <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2233
- <i class="bi bi-search" style="font-size: 40px; color: #BDC3CA"></i>
2234
- <p class="mt-3 mb-0" style="color: #BDC3CA">
2235
- Avvia la ricerca per visualizzare i risultati...
2236
- </p>
2237
- </td>
2238
- </tr>
2239
- </tbody>
2240
- <tbody data-ref="loading" hidden>
2241
- <tr>
2242
- <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2243
- <ful-spinner class="big"></ful-spinner>
2244
- </td>
2245
- </tr>
2246
- </tbody>
2247
- <tbody data-ref="feedback" hidden>
2248
- <tr>
2249
- <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2250
- <div class="alert alert-danger">
2251
- <p>Errore nel caricamento della tabella:</p>
2252
- <p class="mb-0" data-ref="feedback-error"></p>
2253
- </div>
2254
- </td>
2255
- </tr>
2256
- </tbody>
2257
- <tfoot data-tpl-if="slots.footer">
2258
- {{{{ slots.footer }}}}
2259
- </tfoot>
2260
- </table>
2288
+ <div class="table-wrapper">
2289
+ <table class="table">
2290
+ <caption data-tpl-if="slots.caption">{{{{ slots.caption }}}}</caption>
2291
+ <thead>
2292
+ <tr>
2293
+ <th data-tpl-each="schema" scope="col" data-tpl-class="title.classes">
2294
+ {{{{ title.fragment }}}}
2295
+ <ful-sorter data-tpl-if="sorter || order" data-tpl-sorter="sorter" data-tpl-order="order"></ful-sorter>
2296
+ </th>
2297
+ </tr>
2298
+ </thead>
2299
+ <tbody></tbody>
2300
+ <tbody data-ref="no-autoload">
2301
+ <tr>
2302
+ <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2303
+ <i class="bi bi-search" style="font-size: 40px; color: #BDC3CA"></i>
2304
+ <p class="mt-3 mb-0" style="color: #BDC3CA">
2305
+ Avvia la ricerca per visualizzare i risultati...
2306
+ </p>
2307
+ </td>
2308
+ </tr>
2309
+ </tbody>
2310
+ <tbody data-ref="loading" hidden>
2311
+ <tr>
2312
+ <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2313
+ <ful-spinner class="big"></ful-spinner>
2314
+ </td>
2315
+ </tr>
2316
+ </tbody>
2317
+ <tbody data-ref="feedback" hidden>
2318
+ <tr>
2319
+ <td data-tpl-colspan="schema.length" class="text-center align-middle p-4">
2320
+ <div class="alert alert-danger">
2321
+ <p>Errore nel caricamento della tabella:</p>
2322
+ <p class="mb-0" data-ref="feedback-error"></p>
2323
+ </div>
2324
+ </td>
2325
+ </tr>
2326
+ </tbody>
2327
+ <tfoot data-tpl-if="slots.footer">
2328
+ {{{{ slots.footer }}}}
2329
+ </tfoot>
2330
+ </table>
2331
+ </div>
2261
2332
  <ful-pagination current="0" total="1"></ful-pagination>
2262
2333
  `;
2263
2334
  static templates = {
@@ -2286,7 +2357,8 @@ var ful = (function (exports, ftl) {
2286
2357
  const template = this.template();
2287
2358
  const schema = TableSchemaParser.parse(slots.default, template);
2288
2359
  const fragment = template.withOverlay({ slots, schema }).render();
2289
- const table = /** @type HTMLTableElement */ (ftl.Nodes.queryChildren(fragment, 'table'));
2360
+ const tableWrapper = /** @type HTMLTableElement */ (ftl.Nodes.queryChildren(fragment, '.table-wrapper'));
2361
+ const table = /** @type HTMLTableElement */ (tableWrapper.querySelector("table"));
2290
2362
  ftl.Attributes.forward('table-', this, table);
2291
2363
  this.#schema = schema;
2292
2364
  this.#body = table.querySelector(':scope > tbody');
@@ -2379,7 +2451,7 @@ var ful = (function (exports, ftl) {
2379
2451
  static observed = ["value:json"];
2380
2452
  static slots = true;
2381
2453
  static template = `
2382
- <label data-tpl-for="id" class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2454
+ <label class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2383
2455
  <div class="input-group">
2384
2456
  <button data-ref="operator" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" value="LTE" form="">&PrecedesSlantEqual;</button>
2385
2457
  <ul class="dropdown-menu">
@@ -2391,7 +2463,7 @@ var ful = (function (exports, ftl) {
2391
2463
  <li><a class="dropdown-item" role="button" value="GTE">&SucceedsSlantEqual;</a></li>
2392
2464
  <li><a class="dropdown-item" role="button" value="BETWEEN">&LeftRightArrow;</a></li>
2393
2465
  </ul>
2394
- <input data-tpl-id="id" data-ref="value1" type="datetime-local" class="form-control" form="">
2466
+ <input data-ref="value1" type="datetime-local" class="form-control" form="">
2395
2467
  <input data-ref="value2" type="datetime-local" class="form-control" form="" hidden>
2396
2468
  <span class="input-group-text"><i class="bi bi-search"></i></span>
2397
2469
  </div>
@@ -2407,15 +2479,18 @@ var ful = (function (exports, ftl) {
2407
2479
  this.internals = this.attachInternals();
2408
2480
  }
2409
2481
  render({ slots }) {
2410
- const id = ftl.Attributes.uid('instant-filter');
2411
2482
  const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
2412
2483
  const name = this.getAttribute("name");
2413
- const fragment = this.template().withOverlay({ id, label, name }).render(this);
2484
+ const fragment = this.template().withOverlay({ label, name }).render(this);
2414
2485
  this.#operator = fragment.querySelector('[data-ref=operator]');
2415
2486
  this.#value1 = fragment.querySelector('[data-ref=value1]');
2416
2487
  this.#value2 = fragment.querySelector('[data-ref=value2]');
2488
+ this.#fieldError = fragment.querySelector('ful-field-error');
2489
+ const labelEl = fragment.querySelector('label');
2490
+ labelEl?.addEventListener('click', () => this.focus());
2491
+ this.#value1.ariaDescribedByElements = [this.#fieldError];
2492
+ this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
2417
2493
  this.replaceChildren(fragment);
2418
- this.#fieldError = this.querySelector('ful-field-error');
2419
2494
  this.addEventListener('click', (evt) => {
2420
2495
  const target = /** @type HTMLElement */ (evt.target);
2421
2496
  if (!target.matches('ul > li > a')) {
@@ -2478,7 +2553,7 @@ var ful = (function (exports, ftl) {
2478
2553
  static observed = ["value:json"];
2479
2554
  static slots = true;
2480
2555
  static template = `
2481
- <label data-tpl-for="id" class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2556
+ <label class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2482
2557
  <div class="input-group">
2483
2558
  <button data-ref="operator" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" value="EQ" form="">=</button>
2484
2559
  <ul class="dropdown-menu">
@@ -2490,10 +2565,9 @@ var ful = (function (exports, ftl) {
2490
2565
  <li><a class="dropdown-item" role="button" value="GTE">&SucceedsSlantEqual;</a></li>
2491
2566
  <li><a class="dropdown-item" role="button" value="BETWEEN">&LeftRightArrow;</a></li>
2492
2567
  </ul>
2493
- <input data-tpl-id="id" data-ref="value1" type="date" class="form-control" form="">
2568
+ <input data-ref="value1" type="date" class="form-control" form="">
2494
2569
  <input data-ref="value2" type="date" class="form-control" form="" hidden>
2495
2570
  <span class="input-group-text"><i class="bi bi-search"></i></span>
2496
-
2497
2571
  </div>
2498
2572
  <ful-field-error></ful-field-error>
2499
2573
  `;
@@ -2507,15 +2581,18 @@ var ful = (function (exports, ftl) {
2507
2581
  this.internals = this.attachInternals();
2508
2582
  }
2509
2583
  render({ slots }) {
2510
- const id = ftl.Attributes.uid('instant-filter');
2511
2584
  const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
2512
2585
  const name = this.getAttribute("name");
2513
- const fragment = this.template().withOverlay({ id, label, name }).render(this);
2586
+ const fragment = this.template().withOverlay({ label, name }).render(this);
2514
2587
  this.#operator = fragment.querySelector('[data-ref=operator]');
2515
2588
  this.#value1 = fragment.querySelector('[data-ref=value1]');
2516
2589
  this.#value2 = fragment.querySelector('[data-ref=value2]');
2590
+ this.#fieldError = fragment.querySelector('ful-field-error');
2591
+ const labelEl = fragment.querySelector('label');
2592
+ labelEl?.addEventListener('click', () => this.focus());
2593
+ this.#value1.ariaDescribedByElements = [this.#fieldError];
2594
+ this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
2517
2595
  this.replaceChildren(fragment);
2518
- this.#fieldError = this.querySelector('ful-field-error');
2519
2596
  this.addEventListener('click', (evt) => {
2520
2597
  const target = /** @type HTMLElement */(evt.target);
2521
2598
  if (!target.matches('ul > li > a')) {
@@ -2568,7 +2645,7 @@ var ful = (function (exports, ftl) {
2568
2645
  static observed = ["value:json"];
2569
2646
  static slots = true;
2570
2647
  static template = `
2571
- <label data-tpl-for="id" class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2648
+ <label class="form-label" data-tpl-if="label">{{{{ label }}}}</label>
2572
2649
  <div class="input-group">
2573
2650
  <button data-ref="operator" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" value="CONTAINS" form="">&mldr;a&mldr;</button>
2574
2651
  <ul class="dropdown-menu">
@@ -2577,7 +2654,7 @@ var ful = (function (exports, ftl) {
2577
2654
  <li><a class="dropdown-item" role="button" value="ENDS_WITH">&mldr;a</a></li>
2578
2655
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
2579
2656
  </ul>
2580
- <input data-tpl-id="id" data-ref="value" type="text" class="form-control" form="">
2657
+ <input data-ref="value" type="text" class="form-control" form="">
2581
2658
  <span class="input-group-text"><i class="bi bi-search"></i></span>
2582
2659
  </div>
2583
2660
  <ful-field-error></ful-field-error>
@@ -2591,14 +2668,17 @@ var ful = (function (exports, ftl) {
2591
2668
  this.internals = this.attachInternals();
2592
2669
  }
2593
2670
  render({ slots }) {
2594
- const id = ftl.Attributes.uid('string-filter');
2595
2671
  const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
2596
2672
  const name = this.getAttribute("name");
2597
- const fragment = this.template().withOverlay({ id, label, name }).render(this);
2673
+ const fragment = this.template().withOverlay({ label, name }).render(this);
2598
2674
  this.#operator = fragment.querySelector('[data-ref=operator]');
2599
2675
  this.#value = fragment.querySelector('[data-ref=value]');
2676
+ this.#fieldError = fragment.querySelector('ful-field-error');
2677
+ const labelEl = fragment.querySelector('label');
2678
+ labelEl?.addEventListener('click', () => this.focus());
2679
+ this.#value.ariaDescribedByElements = [this.#fieldError];
2680
+ this.#value.ariaLabelledByElements = labelEl ? [labelEl] : [];
2600
2681
  this.replaceChildren(fragment);
2601
- this.#fieldError = this.querySelector('ful-field-error');
2602
2682
  this.addEventListener('click', (evt) => {
2603
2683
  const target = /** @type HTMLElement */(evt.target);
2604
2684
  if (!target.matches('ul > li > a')) {