@optionfactory/ful 0.91.0 → 0.93.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
@@ -87,6 +87,38 @@ class Failure extends Error {
87
87
  }
88
88
  }
89
89
 
90
+ class MediaType {
91
+ #type;
92
+ #subtype;
93
+ constructor(type, subtype) {
94
+ this.#type = type;
95
+ this.#subtype = subtype;
96
+ }
97
+ get normalized(){
98
+ return `${this.#type}/${this.#subtype}`;
99
+ }
100
+ get type() {
101
+ return this.#type;
102
+ }
103
+ get subtype() {
104
+ return this.#subtype;
105
+ }
106
+ /**
107
+ *
108
+ * @param {string|null|undefined} v
109
+ * @returns
110
+ */
111
+ static parse(v) {
112
+ if (!v) {
113
+ return new MediaType("unknown", "unknown");
114
+ }
115
+ const [prefix, _] = v.split(";");
116
+ const [ptype, psubtype] = prefix.trim().split("/");
117
+ return new MediaType(ptype.toLowerCase(), psubtype?.toLowerCase());
118
+ }
119
+ }
120
+
121
+
90
122
  /**
91
123
  * @typedef {Int8Array| Uint8Array| Uint8ClampedArray| Int16Array| Uint16Array| Int32Array| Uint32Array| Float32Array| Float64Array| BigInt64Array| BigUint64Array} TypedArray
92
124
  */
