@optionfactory/ful 4.0.11 → 4.0.13
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.css +1 -1
- package/dist/ful.css.map +1 -1
- package/dist/ful.iife.js +270 -16
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +269 -17
- package/dist/ful.mjs.map +1 -1
- package/package.json +2 -2
package/dist/ful.mjs
CHANGED
|
@@ -1683,6 +1683,246 @@ class InputInstant extends Input {
|
|
|
1683
1683
|
}
|
|
1684
1684
|
}
|
|
1685
1685
|
|
|
1686
|
+
class InputFile extends Input {
|
|
1687
|
+
static l10n = {
|
|
1688
|
+
en: {
|
|
1689
|
+
'dropzonelabel': 'Click or drop your files here',
|
|
1690
|
+
'unaccepptablefiletype': "Only files of type {0} are supported",
|
|
1691
|
+
'maxfilesizeexceeded': "Maximum supported file size is {0}",
|
|
1692
|
+
'maxtotalsizeexceeded': "Maximum supported total file size is {0}"
|
|
1693
|
+
},
|
|
1694
|
+
it: {
|
|
1695
|
+
'dropzonelabel': 'Clicca o trascina i file qui',
|
|
1696
|
+
'unaccepptablefiletype': "Solo i file di tipo {0} sono supportati",
|
|
1697
|
+
'maxfilesizeexceeded': "La dimensione massima di un file è di {0}",
|
|
1698
|
+
'maxtotalsizeexceeded': "La dimensione massima complessiva dei file è di {0}"
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
static observed = ['value', 'readonly:presence', "accept:csv", 'multiple:presence', "itemlist:presence", "dropzone:presence", "maxfilesize:number", "maxtotalsize:number"];
|
|
1702
|
+
#accept;
|
|
1703
|
+
#items;
|
|
1704
|
+
#dropzone;
|
|
1705
|
+
#warnings;
|
|
1706
|
+
_type() {
|
|
1707
|
+
return 'file';
|
|
1708
|
+
}
|
|
1709
|
+
static template = `
|
|
1710
|
+
<div class="form-label">
|
|
1711
|
+
<label>{{{{ slots.default }}}}</label>
|
|
1712
|
+
{{{{ slots.info }}}}
|
|
1713
|
+
</div>
|
|
1714
|
+
<div class="input-group">
|
|
1715
|
+
<span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
|
|
1716
|
+
{{{{ slots.before }}}}
|
|
1717
|
+
<input class="form-control" data-tpl-type="type" placeholder=" " form="">
|
|
1718
|
+
{{{{ slots.after }}}}
|
|
1719
|
+
<span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
|
|
1720
|
+
</div>
|
|
1721
|
+
<div data-ref="dropzone" class="dropzone" data-tpl-if="slots.dropzone">
|
|
1722
|
+
{{{{ slots.dropzone }}}}
|
|
1723
|
+
</div>
|
|
1724
|
+
<div data-ref="dropzone" class="dropzone" data-tpl-if="!slots.dropzone">
|
|
1725
|
+
{{ #l10n:t('dropzonelabel') }}
|
|
1726
|
+
</div>
|
|
1727
|
+
<div data-ref="items" class="items"></div>
|
|
1728
|
+
<ful-field-warnings></ful-field-warnings>
|
|
1729
|
+
<ful-field-error></ful-field-error>
|
|
1730
|
+
`;
|
|
1731
|
+
static templates = {
|
|
1732
|
+
items: `
|
|
1733
|
+
<div class="item" data-tpl-each="files" data-tpl-var="file" data-tpl-data-name="file.name">
|
|
1734
|
+
<div class="filename"><span>{{ file.name }}</span></div>
|
|
1735
|
+
<div class="size">{{ #bytes:format(file.size) }}</div>
|
|
1736
|
+
<button class="btn btn-sm btn-outline-danger"><i class="bi bi-x-lg"></i></button>
|
|
1737
|
+
</div>
|
|
1738
|
+
`,
|
|
1739
|
+
warning: `<ful-field-warning>{{ #l10n:t(key, args) }}</ful-field-warning>`
|
|
1740
|
+
}
|
|
1741
|
+
render(conf) {
|
|
1742
|
+
const { observed } = conf;
|
|
1743
|
+
super.render(conf);
|
|
1744
|
+
this.#items = this.querySelector("[data-ref=items]");
|
|
1745
|
+
this.#dropzone = this.querySelector("[data-ref=dropzone]");
|
|
1746
|
+
this.#warnings = this.querySelector("ful-field-warnings");
|
|
1747
|
+
this.accept = observed.accept;
|
|
1748
|
+
this.multiple = observed.multiple;
|
|
1749
|
+
this.itemlist = observed.itemlist;
|
|
1750
|
+
this.dropzone = observed.dropzone;
|
|
1751
|
+
this.maxfilesize = observed.maxfilesize;
|
|
1752
|
+
this.maxtotalsize = observed.maxtotalsize;
|
|
1753
|
+
this.#warnings.addEventListener('animationend', e => {
|
|
1754
|
+
e.target.remove();
|
|
1755
|
+
});
|
|
1756
|
+
this.#items.addEventListener('click', (e) => {
|
|
1757
|
+
if (!e.target.closest("button")) {
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
const fileName = e.target.closest(".item").dataset.name;
|
|
1761
|
+
const dt = new DataTransfer();
|
|
1762
|
+
[...this.files].filter(f => f.name !== fileName).forEach(f => dt.items.add(f));
|
|
1763
|
+
this.files = dt.files;
|
|
1764
|
+
this.#update();
|
|
1765
|
+
});
|
|
1766
|
+
this.#dropzone.addEventListener("click", (e) => {
|
|
1767
|
+
this.querySelector('input')?.click();
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
this.#dropzone.addEventListener("dragover", (e) => {
|
|
1771
|
+
e.preventDefault();
|
|
1772
|
+
});
|
|
1773
|
+
this.#dropzone.addEventListener("drop", (e) => {
|
|
1774
|
+
e.preventDefault();
|
|
1775
|
+
const dt = new DataTransfer();
|
|
1776
|
+
[...e.dataTransfer.items].filter(i => i.kind === 'file').forEach(i => dt.items.add(i.getAsFile()));
|
|
1777
|
+
this.files = dt.files;
|
|
1778
|
+
this.#update();
|
|
1779
|
+
});
|
|
1780
|
+
this._input.addEventListener("change", (e) => {
|
|
1781
|
+
this.#update();
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
#formatByteSize(v) {
|
|
1785
|
+
return (v > 1024 * 1024) ? `${Math.round(v / 1024 / 1024 * 100) / 100}MiB` : (v > 1024 ? `${Math.round(v / 1024 * 100) / 100}KiB` : `${v}B`);
|
|
1786
|
+
}
|
|
1787
|
+
#update() {
|
|
1788
|
+
this.setCustomValidity();
|
|
1789
|
+
this.#ensureAcceptable();
|
|
1790
|
+
this.#ensureFileSizes();
|
|
1791
|
+
this.#ensureTotalSize();
|
|
1792
|
+
if(this.#useItemlist){
|
|
1793
|
+
this.template('items').withOverlay({ files: this.files }).withModule('bytes', { format: this.#formatByteSize }).renderTo(this.#items);
|
|
1794
|
+
}else {
|
|
1795
|
+
this.#items.replaceChildren();
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
warning(key, args) {
|
|
1799
|
+
this.template('warning').withOverlay({ key, args }).renderTo(this.#warnings);
|
|
1800
|
+
}
|
|
1801
|
+
#ensureAcceptable() {
|
|
1802
|
+
if (!this.#accept) {
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
const unacceptable = [...this.files]
|
|
1806
|
+
.filter(file => this.#accept.some(type => !file.name.toLowerCase().endsWith(type.toLowerCase())));
|
|
1807
|
+
|
|
1808
|
+
if (unacceptable.length === 0) {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
this.warning('unaccepptablefiletype', this.#accept.join(","));
|
|
1812
|
+
const dt = new DataTransfer();
|
|
1813
|
+
[...this.files].filter(f => !unacceptable.includes(f)).forEach(f => dt.items.add(f));
|
|
1814
|
+
this.files = dt.files;
|
|
1815
|
+
}
|
|
1816
|
+
#ensureFileSizes() {
|
|
1817
|
+
if (this.#maxfilesize === null) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const oversized = [...this.files]
|
|
1821
|
+
.filter(file => file.size > this.#maxfilesize);
|
|
1822
|
+
if (oversized.length === 0) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
this.warning('maxfilesizeexceeded', this.#formatByteSize(this.#maxfilesize));
|
|
1826
|
+
const dt = new DataTransfer();
|
|
1827
|
+
[...this.files].filter(f => !oversized.includes(f)).forEach(f => dt.items.add(f));
|
|
1828
|
+
this.files = dt.files;
|
|
1829
|
+
}
|
|
1830
|
+
#ensureTotalSize() {
|
|
1831
|
+
if (this.#maxtotalsize === null) {
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
const totalSize = [...this.files].reduce((acc, file) => acc + file.size, 0);
|
|
1835
|
+
if (totalSize <= this.#maxtotalsize) {
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
this.warning('maxtotalsizeexceeded', this.#formatByteSize(this.#maxtotalsize));
|
|
1839
|
+
this.files = new DataTransfer().files;
|
|
1840
|
+
}
|
|
1841
|
+
get accept() {
|
|
1842
|
+
return this.#accept;
|
|
1843
|
+
}
|
|
1844
|
+
set accept(vs) {
|
|
1845
|
+
this._input.accept = vs.join(",");
|
|
1846
|
+
this.#accept = vs;
|
|
1847
|
+
this.reflect(() => {
|
|
1848
|
+
this.setAttribute('accept', this._input.accept);
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
get multiple() {
|
|
1852
|
+
return this._input.multiple;
|
|
1853
|
+
}
|
|
1854
|
+
set multiple(v) {
|
|
1855
|
+
this._input.multiple = v;
|
|
1856
|
+
this.reflect(() => {
|
|
1857
|
+
this.setAttribute('multiple', this._input.multiple);
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
get files() {
|
|
1861
|
+
return this._input.files;
|
|
1862
|
+
}
|
|
1863
|
+
set files(vs) {
|
|
1864
|
+
this._input.files = vs;
|
|
1865
|
+
}
|
|
1866
|
+
get file() {
|
|
1867
|
+
return this.files[0] ?? null;
|
|
1868
|
+
}
|
|
1869
|
+
set file(v) {
|
|
1870
|
+
const dt = new DataTransfer();
|
|
1871
|
+
dt.items.add(v);
|
|
1872
|
+
this.files = dt.files;
|
|
1873
|
+
}
|
|
1874
|
+
get value() {
|
|
1875
|
+
const names = Array.from(this._input.files).map(f => f.name);
|
|
1876
|
+
return this.multiple ? names : (names[0] ?? null);
|
|
1877
|
+
}
|
|
1878
|
+
set value(v) {
|
|
1879
|
+
//TODO:
|
|
1880
|
+
}
|
|
1881
|
+
#maxfilesize;
|
|
1882
|
+
get maxfilesize() {
|
|
1883
|
+
return this.#maxfilesize;
|
|
1884
|
+
}
|
|
1885
|
+
set maxfilesize(v) {
|
|
1886
|
+
this.#maxfilesize = v;
|
|
1887
|
+
this.reflect(() => {
|
|
1888
|
+
this.setAttribute('maxfilesize', v);
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
}
|
|
1892
|
+
#maxtotalsize;
|
|
1893
|
+
get maxtotalsize() {
|
|
1894
|
+
return this.#maxtotalsize;
|
|
1895
|
+
}
|
|
1896
|
+
set maxtotalsize(v) {
|
|
1897
|
+
this.#maxtotalsize = v;
|
|
1898
|
+
this.reflect(() => {
|
|
1899
|
+
this.setAttribute('maxtotalsize', v);
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
#useItemlist;
|
|
1903
|
+
get itemlist() {
|
|
1904
|
+
return this.#useItemlist;
|
|
1905
|
+
}
|
|
1906
|
+
set itemlist(v) {
|
|
1907
|
+
this.#useItemlist = v;
|
|
1908
|
+
Attributes.toggle(this.#items, "hidden", !v);
|
|
1909
|
+
this.reflect(() => {
|
|
1910
|
+
Attributes.toggle(this, "itemlist", v);
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
#useDropzone;
|
|
1914
|
+
get dropzone() {
|
|
1915
|
+
return this.#useDropzone;
|
|
1916
|
+
}
|
|
1917
|
+
set dropzone(v) {
|
|
1918
|
+
this.#useDropzone = v;
|
|
1919
|
+
Attributes.toggle(this.#dropzone, "hidden", !v);
|
|
1920
|
+
this.reflect(() => {
|
|
1921
|
+
Attributes.toggle(this, "dropzone", v);
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1686
1926
|
class RemoteLoader {
|
|
1687
1927
|
#http;
|
|
1688
1928
|
#url;
|
|
@@ -1724,6 +1964,10 @@ class RemoteLoader {
|
|
|
1724
1964
|
await this.#ensureFetched();
|
|
1725
1965
|
return this.#data.filter(([k, v]) => (v ?? '').toLowerCase().includes(needle?.toLowerCase()));
|
|
1726
1966
|
}
|
|
1967
|
+
async reconfigureUrl(url){
|
|
1968
|
+
this.#data = null;
|
|
1969
|
+
this.#url = url;
|
|
1970
|
+
}
|
|
1727
1971
|
async #ensureFetched() {
|
|
1728
1972
|
if (this.#data !== null) {
|
|
1729
1973
|
return
|
|
@@ -2067,8 +2311,8 @@ class Select extends ParsedElement {
|
|
|
2067
2311
|
});
|
|
2068
2312
|
this.replaceChildren(fragment);
|
|
2069
2313
|
}
|
|
2070
|
-
withLoader(fn) {
|
|
2071
|
-
fn(this.#loader);
|
|
2314
|
+
async withLoader(fn) {
|
|
2315
|
+
await fn(this.#loader);
|
|
2072
2316
|
}
|
|
2073
2317
|
#changed() {
|
|
2074
2318
|
const selection = [...this.#values.entries()].map(e => ({key: e[0], label: e[1][0], metadata: e[1].slice(1)}));
|
|
@@ -2087,7 +2331,7 @@ class Select extends ParsedElement {
|
|
|
2087
2331
|
b.innerText = v[0];
|
|
2088
2332
|
return b;
|
|
2089
2333
|
});
|
|
2090
|
-
this.#badges.
|
|
2334
|
+
this.#badges.replaceChildren();
|
|
2091
2335
|
this.#badges.append(...badges);
|
|
2092
2336
|
}
|
|
2093
2337
|
set value(vs) {
|
|
@@ -2129,7 +2373,7 @@ class Select extends ParsedElement {
|
|
|
2129
2373
|
this.reflect(() => {
|
|
2130
2374
|
Attributes.toggle(this, 'readonly', v);
|
|
2131
2375
|
});
|
|
2132
|
-
}
|
|
2376
|
+
}
|
|
2133
2377
|
focus(options) {
|
|
2134
2378
|
this.#input.focus(options);
|
|
2135
2379
|
}
|
|
@@ -2726,7 +2970,7 @@ class Table extends ParsedElement {
|
|
|
2726
2970
|
return await this.load(this.#latestRequest.pageRequest, this.#latestRequest.sortRequest, this.#latestRequest.filterRequest);
|
|
2727
2971
|
}
|
|
2728
2972
|
async load(pageRequest, sortRequest, filterRequest) {
|
|
2729
|
-
this.#body.
|
|
2973
|
+
this.#body.replaceChildren();
|
|
2730
2974
|
this.#loading.removeAttribute("hidden", "");
|
|
2731
2975
|
this.#feedback.setAttribute("hidden", "");
|
|
2732
2976
|
this.#noAutoload.setAttribute("hidden", "");
|
|
@@ -3045,6 +3289,23 @@ class TextFilter extends ParsedElement {
|
|
|
3045
3289
|
}
|
|
3046
3290
|
}
|
|
3047
3291
|
|
|
3292
|
+
class LocalizationModule {
|
|
3293
|
+
static t(k, ...args) {
|
|
3294
|
+
//@ts-ignore
|
|
3295
|
+
const format = this.l10n[this.language][k] ?? this.l10n['en'][k] ?? k;
|
|
3296
|
+
if (args.length === 0) {
|
|
3297
|
+
return format;
|
|
3298
|
+
}
|
|
3299
|
+
return format.replace(/{(\d+)}/g, (m, is) => {
|
|
3300
|
+
return args[Number(is)];
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
static tl(k, args) {
|
|
3304
|
+
return LocalizationModule.t(k, ...args);
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3048
3309
|
class Plugin {
|
|
3049
3310
|
configure(registry) {
|
|
3050
3311
|
const httpClient = HttpClient.builder()
|
|
@@ -3052,22 +3313,13 @@ class Plugin {
|
|
|
3052
3313
|
.withRedirectOnUnauthorized("/")
|
|
3053
3314
|
.build();
|
|
3054
3315
|
registry
|
|
3055
|
-
.defineModule("l10n",
|
|
3056
|
-
t: function (k, ...args) {
|
|
3057
|
-
const format = this.l10n[this.language][k] ?? this.l10n['en'][k] ?? k;
|
|
3058
|
-
if (args.length === 0) {
|
|
3059
|
-
return format;
|
|
3060
|
-
}
|
|
3061
|
-
return format.replace(/{(\d+)}/g, (m, is) => {
|
|
3062
|
-
return args[Number(is)];
|
|
3063
|
-
});
|
|
3064
|
-
}
|
|
3065
|
-
})
|
|
3316
|
+
.defineModule("l10n", LocalizationModule)
|
|
3066
3317
|
.defineComponent('http-client', httpClient)
|
|
3067
3318
|
.defineElement('ful-spinner', Spinner)
|
|
3068
3319
|
.defineElement('ful-form', Form)
|
|
3069
3320
|
.defineElement('ful-checkbox', Checkbox)
|
|
3070
3321
|
.defineElement('ful-input', Input)
|
|
3322
|
+
.defineElement('ful-input-file', InputFile)
|
|
3071
3323
|
.defineElement('ful-local-date', LocalDate)
|
|
3072
3324
|
.defineElement('ful-instant', Instant)
|
|
3073
3325
|
.defineElement('ful-input-local-date', InputLocalDate)
|
|
@@ -3091,5 +3343,5 @@ class Plugin {
|
|
|
3091
3343
|
}
|
|
3092
3344
|
}
|
|
3093
3345
|
|
|
3094
|
-
export { AsyncEvents, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Bindings, Checkbox, Dropdown, Failure, Form, FormLoader, Hex, HttpClient, HttpClientError, Input, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, Loaders, LocalDate, LocalDateFilter, LocalStorage, MediaType, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Spinner, Table, TableSchemaParser, TextFilter, Timing, VersionedLocalStorage, VersionedSessionStorage };
|
|
3346
|
+
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 };
|
|
3095
3347
|
//# sourceMappingURL=ful.mjs.map
|