@aweebit/react-essentials 0.9.0 → 0.10.1

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.
@@ -0,0 +1,71 @@
1
+ import type { Context, default as React } from 'react';
2
+ /**
3
+ * The return type of {@linkcode contextualize}
4
+ *
5
+ * @see
6
+ * {@linkcode contextualize},
7
+ * {@linkcode ContextualizeWith}
8
+ */
9
+ export type ContextualizePipe = {
10
+ with: ContextualizeWith;
11
+ end: () => React.JSX.Element;
12
+ };
13
+ /**
14
+ * @see
15
+ * {@linkcode contextualize},
16
+ * {@linkcode ContextualizePipe}
17
+ */
18
+ export type ContextualizeWith = <T>(Context: Context<T>, value: NoInfer<T>) => ContextualizePipe;
19
+ /**
20
+ * An alternative way to provide context values to component trees that avoids
21
+ * ever-increasing indentation
22
+ *
23
+ * A context-specific version of the more general {@linkcode wrapJSX} function.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * // Before:
28
+ * return (
29
+ * <CourseIdContext.Provider value={courseId}>
30
+ * <DeckIdContext.Provider value={deckId}>
31
+ * <FlashcardsContext.Provider value={flashcards}>
32
+ * <EventHandlersContext.Provider value={eventHandlers}>
33
+ * <Header />
34
+ * <Main />
35
+ * <Footer />
36
+ * </EventHandlersContext.Provider>
37
+ * </FlashcardsContext.Provider>
38
+ * </DeckIdContext.Provider>
39
+ * </CourseIdContext.Provider>
40
+ * );
41
+ *
42
+ * // After:
43
+ * const jsx = (
44
+ * <>
45
+ * <Header />
46
+ * <Main />
47
+ * <Footer />
48
+ * </>
49
+ * );
50
+ *
51
+ * return contextualize(jsx)
52
+ * .with(EventHandlersContext, eventHandlers)
53
+ * .with(FlashcardsContext, flashcards)
54
+ * .with(DeckIdContext, deckId)
55
+ * .with(CourseIdContext, courseId)
56
+ * .end();
57
+ * ```
58
+ *
59
+ * @param jsx The JSX to contextualize
60
+ *
61
+ * @returns An object with the following properties:
62
+ * - `with`: a function that accepts a context `Context` and a value `value` for
63
+ * it as arguments and returns
64
+ * `contextualize(<Context.Provider value={value}>{jsx}</Context.Provider>)`
65
+ * - `end`: a function that returns `jsx`
66
+ *
67
+ * @see
68
+ * {@linkcode ContextualizePipe}
69
+ */
70
+ export declare function contextualize(jsx: React.JSX.Element): ContextualizePipe;
71
+ //# sourceMappingURL=contextualize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextualize.d.ts","sourceRoot":"","sources":["../../src/misc/contextualize.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC;AAKvD;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;CAC9B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,EAChC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KACd,iBAAiB,CAAC;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,iBAAiB,CAWvE"}
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * An alternative way to provide context values to component trees that avoids
4
+ * ever-increasing indentation
5
+ *
6
+ * A context-specific version of the more general {@linkcode wrapJSX} function.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Before:
11
+ * return (
12
+ * <CourseIdContext.Provider value={courseId}>
13
+ * <DeckIdContext.Provider value={deckId}>
14
+ * <FlashcardsContext.Provider value={flashcards}>
15
+ * <EventHandlersContext.Provider value={eventHandlers}>
16
+ * <Header />
17
+ * <Main />
18
+ * <Footer />
19
+ * </EventHandlersContext.Provider>
20
+ * </FlashcardsContext.Provider>
21
+ * </DeckIdContext.Provider>
22
+ * </CourseIdContext.Provider>
23
+ * );
24
+ *
25
+ * // After:
26
+ * const jsx = (
27
+ * <>
28
+ * <Header />
29
+ * <Main />
30
+ * <Footer />
31
+ * </>
32
+ * );
33
+ *
34
+ * return contextualize(jsx)
35
+ * .with(EventHandlersContext, eventHandlers)
36
+ * .with(FlashcardsContext, flashcards)
37
+ * .with(DeckIdContext, deckId)
38
+ * .with(CourseIdContext, courseId)
39
+ * .end();
40
+ * ```
41
+ *
42
+ * @param jsx The JSX to contextualize
43
+ *
44
+ * @returns An object with the following properties:
45
+ * - `with`: a function that accepts a context `Context` and a value `value` for
46
+ * it as arguments and returns
47
+ * `contextualize(<Context.Provider value={value}>{jsx}</Context.Provider>)`
48
+ * - `end`: a function that returns `jsx`
49
+ *
50
+ * @see
51
+ * {@linkcode ContextualizePipe}
52
+ */
53
+ export function contextualize(jsx) {
54
+ return {
55
+ with(Context, value) {
56
+ return contextualize(_jsx(Context.Provider, { value: value, children: jsx }));
57
+ },
58
+ end() {
59
+ return jsx;
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=contextualize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextualize.js","sourceRoot":"","sources":["../../src/misc/contextualize.tsx"],"names":[],"mappings":";AA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,OAAO;QACL,IAAI,CAAI,OAAmB,EAAE,KAAQ;YACnC,OAAO,aAAa,CAClB,KAAC,OAAO,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,GAAG,GAAoB,CACzD,CAAC;QACJ,CAAC;QACD,GAAG;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,33 +1,5 @@
1
- import { type Context, type Provider } from 'react';
1
+ import { type Context } from 'react';
2
2
  import type { ArgumentFallback } from '../utils.js';
3
- /**
4
- * The return type of {@linkcode createSafeContext}
5
- *
6
- * @see
7
- * {@linkcode createSafeContext},
8
- * {@linkcode RestrictedContext}
9
- */
10
- export type SafeContext<DisplayName extends string, T> = {
11
- [K in `${DisplayName}Context`]: RestrictedContext<T>;
12
- } & {
13
- [K in `use${DisplayName}`]: () => T;
14
- };
15
- /**
16
- * A React context with a required `displayName` and the obsolete `Consumer`
17
- * property purposefully omitted so that it is impossible to pass the context
18
- * as an argument to `useContext` or `use` (the hook produced with
19
- * {@linkcode createSafeContext} should be used instead)
20
- *
21
- * @see
22
- * {@linkcode createSafeContext}
23
- */
24
- export type RestrictedContext<T> = Context<T> extends Provider<T> ? {
25
- Provider: Provider<T>;
26
- displayName: string;
27
- } & Provider<T> : {
28
- Provider: Provider<T>;
29
- displayName: string;
30
- };
31
3
  /**
32
4
  * For a given type `T`, returns a function that produces both a context of that
33
5
  * type and a hook that returns the current context value if one was provided,
@@ -46,7 +18,7 @@ export type RestrictedContext<T> = Context<T> extends Provider<T> ? {
46
18
  * Right,
47
19
  * }
48
20
  *
49
- * // Before
21
+ * // Before:
50
22
  * const DirectionContext = createContext<Direction | undefined>(undefined);
51
23
  * DirectionContext.displayName = 'DirectionContext';
52
24
  *
@@ -63,7 +35,7 @@ export type RestrictedContext<T> = Context<T> extends Provider<T> ? {
63
35
  * return direction;
64
36
  * };
65
37
  *
66
- * // After
38
+ * // After:
67
39
  * const { DirectionContext, useDirection } =
68
40
  * createSafeContext<Direction>()('Direction'); // That's it :)
69
41
  *
@@ -83,9 +55,6 @@ export type RestrictedContext<T> = Context<T> extends Provider<T> ? {
83
55
  * - ``` `${displayName}Context` ``` (e.g. `DirectionContext`): the context
84
56
  * - ``` `use${displayName}` ``` (e.g. `useDirection`): a hook that returns the
85
57
  * current context value if one was provided, or throws an error otherwise
86
- *
87
- * @see
88
- * {@linkcode SafeContext}
89
58
  */
90
- export declare function createSafeContext<T = never>(): <DisplayName extends string>(displayName: [T] extends [never] ? never : ArgumentFallback<DisplayName, never, string>) => SafeContext<DisplayName, T>;
59
+ export declare function createSafeContext<T = never>(): <DisplayName extends string>(displayName: [T] extends [never] ? never : ArgumentFallback<DisplayName, never, string>) => { [K in `${DisplayName}Context`]: Context<T>; } & { [K in `use${DisplayName}`]: () => T; };
91
60
  //# sourceMappingURL=createSafeContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createSafeContext.d.ts","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAA6B,MAAM,OAAO,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,CAAC,WAAW,SAAS,MAAM,EAAE,CAAC,IAAI;KACtD,CAAC,IAAI,GAAG,WAAW,SAAS,GAAG,iBAAiB,CAAC,CAAC,CAAC;CACrD,GAAG;KACD,CAAC,IAAI,MAAM,WAAW,EAAE,GAAG,MAAM,CAAC;CACpC,CAAC;AAEF;;;;;;;;GAQG;AAIH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC7B,OAAO,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAC1B;IAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,GAC5D;IAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,KAAK,MACjC,WAAW,SAAS,MAAM,EAChC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC5B,KAAK,GACL,gBAAgB,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAC/C,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,CAsB/B"}
1
+ {"version":3,"file":"createSafeContext.d.ts","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAA6B,MAAM,OAAO,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,KAAK,MACjC,WAAW,SAAS,MAAM,EAChC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC5B,KAAK,GACL,gBAAgB,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAC/C,GAAG,CAAC,IAAI,GAAG,WAAW,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,GAAE,GAAG,GACjD,CAAC,IAAI,MAAM,WAAW,EAAE,GAAG,MAAM,CAAC,GACpC,CAsBF"}
@@ -18,7 +18,7 @@ const moValueSymbol = Symbol('noValue');
18
18
  * Right,
19
19
  * }
20
20
  *
21
- * // Before
21
+ * // Before:
22
22
  * const DirectionContext = createContext<Direction | undefined>(undefined);
23
23
  * DirectionContext.displayName = 'DirectionContext';
24
24
  *
@@ -35,7 +35,7 @@ const moValueSymbol = Symbol('noValue');
35
35
  * return direction;
36
36
  * };
37
37
  *
38
- * // After
38
+ * // After:
39
39
  * const { DirectionContext, useDirection } =
40
40
  * createSafeContext<Direction>()('Direction'); // That's it :)
41
41
  *
@@ -55,9 +55,6 @@ const moValueSymbol = Symbol('noValue');
55
55
  * - ``` `${displayName}Context` ``` (e.g. `DirectionContext`): the context
56
56
  * - ``` `use${displayName}` ``` (e.g. `useDirection`): a hook that returns the
57
57
  * current context value if one was provided, or throws an error otherwise
58
- *
59
- * @see
60
- * {@linkcode SafeContext}
61
58
  */
62
59
  export function createSafeContext() {
63
60
  return (displayName) => {
@@ -1 +1 @@
1
- {"version":3,"file":"createSafeContext.js","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAG/E,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAgCxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CACL,WAEgD,EACnB,EAAE;QAC/B,MAAM,WAAW,GAAG,GAAG,WAA0B,SAAkB,CAAC;QACpE,MAAM,QAAQ,GAAG,MAAM,WAA0B,EAAW,CAAC;QAE7D,MAAM,OAAO,GAAG,aAAa,CAA2B,aAAa,CAAC,CAAC;QACvE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAElC,OAAO;YACL,CAAC,WAAW,CAAC,EAAE,OAA+B;YAC9C,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE;gBACf,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;gBAClC,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,MAAM,WAAW,qBAAqB,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;SAKF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"createSafeContext.js","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAGhE,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CACL,WAEgD,EAGhD,EAAE;QACF,MAAM,WAAW,GAAG,GAAG,WAA0B,SAAkB,CAAC;QACpE,MAAM,QAAQ,GAAG,MAAM,WAA0B,EAAW,CAAC;QAE7D,MAAM,OAAO,GAAG,aAAa,CAA2B,aAAa,CAAC,CAAC;QACvE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAElC,OAAO;YACL,CAAC,WAAW,CAAC,EAAE,OAAqB;YACpC,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE;gBACf,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;gBAClC,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,MAAM,WAAW,qBAAqB,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;SAKF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -1,2 +1,4 @@
1
+ export * from './contextualize.js';
1
2
  export * from './createSafeContext.js';
3
+ export * from './wrapJSX.js';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC"}
@@ -1,2 +1,4 @@
1
+ export * from './contextualize.js';
1
2
  export * from './createSafeContext.js';
3
+ export * from './wrapJSX.js';
2
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { ComponentProps, JSXElementConstructor, default as React } from 'react';
2
+ /**
3
+ * The return type of {@linkcode wrapJSX}
4
+ *
5
+ * @see
6
+ * {@linkcode wrapJSX},
7
+ * {@linkcode WrapJSXWith}
8
+ */
9
+ export type JSXWrapPipe = {
10
+ with: WrapJSXWith;
11
+ end: () => React.JSX.Element;
12
+ };
13
+ /**
14
+ * @see
15
+ * {@linkcode wrapJSX},
16
+ * {@linkcode JSXWrapPipe}
17
+ */
18
+ export type WrapJSXWith = <C extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>>(...args: [
19
+ Component: C,
20
+ ...(Record<never, unknown> extends Omit<ComponentProps<C>, 'children'> ? [
21
+ props?: React.JSX.IntrinsicAttributes & Omit<ComponentProps<C>, 'children'>
22
+ ] : [
23
+ props: React.JSX.IntrinsicAttributes & Omit<ComponentProps<C>, 'children'>
24
+ ])
25
+ ]) => JSXWrapPipe;
26
+ /**
27
+ * An alternative way to compose JSX that avoids ever-increasing indentation
28
+ *
29
+ * A more general version of the context-specific {@linkcode contextualize}
30
+ * function.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * // Before:
35
+ * createRoot(document.getElementById('root')!).render(
36
+ * <StrictMode>
37
+ * <I18nextProvider i18n={i18n}>
38
+ * <QueryClientProvider client={queryClient}>
39
+ * <NuqsAdapter>
40
+ * <ThemeProvider theme={theme}>
41
+ * <ToasterProvider>
42
+ * <App />
43
+ * </ToasterProvider>
44
+ * </ThemeProvider>
45
+ * </NuqsAdapter>
46
+ * </QueryClientProvider>
47
+ * </I18nextProvider>
48
+ * </StrictMode>,
49
+ * );
50
+ *
51
+ * // After:
52
+ * createRoot(document.getElementById('root')!).render(
53
+ * wrapJSX(<App />)
54
+ * .with(ToasterProvider)
55
+ * .with(ThemeProvider, { theme })
56
+ * .with(NuqsAdapter)
57
+ * .with(QueryClientProvider, { client: queryClient })
58
+ * .with(I18nextProvider, { i18n })
59
+ * .with(StrictMode)
60
+ * .end(),
61
+ * );
62
+ * ```
63
+ *
64
+ * @param jsx The JSX to wrap
65
+ *
66
+ * @returns An object with the following properties:
67
+ * - `with`: a function that accepts a component `Component` and props `props`
68
+ * for it as arguments and returns
69
+ * `wrapJSX(<Component {...props}>{jsx}</Component>)`
70
+ * - `end`: a function that returns `jsx`
71
+ *
72
+ * @see
73
+ * {@linkcode JSXWrapPipe}
74
+ */
75
+ export declare function wrapJSX(jsx: React.JSX.Element): JSXWrapPipe;
76
+ //# sourceMappingURL=wrapJSX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapJSX.d.ts","sourceRoot":"","sources":["../../src/misc/wrapJSX.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,OAAO,IAAI,KAAK,EACjB,MAAM,OAAO,CAAC;AAKf;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;CAC9B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAErB,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,iBAAiB,GAAG,qBAAqB,CAAC,GAAG,CAAC,EACjE,GAAG,IAAI,EAAE;IACP,SAAS,EAAE,CAAC;IACZ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAClE;QACE,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,mBAAmB,GACnC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;KACtC,GACD;QACE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,mBAAmB,GAClC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;KACtC,CAAC;CACP,KACE,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,WAAW,CAc3D"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * An alternative way to compose JSX that avoids ever-increasing indentation
4
+ *
5
+ * A more general version of the context-specific {@linkcode contextualize}
6
+ * function.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Before:
11
+ * createRoot(document.getElementById('root')!).render(
12
+ * <StrictMode>
13
+ * <I18nextProvider i18n={i18n}>
14
+ * <QueryClientProvider client={queryClient}>
15
+ * <NuqsAdapter>
16
+ * <ThemeProvider theme={theme}>
17
+ * <ToasterProvider>
18
+ * <App />
19
+ * </ToasterProvider>
20
+ * </ThemeProvider>
21
+ * </NuqsAdapter>
22
+ * </QueryClientProvider>
23
+ * </I18nextProvider>
24
+ * </StrictMode>,
25
+ * );
26
+ *
27
+ * // After:
28
+ * createRoot(document.getElementById('root')!).render(
29
+ * wrapJSX(<App />)
30
+ * .with(ToasterProvider)
31
+ * .with(ThemeProvider, { theme })
32
+ * .with(NuqsAdapter)
33
+ * .with(QueryClientProvider, { client: queryClient })
34
+ * .with(I18nextProvider, { i18n })
35
+ * .with(StrictMode)
36
+ * .end(),
37
+ * );
38
+ * ```
39
+ *
40
+ * @param jsx The JSX to wrap
41
+ *
42
+ * @returns An object with the following properties:
43
+ * - `with`: a function that accepts a component `Component` and props `props`
44
+ * for it as arguments and returns
45
+ * `wrapJSX(<Component {...props}>{jsx}</Component>)`
46
+ * - `end`: a function that returns `jsx`
47
+ *
48
+ * @see
49
+ * {@linkcode JSXWrapPipe}
50
+ */
51
+ export function wrapJSX(jsx) {
52
+ return {
53
+ with(Component, props = {}) {
54
+ return wrapJSX(_jsx(Component, { ...props, children: jsx }));
55
+ },
56
+ end() {
57
+ return jsx;
58
+ },
59
+ };
60
+ }
61
+ //# sourceMappingURL=wrapJSX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapJSX.js","sourceRoot":"","sources":["../../src/misc/wrapJSX.tsx"],"names":[],"mappings":";AA2CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,UAAU,OAAO,CAAC,GAAsB;IAC5C,OAAO;QACL,IAAI,CACF,SAEiC,EACjC,QAAgB,EAAE;YAElB,OAAO,OAAO,CAAC,KAAC,SAAS,OAAK,KAAK,YAAG,GAAG,GAAa,CAAC,CAAC;QAC1D,CAAC;QACD,GAAG;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aweebit/react-essentials",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "repository": "github:aweebit/react-essentials",
6
6
  "main": "dist/index.js",
@@ -10,7 +10,7 @@
10
10
  "prettier": "npm run prettier:base -- .",
11
11
  "lint": "eslint --max-warnings=0",
12
12
  "build": "rimraf dist && tsc -b -f",
13
- "doc": "rimraf README.md && typedoc",
13
+ "doc": "rimraf README.md && typedoc && node scripts/fixReadmeLinks.js && npm run prettier:base -- README.md",
14
14
  "prepack": "npm run build && npm run doc",
15
15
  "prepare": "husky"
16
16
  },
@@ -29,6 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "@eslint/js": "^9.36.0",
32
+ "@types/node": "^22.18.6",
32
33
  "@types/react": "^18.3.24",
33
34
  "eslint": "^9.36.0",
34
35
  "eslint-config-prettier": "^10.1.8",
@@ -0,0 +1,23 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ const rootDir = join(import.meta.dirname, '..');
5
+
6
+ const headerPath = join(rootDir, 'README-header.md');
7
+ const readmePath = join(rootDir, 'README.md');
8
+
9
+ const header = await readFile(headerPath, 'utf8');
10
+ const readme = await readFile(readmePath, 'utf8');
11
+
12
+ const newReadme =
13
+ header +
14
+ readme
15
+ .slice(header.length)
16
+ .replaceAll(
17
+ /\(#useeventlistener((?:-1)?)\)/g,
18
+ (match, /** @type {string} */ suffix) => {
19
+ return `(#useeventlistener${suffix ? '' : '-1'})`;
20
+ },
21
+ );
22
+
23
+ await writeFile(readmePath, newReadme);
@@ -0,0 +1,90 @@
1
+ import type { Context, default as React } from 'react';
2
+
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import type { wrapJSX } from './wrapJSX.js';
5
+
6
+ /**
7
+ * The return type of {@linkcode contextualize}
8
+ *
9
+ * @see
10
+ * {@linkcode contextualize},
11
+ * {@linkcode ContextualizeWith}
12
+ */
13
+ export type ContextualizePipe = {
14
+ with: ContextualizeWith;
15
+ end: () => React.JSX.Element;
16
+ };
17
+
18
+ /**
19
+ * @see
20
+ * {@linkcode contextualize},
21
+ * {@linkcode ContextualizePipe}
22
+ */
23
+ export type ContextualizeWith = <T>(
24
+ Context: Context<T>,
25
+ value: NoInfer<T>,
26
+ ) => ContextualizePipe;
27
+
28
+ /**
29
+ * An alternative way to provide context values to component trees that avoids
30
+ * ever-increasing indentation
31
+ *
32
+ * A context-specific version of the more general {@linkcode wrapJSX} function.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * // Before:
37
+ * return (
38
+ * <CourseIdContext.Provider value={courseId}>
39
+ * <DeckIdContext.Provider value={deckId}>
40
+ * <FlashcardsContext.Provider value={flashcards}>
41
+ * <EventHandlersContext.Provider value={eventHandlers}>
42
+ * <Header />
43
+ * <Main />
44
+ * <Footer />
45
+ * </EventHandlersContext.Provider>
46
+ * </FlashcardsContext.Provider>
47
+ * </DeckIdContext.Provider>
48
+ * </CourseIdContext.Provider>
49
+ * );
50
+ *
51
+ * // After:
52
+ * const jsx = (
53
+ * <>
54
+ * <Header />
55
+ * <Main />
56
+ * <Footer />
57
+ * </>
58
+ * );
59
+ *
60
+ * return contextualize(jsx)
61
+ * .with(EventHandlersContext, eventHandlers)
62
+ * .with(FlashcardsContext, flashcards)
63
+ * .with(DeckIdContext, deckId)
64
+ * .with(CourseIdContext, courseId)
65
+ * .end();
66
+ * ```
67
+ *
68
+ * @param jsx The JSX to contextualize
69
+ *
70
+ * @returns An object with the following properties:
71
+ * - `with`: a function that accepts a context `Context` and a value `value` for
72
+ * it as arguments and returns
73
+ * `contextualize(<Context.Provider value={value}>{jsx}</Context.Provider>)`
74
+ * - `end`: a function that returns `jsx`
75
+ *
76
+ * @see
77
+ * {@linkcode ContextualizePipe}
78
+ */
79
+ export function contextualize(jsx: React.JSX.Element): ContextualizePipe {
80
+ return {
81
+ with<T>(Context: Context<T>, value: T) {
82
+ return contextualize(
83
+ <Context.Provider value={value}>{jsx}</Context.Provider>,
84
+ );
85
+ },
86
+ end() {
87
+ return jsx;
88
+ },
89
+ };
90
+ }
@@ -1,38 +1,8 @@
1
- import { type Context, type Provider, createContext, useContext } from 'react';
1
+ import { type Context, createContext, useContext } from 'react';
2
2
  import type { ArgumentFallback } from '../utils.js';
