solid-ctrl-flow 2.1.1 → 2.2.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/bun.lockb ADDED
Binary file
package/dist/index.d.ts CHANGED
@@ -9,14 +9,31 @@ import { splitProps } from 'solid-js';
9
9
  /** Like a {@link Match} but gets the value from the closest {@link On} ancestor */
10
10
  export declare function Case(props: ParentProps<{
11
11
  value: unknown;
12
- }>): JSX.Element;
12
+ }>): JSX;
13
13
 
14
14
  /**
15
- * Creates a slot that allows you to show a component outside of its parent.
15
+ * Eventually wraps its content into a template if a certain condition is met.
16
+ * If you set {@link Standard.recycled} to `true`, the content will be memoized in the beginning and will be recycled even if the wrapper changes.
17
+ * You can't use memoization if you want to use a context provider on your template.
18
+ * If {@link Keyed.keyed} is active, the template will be executed each time the content of {@link Standard.when} changes.
19
+ * If {@link Standard.recycled} is not active, the content will be executed each time it is mounted (Each time {@link Standard.when} goes from a falsish value to a truish one and vice versa).
20
+ * If {@link Keyed.keyed} is active and {@link Standard.recycled} isn't, the content will be executed each time {@link Standard.when} changes
21
+ */
22
+ export declare function Enfold<T>(props: Keyed<T>): JSX.Element;
23
+
24
+ export declare function Enfold<T>(props: Unkeyed<T>): JSX.Element;
25
+
26
+ /** Handy type alias */
27
+ declare type Equals = MemoOptions<unknown>["equals"];
28
+
29
+ /**
30
+ * Friendlier version of the {@link Portal}.
16
31
  * It works with arbitrary nesting.
32
+ * The components communicate only if they're part of the same {@link Extractor}.
33
+ * It is uncommon for an {@link Extractor} to be defined inside a component.
17
34
  * Example:
18
35
  * ```tsx
19
- * const e = createExtractor();
36
+ * const e = new Extractor();
20
37
  * return <>
21
38
  * <e.Joint>
22
39
  * <div id="something">
@@ -24,51 +41,33 @@ export declare function Case(props: ParentProps<{
24
41
  * </div>
25
42
  * <div id="something-else">
26
43
  * <e.Source>
27
- * This text will be shown instead of `e.Dest`.
28
- * If there weren't to be any `e.Joint` ancestor, then this text would have stayed here
44
+ * This text will be shown instead of `e.Dest`
29
45
  * </e.Source>
30
46
  * </div>
31
47
  * </e.Joint>
32
48
  * </>
33
49
  * ```
34
- * @param name Name to give to the context of the extractor
35
50
  */
36
- export declare function createExtractor(name?: string): {
37
- /** Shows the content of the closest {@link Source} child of the closest {@link Joint} ancestor of this component */
38
- Dest: () => JSX.Element;
39
- /** Puts its content inside of a {@link Dest} if it is available; There is no need to include this component on the DOM tree */
40
- Source: (props: Slot & {
41
- order?: number | undefined;
42
- } & {
43
- children?: JSX.Element;
44
- } & {
45
- sameContext?: boolean | undefined;
46
- }) => JSX.Element;
47
- /** Allows {@link Dest} and {@link Source} childrens to communicate with each other */
51
+ export declare class Extractor {
52
+ constructor(name?: string);
53
+ readonly ctx: Context<State | undefined>;
48
54
  Joint: (props: {
49
- children?: JSX.Element;
50
- }) => JSX.Element;
55
+ children?: JSX;
56
+ }) => JSX;
57
+ Dest: () => JSX;
58
+ Source: (props: Info) => JSX;
51
59
  /**
52
- * 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})
53
- * @returns Returns `null` if there is no parent {@link Joint}, the number of {@link Dest}s in which this component is being displayed otherwise (Usually 1)
60
+ * Returns the number of destinations available for the current {@link Extractor}.
61
+ * It's a getter that returns a function that will be bound to the {@link Owner} in which the getter was called.
62
+ * If the current {@link Owner} is NOT under a {@link Joint}, it will return `undefined`
54
63
  */
