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