@fluixi/core 1.0.0-alpha.92 → 1.0.0-alpha.94
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/cdn/core.global.js +2 -2
- package/dist/src/cdn/{chunk-MDJLC3PE.mjs → chunk-7XM7Q5VR.mjs} +271 -229
- package/dist/src/cdn/core.cjs +360 -253
- package/dist/src/cdn/core.mjs +92 -20
- package/dist/src/cdn/{router-JYFZP3EZ.mjs → router-VOY43YNM.mjs} +7 -3
- package/dist/src/index.cjs +548 -232
- package/dist/src/index.mjs +549 -224
- package/dist/src/lib/client/chunk-TSXM2PNY.mjs +53 -0
- package/dist/src/lib/client/index.cjs +310 -176
- package/dist/src/lib/client/index.d.ts.map +1 -1
- package/dist/src/lib/client/index.js +33 -18
- package/dist/src/lib/client/index.mjs +86 -15
- package/dist/src/lib/client/{router-PHLUYVQU.mjs → router-BDVPLLYX.mjs} +244 -99
- package/dist/src/lib/context/context.cjs +35 -72
- package/dist/src/lib/context/context.d.ts +45 -291
- package/dist/src/lib/context/context.d.ts.map +1 -1
- package/dist/src/lib/context/context.js +65 -397
- package/dist/src/lib/context/context.mjs +38 -73
- package/dist/src/lib/context/index.cjs +35 -72
- package/dist/src/lib/context/index.d.ts +2 -2
- package/dist/src/lib/context/index.d.ts.map +1 -1
- package/dist/src/lib/context/index.js +1 -5
- package/dist/src/lib/context/index.mjs +38 -73
- package/dist/src/lib/core.cjs +547 -231
- package/dist/src/lib/core.d.ts +5 -3
- package/dist/src/lib/core.d.ts.map +1 -1
- package/dist/src/lib/core.js +6 -4
- package/dist/src/lib/core.mjs +548 -223
- package/dist/src/lib/env/index.cjs +1 -17
- package/dist/src/lib/env/index.d.ts +2 -14
- package/dist/src/lib/env/index.d.ts.map +1 -1
- package/dist/src/lib/env/index.js +2 -26
- package/dist/src/lib/env/index.mjs +1 -17
- package/dist/src/lib/i18n/index.cjs +17 -2
- package/dist/src/lib/i18n/index.d.ts +10 -0
- package/dist/src/lib/i18n/index.d.ts.map +1 -1
- package/dist/src/lib/i18n/index.js +17 -1
- package/dist/src/lib/i18n/index.mjs +17 -2
- package/dist/src/lib/index.cjs +64 -99
- package/dist/src/lib/index.mjs +43 -76
- package/dist/src/lib/isomorphic.cjs +9 -4
- package/dist/src/lib/isomorphic.d.ts +9 -2
- package/dist/src/lib/isomorphic.d.ts.map +1 -1
- package/dist/src/lib/isomorphic.js +11 -4
- package/dist/src/lib/isomorphic.mjs +9 -4
- package/dist/src/lib/jsx-runtime/index.cjs +1 -1
- package/dist/src/lib/jsx-runtime/index.mjs +1 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -1
- package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -1
- package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +2 -2
- package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +2 -2
- package/dist/src/lib/plugins/index.cjs +3 -3
- package/dist/src/lib/plugins/index.mjs +3 -3
- package/dist/src/lib/plugins/route-codegen.cjs +2 -2
- package/dist/src/lib/plugins/route-codegen.js +1 -1
- package/dist/src/lib/plugins/route-codegen.mjs +2 -2
- package/dist/src/lib/render/index.cjs +1 -1
- package/dist/src/lib/render/index.mjs +1 -1
- package/dist/src/lib/render/versions.cjs +1 -1
- package/dist/src/lib/render/versions.mjs +1 -1
- package/dist/src/lib/router/data.cjs +113 -115
- package/dist/src/lib/router/data.d.ts +63 -10
- package/dist/src/lib/router/data.d.ts.map +1 -1
- package/dist/src/lib/router/data.js +158 -68
- package/dist/src/lib/router/data.mjs +112 -112
- package/dist/src/lib/router/index.cjs +260 -163
- package/dist/src/lib/router/index.d.ts +22 -2
- package/dist/src/lib/router/index.d.ts.map +1 -1
- package/dist/src/lib/router/index.js +165 -22
- package/dist/src/lib/router/index.mjs +244 -143
- package/dist/src/lib/server/index.cjs +1 -1
- package/dist/src/lib/server/index.mjs +1 -1
- package/dist/src/lib/storage/cache.cjs +51 -0
- package/dist/src/lib/storage/cache.d.ts +18 -0
- package/dist/src/lib/storage/cache.d.ts.map +1 -0
- package/dist/src/lib/storage/cache.js +42 -0
- package/dist/src/lib/storage/cache.mjs +30 -0
- package/dist/src/lib/storage/index.cjs +246 -0
- package/dist/src/lib/storage/index.d.ts +83 -0
- package/dist/src/lib/storage/index.d.ts.map +1 -0
- package/dist/src/lib/storage/index.js +179 -0
- package/dist/src/lib/storage/index.mjs +231 -0
- package/dist/src/lib/storage/persisted.cjs +91 -0
- package/dist/src/lib/storage/persisted.d.ts +53 -0
- package/dist/src/lib/storage/persisted.d.ts.map +1 -0
- package/dist/src/lib/storage/persisted.js +75 -0
- package/dist/src/lib/storage/persisted.mjs +70 -0
- package/dist/src/lib/theme/color-scheme.cjs +281 -0
- package/dist/src/lib/theme/color-scheme.d.ts +61 -0
- package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
- package/dist/src/lib/theme/color-scheme.js +120 -0
- package/dist/src/lib/theme/color-scheme.mjs +265 -0
- package/dist/src/lib/theme/index.cjs +194 -37
- package/dist/src/lib/theme/index.d.ts +23 -11
- package/dist/src/lib/theme/index.d.ts.map +1 -1
- package/dist/src/lib/theme/index.js +73 -32
- package/dist/src/lib/theme/index.mjs +197 -35
- package/dist/src/lib/theme/provider.cjs +346 -0
- package/dist/src/lib/theme/provider.d.ts +34 -0
- package/dist/src/lib/theme/provider.d.ts.map +1 -0
- package/dist/src/lib/theme/provider.js +60 -0
- package/dist/src/lib/theme/provider.mjs +332 -0
- package/dist/src/routes.cjs +1 -1
- package/dist/src/routes.mjs +1 -1
- package/dist/src/utils.cjs +1 -1
- package/dist/src/utils.mjs +1 -1
- package/dist/src/version.generated.cjs +1 -1
- package/dist/src/version.generated.d.ts +1 -1
- package/dist/src/version.generated.js +1 -1
- package/dist/src/version.generated.mjs +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +18 -11
- package/dist/src/lib/client/chunk-VB567ABU.mjs +0 -133
|
@@ -1,425 +1,93 @@
|
|
|
1
|
-
// /**
|
|
2
|
-
// * @fileoverview Context API for Fluixi that wraps @fluixi/reactive context for Lit templates
|
|
3
|
-
// * @module @fluixi/core/context/context
|
|
4
|
-
// */
|
|
5
|
-
import { asProvider } from '@fluixi/dom';
|
|
6
|
-
import { createContext as createReactiveContext, useContext as useReactiveContext } from '@fluixi/reactive/signal';
|
|
7
|
-
import { readChildren, } from '@fluixi/reactive/signal';
|
|
8
1
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* This wraps @fluixi/reactive's Provider for use with Lit templates.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { createContext, Provider } from '@fluixi/core/context';
|
|
16
|
-
* import { createSignal } from '@fluixi/reactive';
|
|
17
|
-
* import { html } from 'lit';
|
|
2
|
+
* Context: a value provided by a component and read by any component below it.
|
|
18
3
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* view() {
|
|
25
|
-
* return html`
|
|
26
|
-
* ${Provider({
|
|
27
|
-
* context: ThemeContext,
|
|
28
|
-
* value: this.theme(),
|
|
29
|
-
* children: html`
|
|
30
|
-
* <div>
|
|
31
|
-
* <themed-button></themed-button>
|
|
32
|
-
* <themed-text></themed-text>
|
|
33
|
-
* </div>
|
|
34
|
-
* `
|
|
35
|
-
* })}
|
|
36
|
-
* `;
|
|
37
|
-
* }
|
|
38
|
-
* }
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* // Nested providers
|
|
44
|
-
* const UserContext = createContext<User | null>(null);
|
|
45
|
-
* const PermissionsContext = createContext<string[]>([]);
|
|
46
|
-
*
|
|
47
|
-
* class App extends FluixiElement {
|
|
48
|
-
* private [user, setUser] = createSignal<User | null>(null);
|
|
49
|
-
* private [permissions, setPermissions] = createSignal<string[]>(['read']);
|
|
50
|
-
*
|
|
51
|
-
* view() {
|
|
52
|
-
* return html`
|
|
53
|
-
* ${Provider({
|
|
54
|
-
* context: UserContext,
|
|
55
|
-
* value: this.user(),
|
|
56
|
-
* children: Provider({
|
|
57
|
-
* context: PermissionsContext,
|
|
58
|
-
* value: this.permissions(),
|
|
59
|
-
* children: html`<app-dashboard></app-dashboard>`
|
|
60
|
-
* })
|
|
61
|
-
* })}
|
|
62
|
-
* `;
|
|
63
|
-
* }
|
|
64
|
-
* }
|
|
65
|
-
* ```
|
|
4
|
+
* The owner tree holds it (`@fluixi/reactive`), so `useContext` finds the nearest
|
|
5
|
+
* `<Ctx.Provider>` above the component that asks. The same object is the key custom elements use
|
|
6
|
+
* with `@fluixi/web-components`: an element reads what a Provider around it gives, and the
|
|
7
|
+
* components an element renders read what the element provides.
|
|
66
8
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (_warnedEagerProvider)
|
|
70
|
-
return;
|
|
71
|
-
_warnedEagerProvider = true;
|
|
72
|
-
if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production')
|
|
73
|
-
return;
|
|
74
|
-
console.warn('[fluixi] createProvider({ context, value, children }) reads `children` eagerly: ' +
|
|
75
|
-
'the object literal materialises `children` BEFORE the context is stamped, so a ' +
|
|
76
|
-
'routed <Outlet/> that has siblings inside the provider renders detached and its ' +
|
|
77
|
-
'descendants see no context under SSR. Use the lazy form instead:\n' +
|
|
78
|
-
' createProvider(Context, value, props) // children stays a getter\n' +
|
|
79
|
-
' // or JSX: <Context.Provider value={...}>{props.children}</Context.Provider>');
|
|
80
|
-
}
|
|
81
|
-
// Returns the Provider's rendered output, not a component, it was annotated
|
|
82
|
-
// `Component<T>`, which made `<MyProvider>` fail as a JSX element.
|
|
83
|
-
export function createProvider(a, value, childProps) {
|
|
84
|
-
// Distinguish the two overloads: a Context (from createContext) exposes `.Provider`,
|
|
85
|
-
// whereas the deprecated form's first arg is the props object (which has `.context`).
|
|
86
|
-
const isLazyForm = !!a?.Provider;
|
|
87
|
-
const context = (isLazyForm ? a : a.context);
|
|
88
|
-
const rawValue = isLazyForm ? value : a.value;
|
|
89
|
-
const debug = isLazyForm ? false : (a.debug ?? false);
|
|
90
|
-
if (!isLazyForm)
|
|
91
|
-
warnEagerProvider();
|
|
92
|
-
// Resolve value if it's an accessor
|
|
93
|
-
const resolvedValue = typeof rawValue === 'function' ? rawValue() : rawValue;
|
|
94
|
-
// Always read children through a getter so they render INSIDE the stamped scope.
|
|
95
|
-
// For the lazy form `childProps.children` is still a getter here (untouched); for
|
|
96
|
-
// the eager form the caller already materialised it, so this can only mitigate,
|
|
97
|
-
// not fully fix: hence the deprecation warning above.
|
|
98
|
-
const sourceProps = isLazyForm ? childProps : a;
|
|
99
|
-
return context.Provider({
|
|
100
|
-
value: resolvedValue,
|
|
101
|
-
get children() { return sourceProps?.children; },
|
|
102
|
-
debug,
|
|
103
|
-
});
|
|
104
|
-
// // Wrap children in the reactive provider
|
|
105
|
-
// return html`${ReactiveProvider({
|
|
106
|
-
// value: resolvedValue,
|
|
107
|
-
// children: resolveChildren(children),
|
|
108
|
-
// //<!--Array.isArray(children) ? html`${children}` : children,-->
|
|
109
|
-
// } as any)}`;
|
|
110
|
-
}
|
|
9
|
+
import { asProvider } from '@fluixi/dom';
|
|
10
|
+
import { createContext as createReactiveContext, getOwner, useContext as useReactiveContext, } from '@fluixi/reactive/signal';
|
|
111
11
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* Returns both the context and a convenience component that combines
|
|
115
|
-
* context creation with the provider.
|
|
12
|
+
* A context with a default for components that have no Provider above them. `name` is what
|
|
13
|
+
* devtools show for it.
|
|
116
14
|
*
|
|
117
15
|
* @example
|
|
118
|
-
* ```
|
|
119
|
-
*
|
|
120
|
-
* import { createSignal } from '@fluixi/reactive';
|
|
121
|
-
* import { html } from 'lit';
|
|
122
|
-
*
|
|
123
|
-
* const [ThemeContext, ThemeProvider] = createContextWithProvider({
|
|
124
|
-
* primaryColor: '#007bff'
|
|
125
|
-
* });
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const Theme = createContext<'light' | 'dark'>('light', 'theme');
|
|
126
18
|
*
|
|
127
|
-
*
|
|
128
|
-
* private [theme] = createSignal({ primaryColor: '#28a745' });
|
|
129
|
-
*
|
|
130
|
-
* view() {
|
|
131
|
-
* return html`
|
|
132
|
-
* ${ThemeProvider({
|
|
133
|
-
* value: this.theme(),
|
|
134
|
-
* children: html`<div><themed-button></themed-button></div>`
|
|
135
|
-
* })}
|
|
136
|
-
* `;
|
|
137
|
-
* }
|
|
138
|
-
* }
|
|
139
|
-
*
|
|
140
|
-
* class ThemedButton extends FluixiElement {
|
|
141
|
-
* view() {
|
|
142
|
-
* const theme = useContext(ThemeContext);
|
|
143
|
-
* return html`<button style="color: ${theme.primaryColor}">Click</button>`;
|
|
144
|
-
* }
|
|
145
|
-
* }
|
|
19
|
+
* <Theme.Provider value="dark"><Toolbar /></Theme.Provider>
|
|
146
20
|
* ```
|
|
147
21
|
*/
|
|
148
|
-
export function
|
|
149
|
-
const context =
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
22
|
+
export function createContext(defaultValue, name) {
|
|
23
|
+
const context = createReactiveContext(defaultValue, name);
|
|
24
|
+
const Provider = (props) =>
|
|
25
|
+
// Children stay a getter: the reactive Provider reads them after stamping the value on
|
|
26
|
+
// its owner. Reading `props.children` here would render them, and any <Outlet/> in them,
|
|
27
|
+
// before the context exists.
|
|
28
|
+
context.Provider({
|
|
29
|
+
value: props.value,
|
|
30
|
+
get children() {
|
|
31
|
+
return props.children;
|
|
32
|
+
},
|
|
33
|
+
debug: props.debug,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
id: context.id,
|
|
37
|
+
Provider: asProvider(Provider),
|
|
38
|
+
defaultValue: defaultValue,
|
|
154
39
|
};
|
|
155
|
-
|
|
40
|
+
}
|
|
41
|
+
/** The value of the nearest Provider above the calling component, or the context's default. */
|
|
42
|
+
export function useContext(context) {
|
|
43
|
+
const value = useReactiveContext(context);
|
|
44
|
+
return value !== undefined ? value : context.defaultValue;
|
|
156
45
|
}
|
|
157
46
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* Utility function to avoid deeply nested Provider components.
|
|
47
|
+
* A Provider as a function call, for a component that provides without JSX. `value` may be an
|
|
48
|
+
* accessor, read once. Pass the component's own props so `children` stays lazy.
|
|
161
49
|
*
|
|
162
50
|
* @example
|
|
163
|
-
* ```
|
|
164
|
-
*
|
|
165
|
-
* import { createSignal } from '@fluixi/reactive';
|
|
166
|
-
* import { html } from 'lit';
|
|
167
|
-
*
|
|
168
|
-
* const ThemeContext = createContext({ dark: false });
|
|
169
|
-
* const UserContext = createContext<User | null>(null);
|
|
170
|
-
* const LocaleContext = createContext('en');
|
|
171
|
-
*
|
|
172
|
-
* class App extends FluixiElement {
|
|
173
|
-
* private [theme] = createSignal({ dark: true });
|
|
174
|
-
* private [user] = createSignal<User | null>({ id: 1, name: 'Alice' });
|
|
175
|
-
* private [locale] = createSignal('fr');
|
|
176
|
-
*
|
|
177
|
-
* view() {
|
|
178
|
-
* return composeProviders(
|
|
179
|
-
* [
|
|
180
|
-
* [ThemeContext, this.theme()],
|
|
181
|
-
* [UserContext, this.user()],
|
|
182
|
-
* [LocaleContext, this.locale()]
|
|
183
|
-
* ],
|
|
184
|
-
* html`<app-dashboard></app-dashboard>`
|
|
185
|
-
* );
|
|
186
|
-
* }
|
|
187
|
-
* }
|
|
51
|
+
* ```ts
|
|
52
|
+
* const ThemeProvider = (props) => createProvider(Theme, 'dark', props);
|
|
188
53
|
* ```
|
|
189
54
|
*/
|
|
190
|
-
export function
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// stamps its context before the inner providers/children render.
|
|
198
|
-
return createProvider(context, value, {
|
|
199
|
-
get children() { return composeProviders(remaining, children); },
|
|
55
|
+
export function createProvider(context, value, props) {
|
|
56
|
+
const resolved = typeof value === 'function' ? value() : value;
|
|
57
|
+
return context.Provider({
|
|
58
|
+
value: resolved,
|
|
59
|
+
get children() {
|
|
60
|
+
return props.children;
|
|
61
|
+
},
|
|
200
62
|
});
|
|
201
63
|
}
|
|
202
64
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* Alternative to useContext hook, useful for rendering props patterns.
|
|
65
|
+
* Several Providers as one, outermost first, for an app shell that would otherwise nest a
|
|
66
|
+
* Provider per context. Values may be accessors, as in `createProvider`; children stay lazy.
|
|
206
67
|
*
|
|
207
68
|
* @example
|
|
208
|
-
* ```
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* const ThemeContext = createContext({ primaryColor: '#007bff' });
|
|
213
|
-
* const ThemeConsumer = createConsumer(ThemeContext);
|
|
214
|
-
*
|
|
215
|
-
* class MyComponent extends FluixiElement {
|
|
216
|
-
* view() {
|
|
217
|
-
* return html`
|
|
218
|
-
* ${ThemeConsumer({
|
|
219
|
-
* children: (theme) => html`
|
|
220
|
-
* <div style="color: ${theme.primaryColor}">
|
|
221
|
-
* Themed content
|
|
222
|
-
* </div>
|
|
223
|
-
* `
|
|
224
|
-
* })}
|
|
225
|
-
* `;
|
|
226
|
-
* }
|
|
227
|
-
* }
|
|
69
|
+
* ```ts
|
|
70
|
+
* const AppProviders = (props) =>
|
|
71
|
+
* composeProviders([[Theme, 'dark'], [Locale, () => locale()], [Session, session]], props);
|
|
228
72
|
* ```
|
|
229
73
|
*/
|
|
230
|
-
export function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
74
|
+
export function composeProviders(providers, props) {
|
|
75
|
+
const outermost = providers.reduceRight((inner, [context, value]) => ({
|
|
76
|
+
get children() {
|
|
77
|
+
return createProvider(context, value, inner);
|
|
78
|
+
},
|
|
79
|
+
}), props);
|
|
80
|
+
return outermost.children;
|
|
235
81
|
}
|
|
236
82
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* @example
|
|
242
|
-
* ```typescript
|
|
243
|
-
* import { createContext, useProvide, Provider } from '@fluixi/core/context';
|
|
244
|
-
* import { createSignal } from '@fluixi/reactive';
|
|
245
|
-
* import { html } from 'lit';
|
|
246
|
-
*
|
|
247
|
-
* interface Counter {
|
|
248
|
-
* count: number;
|
|
249
|
-
* increment: () => void;
|
|
250
|
-
* }
|
|
251
|
-
*
|
|
252
|
-
* const CounterContext = createContext<Counter>({
|
|
253
|
-
* count: 0,
|
|
254
|
-
* increment: () => {}
|
|
255
|
-
* });
|
|
256
|
-
*
|
|
257
|
-
* class CounterProvider extends FluixiElement {
|
|
258
|
-
* view() {
|
|
259
|
-
* const counter = useProvide(CounterContext, () => {
|
|
260
|
-
* const [count, setCount] = createSignal(0);
|
|
261
|
-
* return {
|
|
262
|
-
* count: count(),
|
|
263
|
-
* increment: () => setCount(count() + 1)
|
|
264
|
-
* };
|
|
265
|
-
* });
|
|
266
|
-
*
|
|
267
|
-
* return html`
|
|
268
|
-
* ${Provider({
|
|
269
|
-
* context: CounterContext,
|
|
270
|
-
* value: counter,
|
|
271
|
-
* children: html`<slot></slot>`
|
|
272
|
-
* })}
|
|
273
|
-
* `;
|
|
274
|
-
* }
|
|
275
|
-
* }
|
|
276
|
-
* ```
|
|
277
|
-
*/
|
|
278
|
-
export function useProvide(context, factory) {
|
|
279
|
-
// Check if already provided
|
|
280
|
-
const existing = useContext(context);
|
|
281
|
-
// If we got the default value, create new instance
|
|
282
|
-
if (existing === context.defaultValue) {
|
|
283
|
-
return factory();
|
|
284
|
-
}
|
|
285
|
-
return existing;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Creates a scoped context that only exists within a specific component tree
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```typescript
|
|
292
|
-
* import { createContext } from '@fluixi/core/context';
|
|
293
|
-
*
|
|
294
|
-
* // This context won't leak to parent or sibling components
|
|
295
|
-
* const LocalContext = createContext({ data: 'local' });
|
|
296
|
-
* ```
|
|
297
|
-
*/
|
|
298
|
-
// export function createContext<T>(defaultValue: T): Context<T> {
|
|
299
|
-
// // Create a unique symbol-based context for scoping
|
|
300
|
-
// return createContext(defaultValue);
|
|
301
|
-
// }
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// ============================================================================
|
|
305
|
-
// CONTEXT HELPERS
|
|
306
|
-
// ============================================================================
|
|
307
|
-
/**
|
|
308
|
-
* Create a context for sharing state across component tree
|
|
309
|
-
*
|
|
310
|
-
* Context is lookup-based, works on server and client
|
|
311
|
-
* Supports nested contexts and complex values
|
|
312
|
-
*
|
|
313
|
-
* @example
|
|
314
|
-
* ```typescript
|
|
315
|
-
* // Simple context
|
|
316
|
-
* const ThemeContext = createContext('light');
|
|
317
|
-
*
|
|
318
|
-
* // Complex context with object
|
|
319
|
-
* const AppContext = createContext({
|
|
320
|
-
* theme: 'light',
|
|
321
|
-
* toggleTheme: () => {}
|
|
322
|
-
* });
|
|
323
|
-
*
|
|
324
|
-
* // Usage in JSX
|
|
325
|
-
* <ThemeContext.Provider value="dark">
|
|
326
|
-
* <Child />
|
|
327
|
-
* </ThemeContext.Provider>
|
|
328
|
-
*
|
|
329
|
-
* // Usage in Lit-html
|
|
330
|
-
* html`
|
|
331
|
-
* <${ThemeContext.Provider} value=${{ theme: 'dark', toggleTheme }}>
|
|
332
|
-
* <${Child} />
|
|
333
|
-
* </${ThemeContext.Provider}>
|
|
334
|
-
* `
|
|
335
|
-
* ```
|
|
336
|
-
*/
|
|
337
|
-
/**
|
|
338
|
-
* Creates a scoped context that only exists within a specific component tree
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* ```typescript
|
|
342
|
-
* import { createContext } from '@fluixi/core';
|
|
343
|
-
*
|
|
344
|
-
* // This context won't leak to parent or sibling components
|
|
345
|
-
* const LocalContext = createContext({ data: 'local' });
|
|
346
|
-
* ```
|
|
347
|
-
* @example
|
|
348
|
-
* ```typescript
|
|
349
|
-
* // Simple context
|
|
350
|
-
* const ThemeContext = createContext('light');
|
|
351
|
-
*
|
|
352
|
-
* // Complex context with object
|
|
353
|
-
* const AppContext = createContext({
|
|
354
|
-
* theme: 'light',
|
|
355
|
-
* toggleTheme: () => {}
|
|
356
|
-
* });
|
|
357
|
-
*
|
|
358
|
-
* // Usage in JSX
|
|
359
|
-
* <ThemeContext.Provider value="dark">
|
|
360
|
-
* <Child />
|
|
361
|
-
* </ThemeContext.Provider>
|
|
362
|
-
*
|
|
363
|
-
* // Usage in Lit-html
|
|
364
|
-
* html`
|
|
365
|
-
* <${ThemeContext.Provider} value=${{ theme: 'dark', toggleTheme }}>
|
|
366
|
-
* <${Child} />
|
|
367
|
-
* </${ThemeContext.Provider}>
|
|
368
|
-
* `
|
|
369
|
-
* ```
|
|
83
|
+
* `useContext` for a value that has to come from a Provider: throws when none is above,
|
|
84
|
+
* instead of handing back the default. For parts that only work inside their parent, a tab
|
|
85
|
+
* outside its tabs.
|
|
370
86
|
*/
|
|
371
|
-
export function
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (props.debug === true) {
|
|
376
|
-
}
|
|
377
|
-
// Pass children LAZILY (a getter). The reactive Provider reads children inside
|
|
378
|
-
// its render effect, after stamping the context on the owner. Reading
|
|
379
|
-
// props.children here (e.g. `children: props.children`) evaluates the imperative
|
|
380
|
-
// `get children()` getter eagerly, so the children, and any <Outlet/> in them,
|
|
381
|
-
// render before the context is available.
|
|
382
|
-
return context.Provider({
|
|
383
|
-
value: props.value,
|
|
384
|
-
get children() {
|
|
385
|
-
return props.children;
|
|
386
|
-
},
|
|
387
|
-
debug: props.debug,
|
|
388
|
-
});
|
|
389
|
-
};
|
|
390
|
-
// For JSX: Provider.$$typeof support
|
|
391
|
-
if (typeof Symbol !== 'undefined' && Symbol.for) {
|
|
392
|
-
Provider.$$typeof = Symbol.for('react.provider');
|
|
87
|
+
export function requireContext(context, message) {
|
|
88
|
+
for (let owner = getOwner(); owner; owner = owner.parent) {
|
|
89
|
+
if (owner.contexts?.has(context.id))
|
|
90
|
+
return useContext(context);
|
|
393
91
|
}
|
|
394
|
-
Provider
|
|
395
|
-
return {
|
|
396
|
-
id: context.id,
|
|
397
|
-
Provider: asProvider(Provider),
|
|
398
|
-
defaultValue: defaultValue,
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Use context value from nearest Provider
|
|
403
|
-
*
|
|
404
|
-
* Lookup-based, no subscriptions on server
|
|
405
|
-
*
|
|
406
|
-
* @example
|
|
407
|
-
* ```typescript
|
|
408
|
-
* const Child = () => {
|
|
409
|
-
* const theme = useContext(ThemeContext);
|
|
410
|
-
* const { theme: currentTheme, toggleTheme } = useContext(AppContext);
|
|
411
|
-
*
|
|
412
|
-
* return html`
|
|
413
|
-
* <div>
|
|
414
|
-
* <p>Theme: ${currentTheme}</p>
|
|
415
|
-
* <button @click=${toggleTheme}>Toggle</button>
|
|
416
|
-
* </div>
|
|
417
|
-
* `;
|
|
418
|
-
* };
|
|
419
|
-
* ```
|
|
420
|
-
*/
|
|
421
|
-
export function useContext(context) {
|
|
422
|
-
// Use reactive context lookup: walks up Owner chain
|
|
423
|
-
const value = useReactiveContext(context);
|
|
424
|
-
return value !== undefined ? value : context.defaultValue;
|
|
92
|
+
throw new Error(message ?? `[fluixi] No Provider for ${context.id.description ?? 'this context'} above this component.`);
|
|
425
93
|
}
|
|
@@ -1,83 +1,24 @@
|
|
|
1
1
|
// src/lib/context/context.ts
|
|
2
2
|
import { asProvider } from "@fluixi/dom";
|
|
3
|
-
import { createContext as createReactiveContext, useContext as useReactiveContext } from "@fluixi/reactive/signal";
|
|
4
3
|
import {
|
|
5
|
-
|
|
4
|
+
createContext as createReactiveContext,
|
|
5
|
+
getOwner,
|
|
6
|
+
useContext as useReactiveContext
|
|
6
7
|
} from "@fluixi/reactive/signal";
|
|
7
|
-
var _warnedEagerProvider = false;
|
|
8
|
-
function warnEagerProvider() {
|
|
9
|
-
if (_warnedEagerProvider) return;
|
|
10
|
-
_warnedEagerProvider = true;
|
|
11
|
-
if (typeof process !== "undefined" && true) return;
|
|
12
|
-
console.warn(
|
|
13
|
-
"[fluixi] createProvider({ context, value, children }) reads `children` eagerly: the object literal materialises `children` BEFORE the context is stamped, so a routed <Outlet/> that has siblings inside the provider renders detached and its descendants see no context under SSR. Use the lazy form instead:\n createProvider(Context, value, props) // children stays a getter\n // or JSX: <Context.Provider value={...}>{props.children}</Context.Provider>"
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
function createProvider(a, value, childProps) {
|
|
17
|
-
const isLazyForm = !!a?.Provider;
|
|
18
|
-
const context = isLazyForm ? a : a.context;
|
|
19
|
-
const rawValue = isLazyForm ? value : a.value;
|
|
20
|
-
const debug = isLazyForm ? false : a.debug ?? false;
|
|
21
|
-
if (!isLazyForm) warnEagerProvider();
|
|
22
|
-
const resolvedValue = typeof rawValue === "function" ? rawValue() : rawValue;
|
|
23
|
-
const sourceProps = isLazyForm ? childProps : a;
|
|
24
|
-
return context.Provider({
|
|
25
|
-
value: resolvedValue,
|
|
26
|
-
get children() {
|
|
27
|
-
return sourceProps?.children;
|
|
28
|
-
},
|
|
29
|
-
debug
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
function createContextWithProvider(defaultValue) {
|
|
33
|
-
const context = createContext(defaultValue);
|
|
34
|
-
const provider = (props) => {
|
|
35
|
-
return createProvider(context, props.value, props);
|
|
36
|
-
};
|
|
37
|
-
return [context, provider];
|
|
38
|
-
}
|
|
39
|
-
function composeProviders(contexts, children) {
|
|
40
|
-
if (contexts.length === 0) {
|
|
41
|
-
return readChildren(() => children);
|
|
42
|
-
}
|
|
43
|
-
const [context, value] = contexts[0];
|
|
44
|
-
const remaining = contexts.slice(1);
|
|
45
|
-
return createProvider(context, value, {
|
|
46
|
-
get children() {
|
|
47
|
-
return composeProviders(remaining, children);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function createConsumer(context) {
|
|
52
|
-
return (props) => {
|
|
53
|
-
const value = useContext(context);
|
|
54
|
-
return props.children(value);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function useProvide(context, factory) {
|
|
58
|
-
const existing = useContext(context);
|
|
59
|
-
if (existing === context.defaultValue) {
|
|
60
|
-
return factory();
|
|
61
|
-
}
|
|
62
|
-
return existing;
|
|
63
|
-
}
|
|
64
8
|
function createContext(defaultValue, name) {
|
|
65
9
|
const context = createReactiveContext(defaultValue, name);
|
|
66
|
-
const Provider = (props) =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
10
|
+
const Provider = (props) => (
|
|
11
|
+
// Children stay a getter: the reactive Provider reads them after stamping the value on
|
|
12
|
+
// its owner. Reading `props.children` here would render them, and any <Outlet/> in them,
|
|
13
|
+
// before the context exists.
|
|
14
|
+
context.Provider({
|
|
70
15
|
value: props.value,
|
|
71
16
|
get children() {
|
|
72
17
|
return props.children;
|
|
73
18
|
},
|
|
74
19
|
debug: props.debug
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
if (typeof Symbol !== "undefined" && Symbol.for) {
|
|
78
|
-
Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
|
|
79
|
-
}
|
|
80
|
-
Provider.$$typeof = /* @__PURE__ */ Symbol.for("react.provider");
|
|
20
|
+
})
|
|
21
|
+
);
|
|
81
22
|
return {
|
|
82
23
|
id: context.id,
|
|
83
24
|
Provider: asProvider(Provider),
|
|
@@ -88,12 +29,36 @@ function useContext(context) {
|
|
|
88
29
|
const value = useReactiveContext(context);
|
|
89
30
|
return value !== void 0 ? value : context.defaultValue;
|
|
90
31
|
}
|
|
32
|
+
function createProvider(context, value, props) {
|
|
33
|
+
const resolved = typeof value === "function" ? value() : value;
|
|
34
|
+
return context.Provider({
|
|
35
|
+
value: resolved,
|
|
36
|
+
get children() {
|
|
37
|
+
return props.children;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function composeProviders(providers, props) {
|
|
42
|
+
const outermost = providers.reduceRight(
|
|
43
|
+
(inner, [context, value]) => ({
|
|
44
|
+
get children() {
|
|
45
|
+
return createProvider(context, value, inner);
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
props
|
|
49
|
+
);
|
|
50
|
+
return outermost.children;
|
|
51
|
+
}
|
|
52
|
+
function requireContext(context, message) {
|
|
53
|
+
for (let owner = getOwner(); owner; owner = owner.parent) {
|
|
54
|
+
if (owner.contexts?.has(context.id)) return useContext(context);
|
|
55
|
+
}
|
|
56
|
+
throw new Error(message ?? `[fluixi] No Provider for ${context.id.description ?? "this context"} above this component.`);
|
|
57
|
+
}
|
|
91
58
|
export {
|
|
92
59
|
composeProviders,
|
|
93
|
-
createConsumer,
|
|
94
60
|
createContext,
|
|
95
|
-
createContextWithProvider,
|
|
96
61
|
createProvider,
|
|
97
|
-
|
|
98
|
-
|
|
62
|
+
requireContext,
|
|
63
|
+
useContext
|
|
99
64
|
};
|