@@ -127,18 +159,32 @@ class HttpClientError extends Failure {
127
159
  * @returns an HttpClientError
128
160
  */
129
161
  static async fromResponse(response) {
130
- const text = await response.text();
131
- const message = `${response.status} ${response.statusText}: ${text}`;
132
- const fallback = [{
133
- type: "GENERIC_PROBLEM",
134
- context: null,
135
- reason: message,
136
- details: null
137
- }];
138
- try {
139
- return new HttpClientError(message, response.status, text ? JSON.parse(text) : fallback);
140
- } catch (e) {
141
- return new HttpClientError(message, response.status, fallback);
162
+ switch(MediaType.parse(response.headers.get("Content-Type")).normalized){
163
+ case 'application/failures+json': {
164
+ const data = await response.json();
165
+ const message = `${response.status} ${response.statusText}: ${data.length} failures`;
166
+ return new HttpClientError(message, response.status, data);
167
+ }
168
+ case 'application/problem+json': {
169
+ const data = await response.json();
170
+ const message = `${response.status} ${response.statusText}: ${data.title} ${data.detail}`;
171
+ return new HttpClientError(message, response.status, data.problems || [{
172
+ type: "GENERIC_PROBLEM",
173
+ context: null,
174
+ reason: message,
175
+ details: null
176
+ }]);
177
+ }
178
+ default: {
179
+ const text = await response.text();
180
+ const message = `${response.status} ${response.statusText}: ${text}`;
181
+ return new HttpClientError(message, response.status, [{
182
+ type: "GENERIC_PROBLEM",
183
+ context: null,
184
+ reason: message,
185
+ details: null
186
+ }]);
187
+ }
142
188
  }
143
189
  }
144
190
  }
@@ -151,9 +197,9 @@ class CsrfTokenInterceptor {
151
197
  constructor() {
152
198
  this.#k = document.querySelector("meta[name='_csrf_header']")?.getAttribute("content");
153
199
  this.#v = document.querySelector("meta[name='_csrf']")?.getAttribute("content");
154
- }
200
+ }
155
201
  async intercept(request, chain) {
156
- if(this.#k && this.#v) {
202
+ if (this.#k && this.#v) {
157
203
  request.headers.set(this.#k, this.#v);
158
204
  }
159
205
  return await chain.proceed(request);
@@ -465,6 +511,7 @@ class HttpRequestBuilder {
465
511
  const builder = new HttpMultipartRequestCustomizer(formData);
466
512
  callback(builder);
467
513
  this.#body = formData;
514
+ return this;
468
515
  }
469
516
  /**
470
517
  * Sets a fetch options for the request.
@@ -588,7 +635,7 @@ class HttpMultipartRequestCustomizer {
588
635
  *
589
636
  * @param {FormData} formData
590
637
  */
591
- constructor(formData){
638
+ constructor(formData) {
592
639
  this.#formData = formData;
593
640
  }
594
641
  /**
@@ -597,7 +644,7 @@ class HttpMultipartRequestCustomizer {
597
644
  * @param {*} value
598
645
  * @returns this builder
599
646
  */
600
- field(name, value){
647
+ field(name, value) {
601
648
  this.#formData.append(name, value);
602
649
  return this;
603
650
  }
@@ -611,10 +658,24 @@ class HttpMultipartRequestCustomizer {
611
658
  * @param {string|undefined} filename
612
659
  * @returns this builder
613
660
  */
614
- blob(name, value, filename){
661
+ blob(name, value, filename) {
615
662
  this.#formData.append(name, value, filename);
616
663
  return this;
617
664
  }
665
+ /**
666
+ * Appends multiple Blobs to the FormData with the same name.
667
+ * The default filename for Blob objects is "blob";
668
+ * The default filename for File objects is the file's filename.
669
+ * @param {string} name
670
+ * @param {Blob[]} values
671
+ * @returns this builder
672
+ */
673
+ blobs(name, values) {
674
+ for (let v of values) {
675
+ this.#formData.append(name, v);
676
+ }
677
+ return this;
678
+ }
618
679
  /**
619
680
  * Appends a JSON serialized blob to the FormData.
620
681
  * @param {string} name
@@ -622,8 +683,8 @@ class HttpMultipartRequestCustomizer {
622
683
  * @param {string|undefined} filename
623
684
  * @returns this builder
624
685
  */
625
- json(name, value, filename){
626
- const blob = new Blob([JSON.stringify(value)], {type: 'application/json'});
686
+ json(name, value, filename) {
687
+ const blob = new Blob([JSON.stringify(value)], { type: 'application/json' });
627
688
  this.#formData.append(name, blob, filename);
628
689
  return this;
629
690
  }
@@ -1398,6 +1459,7 @@ class Form extends ParsedElement() {
1398
1459
  static IGNORED_CHILDREN_SELECTOR = '.d-none, [hidden]';
1399
1460
  static SCROLL_OFFSET = 50;
1400
1461
  static INVALID_CLASS = 'is-invalid';
1462
+ submitter;
1401
1463
  render() {
1402
1464
  const form = document.createElement('form');
1403
1465
  form.replaceChildren(...this.childNodes);
@@ -1410,8 +1472,14 @@ class Form extends ParsedElement() {
1410
1472
  this.replaceChildren(form);
1411
1473
  }
1412
1474
  spinner(spin) {
1413
- this.querySelectorAll('ful-spinner').forEach(el => el.hidden = !spin);
1414
- this.querySelectorAll('[type=submit],[type=reset]').forEach(el => el.disabled = spin);
1475
+ this.querySelectorAll('ful-spinner').forEach(el => {
1476
+ const hel = /** @type HTMLElement} */ (el);
1477
+ hel.hidden = !spin;
1478
+ });
1479
+ this.querySelectorAll('[type=submit],[type=reset]').forEach(el => {
1480
+ const hel = /** @type HTMLButtonElement} */ (el);
1481
+ hel.disabled = spin;
1482
+ });
1415
1483
  }
1416
1484
  async remoting(fn) {
1417
1485
  try {
@@ -1446,7 +1514,7 @@ class Form extends ParsedElement() {
1446
1514
  }
1447
1515
  }
1448
1516
  get values() {
1449
- return Array.from(this.querySelectorAll('[name]'))
1517
+ return Array.from(/** @type {NodeListOf<HTMLElement>} */ (this.querySelectorAll('[name]')))
1450
1518
  .filter(el => {
1451
1519
  if (el.dataset['fulBindInclude'] === 'never') {
1452
1520
  return false;
@@ -1470,10 +1538,14 @@ class Form extends ParsedElement() {
1470
1538
  const validationTargetsSelector = `[name='${CSS.escape(name)}'] [ful-validation-target],[name='${CSS.escape(name)}']:not(:has([ful-validation-target]))`;
1471
1539
  this.querySelectorAll(validationTargetsSelector).forEach(input => input.classList.add(Form.INVALID_CLASS));
1472
1540
  const fieldErrorsSelector = `ful-field-error[field='${CSS.escape(name)}']`;
1473
- this.querySelectorAll(fieldErrorsSelector).forEach(el => el.innerText = e.reason);
1541
+ this.querySelectorAll(fieldErrorsSelector).forEach(el => {
1542
+ const hel = /** @type HTMLElement} */ (el);
1543
+ hel.innerText = e.reason;
1544
+ });
1474
1545
  });
1475
1546
  this.querySelectorAll("ful-errors").forEach(el => {
1476
- el.innerText = globalErrors.map(e => e.reason).join("\n");
1547
+ const hel = /** @type HTMLElement} */ (el);
1548
+ hel.innerText = globalErrors.map(e => e.reason).join("\n");
1477
1549
  if (globalErrors.length !== 0) {
1478
1550
  el.removeAttribute('hidden');
1479
1551
  }
@@ -1572,6 +1644,8 @@ class Select extends ParsedElement({
1572
1644
  </div>
1573
1645
  `
1574
1646
  }) {
1647
+ shouldLoad;
1648
+ _unwrappedRemoteLoad;
1575
1649
  constructor(tsConfig) {
1576
1650
  super();
1577
1651
  this.tsConfig = tsConfig;
@@ -1623,6 +1697,7 @@ class Select extends ParsedElement({
1623
1697
  }
1624
1698
  callback(data);
1625
1699
  };
1700
+ // @ts-ignore
1626
1701
  this.ts = new TomSelect(input, Object.assign(remote ? {
1627
1702
  preload: 'focus',
1628
1703
  load: this._unwrappedRemoteLoad,
@@ -1758,5 +1833,5 @@ class Spinner extends ParsedElement({
1758
1833
  }
1759
1834
  }
1760
1835
 
1761
- export { Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Deferred, ElementsRegistry, Failure, Form, Fragments, Hex, HttpClient, HttpClientError, INPUT_TEMPLATE, Input, LightSlots, LocalStorage, Nodes, ParsedElement, RadioGroup, Select, SessionStorage, Spinner, TemplatesRegistry, VersionedStorage, elements, makeInputFragment, timing };
1836
+ export { Attributes, AuthorizationCodeFlow, AuthorizationCodeFlowInterceptor, AuthorizationCodeFlowSession, Base64, Deferred, ElementsRegistry, Failure, Form, Fragments, Hex, HttpClient, HttpClientError, INPUT_TEMPLATE, Input, LightSlots, LocalStorage, MediaType, Nodes, ParsedElement, RadioGroup, Select, SessionStorage, Spinner, TemplatesRegistry, VersionedStorage, elements, makeInputFragment, timing };
1762
1837
  //# sourceMappingURL=ful.mjs.map