solid-ctrl-flow 2.2.0 → 2.2.2

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/index.d.ts CHANGED
@@ -43,6 +43,9 @@ declare type Equals = MemoOptions<unknown>["equals"];
43
43
  * <e.Source>
44
44
  * This text will be shown instead of `e.Dest`
45
45
  * </e.Source>
46
+ *
47
+ * This would have been shown if there wasn't any `e.Dest`
48
+ * <e.Fallback />
46
49
  * </div>
47
50
  * </e.Joint>
48
51
  * </>
@@ -54,7 +57,10 @@ export declare class Extractor {
54
57
  Joint: (props: {
55
58
  children?: JSX;
56
59
  }) => JSX;
57
- Dest: () => JSX;
60
+ Fallback: () => JSX;
61
+ Dest: (props: {
62
+ hidden?: boolean | undefined;
63
+ }) => JSX;
58
64
  Source: (props: Info) => JSX;
59
65
  /**
60
66
  * Returns the number of destinations available for the current {@link Extractor}.
@@ -174,9 +180,10 @@ declare type Standard<T> = ParentProps<{
174
180
  }>;
175
181
 
176
182
  /** 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;
183
+ declare class State {
184
+ #private;
185
+ get sources(): OrderedLinkedList<Info>;
186
+ get destCount(): number;
180
187
  shift(k: number): void;
181
188
  force(): void;
182
189
  }
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createMemo, splitProps, untrack, createRoot, getOwner, createComponent, Show, createContext, useContext, onCleanup, For, createRenderEffect, on, createSignal, createSelector, equalFn, Match } from "solid-js";
1
+ import { createMemo, splitProps, untrack, createRoot, getOwner, createComponent, Show, createContext, useContext, createRenderEffect, on, onCleanup, For, createSignal, createSelector, equalFn, Match } from "solid-js";
2
2
  function memoProps(obj, keys = Reflect.ownKeys(obj), equals = false) {
3
3
  const out = {};
4
4
  for (const elm of keys)
@@ -66,7 +66,7 @@ class OrderedLinkedList {
66
66
  */
67
67
  add(node, comp) {
68
68
  const { next } = this;
69
- if (next && comp(node.value, next.value) >= 0)
69
+ if (next && comp(node.value, next.value) > 0)
70
70
  return next.add(node, comp);
71
71
  this.next = node;
72
72
  node.prev = this;
@@ -92,6 +92,28 @@ class OrderedLinkedListNode extends OrderedLinkedList {
92
92
  }
93
93
  }
94
94
  const COMPARATOR = (a, b) => (a.order ?? 0) - (b.order ?? 0);
95
+ const SOURCES_OPTS = {
96
+ internal: true,
97
+ equals: false
98
+ }, DEST_COUNT_OPTS = {
99
+ internal: true
100
+ };
101
+ class State {
102
+ #sources = createSignal(new OrderedLinkedList(), SOURCES_OPTS);
103
+ #destCount = createSignal(0, DEST_COUNT_OPTS);
104
+ get sources() {
105
+ return this.#sources[0]();
106
+ }
107
+ get destCount() {
108
+ return this.#destCount[0]();
109
+ }
110
+ shift(k) {
111
+ this.#destCount[1]((v) => v + k);
112
+ }
113
+ force() {
114
+ this.#sources[1]((v) => v);
115
+ }
116
+ }
95
117
  class Extractor {
96
118
  constructor(name) {
97
119
  this.ctx = createContext(void 0, {
@@ -100,6 +122,7 @@ class Extractor {
100
122
  }
101
123
  ctx;
102
124
  Joint = Joint.bind(this);
125
+ Fallback = Fallback.bind(this);
103
126
  Dest = Dest.bind(this);
104
127
  Source = Source.bind(this);
105
128
  /**
@@ -109,11 +132,11 @@ class Extractor {
109
132
  */
110
133
  get getDestCount() {
111
134
  const state = useContext(this.ctx);
112
- return state && (() => state.extracting);
135
+ return state && (() => state.destCount);
113
136
  }
114
137
  }
115
138
  function Joint(props) {
116
- const state = createState();
139
+ const state = new State();
117
140
  return createComponent(this.ctx.Provider, {
118
141
  value: state,
119
142
  get children() {
@@ -121,12 +144,30 @@ function Joint(props) {
121
144
  }
122
145
  });
123
146
  }
124
- function Dest() {
147
+ function Fallback() {
148
+ const _self$ = this;
149
+ const f = this.getDestCount;
150
+ return createComponent(Show, {
151
+ get when() {
152
+ return !f?.();
153
+ },
154
+ get children() {
155
+ return createComponent(_self$.Dest, {
156
+ hidden: true
157
+ });
158
+ }
159
+ });
160
+ }
161
+ function Dest(props) {
125
162
  const state = useContext(this.ctx);
126
163
  if (!state)
127
164
  throw new Error("An extractor's destination must be inside one of its joints");
128
- state.shift(1);
129
- onCleanup(() => state.shift(-1));
165
+ createRenderEffect(on(createMemo(() => props.hidden), (x) => {
166
+ if (x)
167
+ return;
168
+ onCleanup(() => state.shift(-1));
169
+ state.shift(1);
170
+ }));
130
171
  return createComponent(For, {
131
172
  get each() {
132
173
  return [...state.sources];
@@ -159,25 +200,6 @@ function Source(props) {
159
200
  }));
160
201
  return [];
161
202
  }
162
- function createState() {
163
- const [getExtracting, setExtracting] = createSignal(0, {
164
- internal: true
165
- });
166
- const [getSources, setSources] = createSignal(new OrderedLinkedList(), {
167
- internal: true,
168
- equals: false
169
- });
170
- return {
171
- get sources() {
172
- return getSources();
173
- },
174
- get extracting() {
175
- return getExtracting();
176
- },
177
- shift: (k) => setExtracting((v) => v + k),
178
- force: () => setSources((s) => s)
179
- };
180
- }
181
203
  const ctx = createContext(void 0, {
182
204
  name: "on-case"
183
205
  });
package/dist/index.umd.js CHANGED
@@ -69,7 +69,7 @@
69
69
  */
70
70
  add(node, comp) {
71
71
  const { next } = this;
72
- if (next && comp(node.value, next.value) >= 0)
72
+ if (next && comp(node.value, next.value) > 0)
73
73
  return next.add(node, comp);
74
74
  this.next = node;
75
75
  node.prev = this;
@@ -95,6 +95,28 @@
95
95
  }
96
96
  }
97
97
  const COMPARATOR = (a, b) => (a.order ?? 0) - (b.order ?? 0);
98
+ const SOURCES_OPTS = {
99
+ internal: true,
100
+ equals: false
101
+ }, DEST_COUNT_OPTS = {
102
+ internal: true
103
+ };
104
+ class State {
105
+ #sources = solidJs.createSignal(new OrderedLinkedList(), SOURCES_OPTS);
106
+ #destCount = solidJs.createSignal(0, DEST_COUNT_OPTS);
107
+ get sources() {
108
+ return this.#sources[0]();
109
+ }
110
+ get destCount() {
111
+ return this.#destCount[0]();
112
+ }
113
+ shift(k) {
114
+ this.#destCount[1]((v) => v + k);
115
+ }
116
+ force() {
117
+ this.#sources[1]((v) => v);
118
+ }
119
+ }
98
120
  class Extractor {
99
121
  constructor(name) {
100
122
  this.ctx = solidJs.createContext(void 0, {
@@ -103,6 +125,7 @@
103
125
  }
104
126
  ctx;
105
127
  Joint = Joint.bind(this);
128
+ Fallback = Fallback.bind(this);
106
129
  Dest = Dest.bind(this);
107
130
  Source = Source.bind(this);
108
131
  /**
@@ -112,11 +135,11 @@
112
135
  */
113
136
  get getDestCount() {
114
137
  const state = solidJs.useContext(this.ctx);
115
- return state && (() => state.extracting);
138
+ return state && (() => state.destCount);
116
139
  }
117
140
  }
118
141
  function Joint(props) {
119
- const state = createState();
142
+ const state = new State();
120
143
  return solidJs.createComponent(this.ctx.Provider, {
121
144
  value: state,
122
145
  get children() {
@@ -124,12 +147,30 @@
124
147
  }
125
148
  });
126
149
  }
127
- function Dest() {
150
+ function Fallback() {
151
+ const _self$ = this;
152
+ const f = this.getDestCount;
153
+ return solidJs.createComponent(solidJs.Show, {
154
+ get when() {
155
+ return !f?.();
156
+ },
157
+ get children() {
158
+ return solidJs.createComponent(_self$.Dest, {
159
+ hidden: true
160
+ });
161
+ }
162
+ });
163
+ }
164
+ function Dest(props) {
128
165
  const state = solidJs.useContext(this.ctx);
129
166
  if (!state)
130
167
  throw new Error("An extractor's destination must be inside one of its joints");
131
- state.shift(1);
132
- solidJs.onCleanup(() => state.shift(-1));
168
+ solidJs.createRenderEffect(solidJs.on(solidJs.createMemo(() => props.hidden), (x) => {
169
+ if (x)
170
+ return;
171
+ solidJs.onCleanup(() => state.shift(-1));
172
+ state.shift(1);
173
+ }));
133
174
  return solidJs.createComponent(solidJs.For, {
134
175
  get each() {
135
176
  return [...state.sources];
@@ -162,25 +203,6 @@
162
203
  }));
163
204
  return [];
164
205
  }
165
- function createState() {
166
- const [getExtracting, setExtracting] = solidJs.createSignal(0, {
167
- internal: true
168
- });
169
- const [getSources, setSources] = solidJs.createSignal(new OrderedLinkedList(), {
170
- internal: true,
171
- equals: false
172
- });
173
- return {
174
- get sources() {
175
- return getSources();
176
- },
177
- get extracting() {
178
- return getExtracting();
179
- },
180
- shift: (k) => setExtracting((v) => v + k),
181
- force: () => setSources((s) => s)
182
- };
183
- }
184
206
  const ctx = solidJs.createContext(void 0, {
185
207
  name: "on-case"
186
208
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid-ctrl-flow",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Control flow components for solid-js",
5
5
  "author": "AFatNiBBa",
6
6
  "license": "ISC",
package/readMe.md CHANGED
@@ -145,7 +145,79 @@ return <>
145
145
  </a.Joint>
146
146
  </>
147
147
  ```
148
- The output of this code is ***1***, ***5***, 3, ***1***, ***5***, 4, 0, 2, ***8***, 7, ***8***
148
+ The output of this code is ***1***, ***5***, 3, ***1***, ***5***, 4, 0, 2, ***8***, 7, ***8***.
149
+ <br />
150
+ You can detect the number of destinations available for a given extractor through `Extractor.getDestCount()`
151
+ ```tsx
152
+ const e = new Extractor();
153
+ const [ g, s ] = createSignal(true);
154
+ return <>
155
+ {void untrack(() => {
156
+ // The function is `undefined` when we're not under an `Extractor.Joint`
157
+ console.log(e.getDestCount); //→ undefined
158
+ })}
159
+ <e.Joint>
160
+ {untrack(() => {
161
+ // The founction is returned by a getter that bounds it to the current context
162
+ const f = e.getDestCount!;
163
+ return <>
164
+ {untrack(() => {
165
+ // (This scope is not reactive, so it doesn't know about the destinations that will come up later)
166
+ console.log(f()); //→ 0
167
+ })}
168
+
169
+ <e.Dest />
170
+ {untrack(() => {
171
+ console.log(f()); //→ 1
172
+ })}
173
+
174
+ <e.Dest />
175
+
176
+ <Show when={g()}>
177
+ <e.Dest />
178
+ </Show>
179
+
180
+ <button onClick={() => s(x => !x)}>
181
+ Toggle
182
+ </button>
183
+ <br />
184
+ {/* This is reactive and will contain the number 2 or 3 depending on `g()` */}
185
+ {JSON.stringify(g())} - {f()}
186
+
187
+ <e.Joint>
188
+ {untrack(() => {
189
+ // Since `f()` is bound, it will return the destination count of ITS `Extractor.Joint`, not the closest one
190
+ console.log(f()); //→ 3
191
+ console.log(e.getDestCount!()); //→ 0
192
+ })}
193
+ </e.Joint>
194
+ </>
195
+ })}
196
+ </e.Joint>
197
+ </>
198
+ ```
199
+ You can NOT do this to create a fallback destination for when there aren't any other
200
+ ```tsx
201
+ return <>
202
+ <Show when={!someExtractor.getDestCount!()}>
203
+ <someExtractor.Dest />
204
+ </Show>
205
+ </>
206
+ ```
207
+ Because it would start a strange loop due to which the new destination would disable itself. To do that you must use the `hidden` attribute, which will made sure that the destination is not considered by `Extractor.getDestCount()` at all
208
+ ```tsx
209
+ return <>
210
+ <Show when={!someExtractor.getDestCount!()}>
211
+ <someExtractor.Dest hidden />
212
+ </Show>
213
+ </>
214
+ ```
215
+ Due to this pattern being fairly common, there's the `Extractor.Fallback` component that does that exact thing
216
+ ```tsx
217
+ return <>
218
+ <someExtractor.Fallback />
219
+ </>
220
+ ```
149
221
 
150
222
  ## Utility
151
223