@fun-land/fun-web 0.3.2 → 0.5.0-alpha.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/README.md +6 -24
- package/dist/esm/src/dom.d.ts +53 -31
- package/dist/esm/src/dom.js +83 -50
- package/dist/esm/src/dom.js.map +1 -1
- package/dist/esm/src/index.d.ts +1 -1
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/tsconfig.publish.tsbuildinfo +1 -1
- package/dist/src/dom.d.ts +53 -31
- package/dist/src/dom.js +86 -55
- package/dist/src/dom.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +2 -4
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/examples/todo-app/AddTodoForm.ts +4 -4
- package/examples/todo-app/DraggableTodoList.ts +52 -47
- package/examples/todo-app/Todo.ts +7 -7
- package/examples/todo-app/TodoApp.js +94 -51
- package/examples/todo-app/TodoApp.ts +9 -9
- package/package.json +2 -2
- package/src/dom.test.ts +92 -60
- package/src/dom.ts +190 -159
- package/src/index.ts +1 -3
- package/src/types.ts +2 -6
|
@@ -28,18 +28,10 @@
|
|
|
28
28
|
function comp(...accs) {
|
|
29
29
|
return accs.reduce(_comp);
|
|
30
30
|
}
|
|
31
|
-
var viewed = (toView, fromView) => ({
|
|
32
|
-
query: (s) => [toView(s)],
|
|
33
|
-
mod: (f) => (s) => fromView(f(toView(s)))
|
|
34
|
-
});
|
|
35
31
|
var all = () => ({
|
|
36
32
|
query: (xs) => xs,
|
|
37
33
|
mod: (transform) => (xs) => xs.map(transform)
|
|
38
34
|
});
|
|
39
|
-
var filter = (pred) => ({
|
|
40
|
-
query: (xs) => xs.filter(pred),
|
|
41
|
-
mod: (transform) => (s) => s.map((x) => pred(x) ? transform(x) : x)
|
|
42
|
-
});
|
|
43
35
|
var set = (acc) => flow(K, acc.mod);
|
|
44
36
|
var get = (acc) => (s) => {
|
|
45
37
|
var _a;
|
|
@@ -187,7 +179,29 @@
|
|
|
187
179
|
};
|
|
188
180
|
var onTo = (type, handler, signal) => (el) => on(el, type, handler, signal);
|
|
189
181
|
var enhance = (x, ...fns) => fns.reduce((acc, fn) => fn(acc), x);
|
|
190
|
-
|
|
182
|
+
var keyOf = (keyAcc, item) => {
|
|
183
|
+
const k = keyAcc.query(item)[0];
|
|
184
|
+
if (k == null)
|
|
185
|
+
throw new Error("bindListChildren: key accessor returned no value");
|
|
186
|
+
return k;
|
|
187
|
+
};
|
|
188
|
+
var byKey = (keyAcc, key) => ({
|
|
189
|
+
query: (xs) => {
|
|
190
|
+
const hit = xs.find((t) => keyOf(keyAcc, t) === key);
|
|
191
|
+
return hit ? [hit] : [];
|
|
192
|
+
},
|
|
193
|
+
mod: (f) => (xs) => {
|
|
194
|
+
let found = false;
|
|
195
|
+
return xs.map((t) => {
|
|
196
|
+
if (keyOf(keyAcc, t) !== key) return t;
|
|
197
|
+
if (found) throw new Error(`bindListChildren: duplicate key "${key}"`);
|
|
198
|
+
found = true;
|
|
199
|
+
return f(t);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
var bindListChildren = (options) => (parent) => {
|
|
204
|
+
const { signal, state: list, key: keyAcc, row: renderRow } = options;
|
|
191
205
|
const rows = /* @__PURE__ */ new Map();
|
|
192
206
|
const dispose = () => {
|
|
193
207
|
for (const row of rows.values()) {
|
|
@@ -201,8 +215,9 @@
|
|
|
201
215
|
const nextKeys = [];
|
|
202
216
|
const seen = /* @__PURE__ */ new Set();
|
|
203
217
|
for (const it of items) {
|
|
204
|
-
const k = it
|
|
205
|
-
if (seen.has(k))
|
|
218
|
+
const k = keyOf(keyAcc, it);
|
|
219
|
+
if (seen.has(k))
|
|
220
|
+
throw new Error(`bindListChildren: duplicate key "${k}"`);
|
|
206
221
|
seen.add(k);
|
|
207
222
|
nextKeys.push(k);
|
|
208
223
|
}
|
|
@@ -216,13 +231,13 @@
|
|
|
216
231
|
for (const k of nextKeys) {
|
|
217
232
|
if (!rows.has(k)) {
|
|
218
233
|
const ctrl = new AbortController();
|
|
219
|
-
const itemState = list.focus(
|
|
234
|
+
const itemState = list.focus(byKey(keyAcc, k));
|
|
220
235
|
const el = renderRow({
|
|
221
236
|
signal: ctrl.signal,
|
|
222
237
|
state: itemState,
|
|
223
|
-
remove: () => list.mod((
|
|
238
|
+
remove: () => list.mod((xs) => xs.filter((t) => keyOf(keyAcc, t) !== k))
|
|
224
239
|
});
|
|
225
|
-
rows.set(k, {
|
|
240
|
+
rows.set(k, { el, ctrl });
|
|
226
241
|
}
|
|
227
242
|
}
|
|
228
243
|
const children = parent.children;
|
|
@@ -230,17 +245,51 @@
|
|
|
230
245
|
const k = nextKeys[i];
|
|
231
246
|
const row = rows.get(k);
|
|
232
247
|
const currentAtI = children[i];
|
|
233
|
-
if (currentAtI !== row.el)
|
|
248
|
+
if (currentAtI !== row.el)
|
|
234
249
|
parent.insertBefore(row.el, currentAtI ?? null);
|
|
235
|
-
}
|
|
236
250
|
}
|
|
237
251
|
};
|
|
238
252
|
list.watch(signal, reconcile);
|
|
239
253
|
signal.addEventListener("abort", dispose, { once: true });
|
|
240
254
|
reconcile();
|
|
241
|
-
return
|
|
255
|
+
return parent;
|
|
256
|
+
};
|
|
257
|
+
function renderWhen(options) {
|
|
258
|
+
const {
|
|
259
|
+
state,
|
|
260
|
+
predicate = (x) => x,
|
|
261
|
+
component,
|
|
262
|
+
props,
|
|
263
|
+
signal
|
|
264
|
+
} = options;
|
|
265
|
+
const container = document.createElement("span");
|
|
266
|
+
container.style.display = "contents";
|
|
267
|
+
let childCtrl = null;
|
|
268
|
+
let childEl = null;
|
|
269
|
+
const reconcile = () => {
|
|
270
|
+
const shouldRender = predicate(state.get());
|
|
271
|
+
if (shouldRender && !childEl) {
|
|
272
|
+
childCtrl = new AbortController();
|
|
273
|
+
childEl = component(childCtrl.signal, props);
|
|
274
|
+
container.appendChild(childEl);
|
|
275
|
+
} else if (!shouldRender && childEl) {
|
|
276
|
+
childCtrl?.abort();
|
|
277
|
+
childEl.remove();
|
|
278
|
+
childEl = null;
|
|
279
|
+
childCtrl = null;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
state.watch(signal, reconcile);
|
|
283
|
+
signal.addEventListener(
|
|
284
|
+
"abort",
|
|
285
|
+
() => {
|
|
286
|
+
childCtrl?.abort();
|
|
287
|
+
},
|
|
288
|
+
{ once: true }
|
|
289
|
+
);
|
|
290
|
+
reconcile();
|
|
291
|
+
return container;
|
|
242
292
|
}
|
|
243
|
-
var $ = (selector) => document.querySelector(selector) ?? void 0;
|
|
244
293
|
|
|
245
294
|
// src/mount.ts
|
|
246
295
|
var mount = (component, props, container) => {
|
|
@@ -260,8 +309,8 @@
|
|
|
260
309
|
var init_TodoAppState = () => ({
|
|
261
310
|
value: "",
|
|
262
311
|
items: [
|
|
263
|
-
{ checked: false, label: "Learn fun-web",
|
|
264
|
-
{ checked: true, label: "Build something cool",
|
|
312
|
+
{ checked: false, label: "Learn fun-web", key: "asdf" },
|
|
313
|
+
{ checked: true, label: "Build something cool", key: "fdas" }
|
|
265
314
|
]
|
|
266
315
|
});
|
|
267
316
|
var stateAcc = Acc();
|
|
@@ -272,27 +321,13 @@
|
|
|
272
321
|
prepend({
|
|
273
322
|
checked: false,
|
|
274
323
|
label: state.value,
|
|
275
|
-
priority: 1,
|
|
276
324
|
key: crypto.randomUUID()
|
|
277
325
|
})
|
|
278
326
|
)(state);
|
|
279
327
|
|
|
280
|
-
// examples/todo-app/TodoState.ts
|
|
281
|
-
var stateAcc2 = Acc();
|
|
282
|
-
var priorityAsString = stateAcc2.prop("priority").focus(viewed(String, Number));
|
|
283
|
-
|
|
284
328
|
// examples/todo-app/Todo.ts
|
|
285
329
|
var Todo = (signal, { state, removeItem, onDragStart, onDragEnd, onDragOver }) => {
|
|
286
330
|
const todoData = state.get();
|
|
287
|
-
const priorityState = state.focus(priorityAsString);
|
|
288
|
-
const prioritySelect = enhance(
|
|
289
|
-
h("select", {}, [
|
|
290
|
-
h("option", { value: "0" }, "High"),
|
|
291
|
-
h("option", { value: "1" }, "Low")
|
|
292
|
-
]),
|
|
293
|
-
bindPropertyTo("value", priorityState, signal),
|
|
294
|
-
onTo("change", (e) => priorityState.set(e.currentTarget.value), signal)
|
|
295
|
-
);
|
|
296
331
|
const checkedState = state.prop("checked");
|
|
297
332
|
const checkbox = enhance(
|
|
298
333
|
h("input", { type: "checkbox" }),
|
|
@@ -319,7 +354,6 @@
|
|
|
319
354
|
const li = h("li", { className: "todo-item", "data-key": todoData.key }, [
|
|
320
355
|
dragHandle,
|
|
321
356
|
checkbox,
|
|
322
|
-
prioritySelect,
|
|
323
357
|
labelInput,
|
|
324
358
|
deleteBtn
|
|
325
359
|
]);
|
|
@@ -372,7 +406,7 @@
|
|
|
372
406
|
|
|
373
407
|
// examples/todo-app/DraggableTodoList.ts
|
|
374
408
|
var ANIMATION_DURATION = 300;
|
|
375
|
-
var getElementByKey = (key) =>
|
|
409
|
+
var getElementByKey = (key) => document.querySelector(`[data-key="${key}"]`);
|
|
376
410
|
var DraggableTodoList = (signal, { items }) => {
|
|
377
411
|
let draggedKey = null;
|
|
378
412
|
let lastTargetKey = null;
|
|
@@ -382,12 +416,12 @@
|
|
|
382
416
|
if (currentCount > previousItemCount) {
|
|
383
417
|
const positions = /* @__PURE__ */ new Map();
|
|
384
418
|
currentItems.forEach((item) => {
|
|
385
|
-
const el =
|
|
419
|
+
const el = getElementByKey(item.key);
|
|
386
420
|
if (el) positions.set(item.key, el.getBoundingClientRect());
|
|
387
421
|
});
|
|
388
422
|
requestAnimationFrame(() => {
|
|
389
423
|
positions.forEach((first, key) => {
|
|
390
|
-
const el =
|
|
424
|
+
const el = getElementByKey(key);
|
|
391
425
|
if (!el) return;
|
|
392
426
|
const last = el.getBoundingClientRect();
|
|
393
427
|
const deltaY = first.top - last.top;
|
|
@@ -459,19 +493,19 @@
|
|
|
459
493
|
lastTargetKey = null;
|
|
460
494
|
};
|
|
461
495
|
const todoList = h("ul", { className: "todo-list" });
|
|
462
|
-
|
|
463
|
-
|
|
496
|
+
bindListChildren({
|
|
497
|
+
key: prop()("key"),
|
|
464
498
|
signal,
|
|
465
|
-
items,
|
|
466
|
-
(row) => Todo(row.signal, {
|
|
499
|
+
state: items,
|
|
500
|
+
row: (row) => Todo(row.signal, {
|
|
467
501
|
removeItem: () => {
|
|
468
|
-
const element =
|
|
502
|
+
const element = getElementByKey(row.state.get().key);
|
|
469
503
|
if (element) {
|
|
470
504
|
element.classList.add("todo-item-exit");
|
|
471
505
|
setTimeout(() => {
|
|
472
506
|
const positions = /* @__PURE__ */ new Map();
|
|
473
507
|
items.get().forEach((item) => {
|
|
474
|
-
const el =
|
|
508
|
+
const el = getElementByKey(item.key);
|
|
475
509
|
if (el) positions.set(item.key, el.getBoundingClientRect());
|
|
476
510
|
});
|
|
477
511
|
row.remove();
|
|
@@ -505,7 +539,7 @@
|
|
|
505
539
|
onDragEnd: handleDragEnd,
|
|
506
540
|
onDragOver: handleDragOver
|
|
507
541
|
})
|
|
508
|
-
);
|
|
542
|
+
})(todoList);
|
|
509
543
|
return todoList;
|
|
510
544
|
};
|
|
511
545
|
|
|
@@ -564,17 +598,26 @@
|
|
|
564
598
|
signal
|
|
565
599
|
)
|
|
566
600
|
);
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
601
|
+
const AllDoneComponent = () => {
|
|
602
|
+
return h("span", {
|
|
603
|
+
textContent: "\u{1F389} All Done!",
|
|
604
|
+
className: "all-done-text"
|
|
605
|
+
});
|
|
606
|
+
};
|
|
607
|
+
const allDoneState = funState(false);
|
|
571
608
|
state.focus(allCheckedAcc).watchAll(signal, (checks) => {
|
|
572
|
-
|
|
609
|
+
allDoneState.set(checks.length > 0 && checks.every(Boolean));
|
|
610
|
+
});
|
|
611
|
+
const allDoneEl = renderWhen({
|
|
612
|
+
state: allDoneState,
|
|
613
|
+
component: AllDoneComponent,
|
|
614
|
+
props: {},
|
|
615
|
+
signal
|
|
573
616
|
});
|
|
574
617
|
return h("div", { className: "todo-app" }, [
|
|
575
618
|
h("h1", { textContent: "Todo Example" }),
|
|
576
619
|
AddTodoForm(signal, { state }),
|
|
577
|
-
h("div", { className: "controls" }, [markAllBtn,
|
|
620
|
+
h("div", { className: "controls" }, [markAllBtn, allDoneEl]),
|
|
578
621
|
DraggableTodoList(signal, { items: state.prop("items") })
|
|
579
622
|
]);
|
|
580
623
|
};
|
|
@@ -2,17 +2,12 @@ import {
|
|
|
2
2
|
h,
|
|
3
3
|
funState,
|
|
4
4
|
mount,
|
|
5
|
-
|
|
5
|
+
on,
|
|
6
6
|
type Component,
|
|
7
7
|
enhance,
|
|
8
8
|
renderWhen,
|
|
9
9
|
} from "../../src/index";
|
|
10
|
-
import {
|
|
11
|
-
type TodoAppState,
|
|
12
|
-
markAllDone,
|
|
13
|
-
allCheckedAcc,
|
|
14
|
-
init_TodoAppState,
|
|
15
|
-
} from "./TodoAppState";
|
|
10
|
+
import { markAllDone, allCheckedAcc, init_TodoAppState } from "./TodoAppState";
|
|
16
11
|
import { DraggableTodoList } from "./DraggableTodoList";
|
|
17
12
|
import { AddTodoForm } from "./AddTodoForm";
|
|
18
13
|
|
|
@@ -28,7 +23,7 @@ const TodoApp: Component = (signal) => {
|
|
|
28
23
|
textContent: "Mark All Done",
|
|
29
24
|
className: "mark-all-btn",
|
|
30
25
|
}),
|
|
31
|
-
|
|
26
|
+
on(
|
|
32
27
|
"click",
|
|
33
28
|
() => {
|
|
34
29
|
state.mod(markAllDone);
|
|
@@ -51,7 +46,12 @@ const TodoApp: Component = (signal) => {
|
|
|
51
46
|
allDoneState.set(checks.length > 0 && checks.every(Boolean));
|
|
52
47
|
});
|
|
53
48
|
|
|
54
|
-
const allDoneEl = renderWhen(
|
|
49
|
+
const allDoneEl = renderWhen({
|
|
50
|
+
state: allDoneState,
|
|
51
|
+
component: AllDoneComponent,
|
|
52
|
+
props: {},
|
|
53
|
+
signal,
|
|
54
|
+
});
|
|
55
55
|
|
|
56
56
|
return h("div", { className: "todo-app" }, [
|
|
57
57
|
h("h1", { textContent: "Todo Example" }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fun-land/fun-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-alpha.0",
|
|
4
4
|
"description": "A web library for component-based development using fun-land",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"url": "https://github.com/fun-land/fun-land/issues"
|
|
50
50
|
},
|
|
51
51
|
"license": "MIT",
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "68dd8a583be1733e0eec9070e8f4dfd6c8742fc0"
|
|
53
53
|
}
|
package/src/dom.test.ts
CHANGED
|
@@ -779,12 +779,12 @@ describe("renderWhen", () => {
|
|
|
779
779
|
const controller = new AbortController();
|
|
780
780
|
const showState = funState(true);
|
|
781
781
|
|
|
782
|
-
const container = renderWhen(
|
|
783
|
-
showState,
|
|
784
|
-
TestComponent,
|
|
785
|
-
{ text: "Hello" },
|
|
786
|
-
controller.signal
|
|
787
|
-
);
|
|
782
|
+
const container = renderWhen({
|
|
783
|
+
state: showState,
|
|
784
|
+
component: TestComponent,
|
|
785
|
+
props: { text: "Hello" },
|
|
786
|
+
signal: controller.signal
|
|
787
|
+
});
|
|
788
788
|
|
|
789
789
|
expect(container.children.length).toBe(1);
|
|
790
790
|
expect(container.children[0].textContent).toBe("Hello");
|
|
@@ -799,12 +799,12 @@ describe("renderWhen", () => {
|
|
|
799
799
|
const controller = new AbortController();
|
|
800
800
|
const showState = funState(false);
|
|
801
801
|
|
|
802
|
-
const container = renderWhen(
|
|
803
|
-
showState,
|
|
804
|
-
TestComponent,
|
|
805
|
-
{ text: "Hello" },
|
|
806
|
-
controller.signal
|
|
807
|
-
);
|
|
802
|
+
const container = renderWhen({
|
|
803
|
+
state: showState,
|
|
804
|
+
component: TestComponent,
|
|
805
|
+
props: { text: "Hello" },
|
|
806
|
+
signal: controller.signal
|
|
807
|
+
});
|
|
808
808
|
|
|
809
809
|
expect(container.children.length).toBe(0);
|
|
810
810
|
|
|
@@ -815,12 +815,12 @@ describe("renderWhen", () => {
|
|
|
815
815
|
const controller = new AbortController();
|
|
816
816
|
const showState = funState(false);
|
|
817
817
|
|
|
818
|
-
const container = renderWhen(
|
|
819
|
-
showState,
|
|
820
|
-
TestComponent,
|
|
821
|
-
{ text: "Hello" },
|
|
822
|
-
controller.signal
|
|
823
|
-
);
|
|
818
|
+
const container = renderWhen({
|
|
819
|
+
state: showState,
|
|
820
|
+
component: TestComponent,
|
|
821
|
+
props: { text: "Hello" },
|
|
822
|
+
signal: controller.signal
|
|
823
|
+
});
|
|
824
824
|
|
|
825
825
|
expect(container.children.length).toBe(0);
|
|
826
826
|
|
|
@@ -836,12 +836,12 @@ describe("renderWhen", () => {
|
|
|
836
836
|
const controller = new AbortController();
|
|
837
837
|
const showState = funState(true);
|
|
838
838
|
|
|
839
|
-
const container = renderWhen(
|
|
840
|
-
showState,
|
|
841
|
-
TestComponent,
|
|
842
|
-
{ text: "Hello" },
|
|
843
|
-
controller.signal
|
|
844
|
-
);
|
|
839
|
+
const container = renderWhen({
|
|
840
|
+
state: showState,
|
|
841
|
+
component: TestComponent,
|
|
842
|
+
props: { text: "Hello" },
|
|
843
|
+
signal: controller.signal
|
|
844
|
+
});
|
|
845
845
|
|
|
846
846
|
const child = container.children[0] as HTMLElement;
|
|
847
847
|
expect(container.children.length).toBe(1);
|
|
@@ -867,12 +867,12 @@ describe("renderWhen", () => {
|
|
|
867
867
|
return h("div", null, props.text);
|
|
868
868
|
};
|
|
869
869
|
|
|
870
|
-
renderWhen(
|
|
871
|
-
showState,
|
|
872
|
-
ComponentWithAbortListener,
|
|
873
|
-
{ text: "Hello" },
|
|
874
|
-
controller.signal
|
|
875
|
-
);
|
|
870
|
+
renderWhen({
|
|
871
|
+
state: showState,
|
|
872
|
+
component: ComponentWithAbortListener,
|
|
873
|
+
props: { text: "Hello" },
|
|
874
|
+
signal: controller.signal
|
|
875
|
+
});
|
|
876
876
|
|
|
877
877
|
expect(abortCallback).not.toHaveBeenCalled();
|
|
878
878
|
|
|
@@ -896,12 +896,12 @@ describe("renderWhen", () => {
|
|
|
896
896
|
return h("div", null, props.text);
|
|
897
897
|
};
|
|
898
898
|
|
|
899
|
-
renderWhen(
|
|
900
|
-
showState,
|
|
901
|
-
ComponentWithAbortListener,
|
|
902
|
-
{ text: "Hello" },
|
|
903
|
-
controller.signal
|
|
904
|
-
);
|
|
899
|
+
renderWhen({
|
|
900
|
+
state: showState,
|
|
901
|
+
component: ComponentWithAbortListener,
|
|
902
|
+
props: { text: "Hello" },
|
|
903
|
+
signal: controller.signal
|
|
904
|
+
});
|
|
905
905
|
|
|
906
906
|
expect(abortCallback).not.toHaveBeenCalled();
|
|
907
907
|
|
|
@@ -914,12 +914,12 @@ describe("renderWhen", () => {
|
|
|
914
914
|
const controller = new AbortController();
|
|
915
915
|
const showState = funState(false);
|
|
916
916
|
|
|
917
|
-
const container = renderWhen(
|
|
918
|
-
showState,
|
|
919
|
-
TestComponent,
|
|
920
|
-
{ text: "Hello" },
|
|
921
|
-
controller.signal
|
|
922
|
-
);
|
|
917
|
+
const container = renderWhen({
|
|
918
|
+
state: showState,
|
|
919
|
+
component: TestComponent,
|
|
920
|
+
props: { text: "Hello" },
|
|
921
|
+
signal: controller.signal
|
|
922
|
+
});
|
|
923
923
|
|
|
924
924
|
expect(container.children.length).toBe(0);
|
|
925
925
|
|
|
@@ -942,12 +942,12 @@ describe("renderWhen", () => {
|
|
|
942
942
|
const controller = new AbortController();
|
|
943
943
|
const showState = funState(true);
|
|
944
944
|
|
|
945
|
-
const container = renderWhen(
|
|
946
|
-
showState,
|
|
947
|
-
TestComponent,
|
|
948
|
-
{ text: "Hello" },
|
|
949
|
-
controller.signal
|
|
950
|
-
) as HTMLElement;
|
|
945
|
+
const container = renderWhen({
|
|
946
|
+
state: showState,
|
|
947
|
+
component: TestComponent,
|
|
948
|
+
props: { text: "Hello" },
|
|
949
|
+
signal: controller.signal
|
|
950
|
+
}) as HTMLElement;
|
|
951
951
|
|
|
952
952
|
expect(container.style.display).toBe("contents");
|
|
953
953
|
|
|
@@ -958,12 +958,12 @@ describe("renderWhen", () => {
|
|
|
958
958
|
const controller = new AbortController();
|
|
959
959
|
const showState = funState(true);
|
|
960
960
|
|
|
961
|
-
const container = renderWhen(
|
|
962
|
-
showState,
|
|
963
|
-
TestComponent,
|
|
964
|
-
{ text: "Hello" },
|
|
965
|
-
controller.signal
|
|
966
|
-
);
|
|
961
|
+
const container = renderWhen({
|
|
962
|
+
state: showState,
|
|
963
|
+
component: TestComponent,
|
|
964
|
+
props: { text: "Hello" },
|
|
965
|
+
signal: controller.signal
|
|
966
|
+
});
|
|
967
967
|
|
|
968
968
|
expect(container.children.length).toBe(1);
|
|
969
969
|
|
|
@@ -989,15 +989,47 @@ describe("renderWhen", () => {
|
|
|
989
989
|
);
|
|
990
990
|
};
|
|
991
991
|
|
|
992
|
-
const container = renderWhen(
|
|
993
|
-
showState,
|
|
994
|
-
PropsComponent,
|
|
995
|
-
{ text: "Count", count: 42 },
|
|
996
|
-
controller.signal
|
|
997
|
-
);
|
|
992
|
+
const container = renderWhen({
|
|
993
|
+
state: showState,
|
|
994
|
+
component: PropsComponent,
|
|
995
|
+
props: { text: "Count", count: 42 },
|
|
996
|
+
signal: controller.signal
|
|
997
|
+
});
|
|
998
998
|
|
|
999
999
|
expect(container.children[0].textContent).toBe("Count: 42");
|
|
1000
1000
|
|
|
1001
1001
|
controller.abort();
|
|
1002
1002
|
});
|
|
1003
|
+
|
|
1004
|
+
test("should work with predicate function", () => {
|
|
1005
|
+
const controller = new AbortController();
|
|
1006
|
+
enum Status { Loading, Success, Error }
|
|
1007
|
+
const statusState = funState(Status.Loading);
|
|
1008
|
+
|
|
1009
|
+
const container = renderWhen({
|
|
1010
|
+
state: statusState,
|
|
1011
|
+
predicate: (status) => status === Status.Success,
|
|
1012
|
+
component: TestComponent,
|
|
1013
|
+
props: { text: "Success!" },
|
|
1014
|
+
signal: controller.signal
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
// Should not render initially
|
|
1018
|
+
expect(container.children.length).toBe(0);
|
|
1019
|
+
|
|
1020
|
+
// Should render when predicate matches
|
|
1021
|
+
statusState.set(Status.Success);
|
|
1022
|
+
expect(container.children.length).toBe(1);
|
|
1023
|
+
expect(container.children[0].textContent).toBe("Success!");
|
|
1024
|
+
|
|
1025
|
+
// Should unmount when predicate doesn't match
|
|
1026
|
+
statusState.set(Status.Error);
|
|
1027
|
+
expect(container.children.length).toBe(0);
|
|
1028
|
+
|
|
1029
|
+
// Should mount again when predicate matches
|
|
1030
|
+
statusState.set(Status.Success);
|
|
1031
|
+
expect(container.children.length).toBe(1);
|
|
1032
|
+
|
|
1033
|
+
controller.abort();
|
|
1034
|
+
});
|
|
1003
1035
|
});
|