55
- readonly extracting: () => number | null;
56
- };
57
-
58
- /**
59
- * Eventually wraps its content into a template if a certain condition is met.
60
- * If you set {@link Standard.recycled} to `true`, the content will be memoized in the beginning and will be recycled even if the wrapper changes.
61
- * You can't use memoization if you want to use a context provider on your template.
62
- * If {@link Keyed.keyed} is active, the template will be executed each time the content of {@link Standard.when} changes.
63
- * If {@link Standard.recycled} is not active, the content will be executed each time it is mounted (Each time {@link Standard.when} goes from a falsish value to a truish one and vice versa).
64
- * If {@link Keyed.keyed} is active and {@link Standard.recycled} isn't, the content will be executed each time {@link Standard.when} changes
65
- */
66
- export declare function Enfold<T>(props: Keyed<T>): JSX.Element;
67
-
68
- export declare function Enfold<T>(props: Unkeyed<T>): JSX.Element;
64
+ get getDestCount(): (() => number) | undefined;
65
+ }
69
66
 
70
- /** Handy type alias */
71
- declare type Equals = MemoOptions<unknown>["equals"];
67
+ /** Informations about a single {@link Source} */
68
+ declare type Info = ParentProps<{
69
+ order?: number;
70
+ }>;
72
71
 
73
72
  /** Parameters to pass to a keyed {@link Enfold} */
74
73
  declare type Keyed<T> = Standard<T> & {
@@ -115,7 +114,35 @@ export declare function memoProps<T, K extends readonly (keyof T)[]>(obj: T, key
115
114
  export declare function On<T>(props: ParentProps<{
116
115
  value: T;
117
116
  onCompare?(a: unknown, b: T): boolean;
118
- }>): JSX.Element;
117
+ }>): JSX;
118
+
119
+ /**
120
+ * Ordered linked list that allows for:
121
+ * - O(n) Iteration
122
+ * - O(n) Insertion
123
+ * - O(1) Removal (With a node reference)
124
+ */
125
+ export declare class OrderedLinkedList<T> {
126
+ next?: OrderedLinkedListNode<T>;
127
+ /** Yields all the elements PAST the current */
128
+ [Symbol.iterator](): Generator<T>;
129
+ /**
130
+ * Adds a node to the list in order
131
+ * @param node The node to add
132
+ * @param comp The comparison function to use
133
+ * @returns The same value as {@link node}
134
+ */
135
+ add(node: OrderedLinkedListNode<T>, comp: (a: T, b: T) => number): OrderedLinkedListNode<T>;
136
+ }
137
+
138
+ /** Node for the {@link OrderedLinkedList} */
139
+ export declare class OrderedLinkedListNode<T> extends OrderedLinkedList<T> {
140
+ value: T;
141
+ constructor(value: T);
142
+ prev?: OrderedLinkedList<T>;
143
+ /** Removes the current node from the list */
144
+ remove(): void;
145
+ }
119
146
 
