powerpagestoolkit 2.6.3327 → 2.7.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/README.md +2 -1
- package/dist/API.d.ts +1 -1
- package/dist/DOMNodeReference.d.ts +62 -21
- package/dist/DOMNodeReferenceArray.d.ts +12 -0
- package/dist/List.d.ts +1 -0
- package/dist/bindForm.d.ts +11 -0
- package/dist/bundle.js +291 -69
- package/dist/createDOMNodeReferences.d.ts +32 -7
- package/dist/index.d.ts +2 -1
- package/dist/waitFor.d.ts +2 -2
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -95,7 +95,8 @@ const nodes = await createRef(".my-class", { multiple: true });
|
|
|
95
95
|
// ADVANCED OPTIONS
|
|
96
96
|
// in the event that you need to be more granular with how you are targeting
|
|
97
97
|
// and retrieving elements, there are additional options
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
// If the node you are targeting is not available at the initial execution
|
|
99
100
|
// of the script, set a timeout for 2 seconds
|
|
100
101
|
const node2 = await createRef("#target", { timeout: 2000 });
|
|
101
102
|
|
package/dist/API.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ declare const API: {
|
|
|
12
12
|
* @param selectColumns *OPTIONAL* if desired, enter your own custom OData query for advanced GET results. Format = select=column1,column2,column3...
|
|
13
13
|
* @returns a Promise resolving the successful results of the GET request, or rejecting the failed results of the GET request
|
|
14
14
|
*/
|
|
15
|
-
getRecord(tableSetName: string, recordID: string, selectColumns?: string): Promise<
|
|
15
|
+
getRecord<T>(tableSetName: string, recordID: string, selectColumns?: string): Promise<T>;
|
|
16
16
|
/**
|
|
17
17
|
*
|
|
18
18
|
* @param tableSetName The dataverse set name of the table being queried
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
interface IBusinessRule {
|
|
2
|
+
/**
|
|
3
|
+
* @param condition A function that returns a boolean to determine
|
|
4
|
+
* the visibility of the target element. If `condition()` returns true, the element is shown;
|
|
5
|
+
* otherwise, it is hidden.
|
|
6
|
+
|
|
7
|
+
* @param clearValuesOnHide Should the values in the targeted field be cleared when hidden? Defaults to true
|
|
8
|
+
*/
|
|
9
|
+
setVisibility?: [condition: () => boolean, clearValuesOnHide?: boolean];
|
|
10
|
+
/**
|
|
11
|
+
* @param isRequired Function determining if field is required
|
|
12
|
+
* @param isValid Function validating field input. allows access to the invoked expression passed by {@link isRequired}
|
|
13
|
+
*/
|
|
14
|
+
setRequired?: [
|
|
15
|
+
isRequired: () => boolean,
|
|
16
|
+
isValid: (isRequired: () => boolean) => boolean
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* @param condition A function to determine if the value provided should be applied to this field
|
|
20
|
+
* @param value The value to set for the HTML element.
|
|
21
|
+
* for parents of boolean radios, pass true or false as value, or
|
|
22
|
+
* an expression returning a boolean
|
|
23
|
+
*/
|
|
24
|
+
setValue?: [condition: () => boolean, value: any];
|
|
25
|
+
/**
|
|
26
|
+
* @param condition A function to determine if this field
|
|
27
|
+
* should be enabled in a form, or disabled. True || 1 = disabled. False || 0 = enabled
|
|
28
|
+
*/
|
|
29
|
+
setDisabled?: [condition: () => boolean];
|
|
30
|
+
}
|
|
1
31
|
export declare const _init: unique symbol;
|
|
2
32
|
declare const _destroy: unique symbol;
|
|
3
33
|
declare const _valueSync: unique symbol;
|
|
@@ -11,8 +41,9 @@ declare const _debounceTime: unique symbol;
|
|
|
11
41
|
declare const _observers: unique symbol;
|
|
12
42
|
declare const _boundEventListeners: unique symbol;
|
|
13
43
|
export default class DOMNodeReference {
|
|
14
|
-
target:
|
|
15
|
-
|
|
44
|
+
target: Element | string;
|
|
45
|
+
logicalName?: string;
|
|
46
|
+
root: Element;
|
|
16
47
|
private [_debounceTime];
|
|
17
48
|
private isLoaded;
|
|
18
49
|
private defaultDisplay;
|
|
@@ -49,8 +80,9 @@ export default class DOMNodeReference {
|
|
|
49
80
|
* @param root - Optionally specify the element within to search for the element targeted by 'target'
|
|
50
81
|
* Defaults to 'document.body'
|
|
51
82
|
*/
|
|
52
|
-
/******/ /******/ constructor(target:
|
|
83
|
+
/******/ /******/ constructor(target: Element | string, root: Element | undefined, debounceTime: number);
|
|
53
84
|
[_init](): Promise<void>;
|
|
85
|
+
private eventMapping;
|
|
54
86
|
/**
|
|
55
87
|
* Initializes value synchronization with appropriate event listeners
|
|
56
88
|
* based on element type.
|
|
@@ -187,7 +219,7 @@ export default class DOMNodeReference {
|
|
|
187
219
|
remove(): this;
|
|
188
220
|
/**
|
|
189
221
|
*
|
|
190
|
-
* @param
|
|
222
|
+
* @param options and object containing the styles you want to set : {key: value} e.g.: {'display': 'block'}
|
|
191
223
|
* @returns - Instance of this [provides option to method chain]
|
|
192
224
|
*/
|
|
193
225
|
setStyle(options: Partial<CSSStyleDeclaration>): this;
|
|
@@ -196,52 +228,61 @@ export default class DOMNodeReference {
|
|
|
196
228
|
* @returns - Instance of this [provides option to method chain]
|
|
197
229
|
*/
|
|
198
230
|
uncheckRadios(): DOMNodeReference;
|
|
231
|
+
/**
|
|
232
|
+
* Applies a business rule to manage visibility, required state, value, and disabled state dynamically.
|
|
233
|
+
*
|
|
234
|
+
* @param rule The business rule containing conditions for various actions.
|
|
235
|
+
* @param dependencies For re-evaluation conditions when the state of the dependencies change
|
|
236
|
+
* @returns Instance of this for method chaining.
|
|
237
|
+
*/
|
|
238
|
+
applyBusinessRule(rule: IBusinessRule, dependencies: DOMNodeReference[]): DOMNodeReference;
|
|
199
239
|
/**
|
|
200
240
|
* Configures conditional rendering for the target element based on a condition
|
|
201
241
|
* and the visibility of one or more trigger elements.
|
|
202
|
-
*
|
|
203
|
-
* @param condition
|
|
242
|
+
* @deprecated Use the new 'applyBusinessRule Method
|
|
243
|
+
* @param condition A function that returns a boolean to determine
|
|
204
244
|
* the visibility of the target element. If `condition()` returns true, the element is shown;
|
|
205
245
|
* otherwise, it is hidden.
|
|
206
246
|
* @param dependencies - An array of `DOMNodeReference` instances. Event listeners are
|
|
207
247
|
* registered on each to toggle the visibility of the target element based on the `condition` and the visibility of
|
|
208
248
|
* the target node.
|
|
209
|
-
* @throws
|
|
210
|
-
* @returns
|
|
249
|
+
* @throws When there's an error in setting up conditional rendering
|
|
250
|
+
* @returns Instance of this [provides option to method chain]
|
|
211
251
|
*/
|
|
212
252
|
configureConditionalRendering(condition: () => boolean, dependencies?: Array<DOMNodeReference>, clearValuesOnHide?: boolean): DOMNodeReference;
|
|
213
253
|
/**
|
|
214
254
|
* Sets up validation and requirement rules for the field with enhanced error handling and dynamic updates.
|
|
215
|
-
*
|
|
216
|
-
* @param isRequired
|
|
217
|
-
* @param isValid
|
|
218
|
-
* @param fieldDisplayName
|
|
219
|
-
* @param dependencies
|
|
220
|
-
* @returns
|
|
221
|
-
* @throws
|
|
255
|
+
* @deprecated Use the new 'applyBusinessRule Method
|
|
256
|
+
* @param isRequired Function determining if field is required
|
|
257
|
+
* @param isValid Function validating field input
|
|
258
|
+
* @param fieldDisplayName Display name for error messages
|
|
259
|
+
* @param dependencies Fields that trigger requirement/validation updates
|
|
260
|
+
* @returns Instance of this
|
|
261
|
+
* @throws If validation setup fails
|
|
222
262
|
*/
|
|
223
263
|
configureValidationAndRequirements(isRequired: () => boolean, isValid: () => boolean, fieldDisplayName: string, dependencies: Array<DOMNodeReference>): DOMNodeReference;
|
|
224
264
|
/**
|
|
225
265
|
* Sets up tracking for dependencies using both event listeners and mutation observers.
|
|
226
266
|
* @private
|
|
227
|
-
* @param handler
|
|
228
|
-
* @param dependencies
|
|
229
|
-
* @param options
|
|
267
|
+
* @param handler The function to execute when dependencies change
|
|
268
|
+
* @param dependencies Array of dependent DOM nodes to track
|
|
269
|
+
* @param options Additional configuration options. clearValuesOnHide defaults to false.
|
|
270
|
+
* all other options defaults to true
|
|
230
271
|
*/
|
|
231
272
|
private _configDependencyTracking;
|
|
232
273
|
/**
|
|
233
274
|
* Sets the required level for the field by adding or removing the "required-field" class on the label.
|
|
234
275
|
*
|
|
235
|
-
* @param isRequired
|
|
276
|
+
* @param isRequired Determines whether the field should be marked as required.
|
|
236
277
|
* If true, the "required-field" class is added to the label; if false, it is removed.
|
|
237
|
-
* @returns
|
|
278
|
+
* @returns Instance of this [provides option to method chain]
|
|
238
279
|
*/
|
|
239
280
|
setRequiredLevel(isRequired: (() => boolean) | boolean): DOMNodeReference;
|
|
240
281
|
/**
|
|
241
282
|
* Executes a callback function once the element is fully loaded.
|
|
242
283
|
* If the element is already loaded, the callback is called immediately.
|
|
243
284
|
* Otherwise, a MutationObserver is used to detect when the element is added to the DOM.
|
|
244
|
-
* @param callback
|
|
285
|
+
* @param callback A callback function to execute once the element is loaded.
|
|
245
286
|
* Receives instance of 'this' as an argument
|
|
246
287
|
*/
|
|
247
288
|
onceLoaded(callback: (instance: DOMNodeReference) => any): any;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import DOMNodeReference from "./DOMNodeReference.js";
|
|
2
|
+
export declare class DOMNodeReferenceArray extends Array<DOMNodeReference> {
|
|
3
|
+
/**
|
|
4
|
+
* Hides all the containers of the DOMNodeReference instances in the array.
|
|
5
|
+
*/
|
|
6
|
+
hideAll(this: DOMNodeReferenceArray): DOMNodeReferenceArray;
|
|
7
|
+
/**
|
|
8
|
+
* Shows all the containers of the DOMNodeReference instances in the array.
|
|
9
|
+
*/
|
|
10
|
+
showAll(this: DOMNodeReferenceArray): DOMNodeReferenceArray;
|
|
11
|
+
}
|
|
12
|
+
export declare function enhanceArray<T extends string>(array: DOMNodeReference[]): DOMNodeReferenceArray & Record<T, DOMNodeReference>;
|
package/dist/List.d.ts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import DOMNodeReference from "./DOMNodeReference.js";
|
|
2
|
+
import { DOMNodeReferenceArray } from "./DOMNodeReferenceArray.js";
|
|
3
|
+
/**
|
|
4
|
+
* @function
|
|
5
|
+
* Get all controls related to the form for manipulating with the
|
|
6
|
+
* DOMNodeReference class. Rather than having to instantiate each fields that you need manually,
|
|
7
|
+
* you can call this method once with the form ID and gain access to all fields
|
|
8
|
+
* @param formId The string GUID of the form you want to bind to
|
|
9
|
+
* @returns An array of DOMNodeReferences
|
|
10
|
+
*/
|
|
11
|
+
export default function bindForm<T extends string>(formId: string): Promise<DOMNodeReferenceArray & Record<T, DOMNodeReference>>;
|
package/dist/bundle.js
CHANGED
|
@@ -102,6 +102,40 @@ var API = {
|
|
|
102
102
|
};
|
|
103
103
|
var API_default = API;
|
|
104
104
|
|
|
105
|
+
// src/DOMNodeReferenceArray.ts
|
|
106
|
+
var DOMNodeReferenceArray = class extends Array {
|
|
107
|
+
/**
|
|
108
|
+
* Hides all the containers of the DOMNodeReference instances in the array.
|
|
109
|
+
*/
|
|
110
|
+
hideAll() {
|
|
111
|
+
this.forEach((instance) => instance.hide());
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Shows all the containers of the DOMNodeReference instances in the array.
|
|
116
|
+
*/
|
|
117
|
+
showAll() {
|
|
118
|
+
this.forEach((instance) => instance.show());
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
function enhanceArray(array) {
|
|
123
|
+
const enhancedArray = new DOMNodeReferenceArray(...array);
|
|
124
|
+
return new Proxy(enhancedArray, {
|
|
125
|
+
get(target, prop, receiver) {
|
|
126
|
+
if (prop in target) {
|
|
127
|
+
return Reflect.get(target, prop, receiver);
|
|
128
|
+
}
|
|
129
|
+
if (typeof prop === "string") {
|
|
130
|
+
return target.find(
|
|
131
|
+
(instance) => instance.target.toString().replace(/[#\[\]]/g, "") === prop || instance.logicalName === prop
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
105
139
|
// src/waitFor.ts
|
|
106
140
|
function waitFor(target, root = document, multiple = false, debounceTime) {
|
|
107
141
|
return new Promise((resolve, reject) => {
|
|
@@ -130,7 +164,7 @@ function waitFor(target, root = document, multiple = false, debounceTime) {
|
|
|
130
164
|
} else {
|
|
131
165
|
reject(
|
|
132
166
|
new Error(
|
|
133
|
-
`No elements found with target: "${target}" within ${debounceTime / 1e3} seconds. If the element you are
|
|
167
|
+
`No elements found with target: "${target}" within ${debounceTime / 1e3} seconds. If the element you are expecting has not loaded yet, consider raising your timeout.`
|
|
134
168
|
)
|
|
135
169
|
);
|
|
136
170
|
}
|
|
@@ -154,7 +188,7 @@ function waitFor(target, root = document, multiple = false, debounceTime) {
|
|
|
154
188
|
observer.disconnect();
|
|
155
189
|
reject(
|
|
156
190
|
new Error(
|
|
157
|
-
`Element not found by target: "${target}" within ${debounceTime / 1e3} second. If the element you are
|
|
191
|
+
`Element not found by target: "${target}" within ${debounceTime / 1e3} second. If the element you are expecting has not loaded yet, consider raising your timeout.`
|
|
158
192
|
)
|
|
159
193
|
);
|
|
160
194
|
}, debounceTime);
|
|
@@ -285,6 +319,7 @@ var _boundEventListeners = Symbol("BEV");
|
|
|
285
319
|
var DOMNodeReference = class _DOMNodeReference {
|
|
286
320
|
// properties initialized in the constructor
|
|
287
321
|
target;
|
|
322
|
+
logicalName;
|
|
288
323
|
root;
|
|
289
324
|
[_debounceTime];
|
|
290
325
|
isLoaded;
|
|
@@ -306,6 +341,18 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
306
341
|
/******/
|
|
307
342
|
constructor(target, root = document.body, debounceTime) {
|
|
308
343
|
this.target = target;
|
|
344
|
+
if (typeof target === "string") {
|
|
345
|
+
let lName = null;
|
|
346
|
+
const bracketMatch = target.match(/\[([^\]]+)\]/);
|
|
347
|
+
if (bracketMatch) {
|
|
348
|
+
lName = bracketMatch[1];
|
|
349
|
+
const quoteMatch = lName.match(/["']([^"']+)["']/);
|
|
350
|
+
if (quoteMatch) {
|
|
351
|
+
lName = quoteMatch[1];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
this.logicalName = (lName || target).replace(/[#\[\]]/g, "");
|
|
355
|
+
}
|
|
309
356
|
this.root = root;
|
|
310
357
|
this[_debounceTime] = debounceTime;
|
|
311
358
|
this.isLoaded = false;
|
|
@@ -348,6 +395,14 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
348
395
|
throw new DOMNodeInitializationError(this, errorMessage);
|
|
349
396
|
}
|
|
350
397
|
}
|
|
398
|
+
eventMapping = {
|
|
399
|
+
checkbox: "click",
|
|
400
|
+
radio: "click",
|
|
401
|
+
select: "change",
|
|
402
|
+
"select-multiple": "change",
|
|
403
|
+
textarea: "keyup"
|
|
404
|
+
// Add other input types as needed
|
|
405
|
+
};
|
|
351
406
|
/**
|
|
352
407
|
* Initializes value synchronization with appropriate event listeners
|
|
353
408
|
* based on element type.
|
|
@@ -356,30 +411,20 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
356
411
|
async [_valueSync]() {
|
|
357
412
|
try {
|
|
358
413
|
this.updateValue();
|
|
359
|
-
if (!(this.element instanceof HTMLElement)) {
|
|
360
|
-
throw new Error("Element is not a valid HTML element");
|
|
361
|
-
}
|
|
362
|
-
const eventMapping = {
|
|
363
|
-
checkbox: "click",
|
|
364
|
-
radio: "click",
|
|
365
|
-
select: "change",
|
|
366
|
-
"select-multiple": "change"
|
|
367
|
-
// Add other input types as needed
|
|
368
|
-
};
|
|
369
414
|
let eventType;
|
|
370
415
|
if (this.element instanceof HTMLSelectElement) {
|
|
371
416
|
eventType = "change";
|
|
372
417
|
} else if (this.element instanceof HTMLInputElement) {
|
|
373
|
-
eventType = eventMapping[this.element.type]
|
|
418
|
+
eventType = this.eventMapping[this.element.type] ?? "input";
|
|
419
|
+
} else if (this.element instanceof HTMLTextAreaElement) {
|
|
420
|
+
eventType = this.eventMapping[this.element.type] ?? "input";
|
|
374
421
|
} else {
|
|
375
422
|
eventType = "input";
|
|
376
423
|
}
|
|
377
424
|
this.element.addEventListener(eventType, this.updateValue);
|
|
378
|
-
const _element = this.element;
|
|
379
|
-
const _updateValue = this.updateValue;
|
|
380
425
|
this[_boundEventListeners].push({
|
|
381
|
-
element:
|
|
382
|
-
handler:
|
|
426
|
+
element: this.element,
|
|
427
|
+
handler: this.updateValue,
|
|
383
428
|
event: eventType
|
|
384
429
|
});
|
|
385
430
|
if (this.element instanceof HTMLInputElement && this.element.dataset.type === "date") {
|
|
@@ -436,8 +481,11 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
436
481
|
value: input.value !== "" ? Number(input.value) : null
|
|
437
482
|
};
|
|
438
483
|
default:
|
|
484
|
+
let cleanValue = input.value;
|
|
485
|
+
if (this.element.classList.contains("decimal") || this.element.classList.contains("money"))
|
|
486
|
+
cleanValue = input.value.replace(/[$,]/g, "");
|
|
439
487
|
return {
|
|
440
|
-
value: this.element.classList.contains("decimal") || this.element.classList.contains("money") ? parseFloat(
|
|
488
|
+
value: this.element.classList.contains("decimal") || this.element.classList.contains("money") ? parseFloat(cleanValue) : cleanValue
|
|
441
489
|
};
|
|
442
490
|
}
|
|
443
491
|
}
|
|
@@ -481,7 +529,8 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
481
529
|
this.noRadio = await createDOMNodeReference(`#${this.element.id}_0`);
|
|
482
530
|
}
|
|
483
531
|
[_bindMethods]() {
|
|
484
|
-
|
|
532
|
+
const prototype = Object.getPrototypeOf(this);
|
|
533
|
+
for (const key of Object.getOwnPropertyNames(prototype)) {
|
|
485
534
|
const value = this[key];
|
|
486
535
|
if (key !== "constructor" && typeof value === "function") {
|
|
487
536
|
this[key] = value.bind(this);
|
|
@@ -579,9 +628,21 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
579
628
|
if (value instanceof Function) {
|
|
580
629
|
value = value();
|
|
581
630
|
}
|
|
582
|
-
|
|
631
|
+
let eventType;
|
|
632
|
+
if (this.element instanceof HTMLSelectElement) {
|
|
633
|
+
eventType = "change";
|
|
634
|
+
} else if (this.element instanceof HTMLInputElement) {
|
|
635
|
+
eventType = this.eventMapping[this.element.type] ?? "input";
|
|
636
|
+
} else if (this.element instanceof HTMLTextAreaElement) {
|
|
637
|
+
eventType = this.eventMapping[this.element.type] ?? "input";
|
|
638
|
+
} else {
|
|
639
|
+
eventType = "input";
|
|
640
|
+
}
|
|
641
|
+
this.element.dispatchEvent(new Event(eventType, { bubbles: false }));
|
|
642
|
+
if (this.element.classList.contains("boolean-radio") && this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
|
|
583
643
|
this.yesRadio.element.checked = value;
|
|
584
644
|
this.noRadio.element.checked = !value;
|
|
645
|
+
this.value = value;
|
|
585
646
|
} else {
|
|
586
647
|
this.element.value = value;
|
|
587
648
|
}
|
|
@@ -770,7 +831,7 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
770
831
|
}
|
|
771
832
|
/**
|
|
772
833
|
*
|
|
773
|
-
* @param
|
|
834
|
+
* @param options and object containing the styles you want to set : {key: value} e.g.: {'display': 'block'}
|
|
774
835
|
* @returns - Instance of this [provides option to method chain]
|
|
775
836
|
*/
|
|
776
837
|
setStyle(options) {
|
|
@@ -779,7 +840,8 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
779
840
|
`powerpagestoolkit: 'DOMNodeReference.setStyle' required options to be in the form of an object. Argument passed was of type: ${typeof options}`
|
|
780
841
|
);
|
|
781
842
|
}
|
|
782
|
-
for (const
|
|
843
|
+
for (const _key in options) {
|
|
844
|
+
const key = _key;
|
|
783
845
|
this.element.style[key] = options[key];
|
|
784
846
|
}
|
|
785
847
|
return this;
|
|
@@ -799,18 +861,127 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
799
861
|
}
|
|
800
862
|
return this;
|
|
801
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Applies a business rule to manage visibility, required state, value, and disabled state dynamically.
|
|
866
|
+
*
|
|
867
|
+
* @param rule The business rule containing conditions for various actions.
|
|
868
|
+
* @param dependencies For re-evaluation conditions when the state of the dependencies change
|
|
869
|
+
* @returns Instance of this for method chaining.
|
|
870
|
+
*/
|
|
871
|
+
applyBusinessRule(rule, dependencies) {
|
|
872
|
+
try {
|
|
873
|
+
if (rule.setVisibility) {
|
|
874
|
+
const [condition, clearValuesOnHide = true] = rule.setVisibility;
|
|
875
|
+
const initialState = condition.bind(this)();
|
|
876
|
+
this.toggleVisibility(initialState);
|
|
877
|
+
if (dependencies.length) {
|
|
878
|
+
this._configDependencyTracking(
|
|
879
|
+
() => this.toggleVisibility(condition.bind(this)()),
|
|
880
|
+
dependencies,
|
|
881
|
+
{
|
|
882
|
+
clearValuesOnHide,
|
|
883
|
+
observeVisibility: true,
|
|
884
|
+
trackInputEvents: false,
|
|
885
|
+
trackRadioButtons: false
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (rule.setRequired) {
|
|
891
|
+
const [isRequired, isValid] = rule.setRequired;
|
|
892
|
+
const fieldDisplayName = (() => {
|
|
893
|
+
let label = this.getLabel();
|
|
894
|
+
if (!label)
|
|
895
|
+
return new Error(
|
|
896
|
+
`There was an error accessing the label for this element: ${String(
|
|
897
|
+
this.target
|
|
898
|
+
)}`
|
|
899
|
+
);
|
|
900
|
+
label = label.innerHTML;
|
|
901
|
+
if (label.length > 50) {
|
|
902
|
+
label = label.substring(0, 50) + "...";
|
|
903
|
+
}
|
|
904
|
+
return label;
|
|
905
|
+
})();
|
|
906
|
+
if (typeof Page_Validators === "undefined") {
|
|
907
|
+
throw new ValidationConfigError(this, "Page_Validators not found");
|
|
908
|
+
}
|
|
909
|
+
const validatorId = `${this.element.id}Validator`;
|
|
910
|
+
const newValidator = document.createElement("span");
|
|
911
|
+
newValidator.style.display = "none";
|
|
912
|
+
newValidator.id = validatorId;
|
|
913
|
+
Object.assign(newValidator, {
|
|
914
|
+
controltovalidate: this.element.id,
|
|
915
|
+
errormessage: `<a href='#${this.element.id}_label'>${fieldDisplayName} is a required field</a>`,
|
|
916
|
+
evaluationfunction: () => {
|
|
917
|
+
const isFieldRequired = isRequired.bind(this)();
|
|
918
|
+
const isFieldVisible = window.getComputedStyle(this.visibilityController).display !== "none";
|
|
919
|
+
return !isFieldRequired || !isFieldVisible || isValid.bind(this)(isRequired.bind(this));
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
Page_Validators.push(newValidator);
|
|
923
|
+
this.setRequiredLevel(isRequired.bind(this)());
|
|
924
|
+
this._configDependencyTracking(
|
|
925
|
+
() => this.setRequiredLevel(isRequired.bind(this)()),
|
|
926
|
+
dependencies,
|
|
927
|
+
{ clearValuesOnHide: false }
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
if (rule.setValue) {
|
|
931
|
+
const [condition, value] = rule.setValue;
|
|
932
|
+
if (condition.bind(this)()) {
|
|
933
|
+
this.setValue.bind(this)(value);
|
|
934
|
+
}
|
|
935
|
+
if (dependencies.length) {
|
|
936
|
+
this._configDependencyTracking(
|
|
937
|
+
() => {
|
|
938
|
+
if (condition.bind(this)()) {
|
|
939
|
+
this.setValue.bind(this)(value);
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
dependencies,
|
|
943
|
+
{ clearValuesOnHide: false }
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (rule.setDisabled) {
|
|
948
|
+
const [condition] = rule.setDisabled;
|
|
949
|
+
condition.bind(this)() ? this.disable() : this.enable();
|
|
950
|
+
if (dependencies.length) {
|
|
951
|
+
this._configDependencyTracking(
|
|
952
|
+
() => {
|
|
953
|
+
condition.bind(this)() ? this.enable() : this.disable();
|
|
954
|
+
},
|
|
955
|
+
dependencies,
|
|
956
|
+
{
|
|
957
|
+
clearValuesOnHide: false,
|
|
958
|
+
observeVisibility: true,
|
|
959
|
+
trackInputEvents: true,
|
|
960
|
+
trackRadioButtons: true
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return this;
|
|
966
|
+
} catch (error) {
|
|
967
|
+
throw new ValidationConfigError(
|
|
968
|
+
this,
|
|
969
|
+
`Failed to apply business rule: ${error}`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
802
973
|
/**
|
|
803
974
|
* Configures conditional rendering for the target element based on a condition
|
|
804
975
|
* and the visibility of one or more trigger elements.
|
|
805
|
-
*
|
|
806
|
-
* @param condition
|
|
976
|
+
* @deprecated Use the new 'applyBusinessRule Method
|
|
977
|
+
* @param condition A function that returns a boolean to determine
|
|
807
978
|
* the visibility of the target element. If `condition()` returns true, the element is shown;
|
|
808
979
|
* otherwise, it is hidden.
|
|
809
980
|
* @param dependencies - An array of `DOMNodeReference` instances. Event listeners are
|
|
810
981
|
* registered on each to toggle the visibility of the target element based on the `condition` and the visibility of
|
|
811
982
|
* the target node.
|
|
812
|
-
* @throws
|
|
813
|
-
* @returns
|
|
983
|
+
* @throws When there's an error in setting up conditional rendering
|
|
984
|
+
* @returns Instance of this [provides option to method chain]
|
|
814
985
|
*/
|
|
815
986
|
configureConditionalRendering(condition, dependencies, clearValuesOnHide = true) {
|
|
816
987
|
try {
|
|
@@ -844,13 +1015,13 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
844
1015
|
}
|
|
845
1016
|
/**
|
|
846
1017
|
* Sets up validation and requirement rules for the field with enhanced error handling and dynamic updates.
|
|
847
|
-
*
|
|
848
|
-
* @param isRequired
|
|
849
|
-
* @param isValid
|
|
850
|
-
* @param fieldDisplayName
|
|
851
|
-
* @param dependencies
|
|
852
|
-
* @returns
|
|
853
|
-
* @throws
|
|
1018
|
+
* @deprecated Use the new 'applyBusinessRule Method
|
|
1019
|
+
* @param isRequired Function determining if field is required
|
|
1020
|
+
* @param isValid Function validating field input
|
|
1021
|
+
* @param fieldDisplayName Display name for error messages
|
|
1022
|
+
* @param dependencies Fields that trigger requirement/validation updates
|
|
1023
|
+
* @returns Instance of this
|
|
1024
|
+
* @throws If validation setup fails
|
|
854
1025
|
*/
|
|
855
1026
|
configureValidationAndRequirements(isRequired, isValid, fieldDisplayName, dependencies) {
|
|
856
1027
|
if (!fieldDisplayName?.trim()) {
|
|
@@ -899,9 +1070,10 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
899
1070
|
/**
|
|
900
1071
|
* Sets up tracking for dependencies using both event listeners and mutation observers.
|
|
901
1072
|
* @private
|
|
902
|
-
* @param handler
|
|
903
|
-
* @param dependencies
|
|
904
|
-
* @param options
|
|
1073
|
+
* @param handler The function to execute when dependencies change
|
|
1074
|
+
* @param dependencies Array of dependent DOM nodes to track
|
|
1075
|
+
* @param options Additional configuration options. clearValuesOnHide defaults to false.
|
|
1076
|
+
* all other options defaults to true
|
|
905
1077
|
*/
|
|
906
1078
|
_configDependencyTracking(handler, dependencies, options = {
|
|
907
1079
|
clearValuesOnHide: false,
|
|
@@ -978,9 +1150,9 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
978
1150
|
/**
|
|
979
1151
|
* Sets the required level for the field by adding or removing the "required-field" class on the label.
|
|
980
1152
|
*
|
|
981
|
-
* @param isRequired
|
|
1153
|
+
* @param isRequired Determines whether the field should be marked as required.
|
|
982
1154
|
* If true, the "required-field" class is added to the label; if false, it is removed.
|
|
983
|
-
* @returns
|
|
1155
|
+
* @returns Instance of this [provides option to method chain]
|
|
984
1156
|
*/
|
|
985
1157
|
setRequiredLevel(isRequired) {
|
|
986
1158
|
if (isRequired instanceof Function) {
|
|
@@ -995,7 +1167,7 @@ var DOMNodeReference = class _DOMNodeReference {
|
|
|
995
1167
|
* Executes a callback function once the element is fully loaded.
|
|
996
1168
|
* If the element is already loaded, the callback is called immediately.
|
|
997
1169
|
* Otherwise, a MutationObserver is used to detect when the element is added to the DOM.
|
|
998
|
-
* @param callback
|
|
1170
|
+
* @param callback A callback function to execute once the element is loaded.
|
|
999
1171
|
* Receives instance of 'this' as an argument
|
|
1000
1172
|
*/
|
|
1001
1173
|
onceLoaded(callback) {
|
|
@@ -1034,22 +1206,8 @@ async function createDOMNodeReference(target, options = {
|
|
|
1034
1206
|
`'options' must be of type 'object'. Received type: '${typeof options}'`
|
|
1035
1207
|
);
|
|
1036
1208
|
}
|
|
1209
|
+
validateOptions(options);
|
|
1037
1210
|
const { multiple = false, root = document.body, timeout = 0 } = options;
|
|
1038
|
-
if (typeof multiple !== "boolean" && typeof multiple !== "function") {
|
|
1039
|
-
throw new Error(
|
|
1040
|
-
`'multiple' must be of type 'boolean' or 'function'. Received type: '${typeof multiple}'`
|
|
1041
|
-
);
|
|
1042
|
-
}
|
|
1043
|
-
if (!(root instanceof HTMLElement)) {
|
|
1044
|
-
throw new Error(
|
|
1045
|
-
`'root' must be of type 'HTMLElement'. Received type: '${typeof root}'`
|
|
1046
|
-
);
|
|
1047
|
-
}
|
|
1048
|
-
if (typeof timeout !== "number") {
|
|
1049
|
-
throw new Error(
|
|
1050
|
-
`'timeout' must be of type 'number'. Received type: '${typeof timeout}'`
|
|
1051
|
-
);
|
|
1052
|
-
}
|
|
1053
1211
|
const isMultiple = typeof multiple === "function" ? multiple() : multiple;
|
|
1054
1212
|
if (isMultiple) {
|
|
1055
1213
|
if (typeof target !== "string") {
|
|
@@ -1074,6 +1232,33 @@ async function createDOMNodeReference(target, options = {
|
|
|
1074
1232
|
throw new Error(e);
|
|
1075
1233
|
}
|
|
1076
1234
|
}
|
|
1235
|
+
function validateOptions(options) {
|
|
1236
|
+
const { multiple = false, root = document.body, timeout = 0 } = options;
|
|
1237
|
+
if (typeof multiple !== "boolean" && typeof multiple !== "function") {
|
|
1238
|
+
throw new Error(
|
|
1239
|
+
`'multiple' must be of type 'boolean' or 'function'. Received type: '${typeof multiple}'`
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
if (typeof multiple === "function") {
|
|
1243
|
+
const value = multiple();
|
|
1244
|
+
if (typeof value !== "boolean") {
|
|
1245
|
+
throw new Error(
|
|
1246
|
+
`'multiple' function must return a boolean. Received type: '${typeof value}'`
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (!(root instanceof HTMLElement)) {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
`'root' must be of type 'HTMLElement'. Received type: '${typeof root}'`
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
if (typeof timeout !== "number") {
|
|
1256
|
+
throw new Error(
|
|
1257
|
+
`'timeout' must be of type 'number'. Received type: '${typeof timeout}'`
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1077
1262
|
function createProxyHandler() {
|
|
1078
1263
|
return {
|
|
1079
1264
|
get: (target, prop) => {
|
|
@@ -1089,25 +1274,62 @@ function createProxyHandler() {
|
|
|
1089
1274
|
}
|
|
1090
1275
|
};
|
|
1091
1276
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1277
|
+
|
|
1278
|
+
// src/bindForm.ts
|
|
1279
|
+
async function bindForm(formId) {
|
|
1280
|
+
try {
|
|
1281
|
+
const form = await API_default.getRecord("systemforms", formId);
|
|
1282
|
+
const { formxml } = form;
|
|
1283
|
+
const parser = new DOMParser();
|
|
1284
|
+
const xmlDoc = parser.parseFromString(formxml, "application/xml");
|
|
1285
|
+
const controls = processElements(xmlDoc.getElementsByTagName("control"));
|
|
1286
|
+
const sections = processElements(xmlDoc.getElementsByTagName("section"));
|
|
1287
|
+
const tabs = processElements(xmlDoc.getElementsByTagName("tab"));
|
|
1288
|
+
const resolvedRefs = await Promise.all([...controls, ...sections, ...tabs]);
|
|
1289
|
+
return enhanceArray(
|
|
1290
|
+
resolvedRefs.filter((ref) => ref !== null)
|
|
1291
|
+
);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
if (error instanceof Error) {
|
|
1294
|
+
console.error(error.message);
|
|
1295
|
+
throw error;
|
|
1296
|
+
} else {
|
|
1297
|
+
console.error(error);
|
|
1298
|
+
throw new Error(String(error));
|
|
1105
1299
|
}
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function processElements(element) {
|
|
1303
|
+
return Array.from(element).map((element2) => {
|
|
1304
|
+
const identifyingAttribute = getIdentifyingAttribute(element2.tagName);
|
|
1305
|
+
const datafieldname = element2.getAttribute(identifyingAttribute);
|
|
1306
|
+
if (!datafieldname) return null;
|
|
1307
|
+
const referenceString = createReferenceString(
|
|
1308
|
+
element2.tagName,
|
|
1309
|
+
datafieldname
|
|
1310
|
+
);
|
|
1311
|
+
if (!referenceString) return null;
|
|
1312
|
+
return createDOMNodeReference(referenceString).catch((error) => {
|
|
1313
|
+
console.warn(
|
|
1314
|
+
`Failed to create a reference to the form field: ${datafieldname}`,
|
|
1315
|
+
error
|
|
1316
|
+
);
|
|
1317
|
+
return null;
|
|
1318
|
+
});
|
|
1319
|
+
}).filter(Boolean);
|
|
1320
|
+
}
|
|
1321
|
+
function getIdentifyingAttribute(tagName) {
|
|
1322
|
+
return tagName === "control" ? "id" : tagName === "tab" || tagName === "section" ? "name" : "id";
|
|
1323
|
+
}
|
|
1324
|
+
function createReferenceString(tagName, datafieldname) {
|
|
1325
|
+
if (tagName === "control") return `#${datafieldname}`;
|
|
1326
|
+
if (tagName === "tab" || tagName === "section")
|
|
1327
|
+
return `[data-name="${datafieldname}"]`;
|
|
1328
|
+
return null;
|
|
1108
1329
|
}
|
|
1109
1330
|
export {
|
|
1110
1331
|
API_default as API,
|
|
1332
|
+
bindForm,
|
|
1111
1333
|
createDOMNodeReference as createRef,
|
|
1112
1334
|
waitFor
|
|
1113
1335
|
};
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
import { DOMNodeReferenceArray } from "./DOMNodeReferenceArray.js";
|
|
1
2
|
import DOMNodeReference from "./DOMNodeReference.js";
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
interface ICreationOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Should this call return an array of instantiated references, or just a single?
|
|
6
|
+
* Defaults to false, returning a single instance.
|
|
7
|
+
*/
|
|
8
|
+
multiple?: (() => boolean) | boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Optionally specify the element within which to search for the element targeted by 'target'.
|
|
11
|
+
* Defaults to 'document.body'.
|
|
12
|
+
*/
|
|
4
13
|
root?: HTMLElement;
|
|
14
|
+
/**
|
|
15
|
+
* Optionally specify the amount of time that should be waited to find the targeted element before throwing an error.
|
|
16
|
+
* Useful for async DOM loading. Relies on MutationObserver.
|
|
17
|
+
* WARNING: Implementing multiple references with timeout can result in infinite loading.
|
|
18
|
+
*/
|
|
5
19
|
timeout?: number;
|
|
20
|
+
}
|
|
21
|
+
export default function createDOMNodeReference(target: Element, options?: ICreationOptions): Promise<DOMNodeReference>;
|
|
22
|
+
export default function createDOMNodeReference(target: string, options?: Omit<ICreationOptions, "multiple"> & {
|
|
23
|
+
/**
|
|
24
|
+
* Should this call return an array of instantiated references, or just a single instance?
|
|
25
|
+
* Defaults to false, returning a single instance.
|
|
26
|
+
*/
|
|
27
|
+
multiple?: false;
|
|
6
28
|
}): Promise<DOMNodeReference>;
|
|
7
|
-
export default function createDOMNodeReference(target: string, options?: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
export default function createDOMNodeReference(target: string, options?: Omit<ICreationOptions, "multiple"> & {
|
|
30
|
+
/**
|
|
31
|
+
* Should this call return an array of instantiated references, or just a single instance?
|
|
32
|
+
* Defaults to false, returning a single instance.
|
|
33
|
+
*/
|
|
34
|
+
multiple?: true;
|
|
35
|
+
}): Promise<DOMNodeReferenceArray>;
|
|
36
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/waitFor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function waitFor(target:
|
|
2
|
-
export default function waitFor(target:
|
|
1
|
+
export default function waitFor(target: Element | string, root: Element | Document, multiple: false, debounceTime: number): Promise<HTMLElement>;
|
|
2
|
+
export default function waitFor(target: Element | string, root: Element | Document, multiple: true, debounceTime: number): Promise<HTMLElement[]>;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powerpagestoolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Reference, manipulate, and engage with Power Pages sites through the nodes in the DOM; use a variety of custom methods that allow customizing your power pages site quicker and easier. ",
|
|
5
|
-
"main": "./dist/
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"typecheck": "tsc",
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@babel/preset-env": "^7.25.8",
|
|
17
17
|
"@types/node": "^22.8.6",
|
|
18
|
+
"@typescript-eslint/parser": "^8.20.0",
|
|
18
19
|
"css-loader": "^7.1.2",
|
|
19
20
|
"esbuild": "^0.24.0",
|
|
20
21
|
"esbuild-css-modules-plugin": "^3.1.2",
|
|
21
22
|
"eslint": "^8.57.1",
|
|
22
23
|
"eslint-plugin-import": "^2.31.0",
|
|
24
|
+
"globals": "^15.14.0",
|
|
23
25
|
"rimraf": "^6.0.1",
|
|
24
26
|
"ts-loader": "^9.5.1",
|
|
25
27
|
"typescript": "^5.6.3",
|