@fluixi/core 1.0.0-alpha.92 → 1.0.0-alpha.93

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.
Files changed (113) hide show
  1. package/dist/cdn/core.global.js +2 -2
  2. package/dist/src/cdn/{chunk-MDJLC3PE.mjs → chunk-7XM7Q5VR.mjs} +271 -229
  3. package/dist/src/cdn/core.cjs +360 -253
  4. package/dist/src/cdn/core.mjs +92 -20
  5. package/dist/src/cdn/{router-JYFZP3EZ.mjs → router-VOY43YNM.mjs} +7 -3
  6. package/dist/src/index.cjs +547 -232
  7. package/dist/src/index.mjs +547 -223
  8. package/dist/src/lib/client/chunk-QT3RFKH6.mjs +53 -0
  9. package/dist/src/lib/client/index.cjs +310 -176
  10. package/dist/src/lib/client/index.d.ts.map +1 -1
  11. package/dist/src/lib/client/index.js +33 -18
  12. package/dist/src/lib/client/index.mjs +86 -15
  13. package/dist/src/lib/client/{router-PHLUYVQU.mjs → router-ADTS55OP.mjs} +244 -99
  14. package/dist/src/lib/context/context.cjs +35 -72
  15. package/dist/src/lib/context/context.d.ts +45 -291
  16. package/dist/src/lib/context/context.d.ts.map +1 -1
  17. package/dist/src/lib/context/context.js +65 -397
  18. package/dist/src/lib/context/context.mjs +38 -73
  19. package/dist/src/lib/context/index.cjs +35 -72
  20. package/dist/src/lib/context/index.d.ts +2 -2
  21. package/dist/src/lib/context/index.d.ts.map +1 -1
  22. package/dist/src/lib/context/index.js +1 -5
  23. package/dist/src/lib/context/index.mjs +38 -73
  24. package/dist/src/lib/core.cjs +546 -231
  25. package/dist/src/lib/core.d.ts +3 -1
  26. package/dist/src/lib/core.d.ts.map +1 -1
  27. package/dist/src/lib/core.js +5 -3
  28. package/dist/src/lib/core.mjs +546 -222
  29. package/dist/src/lib/env/index.cjs +1 -17
  30. package/dist/src/lib/env/index.d.ts +2 -14
  31. package/dist/src/lib/env/index.d.ts.map +1 -1
  32. package/dist/src/lib/env/index.js +2 -26
  33. package/dist/src/lib/env/index.mjs +1 -17
  34. package/dist/src/lib/i18n/index.cjs +17 -2
  35. package/dist/src/lib/i18n/index.d.ts +10 -0
  36. package/dist/src/lib/i18n/index.d.ts.map +1 -1
  37. package/dist/src/lib/i18n/index.js +17 -1
  38. package/dist/src/lib/i18n/index.mjs +17 -2
  39. package/dist/src/lib/index.cjs +64 -99
  40. package/dist/src/lib/index.mjs +43 -76
  41. package/dist/src/lib/isomorphic.cjs +9 -4
  42. package/dist/src/lib/isomorphic.d.ts +9 -2
  43. package/dist/src/lib/isomorphic.d.ts.map +1 -1
  44. package/dist/src/lib/isomorphic.js +11 -4
  45. package/dist/src/lib/isomorphic.mjs +9 -4
  46. package/dist/src/lib/jsx-runtime/index.cjs +1 -1
  47. package/dist/src/lib/jsx-runtime/index.mjs +1 -1
  48. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -1
  49. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -1
  50. package/dist/src/lib/plugins/fluixi-routes-plugin.cjs +2 -2
  51. package/dist/src/lib/plugins/fluixi-routes-plugin.mjs +2 -2
  52. package/dist/src/lib/plugins/index.cjs +3 -3
  53. package/dist/src/lib/plugins/index.mjs +3 -3
  54. package/dist/src/lib/plugins/route-codegen.cjs +2 -2
  55. package/dist/src/lib/plugins/route-codegen.js +1 -1
  56. package/dist/src/lib/plugins/route-codegen.mjs +2 -2
  57. package/dist/src/lib/render/index.cjs +1 -1
  58. package/dist/src/lib/render/index.mjs +1 -1
  59. package/dist/src/lib/render/versions.cjs +1 -1
  60. package/dist/src/lib/render/versions.mjs +1 -1
  61. package/dist/src/lib/router/data.cjs +113 -115
  62. package/dist/src/lib/router/data.d.ts +63 -10
  63. package/dist/src/lib/router/data.d.ts.map +1 -1
  64. package/dist/src/lib/router/data.js +158 -68
  65. package/dist/src/lib/router/data.mjs +112 -112
  66. package/dist/src/lib/router/index.cjs +260 -163
  67. package/dist/src/lib/router/index.d.ts +22 -2
  68. package/dist/src/lib/router/index.d.ts.map +1 -1
  69. package/dist/src/lib/router/index.js +165 -22
  70. package/dist/src/lib/router/index.mjs +244 -143
  71. package/dist/src/lib/server/index.cjs +1 -1
  72. package/dist/src/lib/server/index.mjs +1 -1
  73. package/dist/src/lib/storage/cache.cjs +51 -0
  74. package/dist/src/lib/storage/cache.d.ts +18 -0
  75. package/dist/src/lib/storage/cache.d.ts.map +1 -0
  76. package/dist/src/lib/storage/cache.js +42 -0
  77. package/dist/src/lib/storage/cache.mjs +30 -0
  78. package/dist/src/lib/storage/index.cjs +246 -0
  79. package/dist/src/lib/storage/index.d.ts +83 -0
  80. package/dist/src/lib/storage/index.d.ts.map +1 -0
  81. package/dist/src/lib/storage/index.js +179 -0
  82. package/dist/src/lib/storage/index.mjs +231 -0
  83. package/dist/src/lib/storage/persisted.cjs +91 -0
  84. package/dist/src/lib/storage/persisted.d.ts +53 -0
  85. package/dist/src/lib/storage/persisted.d.ts.map +1 -0
  86. package/dist/src/lib/storage/persisted.js +75 -0
  87. package/dist/src/lib/storage/persisted.mjs +70 -0
  88. package/dist/src/lib/theme/color-scheme.cjs +281 -0
  89. package/dist/src/lib/theme/color-scheme.d.ts +61 -0
  90. package/dist/src/lib/theme/color-scheme.d.ts.map +1 -0
  91. package/dist/src/lib/theme/color-scheme.js +120 -0
  92. package/dist/src/lib/theme/color-scheme.mjs +265 -0
  93. package/dist/src/lib/theme/index.cjs +194 -37
  94. package/dist/src/lib/theme/index.d.ts +23 -11
  95. package/dist/src/lib/theme/index.d.ts.map +1 -1
  96. package/dist/src/lib/theme/index.js +73 -32
  97. package/dist/src/lib/theme/index.mjs +197 -35
  98. package/dist/src/lib/theme/provider.cjs +346 -0
  99. package/dist/src/lib/theme/provider.d.ts +34 -0
  100. package/dist/src/lib/theme/provider.d.ts.map +1 -0
  101. package/dist/src/lib/theme/provider.js +60 -0
  102. package/dist/src/lib/theme/provider.mjs +332 -0
  103. package/dist/src/routes.cjs +1 -1
  104. package/dist/src/routes.mjs +1 -1
  105. package/dist/src/utils.cjs +1 -1
  106. package/dist/src/utils.mjs +1 -1
  107. package/dist/src/version.generated.cjs +1 -1
  108. package/dist/src/version.generated.d.ts +1 -1
  109. package/dist/src/version.generated.js +1 -1
  110. package/dist/src/version.generated.mjs +1 -1
  111. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  112. package/package.json +18 -11
  113. 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
