@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.
- package/README.md +410 -67
- package/dist/misc/contextualize.d.ts +71 -0
- package/dist/misc/contextualize.d.ts.map +1 -0
- package/dist/misc/contextualize.js +63 -0
- package/dist/misc/contextualize.js.map +1 -0
- package/dist/misc/createSafeContext.d.ts +4 -35
- package/dist/misc/createSafeContext.d.ts.map +1 -1
- package/dist/misc/createSafeContext.js +2 -5
- package/dist/misc/createSafeContext.js.map +1 -1
- package/dist/misc/index.d.ts +2 -0
- package/dist/misc/index.d.ts.map +1 -1
- package/dist/misc/index.js +2 -0
- package/dist/misc/index.js.map +1 -1
- package/dist/misc/wrapJSX.d.ts +76 -0
- package/dist/misc/wrapJSX.d.ts.map +1 -0
- package/dist/misc/wrapJSX.js +61 -0
- package/dist/misc/wrapJSX.js.map +1 -0
- package/package.json +3 -2
- package/scripts/fixReadmeLinks.js +23 -0
- package/src/misc/contextualize.tsx +90 -0
- package/src/misc/createSafeContext.ts +8 -39
- package/src/misc/index.ts +2 -0
- package/src/misc/wrapJSX.tsx +107 -0
|
@@ -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
|
|
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>) =>
|
|
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,
|
|
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,
|
|
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"}
|
package/dist/misc/index.d.ts
CHANGED
package/dist/misc/index.d.ts.map
CHANGED
|
@@ -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"}
|
package/dist/misc/index.js
CHANGED
package/dist/misc/index.js.map
CHANGED
|
@@ -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.
|
|
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,
|
|
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
|
-
):
|
|
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
|
|
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]:
|
|
86
|
+
[K in typeof contextName]: Context<T>;
|
|
118
87
|
} & {
|
|
119
88
|
[K in typeof hookName]: () => T;
|
|
120
89
|
};
|
package/src/misc/index.ts
CHANGED