solid-ctrl-flow 1.0.0

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/flow.d.ts ADDED
@@ -0,0 +1,128 @@
1
+ import { Accessor } from 'solid-js';
2
+ import { JSX } from 'solid-js';
3
+
4
+ /**
5
+ * Allows the use of {@link When}s inside of {@link Switch}es.
6
+ * Example:
7
+ * ```tsx
8
+ * return <>
9
+ * <Switch>
10
+ * <Match when={SOMETHING}>
11
+ * RANDOM_CONDITION
12
+ * </Match>
13
+ * <Case value={1}>
14
+ * <When value={1}>
15
+ * TRUE
16
+ * </When>
17
+ * <When pred={x => x === 2}>
18
+ * FALSE_WITH_PREDICATE
19
+ * </When>
20
+ * <When pred={x => x.innerValue}>
21
+ * {x => <>THRUTHY VALUE {x()}</>}
22
+ * </When>
23
+ * <When keyed pred={x => x.innerValue}>
24
+ * {x => <>THRUTHY KEYED VALUE {x}</>}
25
+ * </When>
26
+ * </Case>
27
+ * <Match when>
28
+ * DEFAULT
29
+ * </Match>
30
+ * </Switch>
31
+ * <>
32
+ * ```
33
+ */
34
+ export declare function Case(props: {
35
+ value: any;
36
+ children: JSX.Element;
37
+ }): JSX.Element;
38
+
39
+ /**
40
+ * Creates a slot that allows you to show a component outside of its parent.
41
+ * It works with arbitrary nesting.
42
+ * Example:
43
+ * ```tsx
44
+ * const e = createExtractor();
45
+ * return <>
46
+ * <e.Joint>
47
+ * <div id="something">
48
+ * <e.Dest />
49
+ * </div>
50
+ * <div id="something-else">
51
+ * <e.Source>
52
+ * This text will be shown instead of "e.Dest".
53
+ * If there weren't to be any "e.Joint" ancestor, then this text would have stayed here
54
+ * </e.Source>
55
+ * </div>
56
+ * </e.Joint>
57
+ * <>
58
+ * ```
59
+ */
60
+ export declare function createExtractor(name?: string): {
61
+ /** Bound {@link Dest} */
62
+ Dest: () => JSX.Element;
63
+ /** Bound {@link Source} */
64
+ Source: (props: {
65
+ children: JSX.Element;
66
+ }) => JSX.Element;
67
+ /** Bound {@link Joint} */
68
+ Joint: (props: {
69
+ children: JSX.Element;
70
+ }) => JSX.Element;
71
+ /**
72
+ * Gets an {@link Accessor} that tells if the context in which THIS getter has been executed has been extracted (Is inside of a {@link Dest})
73
+ * @returns Returns `null` if there is no parent {@link Joint}, `false` if there is a {@link Joint} but not a {@link Dest} and `true` otherwise
74
+ */
75
+ readonly isExtracted: () => boolean | null;
76
+ };
77
+
78
+ /** Sets the value that will be returned by {@link Debug.isDebug} for the children of the current component */
79
+ export declare function Debug(props: {
80
+ value?: boolean;
81
+ children: JSX.Element;
82
+ }): JSX.Element;
83
+
84
+ /** Utility functions for the debug scopes */
85
+ export declare namespace Debug {
86
+ /** Gets an {@link Accessor} that tells if the context in which THIS getter has been executed is in debug mode */
87
+ const isDebug: Accessor<boolean>;
88
+ }
89
+
90
+ /**
91
+ * Memoizes some of the properties of {@link obj}
92
+ * If {@link keys} is not provided, memoizes everything that get returned by {@link Object.keys} when provided with {@link obj}
93
+ * @param obj Object to partially memoize
94
+ * @param keys The keys of the properties to memoize
95
+ */
96
+ export declare function memoProps<T, K extends readonly (keyof T)[] = (keyof T)[]>(obj: T, keys?: K): Pick<T, K[number]>;
97
+
98
+ /**
99
+ * Esecutes {@link splitProps} and memoizes the first of the two returned objects
100
+ * @param obj Object to split and partially memoize
101
+ * @param keys The keys of the properties to memoize and to include in the first of the two objects
102
+ */
103
+ export declare function splitAndMemoProps<T extends object, K extends readonly (keyof T)[]>(obj: T, keys: K): readonly [Pick<Pick<T, Extract<K, readonly (keyof T)[]>[number]>, K[number]>, Omit<T, K[number]>];
104
+
105
+ /** Like a {@link Match} but gets the value from the closest {@link Case} ancestor */
106
+ export declare function When(props: {
107
+ value: unknown;
108
+ children: JSX.Element;
109
+ }): JSX.Element;
110
+
111
+ export declare function When(props: {
112
+ pred: (x: any) => unknown;
113
+ children: JSX.Element;
114
+ }): JSX.Element;
115
+
116
+ export declare function When<T>(props: {
117
+ pred: (x: any) => T;
118
+ keyed?: false;
119
+ children: (x: Accessor<NonNullable<T>>) => JSX.Element;
120
+ }): JSX.Element;
121
+
122
+ export declare function When<T>(props: {
123
+ pred: (x: any) => T;
124
+ keyed: true;
125
+ children: (x: NonNullable<T>) => JSX.Element;
126
+ }): JSX.Element;
127
+
128
+ export { }
package/dist/flow.mjs ADDED
@@ -0,0 +1,120 @@
1
+ import { createContext, createMemo, createComponent, useContext, Match, mergeProps, onCleanup, children, Show, splitProps } from "solid-js";
2
+ import { createMutable } from "solid-js/store";
3
+ const ctx$1 = createContext(() => void 0, {
4
+ name: "case-when"
5
+ });
6
+ function Case(props) {
7
+ const memo = createMemo(() => props.value);
8
+ return createComponent(ctx$1.Provider, {
9
+ value: memo,
10
+ get children() {
11
+ return props.children;
12
+ }
13
+ });
14
+ }
15
+ function When(props) {
16
+ const [mine, other] = splitAndMemoProps(props, ["value", "pred"]);
17
+ const value = useContext(ctx$1);
18
+ const pred = (x) => mine.pred ? mine.pred(x) : x === mine.value;
19
+ return createComponent(Match, mergeProps({
20
+ get when() {
21
+ return pred(value());
22
+ }
23
+ }, other));
24
+ }
25
+ const ctx = createContext(() => false, {
26
+ name: "debug-scope"
27
+ });
28
+ function Debug(props) {
29
+ return createComponent(ctx.Provider, {
30
+ value: () => props.value ?? true,
31
+ get children() {
32
+ return props.children;
33
+ }
34
+ });
35
+ }
36
+ (function(_Debug) {
37
+ Object.defineProperty(Debug, "isDebug", {
38
+ get: () => useContext(ctx)
39
+ });
40
+ })(Debug || (Debug = {}));
41
+ function createExtractor(name = "extractor") {
42
+ const ctx2 = createContext(void 0, {
43
+ name
44
+ });
45
+ return {
46
+ /** Bound {@link Dest} */
47
+ Dest: Dest.bind(ctx2),
48
+ /** Bound {@link Source} */
49
+ Source: Source.bind(ctx2),
50
+ /** Bound {@link Joint} */
51
+ Joint: Joint.bind(ctx2),
52
+ /**
53
+ * Gets an {@link Accessor} that tells if the context in which THIS getter has been executed has been extracted (Is inside of a {@link Dest})
54
+ * @returns Returns `null` if there is no parent {@link Joint}, `false` if there is a {@link Joint} but not a {@link Dest} and `true` otherwise
55
+ */
56
+ get isExtracted() {
57
+ const obj = useContext(ctx2);
58
+ return () => obj?.attached ?? null;
59
+ }
60
+ };
61
+ }
62
+ function Dest() {
63
+ const obj = useContext(this);
64
+ if (!obj)
65
+ return;
66
+ if (obj.attached)
67
+ throw new Error("Un extractor non può avere più di una destinazione");
68
+ obj.attached = true;
69
+ onCleanup(() => obj.attached = false);
70
+ return createMemo(() => obj.source);
71
+ }
72
+ function Source(props) {
73
+ const obj = useContext(this);
74
+ if (!obj)
75
+ return props.children;
76
+ if (obj.source)
77
+ throw new Error("Un extractor non può avere più di una sorgente");
78
+ const memo = children(() => props.children);
79
+ obj.source = createMemo(memo);
80
+ onCleanup(() => obj.source = void 0);
81
+ return createComponent(Show, {
82
+ get when() {
83
+ return !obj.attached;
84
+ },
85
+ get children() {
86
+ return memo();
87
+ }
88
+ });
89
+ }
90
+ function Joint(props) {
91
+ const obj = createMutable({
92
+ attached: false
93
+ });
94
+ return createComponent("this".Provider, {
95
+ value: obj,
96
+ get children() {
97
+ return props.children;
98
+ }
99
+ });
100
+ }
101
+ function memoProps(obj, keys = Object.keys(obj)) {
102
+ const out = {};
103
+ for (const elm of keys)
104
+ Object.defineProperty(out, elm, {
105
+ get: createMemo(() => obj[elm])
106
+ });
107
+ return out;
108
+ }
109
+ function splitAndMemoProps(obj, keys) {
110
+ const [mine, other] = splitProps(obj, keys);
111
+ return [memoProps(mine, keys), other];
112
+ }
113
+ export {
114
+ Case,
115
+ Debug,
116
+ When,
117
+ createExtractor,
118
+ memoProps,
119
+ splitAndMemoProps
120
+ };
@@ -0,0 +1,122 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("solid-js"), require("solid-js/store")) : typeof define === "function" && define.amd ? define(["exports", "solid-js", "solid-js/store"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.solidFlow = {}, global.solidJs, global.solidJsStore));
3
+ })(this, function(exports2, solidJs, store) {
4
+ "use strict";
5
+ const ctx$1 = solidJs.createContext(() => void 0, {
6
+ name: "case-when"
7
+ });
8
+ function Case(props) {
9
+ const memo = solidJs.createMemo(() => props.value);
10
+ return solidJs.createComponent(ctx$1.Provider, {
11
+ value: memo,
12
+ get children() {
13
+ return props.children;
14
+ }
15
+ });
16
+ }
17
+ function When(props) {
18
+ const [mine, other] = splitAndMemoProps(props, ["value", "pred"]);
19
+ const value = solidJs.useContext(ctx$1);
20
+ const pred = (x) => mine.pred ? mine.pred(x) : x === mine.value;
21
+ return solidJs.createComponent(solidJs.Match, solidJs.mergeProps({
22
+ get when() {
23
+ return pred(value());
24
+ }
25
+ }, other));
26
+ }
27
+ const ctx = solidJs.createContext(() => false, {
28
+ name: "debug-scope"
29
+ });
30
+ function Debug(props) {
31
+ return solidJs.createComponent(ctx.Provider, {
32
+ value: () => props.value ?? true,
33
+ get children() {
34
+ return props.children;
35
+ }
36
+ });
37
+ }
38
+ (function(_Debug) {
39
+ Object.defineProperty(Debug, "isDebug", {
40
+ get: () => solidJs.useContext(ctx)
41
+ });
42
+ })(Debug || (Debug = {}));
43
+ function createExtractor(name = "extractor") {
44
+ const ctx2 = solidJs.createContext(void 0, {
45
+ name
46
+ });
47
+ return {
48
+ /** Bound {@link Dest} */
49
+ Dest: Dest.bind(ctx2),
50
+ /** Bound {@link Source} */
51
+ Source: Source.bind(ctx2),
52
+ /** Bound {@link Joint} */
53
+ Joint: Joint.bind(ctx2),
54
+ /**
55
+ * Gets an {@link Accessor} that tells if the context in which THIS getter has been executed has been extracted (Is inside of a {@link Dest})
56
+ * @returns Returns `null` if there is no parent {@link Joint}, `false` if there is a {@link Joint} but not a {@link Dest} and `true` otherwise
57
+ */
58
+ get isExtracted() {
59
+ const obj = solidJs.useContext(ctx2);
60
+ return () => obj?.attached ?? null;
61
+ }
62
+ };
63
+ }
64
+ function Dest() {
65
+ const obj = solidJs.useContext(this);
66
+ if (!obj)
67
+ return;
68
+ if (obj.attached)
69
+ throw new Error("Un extractor non può avere più di una destinazione");
70
+ obj.attached = true;
71
+ solidJs.onCleanup(() => obj.attached = false);
72
+ return solidJs.createMemo(() => obj.source);
73
+ }
74
+ function Source(props) {
75
+ const obj = solidJs.useContext(this);
76
+ if (!obj)
77
+ return props.children;
78
+ if (obj.source)
79
+ throw new Error("Un extractor non può avere più di una sorgente");
80
+ const memo = solidJs.children(() => props.children);
81
+ obj.source = solidJs.createMemo(memo);
82
+ solidJs.onCleanup(() => obj.source = void 0);
83
+ return solidJs.createComponent(solidJs.Show, {
84
+ get when() {
85
+ return !obj.attached;
86
+ },
87
+ get children() {
88
+ return memo();
89
+ }
90
+ });
91
+ }
92
+ function Joint(props) {
93
+ const obj = store.createMutable({
94
+ attached: false
95
+ });
96
+ return solidJs.createComponent("this".Provider, {
97
+ value: obj,
98
+ get children() {
99
+ return props.children;
100
+ }
101
+ });
102
+ }
103
+ function memoProps(obj, keys = Object.keys(obj)) {
104
+ const out = {};
105
+ for (const elm of keys)
106
+ Object.defineProperty(out, elm, {
107
+ get: solidJs.createMemo(() => obj[elm])
108
+ });
109
+ return out;
110
+ }
111
+ function splitAndMemoProps(obj, keys) {
112
+ const [mine, other] = solidJs.splitProps(obj, keys);
113
+ return [memoProps(mine, keys), other];
114
+ }
115
+ exports2.Case = Case;
116
+ exports2.Debug = Debug;
117
+ exports2.When = When;
118
+ exports2.createExtractor = createExtractor;
119
+ exports2.memoProps = memoProps;
120
+ exports2.splitAndMemoProps = splitAndMemoProps;
121
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
122
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "solid-ctrl-flow",
3
+ "version": "1.0.0",
4
+ "description": "Control flow components for solid-js",
5
+ "author": "AFatNiBBa",
6
+ "license": "ISC",
7
+ "types": "./dist/flow.d.ts",
8
+ "exports": {
9
+ "require": "./dist/flow.umd.js",
10
+ "import": "./dist/flow.mjs",
11
+ "types": "./dist/flow.d.ts"
12
+ },
13
+ "scripts": {
14
+ "build": "vite build",
15
+ "save": "npm run build && npm publish"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^20.6.0",
19
+ "vite": "^4.4.9",
20
+ "vite-plugin-dts": "^3.5.3",
21
+ "vite-plugin-solid": "^2.7.0"
22
+ },
23
+ "dependencies": {
24
+ "solid-js": "^1.7.11"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/AFatNiBBa/solid-ctrl-flow.git"
29
+ },
30
+ "keywords": [
31
+ "solid-js",
32
+ "control",
33
+ "flow",
34
+ "switch",
35
+ "case",
36
+ "when",
37
+ "debug"
38
+ ]
39
+ }
package/readme.md ADDED
@@ -0,0 +1,123 @@
1
+
2
+ # solid-ctrl-flow
3
+ Control flow components for "solid-js"
4
+
5
+ ## Components
6
+
7
+ ### `Case` - `When`
8
+ Allows you to not specify the full condition inside of a `Switch` component.
9
+ The following code
10
+ ```tsx
11
+ const value = 1;
12
+ return <>
13
+ <Switch>
14
+ <Case value={value}>
15
+ <When value={1}>One</When>
16
+ <When value={2}>Two</When>
17
+ <When pred={(x) => `${x}`.length === 3}>Long</When>
18
+ </Case>
19
+ <Match when>Default</Match>
20
+ </Switch>
21
+ </>
22
+ ```
23
+ Is equivalent to this
24
+ ```tsx
25
+ const value = 1;
26
+ return <>
27
+ <Switch>
28
+ <Match when={value === 1}>One</Match>
29
+ <Match when={value === 2}>Two</Match>
30
+ <Match when={`${value}`.length === 3}>Long</Match>
31
+ <Match when>Default</Match>
32
+ </Switch>
33
+ </>
34
+ ```
35
+
36
+ ### `Debug`
37
+ Allows you to define ui section that are only visible in debug or NOT in debug
38
+ ```tsx
39
+ const value = false;
40
+ return <>
41
+ <Debug value={value}>
42
+ {/* (A LOT OF NESTING...) */}
43
+ <Show when={Debug.isDebug()}>
44
+ THIS WOULD BE VISIBLE IF "value" HAD BEEN {true}
45
+ </Show>
46
+ </Debug>
47
+ </>
48
+ ```
49
+
50
+ ### `createExtractor()`
51
+ Creates a context for components that may need to be out of their parent in certain conditions.
52
+ If you have this component
53
+ ```tsx
54
+ const myCustomExtractor = createExtractor();
55
+
56
+ function MyCustomComponent() {
57
+ return <>
58
+ 1
59
+ <myCustomExtractor.Source>
60
+ 2 {/* THIS WILL BE MOVED INSIDE THE "Dest" */}
61
+ </myCustomExtractor.Source>
62
+ 3
63
+ </>
64
+ }
65
+ ```
66
+ If you use it normally it will output `1`, `2`, `3`, `4`, `5`
67
+ ```tsx
68
+ return <>
69
+ 4
70
+ <MyCustomComponent />
71
+ 5
72
+ </>
73
+ ```
74
+ But if you use it together with a `Dest` and a `Joint` it will output `6`, `7`, `8`, ***`2`***, `9`, `10`, `11`, ***`1`***, ***`3`***, `12`, `13`, `14`
75
+ ```tsx
76
+ return <>
77
+ 6
78
+ <myCustomExtractor.Joint>
79
+ 7
80
+ <div>
81
+ 8
82
+ <myCustomExtractor.Dest /> {/* THE CONTENT OF "Source" WILL BE MOVED HERE */}
83
+ 9
84
+ </div>
85
+ 10
86
+ <div>
87
+ 11
88
+ <MyCustomComponent />
89
+ 12
90
+ </div>
91
+ 13
92
+ </myCustomExtractor.Joint>
93
+ 14
94
+ </>
95
+ ```
96
+ The `Joint` component is necessary for the `Source` and the `Dest` to communicate with each other.
97
+ For example this will output `15`, `16`, `17`, `18`, `19`, ***`1`***, ***`2`***, ***`3`***, `20`, `21`
98
+ ```tsx
99
+ return <>
100
+ 15
101
+ <div>
102
+ 16
103
+ <myCustomExtractor.Dest />
104
+ 17
105
+ </div>
106
+ 18
107
+ <div>
108
+ 19
109
+ <MyCustomComponent />
110
+ 20
111
+ </div>
112
+ 21
113
+ </>
114
+ ```
115
+
116
+ ## Utility functions
117
+ Utility functions needed for the components above
118
+
119
+ ### `memoProps()`
120
+ Creates a partial version of an object with memoized remaining properties
121
+
122
+ ### `splitAndMemoProps()`
123
+ Like `splitProps()` but memoizes the whole local part