- * Provider component that supplies a context value to its descendants
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
- * const ThemeContext = createContext({ primaryColor: '#007bff' });
20
- *
21
- * class App extends FluixiElement {
22
- * private [theme, setTheme] = createSignal({ primaryColor: '#28a745' });
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
- let _warnedEagerProvider = false;
68
- function warnEagerProvider() {
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
- * Creates a context with a provider component
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
- * ```typescript
119
- * import { createContextWithProvider } from '@fluixi/core/context';
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
- * class App extends FluixiElement {
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 createContextWithProvider(defaultValue) {
149
- const context = createContext(defaultValue);
150
- const provider = (props) => {
151
- // Lazy-safe form so children (which may contain an <Outlet/>) render inside
152
- // the stamped scope.
153
- return createProvider(context, props.value, props);
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
- return [context, provider];
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
- * Creates multiple context providers in a nested structure
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
- * ```typescript
164
- * import { composeProviders, createContext } from '@fluixi/core/context';
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 composeProviders(contexts, children) {
191
- if (contexts.length === 0) {
192
- return readChildren(() => children);
193
- }
194
- const [context, value] = contexts[0];
195
- const remaining = contexts.slice(1);
196
- // Lazy-safe form: defer the nested children behind a getter so each provider
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
- * Creates a context consumer component
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
- * ```typescript
209
- * import { createContext, createConsumer } from '@fluixi/core/context';
210
- * import { html } from 'lit';
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 createConsumer(context) {
231
- return (props) => {
232
- const value = useContext(context);
233
- return props.children(value);
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
- * Hook to provide and consume a context in one component
238
- *
239
- * Useful for creating context-based stores or services.
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 createContext(defaultValue, name) {
372
- const context = createReactiveContext(defaultValue, name);
373
- // Wrap Provider to work with both JSX and Lit-html
374
- const Provider = (props) => {
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.$$typeof = Symbol.for('react.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
- readChildren
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
- if (props.debug === true) {
68
- }
69
- return context.Provider({
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
- useContext,
98
- useProvide
62
+ requireContext,
63
+ useContext
99
64
  };