120
147
  /**
121
148
  * Executes {@link f} with the provided value for the specified {@link Context}.
@@ -133,43 +160,6 @@ export declare function SameContext(props: ParentProps<{
133
160
  owner: Owner;
134
161
  }>): JSX.Element;
135
162
 
136
- /** Element which tracks its index in its containing array */
137
- export declare interface Slot {
138
- slot?: number;
139
- }
140
-
141
- /** Utility of {@link Slot}s */
142
- export declare namespace Slot {
143
- /**
144
- * Pushes an element into a slotted list
145
- * @param list The list in which to add the element
146
- * @param v The element to add to {@link list}
147
- * @returns The index of the new element
148
- */
149
- const push: <T extends Slot>(list: T[], v: T) => number;
150
- /**
151
- * Exactly like {@link removeSlotAt}, but it gets the index from {@link v}
152
- * @param list The list from which to remove the element
153
- * @param v The element to remove from {@link list}
154
- * @returns The removed element
155
- */
156
- const remove: <T extends Slot>(list: T[], v: T) => T | undefined;
157
- /**
158
- * Removes an element from a slotted list.
159
- * The function returns `undefined` ONLY if the index is out of range (A {@link Slot} can't be `undefined`)
160
- * @param list The list from which to remove the element
161
- * @param i The index at which to remove the element
162
- * @returns The removed element
163
- */
164
- export function removeAt<T extends Slot>(list: T[], i: number): T | undefined;
165
- /**
166
- * Restores the indexes of a slotted list.
167
- * This function is useful after a sort, filter, or any other operation that changes the order of the elements in the list without taking care of {@link Slot}s
168
- * @param list The list to restore
169
- */
170
- export function restore(list: Slot[]): void;
171
- }
172
-
173
163
  /**
174
164
  * Executes {@link splitProps} and memoizes every returned object but the last one using {@link memoProps}
175
165
  * @param obj Object to split and partially memoize
@@ -183,6 +173,14 @@ declare type Standard<T> = ParentProps<{
183
173
  recycled?: boolean;
184
174
  }>;
185
175
 
176
+ /** Type of the data stored in the {@link Context} created by each {@link Extractor} */
177
+ declare interface State {
178
+ readonly sources: OrderedLinkedList<Info>;
179
+ readonly extracting: number;
180
+ shift(k: number): void;
181
+ force(): void;
182
+ }
183
+
186
184
  /** Type of an element wrapping call-back which accepts an additional parameter */
187
185
  declare type Template<T> = (c: Accessor<JSX.Element>, x: T) => JSX.Element;
188
186
 
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { createMemo, splitProps, untrack, createRoot, getOwner, createComponent, Show, createContext, useContext, onCleanup, For, createSelector, equalFn, Match } from "solid-js";
2
- import { createMutable } from "solid-js/store";
1
+ import { createMemo, splitProps, untrack, createRoot, getOwner, createComponent, Show, createContext, useContext, onCleanup, For, createRenderEffect, on, createSignal, createSelector, equalFn, Match } from "solid-js";
3
2
  function memoProps(obj, keys = Reflect.ownKeys(obj), equals = false) {
4
3
  const out = {};
5
4
  for (const elm of keys)
@@ -49,117 +48,134 @@ function Enfold(props) {
49
48
  children: (x) => createMemo(() => untrackCall(props.template, children, x))
50
49
  });
51
50
  }