3
3
 
4
4
  const moValueSymbol = Symbol('noValue');
5
5
 
6
- /**
7
- * The return type of {@linkcode createSafeContext}
8
- *
9
- * @see
10
- * {@linkcode createSafeContext},
11
- * {@linkcode RestrictedContext}
12
- */
13
- export type SafeContext<DisplayName extends string, T> = {
14
- [K in `${DisplayName}Context`]: RestrictedContext<T>;
15
- } & {
16
- [K in `use${DisplayName}`]: () => T;
17
- };
18
-
19
- /**
20
- * A React context with a required `displayName` and the obsolete `Consumer`
21
- * property purposefully omitted so that it is impossible to pass the context
22
- * as an argument to `useContext` or `use` (the hook produced with
23
- * {@linkcode createSafeContext} should be used instead)
24
- *
25
- * @see
26
- * {@linkcode createSafeContext}
27
- */
28
- // The type is conditional so that both React 18 and 19 are correctly supported.
29
- // The code duplication is necessary for the type to be displayed correctly by
30
- // TypeDoc.
31
- export type RestrictedContext<T> =
32
- Context<T> extends Provider<T>
33
- ? { Provider: Provider<T>; displayName: string } & Provider<T>
34
- : { Provider: Provider<T>; displayName: string };
35
-
36
6
  /**
37
7
  * For a given type `T`, returns a function that produces both a context of that
38
8
  * type and a hook that returns the current context value if one was provided,
@@ -51,7 +21,7 @@ export type RestrictedContext<T> =
51
21
  * Right,
52
22
  * }
53
23
  *
54
- * // Before
24
+ * // Before:
55
25
  * const DirectionContext = createContext<Direction | undefined>(undefined);
56
26
  * DirectionContext.displayName = 'DirectionContext';
57
27
  *
@@ -68,7 +38,7 @@ export type RestrictedContext<T> =
68
38
  * return direction;
69
39
  * };
70
40
  *
71
- * // After
41
+ * // After:
72
42
  * const { DirectionContext, useDirection } =
73
43
  * createSafeContext<Direction>()('Direction'); // That's it :)
74
44
  *
@@ -88,16 +58,15 @@ export type RestrictedContext<T> =
88
58
  * - ``` `${displayName}Context` ``` (e.g. `DirectionContext`): the context
89
59
  * - ``` `use${displayName}` ``` (e.g. `useDirection`): a hook that returns the
90
60
  * current context value if one was provided, or throws an error otherwise
91
- *
92
- * @see
93
- * {@linkcode SafeContext}
94
61
  */
