@optionfactory/ful 0.46.0 → 0.48.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.iife.js CHANGED
@@ -668,6 +668,38 @@ var ful = (function (exports) {
668
668
  }
669
669
  }
670
670
 
671
+ class TemplateRegistry {
672
+ #idToFragment = {};
673
+ #idToTemplate = {};
674
+ #ec;
675
+ put(k, fragment) {
676
+ if (this.#ec) {
677
+ this.#idToTemplate[k] = ftl.Template.fromFragment(fragment, ec);
678
+ return;
679
+ }
680
+ this.#idToFragment[k] = fragment;
681
+ }
682
+ get(k) {
683
+ if (!this.#ec) {
684
+ throw new Error("evaluationContext is not configured");
685
+ }
686
+ const tpl = this.#idToTemplate[k];
687
+ if (!tpl) {
688
+ throw new Error(`missing template: '${k}'`);
689
+ }
690
+ return tpl;
691
+ }
692
+ configure(ec) {
693
+ this.#ec = ec;
694
+ for (const [k, fragment] of Object.entries(this.#idToFragment)) {
695
+ delete this.#idToFragment[k];
696
+ this.#idToTemplate[k] = ftl.Template.fromFragment(fragment, ec);
697
+ }
698
+ }
699
+ }
700
+
701
+ const templates = new TemplateRegistry();
702
+
671
703
  class UpgradeQueue {
672
704
  #q = [];
673
705
  constructor() {
@@ -686,83 +718,85 @@ var ful = (function (exports) {
686
718
 
687
719
  const upgradeQueue = new UpgradeQueue();
688
720
 
689
- class ParsedElement extends HTMLElement {
690
- #parsed;
691
- connectedCallback() {
692
- if (this.#parsed) {
693
- return;
694
- }
695
- if (this.ownerDocument.readyState === 'complete' || Nodes.isParsed(this)) {
696
- upgradeQueue.enqueue(this);
697
- return;
698
- }
699
- this.ownerDocument.addEventListener('DOMContentLoaded', () => {
700
- observer.disconnect();
701
- upgradeQueue.enqueue(this);
702
- });
703
- const observer = new MutationObserver(() => {
704
- if (!Nodes.isParsed(this)) {
705
- return;
706
- }
707
- observer.disconnect();
708
- upgradeQueue.enqueue(this);
709
- });
710
- observer.observe(this.parentNode, { childList: true, subtree: true });
711
- }
712
- attributeChangedCallback(name, oldValue, newValue) {
713
- if (!this.#parsed || oldValue === newValue) {
714
- return;
715
- }
716
- this[name] = newValue;
717
- }
718
- upgrade() {
719
- if (this.#parsed) {
720
- return;
721
- }
722
- this.#parsed = true;
723
- return this.ready();
724
- }
725
- }
726
-
721
+ const ParsedElement = (flags, others) => {
727
722
 
728
- const Templated = (SuperClass, template) => {
729
- return class extends SuperClass {
730
- async ready() {
731
- const slotted = Slots.from(this);
732
- const fragment = await Promise.resolve(this.render(slotted, template));
733
- this.replaceChildren(fragment);
734
- }
735
- };
736
- };
737
-
738
- const Stateful = (SuperClass, flags, others) => {
723
+ const observed_flags = flags || [];
724
+ const observed_others = others || [];
725
+ const observed = [].concat(observed_flags).concat(observed_others);
739
726
 
740
- const all = [].concat(flags).concat(others || []);
741
-
742
- const k = class extends SuperClass {
727
+ const k = class extends HTMLElement {
743
728
  static get observedAttributes() {
744
- return all;
729
+ return observed;
745
730
  }
731
+ #parsed;
732
+ #internals;
746
733
  constructor(...args) {
747
734
  super(...args);
748
- this.internals_ = this.internals_ || this.attachInternals();
735
+ this.#internals = this.attachInternals();
736
+ }
737
+ get internals() {
738
+ return this.#internals;
739
+ }
740
+ connectedCallback() {
741
+ if (this.#parsed) {
742
+ return;
743
+ }
744
+ if (this.ownerDocument.readyState === 'complete' || Nodes.isParsed(this)) {
745
+ upgradeQueue.enqueue(this);
746
+ return;
747
+ }
748
+ this.ownerDocument.addEventListener('DOMContentLoaded', () => {
749
+ observer.disconnect();
750
+ upgradeQueue.enqueue(this);
751
+ });
752
+ const observer = new MutationObserver(() => {
753
+ if (!Nodes.isParsed(this)) {
754
+ return;
755
+ }
756
+ observer.disconnect();
757
+ upgradeQueue.enqueue(this);
758
+ });
759
+ observer.observe(this.parentNode, { childList: true, subtree: true });
749
760
  }
761
+ attributeChangedCallback(name, oldValue, newValue) {
762
+ if (!this.#parsed || oldValue === newValue) {
763
+ return;
764
+ }
765
+ this[name] = newValue;
766
+ }
767
+ async upgrade() {
768
+ if (this.#parsed) {
769
+ return;
770
+ }
771
+ this.#parsed = true;
772
+ await this.render();
773
+ for (const flag of observed_flags) {
774
+ if(this.hasAttribute(flag)){
775
+ this[flag] = true;
776
+ }
777
+ }
778
+ for (const other of observed_others) {
779
+ if(this.hasAttribute(other)){
780
+ this[other] = this.getAttribute(other);
781
+ }
782
+ }
783
+ }
750
784
  };
751
785
 
752
- for (const flag of flags) {
786
+ for (const flag of observed_flags) {
753
787
  Object.defineProperty(k.prototype, flag, {
754
788
  enumerable: true,
755
789
  configurable: true,
756
790
  get() {
757
- return this.internals_.states.has(`--${flag}`);
791
+ return this.internals.states.has(`--${flag}`);
758
792
  },
759
793
  set(value) {
760
794
  //see https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet#using_double_dash_prefixed_idents
761
795
  if (Attributes.asBoolean(value)) {
762
- this.internals_.states.add(`--${flag}`);
796
+ this.internals.states.add(`--${flag}`);
763
797
  return;
764
798
  }
765
- this.internals_.states.delete(`--${flag}`);
799
+ this.internals.states.delete(`--${flag}`);
766
800
  }
767
801
  });
768
802
  }
@@ -839,12 +873,12 @@ var ful = (function (exports) {
839
873
  el.value = raw;
840
874
  }
841
875
 
842
- class Form extends Templated(ParsedElement) {
876
+ class Form extends ParsedElement() {
843
877
  static IGNORED_CHILDREN_SELECTOR = '.d-none, [hidden]';
844
878
  static SCROLL_OFFSET = 50;
845
- render(slotted) {
879
+ render() {
846
880
  const form = document.createElement('form');
847
- form.append(slotted.default);
881
+ form.replaceChildren(...this.childNodes);
848
882
  form.addEventListener('submit', async (e) => {
849
883
  e.preventDefault();
850
884
  this.spinner(true);
@@ -862,7 +896,7 @@ var ful = (function (exports) {
862
896
  this.spinner(false);
863
897
  }
864
898
  });
865
- return form;
899
+ this.replaceChildren(form);
866
900
  }
867
901
  spinner(spin) {
868
902
  this.querySelectorAll('ful-spinner').forEach(el => el.hidden = !spin);
@@ -919,11 +953,7 @@ var ful = (function (exports) {
919
953
  }
920
954
  }
921
955
 
922
- const ful_input_ec = globalThis.ec || ftl.EvaluationContext.configure({
923
-
924
- });
925
-
926
- const ful_input_template_ = globalThis.ful_input_template || ftl.Template.fromHtml(`
956
+ templates.put('ful-input', Fragments.fromHtml(`
927
957
  <label data-tpl-for="id" class="form-label">{{{{ slotted.default }}}}</label>
928
958
  <div class="input-group">
929
959
  <span data-tpl-if="slotted.ibefore" class="input-group-text">{{{{ slotted.ibefore }}}}</span>
@@ -933,33 +963,31 @@ var ful = (function (exports) {
933
963
  <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
934
964
  </div>
935
965
  <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
936
- `, ful_input_ec);
937
-
938
- class StatelessInput extends Templated(ParsedElement, ful_input_template_) {
939
- render(slotted, template) {
940
- const input = this.input = slotted.input = slotted.input || (() => {
941
- const el = document.createElement("input");
942
- el.classList.add("form-control");
943
- return el;
944
- })();
945
- input.setAttribute('ful-validation-target', '');
946
-
947
- const id = input.getAttribute('id') || this.getAttribute('input-id') || Attributes.uid('ful-input');
948
- Attributes.forward('input-', this, slotted.input);
949
- Attributes.defaultValue(slotted.input, "id", id);
950
- Attributes.defaultValue(slotted.input, "type", "text");
951
- Attributes.defaultValue(slotted.input, "placeholder", " ");
952
- const name = this.getAttribute('name');
953
- return template.render({ id, name, slotted });
954
- }
955
-
956
- }
966
+ `));
967
+
968
+
969
+ const makeInputFragment = (el, slotted) => {
970
+ const input = el.input = slotted.input = slotted.input || (() => {
971
+ const el = document.createElement("input");
972
+ el.classList.add("form-control");
973
+ return el;
974
+ })();
975
+ input.setAttribute('ful-validation-target', '');
976
+
977
+ const id = input.getAttribute('id') || el.getAttribute('input-id') || Attributes.uid('ful-input');
978
+ Attributes.forward('input-', el, slotted.input);
979
+ Attributes.defaultValue(slotted.input, "id", id);
980
+ Attributes.defaultValue(slotted.input, "type", "text");
981
+ Attributes.defaultValue(slotted.input, "placeholder", " ");
982
+ const name = el.getAttribute('name');
983
+ return templates.get('ful-input').render(el, { id, name, slotted });
984
+ };
957
985
 
958
- class Input extends Stateful(StatelessInput, [], ['value']) {
959
- render(slotted, template) {
960
- const fragment = super.render(slotted, template);
961
- this.input.value = this.getAttribute('value');
962
- return fragment;
986
+ class Input extends ParsedElement([], ['value']) {
987
+ render() {
988
+ const slotted = Slots.from(el);
989
+ const fragment = makeInputFragment(this, slotted);
990
+ this.replaceChildren(fragment);
963
991
  }
964
992
  get value() {
965
993
  return this.input.value;
@@ -973,11 +1001,8 @@ var ful = (function (exports) {
973
1001
  * <script src="tom-select.complete.js"></script>
974
1002
  * <link href="tom-select.bootstrap5.css" rel="stylesheet" />
975
1003
  */
976
- const ful_select_ec = globalThis.ec || ftl.EvaluationContext.configure({
977
-
978
- });
979
1004
 
980
- const ful_select_template_ = globalThis.ful_select_template || ftl.Template.fromHtml(`
1005
+ templates.put('ful-select', Fragments.fromHtml(`
981
1006
  <label data-tpl-for="tsId" class="form-label">{{{{ slotted.default }}}}</label>
982
1007
  {{{{ input }}}}
983
1008
  <div class="input-group">
@@ -988,15 +1013,17 @@ var ful = (function (exports) {
988
1013
  <span data-tpl-if="slotted.iafter" class="input-group-text">{{{{ slotted.iafter }}}}</span>
989
1014
  </div>
990
1015
  <ful-field-error data-tpl-if="name" data-tpl-field="name"></ful-field-error>
991
- `, ful_select_ec);
1016
+ `));
992
1017
 
993
1018
 
994
- class Select extends Stateful(Templated(ParsedElement, ful_select_template_), [], ["value"]) {
1019
+ class Select extends ParsedElement([], ["value"]) {
995
1020
  constructor(tsConfig) {
996
1021
  super();
997
1022
  this.tsConfig = tsConfig;
998
1023
  }
999
- render(slotted, template) {
1024
+ render() {
1025
+ const slotted = Slots.from(this);
1026
+
1000
1027
  const type = this.getAttribute("type") || 'local';
1001
1028
  const remote = type != 'local';
1002
1029
  const loadOnce = this.getAttribute('load') != 'always';
@@ -1024,7 +1051,6 @@ var ful = (function (exports) {
1024
1051
  }
1025
1052
  };
1026
1053
 
1027
-
1028
1054
  this._remote = remote;
1029
1055
  // we need to await this load in setValue when remote is configured and the option
1030
1056
  // is not loaded yet.
@@ -1039,7 +1065,7 @@ var ful = (function (exports) {
1039
1065
  const type = query && query.hasOwnProperty('byId') ? 'id' : 'query';
1040
1066
  const qvalue = type === 'id' ? query.byId : query;
1041
1067
  const data = await (this.loader ? this.loader(qvalue, type) : []);
1042
- if(type !== 'id'){
1068
+ if (type !== 'id') {
1043
1069
  this.loaded = true;
1044
1070
  }
1045
1071
  callback(data);
@@ -1053,12 +1079,12 @@ var ful = (function (exports) {
1053
1079
  } : {}, tsDefaultConfig, this.tsConfig));
1054
1080
  //we remove the input to move it
1055
1081
  input.remove();
1056
- return template.render({ id, tsId, name, input, slotted });
1082
+ templates.get('ful-select').renderTo(this, { id, tsId, name, input, slotted });
1057
1083
  }
1058
1084
  set value(v) {
1059
1085
  (async () => {
1060
- if(this._remote){
1061
- await this._unwrappedRemoteLoad({byId: v}, this.ts.loadCallback.bind(this.ts));
1086
+ if (this._remote) {
1087
+ await this._unwrappedRemoteLoad({ byId: v }, this.ts.loadCallback.bind(this.ts));
1062
1088
  }
1063
1089
  this.ts.setValue(v);
1064
1090
  })();
@@ -1069,11 +1095,7 @@ var ful = (function (exports) {
1069
1095
  }
1070
1096
  }
1071
1097
 
1072
- const ful_radiogroup_ec = globalThis.ec || ftl.EvaluationContext.configure({
1073
-
1074
- });
1075
-
1076
- const ful_radiougroup_template_ = globalThis.ful_radiogroup_template || ftl.Template.fromHtml(`
1098
+ templates.put('ful-radio-group', Fragments.fromHtml(`
1077
1099
  <fieldset>
1078
1100
  <legend class="form-label">
1079
1101
  {{{{ slotted.default }}}}
@@ -1092,11 +1114,12 @@ var ful = (function (exports) {
1092
1114
  {{{{ slotted.footer }}}}
1093
1115
  </footer>
1094
1116
  </fieldset>
1095
- `, ful_radiogroup_ec);
1117
+ `));
1096
1118
 
1097
1119
 
1098
- class RadioGroup extends Stateful(Templated(ParsedElement, ful_radiougroup_template_), ['disabled'], ['value']) {
1099
- render(slotted, template) {
1120
+ class RadioGroup extends ParsedElement(['disabled'], ['value']) {
1121
+ render() {
1122
+ const slotted = Slots.from(this);
1100
1123
  const name = this.getAttribute('name') || Attributes.uid('ful-radiogroup');
1101
1124
  const radioEls = Array.from(slotted.default.querySelectorAll('ful-radio'));
1102
1125
  const inputsAndLabels = radioEls.map(el => {
@@ -1111,38 +1134,32 @@ var ful = (function (exports) {
1111
1134
  return [input, label];
1112
1135
  });
1113
1136
  radioEls.forEach(el => el.remove());
1114
-
1115
- const fragment = template.render({
1137
+ templates.get('ful-radio-group').renderTo(this, {
1116
1138
  name: name,
1117
1139
  slotted: slotted,
1118
1140
  inputsAndLabels: inputsAndLabels
1119
1141
  });
1120
- return fragment;
1121
1142
  }
1122
1143
  get value() {
1123
1144
  const checked = this.querySelector('input[type=radio]:checked');
1124
1145
  return checked ? checked.value : null;
1125
1146
  }
1126
- set value(value){
1147
+ set value(value) {
1127
1148
  this.querySelector(`input[type=radio][value=${CSS.escape(value)}]`).checked = true;
1128
1149
  }
1129
1150
  }
1130
1151
 
1131
- const ful_spinner_ec = globalThis.ec || ftl.EvaluationContext.configure({
1132
-
1133
- });
1134
-
1135
- const ful_spinner_template_ = globalThis.ful_spinner_template || ftl.Template.fromHtml(`
1152
+ templates.put('ful-spinner', Fragments.fromHtml(`
1136
1153
  <div class="ful-spinner-wrapper">
1137
1154
  <div class="ful-spinner-text">{{{{ slotted.default }}}}</div>
1138
1155
  <div class="ful-spinner-icon"></div>
1139
1156
  </div>
1140
- `, ful_spinner_ec);
1157
+ `));
1141
1158
 
1142
-
1143
- class Spinner extends Templated(ParsedElement, ful_spinner_template_) {
1144
- render(slotted, template) {
1145
- return template.render({ slotted });
1159
+ class Spinner extends ParsedElement() {
1160
+ render() {
1161
+ const slotted = Slots.from(this);
1162
+ templates.get('ful-spinner').renderTo(this, { slotted });
1146
1163
  }
1147
1164
  }
1148
1165
 
@@ -1219,7 +1236,8 @@ var ful = (function (exports) {
1219
1236
  }
1220
1237
 
1221
1238
  class CustomElements {
1222
- static configure() {
1239
+ static configure(ec) {
1240
+ templates.configure(ec);
1223
1241
  customElements.define('ful-spinner', Spinner);
1224
1242
  customElements.define('ful-input', Input);
1225
1243
  customElements.define('ful-radio-group', RadioGroup);
@@ -1248,16 +1266,16 @@ var ful = (function (exports) {
1248
1266
  exports.SessionStorage = SessionStorage;
1249
1267
  exports.Slots = Slots;
1250
1268
  exports.Spinner = Spinner;
1251
- exports.Stateful = Stateful;
1252
- exports.StatelessInput = StatelessInput;
1253
1269
  exports.SyncEvent = SyncEvent;
1254
- exports.Templated = Templated;
1270
+ exports.TemplateRegistry = TemplateRegistry;
1255
1271
  exports.VersionedStorage = VersionedStorage;
1256
1272
  exports.Wizard = Wizard;
1257
1273
  exports.jsonPatch = jsonPatch;
1258
1274
  exports.jsonPost = jsonPost;
1259
1275
  exports.jsonPut = jsonPut;
1260
1276
  exports.jsonRequest = jsonRequest;
1277
+ exports.makeInputFragment = makeInputFragment;
1278
+ exports.templates = templates;
1261
1279
  exports.timing = timing;
1262
1280
 
1263
1281
  Object.defineProperty(exports, '__esModule', { value: true });