@dinoreic/fez 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -17
- package/dist/fez.js +11 -11
- package/dist/fez.js.map +4 -4
- package/package.json +13 -13
- package/src/fez/connect.js +96 -61
- package/src/fez/defaults.js +0 -5
- package/src/fez/instance.js +76 -91
- package/src/fez/root.js +32 -152
- package/src/fez/utility.js +184 -0
- package/src/fez.js +2 -2
- package/src/rollup.js +73 -16
- package/dist/rollup.js +0 -3
- package/dist/rollup.js.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dinoreic/fez",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Runtime custom dom elements",
|
|
5
5
|
"main": "dist/fez.js",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"./package.json": "./package.json"
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
|
-
"fez": "
|
|
23
|
+
"fez": "bin/fez"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"bin",
|
|
@@ -29,17 +29,6 @@
|
|
|
29
29
|
"README.md",
|
|
30
30
|
"LICENSE"
|
|
31
31
|
],
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "bun build.js b",
|
|
34
|
-
"b": "bun run build",
|
|
35
|
-
"watch": "bun build.js w",
|
|
36
|
-
"server": "bun run lib/server.js",
|
|
37
|
-
"dev": "bunx concurrently --kill-others \"bun run server\" \"find src demo lib | entr -c sh -c 'bun run index && bun run b'\"",
|
|
38
|
-
"test": "bun test",
|
|
39
|
-
"prepublishOnly": "bun run build && bun run test",
|
|
40
|
-
"publish": "npm publish --access public",
|
|
41
|
-
"index": "ruby ./bin/fez-index 'demo/fez/*.fez' > demo/fez/index.json"
|
|
42
|
-
},
|
|
43
32
|
"keywords": [
|
|
44
33
|
"dom",
|
|
45
34
|
"elements",
|
|
@@ -66,5 +55,16 @@
|
|
|
66
55
|
"happy-dom": "^18.0.1",
|
|
67
56
|
"jsdom": "^26.1.0",
|
|
68
57
|
"mime": "^4.0.7"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "bun build.js b",
|
|
61
|
+
"b": "bun run build",
|
|
62
|
+
"watch": "bun build.js w",
|
|
63
|
+
"server": "bun run lib/server.js",
|
|
64
|
+
"dev": "bunx concurrently --kill-others \"bun run server\" \"find src demo lib | entr -cn sh -c 'bun run index && bun run b'\"",
|
|
65
|
+
"test": "bun test",
|
|
66
|
+
"prepublishOnly": "bun run build && bun run test",
|
|
67
|
+
"publish": "npm publish --access public",
|
|
68
|
+
"index": "ruby ./bin/fez-index 'demo/fez/*.fez' > demo/fez/index.json"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/src/fez/connect.js
CHANGED
|
@@ -1,94 +1,122 @@
|
|
|
1
|
-
// templating
|
|
2
1
|
import createTemplate from './lib/template.js'
|
|
3
2
|
import FezBase from './instance.js'
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Registers a new custom element with Fez framework
|
|
6
|
+
* @param {string} name - Custom element name (must contain a dash)
|
|
7
|
+
* @param {Class|Object} klass - Component class or configuration object
|
|
8
|
+
* @example
|
|
9
|
+
* Fez('my-component', class {
|
|
10
|
+
* HTML = '<div>Hello World</div>'
|
|
11
|
+
* CSS = '.my-component { color: blue; }'
|
|
12
|
+
* })
|
|
13
|
+
*/
|
|
14
|
+
export default function connect(name, klass) {
|
|
8
15
|
const Fez = globalThis.window?.Fez || globalThis.Fez;
|
|
9
16
|
// Validate custom element name format (must contain a dash)
|
|
10
17
|
if (!name.includes('-')) {
|
|
11
18
|
console.error(`Fez: Invalid custom element name "${name}". Custom element names must contain a dash (e.g., 'my-element', 'ui-button').`)
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
//
|
|
15
|
-
// Fez('ui-todo', class { ... # instead Fez('ui-todo', class extends FezBase {
|
|
21
|
+
// Transform simple class definitions into Fez components
|
|
16
22
|
if (!klass.fezHtmlRoot) {
|
|
17
23
|
const klassObj = new klass()
|
|
18
24
|
const newKlass = class extends FezBase {}
|
|
19
25
|
|
|
26
|
+
// Copy all properties and methods from the original class
|
|
20
27
|
const props = Object.getOwnPropertyNames(klassObj)
|
|
21
28
|
.concat(Object.getOwnPropertyNames(klass.prototype))
|
|
22
29
|
.filter(el => !['constructor', 'prototype'].includes(el))
|
|
23
30
|
|
|
24
31
|
props.forEach(prop => newKlass.prototype[prop] = klassObj[prop])
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (klassObj.
|
|
29
|
-
if (klassObj.
|
|
30
|
-
|
|
31
|
-
if (klassObj.NAME) { newKlass.nodeName = klassObj.NAME }
|
|
32
|
-
if (klassObj.FAST) {
|
|
33
|
-
newKlass.fastBind = klassObj.FAST
|
|
34
|
-
Fez.fastBindInfo.fast.push(typeof klassObj.FAST == 'function' ? `${name} (func)` : name)
|
|
35
|
-
} else {
|
|
36
|
-
Fez.fastBindInfo.slow.push(name)
|
|
33
|
+
// Map component configuration properties
|
|
34
|
+
if (klassObj.GLOBAL) { newKlass.fezGlobal = klassObj.GLOBAL } // Global instance reference
|
|
35
|
+
if (klassObj.CSS) { newKlass.css = klassObj.CSS } // Component styles
|
|
36
|
+
if (klassObj.HTML) {
|
|
37
|
+
newKlass.html = closeCustomTags(klassObj.HTML) // Component template
|
|
37
38
|
}
|
|
39
|
+
if (klassObj.NAME) { newKlass.nodeName = klassObj.NAME } // Custom DOM node name
|
|
38
40
|
|
|
41
|
+
// Auto-mount global components to body
|
|
39
42
|
if (klassObj.GLOBAL) {
|
|
40
|
-
const
|
|
43
|
+
const mountGlobalComponent = () => document.body.appendChild(document.createElement(name))
|
|
41
44
|
|
|
42
45
|
if (document.readyState === 'loading') {
|
|
43
|
-
document.addEventListener('DOMContentLoaded',
|
|
46
|
+
document.addEventListener('DOMContentLoaded', mountGlobalComponent);
|
|
44
47
|
} else {
|
|
45
|
-
|
|
48
|
+
mountGlobalComponent()
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
klass = newKlass
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
Fez.log(`${name} compiled`)
|
|
55
|
+
} else if (klass.html) {
|
|
56
|
+
// If klass already has html property, process it
|
|
57
|
+
klass.html = closeCustomTags(klass.html)
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
// Process component template
|
|
56
61
|
if (klass.html) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// wrap slot to enable reactive re-renders. It will use existing .fez-slot if found
|
|
62
|
+
// Replace <slot /> with reactive slot containers
|
|
60
63
|
klass.html = klass.html.replace(/<slot\s*\/>|<slot\s*>\s*<\/slot>/g, () => {
|
|
61
|
-
const
|
|
62
|
-
return `<${
|
|
64
|
+
const slotTag = klass.SLOT || 'div'
|
|
65
|
+
return `<${slotTag} class="fez-slot" fez-keep="default-slot"></${slotTag}>`
|
|
63
66
|
})
|
|
64
67
|
|
|
68
|
+
// Compile template function
|
|
65
69
|
klass.fezHtmlFunc = createTemplate(klass.html)
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
//
|
|
72
|
+
// Register component styles globally (available to all components)
|
|
69
73
|
if (klass.css) {
|
|
70
74
|
klass.css = Fez.globalCss(klass.css, {name: name})
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
Fez.classes[name] = klass
|
|
74
78
|
|
|
79
|
+
connectCustomElement(name, klass)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Registers the custom element with the browser
|
|
84
|
+
* Sets up batched rendering for optimal performance
|
|
85
|
+
*/
|
|
86
|
+
function connectCustomElement(name, klass) {
|
|
87
|
+
const Fez = globalThis.window?.Fez || globalThis.Fez;
|
|
88
|
+
|
|
75
89
|
if (!customElements.get(name)) {
|
|
76
90
|
customElements.define(name, class extends HTMLElement {
|
|
77
91
|
connectedCallback() {
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
// Batch all renders using microtasks for consistent timing and DOM completeness
|
|
93
|
+
if (!Fez._pendingConnections) {
|
|
94
|
+
Fez._pendingConnections = []
|
|
95
|
+
Fez._batchScheduled = false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Fez._pendingConnections.push({ name, node: this })
|
|
99
|
+
|
|
100
|
+
if (!Fez._batchScheduled) {
|
|
101
|
+
Fez._batchScheduled = true
|
|
102
|
+
Promise.resolve().then(() => {
|
|
103
|
+
const connections = Fez._pendingConnections.slice()
|
|
104
|
+
// console.error(`Batch processing ${connections.length} components:`, connections.map(c => c.name))
|
|
105
|
+
Fez._pendingConnections = []
|
|
106
|
+
Fez._batchScheduled = false
|
|
107
|
+
|
|
108
|
+
// Sort by DOM order to ensure parent nodes are processed before children
|
|
109
|
+
connections.sort((a, b) => {
|
|
110
|
+
if (a.node.contains(b.node)) return -1
|
|
111
|
+
if (b.node.contains(a.node)) return 1
|
|
112
|
+
return 0
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
connections.forEach(({ name, node }) => {
|
|
116
|
+
if (node.isConnected && node.parentNode) {
|
|
117
|
+
connectNode(name, node)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
92
120
|
})
|
|
93
121
|
}
|
|
94
122
|
}
|
|
@@ -96,8 +124,10 @@ export default function(name, klass) {
|
|
|
96
124
|
}
|
|
97
125
|
}
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Converts self-closing custom tags to full open/close format
|
|
129
|
+
* Required for proper HTML parsing of custom elements
|
|
130
|
+
*/
|
|
101
131
|
function closeCustomTags(html) {
|
|
102
132
|
const selfClosingTags = new Set([
|
|
103
133
|
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr'
|
|
@@ -108,17 +138,11 @@ function closeCustomTags(html) {
|
|
|
108
138
|
})
|
|
109
139
|
}
|
|
110
140
|
|
|
111
|
-
function useFastRender(n, klass) {
|
|
112
|
-
const fezFast = n.getAttribute('fez-fast')
|
|
113
|
-
var isFast = typeof klass.fastBind === 'function' ? klass.fastBind(n) : klass.fastBind
|
|
114
|
-
|
|
115
|
-
if (fezFast == 'false') {
|
|
116
|
-
return false
|
|
117
|
-
} else {
|
|
118
|
-
return fezFast || isFast
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Initializes a Fez component instance from a DOM node
|
|
144
|
+
* Replaces the custom element with the component's rendered content
|
|
145
|
+
*/
|
|
122
146
|
function connectNode(name, node) {
|
|
123
147
|
const klass = Fez.classes[name]
|
|
124
148
|
const parentNode = node.parentNode
|
|
@@ -143,7 +167,7 @@ function connectNode(name, node) {
|
|
|
143
167
|
fez.props = klass.getProps(node, newNode)
|
|
144
168
|
fez.class = klass
|
|
145
169
|
|
|
146
|
-
//
|
|
170
|
+
// Move child nodes to preserve DOM event listeners
|
|
147
171
|
fez.slot(node, newNode)
|
|
148
172
|
|
|
149
173
|
newNode.fez = fez
|
|
@@ -160,10 +184,17 @@ function connectNode(name, node) {
|
|
|
160
184
|
newNode.setAttribute('id', fez.props.id)
|
|
161
185
|
}
|
|
162
186
|
|
|
163
|
-
|
|
164
|
-
|
|
187
|
+
// Component lifecycle initialization
|
|
188
|
+
fez.fezRegister()
|
|
189
|
+
|
|
190
|
+
// Call initialization method (init, created, or connect)
|
|
191
|
+
;(fez.init || fez.created || fez.connect).bind(fez)(fez.props)
|
|
192
|
+
|
|
193
|
+
// Initial render
|
|
165
194
|
fez.render()
|
|
166
195
|
fez.firstRender = true
|
|
196
|
+
|
|
197
|
+
// Trigger mount lifecycle hook
|
|
167
198
|
fez.onMount(fez.props)
|
|
168
199
|
|
|
169
200
|
if (fez.onSubmit) {
|
|
@@ -174,9 +205,11 @@ function connectNode(name, node) {
|
|
|
174
205
|
}
|
|
175
206
|
}
|
|
176
207
|
|
|
177
|
-
//
|
|
208
|
+
// Set up reactive attribute watching
|
|
178
209
|
if (fez.onPropsChange) {
|
|
179
210
|
observer.observe(newNode, {attributes:true})
|
|
211
|
+
|
|
212
|
+
// Trigger initial prop change callbacks
|
|
180
213
|
for (const [key, value] of Object.entries(fez.props)) {
|
|
181
214
|
fez.onPropsChange(key, value)
|
|
182
215
|
}
|
|
@@ -184,8 +217,10 @@ function connectNode(name, node) {
|
|
|
184
217
|
}
|
|
185
218
|
}
|
|
186
219
|
|
|
187
|
-
|
|
188
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Global mutation observer for reactive attribute changes
|
|
222
|
+
* Watches for attribute changes and triggers component updates
|
|
223
|
+
*/
|
|
189
224
|
const observer = new MutationObserver((mutationsList, _) => {
|
|
190
225
|
for (const mutation of mutationsList) {
|
|
191
226
|
if (mutation.type === 'attributes') {
|
package/src/fez/defaults.js
CHANGED
|
@@ -3,8 +3,6 @@ const loadDefaults = () => {
|
|
|
3
3
|
// include fez component by name
|
|
4
4
|
//<fez-component name="some-node" :props="fez.props"></fez-component>
|
|
5
5
|
Fez('fez-component', class {
|
|
6
|
-
FAST = true
|
|
7
|
-
|
|
8
6
|
init(props) {
|
|
9
7
|
const tag = document.createElement(props.name)
|
|
10
8
|
tag.props = props.props || props['data-props'] || props
|
|
@@ -21,8 +19,6 @@ const loadDefaults = () => {
|
|
|
21
19
|
// include remote data from url
|
|
22
20
|
// <fez-include src="./demo/fez/ui-slider.html"></fez-include>
|
|
23
21
|
Fez('fez-include', class {
|
|
24
|
-
FAST = true
|
|
25
|
-
|
|
26
22
|
init(props) {
|
|
27
23
|
Fez.fetch(props.src, (data)=>{
|
|
28
24
|
const dom = Fez.domRoot(data)
|
|
@@ -45,7 +41,6 @@ const loadDefaults = () => {
|
|
|
45
41
|
const hash = Fez.fnv1(this.root.outerHTML)
|
|
46
42
|
const nodeName = `inline-${hash}`
|
|
47
43
|
Fez(nodeName, class {
|
|
48
|
-
FAST = true
|
|
49
44
|
HTML = html
|
|
50
45
|
init() {
|
|
51
46
|
Object.assign(this.state, props.state || {})
|
package/src/fez/instance.js
CHANGED
|
@@ -81,11 +81,6 @@ export default class FezBase {
|
|
|
81
81
|
return formObject
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
static fastBind() {
|
|
85
|
-
// return true to bind without requestAnimationFrame
|
|
86
|
-
// you can do this if you are sure you are not expecting innerHTML data
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
84
|
|
|
90
85
|
static nodeName = 'div'
|
|
91
86
|
|
|
@@ -106,42 +101,11 @@ export default class FezBase {
|
|
|
106
101
|
if (this.root?.isConnected) {
|
|
107
102
|
return true
|
|
108
103
|
} else {
|
|
109
|
-
this.
|
|
104
|
+
this.fezOnDestroy()
|
|
110
105
|
return false
|
|
111
106
|
}
|
|
112
107
|
}
|
|
113
108
|
|
|
114
|
-
// clear all node references
|
|
115
|
-
fezRemoveSelf() {
|
|
116
|
-
this._setIntervalCache ||= {}
|
|
117
|
-
Object.keys(this._setIntervalCache).forEach((key)=> {
|
|
118
|
-
clearInterval(this._setIntervalCache[key])
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
if (this._eventHandlers) {
|
|
122
|
-
Object.entries(this._eventHandlers).forEach(([eventName, handler]) => {
|
|
123
|
-
window.removeEventListener(eventName, handler);
|
|
124
|
-
});
|
|
125
|
-
this._eventHandlers = {};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (this._timeouts) {
|
|
129
|
-
Object.values(this._timeouts).forEach(timeoutId => {
|
|
130
|
-
clearTimeout(timeoutId);
|
|
131
|
-
});
|
|
132
|
-
this._timeouts = {};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.onDestroy()
|
|
136
|
-
this.onDestroy = () => {}
|
|
137
|
-
|
|
138
|
-
if (this.root) {
|
|
139
|
-
this.root.fez = undefined
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this.root = undefined
|
|
143
|
-
}
|
|
144
|
-
|
|
145
109
|
// get single node property
|
|
146
110
|
prop(name) {
|
|
147
111
|
let v = this.oldRoot[name] || this.props[name]
|
|
@@ -178,80 +142,92 @@ export default class FezBase {
|
|
|
178
142
|
}
|
|
179
143
|
}
|
|
180
144
|
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
145
|
+
// clear all node references
|
|
146
|
+
// Centralized destroy logic
|
|
147
|
+
fezOnDestroy() {
|
|
148
|
+
// Execute all registered cleanup callbacks
|
|
149
|
+
if (this._onDestroyCallbacks) {
|
|
150
|
+
this._onDestroyCallbacks.forEach(callback => {
|
|
151
|
+
try {
|
|
152
|
+
callback();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error('Fez: Error in cleanup callback:', e);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
this._onDestroyCallbacks = [];
|
|
191
158
|
}
|
|
192
159
|
|
|
193
|
-
|
|
194
|
-
|
|
160
|
+
// Call user's onDestroy lifecycle hook
|
|
161
|
+
this.onDestroy()
|
|
162
|
+
this.onDestroy = () => {}
|
|
163
|
+
|
|
164
|
+
// Clean up root references
|
|
165
|
+
if (this.root) {
|
|
166
|
+
this.root.fez = undefined
|
|
195
167
|
}
|
|
196
168
|
|
|
197
|
-
|
|
169
|
+
this.root = undefined
|
|
170
|
+
}
|
|
198
171
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
if (this._timeouts[eventName]) {
|
|
206
|
-
clearTimeout(this._timeouts[eventName]);
|
|
207
|
-
delete this._timeouts[eventName];
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
func.call(this);
|
|
212
|
-
return true;
|
|
213
|
-
};
|
|
172
|
+
// Add a cleanup callback to be executed on destroy
|
|
173
|
+
addOnDestroy(callback) {
|
|
174
|
+
this._onDestroyCallbacks = this._onDestroyCallbacks || [];
|
|
175
|
+
this._onDestroyCallbacks.push(callback);
|
|
176
|
+
}
|
|
214
177
|
|
|
215
|
-
|
|
216
|
-
|
|
178
|
+
// Generic function to handle window events with automatic cleanup
|
|
179
|
+
on(eventName, func, delay = 200) {
|
|
180
|
+
this._eventHandlers = this._eventHandlers || {};
|
|
217
181
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
} else {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
182
|
+
if (this._eventHandlers[eventName]) {
|
|
183
|
+
window.removeEventListener(eventName, this._eventHandlers[eventName]);
|
|
184
|
+
}
|
|
225
185
|
|
|
226
|
-
|
|
227
|
-
if (this.
|
|
228
|
-
|
|
186
|
+
const throttledFunc = Fez.throttle(() => {
|
|
187
|
+
if (this.isConnected) {
|
|
188
|
+
func.call(this);
|
|
229
189
|
}
|
|
190
|
+
}, delay);
|
|
230
191
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
lastRun = Date.now();
|
|
234
|
-
}
|
|
235
|
-
delete this._timeouts[eventName];
|
|
236
|
-
}, delay);
|
|
237
|
-
};
|
|
192
|
+
this._eventHandlers[eventName] = throttledFunc;
|
|
193
|
+
window.addEventListener(eventName, throttledFunc);
|
|
238
194
|
|
|
239
|
-
this.
|
|
240
|
-
|
|
195
|
+
this.addOnDestroy(() => {
|
|
196
|
+
window.removeEventListener(eventName, throttledFunc);
|
|
197
|
+
delete this._eventHandlers[eventName];
|
|
198
|
+
});
|
|
241
199
|
}
|
|
242
200
|
|
|
243
201
|
// Helper function for resize events
|
|
244
|
-
|
|
202
|
+
onWindowResize(func, delay) {
|
|
245
203
|
this.on('resize', func, delay);
|
|
246
204
|
func();
|
|
247
205
|
}
|
|
248
206
|
|
|
249
207
|
// Helper function for scroll events
|
|
250
|
-
|
|
208
|
+
onWindowScroll(func, delay) {
|
|
251
209
|
this.on('scroll', func, delay);
|
|
252
210
|
func();
|
|
253
211
|
}
|
|
254
212
|
|
|
213
|
+
// Helper function for element resize events using ResizeObserver
|
|
214
|
+
onElementResize(el, func, delay = 200) {
|
|
215
|
+
const throttledFunc = Fez.throttle(() => {
|
|
216
|
+
if (this.isConnected) {
|
|
217
|
+
func.call(this, el.getBoundingClientRect(), el);
|
|
218
|
+
}
|
|
219
|
+
}, delay);
|
|
220
|
+
|
|
221
|
+
const observer = new ResizeObserver(throttledFunc);
|
|
222
|
+
observer.observe(el);
|
|
223
|
+
|
|
224
|
+
func.call(this, el.getBoundingClientRect(), el);
|
|
225
|
+
|
|
226
|
+
this.addOnDestroy(() => {
|
|
227
|
+
observer.disconnect();
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
255
231
|
// copy child nodes, natively to preserve bound events
|
|
256
232
|
// if node name is SLOT insert adjacent and remove SLOT, else as a child nodes
|
|
257
233
|
slot(source, target) {
|
|
@@ -359,7 +335,8 @@ export default class FezBase {
|
|
|
359
335
|
|
|
360
336
|
this.beforeRender()
|
|
361
337
|
|
|
362
|
-
const
|
|
338
|
+
const nodeName = typeof this.class.nodeName == 'function' ? this.class.nodeName(this.root) : this.class.nodeName
|
|
339
|
+
const newNode = document.createElement(nodeName || 'div')
|
|
363
340
|
|
|
364
341
|
let renderedTpl
|
|
365
342
|
if (Array.isArray(template)) {
|
|
@@ -486,13 +463,21 @@ export default class FezBase {
|
|
|
486
463
|
this._setIntervalCache ||= {}
|
|
487
464
|
clearInterval(this._setIntervalCache[name])
|
|
488
465
|
|
|
489
|
-
|
|
466
|
+
const intervalID = setInterval(() => {
|
|
490
467
|
if (this.isConnected) {
|
|
491
468
|
func()
|
|
492
469
|
}
|
|
493
470
|
}, tick)
|
|
494
471
|
|
|
495
|
-
|
|
472
|
+
this._setIntervalCache[name] = intervalID
|
|
473
|
+
|
|
474
|
+
// Register cleanup callback
|
|
475
|
+
this.addOnDestroy(() => {
|
|
476
|
+
clearInterval(intervalID);
|
|
477
|
+
delete this._setIntervalCache[name];
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return intervalID
|
|
496
481
|
}
|
|
497
482
|
|
|
498
483
|
find(selector) {
|