@optionfactory/ful 5.0.2 → 5.0.4

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
@@ -1688,16 +1688,18 @@ class InputFile extends Input {
1688
1688
  'dropzonelabel': 'Click or drop your files here',
1689
1689
  'unaccepptablefiletype': "Only files of type {0} are supported",
1690
1690
  'maxfilesizeexceeded': "Maximum supported file size is {0}",
1691
- 'maxtotalsizeexceeded': "Maximum supported total file size is {0}"
1691
+ 'maxtotalsizeexceeded': "Maximum supported total file size is {0}",
1692
+ 'maxfilesexceeded': "Maximum files count exceeded"
1692
1693
  },
1693
1694
  it: {
1694
1695
  'dropzonelabel': 'Clicca o trascina i file qui',
1695
1696
  'unaccepptablefiletype': "Solo i file di tipo {0} sono supportati",
1696
1697
  'maxfilesizeexceeded': "La dimensione massima di un file è di {0}",
1697
- 'maxtotalsizeexceeded': "La dimensione massima complessiva dei file è di {0}"
1698
+ 'maxtotalsizeexceeded': "La dimensione massima complessiva dei file è di {0}",
1699
+ 'maxfilesexceeded': "Numero massimo di file superato",
1698
1700
  }
1699
1701
  }
1700
- static observed = ['value', 'readonly:presence', 'required:presence', "accept:csv", 'multiple:presence', "itemlist:presence", "dropzone:presence", "maxfilesize:number", "maxtotalsize:number"];
1702
+ static observed = ['value', 'readonly:presence', 'required:presence', "accept:csv", 'multiple:presence', "itemlist:presence", "dropzone:presence", "maxfiles:number", "maxfilesize:number", "maxtotalsize:number"];
1701
1703
  #accept;
1702
1704
  #items;
1703
1705
  #dropzone;
