thunderous 2.3.13 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +197 -85
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1152 -5
- package/package.json +54 -70
- package/dist/__test__/custom-element.test.js +0 -48
- package/dist/__test__/registry.test.js +0 -60
- package/dist/__test__/render.test.js +0 -48
- package/dist/__test__/server-side.test.js +0 -196
- package/dist/__test__/signals.test.js +0 -247
- package/dist/__test__/utilities.test.js +0 -8
- package/dist/constants.js +0 -13
- package/dist/custom-element.js +0 -391
- package/dist/registry.js +0 -95
- package/dist/render.js +0 -350
- package/dist/server-side.js +0 -136
- package/dist/signals.js +0 -115
- package/dist/utilities.js +0 -9
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { createEffect, createSignal, derived } from '../signals';
|
|
2
|
-
import { describe, it, beforeEach } from 'node:test';
|
|
3
|
-
import assert from 'assert';
|
|
4
|
-
import { NOOP } from '../utilities';
|
|
5
|
-
await describe('createSignal', async () => {
|
|
6
|
-
await it('sets the initial value', () => {
|
|
7
|
-
const [count] = createSignal(0);
|
|
8
|
-
assert.strictEqual(count(), 0);
|
|
9
|
-
});
|
|
10
|
-
await it('sets a new value', () => {
|
|
11
|
-
const [count, setCount] = createSignal(0);
|
|
12
|
-
setCount(1);
|
|
13
|
-
assert.strictEqual(count(), 1);
|
|
14
|
-
});
|
|
15
|
-
await it('does not recalculate for equal primitives', () => {
|
|
16
|
-
const [count, setCount] = createSignal(0);
|
|
17
|
-
let runCount = 0;
|
|
18
|
-
createEffect(() => {
|
|
19
|
-
count();
|
|
20
|
-
runCount++;
|
|
21
|
-
});
|
|
22
|
-
setCount(0);
|
|
23
|
-
assert.strictEqual(runCount, 1);
|
|
24
|
-
});
|
|
25
|
-
await it('does not recalculate for complex data', () => {
|
|
26
|
-
const [count, setCount] = createSignal({ value: 0 });
|
|
27
|
-
let runCount = 0;
|
|
28
|
-
createEffect(() => {
|
|
29
|
-
count();
|
|
30
|
-
runCount++;
|
|
31
|
-
});
|
|
32
|
-
setCount({ value: 0 });
|
|
33
|
-
assert.strictEqual(runCount, 1);
|
|
34
|
-
});
|
|
35
|
-
await it('runs in debug mode', async (testContext) => {
|
|
36
|
-
await it('adds the label when the signal is created with one', async () => {
|
|
37
|
-
testContext.mock.method(console, 'log', NOOP);
|
|
38
|
-
const logMock = console.log.mock;
|
|
39
|
-
beforeEach(() => logMock.resetCalls());
|
|
40
|
-
const [count, setCount] = createSignal(0, { debugMode: true, label: 'count' });
|
|
41
|
-
await it('does not log when the signal is initially created', () => {
|
|
42
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
43
|
-
});
|
|
44
|
-
await it('logs when the signal getter is run', async () => {
|
|
45
|
-
count();
|
|
46
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
47
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
48
|
-
'Signal retrieved:',
|
|
49
|
-
{ value: 0, subscribers: new Set(), label: '(count)' },
|
|
50
|
-
]);
|
|
51
|
-
});
|
|
52
|
-
await it('logs when the signal setter is run', async () => {
|
|
53
|
-
setCount(1);
|
|
54
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
55
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
56
|
-
'Signal set:',
|
|
57
|
-
{ oldValue: 0, newValue: 1, subscribers: new Set(), label: '(count)' },
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
await it('uses "anonymous signal" when the signal is created without a label', async () => {
|
|
62
|
-
testContext.mock.method(console, 'log', NOOP);
|
|
63
|
-
const logMock = console.log.mock;
|
|
64
|
-
beforeEach(() => logMock.resetCalls());
|
|
65
|
-
const [count, setCount] = createSignal(0, { debugMode: true });
|
|
66
|
-
await it('does not log when the signal is initially created', () => {
|
|
67
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
68
|
-
});
|
|
69
|
-
await it('logs when the signal getter is run', () => {
|
|
70
|
-
count();
|
|
71
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
72
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
73
|
-
'Signal retrieved:',
|
|
74
|
-
{ value: 0, subscribers: new Set(), label: 'anonymous signal' },
|
|
75
|
-
]);
|
|
76
|
-
});
|
|
77
|
-
await it('logs when the signal setter is run', () => {
|
|
78
|
-
setCount(1);
|
|
79
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
80
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
81
|
-
'Signal set:',
|
|
82
|
-
{ oldValue: 0, newValue: 1, subscribers: new Set(), label: 'anonymous signal' },
|
|
83
|
-
]);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
await it('does not log if debugMode is false', async () => {
|
|
87
|
-
testContext.mock.method(console, 'log', NOOP);
|
|
88
|
-
const logMock = console.log.mock;
|
|
89
|
-
beforeEach(() => logMock.resetCalls());
|
|
90
|
-
const [count, setCount] = createSignal(0, { debugMode: false, label: 'count' });
|
|
91
|
-
await it('does not log when the signal is initially created', () => {
|
|
92
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
93
|
-
});
|
|
94
|
-
await it('does not log when the signal getter is run', () => {
|
|
95
|
-
count();
|
|
96
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
97
|
-
});
|
|
98
|
-
await it('does not log when the signal setter is run', () => {
|
|
99
|
-
setCount(1);
|
|
100
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
await it('adds getter and setter labels in addition to the overall signal label', async () => {
|
|
104
|
-
testContext.mock.method(console, 'log', NOOP);
|
|
105
|
-
const logMock = console.log.mock;
|
|
106
|
-
beforeEach(() => logMock.resetCalls());
|
|
107
|
-
const [count, setCount] = createSignal(0, { debugMode: true, label: 'count' });
|
|
108
|
-
await it('does not log when the signal is initially created', () => {
|
|
109
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
110
|
-
});
|
|
111
|
-
await it('logs when the signal getter is run with a label', () => {
|
|
112
|
-
count({ debugMode: true, label: 'getter' });
|
|
113
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
114
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
115
|
-
'Signal retrieved:',
|
|
116
|
-
{ value: 0, subscribers: new Set(), label: '(count) getter' },
|
|
117
|
-
]);
|
|
118
|
-
});
|
|
119
|
-
await it('logs when the signal setter is run with a label', () => {
|
|
120
|
-
setCount(1, { debugMode: true, label: 'setter' });
|
|
121
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
122
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
123
|
-
'Signal set:',
|
|
124
|
-
{ oldValue: 0, newValue: 1, subscribers: new Set(), label: '(count) setter' },
|
|
125
|
-
]);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
await it('adds getter and setter labels instead of the overall signal label', async () => {
|
|
129
|
-
testContext.mock.method(console, 'log', NOOP);
|
|
130
|
-
const logMock = console.log.mock;
|
|
131
|
-
beforeEach(() => logMock.resetCalls());
|
|
132
|
-
const [count, setCount] = createSignal(0);
|
|
133
|
-
await it('does not log when the signal is initially created', () => {
|
|
134
|
-
assert.strictEqual(logMock.callCount(), 0);
|
|
135
|
-
});
|
|
136
|
-
await it('logs when the signal getter is run with a label', () => {
|
|
137
|
-
count({ debugMode: true, label: 'getter' });
|
|
138
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
139
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
140
|
-
'Signal retrieved:',
|
|
141
|
-
{ value: 0, subscribers: new Set(), label: 'getter' },
|
|
142
|
-
]);
|
|
143
|
-
});
|
|
144
|
-
await it('logs when the signal setter is run with a label', () => {
|
|
145
|
-
setCount(1, { debugMode: true, label: 'setter' });
|
|
146
|
-
assert.strictEqual(logMock.callCount(), 1);
|
|
147
|
-
assert.deepStrictEqual(logMock.calls[0].arguments, [
|
|
148
|
-
'Signal set:',
|
|
149
|
-
{ oldValue: 0, newValue: 1, subscribers: new Set(), label: 'setter' },
|
|
150
|
-
]);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
await it('handles errors in subscribers', async (testContext) => {
|
|
154
|
-
testContext.mock.method(console, 'error', NOOP);
|
|
155
|
-
const errorMock = console.error.mock;
|
|
156
|
-
const [count, setCount] = createSignal(0);
|
|
157
|
-
const error = new Error('Test error');
|
|
158
|
-
createEffect(() => {
|
|
159
|
-
if (count() === 1) {
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
setCount(1);
|
|
164
|
-
assert.strictEqual(errorMock.callCount(), 1);
|
|
165
|
-
assert.deepStrictEqual(errorMock.calls[0].arguments, [
|
|
166
|
-
'Error in subscriber:',
|
|
167
|
-
{ error, oldValue: 0, newValue: 1, fn: errorMock.calls[0].arguments[1].fn },
|
|
168
|
-
]);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
await describe('createEffect', async () => {
|
|
173
|
-
await it('runs immediately', () => {
|
|
174
|
-
const [count] = createSignal(0);
|
|
175
|
-
let result;
|
|
176
|
-
createEffect(() => {
|
|
177
|
-
result = count();
|
|
178
|
-
});
|
|
179
|
-
assert.strictEqual(result, 0);
|
|
180
|
-
});
|
|
181
|
-
await it('runs when signals change', () => {
|
|
182
|
-
const [count, setCount] = createSignal(0);
|
|
183
|
-
let result;
|
|
184
|
-
createEffect(() => {
|
|
185
|
-
result = count();
|
|
186
|
-
});
|
|
187
|
-
setCount(1);
|
|
188
|
-
assert.strictEqual(result, 1);
|
|
189
|
-
});
|
|
190
|
-
await it('handles multiple subscribers', () => {
|
|
191
|
-
const [count, setCount] = createSignal(0);
|
|
192
|
-
let result1;
|
|
193
|
-
let result2;
|
|
194
|
-
createEffect(() => {
|
|
195
|
-
result1 = count();
|
|
196
|
-
});
|
|
197
|
-
createEffect(() => {
|
|
198
|
-
result2 = count();
|
|
199
|
-
});
|
|
200
|
-
setCount(1);
|
|
201
|
-
assert.strictEqual(result1, 1);
|
|
202
|
-
assert.strictEqual(result2, 1);
|
|
203
|
-
});
|
|
204
|
-
await it('handles errors in effects', (testContext) => {
|
|
205
|
-
testContext.mock.method(console, 'error', NOOP);
|
|
206
|
-
const errorMock = console.error.mock;
|
|
207
|
-
const error = new Error('Test error');
|
|
208
|
-
createEffect(() => {
|
|
209
|
-
throw error;
|
|
210
|
-
});
|
|
211
|
-
assert.strictEqual(errorMock.callCount(), 1);
|
|
212
|
-
assert.deepStrictEqual(errorMock.calls[0].arguments, [
|
|
213
|
-
'Error in effect:',
|
|
214
|
-
{ error, fn: errorMock.calls[0].arguments[1].fn },
|
|
215
|
-
]);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
await describe('derived', async () => {
|
|
219
|
-
await it('calculates the value immediately', () => {
|
|
220
|
-
const [count] = createSignal(1);
|
|
221
|
-
const doubled = derived(() => count() * 2);
|
|
222
|
-
assert.strictEqual(doubled(), 2);
|
|
223
|
-
});
|
|
224
|
-
await it('recalculates the value upon updating', () => {
|
|
225
|
-
const [count, setCount] = createSignal(1);
|
|
226
|
-
const doubled = derived(() => count() * 2);
|
|
227
|
-
setCount(2);
|
|
228
|
-
assert.strictEqual(doubled(), 4);
|
|
229
|
-
});
|
|
230
|
-
await it('handles errors in derived signals', (testContext) => {
|
|
231
|
-
testContext.mock.method(console, 'error', NOOP);
|
|
232
|
-
const errorMock = console.error.mock;
|
|
233
|
-
const error = new Error('Test error');
|
|
234
|
-
const [count, setCount] = createSignal(1);
|
|
235
|
-
derived(() => {
|
|
236
|
-
if (count() === 2) {
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
setCount(2);
|
|
241
|
-
assert.strictEqual(errorMock.callCount(), 1);
|
|
242
|
-
assert.deepStrictEqual(errorMock.calls[0].arguments, [
|
|
243
|
-
'Error in derived signal:',
|
|
244
|
-
{ error, fn: errorMock.calls[0].arguments[1].fn },
|
|
245
|
-
]);
|
|
246
|
-
});
|
|
247
|
-
});
|
package/dist/constants.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_RENDER_OPTIONS = {
|
|
2
|
-
formAssociated: false,
|
|
3
|
-
observedAttributes: [],
|
|
4
|
-
attributesAsProperties: [],
|
|
5
|
-
attachShadow: true,
|
|
6
|
-
shadowRootOptions: {
|
|
7
|
-
mode: 'closed',
|
|
8
|
-
delegatesFocus: false,
|
|
9
|
-
clonable: false,
|
|
10
|
-
serializable: false,
|
|
11
|
-
slotAssignment: 'named',
|
|
12
|
-
},
|
|
13
|
-
};
|
package/dist/custom-element.js
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_RENDER_OPTIONS } from './constants';
|
|
2
|
-
import { isCSSStyleSheet, renderState } from './render';
|
|
3
|
-
import { isServer, serverDefine } from './server-side';
|
|
4
|
-
import { createEffect, createSignal } from './signals';
|
|
5
|
-
const ANY_BINDING_REGEX = /(\{\{.+:.+\}\})/;
|
|
6
|
-
/**
|
|
7
|
-
* Create a custom element that can be defined for use in the DOM.
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* const MyElement = customElement(() => {
|
|
11
|
-
* return html`<h1>Hello, World!</h1>`;
|
|
12
|
-
* });
|
|
13
|
-
* MyElement.define('my-element');
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export const customElement = (render, options) => {
|
|
17
|
-
const _options = { ...DEFAULT_RENDER_OPTIONS, ...options };
|
|
18
|
-
const { formAssociated, observedAttributes: _observedAttributes, attributesAsProperties, attachShadow, shadowRootOptions: _shadowRootOptions, } = _options;
|
|
19
|
-
const shadowRootOptions = { ...DEFAULT_RENDER_OPTIONS.shadowRootOptions, ..._shadowRootOptions };
|
|
20
|
-
const allOptions = { ..._options, shadowRootOptions };
|
|
21
|
-
if (isServer) {
|
|
22
|
-
const serverRender = render;
|
|
23
|
-
let _tagName;
|
|
24
|
-
let _registry;
|
|
25
|
-
const scopedRegistry = (() => {
|
|
26
|
-
if (shadowRootOptions.registry !== undefined &&
|
|
27
|
-
'scoped' in shadowRootOptions.registry &&
|
|
28
|
-
shadowRootOptions.registry.scoped) {
|
|
29
|
-
return shadowRootOptions.registry;
|
|
30
|
-
}
|
|
31
|
-
})();
|
|
32
|
-
return {
|
|
33
|
-
define(tagName) {
|
|
34
|
-
_tagName = tagName;
|
|
35
|
-
serverDefine({
|
|
36
|
-
tagName,
|
|
37
|
-
serverRender,
|
|
38
|
-
options: allOptions,
|
|
39
|
-
scopedRegistry,
|
|
40
|
-
parentRegistry: _registry,
|
|
41
|
-
elementResult: this,
|
|
42
|
-
});
|
|
43
|
-
return this;
|
|
44
|
-
},
|
|
45
|
-
register(registry) {
|
|
46
|
-
if (_tagName !== undefined && 'eject' in registry && registry.scoped) {
|
|
47
|
-
console.error('Must call `register()` before `define()` for scoped registries.');
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
if ('eject' in registry) {
|
|
51
|
-
_registry = registry;
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.error('Registry must be created with `createRegistry()` for SSR.');
|
|
55
|
-
}
|
|
56
|
-
return this;
|
|
57
|
-
},
|
|
58
|
-
eject() {
|
|
59
|
-
const error = new Error('Cannot eject a custom element on the server.');
|
|
60
|
-
console.error(error);
|
|
61
|
-
throw error;
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
shadowRootOptions.registry = shadowRootOptions.customElements =
|
|
66
|
-
shadowRootOptions.registry instanceof CustomElementRegistry
|
|
67
|
-
? shadowRootOptions.registry
|
|
68
|
-
: shadowRootOptions.registry?.eject();
|
|
69
|
-
// must set observedAttributes prior to defining the class
|
|
70
|
-
const observedAttributesSet = new Set(_observedAttributes);
|
|
71
|
-
const attributesAsPropertiesMap = new Map();
|
|
72
|
-
for (const [attrName, coerce] of attributesAsProperties) {
|
|
73
|
-
observedAttributesSet.add(attrName);
|
|
74
|
-
attributesAsPropertiesMap.set(attrName, {
|
|
75
|
-
// convert kebab-case attribute names to camelCase property names
|
|
76
|
-
prop: attrName.replace(/(?<=-|_)([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/(-|_)/g, ''),
|
|
77
|
-
coerce,
|
|
78
|
-
value: null,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
const observedAttributes = Array.from(observedAttributesSet);
|
|
82
|
-
class CustomElement extends HTMLElement {
|
|
83
|
-
#attributesAsPropertiesMap = new Map(attributesAsPropertiesMap);
|
|
84
|
-
#attrSignals = {};
|
|
85
|
-
#propSignals = {};
|
|
86
|
-
#attributeChangedFns = new Set();
|
|
87
|
-
#connectedFns = new Set();
|
|
88
|
-
#disconnectedFns = new Set();
|
|
89
|
-
#adoptedCallbackFns = new Set();
|
|
90
|
-
#formAssociatedCallbackFns = new Set();
|
|
91
|
-
#formDisabledCallbackFns = new Set();
|
|
92
|
-
#formResetCallbackFns = new Set();
|
|
93
|
-
#formStateRestoreCallbackFns = new Set();
|
|
94
|
-
#clientOnlyCallbackFns = new Set();
|
|
95
|
-
#shadowRoot = attachShadow ? this.attachShadow(shadowRootOptions) : null;
|
|
96
|
-
#internals = this.attachInternals();
|
|
97
|
-
#observer = options?.observedAttributes !== undefined
|
|
98
|
-
? null
|
|
99
|
-
: new MutationObserver((mutations) => {
|
|
100
|
-
for (const mutation of mutations) {
|
|
101
|
-
const attrName = mutation.attributeName;
|
|
102
|
-
if (mutation.type !== 'attributes' || attrName === null)
|
|
103
|
-
continue;
|
|
104
|
-
if (!(attrName in this.#attrSignals))
|
|
105
|
-
this.#attrSignals[attrName] = createSignal(null);
|
|
106
|
-
const [getter, setter] = this.#attrSignals[attrName];
|
|
107
|
-
const oldValue = getter();
|
|
108
|
-
const newValue = this.getAttribute(attrName);
|
|
109
|
-
setter(newValue);
|
|
110
|
-
for (const fn of this.#attributeChangedFns) {
|
|
111
|
-
fn(attrName, oldValue, newValue);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
#getPropSignal = ((prop, { allowUndefined = false } = {}) => {
|
|
116
|
-
if (!(prop in this.#propSignals))
|
|
117
|
-
this.#propSignals[prop] = createSignal();
|
|
118
|
-
const [_getter, __setter] = this.#propSignals[prop];
|
|
119
|
-
let stackLength = 0;
|
|
120
|
-
const _setter = (newValue, options) => {
|
|
121
|
-
stackLength++;
|
|
122
|
-
queueMicrotask(() => stackLength--);
|
|
123
|
-
if (stackLength > 999) {
|
|
124
|
-
console.error(new Error(`Property signal setter stack overflow detected. Possible infinite loop. Bailing out.
|
|
125
|
-
|
|
126
|
-
Property: ${prop}
|
|
127
|
-
|
|
128
|
-
New value: ${JSON.stringify(newValue, null, 2)}
|
|
129
|
-
|
|
130
|
-
Element: <${this.tagName.toLowerCase()}>
|
|
131
|
-
`));
|
|
132
|
-
stackLength = 0;
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
__setter(newValue, options);
|
|
136
|
-
};
|
|
137
|
-
const descriptor = Object.getOwnPropertyDescriptor(this, prop);
|
|
138
|
-
if (descriptor === undefined) {
|
|
139
|
-
Object.defineProperty(this, prop, {
|
|
140
|
-
get: _getter,
|
|
141
|
-
set: _setter,
|
|
142
|
-
configurable: false,
|
|
143
|
-
enumerable: true,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const setter = (newValue, options) => {
|
|
147
|
-
// @ts-expect-error // TODO: look into this
|
|
148
|
-
this[prop] = newValue;
|
|
149
|
-
_setter(newValue, options);
|
|
150
|
-
};
|
|
151
|
-
const getter = ((options) => {
|
|
152
|
-
const value = _getter(options);
|
|
153
|
-
if (value === undefined && !allowUndefined) {
|
|
154
|
-
const error = new Error(`Error accessing property: "${prop}"\nYou must set an initial value before calling a property signal's getter.\n`);
|
|
155
|
-
console.error(error);
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
return value;
|
|
159
|
-
});
|
|
160
|
-
getter.getter = true;
|
|
161
|
-
const publicSignal = [getter, setter];
|
|
162
|
-
publicSignal.init = (value) => {
|
|
163
|
-
_setter(value);
|
|
164
|
-
return [getter, setter];
|
|
165
|
-
};
|
|
166
|
-
return publicSignal;
|
|
167
|
-
}).bind(this);
|
|
168
|
-
#render = (() => {
|
|
169
|
-
const root = this.#shadowRoot ?? this;
|
|
170
|
-
renderState.currentShadowRoot = this.#shadowRoot;
|
|
171
|
-
renderState.registry = shadowRootOptions.customElements ?? customElements;
|
|
172
|
-
const fragment = render({
|
|
173
|
-
elementRef: this,
|
|
174
|
-
root,
|
|
175
|
-
internals: this.#internals,
|
|
176
|
-
attributeChangedCallback: (fn) => this.#attributeChangedFns.add(fn),
|
|
177
|
-
connectedCallback: (fn) => this.#connectedFns.add(fn),
|
|
178
|
-
disconnectedCallback: (fn) => this.#disconnectedFns.add(fn),
|
|
179
|
-
adoptedCallback: (fn) => this.#adoptedCallbackFns.add(fn),
|
|
180
|
-
formAssociatedCallback: (fn) => this.#formAssociatedCallbackFns.add(fn),
|
|
181
|
-
formDisabledCallback: (fn) => this.#formDisabledCallbackFns.add(fn),
|
|
182
|
-
formResetCallback: (fn) => this.#formResetCallbackFns.add(fn),
|
|
183
|
-
formStateRestoreCallback: (fn) => this.#formStateRestoreCallbackFns.add(fn),
|
|
184
|
-
clientOnlyCallback: (fn) => this.#clientOnlyCallbackFns.add(fn),
|
|
185
|
-
getter: (fn) => {
|
|
186
|
-
const _fn = () => fn();
|
|
187
|
-
_fn.getter = true;
|
|
188
|
-
return _fn;
|
|
189
|
-
},
|
|
190
|
-
customCallback: (fn) => {
|
|
191
|
-
const key = crypto.randomUUID();
|
|
192
|
-
this.__customCallbackFns?.set(key, fn);
|
|
193
|
-
return `this.getRootNode().host.__customCallbackFns.get('${key}')(event)`;
|
|
194
|
-
},
|
|
195
|
-
attrSignals: new Proxy({}, {
|
|
196
|
-
get: (_, prop) => {
|
|
197
|
-
if (!(prop in this.#attrSignals))
|
|
198
|
-
this.#attrSignals[prop] = createSignal(null);
|
|
199
|
-
const [getter] = this.#attrSignals[prop];
|
|
200
|
-
const setter = (newValue) => this.setAttribute(prop, newValue);
|
|
201
|
-
return [getter, setter];
|
|
202
|
-
},
|
|
203
|
-
set: () => {
|
|
204
|
-
console.error('Signals must be assigned via setters.');
|
|
205
|
-
return false;
|
|
206
|
-
},
|
|
207
|
-
}),
|
|
208
|
-
propSignals: new Proxy({}, {
|
|
209
|
-
get: (_, prop) => this.#getPropSignal(prop),
|
|
210
|
-
set: () => {
|
|
211
|
-
console.error('Signals must be assigned via setters.');
|
|
212
|
-
return false;
|
|
213
|
-
},
|
|
214
|
-
}),
|
|
215
|
-
refs: new Proxy({}, {
|
|
216
|
-
get: (_, prop) => root.querySelector(`[ref=${prop}]`),
|
|
217
|
-
set: () => {
|
|
218
|
-
console.error('Refs are readonly and cannot be assigned.');
|
|
219
|
-
return false;
|
|
220
|
-
},
|
|
221
|
-
}),
|
|
222
|
-
adoptStyleSheet: (stylesheet) => {
|
|
223
|
-
if (!attachShadow) {
|
|
224
|
-
console.warn('Styles are only encapsulated when using shadow DOM. The stylesheet will be applied to the global document instead.');
|
|
225
|
-
}
|
|
226
|
-
if (isCSSStyleSheet(stylesheet)) {
|
|
227
|
-
if (this.#shadowRoot === null) {
|
|
228
|
-
for (const rule of stylesheet.cssRules) {
|
|
229
|
-
if (rule instanceof CSSStyleRule && rule.selectorText.includes(':host')) {
|
|
230
|
-
console.error('Styles with :host are not supported when not using shadow DOM.');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
document.adoptedStyleSheets.push(stylesheet);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
this.#shadowRoot.adoptedStyleSheets.push(stylesheet);
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
requestAnimationFrame(() => {
|
|
240
|
-
root.appendChild(stylesheet);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
fragment.host = this;
|
|
246
|
-
for (const fn of this.#clientOnlyCallbackFns) {
|
|
247
|
-
fn();
|
|
248
|
-
}
|
|
249
|
-
root.replaceChildren(fragment);
|
|
250
|
-
renderState.currentShadowRoot = null;
|
|
251
|
-
renderState.registry = customElements;
|
|
252
|
-
}).bind(this);
|
|
253
|
-
static get formAssociated() {
|
|
254
|
-
return formAssociated;
|
|
255
|
-
}
|
|
256
|
-
static get observedAttributes() {
|
|
257
|
-
return observedAttributes;
|
|
258
|
-
}
|
|
259
|
-
constructor() {
|
|
260
|
-
try {
|
|
261
|
-
super();
|
|
262
|
-
if (!Object.prototype.hasOwnProperty.call(this, '__customCallbackFns')) {
|
|
263
|
-
this.__customCallbackFns = new Map();
|
|
264
|
-
}
|
|
265
|
-
for (const attr of this.attributes) {
|
|
266
|
-
this.#attrSignals[attr.name] = createSignal(attr.value);
|
|
267
|
-
}
|
|
268
|
-
this.#render();
|
|
269
|
-
}
|
|
270
|
-
catch (error) {
|
|
271
|
-
const _error = new Error('Error instantiating element:\nThis usually occurs if you have errors in the function body of your component. Check prior logs for possible causes.\n', { cause: error });
|
|
272
|
-
console.error(_error);
|
|
273
|
-
throw _error;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
connectedCallback() {
|
|
277
|
-
for (const [attrName, attr] of this.#attributesAsPropertiesMap) {
|
|
278
|
-
if (!(attrName in this.#attrSignals))
|
|
279
|
-
this.#attrSignals[attrName] = createSignal(null);
|
|
280
|
-
const propName = attr.prop;
|
|
281
|
-
const [getter] = this.#getPropSignal(propName, { allowUndefined: true });
|
|
282
|
-
let busy = false;
|
|
283
|
-
createEffect(() => {
|
|
284
|
-
if (busy)
|
|
285
|
-
return;
|
|
286
|
-
busy = true;
|
|
287
|
-
const value = getter();
|
|
288
|
-
if (value === undefined)
|
|
289
|
-
return;
|
|
290
|
-
if (value !== null && value !== false) {
|
|
291
|
-
this.setAttribute(attrName, String(value === true ? '' : value));
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
this.removeAttribute(attrName);
|
|
295
|
-
}
|
|
296
|
-
busy = false;
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
if (this.#observer !== null) {
|
|
300
|
-
this.#observer.observe(this, { attributes: true });
|
|
301
|
-
}
|
|
302
|
-
for (const fn of this.#connectedFns) {
|
|
303
|
-
fn();
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
disconnectedCallback() {
|
|
307
|
-
if (this.#observer !== null) {
|
|
308
|
-
this.#observer.disconnect();
|
|
309
|
-
}
|
|
310
|
-
for (const fn of this.#disconnectedFns) {
|
|
311
|
-
fn();
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
#attributesBusy = false;
|
|
315
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
316
|
-
if (this.#attributesBusy || ANY_BINDING_REGEX.test(newValue ?? ''))
|
|
317
|
-
return;
|
|
318
|
-
const [, attrSetter] = this.#attrSignals[name] ?? [];
|
|
319
|
-
attrSetter?.(newValue);
|
|
320
|
-
const prop = this.#attributesAsPropertiesMap.get(name);
|
|
321
|
-
if (prop !== undefined) {
|
|
322
|
-
const propName = prop.prop;
|
|
323
|
-
this.#attributesBusy = true; // prevent infinite loop
|
|
324
|
-
const [, propSetter] = this.#getPropSignal(propName);
|
|
325
|
-
if (prop.coerce === Boolean) {
|
|
326
|
-
const bool = newValue !== null && newValue !== 'false';
|
|
327
|
-
const propValue = (newValue === null ? null : bool);
|
|
328
|
-
propSetter(propValue);
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
const propValue = (newValue === null ? null : prop.coerce(newValue));
|
|
332
|
-
propSetter(propValue);
|
|
333
|
-
}
|
|
334
|
-
this.#attributesBusy = false;
|
|
335
|
-
}
|
|
336
|
-
for (const fn of this.#attributeChangedFns) {
|
|
337
|
-
fn(name, oldValue, newValue);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
adoptedCallback() {
|
|
341
|
-
for (const fn of this.#adoptedCallbackFns) {
|
|
342
|
-
fn();
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
formAssociatedCallback() {
|
|
346
|
-
for (const fn of this.#formAssociatedCallbackFns) {
|
|
347
|
-
fn();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
formDisabledCallback() {
|
|
351
|
-
for (const fn of this.#formDisabledCallbackFns) {
|
|
352
|
-
fn();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
formResetCallback() {
|
|
356
|
-
for (const fn of this.#formResetCallbackFns) {
|
|
357
|
-
fn();
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
formStateRestoreCallback() {
|
|
361
|
-
for (const fn of this.#formStateRestoreCallbackFns) {
|
|
362
|
-
fn();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
let _tagName;
|
|
367
|
-
let _registry;
|
|
368
|
-
const elementResult = {
|
|
369
|
-
define(tagName, options) {
|
|
370
|
-
const registry = _registry ?? customElements;
|
|
371
|
-
const nativeRegistry = 'eject' in registry ? registry.eject() : registry;
|
|
372
|
-
if (nativeRegistry.get(tagName) !== undefined) {
|
|
373
|
-
console.warn(`Custom element "${tagName}" was already defined. Skipping...`);
|
|
374
|
-
return this;
|
|
375
|
-
}
|
|
376
|
-
registry.define(tagName, CustomElement, options);
|
|
377
|
-
_tagName = tagName;
|
|
378
|
-
return this;
|
|
379
|
-
},
|
|
380
|
-
register(registry) {
|
|
381
|
-
if (_tagName !== undefined && 'eject' in registry && registry.scoped) {
|
|
382
|
-
console.error('Must call `register()` before `define()` for scoped registries.');
|
|
383
|
-
return this;
|
|
384
|
-
}
|
|
385
|
-
_registry = registry;
|
|
386
|
-
return this;
|
|
387
|
-
},
|
|
388
|
-
eject: () => CustomElement,
|
|
389
|
-
};
|
|
390
|
-
return elementResult;
|
|
391
|
-
};
|