@optionfactory/ful 4.0.13 → 4.0.15

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
@@ -1462,7 +1462,7 @@ var ful = (function (exports, ftl) {
1462
1462
  _fragment(type, slots) {
1463
1463
  return this.template().withOverlay({ type, slots }).render();
1464
1464
  }
1465
- render({ slots, observed, disabled }) {
1465
+ render({ slots, observed, disabled, skipValueSetup }) {
1466
1466
  const type = this._type();
1467
1467
  const fragment = this._fragment(type, slots);
1468
1468
  this._input = fragment.querySelector("input,textarea");
@@ -1470,7 +1470,9 @@ var ful = (function (exports, ftl) {
1470
1470
  ftl.Attributes.forward('input-', this, this._input);
1471
1471
  this.disabled = disabled;
1472
1472
  this.readonly = observed.readonly;
1473
- this.value = observed.value;
1473
+ if(!skipValueSetup){
1474
+ this.value = observed.value;
1475
+ }
1474
1476
 
1475
1477
  this._input.addEventListener('change', (evt) => {
1476
1478
  evt.stopPropagation();
@@ -1725,24 +1727,24 @@ var ful = (function (exports, ftl) {
1725
1727
  <div data-ref="dropzone" class="dropzone" data-tpl-if="!slots.dropzone">
1726
1728
  {{ #l10n:t('dropzonelabel') }}
1727
1729
  </div>
1728
- <div data-ref="items" class="items"></div>
1730
+ <ful-item-list></ful-item-list>
1729
1731
  <ful-field-warnings></ful-field-warnings>
1730
1732
  <ful-field-error></ful-field-error>
1731
1733
  `;
1732
1734
  static templates = {
1733
1735
  items: `
1734
- <div class="item" data-tpl-each="files" data-tpl-var="file" data-tpl-data-name="file.name">
1735
- <div class="filename"><span>{{ file.name }}</span></div>
1736
- <div class="size">{{ #bytes:format(file.size) }}</div>
1737
- <button class="btn btn-sm btn-outline-danger"><i class="bi bi-x-lg"></i></button>
1738
- </div>
1736
+ <ful-item data-tpl-each="files" data-tpl-var="file" data-tpl-data-name="file.name">
1737
+ <div>{{ file.name }}</div>
1738
+ <div>{{ #bytes:format(file.size) }}</div>
1739
+ <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg"></button>
1740
+ </ful-item>
1739
1741
  `,
1740
1742
  warning: `<ful-field-warning>{{ #l10n:t(key, args) }}</ful-field-warning>`
1741
1743
  }
1742
1744
  render(conf) {
1743
1745
  const { observed } = conf;
1744
1746
  super.render(conf);
1745
- this.#items = this.querySelector("[data-ref=items]");
1747
+ this.#items = this.querySelector("ful-item-list");
1746
1748
  this.#dropzone = this.querySelector("[data-ref=dropzone]");
1747
1749
  this.#warnings = this.querySelector("ful-field-warnings");
1748
1750
  this.accept = observed.accept;
@@ -1758,7 +1760,7 @@ var ful = (function (exports, ftl) {
1758
1760
  if (!e.target.closest("button")) {
1759
1761
  return;
1760
1762
  }
1761
- const fileName = e.target.closest(".item").dataset.name;
1763
+ const fileName = e.target.closest("ful-item").dataset.name;
1762
1764
  const dt = new DataTransfer();
1763
1765
  [...this.files].filter(f => f.name !== fileName).forEach(f => dt.items.add(f));
1764
1766
  this.files = dt.files;
@@ -1790,11 +1792,7 @@ var ful = (function (exports, ftl) {
1790
1792
  this.#ensureAcceptable();
1791
1793
  this.#ensureFileSizes();
1792
1794
  this.#ensureTotalSize();
1793
- if(this.#useItemlist){
1794
- this.template('items').withOverlay({ files: this.files }).withModule('bytes', { format: this.#formatByteSize }).renderTo(this.#items);
1795
- }else {
1796
- this.#items.replaceChildren();
1797
- }
1795
+ this.template('items').withOverlay({ files: this.files }).withModule('bytes', { format: this.#formatByteSize }).renderTo(this.#items);
1798
1796
  }
1799
1797
  warning(key, args) {
1800
1798
  this.template('warning').withOverlay({ key, args }).renderTo(this.#warnings);
@@ -1855,7 +1853,7 @@ var ful = (function (exports, ftl) {
1855
1853
  set multiple(v) {
1856
1854
  this._input.multiple = v;
1857
1855
  this.reflect(() => {
1858
- this.setAttribute('multiple', this._input.multiple);
1856
+ ftl.Attributes.toggle(this, "multiple", v);
1859
1857
  });
1860
1858
  }
1861
1859
  get files() {
@@ -1888,7 +1886,6 @@ var ful = (function (exports, ftl) {
1888
1886
  this.reflect(() => {
1889
1887
  this.setAttribute('maxfilesize', v);
1890
1888
  });
1891
-
1892
1889
  }
1893
1890
  #maxtotalsize;
1894
1891
  get maxtotalsize() {
@@ -1906,7 +1903,6 @@ var ful = (function (exports, ftl) {
1906
1903
  }
1907
1904
  set itemlist(v) {
1908
1905
  this.#useItemlist = v;
1909
- ftl.Attributes.toggle(this.#items, "hidden", !v);
1910
1906
  this.reflect(() => {
1911
1907
  ftl.Attributes.toggle(this, "itemlist", v);
1912
1908
  });
@@ -1917,7 +1913,6 @@ var ful = (function (exports, ftl) {
1917
1913
  }
1918
1914
  set dropzone(v) {
1919
1915
  this.#useDropzone = v;
1920
- ftl.Attributes.toggle(this.#dropzone, "hidden", !v);
1921
1916
  this.reflect(() => {
1922
1917
  ftl.Attributes.toggle(this, "dropzone", v);
1923
1918
  });
@@ -2146,7 +2141,7 @@ var ful = (function (exports, ftl) {
2146
2141
  }
2147
2142
 
2148
2143
  class Select extends ftl.ParsedElement {
2149
- static observed = ['value:csvm', 'readonly:presence']
2144
+ static observed = ['value:csvm', 'readonly:presence', 'itemlist:presence']
2150
2145
  static slots = true
2151
2146
  static template = `
2152
2147
  <div class="form-label">
@@ -2166,8 +2161,17 @@ var ful = (function (exports, ftl) {
2166
2161
  {{{{ slots.after }}}}
2167
2162
  <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
2168
2163
  </div>
2164
+ <ful-item-list></ful-item-list>
2169
2165
  <ful-field-error></ful-field-error>
2170
2166
  `;
2167
+ static templates = {
2168
+ items: `
2169
+ <ful-item data-tpl-each="entries" data-tpl-var="entry" data-tpl-data-key="entry[0]">
2170
+ <div>{{ entry[1][0] }}</div>
2171
+ <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg"></button>
2172
+ </ful-item>
2173
+ `
2174
+ }
2171
2175
  static mappers = {
2172
2176
  "csvm": (v, name, el) => {
2173
2177
  if (el.hasAttribute("multiple")) {
@@ -2182,6 +2186,7 @@ var ful = (function (exports, ftl) {
2182
2186
  #badges
2183
2187
  #ddmenu
2184
2188
  #input
2189
+ #items;
2185
2190
  #multiple
2186
2191
  #fieldError
2187
2192
  #values = new Map()
@@ -2197,12 +2202,14 @@ var ful = (function (exports, ftl) {
2197
2202
  await this.#loader.prefetch?.();
2198
2203
  const fragment = this.template().withOverlay({ slots, name }).render();
2199
2204
  this.#input = fragment.querySelector('input');
2205
+ this.#items = fragment.querySelector("ful-item-list");
2200
2206
  ftl.Attributes.forward('input-', this, this.#input);
2201
2207
  this.#badges = fragment.querySelector('badges');
2202
2208
 
2203
2209
  this.value = observed.value;
2204
2210
  this.disabled = disabled;
2205
2211
  this.readonly = observed.readonly;
2212
+ this.itemlist = observed.itemlist;
2206
2213
 
2207
2214
  this.#ddmenu = fragment.querySelector('ful-dropdown');
2208
2215
  const label = fragment.querySelector('label');
@@ -2227,6 +2234,22 @@ var ful = (function (exports, ftl) {
2227
2234
  this.#input.focus();
2228
2235
  dload();
2229
2236
  });
2237
+ this.#items.addEventListener('click', (e) => {
2238
+ e.stopPropagation();
2239
+ if (!e.target.closest("button")) {
2240
+ return;
2241
+ }
2242
+ if(this.disabled || this.readonly){
2243
+ return;
2244
+ }
2245
+ const idx = [...this.#items.children].indexOf(e.target.closest('ful-item'));
2246
+ if (idx === -1) {
2247
+ return;
2248
+ }
2249
+ this.#values.delete(Array.from(this.#values.keys()).pop());
2250
+ this.#changed();
2251
+ this.#syncBadges();
2252
+ });
2230
2253
  this.#badges.addEventListener('click', (e) => {
2231
2254
  e.stopPropagation();
2232
2255
  if(this.disabled || this.readonly){
@@ -2334,6 +2357,8 @@ var ful = (function (exports, ftl) {
2334
2357
  });
2335
2358
  this.#badges.replaceChildren();
2336
2359
  this.#badges.append(...badges);
2360
+ this.#items.replaceChildren();
2361
+ this.template('items').withOverlay({ entries: this.#values.entries() }).renderTo(this.#items);
2337
2362
  }
2338
2363
  set value(vs) {
2339
2364
  if(vs === null){
@@ -2375,6 +2400,16 @@ var ful = (function (exports, ftl) {
2375
2400
  ftl.Attributes.toggle(this, 'readonly', v);
2376
2401
  });
2377
2402
  }
2403
+ #useItemlist;
2404
+ get itemlist() {
2405
+ return this.#useItemlist;
2406
+ }
2407
+ set itemlist(v) {
2408
+ this.#useItemlist = v;
2409
+ this.reflect(() => {
2410
+ ftl.Attributes.toggle(this, "itemlist", v);
2411
+ });
2412
+ }
2378
2413
  focus(options) {
2379
2414
  this.#input.focus(options);
2380
2415
  }
@@ -3012,15 +3047,16 @@ var ful = (function (exports, ftl) {
3012
3047
  }
3013
3048
  }
3014
3049
 
3015
- class InstantFilter extends ftl.ParsedElement {
3016
- static observed = ["value:json"];
3017
- static slots = true;
3050
+ class InstantFilter extends Input {
3051
+ static observed = ['value:json', 'readonly:presence'];
3018
3052
  static template = `
3019
- <div class="form-label" data-tpl-if="label">
3020
- <label>{{{{ label }}}}</label>
3053
+ <div class="form-label">
3054
+ <label>{{{{ slots.default }}}}</label>
3021
3055
  {{{{ slots.info }}}}
3022
3056
  </div>
3023
3057
  <div class="input-group">
3058
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3059
+ {{{{ slots.before }}}}
3024
3060
  <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>
3025
3061
  <ul class="dropdown-menu">
3026
3062
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
@@ -3033,32 +3069,21 @@ var ful = (function (exports, ftl) {
3033
3069
  </ul>
3034
3070
  <input data-ref="value1" type="datetime-local" class="form-control" form="">
3035
3071
  <input data-ref="value2" type="datetime-local" class="form-control" form="" hidden>
3036
- <span class="input-group-text"><i class="bi bi-search"></i></span>
3072
+ {{{{ slots.after }}}}
3073
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
3037
3074
  </div>
3038
3075
  <ful-field-error></ful-field-error>
3039
3076
  `;
3040
- static formAssociated = true;
3041
3077
  #operator;
3042
3078
  #value1;
3043
3079
  #value2;
3044
- #fieldError;
3045
- constructor() {
3046
- super();
3047
- this.internals = this.attachInternals();
3048
- }
3049
- render({ slots }) {
3050
- const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3051
- const name = this.getAttribute("name");
3052
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3053
- this.#operator = fragment.querySelector('[data-ref=operator]');
3054
- this.#value1 = fragment.querySelector('[data-ref=value1]');
3055
- this.#value2 = fragment.querySelector('[data-ref=value2]');
3056
- this.#fieldError = fragment.querySelector('ful-field-error');
3057
- const labelEl = fragment.querySelector('label');
3058
- labelEl?.addEventListener('click', () => this.focus());
3059
- this.#value1.ariaDescribedByElements = [this.#fieldError];
3060
- this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
3061
- this.replaceChildren(fragment);
3080
+ render(conf) {
3081
+ super.render({...conf, skipValueSetup: true});
3082
+ this.#operator = this.querySelector('[data-ref=operator]');
3083
+ this.#value1 = this.querySelector('[data-ref=value1]');
3084
+ this.#value2 = this.querySelector('[data-ref=value2]');
3085
+ this.value = conf.observed.value;
3086
+
3062
3087
  this.addEventListener('click', (evt) => {
3063
3088
  const target = /** @type HTMLElement */ (evt.target);
3064
3089
  if (!target.matches('ul > li > a')) {
@@ -3081,42 +3106,25 @@ var ful = (function (exports, ftl) {
3081
3106
  if (v === null || v === undefined) {
3082
3107
  this.#value1.value = '';
3083
3108
  this.#value2.value = '';
3084
- this.reflect(() => {
3085
- this.removeAttribute('value');
3086
- });
3087
3109
  return;
3088
3110
  }
3089
3111
  const [operator, ...values] = v;
3090
3112
  this.#operator.setAttribute('value', operator);
3091
3113
  this.#value1.value = values[0] ? Instant.isoToLocal(values[0]) : values[0];
3092
3114
  this.#value2.value = values[1] ? Instant.isoToLocal(values[1]) : values[1];
3093
- this.reflect(() => {
3094
- this.setAttribute('value', JSON.stringify(v));
3095
- });
3096
- }
3097
- focus(options) {
3098
- this.#value1.focus(options);
3099
- }
3100
- setCustomValidity(error) {
3101
- if (!error) {
3102
- this.internals.setValidity({});
3103
- this.#fieldError.innerText = "";
3104
- return;
3105
- }
3106
- this.internals.setValidity({ customError: true }, " ");
3107
- this.#fieldError.innerText = error;
3108
3115
  }
3109
3116
  }
3110
3117
 
3111
- class LocalDateFilter extends ftl.ParsedElement {
3112
- static observed = ["value:json"];
3113
- static slots = true;
3118
+ class LocalDateFilter extends Input {
3119
+ static observed = ["value:json", 'readonly:presence'];
3114
3120
  static template = `
3115
- <div class="form-label" data-tpl-if="label">
3116
- <label>{{{{ label }}}}</label>
3121
+ <div class="form-label">
3122
+ <label>{{{{ slots.default }}}}</label>
3117
3123
  {{{{ slots.info }}}}
3118
3124
  </div>
3119
3125
  <div class="input-group">
3126
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3127
+ {{{{ slots.before }}}}
3120
3128
  <button data-ref="operator" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" value="EQ" form="">=</button>
3121
3129
  <ul class="dropdown-menu">
3122
3130
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
@@ -3129,32 +3137,22 @@ var ful = (function (exports, ftl) {
3129
3137
  </ul>
3130
3138
  <input data-ref="value1" type="date" class="form-control" form="">
3131
3139
  <input data-ref="value2" type="date" class="form-control" form="" hidden>
3132
- <span class="input-group-text"><i class="bi bi-search"></i></span>
3140
+ {{{{ slots.after }}}}
3141
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
3133
3142
  </div>
3134
3143
  <ful-field-error></ful-field-error>
3135
3144
  `;
3136
- static formAssociated = true;
3137
3145
  #operator;
3138
3146
  #value1;
3139
3147
  #value2;
3140
- #fieldError;
3141
- constructor() {
3142
- super();
3143
- this.internals = this.attachInternals();
3144
- }
3145
- render({ slots }) {
3146
- const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3147
- const name = this.getAttribute("name");
3148
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3149
- this.#operator = fragment.querySelector('[data-ref=operator]');
3150
- this.#value1 = fragment.querySelector('[data-ref=value1]');
3151
- this.#value2 = fragment.querySelector('[data-ref=value2]');
3152
- this.#fieldError = fragment.querySelector('ful-field-error');
3153
- const labelEl = fragment.querySelector('label');
3154
- labelEl?.addEventListener('click', () => this.focus());
3155
- this.#value1.ariaDescribedByElements = [this.#fieldError];
3156
- this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
3157
- this.replaceChildren(fragment);
3148
+ render(conf) {
3149
+ super.render({...conf, skipValueSetup: true});
3150
+
3151
+ this.#operator = this.querySelector('[data-ref=operator]');
3152
+ this.#value1 = this.querySelector('[data-ref=value1]');
3153
+ this.#value2 = this.querySelector('[data-ref=value2]');
3154
+ this.value = conf.observed.value;
3155
+
3158
3156
  this.addEventListener('click', (evt) => {
3159
3157
  const target = /** @type HTMLElement */(evt.target);
3160
3158
  if (!target.matches('ul > li > a')) {
@@ -3176,42 +3174,25 @@ var ful = (function (exports, ftl) {
3176
3174
  if (v === null || v === undefined) {
3177
3175
  this.#value1.value = '';
3178
3176
  this.#value2.value = '';
3179
- this.reflect(() => {
3180
- this.removeAttribute('value');
3181
- });
3182
3177
  return;
3183
3178
  }
3184
3179
  const [operator, ...values] = v;
3185
3180
  this.#operator.setAttibute('value', operator);
3186
3181
  this.#value1.value = values[0];
3187
3182
  this.#value2.value = values[1];
3188
- this.reflect(() => {
3189
- this.setAttribute('value', JSON.stringify(v));
3190
- });
3191
- }
3192
- focus(options) {
3193
- this.#value1.focus(options);
3194
- }
3195
- setCustomValidity(error) {
3196
- if (!error) {
3197
- this.internals.setValidity({});
3198
- this.#fieldError.innerText = "";
3199
- return;
3200
- }
3201
- this.internals.setValidity({ customError: true }, " ");
3202
- this.#fieldError.innerText = error;
3203
3183
  }
3204
3184
  }
3205
3185
 
3206
- class TextFilter extends ftl.ParsedElement {
3207
- static observed = ["value:json"];
3208
- static slots = true;
3186
+ class TextFilter extends Input {
3187
+ static observed = ["value:json", 'readonly:presence'];
3209
3188
  static template = `
3210
- <div class="form-label" data-tpl-if="label">
3211
- <label>{{{{ label }}}}</label>
3189
+ <div class="form-label">
3190
+ <label>{{{{ slots.default }}}}</label>
3212
3191
  {{{{ slots.info }}}}
3213
3192
  </div>
3214
3193
  <div class="input-group">
3194
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3195
+ {{{{ slots.before }}}}
3215
3196
  <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>
3216
3197
  <ul class="dropdown-menu">
3217
3198
  <li><a class="dropdown-item" role="button" value="CONTAINS">&mldr;a&mldr;</a></li>
@@ -3220,30 +3201,20 @@ var ful = (function (exports, ftl) {
3220
3201
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
3221
3202
  </ul>
3222
3203
  <input data-ref="value" type="text" class="form-control" form="">
3223
- <span class="input-group-text"><i class="bi bi-search"></i></span>
3204
+ {{{{ slots.after }}}}
3205
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
3224
3206
  </div>
3225
3207
  <ful-field-error></ful-field-error>
3226
3208
  `;
3227
- static formAssociated = true;
3228
3209
  #operator;
3229
3210
  #value;
3230
- #fieldError;
3231
- constructor() {
3232
- super();
3233
- this.internals = this.attachInternals();
3234
- }
3235
- render({ slots }) {
3236
- const label = ftl.Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3237
- const name = this.getAttribute("name");
3238
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3239
- this.#operator = fragment.querySelector('[data-ref=operator]');
3240
- this.#value = fragment.querySelector('[data-ref=value]');
3241
- this.#fieldError = fragment.querySelector('ful-field-error');
3242
- const labelEl = fragment.querySelector('label');
3243
- labelEl?.addEventListener('click', () => this.focus());
3244
- this.#value.ariaDescribedByElements = [this.#fieldError];
3245
- this.#value.ariaLabelledByElements = labelEl ? [labelEl] : [];
3246
- this.replaceChildren(fragment);
3211
+ render(conf) {
3212
+ super.render({...conf, skipValueSetup: true});
3213
+
3214
+ this.#operator = this.querySelector('[data-ref=operator]');
3215
+ this.#value = this.querySelector('[data-ref=value]');
3216
+ this.value = conf.observed.value;
3217
+
3247
3218
  this.addEventListener('click', (evt) => {
3248
3219
  const target = /** @type HTMLElement */(evt.target);
3249
3220
  if (!target.matches('ul > li > a')) {
@@ -3264,29 +3235,11 @@ var ful = (function (exports, ftl) {
3264
3235
  set value(v) {
3265
3236
  if (v === null || v === undefined) {
3266
3237
  this.#value.value = '';
3267
- this.reflect(() => {
3268
- this.removeAttribute('value');
3269
- });
3270
3238
  return;
3271
3239
  }
3272
3240
  const [operator, sensitivity, value] = v;
3273
3241
  this.#operator.setAttribute('value', operator);
3274
3242
  this.#value.value = value;
3275
- this.reflect(() => {
3276
- this.setAttribute('value', JSON.stringify(v));
3277
- });
3278
- }
3279
- focus(options) {
3280
- this.#value.focus(options);
3281
- }
3282
- setCustomValidity(error) {
3283
- if (!error) {
3284
- this.internals.setValidity({});
3285
- this.#fieldError.innerText = "";
3286
- return;
3287
- }
3288
- this.internals.setValidity({ customError: true }, " ");
3289
- this.#fieldError.innerText = error;
3290
3243
  }
3291
3244
  }
3292
3245