@furystack/shades 15.0.0 → 15.0.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/CHANGELOG.md +27 -0
- package/esm/compile-route.d.ts +5 -0
- package/esm/compile-route.d.ts.map +1 -1
- package/esm/compile-route.js +5 -0
- package/esm/compile-route.js.map +1 -1
- package/esm/components/nested-router.js.map +1 -1
- package/esm/css-generator.d.ts +9 -26
- package/esm/css-generator.d.ts.map +1 -1
- package/esm/css-generator.js +9 -26
- package/esm/css-generator.js.map +1 -1
- package/esm/css-generator.spec.js.map +1 -1
- package/esm/models/css-object.d.ts +3 -5
- package/esm/models/css-object.d.ts.map +1 -1
- package/esm/models/render-options.d.ts +16 -20
- package/esm/models/render-options.d.ts.map +1 -1
- package/esm/models/shade-component.d.ts +6 -4
- package/esm/models/shade-component.d.ts.map +1 -1
- package/esm/models/shade-component.js +3 -3
- package/esm/models/shade-component.js.map +1 -1
- package/esm/services/route-meta-utils.js.map +1 -1
- package/esm/services/screen-service.spec.js.map +1 -1
- package/esm/shade-component.d.ts +14 -17
- package/esm/shade-component.d.ts.map +1 -1
- package/esm/shade-component.js +14 -17
- package/esm/shade-component.js.map +1 -1
- package/esm/shade.d.ts +17 -14
- package/esm/shade.d.ts.map +1 -1
- package/esm/shade.js +12 -25
- package/esm/shade.js.map +1 -1
- package/esm/style-manager.d.ts +8 -22
- package/esm/style-manager.d.ts.map +1 -1
- package/esm/style-manager.js +8 -22
- package/esm/style-manager.js.map +1 -1
- package/esm/styled-shade.d.ts +4 -7
- package/esm/styled-shade.d.ts.map +1 -1
- package/esm/styled-shade.js +4 -4
- package/esm/styled-shade.js.map +1 -1
- package/esm/view-transition.d.ts +10 -22
- package/esm/view-transition.d.ts.map +1 -1
- package/esm/view-transition.js +10 -22
- package/esm/view-transition.js.map +1 -1
- package/esm/vnode.js.map +1 -1
- package/esm/vnode.spec.js.map +1 -1
- package/package.json +10 -10
- package/src/compile-route.ts +5 -0
- package/src/components/nested-router.tsx +2 -2
- package/src/css-generator.spec.ts +1 -1
- package/src/css-generator.ts +9 -26
- package/src/models/css-object.ts +3 -5
- package/src/models/render-options.ts +17 -20
- package/src/models/shade-component.ts +6 -4
- package/src/services/route-meta-utils.ts +1 -1
- package/src/services/screen-service.spec.ts +2 -2
- package/src/shade-component.ts +14 -17
- package/src/shade.ts +20 -33
- package/src/style-manager.ts +9 -22
- package/src/styled-shade.ts +5 -8
- package/src/view-transition.ts +10 -22
- package/src/vnode.spec.ts +3 -3
- package/src/vnode.ts +2 -2
package/src/shade.ts
CHANGED
|
@@ -19,18 +19,18 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
19
19
|
*/
|
|
20
20
|
customElementName: string
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Render hook, this method will be executed on each and every render.
|
|
24
|
-
*/
|
|
25
22
|
render: (options: RenderOptions<TProps, TElementBase>) => JSX.Element | string | null
|
|
26
23
|
|
|
27
24
|
/**
|
|
28
|
-
*
|
|
25
|
+
* Tag name of the base built-in element when extending one (e.g. `'a'`,
|
|
26
|
+
* `'button'`, `'input'`). Required when {@link ShadeOptions.elementBase}
|
|
27
|
+
* is set; passed to `customElements.define` as `{ extends }`.
|
|
29
28
|
*/
|
|
30
29
|
elementBaseName?: string
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
32
|
+
* Constructor of the built-in element to extend (e.g. `HTMLButtonElement`).
|
|
33
|
+
* Defaults to `HTMLElement`. Pair with {@link ShadeOptions.elementBaseName}.
|
|
34
34
|
*/
|
|
35
35
|
elementBase?: Constructable<TElementBase>
|
|
36
36
|
|
|
@@ -61,9 +61,14 @@ export type ShadeOptions<TProps, TElementBase extends HTMLElement> = {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* Defines and registers a Shade component as a custom element. Returns a
|
|
65
|
+
* JSX-callable factory `(props, children?) => JSX.Element`. Throws when a
|
|
66
|
+
* component with the same {@link ShadeOptions.customElementName} has
|
|
67
|
+
* already been registered — registration is global and process-wide.
|
|
68
|
+
*
|
|
69
|
+
* The returned factory is the entry point for downstream code. The custom
|
|
70
|
+
* element class itself is installed via `customElements.define` and never
|
|
71
|
+
* directly exposed.
|
|
67
72
|
*/
|
|
68
73
|
export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
69
74
|
o: ShadeOptions<TProps, TElementBase>,
|
|
@@ -84,9 +89,6 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
84
89
|
class extends (ElementBase as Constructable<HTMLElement>) implements JSX.Element {
|
|
85
90
|
private _renderCount = 0
|
|
86
91
|
|
|
87
|
-
/**
|
|
88
|
-
* @returns the current render count
|
|
89
|
-
*/
|
|
90
92
|
public getRenderCount() {
|
|
91
93
|
return this._renderCount
|
|
92
94
|
}
|
|
@@ -128,28 +130,15 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
128
130
|
await this.resourceManager[Symbol.asyncDispose]()
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
/**
|
|
132
|
-
* Will be triggered when updating the external props object
|
|
133
|
-
*/
|
|
134
133
|
public props!: TProps & { children?: JSX.Element[] } & PartialElement<TElementBase>
|
|
135
134
|
|
|
136
|
-
/**
|
|
137
|
-
* Will be updated when on children change
|
|
138
|
-
*/
|
|
139
135
|
public shadeChildren?: ChildrenList
|
|
140
136
|
|
|
141
|
-
/**
|
|
142
|
-
* @param options Options for rendering the component
|
|
143
|
-
* @returns the JSX element
|
|
144
|
-
*/
|
|
145
137
|
public render = (options: RenderOptions<TProps, TElementBase>) => {
|
|
146
138
|
this._renderCount++
|
|
147
139
|
return o.render(options)
|
|
148
140
|
}
|
|
149
141
|
|
|
150
|
-
/**
|
|
151
|
-
* @returns values for the current render options
|
|
152
|
-
*/
|
|
153
142
|
private getRenderOptions = (): RenderOptions<TProps, TElementBase> => {
|
|
154
143
|
const renderOptions: RenderOptions<TProps, TElementBase> = {
|
|
155
144
|
props: this.props,
|
|
@@ -163,8 +152,8 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
163
152
|
const existingRef = this._refs.get(key) as RefObject<T> | undefined
|
|
164
153
|
if (existingRef) return existingRef
|
|
165
154
|
const refObject = { current: null } as { current: T | null }
|
|
166
|
-
this._refs.set(key, refObject
|
|
167
|
-
return refObject
|
|
155
|
+
this._refs.set(key, refObject)
|
|
156
|
+
return refObject
|
|
168
157
|
},
|
|
169
158
|
useObservable: (key, observable, options) => {
|
|
170
159
|
const onChange = options?.onChange || (() => this.updateComponent())
|
|
@@ -308,7 +297,7 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
308
297
|
this.removeAttribute(key)
|
|
309
298
|
}
|
|
310
299
|
if (this._prevHostProps.style) {
|
|
311
|
-
for (const sk of Object.keys(this._prevHostProps.style
|
|
300
|
+
for (const sk of Object.keys(this._prevHostProps.style)) {
|
|
312
301
|
if (sk.startsWith('--')) {
|
|
313
302
|
this.style.removeProperty(sk)
|
|
314
303
|
} else {
|
|
@@ -464,11 +453,9 @@ export const Shade = <TProps, TElementBase extends HTMLElement = HTMLElement>(
|
|
|
464
453
|
}
|
|
465
454
|
|
|
466
455
|
/**
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* `await flushUpdates()` is sufficient to settle the entire component tree after a state change.
|
|
472
|
-
* @returns a promise that resolves after the current microtask queue has been processed
|
|
456
|
+
* Awaits the next microtask tick — long enough for `updateComponent`'s
|
|
457
|
+
* batching microtask to drain. A single `await flushUpdates()` settles the
|
|
458
|
+
* entire component tree because child reconciliation is synchronous within
|
|
459
|
+
* the parent's render. Use in tests before asserting on DOM state.
|
|
473
460
|
*/
|
|
474
461
|
export const flushUpdates = (): Promise<void> => new Promise<void>((resolve) => queueMicrotask(resolve))
|
package/src/style-manager.ts
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { generateCSS } from './css-generator.js'
|
|
2
2
|
import type { CSSObject } from './models/css-object.js'
|
|
3
|
+
import type { Shade } from './shade.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Owns a shared `<style data-shades-styles>` element in `document.head`
|
|
7
|
+
* and dedupes component-style registration. Internal — consumers use
|
|
8
|
+
* the {@link StyleManager} singleton.
|
|
8
9
|
*/
|
|
9
10
|
class StyleManagerClass {
|
|
10
11
|
private styleElement: HTMLStyleElement | null = null
|
|
11
12
|
private registeredComponents = new Set<string>()
|
|
12
13
|
|
|
13
|
-
/**
|
|
14
|
-
* Gets or creates the shared style element
|
|
15
|
-
* @returns The style element for CSS injection
|
|
16
|
-
*/
|
|
17
14
|
private getStyleElement(): HTMLStyleElement {
|
|
18
15
|
if (!this.styleElement) {
|
|
19
16
|
this.styleElement = document.createElement('style')
|
|
@@ -66,26 +63,16 @@ class StyleManagerClass {
|
|
|
66
63
|
return false
|
|
67
64
|
}
|
|
68
65
|
|
|
69
|
-
/**
|
|
70
|
-
* Checks if a component's styles have already been registered
|
|
71
|
-
* @param customElementName - The component identifier to check
|
|
72
|
-
* @returns True if styles are already registered
|
|
73
|
-
*/
|
|
74
66
|
public isRegistered(customElementName: string): boolean {
|
|
75
67
|
return this.registeredComponents.has(customElementName)
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
/**
|
|
79
|
-
* Gets all registered component names (for debugging/testing)
|
|
80
|
-
* @returns Set of registered component names
|
|
81
|
-
*/
|
|
70
|
+
/** Snapshot of registered component names. Intended for diagnostics + tests. */
|
|
82
71
|
public getRegisteredComponents(): ReadonlySet<string> {
|
|
83
72
|
return this.registeredComponents
|
|
84
73
|
}
|
|
85
74
|
|
|
86
|
-
/**
|
|
87
|
-
* Clears all registered styles (useful for testing)
|
|
88
|
-
*/
|
|
75
|
+
/** Removes the shared style element and clears the registration set. Test-only. */
|
|
89
76
|
public clear(): void {
|
|
90
77
|
this.registeredComponents.clear()
|
|
91
78
|
if (this.styleElement) {
|
|
@@ -97,8 +84,8 @@ class StyleManagerClass {
|
|
|
97
84
|
}
|
|
98
85
|
|
|
99
86
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
87
|
+
* Process-wide CSS injection registry for Shade components. The {@link Shade}
|
|
88
|
+
* factory calls `registerComponentStyles` automatically; direct use is rare
|
|
89
|
+
* (e.g. injecting a third-party stylesheet keyed off a component name).
|
|
103
90
|
*/
|
|
104
91
|
export const StyleManager = new StyleManagerClass()
|
package/src/styled-shade.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import type { ChildrenList } from './models/children-list.js'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Props constraint for styled components - must have an optional style property
|
|
5
|
-
*/
|
|
6
3
|
type StyledProps = {
|
|
7
4
|
style?: Partial<CSSStyleDeclaration>
|
|
8
5
|
}
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
8
|
+
* Wraps a Shade factory with a baked-in `style` overlay. Baked-in `styles`
|
|
9
|
+
* win over caller-supplied `style` props (the wrapper merges last). Use to
|
|
10
|
+
* specialise an existing Shade (e.g. theme variants) without registering
|
|
11
|
+
* a new custom element.
|
|
15
12
|
*/
|
|
16
13
|
export const styledShade = <TProps extends StyledProps>(
|
|
17
14
|
element: (props: TProps, children?: ChildrenList) => JSX.Element,
|
|
@@ -25,7 +22,7 @@ export const styledShade = <TProps extends StyledProps>(
|
|
|
25
22
|
...props.style,
|
|
26
23
|
...styles,
|
|
27
24
|
},
|
|
28
|
-
}
|
|
25
|
+
},
|
|
29
26
|
childrenList,
|
|
30
27
|
)
|
|
31
28
|
}
|
package/src/view-transition.ts
CHANGED
|
@@ -8,16 +8,13 @@ export type ViewTransitionConfig = {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Runs `update` inside `document.startViewTransition()` when the API is
|
|
12
|
+
* available and `config` is truthy; otherwise calls `update` synchronously.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* @param config - The view transition configuration (boolean or object with types)
|
|
19
|
-
* @param update - The synchronous DOM update callback
|
|
20
|
-
* @returns A promise that resolves after the update callback completes inside the transition, or `undefined`
|
|
14
|
+
* The returned promise (when a transition starts) resolves once the update
|
|
15
|
+
* callback completes inside the transition — callers that need to chain
|
|
16
|
+
* lifecycle work after the DOM swap can `await` it. Returns `undefined`
|
|
17
|
+
* when no transition is used.
|
|
21
18
|
*/
|
|
22
19
|
export const maybeViewTransition = (
|
|
23
20
|
config: boolean | ViewTransitionConfig | undefined,
|
|
@@ -33,19 +30,10 @@ export const maybeViewTransition = (
|
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* the displayed value is updated synchronously without an animation.
|
|
41
|
-
*
|
|
42
|
-
* @param useState - The component's `useState` hook
|
|
43
|
-
* @param key - Unique state key for caching the displayed value
|
|
44
|
-
* @param value - The latest source value (e.g. from `useObservable` or derived from props)
|
|
45
|
-
* @param config - View transition configuration forwarded to `maybeViewTransition`
|
|
46
|
-
* @param shouldTransition - Predicate that decides whether a value change warrants a transition.
|
|
47
|
-
* Defaults to `() => true` (always transition).
|
|
48
|
-
* @returns The currently displayed value
|
|
33
|
+
* Decouples a component's "displayed" value from its source. When `value`
|
|
34
|
+
* changes and `shouldTransition(prev, next)` returns true, the swap is
|
|
35
|
+
* routed through {@link maybeViewTransition}; otherwise the displayed
|
|
36
|
+
* value updates synchronously. `shouldTransition` defaults to always-true.
|
|
49
37
|
*/
|
|
50
38
|
export const transitionedValue = <T>(
|
|
51
39
|
useState: <S>(key: string, initialValue: S) => [S, (v: S) => void],
|
package/src/vnode.spec.ts
CHANGED
|
@@ -545,7 +545,7 @@ describe('vnode', () => {
|
|
|
545
545
|
;(fakeShadeEl as unknown as Record<string, unknown>).props = { count: 1 }
|
|
546
546
|
;(fakeShadeEl as unknown as Record<string, unknown>).shadeChildren = undefined
|
|
547
547
|
|
|
548
|
-
const factory = vi.fn(() => fakeShadeEl
|
|
548
|
+
const factory = vi.fn(() => fakeShadeEl)
|
|
549
549
|
|
|
550
550
|
const old: VChild[] = [{ _brand: 'vnode', type: factory, props: { count: 1 }, children: [], _el: fakeShadeEl }]
|
|
551
551
|
parent.appendChild(fakeShadeEl)
|
|
@@ -566,7 +566,7 @@ describe('vnode', () => {
|
|
|
566
566
|
;(fakeShadeEl as unknown as Record<string, unknown>).props = { elevation: 2 }
|
|
567
567
|
;(fakeShadeEl as unknown as Record<string, unknown>).shadeChildren = undefined
|
|
568
568
|
|
|
569
|
-
const factory = vi.fn(() => fakeShadeEl
|
|
569
|
+
const factory = vi.fn(() => fakeShadeEl)
|
|
570
570
|
|
|
571
571
|
const old: VChild[] = [
|
|
572
572
|
{ _brand: 'vnode', type: factory, props: { elevation: 2 }, children: [], _el: fakeShadeEl },
|
|
@@ -591,7 +591,7 @@ describe('vnode', () => {
|
|
|
591
591
|
;(fakeShadeEl as unknown as Record<string, unknown>).props = props
|
|
592
592
|
;(fakeShadeEl as unknown as Record<string, unknown>).shadeChildren = undefined
|
|
593
593
|
|
|
594
|
-
const factory = vi.fn(() => fakeShadeEl
|
|
594
|
+
const factory = vi.fn(() => fakeShadeEl)
|
|
595
595
|
|
|
596
596
|
const old: VChild[] = [{ _brand: 'vnode', type: factory, props: { count: 1 }, children: [], _el: fakeShadeEl }]
|
|
597
597
|
parent.appendChild(fakeShadeEl)
|
package/src/vnode.ts
CHANGED
|
@@ -197,9 +197,9 @@ export const toVChildArray = (renderResult: unknown): VChild[] => {
|
|
|
197
197
|
// Real DOM element (from async code that ran outside renderMode)
|
|
198
198
|
if (renderResult instanceof DocumentFragment) {
|
|
199
199
|
return Array.from(renderResult.childNodes).map((node) => ({
|
|
200
|
-
_brand: VNODE_BRAND
|
|
200
|
+
_brand: VNODE_BRAND,
|
|
201
201
|
type: EXISTING_NODE,
|
|
202
|
-
props: {}
|
|
202
|
+
props: {},
|
|
203
203
|
children: [] as VChild[],
|
|
204
204
|
_el: node,
|
|
205
205
|
}))
|