jj 2.9.0 → 3.0.0-rc.4
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 +51 -2
- package/lib/bundle.cjs +1547 -698
- package/lib/bundle.cjs.map +1 -1
- package/lib/bundle.d.cts +1248 -694
- package/lib/bundle.d.ts +1248 -694
- package/lib/bundle.global.js +1541 -666
- package/lib/bundle.global.js.map +1 -1
- package/lib/bundle.js +1544 -682
- package/lib/bundle.js.map +1 -1
- package/lib/bundle.min.cjs +2 -1
- package/lib/bundle.min.cjs.map +1 -1
- package/lib/bundle.min.d.cts +1248 -694
- package/lib/bundle.min.d.ts +1248 -694
- package/lib/bundle.min.global.js +2 -1
- package/lib/bundle.min.global.js.map +1 -1
- package/lib/bundle.min.js +2 -1
- package/lib/bundle.min.js.map +1 -1
- package/package.json +13 -16
- package/SKILL.md +0 -685
package/lib/bundle.cjs
CHANGED
|
@@ -23,7 +23,6 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
|
|
|
23
23
|
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
24
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
25
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
26
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
27
26
|
|
|
28
27
|
// src/index.ts
|
|
29
28
|
var src_exports = {};
|
|
@@ -33,159 +32,47 @@ __export(src_exports, {
|
|
|
33
32
|
JJE: () => JJE,
|
|
34
33
|
JJET: () => JJET,
|
|
35
34
|
JJHE: () => JJHE,
|
|
35
|
+
JJME: () => JJME,
|
|
36
36
|
JJN: () => JJN,
|
|
37
37
|
JJSE: () => JJSE,
|
|
38
38
|
JJSR: () => JJSR,
|
|
39
39
|
JJT: () => JJT,
|
|
40
|
-
ShadowMaster: () => ShadowMaster,
|
|
41
|
-
addLinkPre: () => addLinkPre,
|
|
42
40
|
attr2prop: () => attr2prop,
|
|
43
|
-
|
|
44
|
-
cssToStyle: () => cssToStyle,
|
|
45
|
-
doc: () => doc,
|
|
46
|
-
fetchCss: () => fetchCss,
|
|
47
|
-
fetchHtml: () => fetchHtml,
|
|
41
|
+
defineComponent: () => defineComponent,
|
|
48
42
|
fetchStyle: () => fetchStyle,
|
|
49
|
-
|
|
50
|
-
fileExt: () => fileExt,
|
|
51
|
-
h: () => h,
|
|
52
|
-
keb2cam: () => keb2cam,
|
|
53
|
-
keb2pas: () => keb2pas,
|
|
54
|
-
nextAnimationFrame: () => nextAnimationFrame,
|
|
55
|
-
pas2keb: () => pas2keb,
|
|
56
|
-
registerComponent: () => registerComponent,
|
|
57
|
-
sleep: () => sleep
|
|
43
|
+
fetchTemplate: () => fetchTemplate
|
|
58
44
|
});
|
|
59
45
|
module.exports = __toCommonJS(src_exports);
|
|
60
46
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// node_modules/jty/lib/number.js
|
|
70
|
-
var { isNaN, isFinite, isInteger } = Number;
|
|
71
|
-
function isNum(x) {
|
|
72
|
-
return typeof x === "number" && !isNaN(x);
|
|
73
|
-
}
|
|
74
|
-
function inRange(x, min, max) {
|
|
75
|
-
if (!isNum(x)) {
|
|
76
|
-
throw new TypeError(`inRange(): "x" must be a number. Got ${x} (${typeof x})`);
|
|
77
|
-
}
|
|
78
|
-
if (isDef(min)) {
|
|
79
|
-
if (!isNum(min)) {
|
|
80
|
-
throw new TypeError(`inRange(): "min" must be a number. Got ${min} (${typeof min})`);
|
|
81
|
-
}
|
|
82
|
-
if (isDef(max)) {
|
|
83
|
-
if (!isNum(max)) {
|
|
84
|
-
throw new TypeError(`inRange(): "max" must be a number. Got ${max} (${typeof max})`);
|
|
85
|
-
}
|
|
86
|
-
if (min > max) {
|
|
87
|
-
return max <= x && x <= min;
|
|
88
|
-
}
|
|
89
|
-
return min <= x && x <= max;
|
|
90
|
-
}
|
|
91
|
-
return x >= min;
|
|
92
|
-
} else if (isDef(max)) {
|
|
93
|
-
if (!isNum(max)) {
|
|
94
|
-
throw new TypeError(`inRange(): "max" must be a number. Got ${max} (${typeof max})`);
|
|
95
|
-
}
|
|
96
|
-
return x <= max;
|
|
97
|
-
}
|
|
98
|
-
throw new TypeError(`inRange(): expected at least min or max to be defined. Got min=${min} and max=${max}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// node_modules/jty/lib/array.js
|
|
102
|
-
var { isArray } = Array;
|
|
103
|
-
function isArr(x, minLen = 0, maxLen) {
|
|
104
|
-
return isArray(x) && inRange(x.length, minLen, maxLen);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// node_modules/jty/lib/object.js
|
|
108
|
-
var { hasOwnProperty } = Object;
|
|
109
|
-
function isObj(x) {
|
|
110
|
-
return Boolean(x) && typeof x === "object";
|
|
111
|
-
}
|
|
112
|
-
function isA(x, classConstructor) {
|
|
113
|
-
if (!isFn(classConstructor)) {
|
|
114
|
-
throw new TypeError(`Expected a constructor function. Got ${classConstructor} (${typeof classConstructor})`);
|
|
115
|
-
}
|
|
116
|
-
return x instanceof classConstructor;
|
|
47
|
+
// src/internal.ts
|
|
48
|
+
var NS = "http://www.w3.org/";
|
|
49
|
+
var MATHML_NS = NS + "1998/Math/MathML";
|
|
50
|
+
var SVG_NS = NS + "2000/svg";
|
|
51
|
+
function errMsg(varName, expected, received, extra) {
|
|
52
|
+
const ret = `Expected '${varName}' to be ${expected}. Got ${received} (${typeof received})`;
|
|
53
|
+
return extra ? `${ret}.
|
|
54
|
+
${extra}` : ret;
|
|
117
55
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
for (let propName of propNames) {
|
|
123
|
-
if (!(propName in x)) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return true;
|
|
56
|
+
function typeErr(varName, expected, received, extra) {
|
|
57
|
+
return new TypeError(errMsg(varName, expected, received, extra));
|
|
128
58
|
}
|
|
129
|
-
|
|
130
|
-
// node_modules/jty/lib/string.js
|
|
131
59
|
function isStr(x) {
|
|
132
60
|
return typeof x === "string";
|
|
133
61
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var { hasOwnProperty: hasOwnProperty2 } = Object;
|
|
137
|
-
var { isArray: isArray2 } = Array;
|
|
138
|
-
|
|
139
|
-
// src/internal.ts
|
|
140
|
-
function errMsg(varName, expected, received) {
|
|
141
|
-
return `Expected '${varName}' to be ${expected}. Got ${received} (${typeof received})`;
|
|
142
|
-
}
|
|
143
|
-
function typeErr(varName, expected, received) {
|
|
144
|
-
return new TypeError(errMsg(varName, expected, received));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// src/util.ts
|
|
148
|
-
function fileExt(path) {
|
|
149
|
-
if (!isStr(path)) {
|
|
150
|
-
throw typeErr("path", "a string", path);
|
|
151
|
-
}
|
|
152
|
-
const lastDotIndex = path.lastIndexOf(".");
|
|
153
|
-
if (lastDotIndex === -1) {
|
|
154
|
-
return "";
|
|
155
|
-
}
|
|
156
|
-
const ext = path.slice(lastDotIndex + 1);
|
|
157
|
-
if (ext.indexOf("/") !== -1) {
|
|
158
|
-
return "";
|
|
159
|
-
}
|
|
160
|
-
return ext.toLowerCase().trim();
|
|
161
|
-
}
|
|
162
|
-
function nextAnimationFrame() {
|
|
163
|
-
return new Promise((resolve) => requestAnimationFrame(resolve));
|
|
164
|
-
}
|
|
165
|
-
function sleep(ms = 0) {
|
|
166
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
62
|
+
function isNum(x) {
|
|
63
|
+
return typeof x === "number" && !Number.isNaN(x);
|
|
167
64
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return await sheet.replace(css);
|
|
65
|
+
function isObj(x) {
|
|
66
|
+
return typeof x === "object" && x !== null && Object.getPrototypeOf(x) === Object.prototype;
|
|
171
67
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
function
|
|
175
|
-
if (!isStr(str)) {
|
|
176
|
-
throw typeErr("str", "a string", str);
|
|
177
|
-
}
|
|
178
|
-
if (/[^a-zA-Z0-9_]/.test(str)) {
|
|
179
|
-
throw new SyntaxError(errMsg("str", "alphanumeric characters and underscores", str));
|
|
68
|
+
function isInstance(x, classConstructor) {
|
|
69
|
+
if (typeof classConstructor !== "function") {
|
|
70
|
+
throw typeErr("classConstructor", "a constructor function", classConstructor);
|
|
180
71
|
}
|
|
181
|
-
return
|
|
72
|
+
return x instanceof classConstructor;
|
|
182
73
|
}
|
|
183
|
-
function
|
|
184
|
-
|
|
185
|
-
throw typeErr("str", "a string", str);
|
|
186
|
-
}
|
|
187
|
-
return str.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("") || // Handle strings that were not kebab-case to begin with (e.g. 'single', 'camelCase')
|
|
188
|
-
(str.length > 0 ? str.charAt(0).toUpperCase() + str.slice(1) : "");
|
|
74
|
+
function hasProp(x, propName) {
|
|
75
|
+
return x !== null && typeof x === "object" && propName in x;
|
|
189
76
|
}
|
|
190
77
|
function keb2cam(str) {
|
|
191
78
|
if (!isStr(str)) {
|
|
@@ -193,30 +80,57 @@ function keb2cam(str) {
|
|
|
193
80
|
}
|
|
194
81
|
return str.replace(/^-+|-+$/g, "").replace(/-+([a-z])/g, (g, c) => c.toUpperCase());
|
|
195
82
|
}
|
|
83
|
+
function toStr(x) {
|
|
84
|
+
switch (typeof x) {
|
|
85
|
+
case "string":
|
|
86
|
+
return x;
|
|
87
|
+
case "object":
|
|
88
|
+
if (x === null) {
|
|
89
|
+
return "null";
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
return JSON.stringify(x);
|
|
93
|
+
} catch {
|
|
94
|
+
return String(x);
|
|
95
|
+
}
|
|
96
|
+
case "function":
|
|
97
|
+
return x.toString();
|
|
98
|
+
default:
|
|
99
|
+
return String(x);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
196
102
|
|
|
197
103
|
// src/wrappers/JJET.ts
|
|
198
|
-
var _ref
|
|
104
|
+
var _ref;
|
|
199
105
|
var _JJET = class _JJET {
|
|
200
106
|
/**
|
|
201
107
|
* Creates a JJET instance.
|
|
202
108
|
*
|
|
203
109
|
* @param ref - The EventTarget to wrap.
|
|
204
110
|
* @throws {TypeError} If `ref` is not an EventTarget.
|
|
111
|
+
* @see {@link JJET.from} for the factory form.
|
|
205
112
|
*/
|
|
206
113
|
constructor(ref) {
|
|
207
|
-
__privateAdd(this, _JJET_instances);
|
|
208
114
|
__privateAdd(this, _ref);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
throw new TypeError(`JJET expects an EventTarget instance. Got ${ref} (${typeof ref}). `);
|
|
115
|
+
if (!isInstance(ref, EventTarget)) {
|
|
116
|
+
throw typeErr("ref", "an EventTarget instance", ref);
|
|
212
117
|
}
|
|
213
118
|
__privateSet(this, _ref, ref);
|
|
214
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Creates a JJET instance from an EventTarget reference.
|
|
122
|
+
*
|
|
123
|
+
* @param ref - The EventTarget instance.
|
|
124
|
+
* @returns A new JJET instance.
|
|
125
|
+
* @see {@link constructor} for input validation behavior.
|
|
126
|
+
*/
|
|
215
127
|
static from(ref) {
|
|
216
128
|
return new _JJET(ref);
|
|
217
129
|
}
|
|
218
130
|
/**
|
|
219
131
|
* Gets the underlying DOM object.
|
|
132
|
+
*
|
|
133
|
+
* @see {@link run} for fluent callbacks that can also access this same wrapped target.
|
|
220
134
|
*/
|
|
221
135
|
get ref() {
|
|
222
136
|
return __privateGet(this, _ref);
|
|
@@ -224,26 +138,34 @@ var _JJET = class _JJET {
|
|
|
224
138
|
/**
|
|
225
139
|
* Adds an event listener.
|
|
226
140
|
*
|
|
227
|
-
* @
|
|
228
|
-
*
|
|
229
|
-
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const onResize = () => {
|
|
144
|
+
* console.log('resized')
|
|
145
|
+
* }
|
|
146
|
+
*
|
|
147
|
+
* JJET.from(window).on('resize', onResize)
|
|
148
|
+
* ```
|
|
230
149
|
*
|
|
231
150
|
* @example
|
|
232
151
|
* ```ts
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
152
|
+
* const target = {
|
|
153
|
+
* dispatching a plain Event object.
|
|
154
|
+
* increment() {
|
|
155
|
+
* this.count++
|
|
156
|
+
* },
|
|
157
|
+
* }
|
|
158
|
+
*
|
|
159
|
+
* @param options Optional event initialization dictionary.
|
|
237
160
|
* ```
|
|
238
|
-
* @
|
|
161
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/Event | Event()} for details on event options.
|
|
239
162
|
* @param handler - The event handler.
|
|
240
163
|
* @param options - Optional event listener options.
|
|
241
164
|
* @returns This instance for chaining.
|
|
242
165
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | EventTarget.addEventListener}
|
|
243
166
|
*/
|
|
244
167
|
on(eventName, handler, options) {
|
|
245
|
-
|
|
246
|
-
this.ref.addEventListener(eventName, boundHandler, options);
|
|
168
|
+
this.ref.addEventListener(eventName, handler, options);
|
|
247
169
|
return this;
|
|
248
170
|
}
|
|
249
171
|
/**
|
|
@@ -254,10 +176,15 @@ var _JJET = class _JJET {
|
|
|
254
176
|
*
|
|
255
177
|
* @example
|
|
256
178
|
* ```ts
|
|
257
|
-
* const
|
|
258
|
-
*
|
|
259
|
-
*
|
|
179
|
+
* const onResize = () => {
|
|
180
|
+
* console.log('resized')
|
|
181
|
+
* }
|
|
182
|
+
*
|
|
183
|
+
* const win = JJET.from(window)
|
|
184
|
+
* win.on('resize', onResize)
|
|
185
|
+
* win.off('resize', onResize)
|
|
260
186
|
* ```
|
|
187
|
+
*
|
|
261
188
|
* @param eventName - The name of the event.
|
|
262
189
|
* @param handler - The event handler.
|
|
263
190
|
* @param options - Optional event listener options or boolean.
|
|
@@ -265,70 +192,109 @@ var _JJET = class _JJET {
|
|
|
265
192
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener | EventTarget.removeEventListener}
|
|
266
193
|
*/
|
|
267
194
|
off(eventName, handler, options) {
|
|
268
|
-
|
|
269
|
-
this.ref.removeEventListener(eventName, boundHandler, options);
|
|
195
|
+
this.ref.removeEventListener(eventName, handler, options);
|
|
270
196
|
return this;
|
|
271
197
|
}
|
|
272
198
|
/**
|
|
273
199
|
* Dispatches an Event at the specified EventTarget.
|
|
274
200
|
*
|
|
275
|
-
* @param event - The Event object to dispatch.
|
|
201
|
+
* @param event - The Event object to dispatch. Since CustomEvent extends Event, you can also dispatch CustomEvent instances here.
|
|
276
202
|
* @returns This instance for chaining.
|
|
277
203
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent | EventTarget.dispatchEvent}
|
|
204
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | CustomEvent} for creating custom events with payloads.
|
|
205
|
+
* @see {@link triggerEvent} for a convenience wrapper that creates and dispatches an Event in one step.
|
|
206
|
+
* @see {@link triggerCustomEvent} for a convenience wrapper that creates and dispatches a CustomEvent with JJ's default settings in one step.
|
|
278
207
|
*/
|
|
279
208
|
trigger(event) {
|
|
280
|
-
|
|
281
|
-
|
|
209
|
+
try {
|
|
210
|
+
this.ref.dispatchEvent(event);
|
|
211
|
+
return this;
|
|
212
|
+
} catch (cause) {
|
|
213
|
+
throw new Error(`Failed to trigger the event ${JSON.stringify(event)}`, { cause });
|
|
214
|
+
}
|
|
282
215
|
}
|
|
283
216
|
/**
|
|
284
|
-
*
|
|
217
|
+
* Creates a new `Event` and dispatches on the wrapped target.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* This is a convenience wrapper around {@link trigger} for the common case of
|
|
221
|
+
* dispatching a plain Event object.
|
|
222
|
+
*
|
|
223
|
+
* The created event defaults to `bubbles: true` and `composed: true`.
|
|
224
|
+
* Pass `options` to override those defaults.
|
|
225
|
+
*
|
|
226
|
+
* @param type The name identifying the type of the event for example 'click'
|
|
227
|
+
* @param options Optional event initialization dictionary.
|
|
228
|
+
* @returns This instance for chaining.
|
|
229
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/Event | Event()} for details on event options.
|
|
230
|
+
*/
|
|
231
|
+
triggerEvent(type, options) {
|
|
232
|
+
return this.trigger(new Event(type, { bubbles: true, composed: true, ...options }));
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Creates a new `CustomEvent` and dispatches on the wrapped target.
|
|
236
|
+
*
|
|
237
|
+
* @remarks
|
|
238
|
+
* This is a convenience wrapper around {@link trigger} for the common case of
|
|
239
|
+
* dispatching a payload-bearing custom event.
|
|
240
|
+
*
|
|
241
|
+
* The created event defaults to `bubbles: true` and `composed: true`.
|
|
242
|
+
* Pass `options` to override those defaults.
|
|
285
243
|
*
|
|
244
|
+
* @param type - The event type name.
|
|
245
|
+
* @param detail - Optional payload exposed as `event.detail`.
|
|
246
|
+
* @param options - Additional `CustomEvent` options excluding `detail`.
|
|
247
|
+
* @returns This instance for chaining.
|
|
286
248
|
* @example
|
|
287
249
|
* ```ts
|
|
288
|
-
*
|
|
289
|
-
* console.log(this.ref)
|
|
290
|
-
* })
|
|
250
|
+
* JJET.from(window).triggerCustomEvent('panel-ready', { id: '123' })
|
|
291
251
|
* ```
|
|
292
|
-
* @remarks
|
|
293
|
-
* If you want to access the current JJ* instance using `this` keyword, you SHOULD use a `function` not an arrow function.
|
|
294
|
-
* If the function throws, `run()` doesn't swallow the exception.
|
|
295
|
-
* So if you're expecting an error, make sure to wrap it in a `try..catch` block and handle the exception.
|
|
296
|
-
* If the function returns a promise, you can `await` on the response.
|
|
297
252
|
*
|
|
298
|
-
* @
|
|
299
|
-
*
|
|
300
|
-
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* JJET.from(this).triggerCustomEvent('todo-toggle', {
|
|
256
|
+
* id: '123',
|
|
257
|
+
* done: true,
|
|
258
|
+
* })
|
|
259
|
+
* ```
|
|
260
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent | CustomEvent()}
|
|
301
261
|
*/
|
|
302
|
-
|
|
303
|
-
return
|
|
262
|
+
triggerCustomEvent(type, detail, options) {
|
|
263
|
+
return this.trigger(new CustomEvent(type, { bubbles: true, composed: true, ...options, detail }));
|
|
304
264
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
265
|
+
/**
|
|
266
|
+
* Runs a function in the context of this JJET instance.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* node
|
|
271
|
+
* .run(function (context) {
|
|
272
|
+
* console.log(this.ref)
|
|
273
|
+
* console.log(context.ref)
|
|
274
|
+
* })
|
|
275
|
+
* .trigger(new Event('ready'))
|
|
276
|
+
* ```
|
|
277
|
+
* @remarks
|
|
278
|
+
* Use this to make synchronous adjustments while staying in a fluent chain.
|
|
279
|
+
* The callback return value is ignored.
|
|
280
|
+
* If you want to access the current JJ* instance using `this`, use a `function` rather than an arrow function.
|
|
281
|
+
*
|
|
282
|
+
* @param fn - The synchronous function to run. `this` inside the function will refer to this JJET instance, and the wrapped instance is also passed as the first argument.
|
|
283
|
+
* @returns This instance for chaining.
|
|
284
|
+
* @see {@link ref} for direct access to the wrapped native target.
|
|
285
|
+
* @see {@link on} for event listener chaining.
|
|
286
|
+
* @see {@link trigger} for dispatching events in-chain.
|
|
287
|
+
*/
|
|
288
|
+
run(fn) {
|
|
289
|
+
try {
|
|
290
|
+
fn.call(this, this);
|
|
291
|
+
} catch (cause) {
|
|
292
|
+
throw new Error(`Failed to run the function`, { cause });
|
|
327
293
|
}
|
|
328
|
-
|
|
294
|
+
return this;
|
|
329
295
|
}
|
|
330
|
-
return bound;
|
|
331
296
|
};
|
|
297
|
+
_ref = new WeakMap();
|
|
332
298
|
var JJET = _JJET;
|
|
333
299
|
|
|
334
300
|
// src/wrappers/JJN-raw.ts
|
|
@@ -336,6 +302,12 @@ var JJN = class _JJN extends JJET {
|
|
|
336
302
|
/**
|
|
337
303
|
* Creates a JJN instance from a Node reference.
|
|
338
304
|
*
|
|
305
|
+
* @remarks
|
|
306
|
+
* For better type safety, use the specific wrapper type if you know the Node type:
|
|
307
|
+
* {@link JJHE} for HTMLElement, {@link JJSE} for SVGElement, {@link JJT} for Text, etc.
|
|
308
|
+
*
|
|
309
|
+
* Alternatively, use {@link JJN.wrap} to automatically determine and create the appropriate wrapper.
|
|
310
|
+
*
|
|
339
311
|
* @example
|
|
340
312
|
* ```ts
|
|
341
313
|
* const node = JJN.from(document.createTextNode('hello'))
|
|
@@ -343,6 +315,7 @@ var JJN = class _JJN extends JJET {
|
|
|
343
315
|
*
|
|
344
316
|
* @param node - The Node instance.
|
|
345
317
|
* @returns A new JJN instance.
|
|
318
|
+
* @see {@link JJN.wrap} for automatic wrapper selection
|
|
346
319
|
*/
|
|
347
320
|
static from(node) {
|
|
348
321
|
return new _JJN(node);
|
|
@@ -351,75 +324,76 @@ var JJN = class _JJN extends JJET {
|
|
|
351
324
|
* Checks if a value can be passed to the `wrap()` or `unwrap()` function.
|
|
352
325
|
*
|
|
353
326
|
* @remarks
|
|
354
|
-
* This is useful for filtering the array that is passed to `append()`, `prepend()` or `setChildren()
|
|
327
|
+
* This is useful for filtering the array that is passed to `append()`, `prepend()` or `setChildren()`.
|
|
328
|
+
* See {@link Wrappable} type for the full definition.
|
|
355
329
|
*
|
|
356
330
|
* @param x an unknown value
|
|
357
331
|
* @returns true if `x` is a string, Node (or its descendent), JJN (or its descendent)
|
|
332
|
+
* @see {@link JJN.wrap} for converting Wrappable into wrappers.
|
|
333
|
+
* @see {@link JJN.unwrap} for converting Wrappable into native nodes.
|
|
358
334
|
*/
|
|
359
335
|
static isWrappable(x) {
|
|
360
|
-
return isStr(x) ||
|
|
336
|
+
return isStr(x) || isInstance(x, Node) || isInstance(x, _JJN);
|
|
361
337
|
}
|
|
362
338
|
/**
|
|
363
339
|
* Wraps a native DOM node or string into the most specific JJ wrapper available.
|
|
364
340
|
*
|
|
365
341
|
* @remarks
|
|
366
|
-
* This function acts as
|
|
367
|
-
* subclass of `JJN` (e.g., `JJHE` for `HTMLElement`, `JJT` for `Text`).
|
|
368
|
-
*
|
|
342
|
+
* This factory function acts as an intelligent wrapper, inspecting the input type and returning the appropriate
|
|
343
|
+
* subclass of `JJN` (e.g., `JJHE` for `HTMLElement`, `JJT` for `Text`, `JJSE` for `SVGElement`, etc.).
|
|
344
|
+
* Strings are automatically converted to Text nodes wrapped in `JJT`.
|
|
345
|
+
*
|
|
346
|
+
* If the input is already a JJ wrapper, it is returned unchanged (no double-wrapping).
|
|
347
|
+
* See the full implementation in src/wrappers/JJN.ts which extends this with support for internal wrapper types.
|
|
369
348
|
*
|
|
370
349
|
* @example
|
|
371
350
|
* ```ts
|
|
372
|
-
* const bodyWrapper = JJN.wrap(document.body) // Returns JJHE
|
|
351
|
+
* const bodyWrapper = JJN.wrap(document.body) // Returns JJHE<HTMLBodyElement>
|
|
373
352
|
* const textWrapper = JJN.wrap('Hello') // Returns JJT wrapping a new Text node
|
|
353
|
+
* const existing = JJN.wrap(alreadyWrapped) // Returns alreadyWrapped unchanged
|
|
374
354
|
* ```
|
|
375
355
|
*
|
|
376
356
|
* @param raw - The object to wrap. If it's already Wrapped, it'll be returned without any change. We don't double-wrap or clone it.
|
|
377
357
|
* @returns The most granular Wrapped subclass instance. If the input is already wrapped, it'll be returned as is without cloning.
|
|
378
|
-
* @
|
|
358
|
+
* @see {@link JJN.from} for explicit base wrapper construction.
|
|
359
|
+
* @see {@link JJN.unwrap} for the reverse conversion.
|
|
379
360
|
*/
|
|
380
361
|
static wrap(raw) {
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return new _JJN(raw);
|
|
387
|
-
}
|
|
362
|
+
if (isInstance(raw, _JJN)) {
|
|
363
|
+
return raw;
|
|
364
|
+
}
|
|
365
|
+
if (isInstance(raw, Node)) {
|
|
366
|
+
return new _JJN(raw);
|
|
388
367
|
}
|
|
389
|
-
|
|
368
|
+
return _JJN.from(document.createTextNode(toStr(raw)));
|
|
390
369
|
}
|
|
391
370
|
/**
|
|
392
371
|
* Extracts the underlying native DOM node from a wrapper.
|
|
393
372
|
*
|
|
394
373
|
* @remarks
|
|
395
374
|
* If the input is already a native Node, it is returned as is.
|
|
396
|
-
* If the input is a
|
|
375
|
+
* If the input is a JJ wrapper, its underlying node is returned.
|
|
376
|
+
* Otherwise, the input is coerced into a Text node.
|
|
377
|
+
* Plain objects are stringified with JSON when possible, and fall back to `String(...)`.
|
|
397
378
|
*
|
|
398
379
|
* @example
|
|
399
380
|
* ```ts
|
|
400
381
|
* const rawElement = JJN.unwrap(myJJHE) // Returns HTMLElement
|
|
401
382
|
* ```
|
|
402
383
|
*
|
|
403
|
-
* @param obj - The
|
|
384
|
+
* @param obj - The value to unwrap.
|
|
404
385
|
* @returns The underlying DOM node.
|
|
405
|
-
* @
|
|
386
|
+
* @see {@link JJN.wrap} for the reverse conversion.
|
|
387
|
+
* @see {@link JJN.isWrappable} for pre-validation checks.
|
|
406
388
|
*/
|
|
407
389
|
static unwrap(obj) {
|
|
408
|
-
if (
|
|
409
|
-
return document.createTextNode(obj);
|
|
410
|
-
}
|
|
411
|
-
if (!isObj(obj)) {
|
|
412
|
-
throw new TypeError(`JJN.unwrap() expects a string, DOM Node, or JJ wrapper. Got ${obj} (${typeof obj}). `);
|
|
413
|
-
}
|
|
414
|
-
if (isA(obj, Node)) {
|
|
390
|
+
if (isInstance(obj, Node)) {
|
|
415
391
|
return obj;
|
|
416
392
|
}
|
|
417
|
-
if (
|
|
393
|
+
if (isInstance(obj, _JJN)) {
|
|
418
394
|
return obj.ref;
|
|
419
395
|
}
|
|
420
|
-
|
|
421
|
-
`Could not unwrap ${obj} (${typeof obj}). Expected a string, Node, or JJ wrapper. Make sure you're passing a valid DOM element or JJ wrapper.`
|
|
422
|
-
);
|
|
396
|
+
return document.createTextNode(toStr(obj));
|
|
423
397
|
}
|
|
424
398
|
/**
|
|
425
399
|
* Wraps an iterable object (e.g. an array of wrapped or DOM elements).
|
|
@@ -431,6 +405,8 @@ var JJN = class _JJN extends JJET {
|
|
|
431
405
|
*
|
|
432
406
|
* @param iterable - The iterable to wrap.
|
|
433
407
|
* @returns An array of wrapped instances.
|
|
408
|
+
* @see {@link JJN.wrap} for single-item wrapping.
|
|
409
|
+
* @see {@link JJN.unwrapAll} for the reverse iterable conversion.
|
|
434
410
|
*/
|
|
435
411
|
static wrapAll(iterable) {
|
|
436
412
|
return Array.from(iterable, _JJN.wrap);
|
|
@@ -445,6 +421,8 @@ var JJN = class _JJN extends JJET {
|
|
|
445
421
|
*
|
|
446
422
|
* @param iterable - The iterable to unwrap.
|
|
447
423
|
* @returns An array of native DOM nodes.
|
|
424
|
+
* @see {@link JJN.unwrap} for single-item unwrapping.
|
|
425
|
+
* @see {@link JJN.wrapAll} for iterable wrapping.
|
|
448
426
|
*/
|
|
449
427
|
static unwrapAll(iterable) {
|
|
450
428
|
return Array.from(iterable, _JJN.unwrap);
|
|
@@ -454,52 +432,31 @@ var JJN = class _JJN extends JJET {
|
|
|
454
432
|
*
|
|
455
433
|
* @param ref - The Node to wrap.
|
|
456
434
|
* @throws {TypeError} If `ref` is not a Node.
|
|
435
|
+
* @see {@link JJN.from} for the factory form.
|
|
436
|
+
* @see {@link JJN.wrap} for automatic subtype wrapping.
|
|
457
437
|
*/
|
|
458
438
|
constructor(ref) {
|
|
459
|
-
if (!
|
|
460
|
-
throw
|
|
461
|
-
`JJN expects a Node instance. Got ${ref} (${typeof ref}). Use JJN.from(node) with a DOM Node, or check that you're passing a valid DOM element.`
|
|
462
|
-
);
|
|
439
|
+
if (!isInstance(ref, Node)) {
|
|
440
|
+
throw typeErr("ref", "a DOM Node instance", ref);
|
|
463
441
|
}
|
|
464
442
|
super(ref);
|
|
465
443
|
}
|
|
466
|
-
|
|
467
|
-
* Gets the parent node wrapped in the most specific JJ wrapper available.
|
|
468
|
-
*
|
|
469
|
-
* @remarks
|
|
470
|
-
* Returns `null` when this node is detached and therefore has no parent.
|
|
471
|
-
*
|
|
472
|
-
* @example
|
|
473
|
-
* ```ts
|
|
474
|
-
* const text = JJT.fromStr('hello')
|
|
475
|
-
* JJHE.create('div').addChild(text)
|
|
476
|
-
* const parent = text.parent // JJHE
|
|
477
|
-
* ```
|
|
478
|
-
*
|
|
479
|
-
* @returns The wrapped parent node, or `null` if this node has no parent.
|
|
480
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode | Node.parentNode}
|
|
481
|
-
*/
|
|
482
|
-
get parent() {
|
|
444
|
+
getParent(required = false) {
|
|
483
445
|
const { parentNode } = this.ref;
|
|
484
|
-
|
|
446
|
+
if (parentNode) {
|
|
447
|
+
return _JJN.wrap(parentNode);
|
|
448
|
+
}
|
|
449
|
+
if (required) {
|
|
450
|
+
throw new ReferenceError("Node has no parent. Did you forget to attach it to the document?");
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
485
453
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
* @example
|
|
493
|
-
* ```ts
|
|
494
|
-
* const el = JJHE.create('div').addChild('hello', JJHE.create('span'))
|
|
495
|
-
* const children = el.children // [JJT, JJHE]
|
|
496
|
-
* ```
|
|
497
|
-
*
|
|
498
|
-
* @returns The wrapped child nodes.
|
|
499
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes | Node.childNodes}
|
|
500
|
-
*/
|
|
501
|
-
get children() {
|
|
502
|
-
return _JJN.wrapAll(this.ref.childNodes);
|
|
454
|
+
getChildren(required = false) {
|
|
455
|
+
const children = _JJN.wrapAll(this.ref.childNodes);
|
|
456
|
+
if (required && children.length === 0) {
|
|
457
|
+
throw new ReferenceError("Node has no children. Did you forget to initialize or append them?");
|
|
458
|
+
}
|
|
459
|
+
return children;
|
|
503
460
|
}
|
|
504
461
|
/**
|
|
505
462
|
* Clones the Node.
|
|
@@ -519,8 +476,9 @@ var JJN = class _JJN extends JJET {
|
|
|
519
476
|
*
|
|
520
477
|
* @example
|
|
521
478
|
* ```ts
|
|
479
|
+
* const doc = JJD.from(document)
|
|
522
480
|
* const el = JJHE.create('div')
|
|
523
|
-
* doc.body.addChild(el)
|
|
481
|
+
* doc.find('body', true).addChild(el)
|
|
524
482
|
* el.rm()
|
|
525
483
|
* ```
|
|
526
484
|
*
|
|
@@ -544,9 +502,12 @@ var JJN = class _JJN extends JJET {
|
|
|
544
502
|
* ```ts
|
|
545
503
|
* el.addText('Hello ')
|
|
546
504
|
* el.addText('World')
|
|
505
|
+
* // Behaves like document.createTextNode('Hello World') and appends it to el
|
|
506
|
+
* // Falsy values are converted to their string representation, except for empty string which is added as is.
|
|
507
|
+
* el.addText('Hello', '', 'world', null, undefined, '!!!') // Adds 6 text nodes with content 'Hello', '', 'world', 'null', 'undefined', and '!!!' respectively.
|
|
547
508
|
* ```
|
|
548
509
|
*
|
|
549
|
-
* @param
|
|
510
|
+
* @param textArr - The text to add. The actual text that's added follows the rules in document.createTextNode() which is basically what you get from String()
|
|
550
511
|
* @returns This instance for chaining.
|
|
551
512
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode | document.createTextNode}
|
|
552
513
|
*/
|
|
@@ -559,29 +520,17 @@ var JJN = class _JJN extends JJET {
|
|
|
559
520
|
};
|
|
560
521
|
|
|
561
522
|
// src/wrappers/JJNx.ts
|
|
523
|
+
function notNullish(x) {
|
|
524
|
+
return x != null;
|
|
525
|
+
}
|
|
562
526
|
var JJNx = class extends JJN {
|
|
563
|
-
/**
|
|
564
|
-
* Finds the first element matching a selector within this Element.
|
|
565
|
-
*
|
|
566
|
-
* @example
|
|
567
|
-
* ```ts
|
|
568
|
-
* const span = el.find('span') // Returns null if not found
|
|
569
|
-
* const span = el.find('span', true) // Throws if not found
|
|
570
|
-
* ```
|
|
571
|
-
*
|
|
572
|
-
* @param selector - The CSS selector.
|
|
573
|
-
* @param required - Whether to throw an error if not found. Defaults to false.
|
|
574
|
-
* @returns The wrapped element, or null if not found and required is false.
|
|
575
|
-
* @throws {TypeError} If selector is not a string or element not found and required is true.
|
|
576
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector | Element.querySelector}
|
|
577
|
-
*/
|
|
578
527
|
find(selector, required = false) {
|
|
579
528
|
const queryResult = this.ref.querySelector(selector);
|
|
580
529
|
if (queryResult) {
|
|
581
530
|
return JJN.wrap(queryResult);
|
|
582
531
|
}
|
|
583
532
|
if (required) {
|
|
584
|
-
throw new
|
|
533
|
+
throw new ReferenceError(`No element matched query "${selector}"`);
|
|
585
534
|
}
|
|
586
535
|
return null;
|
|
587
536
|
}
|
|
@@ -606,58 +555,128 @@ var JJNx = class extends JJN {
|
|
|
606
555
|
*
|
|
607
556
|
* @example
|
|
608
557
|
* ```ts
|
|
609
|
-
* el.addChild(
|
|
558
|
+
* el.addChild(JJHE.tree('span', null, 'hello'))
|
|
610
559
|
* ```
|
|
611
560
|
*
|
|
612
561
|
* @remarks
|
|
613
|
-
* To make template codes easier, this function ignores
|
|
562
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
563
|
+
* Other non-node values are coerced into Text nodes.
|
|
614
564
|
*
|
|
615
565
|
* @param children - The children to append.
|
|
616
566
|
* @returns This instance for chaining.
|
|
567
|
+
* @see {@link addChildren} for the array-based form.
|
|
568
|
+
* @see {@link addChildMap} for mapping arrays into appended children.
|
|
617
569
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/append | Element.append}
|
|
618
570
|
*/
|
|
619
571
|
addChild(...children) {
|
|
620
|
-
const nodes = JJN.unwrapAll(children.filter(
|
|
572
|
+
const nodes = JJN.unwrapAll(children.filter(notNullish));
|
|
621
573
|
this.ref.append(...nodes);
|
|
622
574
|
return this;
|
|
623
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Appends an array of children to this Element.
|
|
578
|
+
*
|
|
579
|
+
* @remarks
|
|
580
|
+
* This is the array-based companion to {@link addChild}.
|
|
581
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
582
|
+
* Other non-node values are coerced into Text nodes.
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```ts
|
|
586
|
+
* el.addChildren([JJHE.tree('span', null, 'hello'), ' world'])
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @param children - The children to append.
|
|
590
|
+
* @returns This instance for chaining.
|
|
591
|
+
* @throws {TypeError} If `children` is not an array.
|
|
592
|
+
* @see {@link addChild} for the variadic form.
|
|
593
|
+
* @see {@link addChildMap} for mapping arrays into appended children.
|
|
594
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/append | Element.append}
|
|
595
|
+
*/
|
|
596
|
+
addChildren(children) {
|
|
597
|
+
if (!Array.isArray(children)) {
|
|
598
|
+
throw typeErr("children", "an array of Wrappable", children);
|
|
599
|
+
}
|
|
600
|
+
return this.addChild(...children);
|
|
601
|
+
}
|
|
624
602
|
/**
|
|
625
603
|
* Prepends children to this Element.
|
|
626
604
|
*
|
|
627
605
|
* @example
|
|
628
606
|
* ```ts
|
|
629
|
-
* el.preChild(
|
|
607
|
+
* el.preChild(JJHE.tree('span', null, 'first'))
|
|
630
608
|
* ```
|
|
631
609
|
*
|
|
632
610
|
* @remarks
|
|
633
|
-
* To make template codes easier, this function ignores
|
|
611
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
612
|
+
* Other non-node values are coerced into Text nodes.
|
|
634
613
|
*
|
|
635
614
|
* @param children - The children to prepend.
|
|
636
615
|
* @returns This instance for chaining.
|
|
616
|
+
* @see {@link preChildren} for the array-based form.
|
|
617
|
+
* @see {@link preChildMap} for mapping arrays into prepended children.
|
|
637
618
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend | Element.prepend}
|
|
638
619
|
*/
|
|
639
620
|
preChild(...children) {
|
|
640
|
-
const nodes = JJN.unwrapAll(children.filter(
|
|
621
|
+
const nodes = JJN.unwrapAll(children.filter(notNullish));
|
|
641
622
|
this.ref.prepend(...nodes);
|
|
642
623
|
return this;
|
|
643
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Prepends an array of children to this Element.
|
|
627
|
+
*
|
|
628
|
+
* @remarks
|
|
629
|
+
* This is the array-based companion to {@link preChild}.
|
|
630
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
631
|
+
* Other non-node values are coerced into Text nodes.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```ts
|
|
635
|
+
* el.preChildren([JJHE.tree('span', null, 'first'), ' child'])
|
|
636
|
+
* ```
|
|
637
|
+
*
|
|
638
|
+
* @param children - The children to prepend.
|
|
639
|
+
* @returns This instance for chaining.
|
|
640
|
+
* @throws {TypeError} If `children` is not an array.
|
|
641
|
+
* @see {@link preChild} for the variadic form.
|
|
642
|
+
* @see {@link preChildMap} for mapping arrays into prepended children.
|
|
643
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend | Element.prepend}
|
|
644
|
+
*/
|
|
645
|
+
preChildren(children) {
|
|
646
|
+
if (!Array.isArray(children)) {
|
|
647
|
+
throw typeErr("children", "an array of Wrappable", children);
|
|
648
|
+
}
|
|
649
|
+
return this.preChild(...children);
|
|
650
|
+
}
|
|
644
651
|
/**
|
|
645
652
|
* Maps an array to children and appends them.
|
|
646
653
|
*
|
|
647
654
|
* @example
|
|
648
655
|
* ```ts
|
|
649
|
-
* node.addChildMap(['a', 'b'], item =>
|
|
656
|
+
* node.addChildMap(['a', 'b'], item => JJHE.tree('li', null, item))
|
|
650
657
|
* ```
|
|
651
658
|
*
|
|
652
659
|
* @remarks
|
|
653
|
-
* To make template codes easier, this function ignores
|
|
660
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
661
|
+
* Other non-node values are coerced into Text nodes.
|
|
654
662
|
*
|
|
655
663
|
* @param array - The source array.
|
|
656
664
|
* @param mapFn - The mapping function returning a Wrappable.
|
|
657
665
|
* @returns This instance for chaining.
|
|
666
|
+
* @throws {TypeError} If `array` is not an array.
|
|
667
|
+
* @throws {Error} If mapping the array or appending the children fails.
|
|
668
|
+
* @see {@link addChild} for directly appending variadic children.
|
|
669
|
+
* @see {@link addChildren} for appending a pre-built array of children.
|
|
658
670
|
*/
|
|
659
671
|
addChildMap(array, mapFn) {
|
|
660
|
-
|
|
672
|
+
if (!Array.isArray(array)) {
|
|
673
|
+
throw typeErr("array", "an array", array);
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
return this.addChildren(array.map(mapFn));
|
|
677
|
+
} catch (cause) {
|
|
678
|
+
throw new Error("Failed to map array to children", { cause });
|
|
679
|
+
}
|
|
661
680
|
}
|
|
662
681
|
/**
|
|
663
682
|
* Maps an array to children and prepends them.
|
|
@@ -668,35 +687,116 @@ var JJNx = class extends JJN {
|
|
|
668
687
|
* ```
|
|
669
688
|
*
|
|
670
689
|
* @remarks
|
|
671
|
-
* To make template codes easier, this function ignores
|
|
690
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
691
|
+
* Other non-node values are coerced into Text nodes.
|
|
672
692
|
*
|
|
673
693
|
* @param array - The source array.
|
|
674
694
|
* @param mapFn - The mapping function.
|
|
675
695
|
* @returns This instance for chaining.
|
|
696
|
+
* @throws {TypeError} If `array` is not an array.
|
|
697
|
+
* @throws {Error} If mapping the array or prepending the children fails.
|
|
698
|
+
* @see {@link preChild} for directly prepending variadic children.
|
|
699
|
+
* @see {@link preChildren} for prepending a pre-built array of children.
|
|
676
700
|
*/
|
|
677
701
|
preChildMap(array, mapFn) {
|
|
678
|
-
|
|
702
|
+
if (!Array.isArray(array)) {
|
|
703
|
+
throw typeErr("array", "an array", array);
|
|
704
|
+
}
|
|
705
|
+
try {
|
|
706
|
+
return this.preChildren(array.map(mapFn));
|
|
707
|
+
} catch (cause) {
|
|
708
|
+
throw new Error("Failed to map array to children", { cause });
|
|
709
|
+
}
|
|
679
710
|
}
|
|
680
711
|
/**
|
|
681
|
-
* Replaces the existing children of an Element with
|
|
712
|
+
* Replaces the existing children of an Element with new children.
|
|
682
713
|
*
|
|
683
714
|
* @remarks
|
|
684
715
|
* If no children are provided, it empties the Element.
|
|
685
|
-
* To make template codes easier, this function ignores
|
|
716
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
717
|
+
* Other non-node values are coerced into Text nodes.
|
|
686
718
|
*
|
|
687
719
|
* @example
|
|
688
720
|
* ```ts
|
|
689
|
-
* el.
|
|
721
|
+
* el.setChild(JJHE.tree('p', null, 'New Content'))
|
|
690
722
|
* ```
|
|
691
723
|
* @param children - The children to replace with.
|
|
692
724
|
* @returns This instance for chaining.
|
|
725
|
+
* @see {@link setChildren} for the array-based form.
|
|
726
|
+
* @see {@link setChildMap} for mapping arrays into replacement children.
|
|
727
|
+
* @see {@link empty} for clearing all children.
|
|
693
728
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren | Element.replaceChildren}
|
|
694
729
|
*/
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
730
|
+
setChild(...children) {
|
|
731
|
+
if (children.length === 0) {
|
|
732
|
+
this.ref.replaceChildren();
|
|
733
|
+
} else {
|
|
734
|
+
const nodes = JJN.unwrapAll(children.filter(notNullish));
|
|
735
|
+
this.ref.replaceChildren(...nodes);
|
|
736
|
+
}
|
|
698
737
|
return this;
|
|
699
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Replaces the existing children of an Element with an array of children.
|
|
741
|
+
*
|
|
742
|
+
* @remarks
|
|
743
|
+
* This is the array-based companion to {@link setChild}.
|
|
744
|
+
* Passing an empty array empties the Element.
|
|
745
|
+
* To make template codes easier, this function ignores nullish children (`null` and `undefined`).
|
|
746
|
+
* Other non-node values are coerced into Text nodes.
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* ```ts
|
|
750
|
+
* el.setChildren([JJHE.tree('p', null, 'New Content')])
|
|
751
|
+
* ```
|
|
752
|
+
*
|
|
753
|
+
* @param children - The children to replace with.
|
|
754
|
+
* @returns This instance for chaining.
|
|
755
|
+
* @throws {TypeError} If `children` is not an array of Wrappable.
|
|
756
|
+
* @see {@link setChild} for the variadic form.
|
|
757
|
+
* @see {@link setChildMap} for mapping arrays into replacement children.
|
|
758
|
+
* @see {@link empty} for clearing all children.
|
|
759
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren | Element.replaceChildren}
|
|
760
|
+
*/
|
|
761
|
+
setChildren(children) {
|
|
762
|
+
if (!Array.isArray(children)) {
|
|
763
|
+
throw typeErr("children", "an array of Wrappable", children);
|
|
764
|
+
}
|
|
765
|
+
return this.setChild(...children);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Maps an array to children and replaces the existing children with the result.
|
|
769
|
+
*
|
|
770
|
+
* @remarks
|
|
771
|
+
* This is the mapping companion to {@link setChildren}.
|
|
772
|
+
* To make template codes easier, this function ignores mapped nullish children (`null` and `undefined`).
|
|
773
|
+
* Other non-node values are coerced into Text nodes.
|
|
774
|
+
* Errors thrown by the mapping function or child replacement are wrapped with a higher-level error that preserves the original cause.
|
|
775
|
+
*
|
|
776
|
+
* @example
|
|
777
|
+
* ```ts
|
|
778
|
+
* node.setChildMap(['a', 'b'], item => JJHE.tree('li', null, item))
|
|
779
|
+
* ```
|
|
780
|
+
*
|
|
781
|
+
* @param array - The source array.
|
|
782
|
+
* @param mapFn - The mapping function returning a Wrappable.
|
|
783
|
+
* @returns This instance for chaining.
|
|
784
|
+
* @throws {TypeError} If `array` is not an array of Wrappable.
|
|
785
|
+
* @throws {Error} If mapping the array or replacing the children fails.
|
|
786
|
+
* @see {@link setChildren} for replacing children from a pre-built array.
|
|
787
|
+
* @see {@link setChild} for the variadic replacement form.
|
|
788
|
+
* @see {@link empty} for clearing all children without replacements.
|
|
789
|
+
*/
|
|
790
|
+
setChildMap(array, mapFn) {
|
|
791
|
+
if (!Array.isArray(array)) {
|
|
792
|
+
throw typeErr("array", "an array of Wrappable", array);
|
|
793
|
+
}
|
|
794
|
+
try {
|
|
795
|
+
return this.setChildren(array.map(mapFn));
|
|
796
|
+
} catch (cause) {
|
|
797
|
+
throw new Error("Failed to map array to children", { cause });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
700
800
|
/**
|
|
701
801
|
* Removes all children from this Element.
|
|
702
802
|
*
|
|
@@ -706,11 +806,79 @@ var JJNx = class extends JJN {
|
|
|
706
806
|
* ```
|
|
707
807
|
*
|
|
708
808
|
* @returns This instance for chaining.
|
|
809
|
+
* @see {@link setChild} for replacing children with a variadic list.
|
|
810
|
+
* @see {@link setChildren} for replacing children with an array.
|
|
811
|
+
* @see {@link setChildMap} for replacing children from mapped input.
|
|
709
812
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren | Element.setChildren}
|
|
710
813
|
*/
|
|
711
814
|
empty() {
|
|
712
|
-
this.
|
|
713
|
-
|
|
815
|
+
return this.setChild();
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Clones and appends template-like input to this node.
|
|
819
|
+
*
|
|
820
|
+
* @remarks
|
|
821
|
+
* Supports HTML strings, native template sources, native Nodes, and any JJ wrapper via {@link JJN}.
|
|
822
|
+
* Wrapper inputs are unwrapped and cloned before append.
|
|
823
|
+
*
|
|
824
|
+
* @param template - The template source to clone and append.
|
|
825
|
+
* @returns This instance for chaining.
|
|
826
|
+
* @throws {TypeError} If the template type is unsupported or a Promise was passed.
|
|
827
|
+
* @see {@link setTemplate} for replacing existing children with a cloned template.
|
|
828
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createRange | Document.createRange}
|
|
829
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement | HTMLTemplateElement}
|
|
830
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment | DocumentFragment}
|
|
831
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement | HTMLElement}
|
|
832
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode | Node.cloneNode}
|
|
833
|
+
*/
|
|
834
|
+
addTemplate(template) {
|
|
835
|
+
if (isStr(template)) {
|
|
836
|
+
return this.addChild(document.createRange().createContextualFragment(template));
|
|
837
|
+
}
|
|
838
|
+
if (isInstance(template, DocumentFragment) || isInstance(template, HTMLElement)) {
|
|
839
|
+
return this.addChild(
|
|
840
|
+
isInstance(template, HTMLTemplateElement) ? template.content.cloneNode(true) : template.cloneNode(true)
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
if (isInstance(template, Node)) {
|
|
844
|
+
return this.addChild(template.cloneNode(true));
|
|
845
|
+
}
|
|
846
|
+
if (isInstance(template, JJN)) {
|
|
847
|
+
return this.addTemplate(template.ref);
|
|
848
|
+
}
|
|
849
|
+
if (isInstance(template, Promise)) {
|
|
850
|
+
throw typeErr(
|
|
851
|
+
"template",
|
|
852
|
+
"not a Promise",
|
|
853
|
+
template,
|
|
854
|
+
"Templates must be provided synchronously. Did you forget to await?"
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
throw typeErr(
|
|
858
|
+
"template",
|
|
859
|
+
"a string, Node, DocumentFragment, HTMLElement, JJDF, or JJHE",
|
|
860
|
+
template,
|
|
861
|
+
"Pass an HTML string, a DOM template/fragment/element, or a JJ wrapper."
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Clones and sets template-like input as the only children of this node.
|
|
866
|
+
*
|
|
867
|
+
* @remarks
|
|
868
|
+
* Supports HTML strings, native template sources, native Nodes, and any JJ wrapper via {@link JJN}.
|
|
869
|
+
* Wrapper inputs are unwrapped and cloned before set.
|
|
870
|
+
*
|
|
871
|
+
* @example
|
|
872
|
+
* ```ts
|
|
873
|
+
* el.setTemplate('<p>New Content</p>')
|
|
874
|
+
* ```
|
|
875
|
+
* @param template - The template source to clone and set.
|
|
876
|
+
* @returns This instance for chaining.
|
|
877
|
+
* @see {@link addTemplate}
|
|
878
|
+
* @see {@link empty} for clearing children without adding a replacement template.
|
|
879
|
+
*/
|
|
880
|
+
setTemplate(template) {
|
|
881
|
+
return this.empty().addTemplate(template);
|
|
714
882
|
}
|
|
715
883
|
};
|
|
716
884
|
|
|
@@ -719,12 +887,15 @@ var JJDF = class _JJDF extends JJNx {
|
|
|
719
887
|
/**
|
|
720
888
|
* Creates a JJDF instance from a DocumentFragment reference.
|
|
721
889
|
*
|
|
890
|
+
* @remarks
|
|
891
|
+
* Use {@link JJDF.create} to create a new empty DocumentFragment.
|
|
892
|
+
* For other Node types, use {@link JJN.from} or the specific wrapper type.
|
|
893
|
+
*
|
|
722
894
|
* @example
|
|
723
895
|
* ```ts
|
|
724
896
|
* const frag = JJDF.from(myFrag)
|
|
725
897
|
* ```
|
|
726
898
|
*
|
|
727
|
-
*
|
|
728
899
|
* @param ref - The DocumentFragment instance.
|
|
729
900
|
* @returns A new JJDF instance.
|
|
730
901
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment | DocumentFragment}
|
|
@@ -735,6 +906,9 @@ var JJDF = class _JJDF extends JJNx {
|
|
|
735
906
|
/**
|
|
736
907
|
* Creates a new empty JJDF instance (wraps a new DocumentFragment).
|
|
737
908
|
*
|
|
909
|
+
* @remarks
|
|
910
|
+
* To wrap an existing DocumentFragment, use {@link JJDF.from}.
|
|
911
|
+
*
|
|
738
912
|
* @example
|
|
739
913
|
* ```ts
|
|
740
914
|
* const frag = JJDF.create()
|
|
@@ -751,10 +925,12 @@ var JJDF = class _JJDF extends JJNx {
|
|
|
751
925
|
*
|
|
752
926
|
* @param ref - The DocumentFragment instance to wrap.
|
|
753
927
|
* @throws {TypeError} If `ref` is not a DocumentFragment.
|
|
928
|
+
* @see {@link JJDF.from} to wrap an existing fragment.
|
|
929
|
+
* @see {@link JJDF.create} to create a new empty fragment.
|
|
754
930
|
*/
|
|
755
931
|
constructor(ref) {
|
|
756
|
-
if (!
|
|
757
|
-
throw typeErr("ref", "a DocumentFragment", ref);
|
|
932
|
+
if (!isInstance(ref, DocumentFragment)) {
|
|
933
|
+
throw typeErr("ref", "a DocumentFragment", ref, "Use JJDF.from(documentFragment) to create an instance.");
|
|
758
934
|
}
|
|
759
935
|
super(ref);
|
|
760
936
|
}
|
|
@@ -765,6 +941,10 @@ var JJSR = class _JJSR extends JJDF {
|
|
|
765
941
|
/**
|
|
766
942
|
* Creates a JJSR instance from a ShadowRoot reference.
|
|
767
943
|
*
|
|
944
|
+
* @remarks
|
|
945
|
+
* Typically created via `element.attachShadow({ mode: 'open' })` and passed here to be wrapped.
|
|
946
|
+
* Inherits from {@link JJDF} (DocumentFragment), so you can use all fragment methods like `find()`, `findAll()`, etc.
|
|
947
|
+
*
|
|
768
948
|
* @example
|
|
769
949
|
* ```ts
|
|
770
950
|
* const shadow = JJSR.from(element.shadowRoot)
|
|
@@ -772,6 +952,8 @@ var JJSR = class _JJSR extends JJDF {
|
|
|
772
952
|
*
|
|
773
953
|
* @param shadowRoot - The ShadowRoot instance.
|
|
774
954
|
* @returns A new JJSR instance.
|
|
955
|
+
* @see {@link JJHE.setShadow} for attaching and initializing a shadow root from a host element.
|
|
956
|
+
* @see {@link JJSR.constructor} for validation behavior.
|
|
775
957
|
*/
|
|
776
958
|
static from(shadowRoot) {
|
|
777
959
|
return new _JJSR(shadowRoot);
|
|
@@ -781,15 +963,38 @@ var JJSR = class _JJSR extends JJDF {
|
|
|
781
963
|
*
|
|
782
964
|
* @param shadowRoot - The ShadowRoot to wrap.
|
|
783
965
|
* @throws {TypeError} If `shadowRoot` is not a ShadowRoot.
|
|
966
|
+
* @see {@link JJSR.from} to wrap an existing ShadowRoot
|
|
784
967
|
*/
|
|
785
968
|
constructor(shadowRoot) {
|
|
786
|
-
if (!
|
|
787
|
-
throw
|
|
788
|
-
|
|
969
|
+
if (!isInstance(shadowRoot, ShadowRoot)) {
|
|
970
|
+
throw typeErr(
|
|
971
|
+
"shadowRoot",
|
|
972
|
+
"a ShadowRoot instance",
|
|
973
|
+
shadowRoot,
|
|
974
|
+
'Use JJHE.from(element).setShadow() or element.attachShadow({ mode: "open" }).'
|
|
789
975
|
);
|
|
790
976
|
}
|
|
791
977
|
super(shadowRoot);
|
|
792
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Initializes the ShadowRoot with template content and optional styles.
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```ts
|
|
984
|
+
* shadow.init('<p>Hello</p>', 'p { color: red; }')
|
|
985
|
+
* ```
|
|
986
|
+
*
|
|
987
|
+
* @param template - The template content to clone into the shadow root.
|
|
988
|
+
* @param styles - Optional styles to add to the shadow root.
|
|
989
|
+
* @returns This instance for chaining.
|
|
990
|
+
* @throws {Error} If the template or styles cannot be applied.
|
|
991
|
+
* @see {@link JJDF.addTemplate} for supported template inputs.
|
|
992
|
+
* @see {@link addStyle} for stylesheet handling.
|
|
993
|
+
*/
|
|
994
|
+
init(template, ...styles) {
|
|
995
|
+
this.addTemplate(template).addStyle(...styles);
|
|
996
|
+
return this;
|
|
997
|
+
}
|
|
793
998
|
/**
|
|
794
999
|
* Gets the inner HTML of the ShadowRoot.
|
|
795
1000
|
*
|
|
@@ -824,19 +1029,46 @@ var JJSR = class _JJSR extends JJDF {
|
|
|
824
1029
|
/**
|
|
825
1030
|
* Adds constructed stylesheets to the ShadowRoot.
|
|
826
1031
|
*
|
|
1032
|
+
* @remarks
|
|
1033
|
+
* Although this function accepts CSS strings, it converts them to CSSStyleSheet synchronously which has a performance penalty.
|
|
1034
|
+
* For better performance, create CSSStyleSheet instances in advance.
|
|
1035
|
+
*
|
|
827
1036
|
* @example
|
|
828
1037
|
* ```ts
|
|
829
1038
|
* const sheet = new CSSStyleSheet()
|
|
830
1039
|
* sheet.replaceSync('p { color: red; }')
|
|
831
|
-
* shadow.
|
|
1040
|
+
* shadow.addStyle(sheet)
|
|
832
1041
|
* ```
|
|
833
1042
|
*
|
|
834
|
-
* @param
|
|
1043
|
+
* @param styles - The stylesheets to add.
|
|
835
1044
|
* @returns This instance for chaining.
|
|
836
1045
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/adoptedStyleSheets | ShadowRoot.adoptedStyleSheets}
|
|
837
1046
|
*/
|
|
838
|
-
|
|
839
|
-
|
|
1047
|
+
addStyle(...styles) {
|
|
1048
|
+
if (styles.length) {
|
|
1049
|
+
const cssStyleSheets = [];
|
|
1050
|
+
try {
|
|
1051
|
+
for (const sheet of styles) {
|
|
1052
|
+
if (isInstance(sheet, CSSStyleSheet)) {
|
|
1053
|
+
cssStyleSheets.push(sheet);
|
|
1054
|
+
} else if (isStr(sheet)) {
|
|
1055
|
+
const cssSheet = new CSSStyleSheet();
|
|
1056
|
+
cssSheet.replaceSync(sheet);
|
|
1057
|
+
cssStyleSheets.push(cssSheet);
|
|
1058
|
+
} else {
|
|
1059
|
+
throw typeErr(
|
|
1060
|
+
"styleSheets",
|
|
1061
|
+
"CSSStyleSheet instances or CSS strings",
|
|
1062
|
+
sheet,
|
|
1063
|
+
"Pass a CSS string or a stylesheet created with `new CSSStyleSheet()`."
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} catch (cause) {
|
|
1068
|
+
throw new Error(`Failed to create CSSStyleSheet from provided styles.`, { cause });
|
|
1069
|
+
}
|
|
1070
|
+
this.ref.adoptedStyleSheets.push(...cssStyleSheets);
|
|
1071
|
+
}
|
|
840
1072
|
return this;
|
|
841
1073
|
}
|
|
842
1074
|
};
|
|
@@ -846,6 +1078,10 @@ var JJE = class _JJE extends JJNx {
|
|
|
846
1078
|
/**
|
|
847
1079
|
* Creates a JJE instance from an Element reference.
|
|
848
1080
|
*
|
|
1081
|
+
* @remarks
|
|
1082
|
+
* Use this factory method to wrap an existing Element. For creating new Elements,
|
|
1083
|
+
* use the specific wrapper type: {@link JJHE.create}, {@link JJSE.create}, or {@link JJME.create}.
|
|
1084
|
+
*
|
|
849
1085
|
* @example
|
|
850
1086
|
* ```ts
|
|
851
1087
|
* const el = JJE.from(document.querySelector('.my-class'))
|
|
@@ -853,6 +1089,9 @@ var JJE = class _JJE extends JJNx {
|
|
|
853
1089
|
*
|
|
854
1090
|
* @param ref - The Element instance.
|
|
855
1091
|
* @returns A new JJE instance.
|
|
1092
|
+
* @see {@link JJHE.create} for creating HTMLElements
|
|
1093
|
+
* @see {@link JJSE.create} for creating SVGElements
|
|
1094
|
+
* @see {@link JJME.create} for creating MathMLElements
|
|
856
1095
|
*/
|
|
857
1096
|
static from(ref) {
|
|
858
1097
|
return new _JJE(ref);
|
|
@@ -862,11 +1101,17 @@ var JJE = class _JJE extends JJNx {
|
|
|
862
1101
|
*
|
|
863
1102
|
* @param ref - The Element to wrap.
|
|
864
1103
|
* @throws {TypeError} If `ref` is not an Element.
|
|
1104
|
+
* @see {@link JJHE} for wrapping HTMLElements
|
|
1105
|
+
* @see {@link JJSE} for wrapping SVGElements
|
|
1106
|
+
* @see {@link JJME} for wrapping MathMLElements
|
|
865
1107
|
*/
|
|
866
1108
|
constructor(ref) {
|
|
867
|
-
if (!
|
|
868
|
-
throw
|
|
869
|
-
|
|
1109
|
+
if (!isInstance(ref, Element)) {
|
|
1110
|
+
throw typeErr(
|
|
1111
|
+
"ref",
|
|
1112
|
+
"an Element instance",
|
|
1113
|
+
ref,
|
|
1114
|
+
"Use JJHE.from(), JJSE.from(), or JJME.from() with the appropriate Element type."
|
|
870
1115
|
);
|
|
871
1116
|
}
|
|
872
1117
|
super(ref);
|
|
@@ -899,15 +1144,60 @@ var JJE = class _JJE extends JJNx {
|
|
|
899
1144
|
}
|
|
900
1145
|
return this.ref.hasAttribute(name);
|
|
901
1146
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1147
|
+
/**
|
|
1148
|
+
* Sets a single attribute on the Element.
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```ts
|
|
1152
|
+
* el.setAttr('id', 'my-id')
|
|
1153
|
+
* el.setAttr('x', 50) // Numbers are automatically converted
|
|
1154
|
+
* ```
|
|
1155
|
+
*
|
|
1156
|
+
* @throws {TypeError} If arguments are invalid types.
|
|
1157
|
+
* @see {@link setAttrs} for setting multiple attributes at once.
|
|
1158
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute | Element.setAttribute}
|
|
1159
|
+
*/
|
|
1160
|
+
setAttr(name, value) {
|
|
1161
|
+
if (!isStr(name)) {
|
|
1162
|
+
throw typeErr("name", "a string", name);
|
|
1163
|
+
}
|
|
1164
|
+
this.ref.setAttribute(name, value);
|
|
1165
|
+
return this;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Sets multiple attributes from an object, or no-ops for nullish input.
|
|
1169
|
+
*
|
|
1170
|
+
* @remarks
|
|
1171
|
+
* This helper is useful for optional attribute bags in builder APIs.
|
|
1172
|
+
* - `null` or `undefined`: does nothing and returns `this`
|
|
1173
|
+
* - plain object: sets each attribute on the element
|
|
1174
|
+
* - anything else: throws `TypeError`
|
|
1175
|
+
*
|
|
1176
|
+
* @example
|
|
1177
|
+
* ```ts
|
|
1178
|
+
* el.setAttrs({ id: 'app', role: 'main' })
|
|
1179
|
+
* el.setAttrs(null) // no-op
|
|
1180
|
+
* ```
|
|
1181
|
+
*
|
|
1182
|
+
* @param attributes - Attributes object or nullish to skip.
|
|
1183
|
+
* @returns This instance for chaining.
|
|
1184
|
+
* @throws {TypeError} If `attributes` is not nullish and not a plain object.
|
|
1185
|
+
* @see {@link setAttr} for setting a single attribute.
|
|
1186
|
+
*/
|
|
1187
|
+
setAttrs(attributes) {
|
|
1188
|
+
if (attributes == null) {
|
|
1189
|
+
return this;
|
|
1190
|
+
}
|
|
1191
|
+
if (!isObj(attributes)) {
|
|
1192
|
+
throw typeErr(
|
|
1193
|
+
"attributes",
|
|
1194
|
+
"a plain object",
|
|
1195
|
+
attributes,
|
|
1196
|
+
'Pass null/undefined or an object like { id: "app" }.'
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
1200
|
+
this.setAttr(name, value);
|
|
911
1201
|
}
|
|
912
1202
|
return this;
|
|
913
1203
|
}
|
|
@@ -934,6 +1224,73 @@ var JJE = class _JJE extends JJNx {
|
|
|
934
1224
|
}
|
|
935
1225
|
return this;
|
|
936
1226
|
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Conditionally sets or removes a boolean-style (presence-based) attribute, or
|
|
1229
|
+
* auto-toggles it when called without a `force` value.
|
|
1230
|
+
*
|
|
1231
|
+
* @remarks
|
|
1232
|
+
* HTML boolean attributes (e.g. `disabled`, `hidden`, `checked`, `readonly`, `required`)
|
|
1233
|
+
* are active whenever they are **present**, regardless of their value.
|
|
1234
|
+
*
|
|
1235
|
+
* This method has two modes:
|
|
1236
|
+
*
|
|
1237
|
+
* **Explicit mode** — pass any `force` value other than `undefined`:
|
|
1238
|
+
* - Truthy `force` → sets the attribute to `""` (presence = active).
|
|
1239
|
+
* - Falsy `force` (e.g. `false`, `null`, `0`, `""`) → removes the attribute. For `undefined`, see auto mode.
|
|
1240
|
+
*
|
|
1241
|
+
* **Auto mode** — omit `force` entirely (i.e. call with one argument) or pass `undefined` explicitly:
|
|
1242
|
+
* - Delegates to the native `Element.toggleAttribute()`: removes the attribute if
|
|
1243
|
+
* present, adds it (as `""`) if absent. Useful for click handlers and event
|
|
1244
|
+
* callbacks where you just want to flip the current state.
|
|
1245
|
+
*
|
|
1246
|
+
* > **Warning:** Passing `undefined` explicitly — `swAttr('disabled', undefined)` —
|
|
1247
|
+
* > is treated as **auto mode**, not as an explicit remove. If you need to
|
|
1248
|
+
* > unconditionally remove the attribute, use {@link rmAttr} instead.
|
|
1249
|
+
*
|
|
1250
|
+
* ARIA attributes are not HTML boolean attributes. Even boolean-like ARIA states
|
|
1251
|
+
* such as `aria-hidden`, `aria-disabled`, and `aria-readonly` require explicit
|
|
1252
|
+
* string values like `"true"` or `"false"`, so they should be managed with
|
|
1253
|
+
* {@link setAriaAttr} / {@link rmAriaAttr}, not with `swAttr()`.
|
|
1254
|
+
*
|
|
1255
|
+
* For other attributes that carry a meaningful string value (e.g.
|
|
1256
|
+
* `dir="rtl"`, `hidden="until-found"`), use {@link setAttr} and {@link rmAttr} directly.
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```ts
|
|
1260
|
+
* // Explicit mode — driven by a runtime condition (most common)
|
|
1261
|
+
* btn.swAttr('disabled', !isReady)
|
|
1262
|
+
* panel.swAttr('hidden', !isExpanded)
|
|
1263
|
+
* input.swAttr('readonly', isReadonly)
|
|
1264
|
+
*
|
|
1265
|
+
* // Not for ARIA — these need explicit string values
|
|
1266
|
+
* dialog.setAriaAttr('hidden', 'true')
|
|
1267
|
+
* button.setAriaAttr('disabled', 'false')
|
|
1268
|
+
*
|
|
1269
|
+
* // Auto mode — flip current state (no condition required)
|
|
1270
|
+
* btn.on('click', () => btn.swAttr('disabled'))
|
|
1271
|
+
*
|
|
1272
|
+
* // ⚠️ Watch out: passing undefined is NOT an explicit remove
|
|
1273
|
+
* swAttr('disabled', undefined) // → auto mode, NOT rmAttr!
|
|
1274
|
+
* ```
|
|
1275
|
+
*
|
|
1276
|
+
* @param name - The attribute name.
|
|
1277
|
+
* @param force - If omitted or `undefined`: auto-toggle. Truthy: add. Falsy (not `undefined`): remove.
|
|
1278
|
+
* @returns This instance for chaining.
|
|
1279
|
+
* @throws {TypeError} If `name` is not a string.
|
|
1280
|
+
* @see {@link setAttr} for setting a non-ARIA attribute with a specific string value.
|
|
1281
|
+
* @see {@link setAriaAttr} for ARIA attributes that require explicit values.
|
|
1282
|
+
* @see {@link rmAriaAttr} for unconditionally removing an ARIA attribute.
|
|
1283
|
+
* @see {@link rmAttr} for unconditionally removing an attribute.
|
|
1284
|
+
* @see {@link swClass} for the class-list equivalent.
|
|
1285
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/toggleAttribute | Element.toggleAttribute}
|
|
1286
|
+
*/
|
|
1287
|
+
swAttr(name, force) {
|
|
1288
|
+
if (!isStr(name)) {
|
|
1289
|
+
throw typeErr("name", "a string", name);
|
|
1290
|
+
}
|
|
1291
|
+
this.ref.toggleAttribute(name, force);
|
|
1292
|
+
return this;
|
|
1293
|
+
}
|
|
937
1294
|
/**
|
|
938
1295
|
* Gets the value of an ARIA attribute.
|
|
939
1296
|
*
|
|
@@ -942,7 +1299,7 @@ var JJE = class _JJE extends JJNx {
|
|
|
942
1299
|
*
|
|
943
1300
|
* @example
|
|
944
1301
|
* ```ts
|
|
945
|
-
* el.
|
|
1302
|
+
* el.getAriaAttr('label') // gets 'aria-label'
|
|
946
1303
|
* ```
|
|
947
1304
|
*
|
|
948
1305
|
* @param name - The ARIA attribute suffix (e.g., 'label' for 'aria-label').
|
|
@@ -950,7 +1307,7 @@ var JJE = class _JJE extends JJNx {
|
|
|
950
1307
|
* @throws {TypeError} If `name` is not a string.
|
|
951
1308
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes | ARIA Attributes}
|
|
952
1309
|
*/
|
|
953
|
-
|
|
1310
|
+
getAriaAttr(name) {
|
|
954
1311
|
if (!isStr(name)) {
|
|
955
1312
|
throw typeErr("name", "a string", name);
|
|
956
1313
|
}
|
|
@@ -962,22 +1319,78 @@ var JJE = class _JJE extends JJNx {
|
|
|
962
1319
|
* @param name - The ARIA attribute suffix.
|
|
963
1320
|
* @returns `true` if the attribute exists.
|
|
964
1321
|
* @throws {TypeError} If `name` is not a string.
|
|
1322
|
+
* @see {@link getAriaAttr} for reading ARIA values.
|
|
1323
|
+
* @see {@link setAriaAttr} for setting ARIA values.
|
|
1324
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes | ARIA Attributes}
|
|
965
1325
|
*/
|
|
966
|
-
|
|
1326
|
+
hasAriaAttr(name) {
|
|
967
1327
|
if (!isStr(name)) {
|
|
968
1328
|
throw typeErr("name", "a string", name);
|
|
969
1329
|
}
|
|
970
1330
|
return this.ref.hasAttribute(`aria-${name}`);
|
|
971
1331
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1332
|
+
/**
|
|
1333
|
+
* Sets a single ARIA attribute on the Element.
|
|
1334
|
+
*
|
|
1335
|
+
* @example
|
|
1336
|
+
* ```ts
|
|
1337
|
+
* el.setAriaAttr('hidden', 'true')
|
|
1338
|
+
* el.setAriaAttr('level', 2)
|
|
1339
|
+
* ```
|
|
1340
|
+
*
|
|
1341
|
+
* @param name - The ARIA attribute suffix.
|
|
1342
|
+
* @param value - The value to assign.
|
|
1343
|
+
* @returns This instance for chaining.
|
|
1344
|
+
* @throws {TypeError} If `name` is not a string.
|
|
1345
|
+
* @see {@link setAriaAttrs} for setting multiple ARIA attributes at once.
|
|
1346
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes | ARIA Attributes}
|
|
1347
|
+
*/
|
|
1348
|
+
setAriaAttr(name, value) {
|
|
1349
|
+
if (!isStr(name)) {
|
|
1350
|
+
throw typeErr("name", "a string", name);
|
|
1351
|
+
}
|
|
1352
|
+
this.ref.setAttribute(`aria-${name}`, value);
|
|
1353
|
+
return this;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Sets multiple ARIA attributes from an object, or no-ops for nullish input.
|
|
1357
|
+
*
|
|
1358
|
+
* @remarks
|
|
1359
|
+
* This helper is useful for optional ARIA attribute bags in builder APIs.
|
|
1360
|
+
* - `null` or `undefined`: does nothing and returns `this`
|
|
1361
|
+
* - plain object: sets each ARIA attribute on the element
|
|
1362
|
+
* - anything else: throws `TypeError`
|
|
1363
|
+
*
|
|
1364
|
+
* @example
|
|
1365
|
+
* ```ts
|
|
1366
|
+
* el.setAriaAttrs({ label: 'Close', hidden: 'false' })
|
|
1367
|
+
* el.setAriaAttrs(null) // no-op
|
|
1368
|
+
* ```
|
|
1369
|
+
*
|
|
1370
|
+
* @param attributes - ARIA attributes object or nullish to skip.
|
|
1371
|
+
* @returns This instance for chaining.
|
|
1372
|
+
* @throws {TypeError} If `attributes` is not nullish and not a plain object.
|
|
1373
|
+
* @see {@link setAriaAttr} for setting a single ARIA attribute.
|
|
1374
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes | ARIA Attributes}
|
|
1375
|
+
*/
|
|
1376
|
+
setAriaAttrs(attributes) {
|
|
1377
|
+
if (attributes == null) {
|
|
1378
|
+
return this;
|
|
1379
|
+
}
|
|
1380
|
+
if (!isObj(attributes)) {
|
|
1381
|
+
throw typeErr(
|
|
1382
|
+
"attributes",
|
|
1383
|
+
"a plain object",
|
|
1384
|
+
attributes,
|
|
1385
|
+
'Pass null/undefined or an object like { hidden: "true" }.'
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
try {
|
|
1389
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
1390
|
+
this.setAriaAttr(name, value);
|
|
978
1391
|
}
|
|
979
|
-
}
|
|
980
|
-
throw
|
|
1392
|
+
} catch (cause) {
|
|
1393
|
+
throw new Error(`Failed to set some ARIA attributes from object: ${JSON.stringify(attributes)}.`, { cause });
|
|
981
1394
|
}
|
|
982
1395
|
return this;
|
|
983
1396
|
}
|
|
@@ -986,20 +1399,27 @@ var JJE = class _JJE extends JJNx {
|
|
|
986
1399
|
*
|
|
987
1400
|
* @example
|
|
988
1401
|
* ```ts
|
|
989
|
-
* el.
|
|
990
|
-
* el.
|
|
1402
|
+
* el.rmAriaAttr('hidden') // Remove single
|
|
1403
|
+
* el.rmAriaAttr('label', 'hidden') // Remove multiple
|
|
991
1404
|
* ```
|
|
992
1405
|
*
|
|
993
1406
|
* @param names - The ARIA attribute suffix(es) to remove.
|
|
994
1407
|
* @returns This instance for chaining.
|
|
995
1408
|
* @throws {TypeError} If any name is not a string.
|
|
1409
|
+
* @see {@link setAriaAttr} for setting a single ARIA attribute.
|
|
1410
|
+
* @see {@link setAriaAttrs} for setting multiple ARIA attributes.
|
|
1411
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute | Element.removeAttribute}
|
|
996
1412
|
*/
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1413
|
+
rmAriaAttr(...names) {
|
|
1414
|
+
try {
|
|
1415
|
+
for (const name of names) {
|
|
1416
|
+
if (!isStr(name)) {
|
|
1417
|
+
throw typeErr("name", "a string", name);
|
|
1418
|
+
}
|
|
1419
|
+
this.ref.removeAttribute(`aria-${name}`);
|
|
1001
1420
|
}
|
|
1002
|
-
|
|
1421
|
+
} catch (cause) {
|
|
1422
|
+
throw new Error(`Failed to remove some ARIA attributes: ${JSON.stringify(names)}.`, { cause });
|
|
1003
1423
|
}
|
|
1004
1424
|
return this;
|
|
1005
1425
|
}
|
|
@@ -1012,16 +1432,76 @@ var JJE = class _JJE extends JJNx {
|
|
|
1012
1432
|
getClass() {
|
|
1013
1433
|
return this.getAttr("class");
|
|
1014
1434
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1435
|
+
/**
|
|
1436
|
+
* Sets the class attribute.
|
|
1437
|
+
*
|
|
1438
|
+
* @remarks
|
|
1439
|
+
* To remove all classes, pass an empty string: `setClass('')`
|
|
1440
|
+
*
|
|
1441
|
+
* @example
|
|
1442
|
+
* ```ts
|
|
1443
|
+
* el.setClass('btn btn-primary')
|
|
1444
|
+
* el.setClass('')
|
|
1445
|
+
* ```
|
|
1446
|
+
*
|
|
1447
|
+
* @param className - The full class attribute value.
|
|
1448
|
+
* @returns This instance for chaining.
|
|
1449
|
+
* @throws {TypeError} If `className` is not a string.
|
|
1450
|
+
* @see {@link setClasses} for conditional class maps.
|
|
1451
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/className | Element.className}
|
|
1452
|
+
*/
|
|
1453
|
+
setClass(className) {
|
|
1454
|
+
if (!isStr(className)) {
|
|
1455
|
+
throw typeErr("className", "a string", className);
|
|
1018
1456
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1457
|
+
return this.setAttr("class", className);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Conditionally adds or removes classes from an object map, or no-ops for nullish input.
|
|
1461
|
+
*
|
|
1462
|
+
* @remarks
|
|
1463
|
+
* - `null` or `undefined`: does nothing and returns `this`
|
|
1464
|
+
* - plain object: truthy values add a class, falsy values remove it
|
|
1465
|
+
* - anything else: throws `TypeError`
|
|
1466
|
+
*
|
|
1467
|
+
* @example
|
|
1468
|
+
* ```ts
|
|
1469
|
+
* el.setClasses({
|
|
1470
|
+
* active: true,
|
|
1471
|
+
* disabled: false,
|
|
1472
|
+
* highlight: isHighlighted,
|
|
1473
|
+
* })
|
|
1474
|
+
* el.setClasses(null) // no-op
|
|
1475
|
+
* ```
|
|
1476
|
+
*
|
|
1477
|
+
* @param classMap - Conditional class map or nullish to skip.
|
|
1478
|
+
* @returns This instance for chaining.
|
|
1479
|
+
* @throws {TypeError} If `classMap` is not nullish and not a plain object.
|
|
1480
|
+
* @see {@link setClass} for replacing the full class attribute.
|
|
1481
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/classList | Element.classList}
|
|
1482
|
+
*/
|
|
1483
|
+
setClasses(classMap) {
|
|
1484
|
+
if (classMap == null) {
|
|
1485
|
+
return this;
|
|
1486
|
+
}
|
|
1487
|
+
if (!isObj(classMap)) {
|
|
1488
|
+
throw typeErr(
|
|
1489
|
+
"classMap",
|
|
1490
|
+
"a plain object",
|
|
1491
|
+
classMap,
|
|
1492
|
+
"Pass null/undefined or an object like { active: true }."
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
try {
|
|
1496
|
+
for (const [className, condition] of Object.entries(classMap)) {
|
|
1497
|
+
if (condition) {
|
|
1498
|
+
this.addClass(className);
|
|
1499
|
+
} else {
|
|
1500
|
+
this.rmClass(className);
|
|
1501
|
+
}
|
|
1024
1502
|
}
|
|
1503
|
+
} catch (cause) {
|
|
1504
|
+
throw new Error(`Failed to set some classes from object: ${JSON.stringify(classMap)}.`, { cause });
|
|
1025
1505
|
}
|
|
1026
1506
|
return this;
|
|
1027
1507
|
}
|
|
@@ -1048,6 +1528,28 @@ var JJE = class _JJE extends JJNx {
|
|
|
1048
1528
|
this.ref.classList.add(...classNames);
|
|
1049
1529
|
return this;
|
|
1050
1530
|
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Adds multiple classes from an array of class names.
|
|
1533
|
+
*
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```ts
|
|
1536
|
+
* el.addClasses(['btn', 'btn-primary'])
|
|
1537
|
+
* ```
|
|
1538
|
+
*
|
|
1539
|
+
* @param classNames - The classes to add.
|
|
1540
|
+
* @returns This instance for chaining.
|
|
1541
|
+
* @throws {TypeError} If `classNames` is not an array.
|
|
1542
|
+
* @throws {TypeError} If any class name in `classNames` is not a string.
|
|
1543
|
+
* @see {@link addClass} for adding one or more classes via rest arguments.
|
|
1544
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/classList | Element.classList}
|
|
1545
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/add | DOMTokenList.add}
|
|
1546
|
+
*/
|
|
1547
|
+
addClasses(classNames) {
|
|
1548
|
+
if (!Array.isArray(classNames)) {
|
|
1549
|
+
throw typeErr("classNames", "an array of strings", classNames);
|
|
1550
|
+
}
|
|
1551
|
+
return this.addClass(...classNames);
|
|
1552
|
+
}
|
|
1051
1553
|
/**
|
|
1052
1554
|
* Removes one or more classes from the Element.
|
|
1053
1555
|
*
|
|
@@ -1072,6 +1574,28 @@ var JJE = class _JJE extends JJNx {
|
|
|
1072
1574
|
this.ref.classList.remove(...classNames);
|
|
1073
1575
|
return this;
|
|
1074
1576
|
}
|
|
1577
|
+
/**
|
|
1578
|
+
* Removes multiple classes from an array of class names.
|
|
1579
|
+
*
|
|
1580
|
+
* @example
|
|
1581
|
+
* ```ts
|
|
1582
|
+
* el.rmClasses(['btn', 'btn-primary'])
|
|
1583
|
+
* ```
|
|
1584
|
+
*
|
|
1585
|
+
* @param classNames - The classes to remove.
|
|
1586
|
+
* @returns This instance for chaining.
|
|
1587
|
+
* @throws {TypeError} If `classNames` is not an array.
|
|
1588
|
+
* @throws {TypeError} If any class name in `classNames` is not a string.
|
|
1589
|
+
* @see {@link rmClass} for removing one or more classes via rest arguments.
|
|
1590
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/classList | Element.classList}
|
|
1591
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/remove | DOMTokenList.remove}
|
|
1592
|
+
*/
|
|
1593
|
+
rmClasses(classNames) {
|
|
1594
|
+
if (!Array.isArray(classNames)) {
|
|
1595
|
+
throw typeErr("classNames", "an array of strings", classNames);
|
|
1596
|
+
}
|
|
1597
|
+
return this.rmClass(...classNames);
|
|
1598
|
+
}
|
|
1075
1599
|
/**
|
|
1076
1600
|
* Checks if the Element has a specific class.
|
|
1077
1601
|
*
|
|
@@ -1088,39 +1612,56 @@ var JJE = class _JJE extends JJNx {
|
|
|
1088
1612
|
return this.ref.classList.contains(className);
|
|
1089
1613
|
}
|
|
1090
1614
|
/**
|
|
1091
|
-
*
|
|
1615
|
+
* Conditionally adds or removes a single class, or auto-toggles it when called
|
|
1616
|
+
* without a `force` value.
|
|
1617
|
+
*
|
|
1618
|
+
* @remarks
|
|
1619
|
+
* This method has two modes, mirroring the behavior of {@link swAttr}:
|
|
1620
|
+
*
|
|
1621
|
+
* **Explicit mode** — pass any `force` value other than `undefined`:
|
|
1622
|
+
* - Truthy `force` → adds the class.
|
|
1623
|
+
* - Falsy `force` (e.g. `false`, `null`, `0`, `""`) → removes the class.
|
|
1624
|
+
*
|
|
1625
|
+
* **Auto mode** — omit `force` entirely (i.e. call with one argument):
|
|
1626
|
+
* - Delegates to the native `DOMTokenList.toggle()`: removes the class if present,
|
|
1627
|
+
* adds it if absent. Useful for click handlers and event callbacks where you just
|
|
1628
|
+
* want to flip the current state.
|
|
1629
|
+
*
|
|
1630
|
+
* > **Warning:** Passing `undefined` explicitly — `swClass('foo', undefined)` —
|
|
1631
|
+
* > is treated as **auto mode**, not as an explicit remove. If you need to
|
|
1632
|
+
* > unconditionally remove a class, use {@link rmClass} instead.
|
|
1633
|
+
*
|
|
1634
|
+
* To toggle multiple classes from a condition map in a single call, use {@link setClasses}.
|
|
1092
1635
|
*
|
|
1093
|
-
* @
|
|
1636
|
+
* @example
|
|
1637
|
+
* ```ts
|
|
1638
|
+
* // Explicit mode — driven by a runtime condition (most common)
|
|
1639
|
+
* el.swClass('is-expanded', isExpanded)
|
|
1640
|
+
* el.swClass('is-loading', isPending)
|
|
1641
|
+
*
|
|
1642
|
+
* // Auto mode — flip current state (no condition required)
|
|
1643
|
+
* btn.on('click', () => btn.swClass('is-active'))
|
|
1644
|
+
*
|
|
1645
|
+
* // ⚠️ Watch out: passing undefined is NOT an explicit remove
|
|
1646
|
+
* swClass('foo', undefined) // → auto mode, NOT rmClass!
|
|
1647
|
+
* ```
|
|
1648
|
+
*
|
|
1649
|
+
* @param className - The class to add or remove.
|
|
1650
|
+
* @param force - If omitted or `undefined`: auto-toggle. Truthy: add. Falsy (not `undefined`): remove.
|
|
1094
1651
|
* @returns This instance for chaining.
|
|
1095
1652
|
* @throws {TypeError} If `className` is not a string.
|
|
1653
|
+
* @see {@link setClasses} for toggling multiple classes at once via a condition map.
|
|
1654
|
+
* @see {@link addClass} for unconditionally adding a class.
|
|
1655
|
+
* @see {@link rmClass} for unconditionally removing a class.
|
|
1656
|
+
* @see {@link swAttr} for the attribute equivalent.
|
|
1096
1657
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/classList | Element.classList}
|
|
1097
1658
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle | DOMTokenList.toggle}
|
|
1098
1659
|
*/
|
|
1099
|
-
|
|
1660
|
+
swClass(className, force) {
|
|
1100
1661
|
if (!isStr(className)) {
|
|
1101
1662
|
throw typeErr("className", "a string", className);
|
|
1102
1663
|
}
|
|
1103
|
-
this.ref.classList.toggle(className);
|
|
1104
|
-
return this;
|
|
1105
|
-
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Replaces a class with another one
|
|
1108
|
-
*
|
|
1109
|
-
* @remarks
|
|
1110
|
-
* If the `oldClassName` doesn't exist, the `newClassName` isn't added
|
|
1111
|
-
*
|
|
1112
|
-
* @param oldClassName - The class name to remove
|
|
1113
|
-
* @param newClassName - The class name to add
|
|
1114
|
-
* @throws {TypeError} If either className is not a string.
|
|
1115
|
-
*/
|
|
1116
|
-
replaceClass(oldClassName, newClassName) {
|
|
1117
|
-
if (!isStr(oldClassName)) {
|
|
1118
|
-
throw typeErr("oldClassName", "a string", oldClassName);
|
|
1119
|
-
}
|
|
1120
|
-
if (!isStr(newClassName)) {
|
|
1121
|
-
throw typeErr("newClassName", "a string", newClassName);
|
|
1122
|
-
}
|
|
1123
|
-
this.ref.classList.replace(oldClassName, newClassName);
|
|
1664
|
+
this.ref.classList.toggle(className, force);
|
|
1124
1665
|
return this;
|
|
1125
1666
|
}
|
|
1126
1667
|
/**
|
|
@@ -1148,7 +1689,7 @@ var JJE = class _JJE extends JJNx {
|
|
|
1148
1689
|
throw typeErr("selector", "a string", selector);
|
|
1149
1690
|
}
|
|
1150
1691
|
const match = this.ref.closest(selector);
|
|
1151
|
-
return match ?
|
|
1692
|
+
return match ? _JJE.wrap(match) : null;
|
|
1152
1693
|
}
|
|
1153
1694
|
/**
|
|
1154
1695
|
|
|
@@ -1165,6 +1706,8 @@ var JJE = class _JJE extends JJNx {
|
|
|
1165
1706
|
* Shows the Element by removing the `hidden` and `aria-hidden` attributes.
|
|
1166
1707
|
*
|
|
1167
1708
|
* @returns This instance for chaining.
|
|
1709
|
+
* @see {@link hide} for the inverse operation.
|
|
1710
|
+
* @see {@link rmAttr} for generic attribute removal.
|
|
1168
1711
|
*/
|
|
1169
1712
|
show() {
|
|
1170
1713
|
return this.rmAttr("hidden", "aria-hidden");
|
|
@@ -1183,6 +1726,8 @@ var JJE = class _JJE extends JJNx {
|
|
|
1183
1726
|
* Enables the Element by removing the `disabled` and `aria-disabled` attributes.
|
|
1184
1727
|
*
|
|
1185
1728
|
* @returns This instance for chaining.
|
|
1729
|
+
* @see {@link disable} for the inverse operation.
|
|
1730
|
+
* @see {@link rmAttr} for generic attribute removal.
|
|
1186
1731
|
*/
|
|
1187
1732
|
enable() {
|
|
1188
1733
|
return this.rmAttr("disabled", "aria-disabled");
|
|
@@ -1221,74 +1766,220 @@ var JJE = class _JJE extends JJNx {
|
|
|
1221
1766
|
return this;
|
|
1222
1767
|
}
|
|
1223
1768
|
/**
|
|
1224
|
-
* Attaches a Shadow DOM to the Element
|
|
1769
|
+
* Attaches a Shadow DOM to the Element if it's not already attached.
|
|
1225
1770
|
*
|
|
1226
1771
|
* @remarks
|
|
1227
|
-
*
|
|
1228
|
-
* **Note:** You can't attach a shadow root to every type of element. There are some that can't have a
|
|
1772
|
+
* You can't attach a shadow root to every type of element. There are some that can't have a
|
|
1229
1773
|
* shadow DOM for security reasons (for example `<a>`).
|
|
1230
1774
|
*
|
|
1231
1775
|
* @param mode - The encapsulation mode ('open' or 'closed'). Defaults to 'open'.
|
|
1232
|
-
* @param config - Optional configuration object containing `template` (HTML string) and `styles` (array of CSSStyleSheet).
|
|
1233
1776
|
* @returns This instance for chaining.
|
|
1777
|
+
* @throws {DOMException} If the element cannot have a shadow root. See {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow | Element.attachShadow} for more details.
|
|
1778
|
+
* @throws {Error} If the shadow DOM is already attached but with another mode.
|
|
1234
1779
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow | Element.attachShadow}
|
|
1235
1780
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/adoptedStyleSheets | ShadowRoot.adoptedStyleSheets}
|
|
1236
1781
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets | Document.adoptedStyleSheets}
|
|
1237
1782
|
*/
|
|
1238
|
-
|
|
1239
|
-
const shadowRoot = this.ref
|
|
1240
|
-
if (
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
shadowRoot.appendChild(template);
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
if (isArr(styles) && styles.length) {
|
|
1250
|
-
shadowRoot.adoptedStyleSheets.push(...styles);
|
|
1251
|
-
}
|
|
1783
|
+
setShadow(mode = "open") {
|
|
1784
|
+
const { shadowRoot } = this.ref;
|
|
1785
|
+
if (!shadowRoot) {
|
|
1786
|
+
this.ref.attachShadow({ mode });
|
|
1787
|
+
} else if (shadowRoot.mode !== mode) {
|
|
1788
|
+
throw new Error(
|
|
1789
|
+
`Element already has a shadow root with mode "${shadowRoot.mode}". Cannot attach another with mode "${mode}".`
|
|
1790
|
+
);
|
|
1252
1791
|
}
|
|
1253
1792
|
return this;
|
|
1254
1793
|
}
|
|
1255
1794
|
/**
|
|
1256
|
-
*
|
|
1795
|
+
* Initializes an already-attached Shadow DOM with template content and optional styles.
|
|
1257
1796
|
*
|
|
1258
|
-
* @
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1797
|
+
* @remarks
|
|
1798
|
+
* This method expects the shadow root to already exist.
|
|
1799
|
+
* You would typically call `setShadow()` in the custom component constructor and then
|
|
1800
|
+
* perform initialization in `connectedCallback()`.
|
|
1801
|
+
*
|
|
1802
|
+
* @example
|
|
1803
|
+
* ```ts
|
|
1804
|
+
* const host = JJHE.from(this).setShadow('open')
|
|
1805
|
+
* host.initShadow(await templatePromise, await stylePromise)
|
|
1806
|
+
* ```
|
|
1807
|
+
*
|
|
1808
|
+
* @param template - The template content to clone into the shadow root.
|
|
1809
|
+
* @param styles - Optional styles to add to the shadow root.
|
|
1810
|
+
* @returns This instance for chaining.
|
|
1811
|
+
* @throws {ReferenceError} If the element does not have a shadow root yet.
|
|
1812
|
+
* @throws {Error} If it fails to initialize the shadow DOM with the provided template or styles.
|
|
1813
|
+
* @see {@link setShadow} for attaching the shadow root first.
|
|
1814
|
+
* @see {@link JJSR.init} for the underlying shadow-root initialization helper.
|
|
1815
|
+
*/
|
|
1816
|
+
initShadow(template, ...styles) {
|
|
1817
|
+
try {
|
|
1818
|
+
this.getShadow(true).init(template, ...styles);
|
|
1819
|
+
} catch (cause) {
|
|
1820
|
+
throw new Error(`Failed to initialize shadow DOM`, { cause });
|
|
1821
|
+
}
|
|
1822
|
+
return this;
|
|
1823
|
+
}
|
|
1824
|
+
getShadow(required = false) {
|
|
1825
|
+
const shadowRoot = this.ref.shadowRoot;
|
|
1826
|
+
if (shadowRoot) {
|
|
1827
|
+
return new JJSR(shadowRoot);
|
|
1828
|
+
}
|
|
1829
|
+
if (required) {
|
|
1830
|
+
throw new ReferenceError("No shadow root found on this element. Did you forget to call setShadow() first?");
|
|
1831
|
+
}
|
|
1832
|
+
return null;
|
|
1262
1833
|
}
|
|
1263
1834
|
};
|
|
1264
1835
|
|
|
1265
1836
|
// src/wrappers/JJEx.ts
|
|
1266
1837
|
var JJEx = class extends JJE {
|
|
1267
1838
|
/**
|
|
1268
|
-
* Gets a
|
|
1839
|
+
* Gets the value of a single inline style property.
|
|
1840
|
+
*
|
|
1841
|
+
* @example
|
|
1842
|
+
* ```ts
|
|
1843
|
+
* const display = el.getStyle('display')
|
|
1844
|
+
* ```
|
|
1845
|
+
*
|
|
1846
|
+
* @param name - The CSS property name (kebab-case recommended).
|
|
1847
|
+
* @returns The property value, or an empty string when it is not set.
|
|
1848
|
+
* @throws {TypeError} If `name` is not a string.
|
|
1849
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style | HTMLElement.style}
|
|
1850
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/getPropertyValue | CSSStyleDeclaration.getPropertyValue}
|
|
1851
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/style | SVG style attribute}
|
|
1852
|
+
*/
|
|
1853
|
+
getStyle(name) {
|
|
1854
|
+
if (!isStr(name)) {
|
|
1855
|
+
throw typeErr("name", "a string", name);
|
|
1856
|
+
}
|
|
1857
|
+
return this.ref.style.getPropertyValue(name);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Sets a single inline style property.
|
|
1861
|
+
*
|
|
1862
|
+
* @example
|
|
1863
|
+
* ```ts
|
|
1864
|
+
* el.setStyle('display', 'grid')
|
|
1865
|
+
* el.setStyle('opacity', 0.8)
|
|
1866
|
+
* ```
|
|
1867
|
+
*
|
|
1868
|
+
* @param name - The CSS property name (kebab-case recommended).
|
|
1869
|
+
* @param value - The CSS property value.
|
|
1870
|
+
* @returns This instance for chaining.
|
|
1871
|
+
* @throws {TypeError} If `name` is not a string.
|
|
1872
|
+
* @see {@link setStyles} for setting/removing specific style properties.
|
|
1873
|
+
* @see {@link rmStyle} for removing style properties.
|
|
1874
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style | HTMLElement.style}
|
|
1875
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty | CSSStyleDeclaration.setProperty}
|
|
1876
|
+
*/
|
|
1877
|
+
setStyle(name, value) {
|
|
1878
|
+
if (!isStr(name)) {
|
|
1879
|
+
throw typeErr("name", "a string", name);
|
|
1880
|
+
}
|
|
1881
|
+
this.ref.style.setProperty(name, value);
|
|
1882
|
+
return this;
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Removes one or more inline style properties.
|
|
1886
|
+
*
|
|
1887
|
+
* @example
|
|
1888
|
+
* ```ts
|
|
1889
|
+
* el.rmStyle('display')
|
|
1890
|
+
* el.rmStyle('display', 'gap')
|
|
1891
|
+
* ```
|
|
1892
|
+
*
|
|
1893
|
+
* @param names - The CSS property names to remove.
|
|
1894
|
+
* @returns This instance for chaining.
|
|
1895
|
+
* @throws {TypeError} If any property name is not a string.
|
|
1896
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/removeProperty | CSSStyleDeclaration.removeProperty}
|
|
1897
|
+
*/
|
|
1898
|
+
rmStyle(...names) {
|
|
1899
|
+
for (const name of names) {
|
|
1900
|
+
if (!isStr(name)) {
|
|
1901
|
+
throw typeErr("name", "a string", name);
|
|
1902
|
+
}
|
|
1903
|
+
this.ref.style.removeProperty(name);
|
|
1904
|
+
}
|
|
1905
|
+
return this;
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Sets or removes multiple inline style properties from an object map, or no-ops for nullish input.
|
|
1909
|
+
*
|
|
1910
|
+
* @remarks
|
|
1911
|
+
* - `null` or `undefined`: does nothing and returns `this`
|
|
1912
|
+
* - plain object: non-nullish values set properties, `null`/`undefined`/`false` remove properties
|
|
1913
|
+
* - anything else: throws `TypeError`
|
|
1914
|
+
*
|
|
1915
|
+
* @example
|
|
1916
|
+
* ```ts
|
|
1917
|
+
* el.setStyles({
|
|
1918
|
+
* display: 'grid',
|
|
1919
|
+
* gap: '1rem',
|
|
1920
|
+
* opacity: 0,
|
|
1921
|
+
* color: null,
|
|
1922
|
+
* })
|
|
1923
|
+
* el.setStyles(null) // no-op
|
|
1924
|
+
* ```
|
|
1925
|
+
*
|
|
1926
|
+
* @param styleMap - Style property map or nullish to skip.
|
|
1927
|
+
* @returns This instance for chaining.
|
|
1928
|
+
* @throws {TypeError} If `styleMap` is not nullish and not a plain object.
|
|
1929
|
+
* @see {@link setStyle} for setting a single style property.
|
|
1930
|
+
*/
|
|
1931
|
+
setStyles(styleMap) {
|
|
1932
|
+
if (styleMap == null) {
|
|
1933
|
+
return this;
|
|
1934
|
+
}
|
|
1935
|
+
if (!isObj(styleMap)) {
|
|
1936
|
+
throw typeErr(
|
|
1937
|
+
"styleMap",
|
|
1938
|
+
"a plain object",
|
|
1939
|
+
styleMap,
|
|
1940
|
+
'Pass null/undefined or an object like { display: "grid" }.'
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
try {
|
|
1944
|
+
for (const [name, value] of Object.entries(styleMap)) {
|
|
1945
|
+
if (value == null || value === false) {
|
|
1946
|
+
this.rmStyle(name);
|
|
1947
|
+
} else {
|
|
1948
|
+
this.setStyle(name, value);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
} catch (cause) {
|
|
1952
|
+
throw new Error(`Failed to set some styles from object: ${JSON.stringify(styleMap)}.`, { cause });
|
|
1953
|
+
}
|
|
1954
|
+
return this;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Gets a data attribute from the element.
|
|
1269
1958
|
*
|
|
1270
1959
|
* @example
|
|
1271
1960
|
* ```ts
|
|
1272
|
-
* const value = el.
|
|
1961
|
+
* const value = el.getDataAttr('myKey')
|
|
1273
1962
|
* ```
|
|
1274
1963
|
*
|
|
1275
1964
|
* @param name - The data attribute name (in camelCase).
|
|
1276
1965
|
* @returns The value of the attribute, or undefined if not set.
|
|
1277
1966
|
* @throws {TypeError} If `name` is not a string.
|
|
1278
1967
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
1968
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
1969
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
1279
1970
|
*/
|
|
1280
|
-
|
|
1971
|
+
getDataAttr(name) {
|
|
1281
1972
|
if (!isStr(name)) {
|
|
1282
1973
|
throw typeErr("name", "a string", name);
|
|
1283
1974
|
}
|
|
1284
1975
|
return this.ref.dataset[name];
|
|
1285
1976
|
}
|
|
1286
1977
|
/**
|
|
1287
|
-
* Checks if a data attribute exists on the
|
|
1978
|
+
* Checks if a data attribute exists on the element.
|
|
1288
1979
|
*
|
|
1289
1980
|
* @example
|
|
1290
1981
|
* ```ts
|
|
1291
|
-
* if (el.
|
|
1982
|
+
* if (el.hasDataAttr('myKey')) {
|
|
1292
1983
|
* // ...
|
|
1293
1984
|
* }
|
|
1294
1985
|
* ```
|
|
@@ -1297,40 +1988,101 @@ var JJEx = class extends JJE {
|
|
|
1297
1988
|
* @returns True if the attribute exists, false otherwise.
|
|
1298
1989
|
* @throws {TypeError} If `name` is not a string.
|
|
1299
1990
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
1991
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
1992
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
1300
1993
|
*/
|
|
1301
|
-
|
|
1994
|
+
hasDataAttr(name) {
|
|
1302
1995
|
if (!isStr(name)) {
|
|
1303
1996
|
throw typeErr("name", "a string", name);
|
|
1304
1997
|
}
|
|
1305
1998
|
return hasProp(this.ref.dataset, name);
|
|
1306
1999
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
2000
|
+
/**
|
|
2001
|
+
* Sets a single data attribute on the element.
|
|
2002
|
+
*
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```ts
|
|
2005
|
+
* el.setDataAttr('myKey', 'myValue')
|
|
2006
|
+
* el.setDataAttr('count', 42 as unknown as string)
|
|
2007
|
+
* ```
|
|
2008
|
+
*
|
|
2009
|
+
* @param name - The data attribute name (in camelCase).
|
|
2010
|
+
* @param value - The value to assign.
|
|
2011
|
+
* @returns This instance for chaining.
|
|
2012
|
+
* @throws {TypeError} If `name` is not a string.
|
|
2013
|
+
* @see {@link setDataAttrs} for setting multiple data attributes at once.
|
|
2014
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
2015
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
2016
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
2017
|
+
*/
|
|
2018
|
+
setDataAttr(name, value) {
|
|
2019
|
+
if (!isStr(name)) {
|
|
2020
|
+
throw typeErr("name", "a string", name);
|
|
2021
|
+
}
|
|
2022
|
+
this.ref.dataset[name] = value;
|
|
2023
|
+
return this;
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Sets multiple data attributes from an object, or no-ops for nullish input.
|
|
2027
|
+
*
|
|
2028
|
+
* @remarks
|
|
2029
|
+
* This helper is useful for optional dataset bags in builder APIs.
|
|
2030
|
+
* - `null` or `undefined`: does nothing and returns `this`
|
|
2031
|
+
* - plain object: sets each data attribute on the element
|
|
2032
|
+
* - anything else: throws `TypeError`
|
|
2033
|
+
*
|
|
2034
|
+
* @example
|
|
2035
|
+
* ```ts
|
|
2036
|
+
* el.setDataAttrs({ myKey: 'myValue', otherKey: 'otherValue' })
|
|
2037
|
+
* el.setDataAttrs(null) // no-op
|
|
2038
|
+
* ```
|
|
2039
|
+
*
|
|
2040
|
+
* @param attributes - Data attributes object or nullish to skip.
|
|
2041
|
+
* @returns This instance for chaining.
|
|
2042
|
+
* @throws {TypeError} If `attributes` is not nullish and not a plain object.
|
|
2043
|
+
* @see {@link setDataAttr} for setting a single data attribute.
|
|
2044
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
2045
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
2046
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
2047
|
+
*/
|
|
2048
|
+
setDataAttrs(attributes) {
|
|
2049
|
+
if (attributes == null) {
|
|
2050
|
+
return this;
|
|
2051
|
+
}
|
|
2052
|
+
if (!isObj(attributes)) {
|
|
2053
|
+
throw typeErr(
|
|
2054
|
+
"attributes",
|
|
2055
|
+
"a plain object",
|
|
2056
|
+
attributes,
|
|
2057
|
+
'Pass null/undefined or an object like { userId: "42" }.'
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
try {
|
|
2061
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
2062
|
+
this.setDataAttr(name, value);
|
|
1313
2063
|
}
|
|
1314
|
-
}
|
|
1315
|
-
throw
|
|
2064
|
+
} catch (cause) {
|
|
2065
|
+
throw new Error(`Failed to set some data attributes from object: ${JSON.stringify(attributes)}.`, { cause });
|
|
1316
2066
|
}
|
|
1317
2067
|
return this;
|
|
1318
2068
|
}
|
|
1319
2069
|
/**
|
|
1320
|
-
* Removes one or more data attributes from the
|
|
2070
|
+
* Removes one or more data attributes from the element.
|
|
1321
2071
|
*
|
|
1322
2072
|
* @example
|
|
1323
2073
|
* ```ts
|
|
1324
|
-
* el.
|
|
1325
|
-
* el.
|
|
2074
|
+
* el.rmDataAttr('myKey') // Remove single
|
|
2075
|
+
* el.rmDataAttr('myKey', 'otherKey') // Remove multiple
|
|
1326
2076
|
* ```
|
|
1327
2077
|
*
|
|
1328
2078
|
* @param names - The data attribute name(s) (in camelCase).
|
|
1329
2079
|
* @returns This instance for chaining.
|
|
1330
2080
|
* @throws {TypeError} If any name is not a string.
|
|
1331
2081
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
2082
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
2083
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
1332
2084
|
*/
|
|
1333
|
-
|
|
2085
|
+
rmDataAttr(...names) {
|
|
1334
2086
|
for (const name of names) {
|
|
1335
2087
|
if (!isStr(name)) {
|
|
1336
2088
|
throw typeErr("name", "a string", name);
|
|
@@ -1339,13 +2091,42 @@ var JJEx = class extends JJE {
|
|
|
1339
2091
|
}
|
|
1340
2092
|
return this;
|
|
1341
2093
|
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Removes multiple data attributes from an array of names.
|
|
2096
|
+
*
|
|
2097
|
+
* @example
|
|
2098
|
+
* ```ts
|
|
2099
|
+
* el.rmDataAttrs(['myKey', 'otherKey'])
|
|
2100
|
+
* ```
|
|
2101
|
+
*
|
|
2102
|
+
* @param names - The data attribute names to remove (in camelCase).
|
|
2103
|
+
* @returns This instance for chaining.
|
|
2104
|
+
* @throws {TypeError} If `names` is not an array.
|
|
2105
|
+
* @throws {TypeError} If any name in `names` is not a string.
|
|
2106
|
+
* @see {@link rmDataAttr} for removing one or more data attributes via rest arguments.
|
|
2107
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset | HTMLElement.dataset}
|
|
2108
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement/dataset | SVGElement.dataset}
|
|
2109
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement/dataset | MathMLElement.dataset}
|
|
2110
|
+
*/
|
|
2111
|
+
rmDataAttrs(names) {
|
|
2112
|
+
if (!Array.isArray(names)) {
|
|
2113
|
+
throw typeErr("names", "an array", names);
|
|
2114
|
+
}
|
|
2115
|
+
return this.rmDataAttr(...names);
|
|
2116
|
+
}
|
|
1342
2117
|
};
|
|
1343
2118
|
|
|
1344
2119
|
// src/wrappers/JJHE.ts
|
|
2120
|
+
var COMMON_SVG_TAGS = ["svg", "rect", "circle", "line", "path", "text"];
|
|
2121
|
+
var COMMON_MATHML_TAGS = ["math", "mi", "mn", "mo", "mtext"];
|
|
1345
2122
|
var JJHE = class _JJHE extends JJEx {
|
|
1346
2123
|
/**
|
|
1347
2124
|
* Creates a JJHE instance from an HTMLElement reference.
|
|
1348
2125
|
*
|
|
2126
|
+
* @remarks
|
|
2127
|
+
* Use {@link JJHE.create} to create new HTMLElements, or use this method to wrap existing ones.
|
|
2128
|
+
* For other element types, use {@link JJSE.from} for SVGElements or {@link JJME.from} for MathMLElements.
|
|
2129
|
+
*
|
|
1349
2130
|
* @example
|
|
1350
2131
|
* ```ts
|
|
1351
2132
|
* const el = JJHE.from(document.getElementById('my-id')) // from an existing HTMLElement
|
|
@@ -1360,19 +2141,77 @@ var JJHE = class _JJHE extends JJEx {
|
|
|
1360
2141
|
}
|
|
1361
2142
|
static create(tagName, options) {
|
|
1362
2143
|
if (!isStr(tagName)) {
|
|
1363
|
-
throw typeErr("tagName", "a string like 'div' or 'button'", tagName);
|
|
2144
|
+
throw typeErr("tagName", "a string like 'div' or 'button'", tagName, "Pass a valid HTML tag name.");
|
|
2145
|
+
}
|
|
2146
|
+
if (COMMON_SVG_TAGS.includes(tagName)) {
|
|
2147
|
+
throw errMsg(
|
|
2148
|
+
`tagName`,
|
|
2149
|
+
`a HTML tag name (not an SVG tag name)`,
|
|
2150
|
+
tagName,
|
|
2151
|
+
`Use JJSE.create("${tagName}") for SVG elements.`
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
if (COMMON_MATHML_TAGS.includes(tagName)) {
|
|
2155
|
+
throw errMsg(
|
|
2156
|
+
`tagName`,
|
|
2157
|
+
`a HTML tag name (not a MathML tag name)`,
|
|
2158
|
+
tagName,
|
|
2159
|
+
`Use JJME.create("${tagName}") for MathML elements.`
|
|
2160
|
+
);
|
|
1364
2161
|
}
|
|
1365
2162
|
return new _JJHE(document.createElement(tagName, options));
|
|
1366
2163
|
}
|
|
2164
|
+
/**
|
|
2165
|
+
* Builds an HTML element tree with optional attributes and children.
|
|
2166
|
+
*
|
|
2167
|
+
* @remarks
|
|
2168
|
+
* A concise declarative way to build HTML DOM snippets. Chain further JJ methods on the return value.
|
|
2169
|
+
* Pass `null` or omit `attributes` when no attributes are needed. Pass children as additional arguments.
|
|
2170
|
+
* Unlike `create()`, the return type is always `JJHE` (not the specific subtype), which is fine for
|
|
2171
|
+
* snippet construction where precise inference is not needed.
|
|
2172
|
+
*
|
|
2173
|
+
* If you prefer a shorter alias compatible with hyperscript conventions, you can use:
|
|
2174
|
+
* ```ts
|
|
2175
|
+
* const h = JJHE.tree
|
|
2176
|
+
* ```
|
|
2177
|
+
*
|
|
2178
|
+
* @example
|
|
2179
|
+
* ```ts
|
|
2180
|
+
* // Simple element with text
|
|
2181
|
+
* JJHE.tree('p', { class: 'intro' }, 'Hello World')
|
|
2182
|
+
*
|
|
2183
|
+
* // Nested structure
|
|
2184
|
+
* JJHE.tree('nav', { class: 'main-nav' },
|
|
2185
|
+
* JJHE.tree('a', { href: '/' }, 'Home'),
|
|
2186
|
+
* JJHE.tree('a', { href: '/about' }, 'About'),
|
|
2187
|
+
* )
|
|
2188
|
+
*
|
|
2189
|
+
* // No attributes
|
|
2190
|
+
* JJHE.tree('section', null, JJHE.tree('h1', null, 'Title'), JJHE.tree('p', null, 'Body'))
|
|
2191
|
+
* ```
|
|
2192
|
+
*
|
|
2193
|
+
* @param tagName - The HTML tag name.
|
|
2194
|
+
* @param attributes - Attributes to set. Pass `null` or `undefined` to skip.
|
|
2195
|
+
* @param children - Children to append (strings, nodes, or JJ wrappers).
|
|
2196
|
+
* @returns A new JJHE instance.
|
|
2197
|
+
* @throws {TypeError} If `attributes` is not a plain object.
|
|
2198
|
+
* @see {@link JJHE.create} for a type-narrowed single-element factory.
|
|
2199
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement | document.createElement}
|
|
2200
|
+
*/
|
|
2201
|
+
static tree(tagName, attributes, ...children) {
|
|
2202
|
+
return _JJHE.create(tagName).setAttrs(attributes).addChild(...children);
|
|
2203
|
+
}
|
|
1367
2204
|
/**
|
|
1368
2205
|
* Creates an instance of JJHE.
|
|
1369
2206
|
*
|
|
1370
2207
|
* @param ref - The HTMLElement to wrap.
|
|
1371
2208
|
* @throws {TypeError} If `ref` is not an HTMLElement.
|
|
2209
|
+
* @see {@link JJHE.from} to wrap existing HTMLElements
|
|
2210
|
+
* @see {@link JJHE.create} to create new HTMLElements
|
|
1372
2211
|
*/
|
|
1373
2212
|
constructor(ref) {
|
|
1374
|
-
if (!
|
|
1375
|
-
throw typeErr("ref", "an HTMLElement", ref);
|
|
2213
|
+
if (!isInstance(ref, HTMLElement)) {
|
|
2214
|
+
throw typeErr("ref", "an HTMLElement", ref, "Use JJHE.from() or JJHE.create().");
|
|
1376
2215
|
}
|
|
1377
2216
|
super(ref);
|
|
1378
2217
|
}
|
|
@@ -1465,6 +2304,10 @@ var JJT = class _JJT extends JJN {
|
|
|
1465
2304
|
/**
|
|
1466
2305
|
* Creates a JJT instance from a Text node.
|
|
1467
2306
|
*
|
|
2307
|
+
* @remarks
|
|
2308
|
+
* Use {@link JJT.create} to create a Text node from a string.
|
|
2309
|
+
* For other Node types, use {@link JJN.from} or the specific wrapper type.
|
|
2310
|
+
*
|
|
1468
2311
|
* @example
|
|
1469
2312
|
* ```ts
|
|
1470
2313
|
* const textNode = document.createTextNode('foo')
|
|
@@ -1474,12 +2317,21 @@ var JJT = class _JJT extends JJN {
|
|
|
1474
2317
|
* @param text - The Text node.
|
|
1475
2318
|
* @returns A new JJT instance.
|
|
1476
2319
|
* @throws {TypeError} If `text` is not a Text node.
|
|
2320
|
+
* @see {@link JJT.create} for creating from string input.
|
|
1477
2321
|
*/
|
|
1478
2322
|
static from(text) {
|
|
1479
2323
|
return new _JJT(text);
|
|
1480
2324
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
2325
|
+
/**
|
|
2326
|
+
* Creates a JJT instance from a string.
|
|
2327
|
+
*
|
|
2328
|
+
* @param text - The string to convert into a Text node.
|
|
2329
|
+
* @returns A new JJT instance wrapping a Text node.
|
|
2330
|
+
* @see {@link JJT.from} for wrapping an existing Text node.
|
|
2331
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode | document.createTextNode}
|
|
2332
|
+
*/
|
|
2333
|
+
static create(text) {
|
|
2334
|
+
return new _JJT(document.createTextNode(toStr(text)));
|
|
1483
2335
|
}
|
|
1484
2336
|
/**
|
|
1485
2337
|
* Creates an instance of JJT.
|
|
@@ -1491,11 +2343,16 @@ var JJT = class _JJT extends JJN {
|
|
|
1491
2343
|
*
|
|
1492
2344
|
* @param ref - The Text node or a string to create a Text node from.
|
|
1493
2345
|
* @throws {TypeError} If `ref` is not a Text node or string.
|
|
2346
|
+
* @see {@link JJT.from} for wrapping an existing Text node.
|
|
2347
|
+
* @see {@link JJT.create} for creating from a plain string.
|
|
1494
2348
|
*/
|
|
1495
2349
|
constructor(ref) {
|
|
1496
|
-
if (!
|
|
1497
|
-
throw
|
|
1498
|
-
|
|
2350
|
+
if (!isInstance(ref, Text)) {
|
|
2351
|
+
throw typeErr(
|
|
2352
|
+
"ref",
|
|
2353
|
+
"a Text node",
|
|
2354
|
+
ref,
|
|
2355
|
+
"Create a Text node with JJT.create() or document.createTextNode('text')."
|
|
1499
2356
|
);
|
|
1500
2357
|
}
|
|
1501
2358
|
super(ref);
|
|
@@ -1569,6 +2426,10 @@ var JJD = class _JJD extends JJNx {
|
|
|
1569
2426
|
/**
|
|
1570
2427
|
* Creates a JJD instance from a Document reference.
|
|
1571
2428
|
*
|
|
2429
|
+
* @remarks
|
|
2430
|
+
* Typically, you'll use this to wrap the global `document` object.
|
|
2431
|
+
* Use {@link JJHE.from} to wrap individual elements, and {@link JJHE} for the head/body elements.
|
|
2432
|
+
*
|
|
1572
2433
|
* @example
|
|
1573
2434
|
* ```ts
|
|
1574
2435
|
* const doc = JJD.from(document)
|
|
@@ -1586,39 +2447,156 @@ var JJD = class _JJD extends JJNx {
|
|
|
1586
2447
|
*
|
|
1587
2448
|
* @param ref - The Document instance to wrap.
|
|
1588
2449
|
* @throws {TypeError} If `ref` is not a Document.
|
|
2450
|
+
* @see {@link JJD.from} to wrap a Document
|
|
2451
|
+
*/
|
|
2452
|
+
constructor(ref) {
|
|
2453
|
+
if (!isInstance(ref, Document)) {
|
|
2454
|
+
throw typeErr("ref", "a Document instance", ref, "Use JJD.from(document) to create an instance.");
|
|
2455
|
+
}
|
|
2456
|
+
super(ref);
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
|
|
2460
|
+
// src/wrappers/JJME.ts
|
|
2461
|
+
var JJME = class _JJME extends JJEx {
|
|
2462
|
+
/**
|
|
2463
|
+
* Creates a JJME instance from a MathMLElement reference.
|
|
2464
|
+
*
|
|
2465
|
+
* @remarks
|
|
2466
|
+
* Use {@link JJME.create} to create new MathMLElements, or use this method to wrap existing ones.
|
|
2467
|
+
* For HTMLElements, use {@link JJHE.from}, or {@link JJSE.from} for SVGElements.
|
|
2468
|
+
*
|
|
2469
|
+
* @example
|
|
2470
|
+
* ```ts
|
|
2471
|
+
* const mrow = JJME.from(myMrow)
|
|
2472
|
+
* ```
|
|
2473
|
+
*
|
|
2474
|
+
* @param ref - The MathMLElement.
|
|
2475
|
+
* @returns A new JJME instance.
|
|
2476
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MathMLElement | MathMLElement}
|
|
2477
|
+
*/
|
|
2478
|
+
static from(ref) {
|
|
2479
|
+
return new _JJME(ref);
|
|
2480
|
+
}
|
|
2481
|
+
static create(tagName, options) {
|
|
2482
|
+
if (!isStr(tagName)) {
|
|
2483
|
+
throw typeErr(
|
|
2484
|
+
"tagName",
|
|
2485
|
+
'a string like "math" or "mfrac"',
|
|
2486
|
+
tagName,
|
|
2487
|
+
'Pass a valid MathML tag name like "math", "mrow", or "mfrac".'
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
const element = document.createElementNS(MATHML_NS, tagName, options);
|
|
2491
|
+
return new _JJME(element);
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Builds a MathML element tree with optional attributes and children.
|
|
2495
|
+
*
|
|
2496
|
+
* @remarks
|
|
2497
|
+
* A concise declarative way to build MathML DOM snippets. All elements are created in the MathML namespace.
|
|
2498
|
+
* Chain further JJ methods on the return value. Pass `null` or omit `attributes` when no attributes are needed.
|
|
2499
|
+
*
|
|
2500
|
+
* If you prefer a shorter alias compatible with hyperscript conventions, you can use:
|
|
2501
|
+
* ```ts
|
|
2502
|
+
* const h = JJME.tree
|
|
2503
|
+
* ```
|
|
2504
|
+
*
|
|
2505
|
+
* @example
|
|
2506
|
+
* ```ts
|
|
2507
|
+
* // Fraction: x/y
|
|
2508
|
+
* JJME.tree('math', null,
|
|
2509
|
+
* JJME.tree('mfrac', null,
|
|
2510
|
+
* JJME.tree('mi', null, 'x'),
|
|
2511
|
+
* JJME.tree('mi', null, 'y'),
|
|
2512
|
+
* ),
|
|
2513
|
+
* )
|
|
2514
|
+
*
|
|
2515
|
+
* // Subscript: a_n
|
|
2516
|
+
* JJME.tree('math', null,
|
|
2517
|
+
* JJME.tree('msub', null,
|
|
2518
|
+
* JJME.tree('mi', null, 'a'),
|
|
2519
|
+
* JJME.tree('mi', null, 'n'),
|
|
2520
|
+
* ),
|
|
2521
|
+
* )
|
|
2522
|
+
* ```
|
|
2523
|
+
*
|
|
2524
|
+
* @param tagName - The MathML tag name.
|
|
2525
|
+
* @param attributes - Attributes to set. Pass `null` or `undefined` to skip.
|
|
2526
|
+
* @param children - Children to append (strings, nodes, or JJ wrappers).
|
|
2527
|
+
* @returns A new JJME instance.
|
|
2528
|
+
* @throws {TypeError} If `attributes` is not a plain object.
|
|
2529
|
+
* @see {@link JJME.create} for a type-narrowed single-element factory.
|
|
2530
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS | document.createElementNS}
|
|
2531
|
+
*/
|
|
2532
|
+
static tree(tagName, attributes, ...children) {
|
|
2533
|
+
return _JJME.create(tagName).setAttrs(attributes).addChild(...children);
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Creates an instance of JJME.
|
|
2537
|
+
*
|
|
2538
|
+
* @param ref - The MathMLElement to wrap.
|
|
2539
|
+
* @throws {TypeError} If `ref` is not a MathMLElement.
|
|
2540
|
+
* @see {@link JJME.from} to wrap existing MathMLElements
|
|
2541
|
+
* @see {@link JJME.create} to create new MathMLElements
|
|
1589
2542
|
*/
|
|
1590
2543
|
constructor(ref) {
|
|
1591
|
-
if (!
|
|
1592
|
-
throw
|
|
2544
|
+
if (!isInstance(ref, Element) || ref.namespaceURI !== MATHML_NS) {
|
|
2545
|
+
throw typeErr("ref", `a MathML element (${MATHML_NS})`, ref, "Use JJME.from() or JJME.create().");
|
|
1593
2546
|
}
|
|
1594
2547
|
super(ref);
|
|
1595
2548
|
}
|
|
1596
2549
|
/**
|
|
1597
|
-
* Gets the
|
|
2550
|
+
* Gets the text content of the MathMLElement.
|
|
2551
|
+
*
|
|
2552
|
+
* @remarks
|
|
2553
|
+
* This method operates on `textContent`. The method name is kept short for convenience.
|
|
1598
2554
|
*
|
|
1599
|
-
* @
|
|
1600
|
-
*
|
|
2555
|
+
* @example
|
|
2556
|
+
* ```ts
|
|
2557
|
+
* const text = math.getText()
|
|
2558
|
+
* ```
|
|
2559
|
+
*
|
|
2560
|
+
* @returns The text content.
|
|
2561
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent | Node.textContent}
|
|
1601
2562
|
*/
|
|
1602
|
-
|
|
1603
|
-
return
|
|
2563
|
+
getText() {
|
|
2564
|
+
return this.ref.textContent ?? "";
|
|
1604
2565
|
}
|
|
1605
2566
|
/**
|
|
1606
|
-
*
|
|
2567
|
+
* Sets the text content of the MathMLElement.
|
|
1607
2568
|
*
|
|
1608
|
-
* @
|
|
1609
|
-
*
|
|
2569
|
+
* @remarks
|
|
2570
|
+
* This method operates on `textContent`. The method name is kept short for convenience.
|
|
2571
|
+
* Pass an empty string, `null`, or `undefined` to clear the content.
|
|
2572
|
+
* Numbers and booleans are automatically converted to strings.
|
|
2573
|
+
*
|
|
2574
|
+
* @example
|
|
2575
|
+
* ```ts
|
|
2576
|
+
* mi.setText('x')
|
|
2577
|
+
* mi.setText(null) // Clear content
|
|
2578
|
+
* mi.setText(42) // Numbers are converted
|
|
2579
|
+
* ```
|
|
2580
|
+
*
|
|
2581
|
+
* @param text - The text to set, or null/undefined to clear.
|
|
2582
|
+
* @returns This instance for chaining.
|
|
2583
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent | Node.textContent}
|
|
1610
2584
|
*/
|
|
1611
|
-
|
|
1612
|
-
|
|
2585
|
+
setText(text) {
|
|
2586
|
+
this.ref.textContent = text;
|
|
2587
|
+
return this;
|
|
1613
2588
|
}
|
|
1614
2589
|
};
|
|
1615
2590
|
|
|
1616
2591
|
// src/wrappers/JJSE.ts
|
|
1617
|
-
var SVG_NAMESPACE_URI = "http://www.w3.org/2000/svg";
|
|
1618
2592
|
var JJSE = class _JJSE extends JJEx {
|
|
1619
2593
|
/**
|
|
1620
2594
|
* Creates a JJSE instance from an SVGElement reference.
|
|
1621
2595
|
*
|
|
2596
|
+
* @remarks
|
|
2597
|
+
* Use {@link JJSE.create} to create new SVGElements, or use this method to wrap existing ones.
|
|
2598
|
+
* For HTMLElements, use {@link JJHE.from}, or {@link JJME.from} for MathMLElements.
|
|
2599
|
+
*
|
|
1622
2600
|
* @example
|
|
1623
2601
|
* ```ts
|
|
1624
2602
|
* const svg = JJSE.from(myCircle)
|
|
@@ -1631,39 +2609,68 @@ var JJSE = class _JJSE extends JJEx {
|
|
|
1631
2609
|
static from(ref) {
|
|
1632
2610
|
return new _JJSE(ref);
|
|
1633
2611
|
}
|
|
2612
|
+
static create(tagName, options) {
|
|
2613
|
+
if (!isStr(tagName)) {
|
|
2614
|
+
throw typeErr(
|
|
2615
|
+
"tagName",
|
|
2616
|
+
'a string like "circle" or "path"',
|
|
2617
|
+
tagName,
|
|
2618
|
+
'Pass a valid SVG tag name like "svg", "circle", or "path".'
|
|
2619
|
+
);
|
|
2620
|
+
}
|
|
2621
|
+
const element = document.createElementNS(SVG_NS, tagName, options);
|
|
2622
|
+
return new _JJSE(element);
|
|
2623
|
+
}
|
|
1634
2624
|
/**
|
|
1635
|
-
*
|
|
2625
|
+
* Builds an SVG element tree with optional attributes and children.
|
|
1636
2626
|
*
|
|
1637
2627
|
* @remarks
|
|
1638
|
-
*
|
|
2628
|
+
* A concise declarative way to build SVG DOM snippets. All elements are created in the SVG namespace.
|
|
2629
|
+
* Chain further JJ methods on the return value. Pass `null` or omit `attributes` when no attributes are needed.
|
|
2630
|
+
*
|
|
2631
|
+
* If you prefer a shorter alias compatible with hyperscript conventions, you can use:
|
|
2632
|
+
* ```ts
|
|
2633
|
+
* const h = JJSE.tree
|
|
2634
|
+
* ```
|
|
1639
2635
|
*
|
|
1640
2636
|
* @example
|
|
1641
2637
|
* ```ts
|
|
1642
|
-
*
|
|
2638
|
+
* // Simple SVG icon
|
|
2639
|
+
* JJSE.tree('svg', { viewBox: '0 0 24 24', width: '24', height: '24' },
|
|
2640
|
+
* JJSE.tree('circle', { cx: '12', cy: '12', r: '10', fill: 'currentColor' }),
|
|
2641
|
+
* )
|
|
2642
|
+
*
|
|
2643
|
+
* // No attributes
|
|
2644
|
+
* JJSE.tree('g', null, JJSE.tree('rect', { x: '0', y: '0', width: '10', height: '10' }))
|
|
1643
2645
|
* ```
|
|
1644
2646
|
*
|
|
1645
|
-
* @param tagName - The tag name.
|
|
1646
|
-
* @param
|
|
2647
|
+
* @param tagName - The SVG tag name.
|
|
2648
|
+
* @param attributes - Attributes to set. Pass `null` or `undefined` to skip.
|
|
2649
|
+
* @param children - Children to append (strings, nodes, or JJ wrappers).
|
|
1647
2650
|
* @returns A new JJSE instance.
|
|
1648
|
-
* @throws {TypeError} If `
|
|
2651
|
+
* @throws {TypeError} If `attributes` is not a plain object.
|
|
2652
|
+
* @see {@link JJSE.create} for a type-narrowed single-element factory.
|
|
1649
2653
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS | document.createElementNS}
|
|
1650
2654
|
*/
|
|
1651
|
-
static
|
|
1652
|
-
|
|
1653
|
-
throw typeErr("tagName", 'a string like "circle" or "path"', tagName);
|
|
1654
|
-
}
|
|
1655
|
-
const element = document.createElementNS(SVG_NAMESPACE_URI, tagName, options);
|
|
1656
|
-
return new _JJSE(element);
|
|
2655
|
+
static tree(tagName, attributes, ...children) {
|
|
2656
|
+
return _JJSE.create(tagName).setAttrs(attributes).addChild(...children);
|
|
1657
2657
|
}
|
|
1658
2658
|
/**
|
|
1659
2659
|
* Creates an instance of JJSE.
|
|
1660
2660
|
*
|
|
1661
2661
|
* @param ref - The SVGElement to wrap.
|
|
1662
2662
|
* @throws {TypeError} If `ref` is not an SVGElement.
|
|
2663
|
+
* @see {@link JJSE.from} to wrap an existing SVG element.
|
|
2664
|
+
* @see {@link JJSE.create} to create a new SVG element.
|
|
1663
2665
|
*/
|
|
1664
2666
|
constructor(ref) {
|
|
1665
|
-
if (!
|
|
1666
|
-
throw typeErr(
|
|
2667
|
+
if (!isInstance(ref, SVGElement)) {
|
|
2668
|
+
throw typeErr(
|
|
2669
|
+
"ref",
|
|
2670
|
+
"an SVGElement",
|
|
2671
|
+
ref,
|
|
2672
|
+
'Wrap an existing SVG element with JJSE.from(el) or create one with JJSE.create("svg").'
|
|
2673
|
+
);
|
|
1667
2674
|
}
|
|
1668
2675
|
super(ref);
|
|
1669
2676
|
}
|
|
@@ -1754,7 +2761,7 @@ var JJSE = class _JJSE extends JJEx {
|
|
|
1754
2761
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox | viewBox}
|
|
1755
2762
|
*/
|
|
1756
2763
|
setViewBox(p1, p2, p3, p4) {
|
|
1757
|
-
if (
|
|
2764
|
+
if (isNum(p1) && isNum(p2) && isNum(p3) && isNum(p4)) {
|
|
1758
2765
|
return this.setAttr("viewBox", `${p1} ${p2} ${p3} ${p4}`);
|
|
1759
2766
|
}
|
|
1760
2767
|
const value = p1;
|
|
@@ -1804,96 +2811,42 @@ var JJSE = class _JJSE extends JJEx {
|
|
|
1804
2811
|
|
|
1805
2812
|
// src/wrappers/JJN.ts
|
|
1806
2813
|
JJN.wrap = function wrap(raw) {
|
|
1807
|
-
if (
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
if (!isObj(raw)) {
|
|
1811
|
-
throw typeErr("raw", "an object", raw);
|
|
1812
|
-
}
|
|
1813
|
-
if (isA(raw, JJN)) {
|
|
1814
|
-
return raw;
|
|
1815
|
-
}
|
|
1816
|
-
if (isA(raw, HTMLElement)) {
|
|
1817
|
-
return JJHE.from(raw);
|
|
1818
|
-
}
|
|
1819
|
-
if (isA(raw, SVGElement)) {
|
|
1820
|
-
return JJSE.from(raw);
|
|
1821
|
-
}
|
|
1822
|
-
if (isA(raw, Element)) {
|
|
1823
|
-
return JJE.from(raw);
|
|
1824
|
-
}
|
|
1825
|
-
if (isA(raw, ShadowRoot)) {
|
|
1826
|
-
return JJSR.from(raw);
|
|
1827
|
-
}
|
|
1828
|
-
if (isA(raw, DocumentFragment)) {
|
|
1829
|
-
return JJDF.from(raw);
|
|
1830
|
-
}
|
|
1831
|
-
if (isA(raw, Document)) {
|
|
1832
|
-
return JJD.from(raw);
|
|
1833
|
-
}
|
|
1834
|
-
if (isA(raw, Text)) {
|
|
1835
|
-
return JJT.from(raw);
|
|
1836
|
-
}
|
|
1837
|
-
if (isA(raw, Node)) {
|
|
1838
|
-
return JJN.from(raw);
|
|
1839
|
-
}
|
|
1840
|
-
throw typeErr("raw", "a Node", raw);
|
|
1841
|
-
};
|
|
1842
|
-
|
|
1843
|
-
// src/helpers.ts
|
|
1844
|
-
function h(tagName, attributes, ...children) {
|
|
1845
|
-
const ret = JJHE.create(tagName).addChild(...children);
|
|
1846
|
-
if (attributes) {
|
|
1847
|
-
ret.setAttr(attributes);
|
|
1848
|
-
}
|
|
1849
|
-
return ret;
|
|
1850
|
-
}
|
|
1851
|
-
function linkAs(href) {
|
|
1852
|
-
switch (fileExt(href)) {
|
|
1853
|
-
case "html":
|
|
1854
|
-
case "htm":
|
|
1855
|
-
case "md":
|
|
1856
|
-
return "fetch";
|
|
1857
|
-
case "css":
|
|
1858
|
-
return "style";
|
|
1859
|
-
case "js":
|
|
1860
|
-
case "mjs":
|
|
1861
|
-
case "cjs":
|
|
1862
|
-
return "script";
|
|
1863
|
-
default:
|
|
1864
|
-
throw new Error(`No 'as' attribute was specified and we failed to guess it from the URL: ${href}`);
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
function createLinkPre(href, rel, as) {
|
|
1868
|
-
if (!isStr(href)) {
|
|
1869
|
-
if (!isA(href, URL)) {
|
|
1870
|
-
throw typeErr("href", "a string or URL", href);
|
|
2814
|
+
if (raw && typeof raw === "object") {
|
|
2815
|
+
if (isInstance(raw, JJN)) {
|
|
2816
|
+
return raw;
|
|
1871
2817
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
2818
|
+
if (isInstance(raw, HTMLElement)) {
|
|
2819
|
+
return JJHE.from(raw);
|
|
2820
|
+
}
|
|
2821
|
+
if (isInstance(raw, SVGElement)) {
|
|
2822
|
+
return JJSE.from(raw);
|
|
2823
|
+
}
|
|
2824
|
+
if (isInstance(raw, MathMLElement)) {
|
|
2825
|
+
return JJME.from(raw);
|
|
2826
|
+
}
|
|
2827
|
+
if (isInstance(raw, Element)) {
|
|
2828
|
+
return JJE.from(raw);
|
|
2829
|
+
}
|
|
2830
|
+
if (isInstance(raw, ShadowRoot)) {
|
|
2831
|
+
return JJSR.from(raw);
|
|
2832
|
+
}
|
|
2833
|
+
if (isInstance(raw, DocumentFragment)) {
|
|
2834
|
+
return JJDF.from(raw);
|
|
2835
|
+
}
|
|
2836
|
+
if (isInstance(raw, Document)) {
|
|
2837
|
+
return JJD.from(raw);
|
|
2838
|
+
}
|
|
2839
|
+
if (isInstance(raw, Text)) {
|
|
2840
|
+
return JJT.from(raw);
|
|
2841
|
+
}
|
|
2842
|
+
if (isInstance(raw, Node)) {
|
|
2843
|
+
return JJN.from(raw);
|
|
1881
2844
|
}
|
|
1882
2845
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
href,
|
|
1888
|
-
rel,
|
|
1889
|
-
as
|
|
1890
|
-
});
|
|
1891
|
-
}
|
|
1892
|
-
function addLinkPre(...args) {
|
|
1893
|
-
const link = createLinkPre(...args);
|
|
1894
|
-
document.head.append(link.ref);
|
|
1895
|
-
return link;
|
|
1896
|
-
}
|
|
2846
|
+
return JJT.create(String(raw));
|
|
2847
|
+
};
|
|
2848
|
+
|
|
2849
|
+
// src/fetchers.ts
|
|
1897
2850
|
async function fetchText(url, mime = "text/*") {
|
|
1898
2851
|
if (!isStr(mime)) {
|
|
1899
2852
|
throw typeErr("mime", "a string", mime);
|
|
@@ -1904,168 +2857,77 @@ async function fetchText(url, mime = "text/*") {
|
|
|
1904
2857
|
}
|
|
1905
2858
|
return response.text();
|
|
1906
2859
|
}
|
|
1907
|
-
async function
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2860
|
+
async function fetchTemplate(url) {
|
|
2861
|
+
try {
|
|
2862
|
+
const htmlStr = await fetchText(url, "text/html");
|
|
2863
|
+
return JJDF.from(document.createRange().createContextualFragment(htmlStr));
|
|
2864
|
+
} catch (err) {
|
|
2865
|
+
throw new Error(`Failed to fetch or process HTML from ${url}`, { cause: err });
|
|
2866
|
+
}
|
|
1912
2867
|
}
|
|
1913
2868
|
async function fetchStyle(url) {
|
|
1914
|
-
|
|
2869
|
+
try {
|
|
2870
|
+
const sheet = new CSSStyleSheet();
|
|
2871
|
+
return await sheet.replace(await fetchText(url, "text/css"));
|
|
2872
|
+
} catch (err) {
|
|
2873
|
+
throw new Error(`Failed to fetch or convert CSS string to CSSStyleSheet from ${url}`, { cause: err });
|
|
2874
|
+
}
|
|
1915
2875
|
}
|
|
1916
2876
|
|
|
1917
2877
|
// src/components.ts
|
|
1918
2878
|
function attr2prop(instance, name, oldValue, newValue) {
|
|
1919
|
-
if (!
|
|
1920
|
-
throw typeErr(
|
|
2879
|
+
if (!isInstance(instance, HTMLElement)) {
|
|
2880
|
+
throw typeErr(
|
|
2881
|
+
"instance",
|
|
2882
|
+
"an HTMLElement",
|
|
2883
|
+
instance,
|
|
2884
|
+
"Call attr2prop(this, ...) from attributeChangedCallback on a custom element instance."
|
|
2885
|
+
);
|
|
1921
2886
|
}
|
|
1922
2887
|
if (oldValue !== newValue) {
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
instance
|
|
1926
|
-
|
|
2888
|
+
try {
|
|
2889
|
+
const propName = keb2cam(name);
|
|
2890
|
+
if (hasProp(instance, propName)) {
|
|
2891
|
+
instance[propName] = newValue;
|
|
2892
|
+
return true;
|
|
2893
|
+
}
|
|
2894
|
+
} catch (err) {
|
|
2895
|
+
throw new Error(
|
|
2896
|
+
`Failed to set property using attribute change event ${name}. Old value: ${oldValue}, New value: ${newValue}.`,
|
|
2897
|
+
{ cause: err }
|
|
2898
|
+
);
|
|
1927
2899
|
}
|
|
1928
2900
|
}
|
|
1929
2901
|
return false;
|
|
1930
2902
|
}
|
|
1931
|
-
async function
|
|
2903
|
+
async function defineComponent(name, constructor, options) {
|
|
1932
2904
|
if (!isStr(name)) {
|
|
1933
|
-
throw typeErr("name", "a string", name);
|
|
1934
|
-
}
|
|
1935
|
-
if (!isFn(constructor)) {
|
|
1936
|
-
throw typeErr("constructor", "a function", constructor);
|
|
1937
|
-
}
|
|
1938
|
-
if (!customElements.get(name)) {
|
|
1939
|
-
customElements.define(name, constructor, options);
|
|
1940
|
-
await customElements.whenDefined(name);
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
// src/ShadowMaster.ts
|
|
1945
|
-
async function templatePromise(templateConfig) {
|
|
1946
|
-
if (templateConfig === void 0) {
|
|
1947
|
-
return void 0;
|
|
2905
|
+
throw typeErr("name", "a string", name, 'Pass a valid name like "my-component".');
|
|
1948
2906
|
}
|
|
1949
|
-
if (
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
if (isStr(templateConfig)) {
|
|
1954
|
-
return templateConfig;
|
|
1955
|
-
}
|
|
1956
|
-
if (isA(templateConfig, JJDF)) {
|
|
1957
|
-
return templateConfig.ref.cloneNode(true);
|
|
2907
|
+
if (!name.includes("-")) {
|
|
2908
|
+
throw new SyntaxError(
|
|
2909
|
+
errMsg("name", "a custom-element name containing a hyphen", name, 'Use kebab-case like "my-component".')
|
|
2910
|
+
);
|
|
1958
2911
|
}
|
|
1959
|
-
if (
|
|
1960
|
-
|
|
2912
|
+
if (typeof constructor !== "function") {
|
|
2913
|
+
throw typeErr(
|
|
2914
|
+
"constructor",
|
|
2915
|
+
"a function",
|
|
2916
|
+
constructor,
|
|
2917
|
+
'Pass the custom element class itself, e.g. defineComponent("my-component", MyComponent).'
|
|
2918
|
+
);
|
|
1961
2919
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2920
|
+
const definedConstructor = customElements.get(name);
|
|
2921
|
+
if (definedConstructor) {
|
|
2922
|
+
if (definedConstructor !== constructor) {
|
|
2923
|
+
throw new ReferenceError(`A different constructor is already defined for the custom element "${name}".`);
|
|
1965
2924
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
return templateConfig instanceof HTMLTemplateElement ? templateConfig.content.cloneNode(true) : templateConfig.outerHTML;
|
|
1970
|
-
}
|
|
1971
|
-
throw typeErr("template", "a string, JJHE, JJDF, HTMLElement, or DocumentFragment", templateConfig);
|
|
1972
|
-
}
|
|
1973
|
-
async function stylePromise(styleConfig) {
|
|
1974
|
-
if (isFn(styleConfig)) {
|
|
1975
|
-
styleConfig = await styleConfig();
|
|
1976
|
-
}
|
|
1977
|
-
styleConfig = await styleConfig;
|
|
1978
|
-
if (isA(styleConfig, CSSStyleSheet)) {
|
|
1979
|
-
return styleConfig;
|
|
1980
|
-
}
|
|
1981
|
-
if (isStr(styleConfig)) {
|
|
1982
|
-
return await cssToStyle(styleConfig);
|
|
1983
|
-
}
|
|
1984
|
-
throw typeErr("style", "a CSS string or CSSStyleSheet", styleConfig);
|
|
1985
|
-
}
|
|
1986
|
-
function stylePromises(styleConfigs) {
|
|
1987
|
-
if (!isArr(styleConfigs)) {
|
|
1988
|
-
return [];
|
|
2925
|
+
} else {
|
|
2926
|
+
customElements.define(name, constructor, options);
|
|
2927
|
+
await customElements.whenDefined(name);
|
|
1989
2928
|
}
|
|
1990
|
-
return
|
|
1991
|
-
}
|
|
1992
|
-
async function resolveConfig(templateConfig, styleConfigs) {
|
|
1993
|
-
const [template, ...styles] = await Promise.all([templatePromise(templateConfig), ...stylePromises(styleConfigs)]);
|
|
1994
|
-
return { template, styles };
|
|
2929
|
+
return Boolean(definedConstructor);
|
|
1995
2930
|
}
|
|
1996
|
-
var _templateConfig, _stylesConfig, _normalizedConfig;
|
|
1997
|
-
var _ShadowMaster = class _ShadowMaster {
|
|
1998
|
-
constructor() {
|
|
1999
|
-
__privateAdd(this, _templateConfig);
|
|
2000
|
-
__privateAdd(this, _stylesConfig, []);
|
|
2001
|
-
__privateAdd(this, _normalizedConfig);
|
|
2002
|
-
}
|
|
2003
|
-
/**
|
|
2004
|
-
* Creates a new instance of ShadowMaster.
|
|
2005
|
-
*
|
|
2006
|
-
* @returns A new ShadowMaster instance.
|
|
2007
|
-
*/
|
|
2008
|
-
static create() {
|
|
2009
|
-
return new _ShadowMaster();
|
|
2010
|
-
}
|
|
2011
|
-
/**
|
|
2012
|
-
* Sets the template configuration.
|
|
2013
|
-
*
|
|
2014
|
-
* @param templateConfig - The template configuration.
|
|
2015
|
-
* @returns The instance for chaining.
|
|
2016
|
-
*
|
|
2017
|
-
* @example
|
|
2018
|
-
* ```ts
|
|
2019
|
-
* // Accepts string, promise, or fetchHtml result
|
|
2020
|
-
* sm.setTemplate(fetchHtml('./template.html'))
|
|
2021
|
-
* ```
|
|
2022
|
-
*/
|
|
2023
|
-
setTemplate(templateConfig) {
|
|
2024
|
-
__privateSet(this, _templateConfig, templateConfig);
|
|
2025
|
-
return this;
|
|
2026
|
-
}
|
|
2027
|
-
/**
|
|
2028
|
-
* Adds one or more style configurations.
|
|
2029
|
-
*
|
|
2030
|
-
* @param stylesConfig - Variable number of style configurations.
|
|
2031
|
-
* @returns The instance for chaining.
|
|
2032
|
-
*
|
|
2033
|
-
* @example
|
|
2034
|
-
* ```ts
|
|
2035
|
-
* sm.addStyles(
|
|
2036
|
-
* 'p { color: red; }',
|
|
2037
|
-
* fetchCss('./styles.css'),
|
|
2038
|
-
* () => fetchCss('../lazy-loaded-styles.css'),
|
|
2039
|
-
* )
|
|
2040
|
-
* ```
|
|
2041
|
-
*/
|
|
2042
|
-
addStyles(...stylesConfig) {
|
|
2043
|
-
__privateGet(this, _stylesConfig).push(...stylesConfig);
|
|
2044
|
-
return this;
|
|
2045
|
-
}
|
|
2046
|
-
/**
|
|
2047
|
-
* Resolves the configuration to something that can be fed to `JJHE.initShadow()` function
|
|
2048
|
-
*
|
|
2049
|
-
* The result is cached, so subsequent calls return the same promise.
|
|
2050
|
-
* Note: Any changes made to the ShadowMaster instance (via setTemplate/addStyles)
|
|
2051
|
-
* after the first call to getResolved() will be ignored.
|
|
2052
|
-
*
|
|
2053
|
-
* @returns A promise resolving to the ShadowConfig.
|
|
2054
|
-
*/
|
|
2055
|
-
async getResolved() {
|
|
2056
|
-
if (!__privateGet(this, _normalizedConfig)) {
|
|
2057
|
-
__privateSet(this, _normalizedConfig, resolveConfig(__privateGet(this, _templateConfig), __privateGet(this, _stylesConfig)));
|
|
2058
|
-
}
|
|
2059
|
-
return await __privateGet(this, _normalizedConfig);
|
|
2060
|
-
}
|
|
2061
|
-
};
|
|
2062
|
-
_templateConfig = new WeakMap();
|
|
2063
|
-
_stylesConfig = new WeakMap();
|
|
2064
|
-
_normalizedConfig = new WeakMap();
|
|
2065
|
-
var ShadowMaster = _ShadowMaster;
|
|
2066
|
-
|
|
2067
|
-
// src/index.ts
|
|
2068
|
-
var doc = JJD.from(document);
|
|
2069
2931
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2070
2932
|
0 && (module.exports = {
|
|
2071
2933
|
JJD,
|
|
@@ -2073,27 +2935,14 @@ var doc = JJD.from(document);
|
|
|
2073
2935
|
JJE,
|
|
2074
2936
|
JJET,
|
|
2075
2937
|
JJHE,
|
|
2938
|
+
JJME,
|
|
2076
2939
|
JJN,
|
|
2077
2940
|
JJSE,
|
|
2078
2941
|
JJSR,
|
|
2079
2942
|
JJT,
|
|
2080
|
-
ShadowMaster,
|
|
2081
|
-
addLinkPre,
|
|
2082
2943
|
attr2prop,
|
|
2083
|
-
|
|
2084
|
-
cssToStyle,
|
|
2085
|
-
doc,
|
|
2086
|
-
fetchCss,
|
|
2087
|
-
fetchHtml,
|
|
2944
|
+
defineComponent,
|
|
2088
2945
|
fetchStyle,
|
|
2089
|
-
|
|
2090
|
-
fileExt,
|
|
2091
|
-
h,
|
|
2092
|
-
keb2cam,
|
|
2093
|
-
keb2pas,
|
|
2094
|
-
nextAnimationFrame,
|
|
2095
|
-
pas2keb,
|
|
2096
|
-
registerComponent,
|
|
2097
|
-
sleep
|
|
2946
|
+
fetchTemplate
|
|
2098
2947
|
});
|
|
2099
2948
|
//# sourceMappingURL=bundle.cjs.map
|