95
62
  export function createSafeContext<T = never>() {
96
63
  return <DisplayName extends string>(
97
64
  displayName: [T] extends [never]
98
65
  ? never
99
66
  : ArgumentFallback<DisplayName, never, string>,
100
- ): SafeContext<DisplayName, T> => {
67
+ ): { [K in `${DisplayName}Context`]: Context<T> } & {
68
+ [K in `use${DisplayName}`]: () => T;
69
+ } => {
101
70
  const contextName = `${displayName as DisplayName}Context` as const;
102
71
  const hookName = `use${displayName as DisplayName}` as const;
103
72
 
@@ -105,7 +74,7 @@ export function createSafeContext<T = never>() {
105
74
  Context.displayName = contextName;
106
75
 
107
76
  return {
108
- [contextName]: Context as RestrictedContext<T>,
77
+ [contextName]: Context as Context<T>,
109
78
  [hookName]: () => {
110
79
  const value = useContext(Context);
111
80
  if (value === moValueSymbol) {
@@ -114,7 +83,7 @@ export function createSafeContext<T = never>() {
114
83
  return value;
115
84
  },
116
85
  } as {
117
- [K in typeof contextName]: RestrictedContext<T>;
86
+ [K in typeof contextName]: Context<T>;
118
87
  } & {
119
88
  [K in typeof hookName]: () => T;
120
89
  };
package/src/misc/index.ts CHANGED
@@ -1 +1,3 @@
1
+ export * from './contextualize.js';
1
2
  export * from './createSafeContext.js';
3
+ export * from './wrapJSX.js';