52
- var Slot;
53
- ((Slot2) => {
54
- Slot2.push = (list, v) => v.slot = list.push(v) - 1;
55
- Slot2.remove = (list, v) => v.slot != null ? removeAt(list, v.slot) : v;
56
- function removeAt(list, i) {
57
- if (i >= list.length)
51
+ class OrderedLinkedList {
52
+ next;
53
+ /** Yields all the elements PAST the current */
54
+ *[Symbol.iterator]() {
55
+ const { next } = this;
56
+ if (!next)
58
57
  return;
59
- const last = list.pop();
60
- if (i === list.length)
61
- return last;
62
- const out = list[i];
63
- out.slot = void 0;
64
- list[last.slot = i] = last;
65
- return out;
58
+ yield next.value;
59
+ yield* next;
66
60
  }
67
- Slot2.removeAt = removeAt;
68
- function restore(list) {
69
- for (var i = 0; i < list.length; i++)
70
- list[i].slot = i;
61
+ /**
62
+ * Adds a node to the list in order
63
+ * @param node The node to add
64
+ * @param comp The comparison function to use
65
+ * @returns The same value as {@link node}
66
+ */
67
+ add(node, comp) {
68
+ const { next } = this;
69
+ if (next && comp(node.value, next.value) >= 0)
70
+ return next.add(node, comp);
71
+ this.next = node;
72
+ node.prev = this;
73
+ if (next)
74
+ (node.next = next).prev = node;
75
+ return node;
71
76
  }
72
- Slot2.restore = restore;
73
- })(Slot || (Slot = {}));
74
- function createExtractor(name = "extractor") {
75
- const ctx2 = createContext(void 0, {
76
- name
77
- });
78
- return {
79
- /** Shows the content of the closest {@link Source} child of the closest {@link Joint} ancestor of this component */
80
- Dest: Dest.bind(ctx2),
81
- /** Puts its content inside of a {@link Dest} if it is available; There is no need to include this component on the DOM tree */
82
- Source: Source.bind(ctx2),
83
- /** Allows {@link Dest} and {@link Source} childrens to communicate with each other */
84
- Joint: Joint.bind(ctx2),
85
- /**
86
- * 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})
87
- * @returns Returns `null` if there is no parent {@link Joint}, the number of {@link Dest}s in which this component is being displayed otherwise (Usually 1)
88
- */
89
- get extracting() {
90
- const obj = useContext(ctx2);
91
- return () => obj?.attached ?? null;
77
+ }
78
+ class OrderedLinkedListNode extends OrderedLinkedList {
79
+ constructor(value) {
80
+ super();
81
+ this.value = value;
82
+ }
83
+ prev;
84
+ /** Removes the current node from the list */
85
+ remove() {
86
+ const { prev, next } = this;
87
+ if (prev)
88
+ prev.next = next;
89
+ if (next)
90
+ next.prev = prev;
91
+ this.next = this.prev = void 0;
92
+ }
93
+ }
94
+ const COMPARATOR = (a, b) => (a.order ?? 0) - (b.order ?? 0);
95
+ class Extractor {
96
+ constructor(name) {
97
+ this.ctx = createContext(void 0, {
98
+ name
99
+ });
100
+ }
101
+ ctx;
102
+ Joint = Joint.bind(this);
103
+ Dest = Dest.bind(this);
104
+ Source = Source.bind(this);
105
+ /**
106
+ * Returns the number of destinations available for the current {@link Extractor}.
107
+ * It's a getter that returns a function that will be bound to the {@link Owner} in which the getter was called.
108
+ * If the current {@link Owner} is NOT under a {@link Joint}, it will return `undefined`
109
+ */
110
+ get getDestCount() {
111
+ const state = useContext(this.ctx);
112
+ return state && (() => state.extracting);
113
+ }
114
+ }
115
+ function Joint(props) {
116
+ const state = createState();
117
+ return createComponent(this.ctx.Provider, {
118
+ value: state,
119
+ get children() {
120
+ return props.children;
92
121
  }
93
- };
122
+ });
94
123
  }
95
124
  function Dest() {
96
- const obj = useContext(this);
97
- if (!obj)
98
- return;
99
- obj.attached++;
100
- onCleanup(() => obj.attached--);
125
+ const state = useContext(this.ctx);
126
+ if (!state)
127
+ throw new Error("An extractor's destination must be inside one of its joints");
128
+ state.shift(1);
129
+ onCleanup(() => state.shift(-1));
101
130
  return createComponent(For, {
102
131
  get each() {
103
- return obj.source.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
132
+ return [...state.sources];
104
133
  },
105
134
  children: (x) => createMemo(() => x.children)
106
135
  });
107
136
  }
108
137
  function Source(props) {
109
- const obj = useContext(this);
110
- if (!obj)
111
- return createMemo(() => props.children);
112
- const info = createInfo(props);
113
- Slot.push(obj.source, info);
114
- onCleanup(() => Slot.remove(obj.source, info));
115
- return createComponent(Show, {
116
- get when() {
117
- return !obj.attached;
138
+ const state = useContext(this.ctx);
139
+ if (!state)
140
+ throw new Error("An extractor's source must be inside one of its joints");
141
+ const {
142
+ sources,
143
+ force
144
+ } = state;
145
+ const order = createMemo(() => props.order);
146
+ const info = {
147
+ get order() {
148
+ return order();
118
149
  },
119
150
  get children() {
120
- return info.children;
151
+ return props.children;
121
152
  }
122
- });
153
+ };
154
+ const node = new OrderedLinkedListNode(info);
155
+ createRenderEffect(on(order, () => {
156
+ onCleanup(() => node.remove());
157
+ sources.add(node, COMPARATOR);
158
+ force();
159
+ }));
160
+ return [];
123
161
  }
124
- function Joint(props) {
125
- const {
126
- Provider
127
- } = this;
128
- const obj = createMutable({
129
- attached: 0,
130
- source: []
162
+ function createState() {
163
+ const [getExtracting, setExtracting] = createSignal(0, {
164
+ internal: true
131
165
  });
132
- return createComponent(Provider, {
133
- value: obj,
134
- get children() {
135
- return props.children;
136
- }
166
+ const [getSources, setSources] = createSignal(new OrderedLinkedList(), {
167
+ internal: true,
168
+ equals: false
137
169
  });
138
- }
139
- function createInfo(props) {
140
- const owner = getOwner(), memo = createMemo(() => props.order);
141
170
  return {
142
- get order() {
143
- return memo();
171
+ get sources() {
172
+ return getSources();
144
173
  },
145
- get children() {
146
- return createComponent(Show, {
147
- get when() {
148
- return props.sameContext;
149
- },
150
- get fallback() {
151
- return props.children;
152
- },
153
- get children() {
154
- return createComponent(SameContext, {
155
- owner,
156
- get children() {
157
- return props.children;
158
- }
159
- });
160
- }
161
- });
162
- }
174
+ get extracting() {
175
+ return getExtracting();
176
+ },
177
+ shift: (k) => setExtracting((v) => v + k),
178
+ force: () => setSources((s) => s)
163
179
  };
164
180
  }
165
181
  const ctx = createContext(void 0, {
@@ -195,10 +211,11 @@ function SameContext(props) {
195
211
  export {
196
212
  Case,
197
213
  Enfold,
214
+ Extractor,
198
215
  On,
216
+ OrderedLinkedList,
217
+ OrderedLinkedListNode,
199
218
  SameContext,
200
- Slot,
201
- createExtractor,
202
219
  memoProps,
203
220
  runWithContext,
204
221
  splitAndMemoProps,
package/dist/index.umd.js CHANGED
@@ -1,6 +1,6 @@
1
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.solidCtrlFlow = {}, global.solidJs, global.solidJsStore));
3
- })(this, function(exports2, solidJs, store) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("solid-js")) : typeof define === "function" && define.amd ? define(["exports", "solid-js"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.solidCtrlFlow = {}, global.solidJs));
3
+ })(this, function(exports2, solidJs) {
4
4
  "use strict";
5
5
  function memoProps(obj, keys = Reflect.ownKeys(obj), equals = false) {
6
6
  const out = {};
@@ -51,117 +51,134 @@
51
51
  children: (x) => solidJs.createMemo(() => untrackCall(props.template, children, x))
52
52
  });
53
53
  }
54
- exports2.Slot = void 0;
55
- ((Slot2) => {
56
- Slot2.push = (list, v) => v.slot = list.push(v) - 1;
57
- Slot2.remove = (list, v) => v.slot != null ? removeAt(list, v.slot) : v;
58
- function removeAt(list, i) {
59
- if (i >= list.length)
54
+ class OrderedLinkedList {
55
+ next;
56
+ /** Yields all the elements PAST the current */
57
+ *[Symbol.iterator]() {
58
+ const { next } = this;
59
+ if (!next)
60
60
  return;
61
- const last = list.pop();
62
- if (i === list.length)
63
- return last;
64
- const out = list[i];
65
- out.slot = void 0;
66
- list[last.slot = i] = last;
67
- return out;
61
+ yield next.value;
62
+ yield* next;
68
63
  }
69
- Slot2.removeAt = removeAt;
70
- function restore(list) {
71
- for (var i = 0; i < list.length; i++)
72
- list[i].slot = i;
64
+ /**
65
+ * Adds a node to the list in order
66
+ * @param node The node to add
67
+ * @param comp The comparison function to use
68
+ * @returns The same value as {@link node}
69
+ */
70
+ add(node, comp) {
71
+ const { next } = this;
72
+ if (next && comp(node.value, next.value) >= 0)
73
+ return next.add(node, comp);
74
+ this.next = node;
75
+ node.prev = this;
76
+ if (next)
77
+ (node.next = next).prev = node;
78
+ return node;
73
79
  }
74
- Slot2.restore = restore;
75
- })(exports2.Slot || (exports2.Slot = {}));
76
- function createExtractor(name = "extractor") {
77
- const ctx2 = solidJs.createContext(void 0, {
78
- name
79
- });
80
- return {
81
- /** Shows the content of the closest {@link Source} child of the closest {@link Joint} ancestor of this component */
82
- Dest: Dest.bind(ctx2),
83
- /** Puts its content inside of a {@link Dest} if it is available; There is no need to include this component on the DOM tree */
84
- Source: Source.bind(ctx2),
85
- /** Allows {@link Dest} and {@link Source} childrens to communicate with each other */
86
- Joint: Joint.bind(ctx2),
87
- /**
88
- * 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})
89
- * @returns Returns `null` if there is no parent {@link Joint}, the number of {@link Dest}s in which this component is being displayed otherwise (Usually 1)
90
- */
91
- get extracting() {
92
- const obj = solidJs.useContext(ctx2);
93
- return () => obj?.attached ?? null;
80
+ }
81
+ class OrderedLinkedListNode extends OrderedLinkedList {
82
+ constructor(value) {
83
+ super();
84
+ this.value = value;
85
+ }
86
+ prev;
87
+ /** Removes the current node from the list */
88
+ remove() {
89
+ const { prev, next } = this;
90
+ if (prev)
91
+ prev.next = next;
92
+ if (next)
93
+ next.prev = prev;
94
+ this.next = this.prev = void 0;
95
+ }
96
+ }
97
+ const COMPARATOR = (a, b) => (a.order ?? 0) - (b.order ?? 0);
98
+ class Extractor {
99
+ constructor(name) {
100
+ this.ctx = solidJs.createContext(void 0, {
101
+ name
102
+ });
103
+ }
104
+ ctx;
105
+ Joint = Joint.bind(this);
106
+ Dest = Dest.bind(this);
107
+ Source = Source.bind(this);
108
+ /**
109
+ * Returns the number of destinations available for the current {@link Extractor}.
110
+ * It's a getter that returns a function that will be bound to the {@link Owner} in which the getter was called.
111
+ * If the current {@link Owner} is NOT under a {@link Joint}, it will return `undefined`
112
+ */
113
+ get getDestCount() {
114
+ const state = solidJs.useContext(this.ctx);
115
+ return state && (() => state.extracting);
116
+ }
117
+ }
118
+ function Joint(props) {
119
+ const state = createState();
120
+ return solidJs.createComponent(this.ctx.Provider, {
121
+ value: state,
122
+ get children() {
123
+ return props.children;
94
124
  }
95
- };
125
+ });
96
126
  }
97
127
  function Dest() {
98
- const obj = solidJs.useContext(this);
99
- if (!obj)
100
- return;
101
- obj.attached++;
102
- solidJs.onCleanup(() => obj.attached--);
128
+ const state = solidJs.useContext(this.ctx);
129
+ if (!state)
130
+ throw new Error("An extractor's destination must be inside one of its joints");
131
+ state.shift(1);
132
+ solidJs.onCleanup(() => state.shift(-1));
103
133
  return solidJs.createComponent(solidJs.For, {
104
134
  get each() {
105
- return obj.source.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
135
+ return [...state.sources];
106
136
  },
107
137
  children: (x) => solidJs.createMemo(() => x.children)
108
138
  });
109
139
  }
110
140
  function Source(props) {
111
- const obj = solidJs.useContext(this);
112
- if (!obj)
113
- return solidJs.createMemo(() => props.children);
114
- const info = createInfo(props);
115
- exports2.Slot.push(obj.source, info);
116
- solidJs.onCleanup(() => exports2.Slot.remove(obj.source, info));
117
- return solidJs.createComponent(solidJs.Show, {
118
- get when() {
119
- return !obj.attached;
141
+ const state = solidJs.useContext(this.ctx);
142
+ if (!state)
143
+ throw new Error("An extractor's source must be inside one of its joints");
144
+ const {
145
+ sources,
146
+ force
147
+ } = state;
148
+ const order = solidJs.createMemo(() => props.order);
149
+ const info = {
150
+ get order() {
151
+ return order();
120
152
  },
121
153
  get children() {
122
- return info.children;
154
+ return props.children;
123
155
  }
124
- });
156
+ };
157
+ const node = new OrderedLinkedListNode(info);
158
+ solidJs.createRenderEffect(solidJs.on(order, () => {
159
+ solidJs.onCleanup(() => node.remove());
160
+ sources.add(node, COMPARATOR);
161
+ force();
162
+ }));
163
+ return [];
125
164
  }
126
- function Joint(props) {
127
- const {
128
- Provider
129
- } = this;
130
- const obj = store.createMutable({
131
- attached: 0,
132
- source: []
165
+ function createState() {
166
+ const [getExtracting, setExtracting] = solidJs.createSignal(0, {
167
+ internal: true
133
168
  });
134
- return solidJs.createComponent(Provider, {
135
- value: obj,
136
- get children() {
137
- return props.children;
138
- }
169
+ const [getSources, setSources] = solidJs.createSignal(new OrderedLinkedList(), {
170
+ internal: true,
171
+ equals: false
139
172
  });
140
- }
141
- function createInfo(props) {
142
- const owner = solidJs.getOwner(), memo = solidJs.createMemo(() => props.order);
143
173
  return {
144
- get order() {
145
- return memo();
174
+ get sources() {
175
+ return getSources();
146
176
  },
147
- get children() {
148
- return solidJs.createComponent(solidJs.Show, {
149
- get when() {
150
- return props.sameContext;
151
- },
152
- get fallback() {
153
- return props.children;
154
- },
155
- get children() {
156
- return solidJs.createComponent(SameContext, {
157
- owner,
158
- get children() {
159
- return props.children;
160
- }
161
- });
162
- }
163
- });
164
- }
177
+ get extracting() {
178
+ return getExtracting();
179
+ },
180
+ shift: (k) => setExtracting((v) => v + k),
181
+ force: () => setSources((s) => s)
165
182
  };
166
183
  }
167
184
  const ctx = solidJs.createContext(void 0, {
@@ -196,9 +213,11 @@
196
213
  }
197
214
  exports2.Case = Case;
198
215
  exports2.Enfold = Enfold;
216
+ exports2.Extractor = Extractor;
199
217
  exports2.On = On;
218
+ exports2.OrderedLinkedList = OrderedLinkedList;
219
+ exports2.OrderedLinkedListNode = OrderedLinkedListNode;
200
220
  exports2.SameContext = SameContext;
201
- exports2.createExtractor = createExtractor;
202
221
  exports2.memoProps = memoProps;
203
222
  exports2.runWithContext = runWithContext;
204
223
  exports2.splitAndMemoProps = splitAndMemoProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid-ctrl-flow",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Control flow components for solid-js",
5
5
  "author": "AFatNiBBa",
6
6
  "license": "ISC",
package/readMe.md CHANGED
@@ -76,87 +76,76 @@ return <>
76
76
  </>
77
77
  ```
78
78
 
79
- ### `createExtractor()`
80
- Creates a context for components that may need to be out of their parent in certain conditions.
81
- If you have this component
82
- ```tsx
83
- const myCustomExtractor = createExtractor();
84
-
85
- function MyCustomComponent() {
86
- return <>
87
- 1
88
- <myCustomExtractor.Source>
89
- 2 {/* THIS WILL BE MOVED INSIDE THE "Dest" */}
90
- </myCustomExtractor.Source>
91
- 3
92
- </>
93
- }
94
- ```
95
- If you use it normally it will output `4`, ***`1`***, ***`2`***, ***`3`***, `5`
79
+ ### `Extractor`
80
+ Friendlier version of the `Portal`.
81
+ Example:
96
82
  ```tsx
83
+ const e = new Extractor();
97
84
  return <>
98
- 4
99
- <MyCustomComponent />
100
- 5
101
- </>
102
- ```
103
- 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`
104
- ```tsx
105
- return <>
106
- 6
107
- <myCustomExtractor.Joint>
108
- 7
109
- <div>
110
- 8
111
- <myCustomExtractor.Dest /> {/* THE CONTENT OF "Source" WILL BE MOVED HERE */}
112
- 9
85
+ <e.Joint>
86
+ <div id="something">
87
+ <e.Dest />
113
88
  </div>
114
- 10
115
- <div>
116
- 11
117
- <MyCustomComponent />
118
- 12
89
+ <div id="something-else">
90
+ <e.Source>
91
+ This text will be shown instead of `e.Dest`
92
+ </e.Source>
119
93
  </div>
120
- 13
121
- </myCustomExtractor.Joint>
122
- 14
123
- </>
124
- ```
125
- The `Joint` component is necessary for the `Source` and the `Dest` to communicate with each other.
126
- For example this will output `15`, `16`, `17`, `18`, `19`, ***`1`***, ***`2`***, ***`3`***, `20`, `21`
127
- ```tsx
128
- return <>
129
- 15
130
- <div>
131
- 16
132
- <myCustomExtractor.Dest />
133
- 17
134
- </div>
135
- 18
136
- <div>
137
- 19
138
- <MyCustomComponent />
139
- 20
140
- </div>
141
- 21
94
+ </e.Joint>
142
95
  </>
143
96
  ```
144
- Notice that you can use a `Source` component without adding it to the DOM:
145
- ```tsx
146
- <myCustomExtractor.Source>
147
- {/* This doesn't get returned, but still gets shown if there is an available "Dest" */}
148
- Something
149
- </myCustomExtractor.Source>
150
- return <>Something else</>
151
- ```
152
- When something gets moved from a `Source` to a `Dest`, it will inherit contexts from its destination, UNLESS you use the `sameContext` attribute
97
+ Both `Extractor.Source` and `Extractor.Dest` will throw if they're not inside a `Extractor.Joint`.
98
+ <br />
99
+ Componets from different `Extractor`s don't "talk" to each other and there can be more than one source and destinations:
153
100
  ```tsx
101
+ const a = new Extractor(), b = new Extractor(), c = new Extractor()
102
+
103
+ function ProofThatItWorksCrossComponent() {
104
+ return <>
105
+ 0
106
+ {/* Sources will be sorted by the "order" attribute before being rendered inside a destination */}
107
+ {/* The value defaults to 0 and doesn't need to be an integer */}
108
+ <a.Source order={-1.1}>
109
+ 1
110
+ </a.Source>
111
+ 2
112
+ </>
113
+ }
114
+
154
115
  return <>
155
- <myCustomExtractor.Source sameContext>
156
- ...
157
- </myCustomExtractor.Source>
116
+ <a.Joint>
117
+ <a.Dest />
118
+ <b.Joint>
119
+ {/* No source */}
120
+ <b.Dest />
121
+ <c.Joint>
122
+ 3
123
+ {/* Double destination */}
124
+ <a.Dest />
125
+ 4
126
+ <a.Source>
127
+ 5
128
+ </a.Source>
129
+ <c.Source>
130
+ {/* No destination */}
131
+ 6
132
+ </c.Source>
133
+ <ProofThatItWorksCrossComponent />
134
+ <c.Joint>
135
+ {/* Inner scope for the "c" extractor */}
136
+ <c.Dest />
137
+ 7
138
+ <c.Source>
139
+ 8
140
+ </c.Source>
141
+ <c.Dest />
142
+ </c.Joint>
143
+ </c.Joint>
144
+ </b.Joint>
145
+ </a.Joint>
158
146
  </>
159
147
  ```
148
+ The output of this code is ***1***, ***5***, 3, ***1***, ***5***, 4, 0, 2, ***8***, 7, ***8***
160
149
 
161
150
  ## Utility
162
151