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 +0 -0
- package/dist/index.d.ts +74 -76
- package/dist/index.mjs +111 -94
- package/dist/index.umd.js +112 -93
- package/package.json +1 -1
- package/readMe.md +60 -71
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
|
|
12
|
+
}>): JSX;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
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 =
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
50
|
-
}) => JSX
|
|
55
|
+
children?: JSX;
|
|
56
|
+
}) => JSX;
|
|
57
|
+
Dest: () => JSX;
|
|
58
|
+
Source: (props: Info) => JSX;
|
|
51
59
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
71
|
-
declare type
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
onCleanup(() =>
|
|
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
|
|
132
|
+
return [...state.sources];
|
|
104
133
|
},
|
|
105
134
|
children: (x) => createMemo(() => x.children)
|
|
106
135
|
});
|
|
107
136
|
}
|
|
108
137
|
function Source(props) {
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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
|
|
125
|
-
const {
|
|
126
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
143
|
-
return
|
|
171
|
+
get sources() {
|
|
172
|
+
return getSources();
|
|
144
173
|
},
|
|
145
|
-
get
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
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")
|
|
3
|
-
})(this, function(exports2, solidJs
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
99
|
-
if (!
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
solidJs.onCleanup(() =>
|
|
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
|
|
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
|
|
112
|
-
if (!
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
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
|
|
127
|
-
const {
|
|
128
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
145
|
-
return
|
|
174
|
+
get sources() {
|
|
175
|
+
return getSources();
|
|
146
176
|
},
|
|
147
|
-
get
|
|
148
|
-
return
|
|
149
|
-
|
|
150
|
-
|
|
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
package/readMe.md
CHANGED
|
@@ -76,87 +76,76 @@ return <>
|
|
|
76
76
|
</>
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
### `
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|