@@ -1720,7 +1722,7 @@ class InputFile extends Input {
1720
1722
  <div data-ref="dropzone" class="dropzone" data-tpl-if="slots.dropzone">
1721
1723
  {{{{ slots.dropzone }}}}
1722
1724
  </div>
1723
- <div data-ref="dropzone" class="dropzone" data-tpl-if="!slots.dropzone">
1725
+ <div data-ref="dropzone" class="default-dropzone" data-tpl-if="!slots.dropzone">
1724
1726
  {{ #l10n:t('dropzonelabel') }}
1725
1727
  </div>
1726
1728
  <ful-item-list></ful-item-list>
@@ -1747,6 +1749,7 @@ class InputFile extends Input {
1747
1749
  this.multiple = observed.multiple;
1748
1750
  this.itemlist = observed.itemlist;
1749
1751
  this.dropzone = observed.dropzone;
1752
+ this.maxfiles = observed.maxfiles;
1750
1753
  this.maxfilesize = observed.maxfilesize;
1751
1754
  this.maxtotalsize = observed.maxtotalsize;
1752
1755
  this.#warnings.addEventListener('animationend', e => {
@@ -1788,13 +1791,14 @@ class InputFile extends Input {
1788
1791
  this.#ensureAcceptable();
1789
1792
  this.#ensureFileSizes();
1790
1793
  this.#ensureTotalSize();
1794
+ this.#ensureFilesCount();
1791
1795
  this.template('items').withOverlay({ files: this.files }).withModule('bytes', { format: this.#formatByteSize }).renderTo(this.#items);
1792
1796
  }
1793
1797
  warning(key, args) {
1794
1798
  this.template('warning').withOverlay({ key, args }).renderTo(this.#warnings);
1795
1799
  }
1796
1800
  #ensureAcceptable() {
1797
- if (!this.#accept) {
1801
+ if (!this.#accept.length) {
1798
1802
  return;
1799
1803
  }
1800
1804
  const unacceptable = [...this.files]
@@ -1803,11 +1807,23 @@ class InputFile extends Input {
1803
1807
  if (unacceptable.length === 0) {
1804
1808
  return;
1805
1809
  }
1806
- this.warning('unaccepptablefiletype', this.#accept.join(","));
1810
+ this.warning('unaccepptablefiletype', this.#accept.join(", "));
1807
1811
  const dt = new DataTransfer();
1808
1812
  [...this.files].filter(f => !unacceptable.includes(f)).forEach(f => dt.items.add(f));
1809
1813
  this.files = dt.files;
1810
1814
  }
1815
+ #ensureFilesCount() {
1816
+ if (this.#maxfiles === null) {
1817
+ return;
1818
+ }
1819
+ if (this.files.length <= this.#maxfiles) {
1820
+ return;
1821
+ }
1822
+ this.warning('maxfilesexceeded');
1823
+ const dt = new DataTransfer();
1824
+ this.files = dt.files;
1825
+ }
1826
+
1811
1827
  #ensureFileSizes() {
1812
1828
  if (this.#maxfilesize === null) {
1813
1829
  return;
@@ -1873,6 +1889,19 @@ class InputFile extends Input {
1873
1889
  set value(v) {
1874
1890
  //TODO:
1875
1891
  }
1892
+ get totalsize() {
1893
+ return Array.from(this.files).reduce((a, f) => a + f.size, 0);
1894
+ }
1895
+ #maxfiles;
1896
+ get maxfiles() {
1897
+ return this.#maxfiles;
1898
+ }
1899
+ set maxfiles(v) {
1900
+ this.#maxfiles = v;
1901
+ this.reflect(() => {
1902
+ Attributes.set(this, 'maxfiles', v);
1903
+ });
1904
+ }
1876
1905
  #maxfilesize;
1877
1906
  get maxfilesize() {
1878
1907
  return this.#maxfilesize;
@@ -1880,7 +1909,7 @@ class InputFile extends Input {
1880
1909
  set maxfilesize(v) {
1881
1910
  this.#maxfilesize = v;
1882
1911
  this.reflect(() => {
1883
- this.setAttribute('maxfilesize', v);
1912
+ Attributes.set(this, 'maxfilesize', v);
1884
1913
  });
1885
1914
  }
1886
1915
  #maxtotalsize;
@@ -1890,7 +1919,7 @@ class InputFile extends Input {
1890
1919
  set maxtotalsize(v) {
1891
1920
  this.#maxtotalsize = v;
1892
1921
  this.reflect(() => {
1893
- this.setAttribute('maxtotalsize', v);
1922
+ Attributes.set(this, 'maxtotalsize', v);
1894
1923
  });
1895
1924
  }
1896
1925
  #useItemlist;
@@ -1912,7 +1941,7 @@ class InputFile extends Input {
1912
1941
  this.reflect(() => {
1913
1942
  Attributes.toggle(this, "dropzone", v);
1914
1943
  });
1915
- }
1944
+ }
1916
1945
  }
1917
1946
 
1918
1947
  class RemoteLoader {
@@ -1983,16 +2012,16 @@ class PartialRemoteLoader {
1983
2012
  this.#responseMapper = responseMapper;
1984
2013
  }
1985
2014
  async exact(...keys) {
1986
- const data = await this.#http.request(this.#method, this.#url)
2015
+ const response = await this.#http.request(this.#method, this.#url)
1987
2016
  .param("k", ...keys)
1988
2017
  .fetchJson();
1989
- return this.#responseMapper(data);
2018
+ return this.#responseMapper(response);
1990
2019
  }
1991
2020
  async load(needle) {
1992
- const data = await this.#http.request(this.#method, this.#url)
2021
+ const response = await this.#http.request(this.#method, this.#url)
1993
2022
  .param("s", needle)
1994
2023
  .fetchJson();
1995
- return this.#responseMapper(data);
2024
+ return this.#responseMapper(response);
1996
2025
  }
1997
2026
  }
1998
2027
 
@@ -2044,19 +2073,22 @@ class SelectLoader {
2044
2073
  }
2045
2074
  static #responseMapperFrom(el) {
2046
2075
  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
- ];
2076
+ return response => {
2077
+ const rows = registry.evaluator().withOverlay(response).evaluateExpression(el.getAttribute("d-expr") ?? 'self');
2078
+ return rows.map(row => {
2079
+ const evaluator = registry.evaluator().withOverlay(row);
2080
+ return [
2081
+ evaluator.evaluateExpression(el.getAttribute("k-expr")),
2082
+ evaluator.evaluateExpression(el.getAttribute("l-expr")),
2083
+ evaluator.evaluateExpression(el.getAttribute("m-expr") ?? 'self'),
2084
+ ];
2085
+ })
2054
2086
  };
2055
2087
  }
2056
2088
  if (el.hasAttribute("response-mapper")) {
2057
2089
  return registry.component(el.getAttribute("response-mapper"));
2058
2090
  }
2059
- return v => v;
2091
+ return response => response;
2060
2092
  }
2061
2093
  }
2062
2094
 
@@ -2200,7 +2232,7 @@ class Select extends ParsedElement {
2200
2232
  }
2201
2233
  async render({ slots, observed, disabled }) {
2202
2234
  const name = this.getAttribute("name");
2203
- this.#loader = registry.component(this.getAttribute("loader") ?? 'loaders:select').create(this, {options: slots.options});
2235
+ this.#loader = registry.component(this.getAttribute("loader") ?? 'loaders:select').create(this, { options: slots.options });
2204
2236
 
2205
2237
  this.#multiple = this.hasAttribute("multiple");
2206
2238
  await this.#loader.prefetch?.();
@@ -2341,7 +2373,7 @@ class Select extends ParsedElement {
2341
2373
  this.replaceChildren(fragment);
2342
2374
  }
2343
2375
  async withLoader(fn) {
2344
- await fn(this.#loader);
2376
+ return await fn(this.#loader);
2345
2377
  }
2346
2378
  #changed() {
2347
2379
  const selection = [...this.#values.entries()].map(e => ({ key: e[0], label: e[1][0], metadata: e[1].slice(1) }));
@@ -2837,8 +2869,8 @@ class TableSchemaParser {
2837
2869
  rowsTr.setAttribute(attr, value ?? '');
2838
2870
  }
2839
2871
  const columns = Nodes.queryChildrenAll(schema, "column");
2840
- const sort = columns.filter(v => v.hasAttribute('order')).map(v => ({sorter: v.getAttribute("sorter"), order: v.getAttribute("order")}))[0] ?? null;
2841
- for(var column of columns){
2872
+ const sort = columns.filter(v => v.hasAttribute('order')).map(v => ({ sorter: v.getAttribute("sorter"), order: v.getAttribute("order") }))[0] ?? null;
2873
+ for (var column of columns) {
2842
2874
  const maybeTitleTag = Nodes.queryChildren(column, 'title');
2843
2875
  const sorter = column.getAttribute("sorter");
2844
2876
  const order = column.getAttribute("order");
@@ -2847,12 +2879,12 @@ class TableSchemaParser {
2847
2879
  column.removeAttribute("sorter");
2848
2880
  column.removeAttribute("order");
2849
2881
  column.removeAttribute("title");
2850
- const wrappedTitleNode = (!sorter && !order ) ? titleNode : (() => {
2882
+ const wrappedTitleNode = (!sorter && !order) ? titleNode : (() => {
2851
2883
  const fulSorter = document.createElement("ful-sorter");
2852
- if(sorter){
2884
+ if (sorter) {
2853
2885
  fulSorter.setAttribute("sorter", sorter);
2854
2886
  }
2855
- if(order){
2887
+ if (order) {
2856
2888
  fulSorter.setAttribute("order", order);
2857
2889
  }
2858
2890
  fulSorter.append(titleNode);
@@ -2872,14 +2904,32 @@ class TableSchemaParser {
2872
2904
  }
2873
2905
 
2874
2906
  return {
2875
- headersTemplate: template.withOverlay({inHeaders: true, inRows: false}).withFragment(Fragments.from(headersTr)),
2876
- rowsTemplate: template.withOverlay({inHeaders: false, inRows: true}).withFragment(Fragments.from(rowsTr)),
2907
+ headersTemplate: template.withOverlay({ inHeaders: true, inRows: false }).withFragment(Fragments.from(headersTr)),
2908
+ rowsTemplate: template.withOverlay({ inHeaders: false, inRows: true }).withFragment(Fragments.from(rowsTr)),
2877
2909
  sort: sort,
2878
2910
  length: columns.length
2879
2911
  }
2880
2912
  }
2881
2913
  }
2882
2914
 
2915
+
2916
+
2917
+ class InMemoryTableLoader {
2918
+ #data
2919
+ constructor(data) {
2920
+ this.#data = data;
2921
+ }
2922
+ async load(pageRequest, sortRequest, filterRequest) {
2923
+ return {
2924
+ page: this.#data,
2925
+ size: this.#data.length
2926
+ };
2927
+ }
2928
+ update(data) {
2929
+ this.#data = data;
2930
+ }
2931
+ }
2932
+
2883
2933
  class RemoteTableLoader {
2884
2934
  #http;
2885
2935
  #url;
@@ -2903,10 +2953,13 @@ class RemoteTableLoader {
2903
2953
 
2904
2954
  class TableLoader {
2905
2955
  static create(el, conf) {
2906
- const http = registry.component("http-client");
2907
2956
  const url = el.getAttribute("src");
2908
- const method = el.getAttribute("method") ?? 'GET';
2909
- return new RemoteTableLoader(http, url, method);
2957
+ if (url) {
2958
+ const http = registry.component("http-client");
2959
+ const method = el.getAttribute("method") ?? 'GET';
2960
+ return new RemoteTableLoader(http, url, method);
2961
+ }
2962
+ return new InMemoryTableLoader([]);
2910
2963
  }
2911
2964
  }
2912
2965
 
@@ -2980,6 +3033,7 @@ class Table extends ParsedElement {
2980
3033
  {{{{ schema.rowsTemplate.withOverlay({'rows': pageResponse.data}).render() }}}}
2981
3034
  `
2982
3035
  };
3036
+ #loader;
2983
3037
  #schema;
2984
3038
  #body;
2985
3039
  #loading;
@@ -2995,6 +3049,8 @@ class Table extends ParsedElement {
2995
3049
  const tableWrapper = /** @type HTMLTableElement */ (Nodes.queryChildren(fragment, '.table-wrapper'));
2996
3050
  const table = /** @type HTMLTableElement */ (tableWrapper.querySelector("table"));
2997
3051
  Attributes.forward('table-', this, table);
3052
+ this.#loader = registry.component(this.getAttribute("loader") ?? 'loaders:table').create(this);
3053
+
2998
3054
  this.#schema = schema;
2999
3055
  this.#body = table.querySelector(':scope > tbody');
3000
3056
  this.#loading = table.querySelector(":scope > tbody[data-ref=loading]");
@@ -3047,8 +3103,7 @@ class Table extends ParsedElement {
3047
3103
  this.#feedback.setAttribute("hidden", "");
3048
3104
  this.#noAutoload.setAttribute("hidden", "");
3049
3105
  try {
3050
- const loader = registry.component(this.getAttribute("loader") ?? 'loaders:table').create(this);
3051
- const pageResponse = await loader.load(pageRequest, sortRequest, filterRequest);
3106
+ const pageResponse = await this.#loader.load(pageRequest, sortRequest, filterRequest);
3052
3107
  this.#latestRequest = { pageRequest, sortRequest, filterRequest };
3053
3108
  this.#update(pageRequest, sortRequest, filterRequest, pageResponse);
3054
3109
  } catch (/** @type any */error) {
@@ -3062,14 +3117,15 @@ class Table extends ParsedElement {
3062
3117
  throw error;
3063
3118
  }
3064
3119
  }
3065
-
3120
+ async withLoader(fn) {
3121
+ return await fn(this.#loader);
3122
+ }
3066
3123
  async resetWithFilter(filterRequest) {
3067
3124
  return await this.load({
3068
3125
  page: 0,
3069
3126
  size: this.#latestRequest.pageRequest.size
3070
3127
  }, this.#latestRequest.sortRequest, filterRequest);
3071
3128
  }
3072
-
3073
3129
  #update(pageRequest, sortRequest, filterRequest, pageResponse) {
3074
3130
  this.#loading.setAttribute("hidden", "");
3075
3131
  this.#body.replaceChildren(this.template('row').withOverlay({