@optionfactory/ful 4.0.14 → 4.0.16

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
@@ -1461,15 +1461,17 @@ class Input extends ParsedElement {
1461
1461
  _fragment(type, slots) {
1462
1462
  return this.template().withOverlay({ type, slots }).render();
1463
1463
  }
1464
- render({ slots, observed, disabled }) {
1464
+ render({ slots, observed, disabled, skipObservedSetup }) {
1465
1465
  const type = this._type();
1466
1466
  const fragment = this._fragment(type, slots);
1467
1467
  this._input = fragment.querySelector("input,textarea");
1468
1468
 
1469
1469
  Attributes.forward('input-', this, this._input);
1470
- this.disabled = disabled;
1471
- this.readonly = observed.readonly;
1472
- this.value = observed.value;
1470
+ if (!skipObservedSetup) {
1471
+ this.disabled = disabled;
1472
+ this.readonly = observed.readonly;
1473
+ this.value = observed.value;
1474
+ }
1473
1475
 
1474
1476
  this._input.addEventListener('change', (evt) => {
1475
1477
  evt.stopPropagation();
@@ -2164,7 +2166,7 @@ class Select extends ParsedElement {
2164
2166
  static templates = {
2165
2167
  items: `
2166
2168
  <ful-item data-tpl-each="entries" data-tpl-var="entry" data-tpl-data-key="entry[0]">
2167
- <div>{{ entry[1] }}</div>
2169
+ <div>{{ entry[1][0] }}</div>
2168
2170
  <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg"></button>
2169
2171
  </ful-item>
2170
2172
  `
@@ -2990,7 +2992,8 @@ class Table extends ParsedElement {
2990
2992
  }, this.#latestRequest.sortRequest, this.#latestRequest.filterRequest);
2991
2993
  });
2992
2994
  this.addEventListener('sort-requested', async (/** @type any */e) => {
2993
- await this.load(this.#latestRequest.pageRequest, e.detail.value, this.#latestRequest.filterRequest);
2995
+ const sortRequest = e.detail.value.order ? e.detail.value : null;
2996
+ await this.load(this.#latestRequest.pageRequest, sortRequest, this.#latestRequest.filterRequest);
2994
2997
  this.#sorters.forEach(s => s.order = null);
2995
2998
  e.target.order = e.detail.value.order;
2996
2999
  });
@@ -3044,15 +3047,16 @@ class Table extends ParsedElement {
3044
3047
  }
3045
3048
  }
3046
3049
 
3047
- class InstantFilter extends ParsedElement {
3048
- static observed = ["value:json"];
3049
- static slots = true;
3050
+ class InstantFilter extends Input {
3051
+ static observed = ['value:json', 'readonly:presence'];
3050
3052
  static template = `
3051
- <div class="form-label" data-tpl-if="label">
3052
- <label>{{{{ label }}}}</label>
3053
+ <div class="form-label">
3054
+ <label>{{{{ slots.default }}}}</label>
3053
3055
  {{{{ slots.info }}}}
3054
3056
  </div>
3055
3057
  <div class="input-group">
3058
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3059
+ {{{{ slots.before }}}}
3056
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>
3057
3061
  <ul class="dropdown-menu">
3058
3062
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
@@ -3065,32 +3069,24 @@ class InstantFilter extends ParsedElement {
3065
3069
  </ul>
3066
3070
  <input data-ref="value1" type="datetime-local" class="form-control" form="">
3067
3071
  <input data-ref="value2" type="datetime-local" class="form-control" form="" hidden>
3068
- <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>
3069
3074
  </div>
3070
3075
  <ful-field-error></ful-field-error>
3071
3076
  `;
3072
- static formAssociated = true;
3073
3077
  #operator;
3074
3078
  #value1;
3075
3079
  #value2;
3076
- #fieldError;
3077
- constructor() {
3078
- super();
3079
- this.internals = this.attachInternals();
3080
- }
3081
- render({ slots }) {
3082
- const label = Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3083
- const name = this.getAttribute("name");
3084
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3085
- this.#operator = fragment.querySelector('[data-ref=operator]');
3086
- this.#value1 = fragment.querySelector('[data-ref=value1]');
3087
- this.#value2 = fragment.querySelector('[data-ref=value2]');
3088
- this.#fieldError = fragment.querySelector('ful-field-error');
3089
- const labelEl = fragment.querySelector('label');
3090
- labelEl?.addEventListener('click', () => this.focus());
3091
- this.#value1.ariaDescribedByElements = [this.#fieldError];
3092
- this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
3093
- this.replaceChildren(fragment);
3080
+ render(conf) {
3081
+ super.render({...conf, skipObservedSetup: 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
+
3086
+ this.disabled = conf.disabled;
3087
+ this.readonly = conf.observed.readonly;
3088
+ this.value = conf.observed.value;
3089
+
3094
3090
  this.addEventListener('click', (evt) => {
3095
3091
  const target = /** @type HTMLElement */ (evt.target);
3096
3092
  if (!target.matches('ul > li > a')) {
@@ -3113,42 +3109,38 @@ class InstantFilter extends ParsedElement {
3113
3109
  if (v === null || v === undefined) {
3114
3110
  this.#value1.value = '';
3115
3111
  this.#value2.value = '';
3116
- this.reflect(() => {
3117
- this.removeAttribute('value');
3118
- });
3119
3112
  return;
3120
3113
  }
3121
3114
  const [operator, ...values] = v;
3122
3115
  this.#operator.setAttribute('value', operator);
3123
3116
  this.#value1.value = values[0] ? Instant.isoToLocal(values[0]) : values[0];
3124
3117
  this.#value2.value = values[1] ? Instant.isoToLocal(values[1]) : values[1];
3125
- this.reflect(() => {
3126
- this.setAttribute('value', JSON.stringify(v));
3127
- });
3128
3118
  }
3129
- focus(options) {
3130
- this.#value1.focus(options);
3119
+ set readonly(v) {
3120
+ this.#value2.readOnly = v;
3121
+ super.readonly = v;
3122
+ }
3123
+ set disabled(d) {
3124
+ Attributes.toggle(this.#value2, 'disabled', d);
3125
+ super.disabled = d;
3131
3126
  }
3132
- setCustomValidity(error) {
3133
- if (!error) {
3134
- this.internals.setValidity({});
3135
- this.#fieldError.innerText = "";
3136
- return;
3137
- }
3138
- this.internals.setValidity({ customError: true }, " ");
3139
- this.#fieldError.innerText = error;
3127
+ formResetCallback() {
3128
+ const v = this.getAttribute("value");
3129
+ this.value = v === null ? null : JSON.parse(v);
3140
3130
  }
3131
+
3141
3132
  }
3142
3133
 
3143
- class LocalDateFilter extends ParsedElement {
3144
- static observed = ["value:json"];
3145
- static slots = true;
3134
+ class LocalDateFilter extends Input {
3135
+ static observed = ["value:json", 'readonly:presence'];
3146
3136
  static template = `
3147
- <div class="form-label" data-tpl-if="label">
3148
- <label>{{{{ label }}}}</label>
3137
+ <div class="form-label">
3138
+ <label>{{{{ slots.default }}}}</label>
3149
3139
  {{{{ slots.info }}}}
3150
3140
  </div>
3151
3141
  <div class="input-group">
3142
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3143
+ {{{{ slots.before }}}}
3152
3144
  <button data-ref="operator" class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" value="EQ" form="">=</button>
3153
3145
  <ul class="dropdown-menu">
3154
3146
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
@@ -3161,32 +3153,25 @@ class LocalDateFilter extends ParsedElement {
3161
3153
  </ul>
3162
3154
  <input data-ref="value1" type="date" class="form-control" form="">
3163
3155
  <input data-ref="value2" type="date" class="form-control" form="" hidden>
3164
- <span class="input-group-text"><i class="bi bi-search"></i></span>
3156
+ {{{{ slots.after }}}}
3157
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
3165
3158
  </div>
3166
3159
  <ful-field-error></ful-field-error>
3167
3160
  `;
3168
- static formAssociated = true;
3169
3161
  #operator;
3170
3162
  #value1;
3171
3163
  #value2;
3172
- #fieldError;
3173
- constructor() {
3174
- super();
3175
- this.internals = this.attachInternals();
3176
- }
3177
- render({ slots }) {
3178
- const label = Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3179
- const name = this.getAttribute("name");
3180
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3181
- this.#operator = fragment.querySelector('[data-ref=operator]');
3182
- this.#value1 = fragment.querySelector('[data-ref=value1]');
3183
- this.#value2 = fragment.querySelector('[data-ref=value2]');
3184
- this.#fieldError = fragment.querySelector('ful-field-error');
3185
- const labelEl = fragment.querySelector('label');
3186
- labelEl?.addEventListener('click', () => this.focus());
3187
- this.#value1.ariaDescribedByElements = [this.#fieldError];
3188
- this.#value1.ariaLabelledByElements = labelEl ? [labelEl] : [];
3189
- this.replaceChildren(fragment);
3164
+ render(conf) {
3165
+ super.render({...conf, skipObservedSetup: true});
3166
+
3167
+ this.#operator = this.querySelector('[data-ref=operator]');
3168
+ this.#value1 = this.querySelector('[data-ref=value1]');
3169
+ this.#value2 = this.querySelector('[data-ref=value2]');
3170
+
3171
+ this.disabled = conf.disabled;
3172
+ this.readonly = conf.observed.readonly;
3173
+ this.value = conf.observed.value;
3174
+
3190
3175
  this.addEventListener('click', (evt) => {
3191
3176
  const target = /** @type HTMLElement */(evt.target);
3192
3177
  if (!target.matches('ul > li > a')) {
@@ -3208,42 +3193,37 @@ class LocalDateFilter extends ParsedElement {
3208
3193
  if (v === null || v === undefined) {
3209
3194
  this.#value1.value = '';
3210
3195
  this.#value2.value = '';
3211
- this.reflect(() => {
3212
- this.removeAttribute('value');
3213
- });
3214
3196
  return;
3215
3197
  }
3216
3198
  const [operator, ...values] = v;
3217
3199
  this.#operator.setAttibute('value', operator);
3218
3200
  this.#value1.value = values[0];
3219
3201
  this.#value2.value = values[1];
3220
- this.reflect(() => {
3221
- this.setAttribute('value', JSON.stringify(v));
3222
- });
3223
3202
  }
3224
- focus(options) {
3225
- this.#value1.focus(options);
3203
+ set readonly(v) {
3204
+ this.#value2.readOnly = v;
3205
+ super.readonly = v;
3226
3206
  }
3227
- setCustomValidity(error) {
3228
- if (!error) {
3229
- this.internals.setValidity({});
3230
- this.#fieldError.innerText = "";
3231
- return;
3232
- }
3233
- this.internals.setValidity({ customError: true }, " ");
3234
- this.#fieldError.innerText = error;
3207
+ set disabled(d) {
3208
+ Attributes.toggle(this.#value2, 'disabled', d);
3209
+ super.disabled = d;
3210
+ }
3211
+ formResetCallback() {
3212
+ const v = this.getAttribute("value");
3213
+ this.value = v === null ? null : JSON.parse(v);
3235
3214
  }
3236
3215
  }
3237
3216
 
3238
- class TextFilter extends ParsedElement {
3239
- static observed = ["value:json"];
3240
- static slots = true;
3217
+ class TextFilter extends Input {
3218
+ static observed = ["value:json", 'readonly:presence'];
3241
3219
  static template = `
3242
- <div class="form-label" data-tpl-if="label">
3243
- <label>{{{{ label }}}}</label>
3220
+ <div class="form-label">
3221
+ <label>{{{{ slots.default }}}}</label>
3244
3222
  {{{{ slots.info }}}}
3245
3223
  </div>
3246
3224
  <div class="input-group">
3225
+ <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>
3226
+ {{{{ slots.before }}}}
3247
3227
  <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>
3248
3228
  <ul class="dropdown-menu">
3249
3229
  <li><a class="dropdown-item" role="button" value="CONTAINS">&mldr;a&mldr;</a></li>
@@ -3252,30 +3232,23 @@ class TextFilter extends ParsedElement {
3252
3232
  <li><a class="dropdown-item" role="button" value="EQ">=</a></li>
3253
3233
  </ul>
3254
3234
  <input data-ref="value" type="text" class="form-control" form="">
3255
- <span class="input-group-text"><i class="bi bi-search"></i></span>
3235
+ {{{{ slots.after }}}}
3236
+ <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>
3256
3237
  </div>
3257
3238
  <ful-field-error></ful-field-error>
3258
3239
  `;
3259
- static formAssociated = true;
3260
3240
  #operator;
3261
3241
  #value;
3262
- #fieldError;
3263
- constructor() {
3264
- super();
3265
- this.internals = this.attachInternals();
3266
- }
3267
- render({ slots }) {
3268
- const label = Fragments.toHtml(slots.default.cloneNode(true)).trim().length === 0 ? null : slots.default;
3269
- const name = this.getAttribute("name");
3270
- const fragment = this.template().withOverlay({ slots, label, name }).render();
3271
- this.#operator = fragment.querySelector('[data-ref=operator]');
3272
- this.#value = fragment.querySelector('[data-ref=value]');
3273
- this.#fieldError = fragment.querySelector('ful-field-error');
3274
- const labelEl = fragment.querySelector('label');
3275
- labelEl?.addEventListener('click', () => this.focus());
3276
- this.#value.ariaDescribedByElements = [this.#fieldError];
3277
- this.#value.ariaLabelledByElements = labelEl ? [labelEl] : [];
3278
- this.replaceChildren(fragment);
3242
+ render(conf) {
3243
+ super.render({...conf, skipObservedSetup: true});
3244
+
3245
+ this.#operator = this.querySelector('[data-ref=operator]');
3246
+ this.#value = this.querySelector('[data-ref=value]');
3247
+
3248
+ this.disabled = conf.disabled;
3249
+ this.readonly = conf.observed.readonly;
3250
+ this.value = conf.observed.value;
3251
+
3279
3252
  this.addEventListener('click', (evt) => {
3280
3253
  const target = /** @type HTMLElement */(evt.target);
3281
3254
  if (!target.matches('ul > li > a')) {
@@ -3287,38 +3260,22 @@ class TextFilter extends ParsedElement {
3287
3260
  btn.innerHTML = target.innerHTML;
3288
3261
  });
3289
3262
  }
3290
-
3291
3263
  get value() {
3292
3264
  const operator = this.#operator.getAttribute('value');
3293
3265
  return this.#value.value === '' ? undefined : [operator, 'IGNORE_CASE', this.#value.value];
3294
3266
  }
3295
-
3296
3267
  set value(v) {
3297
3268
  if (v === null || v === undefined) {
3298
3269
  this.#value.value = '';
3299
- this.reflect(() => {
3300
- this.removeAttribute('value');
3301
- });
3302
3270
  return;
3303
3271
  }
3304
3272
  const [operator, sensitivity, value] = v;
3305
3273
  this.#operator.setAttribute('value', operator);
3306
3274
  this.#value.value = value;
3307
- this.reflect(() => {
3308
- this.setAttribute('value', JSON.stringify(v));
3309
- });
3310
3275
  }
3311
- focus(options) {
3312
- this.#value.focus(options);
3313
- }
3314
- setCustomValidity(error) {
3315
- if (!error) {
3316
- this.internals.setValidity({});
3317
- this.#fieldError.innerText = "";
3318
- return;
3319
- }
3320
- this.internals.setValidity({ customError: true }, " ");
3321
- this.#fieldError.innerText = error;
3276
+ formResetCallback() {
3277
+ const v = this.getAttribute("value");
3278
+ this.value = v === null ? null : JSON.parse(v);
3322
3279
  }
3323
3280
  }
3324
3281