@xtia/jel 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/internal/element.js +76 -1
- package/internal/emitter.d.ts +21 -0
- package/internal/emitter.js +25 -4
- package/internal/proxy.d.ts +2 -1
- package/internal/proxy.js +9 -10
- package/internal/types.d.ts +13 -5
- package/package.json +1 -1
package/internal/element.js
CHANGED
|
@@ -97,6 +97,30 @@ export const $ = new Proxy(createElement, {
|
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
|
+
const elementMutationMap = new WeakMap();
|
|
101
|
+
let mutationObserver = null;
|
|
102
|
+
function observeMutations() {
|
|
103
|
+
if (mutationObserver !== null)
|
|
104
|
+
return;
|
|
105
|
+
mutationObserver = new MutationObserver((mutations) => {
|
|
106
|
+
mutations.forEach(mut => {
|
|
107
|
+
mut.addedNodes.forEach(node => {
|
|
108
|
+
if (elementMutationMap.has(node)) {
|
|
109
|
+
elementMutationMap.get(node).add();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
mut.removedNodes.forEach(node => {
|
|
113
|
+
if (elementMutationMap.has(node)) {
|
|
114
|
+
elementMutationMap.get(node).remove();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
mutationObserver.observe(document.body, {
|
|
120
|
+
childList: true,
|
|
121
|
+
subtree: true
|
|
122
|
+
});
|
|
123
|
+
}
|
|
100
124
|
function getWrappedElement(element) {
|
|
101
125
|
if (!elementWrapCache.has(element)) {
|
|
102
126
|
const setCSSVariable = (k, v) => {
|
|
@@ -107,6 +131,57 @@ function getWrappedElement(element) {
|
|
|
107
131
|
element.style.setProperty("--" + k, v);
|
|
108
132
|
}
|
|
109
133
|
};
|
|
134
|
+
const styleListeners = {};
|
|
135
|
+
function addStyleListener(prop, source) {
|
|
136
|
+
const subscribe = "subscribe" in source
|
|
137
|
+
? () => source.subscribe(v => element.style[prop] = v)
|
|
138
|
+
: () => source.listen(v => element.style[prop] = v);
|
|
139
|
+
styleListeners[prop] = {
|
|
140
|
+
subscribe,
|
|
141
|
+
unsubscribe: element.isConnected ? subscribe() : null,
|
|
142
|
+
};
|
|
143
|
+
if (!elementMutationMap.has(element)) {
|
|
144
|
+
elementMutationMap.set(element, {
|
|
145
|
+
add: () => {
|
|
146
|
+
Object.values(styleListeners).forEach(l => { var _a; return l.unsubscribe = (_a = l.subscribe) === null || _a === void 0 ? void 0 : _a.call(l); });
|
|
147
|
+
},
|
|
148
|
+
remove: () => {
|
|
149
|
+
Object.values(styleListeners).forEach(l => {
|
|
150
|
+
var _a;
|
|
151
|
+
(_a = l.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(l);
|
|
152
|
+
l.unsubscribe = null;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
observeMutations();
|
|
158
|
+
}
|
|
159
|
+
function removeStyleListener(prop) {
|
|
160
|
+
if (styleListeners[prop].unsubscribe) {
|
|
161
|
+
styleListeners[prop].unsubscribe();
|
|
162
|
+
}
|
|
163
|
+
delete styleListeners[prop];
|
|
164
|
+
if (Object.keys(styleListeners).length == 0) {
|
|
165
|
+
elementMutationMap.delete(element);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function setStyle(prop, value) {
|
|
169
|
+
if (styleListeners[prop])
|
|
170
|
+
removeStyleListener(prop);
|
|
171
|
+
if (typeof value == "object" && value) {
|
|
172
|
+
if ("listen" in value || "subscribe" in value) {
|
|
173
|
+
addStyleListener(prop, value);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
value = value.toString();
|
|
177
|
+
}
|
|
178
|
+
if (value === undefined) {
|
|
179
|
+
return prop in styleListeners
|
|
180
|
+
? styleListeners[prop].subscribe
|
|
181
|
+
: element.style[prop];
|
|
182
|
+
}
|
|
183
|
+
element.style[prop] = value;
|
|
184
|
+
}
|
|
110
185
|
const domEntity = {
|
|
111
186
|
[entityDataSymbol]: {
|
|
112
187
|
dom: element,
|
|
@@ -210,7 +285,7 @@ function getWrappedElement(element) {
|
|
|
210
285
|
element.setAttribute("name", v);
|
|
211
286
|
}
|
|
212
287
|
},
|
|
213
|
-
style: new Proxy(
|
|
288
|
+
style: new Proxy(setStyle, styleProxy),
|
|
214
289
|
classes: element.classList,
|
|
215
290
|
events: new Proxy(element, eventsProxy)
|
|
216
291
|
};
|
package/internal/emitter.d.ts
CHANGED
|
@@ -74,11 +74,32 @@ export declare class EventEmitter<T> extends Emitter<T> {
|
|
|
74
74
|
throttle(ms: number): EventEmitter<T>;
|
|
75
75
|
batch(ms: number): Emitter<T[]>;
|
|
76
76
|
filter(check: (value: T) => boolean): EventEmitter<T>;
|
|
77
|
+
/**
|
|
78
|
+
* **Experimental**: May change in future revisions
|
|
79
|
+
* Note: potential leak - This link will remain subscribed to the parent
|
|
80
|
+
* until it emits, regardless of subscriptions to this link.
|
|
81
|
+
* @param notifier
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
77
84
|
once(): EventEmitter<T>;
|
|
78
85
|
scan<S>(updater: (state: S, value: T) => S, initial: S): EventEmitter<S>;
|
|
79
86
|
buffer(count: number): EventEmitter<T[]>;
|
|
87
|
+
/**
|
|
88
|
+
* **Experimental**: May change in future revisions
|
|
89
|
+
* Note: potential leak - This link will remain subscribed to the parent
|
|
90
|
+
* until emission limit is reached, regardless of subscriptions to this link.
|
|
91
|
+
* @param notifier
|
|
92
|
+
* @returns
|
|
93
|
+
*/
|
|
80
94
|
take(limit: number): EventEmitter<T>;
|
|
81
95
|
tap(cb: Handler<T>): EventEmitter<T>;
|
|
96
|
+
/**
|
|
97
|
+
* **Experimental**: May change in future revisions
|
|
98
|
+
* Note: potential leak - This link will remain subscribed to the notifier
|
|
99
|
+
* until it emits, regardless of subscriptions to this link.
|
|
100
|
+
* @param notifier
|
|
101
|
+
* @returns
|
|
102
|
+
*/
|
|
82
103
|
takeUntil(notifier: Emitter<any>): Emitter<T>;
|
|
83
104
|
}
|
|
84
105
|
/**
|
package/internal/emitter.js
CHANGED
|
@@ -126,10 +126,10 @@ export class EventEmitter extends Emitter {
|
|
|
126
126
|
return new EventEmitter(listen);
|
|
127
127
|
}
|
|
128
128
|
throttle(ms) {
|
|
129
|
-
let lastTime =
|
|
129
|
+
let lastTime = -Infinity;
|
|
130
130
|
const listen = this.transform((value, emit) => {
|
|
131
|
-
const now =
|
|
132
|
-
if (now
|
|
131
|
+
const now = performance.now();
|
|
132
|
+
if (now >= lastTime + ms) {
|
|
133
133
|
lastTime = now;
|
|
134
134
|
emit(value);
|
|
135
135
|
}
|
|
@@ -156,6 +156,13 @@ export class EventEmitter extends Emitter {
|
|
|
156
156
|
const listen = this.transform((value, emit) => check(value) && emit(value));
|
|
157
157
|
return new EventEmitter(listen);
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* **Experimental**: May change in future revisions
|
|
161
|
+
* Note: potential leak - This link will remain subscribed to the parent
|
|
162
|
+
* until it emits, regardless of subscriptions to this link.
|
|
163
|
+
* @param notifier
|
|
164
|
+
* @returns
|
|
165
|
+
*/
|
|
159
166
|
once() {
|
|
160
167
|
const { emit, listen } = createListenable();
|
|
161
168
|
const unsub = this.apply(v => {
|
|
@@ -183,6 +190,13 @@ export class EventEmitter extends Emitter {
|
|
|
183
190
|
});
|
|
184
191
|
return new EventEmitter(listen);
|
|
185
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* **Experimental**: May change in future revisions
|
|
195
|
+
* Note: potential leak - This link will remain subscribed to the parent
|
|
196
|
+
* until emission limit is reached, regardless of subscriptions to this link.
|
|
197
|
+
* @param notifier
|
|
198
|
+
* @returns
|
|
199
|
+
*/
|
|
186
200
|
take(limit) {
|
|
187
201
|
const { emit, listen } = createListenable();
|
|
188
202
|
let count = 0;
|
|
@@ -204,6 +218,13 @@ export class EventEmitter extends Emitter {
|
|
|
204
218
|
});
|
|
205
219
|
return new EventEmitter(listen);
|
|
206
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* **Experimental**: May change in future revisions
|
|
223
|
+
* Note: potential leak - This link will remain subscribed to the notifier
|
|
224
|
+
* until it emits, regardless of subscriptions to this link.
|
|
225
|
+
* @param notifier
|
|
226
|
+
* @returns
|
|
227
|
+
*/
|
|
207
228
|
takeUntil(notifier) {
|
|
208
229
|
const { emit, listen } = createListenable();
|
|
209
230
|
const unsub = this.apply(v => {
|
|
@@ -213,7 +234,7 @@ export class EventEmitter extends Emitter {
|
|
|
213
234
|
unsub();
|
|
214
235
|
unsubNotifier();
|
|
215
236
|
});
|
|
216
|
-
return new
|
|
237
|
+
return new EventEmitter(listen);
|
|
217
238
|
}
|
|
218
239
|
}
|
|
219
240
|
/**
|
package/internal/proxy.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { SetGetStyleFunc } from "./types";
|
|
2
|
+
export declare const styleProxy: ProxyHandler<SetGetStyleFunc>;
|
|
2
3
|
export declare const attribsProxy: ProxyHandler<HTMLElement>;
|
|
3
4
|
export declare const eventsProxy: ProxyHandler<HTMLElement>;
|
package/internal/proxy.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { EventEmitter } from "./emitter";
|
|
2
2
|
export const styleProxy = {
|
|
3
|
-
get(
|
|
4
|
-
return
|
|
3
|
+
get(style, prop) {
|
|
4
|
+
return style(prop);
|
|
5
5
|
},
|
|
6
|
-
set(
|
|
7
|
-
|
|
6
|
+
set(style, prop, value) {
|
|
7
|
+
style(prop, value);
|
|
8
8
|
return true;
|
|
9
9
|
},
|
|
10
|
-
apply(
|
|
11
|
-
const style = getStyle();
|
|
10
|
+
apply(style, _, [stylesOrProp, value]) {
|
|
12
11
|
if (typeof stylesOrProp == "object") {
|
|
13
|
-
Object.entries(stylesOrProp).forEach(([prop, val]) => style
|
|
12
|
+
Object.entries(stylesOrProp).forEach(([prop, val]) => style(prop, val));
|
|
14
13
|
return;
|
|
15
14
|
}
|
|
16
|
-
style
|
|
15
|
+
style(stylesOrProp, value);
|
|
17
16
|
},
|
|
18
|
-
deleteProperty(
|
|
19
|
-
|
|
17
|
+
deleteProperty(style, prop) {
|
|
18
|
+
style(prop, null);
|
|
20
19
|
return true;
|
|
21
20
|
}
|
|
22
21
|
};
|
package/internal/types.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { EventEmitter } from "./emitter";
|
|
1
|
+
import { EventEmitter, UnsubscribeFunc } from "./emitter";
|
|
2
2
|
import { entityDataSymbol } from "./util";
|
|
3
3
|
export type ElementClassDescriptor = string | Record<string, boolean | undefined> | undefined | ElementClassDescriptor[];
|
|
4
4
|
export type DOMContent = number | null | string | Element | JelEntity<object> | Text | DOMContent[];
|
|
5
5
|
export type DomEntity<T extends HTMLElement> = JelEntity<ElementAPI<T>>;
|
|
6
|
-
type
|
|
6
|
+
export type ReactiveSource<T> = ({
|
|
7
|
+
listen: (handler: (value: T) => void) => UnsubscribeFunc;
|
|
8
|
+
} | {
|
|
9
|
+
subscribe: (handler: (value: T) => void) => UnsubscribeFunc;
|
|
10
|
+
});
|
|
11
|
+
export type CSSValue = string | number | null | HexCodeContainer;
|
|
12
|
+
export type CSSProperty = keyof StylesDescriptor;
|
|
7
13
|
type HexCodeContainer = {
|
|
8
14
|
hexCode: string;
|
|
9
15
|
toString(): string;
|
|
@@ -12,9 +18,11 @@ export type StylesDescriptor = {
|
|
|
12
18
|
[K in keyof CSSStyleDeclaration as [
|
|
13
19
|
K,
|
|
14
20
|
CSSStyleDeclaration[K]
|
|
15
|
-
] extends [string, string] ? K : never]+?: CSSValue
|
|
21
|
+
] extends [string, string] ? K : never]+?: CSSValue | ReactiveSource<CSSValue>;
|
|
16
22
|
};
|
|
17
|
-
export type
|
|
23
|
+
export type SetStyleFunc = ((property: CSSProperty, value: CSSValue | ReactiveSource<CSSValue>) => void);
|
|
24
|
+
export type SetGetStyleFunc = SetStyleFunc & ((property: CSSProperty) => string | ReactiveSource<CSSValue>);
|
|
25
|
+
export type StyleAccessor = ((styles: StylesDescriptor) => void) & StylesDescriptor & SetStyleFunc;
|
|
18
26
|
type ContentlessTag = "area" | "br" | "hr" | "iframe" | "input" | "textarea" | "img" | "canvas" | "link" | "meta" | "source" | "embed" | "track" | "base";
|
|
19
27
|
type TagWithHref = "a" | "link" | "base";
|
|
20
28
|
type TagWithSrc = "img" | "script" | "iframe" | "video" | "audio" | "embed" | "source" | "track";
|
|
@@ -34,7 +42,7 @@ export type ElementDescriptor<Tag extends string> = {
|
|
|
34
42
|
} & (Tag extends TagWithValue ? {
|
|
35
43
|
value?: string | number;
|
|
36
44
|
} : {}) & (Tag extends ContentlessTag ? {} : {
|
|
37
|
-
content?: DOMContent
|
|
45
|
+
content?: DOMContent | ReactiveSource<DOMContent>;
|
|
38
46
|
}) & (Tag extends TagWithSrc ? {
|
|
39
47
|
src?: string;
|
|
40
48
|
} : {}) & (Tag extends TagWithHref ? {
|