@pfern/elements 0.1.7 → 0.1.9
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/elements.js +53 -7
- package/package.json +1 -1
- package/types/elements.d.ts +9 -0
package/elements.js
CHANGED
|
@@ -24,9 +24,10 @@ const svgNS = 'http://www.w3.org/2000/svg'
|
|
|
24
24
|
*/
|
|
25
25
|
const rootMap = new WeakMap()
|
|
26
26
|
|
|
27
|
-
const isNodeEnv = typeof document === 'undefined'
|
|
27
|
+
const isNodeEnv = () => typeof document === 'undefined'
|
|
28
28
|
|
|
29
29
|
let componentUpdateDepth = 0
|
|
30
|
+
let currentEventRoot = null
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Determines whether two nodes have changed enough to require replacement.
|
|
@@ -99,6 +100,8 @@ const assignProperties = (el, props) =>
|
|
|
99
100
|
while (target && !target.__root) target = target.parentNode
|
|
100
101
|
if (!target) return
|
|
101
102
|
|
|
103
|
+
const prevEventRoot = currentEventRoot
|
|
104
|
+
currentEventRoot = target
|
|
102
105
|
try {
|
|
103
106
|
const event = args[0]
|
|
104
107
|
const isFormEvent = /^(oninput|onsubmit|onchange)$/.test(key)
|
|
@@ -142,6 +145,8 @@ const assignProperties = (el, props) =>
|
|
|
142
145
|
}
|
|
143
146
|
} catch (error) {
|
|
144
147
|
console.error(error)
|
|
148
|
+
} finally {
|
|
149
|
+
currentEventRoot = prevEventRoot
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
152
|
} else if (key === 'style' && typeof value === 'object') {
|
|
@@ -174,7 +179,7 @@ const assignProperties = (el, props) =>
|
|
|
174
179
|
*/
|
|
175
180
|
const renderTree = (node, isRoot = true) => {
|
|
176
181
|
if (typeof node === 'string' || typeof node === 'number') {
|
|
177
|
-
return isNodeEnv ? node : document.createTextNode(node)
|
|
182
|
+
return isNodeEnv() ? node : document.createTextNode(node)
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
if (!node || node.length === 0) {
|
|
@@ -213,6 +218,11 @@ const renderTree = (node, isRoot = true) => {
|
|
|
213
218
|
? document.createElementNS(svgNS, tag)
|
|
214
219
|
: document.createElement(tag)
|
|
215
220
|
|
|
221
|
+
if (!el && (tag === 'head' || tag === 'body')) {
|
|
222
|
+
el = document.createElement(tag)
|
|
223
|
+
document.documentElement.appendChild(el)
|
|
224
|
+
}
|
|
225
|
+
|
|
216
226
|
el.__vnode = node
|
|
217
227
|
|
|
218
228
|
if (isRoot && tag !== 'html' && tag !== 'head' && tag !== 'body') {
|
|
@@ -284,7 +294,9 @@ export const render = (vtree, container = null) => {
|
|
|
284
294
|
if (!prevVNode) {
|
|
285
295
|
const dom = renderTree(vtree)
|
|
286
296
|
if (target === document.documentElement) {
|
|
287
|
-
|
|
297
|
+
if (dom !== document.documentElement) {
|
|
298
|
+
document.replaceChild(dom, document.documentElement)
|
|
299
|
+
}
|
|
288
300
|
} else {
|
|
289
301
|
target.appendChild(dom)
|
|
290
302
|
}
|
|
@@ -297,6 +309,34 @@ export const render = (vtree, container = null) => {
|
|
|
297
309
|
rootMap.set(vtree, target)
|
|
298
310
|
}
|
|
299
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Updates the browser URL via the History API (no full page load).
|
|
314
|
+
* No-ops outside the browser.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} to - The target URL (path/search/hash).
|
|
317
|
+
* @param {Object} [options]
|
|
318
|
+
* @param {boolean} [options.replace=false] - Use replaceState instead of pushState.
|
|
319
|
+
* @param {boolean} [options.force=false] - Update even if URL is already `to`.
|
|
320
|
+
* @param {any} [options.state={}] - History state.
|
|
321
|
+
* @param {string} [options.title=''] - History title (mostly ignored by browsers).
|
|
322
|
+
*/
|
|
323
|
+
export const navigate = (to, {
|
|
324
|
+
replace = false,
|
|
325
|
+
force = false,
|
|
326
|
+
state = {},
|
|
327
|
+
title = ''
|
|
328
|
+
} = {}) => {
|
|
329
|
+
if (typeof window === 'undefined') return
|
|
330
|
+
if (typeof to !== 'string' || !to.length) return
|
|
331
|
+
|
|
332
|
+
const { pathname, search, hash } = window.location
|
|
333
|
+
const current = `${pathname}${search}${hash}`
|
|
334
|
+
if (!force && current === to) return
|
|
335
|
+
|
|
336
|
+
const method = replace ? 'replaceState' : 'pushState'
|
|
337
|
+
window.history[method](state, title, to)
|
|
338
|
+
}
|
|
339
|
+
|
|
300
340
|
/**
|
|
301
341
|
* Wraps a function component so that it participates in reconciliation.
|
|
302
342
|
*
|
|
@@ -308,11 +348,18 @@ export const component = fn => {
|
|
|
308
348
|
return (...args) => {
|
|
309
349
|
try {
|
|
310
350
|
const prevEl = rootMap.get(instance)
|
|
311
|
-
const canUpdateInPlace =
|
|
351
|
+
const canUpdateInPlace =
|
|
352
|
+
!!prevEl?.parentNode
|
|
353
|
+
&& componentUpdateDepth === 0
|
|
354
|
+
&& !currentEventRoot
|
|
312
355
|
|
|
313
356
|
componentUpdateDepth++
|
|
314
|
-
|
|
315
|
-
|
|
357
|
+
let vnode
|
|
358
|
+
try {
|
|
359
|
+
vnode = fn(...args)
|
|
360
|
+
} finally {
|
|
361
|
+
componentUpdateDepth--
|
|
362
|
+
}
|
|
316
363
|
|
|
317
364
|
if (canUpdateInPlace) {
|
|
318
365
|
const replacement = renderTree(['wrap', { __instance: instance }, vnode], true)
|
|
@@ -322,7 +369,6 @@ export const component = fn => {
|
|
|
322
369
|
|
|
323
370
|
return ['wrap', { __instance: instance }, vnode]
|
|
324
371
|
} catch (err) {
|
|
325
|
-
componentUpdateDepth = Math.max(0, componentUpdateDepth - 1)
|
|
326
372
|
console.error('Component error:', err)
|
|
327
373
|
return ['div', {}, `Error: ${err.message}`]
|
|
328
374
|
}
|
package/package.json
CHANGED
package/types/elements.d.ts
CHANGED
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|
export const DEBUG: boolean;
|
|
13
13
|
export function render(vtree: any, container?: any): void;
|
|
14
14
|
export function component(fn: (...args: any[]) => any): (...args: any[]) => any;
|
|
15
|
+
export function navigate(
|
|
16
|
+
to: string,
|
|
17
|
+
options?: {
|
|
18
|
+
replace?: boolean;
|
|
19
|
+
force?: boolean;
|
|
20
|
+
state?: any;
|
|
21
|
+
title?: string;
|
|
22
|
+
}
|
|
23
|
+
): void;
|
|
15
24
|
/**
|
|
16
25
|
* @typedef {Record<string, any>} Props
|
|
17
26
|
* @typedef {any[] | string | number | boolean | null | undefined | Node} Child
|