@optionfactory/ful 0.34.0 → 0.36.0

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
@@ -70,38 +70,6 @@ class Hex {
70
70
  }
71
71
  }
72
72
 
73
- const Observable = (SuperClass) => class extends SuperClass {
74
- constructor(...args) {
75
- super(...args);
76
- this.listeners = {};
77
- }
78
- fireSync(event, data, initialAcc) {
79
- const listeners = this.listeners[event] || [];
80
- let acc = initialAcc;
81
- for (const l of listeners) {
82
- acc = l(data, this, acc);
83
- }
84
- return acc;
85
- }
86
- async fire(event, data, initialAcc) {
87
- const listeners = this.listeners[event] || [];
88
- let acc = initialAcc;
89
- for (const l of listeners) {
90
- acc = await l(data, this, acc);
91
- }
92
- return acc;
93
- }
94
- on(event, listener) {
95
- this.listeners[event] = this.listeners[event] || [];
96
- this.listeners[event].push(listener);
97
- }
98
- un(event, listener) {
99
- const listeners = this.listeners[event] || [];
100
- const idx = listeners.indexOf(listener);
101
- return idx === -1 ? [] : listeners.splice(idx, 1);
102
- }
103
- };
104
-
105
73
  class ContextInterceptor {
106
74
  constructor() {
107
75
  const context = document.querySelector("meta[name='context']").getAttribute("content");
@@ -727,35 +695,14 @@ const Stateful = (SuperClass, flags, others) => {
727
695
  };
728
696
  };
729
697
 
730
- class FieldError extends Templated(HTMLElement) {
731
- render(slotted, template) {
732
- this.classList.add('invalid-feedback');
733
- }
734
- static configure() {
735
- customElements.define('ful-field-error', FieldError);
736
- }
737
- }
738
-
739
- class Errors extends Templated(HTMLElement) {
740
- render(slotted, template) {
741
- this.classList.add('alert', 'alert-danger', 'd-none');
742
- }
743
- static configure() {
744
- customElements.define('ful-errors', Errors);
745
- }
746
-
747
- }
748
-
749
698
  /* global Infinity, CSS */
750
699
 
751
- class Form extends Templated(Observable(HTMLElement)) {
752
- constructor({ mutators, extractors, valueHoldersSelector, ignoredChildrenSelector }) {
753
- super();
754
- this.mutators = mutators || {};
755
- this.extractors = extractors || {};
756
- this.valueHoldersSelector = valueHoldersSelector || '[name]';
757
- this.ignoredChildrenSelector = ignoredChildrenSelector || '.d-none, [hidden]';
758
- }
700
+ class Form extends Templated(HTMLElement) {
701
+ static MUTATORS = {};
702
+ static EXTRACTORS = {};
703
+ static VALUE_HOLDERS_SELECTOR = '[name]';
704
+ static IGNORED_CHILDREN_SELECTOR = '.d-none, [hidden]';
705
+
759
706
  render(slotted, template) {
760
707
  const form = document.createElement('form');
761
708
  form.append(slotted.default);
@@ -763,7 +710,9 @@ class Form extends Templated(Observable(HTMLElement)) {
763
710
  e.preventDefault();
764
711
  this.spinner(true);
765
712
  try {
766
- await this.fire('submit', this.getValues(), this);
713
+ if(this.submitter) {
714
+ await this.submitter(this.getValues(), this);
715
+ }
767
716
  } catch (e) {
768
717
  if (e instanceof Failure) {
769
718
  this.setErrors(e.problems);
@@ -790,20 +739,20 @@ class Form extends Templated(Observable(HTMLElement)) {
790
739
  continue;
791
740
  }
792
741
  Array.from(this.querySelectorAll(`[name='${CSS.escape(k)}']`)).forEach((el) => {
793
- Form.mutate(this.mutators, el, values[k], k, values);
742
+ Form.mutate(Form.MUTATORS, el, values[k], k, values);
794
743
  });
795
744
  }
796
745
  }
797
746
  getValues() {
798
- return Array.from(this.querySelectorAll(this.valueHoldersSelector))
747
+ return Array.from(this.querySelectorAll(Form.VALUE_HOLDERS_SELECTOR))
799
748
  .filter((el) => {
800
749
  if (el.dataset['fulBindInclude'] === 'never') {
801
750
  return false;
802
751
  }
803
- return el.dataset['fulBindInclude'] === 'always' || el.closest(this.ignoredChildrenSelector) === null;
752
+ return el.dataset['fulBindInclude'] === 'always' || el.closest(Form.IGNORED_CHILDREN_SELECTOR) === null;
804
753
  })
805
754
  .reduce((result, el) => {
806
- return Form.providePath(result, el.getAttribute('name'), Form.extract(this.extractors, el));
755
+ return Form.providePath(result, el.getAttribute('name'), Form.extract(Form.EXTRACTORS, el));
807
756
  }, {});
808
757
  }
809
758
  setErrors(errors, scroll) {
@@ -812,13 +761,10 @@ class Form extends Templated(Observable(HTMLElement)) {
812
761
  .filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT')
813
762
  .forEach((e) => {
814
763
  const name = e.context.replace("[", ".").replace("].", ".");
764
+ //TODO: match [name=] ful-validation-target and [name=]:not(:has(ful-validation-target))
765
+ //
815
766
  this.querySelectorAll(`[name='${CSS.escape(name)}']`)
816
- .forEach(input => {
817
- input.classList.add('is-invalid');
818
- if (input.parentElement.classList.contains("form-floating")) {
819
- input.parentElement.classList.add('is-invalid');
820
- }
821
- });
767
+ .forEach(input => input.classList.add('is-invalid'));
822
768
  this.querySelectorAll(`ful-field-error[field='${CSS.escape(name)}']`)
823
769
  .forEach(el => el.innerText = e.reason);
824
770
  });
@@ -827,14 +773,14 @@ class Form extends Templated(Observable(HTMLElement)) {
827
773
  const globalErrors = errors.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
828
774
  el.innerHTML = globalErrors.map(e => e.reason).join("\n");
829
775
  if (globalErrors.length !== 0) {
830
- el.classList.remove('d-none');
776
+ el.removeAttribute('hidden');
831
777
  }
832
778
  });
833
779
 
834
780
  if (!scroll) {
835
781
  return;
836
782
  }
837
- const ys = Array.from(this.querySelectorAll('ful-field-error:not(.d-none)'))
783
+ const ys = Array.from(this.querySelectorAll('ful-field-error'))
838
784
  .map(el => el.getBoundingClientRect().y + window.scrollY);
839
785
  const miny = Math.min(...ys);
840
786
  if (miny !== Infinity) {
@@ -842,12 +788,12 @@ class Form extends Templated(Observable(HTMLElement)) {
842
788
  }
843
789
  }
844
790
  clearErrors() {
845
- this.querySelectorAll('[name].is-invalid, .form-floating.is-invalid')
791
+ this.querySelectorAll('.is-invalid')
846
792
  .forEach(el => el.classList.remove('is-invalid'));
847
793
  this.querySelectorAll("ful-errors")
848
794
  .forEach(el => {
849
795
  el.innerHTML = '';
850
- el.classList.add('d-none');
796
+ el.setAttribute('hidden', '');
851
797
  });
852
798
  }
853
799
  static extract(extractors, el) {
@@ -867,9 +813,6 @@ class Form extends Templated(Observable(HTMLElement)) {
867
813
  if (el.dataset['fulBindType'] === 'boolean') {
868
814
  return !el.value ? null : el.value === 'true';
869
815
  }
870
- if (el.getValue) {
871
- return el.getValue();
872
- }
873
816
  return el.value || null;
874
817
  }
875
818
  static mutate(mutators, el, raw, key, values) {
@@ -886,10 +829,6 @@ class Form extends Templated(Observable(HTMLElement)) {
886
829
  el.checked = raw;
887
830
  return;
888
831
  }
889
- if (el.setValue) {
890
- el.setValue(raw);
891
- return;
892
- }
893
832
  el.value = raw;
894
833
  }
895
834
 
@@ -933,35 +872,19 @@ const ful_input_ec = globalThis.ec || ftl.EvaluationContext.configure({
933
872
  });
934
873
 
935
874
  const ful_input_template_ = globalThis.ful_input_template || ftl.Template.fromHtml(`
936
- <div data-tpl-if="floating" class="input-group has-validation">
875
+ <label data-tpl-for="id" class="form-label">{{{{ slotted.default }}}}</label>
876
+ <div class="input-group">
937
877
  <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
938
878
  <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
939
- <div class="form-floating">
940
- {{{{ slotted.input }}}}
941
- <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
942
- </div>
879
+ {{{{ slotted.input }}}}
943
880
  <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
944
881
  <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
945
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
946
- </div>
947
- <div data-tpl-if="!floating" data-tpl-remove="tag">
948
- <label data-tpl-for="id" class="form-label">{{{{ slotted.default }}}}</label>
949
- <div class="input-group has-validation">
950
- <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
951
- <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
952
- {{{{ slotted.input }}}}
953
- <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
954
- <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
955
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
956
- </div>
957
882
  </div>
883
+ <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
958
884
  `, ful_input_ec);
959
885
 
960
-
961
-
962
886
  class Input extends Templated(HTMLElement, ful_input_template_) {
963
887
  render(slotted, template) {
964
- const floating = this.hasAttribute('floating');
965
888
  const input = slotted.input = slotted.input || (() => {
966
889
  const el = document.createElement("input");
967
890
  el.classList.add("form-control");
@@ -973,7 +896,7 @@ class Input extends Templated(HTMLElement, ful_input_template_) {
973
896
  Attributes.defaultValue(slotted.input, "type", "text");
974
897
  Attributes.defaultValue(slotted.input, "placeholder", " ");
975
898
  const name = input.getAttribute('name');
976
- return template.render({ id, name, floating, slotted });
899
+ return template.render({ id, name, slotted });
977
900
  }
978
901
  static configure() {
979
902
  customElements.define('ful-input', Input);
@@ -989,28 +912,16 @@ const ful_select_ec = globalThis.ec || ftl.EvaluationContext.configure({
989
912
  });
990
913
 
991
914
  const ful_select_template_ = globalThis.ful_select_template || ftl.Template.fromHtml(`
992
- <div data-tpl-if="floating" class="input-group has-validation">
915
+ <label data-tpl-for="tsId" class="form-label">{{{{ slotted.default }}}}</label>
916
+ {{{{ input }}}}
917
+ <div class="input-group">
993
918
  <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
994
919
  <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
995
- <div class="form-floating">
996
- {{{{ slotted.input }}}}
997
- <label data-tpl-for="name" class="form-label">{{{{ slotted.default }}}}</label>
998
- </div>
920
+ {{{{ slotted.input }}}}
999
921
  <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
1000
922
  <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
1001
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1002
- </div>
1003
- <div data-tpl-if="!floating" data-tpl-remove="tag">
1004
- <label data-tpl-for="id" class="form-label">{{{{ slotted.default }}}}</label>
1005
- <div class="input-group has-validation">
1006
- <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
1007
- <div data-tpl-if="slotted.before" data-tpl-remove="tag">{{{{ slotted.before }}}}</div>
1008
- {{{{ slotted.input }}}}
1009
- <div data-tpl-if="slotted.after" data-tpl-remove="tag">{{{{ slotted.after }}}}</div>
1010
- <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
1011
- <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1012
- </div>
1013
923
  </div>
924
+ <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
1014
925
  `, ful_select_ec);
1015
926
 
1016
927
 
@@ -1020,8 +931,6 @@ class Select extends Templated(HTMLElement, ful_select_template_) {
1020
931
  this.tsConfig = tsConfig;
1021
932
  }
1022
933
  render(slotted, template) {
1023
- const floating = this.hasAttribute('floating');
1024
-
1025
934
  const type = this.getAttribute("type") || 'local';
1026
935
  const remote = type != 'local';
1027
936
  const loadOnce = this.getAttribute('load') != 'always';
@@ -1030,6 +939,7 @@ class Select extends Templated(HTMLElement, ful_select_template_) {
1030
939
  return document.createElement("select");
1031
940
  })();
1032
941
  const id = input.getAttribute('id') || this.getAttribute('input-id') || Attributes.uid('ful-select');
942
+ const tsId = `${id}-ts-control`;
1033
943
  Attributes.forward('input-', this, input);
1034
944
  Attributes.defaultValue(input, "id", id);
1035
945
  Attributes.defaultValue(input, "placeholder", " ");
@@ -1049,31 +959,42 @@ class Select extends Templated(HTMLElement, ful_select_template_) {
1049
959
  }
1050
960
  };
1051
961
 
962
+
963
+ this._remote = remote;
964
+ // we need to await this load in setValue when remote is configured and the option
965
+ // is not loaded yet.
966
+ // tomselect settings.load does not retun a promise as it wraps the configured load function
967
+ // with a debouncer
968
+ this._unwrappedRemoteLoad = async (query, callback) => {
969
+
970
+ if (!remote || remote && loadOnce && this.loaded) {
971
+ callback();
972
+ return;
973
+ }
974
+ const type = query && query.hasOwnProperty('byId') ? 'id' : 'query';
975
+ const qvalue = type === 'id' ? query.byId : query;
976
+ const data = await (this.loader ? this.loader(qvalue, type) : []);
977
+ if(type !== 'id'){
978
+ this.loaded = true;
979
+ }
980
+ callback(data);
981
+ };
982
+
983
+
1052
984
  this.ts = new TomSelect(input, Object.assign(remote ? {
1053
985
  preload: 'focus',
1054
- load: async (query, callback) => {
1055
- if (!remote || remote && loadOnce && this.loaded) {
1056
- callback();
1057
- return;
1058
- }
1059
- const data = await this.load(query);
1060
- this.loaded = true;
1061
- callback(data);
1062
- },
1063
- shouldLoad: (query) => this.shouldLoad(query)
986
+ load: this._unwrappedRemoteLoad,
987
+ shouldLoad: (query) => this.shouldLoad ? this.shouldLoad(query) : true
1064
988
  } : {}, tsDefaultConfig, this.tsConfig));
989
+ console.log("ts created");
990
+ //we remove the input to move it
991
+ input.remove();
1065
992
 
1066
- return template.render({ id, name, floating, slotted });
1067
- }
1068
- shouldLoad(q){
1069
- return true;
1070
- }
1071
- load(q){
1072
- return []
993
+ return template.render({ id, tsId, name, input, slotted });
1073
994
  }
1074
995
  async setValue(v) {
1075
- if (!this.loaded) {
1076
- await this.ts.load();
996
+ if(this._remote){
997
+ await this._unwrappedRemoteLoad({byId: v}, this.ts.loadCallback.bind(this.ts));
1077
998
  }
1078
999
  this.ts.setValue(v);
1079
1000
  }
@@ -1244,31 +1165,5 @@ class Wizard extends HTMLElement {
1244
1165
  }
1245
1166
  }
1246
1167
 
1247
- class App {
1248
- constructor() {
1249
- this.configurers = [];
1250
- this.initializers = [];
1251
- this.handlers = [];
1252
- this.running = false;
1253
- document.addEventListener("DOMContentLoaded", async () => {
1254
- await Promise.all(this.configurers);
1255
- await Promise.all(this.initializers.map(h => Promise.resolve(h())));
1256
- await Promise.all(this.handlers.map(h => Promise.resolve(h())));
1257
- });
1258
- }
1259
- configure(cb) {
1260
- this.configurers.push(Promise.resolve(cb()));
1261
- return this;
1262
- }
1263
- initialize(cb) {
1264
- this.initializers.push(cb);
1265
- return this;
1266
- }
1267
- ready(cb) {
1268
- this.handlers.push(cb);
1269
- return this;
1270
- }
1271
- }
1272
-
1273
- export { App, Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Errors, Failure, FieldError, Form, Fragments, Hex, HttpClient, Input, LocalStorage, Observable, RadioGroup, Select, SessionStorage, Slots, Spinner, Stateful, Templated, VersionedStorage, Wizard, jsonPatch, jsonPost, jsonPut, jsonRequest, timing };
1168
+ export { Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Failure, Form, Fragments, Hex, HttpClient, Input, LocalStorage, RadioGroup, Select, SessionStorage, Slots, Spinner, Stateful, Templated, VersionedStorage, Wizard, jsonPatch, jsonPost, jsonPut, jsonRequest, timing };
1274
1169
  //# sourceMappingURL=ful.mjs.map