Skip to content

Commit 265e730

Browse files
committed
Complete Await component
1 parent 963ac2c commit 265e730

28 files changed

+295
-250
lines changed

components/README.md

Lines changed: 120 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,110 @@ Try on jsfiddle: [Modal](https://jsfiddle.net/mks9253o/1/), [MessageBoard](https
6363

6464
## Documentation
6565

66-
The following UI components has been implemented so far:
67-
* [Modal](#modal) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/modal?file=%2Fsrc%2Fmain.ts%3A1%2C1))
68-
* [Tabs](#tabs) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tabs?file=%2Fsrc%2Fmain.ts%3A1%2C1))
69-
* [MessageBoard](#message) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/message?file=/src/main.ts))
70-
* [Tooltip](#tooltip) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tooltip?file=/src/main.ts:1,1))
71-
* [Toggle](#toggle) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/toggle?file=%2Fsrc%2Fmain.ts%3A1%2C1))
72-
* [OptionGroup](#optiongroup) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/option-group?file=%2Fsrc%2Fmain.ts%3A1%2C1))
73-
* [Banner](#banner) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/banner?file=/src/main.ts:1,1))
74-
* <span style="color:red; padding-right: 0.3rem;">**New!**</span> [FloatingWindow](#floatingwindow) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/window?file=%2Fsrc%2Fmain.ts%3A1%2C1))
66+
The following components have been implemented so far:
67+
68+
* Utility components:
69+
* [Await](#await) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/await?file=%2Fsrc%2Fmain.ts%3A1%2C1))
70+
* UI components:
71+
* [Modal](#modal) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/modal?file=%2Fsrc%2Fmain.ts%3A1%2C1))
72+
* [Tabs](#tabs) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tabs?file=%2Fsrc%2Fmain.ts%3A1%2C1))
73+
* [MessageBoard](#message) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/message?file=/src/main.ts))
74+
* [Tooltip](#tooltip) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tooltip?file=/src/main.ts:1,1))
75+
* [Toggle](#toggle) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/toggle?file=%2Fsrc%2Fmain.ts%3A1%2C1))
76+
* [OptionGroup](#optiongroup) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/option-group?file=%2Fsrc%2Fmain.ts%3A1%2C1))
77+
* [Banner](#banner) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/banner?file=/src/main.ts:1,1))
78+
* <span style="color:red; padding-right: 0.3rem;">**New!**</span> [FloatingWindow](#floatingwindow) ([preview](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/window?file=%2Fsrc%2Fmain.ts%3A1%2C1))
79+
80+
### Await
81+
82+
`Await` is a utility component that helps you build UI components based on asynchronous data (i.e.: a JavaScript [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) object).
83+
84+
#### Signature
85+
86+
```js
87+
Await(
88+
{
89+
value, // A `Promise` object for asynchronous data
90+
container, // The container of the result. Default `div`
91+
Loading, // What to render when the data is being loaded
92+
Error, // What to render when error occurs
93+
},
94+
children,
95+
) => <The created UI element>
96+
```
97+
98+
The `children` parameter (type: `(data: T) => ValidChildDomValue`) is a function that takes the resolved data as input and returns a valid child DOM value (`Node`, primitives, `null` or `undefined`), used to indicate what to render after the data is loaded.
99+
100+
#### Examples
101+
102+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/await?file=%2Fsrc%2Fmain.ts%3A1%2C1).
103+
104+
Example 1 (fetching the number of GitHub stars):
105+
106+
```ts
107+
const Example1 = () => {
108+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
109+
110+
const fetchWithDelay = (url: string, waitMs: number) =>
111+
sleep(waitMs).then(() => fetch(url)).then(r => r.json())
112+
113+
const fetchStar = () =>
114+
fetchWithDelay("https://api.github.com/repos/vanjs-org/van", 1000)
115+
.then(data => data.stargazers_count)
116+
117+
const data = van.state(fetchStar())
118+
119+
return [
120+
() => h2(
121+
"Github Star: ",
122+
Await({
123+
value: data.val, container: span,
124+
Loading: () => "🌀 Loading...",
125+
Error: () => "🙀 Request failed.",
126+
}, starNumber => `⭐️ ${starNumber}!`)
127+
),
128+
() => Await({
129+
value: data.val,
130+
Loading: () => '',
131+
}, () => button({onclick: () => (data.val = fetchStar())}, "Refetch")),
132+
]
133+
}
134+
```
135+
136+
Example 2 (parallel `Await`):
137+
138+
```ts
139+
const Example2 = () => {
140+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
141+
142+
const loadNumber = () =>
143+
sleep(Math.random() * 1000).then(() => Math.floor(Math.random() * 10))
144+
145+
const a = van.state(loadNumber()), b = van.state(loadNumber())
146+
147+
return [
148+
h2("Parallel Await"),
149+
() => {
150+
const sum = van.derive(() => Promise.all([a.val, b.val]).then(([a, b]) => a + b))
151+
return Await({
152+
value: sum.val,
153+
Loading: () => div(
154+
Await({value: a.val, Loading: () => "🌀 Loading a..."}, () => "Done"),
155+
Await({value: b.val, Loading: () => "🌀 Loading b..."}, () => "Done"),
156+
),
157+
}, sum => "a + b = " + sum)
158+
},
159+
p(button({onclick: () => (a.val = loadNumber(), b.val = loadNumber())}, "Reload")),
160+
]
161+
}
162+
```
163+
164+
#### Property Reference
165+
166+
* `value`: Type `Promise`. Required. The asynchronous data that the result UI element is based on.
167+
* `container`: Type `TagFunction<Element>`. Default `div` (`van.tags.div`). Optional. The type of the wrapper HTML element for the result.
168+
* `Loading`: Type `() => ValidChildDomValue`. Optional. If specified, indicates what to render when the asynchronous data is being loaded.
169+
* `Error`: Type `(reason: Error) => ValidChildDomValue`. Optional. If specified, indicates what to render when error occurs while fetching the asynchronous data.
75170

76171
### Modal
77172

@@ -85,6 +180,8 @@ Modal({...props}, ...children) => <The created modal window>
85180

86181
#### Examples
87182

183+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/modal?file=%2Fsrc%2Fmain.ts%3A1%2C1).
184+
88185
Example 1:
89186

90187
```ts
@@ -125,8 +222,6 @@ van.add(document.body, Modal({closed, blurBackground: true},
125222
))
126223
```
127224

128-
You can live preview the examples with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/modal?file=%2Fsrc%2Fmain.ts%3A1%2C1).
129-
130225
#### Property Reference
131226

132227
* `closed`: Type `State<boolean>`. Required. A `State` object used to close the created modal window. Basically, setting `closed.val = true` will close the created modal window. You can also subscribe the closing event of the modal window via [`van.derive`](https://vanjs.org/tutorial#api-derive).
@@ -151,6 +246,8 @@ The `tabContents` parameter is an object whose keys are the titles of the tabs a
151246

152247
#### Example
153248

249+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tabs?file=%2Fsrc%2Fmain.ts%3A1%2C1).
250+
154251
```ts
155252
van.add(document.body, Tabs(
156253
{
@@ -177,8 +274,6 @@ van.add(document.body, Tabs(
177274
))
178275
```
179276

180-
You can live preview the example with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tabs?file=%2Fsrc%2Fmain.ts%3A1%2C1).
181-
182277
#### Property Reference
183278

184279
* `activeTab`: Type `State<string>`. Optional. If specified, you can activate a tab via the specified `State` object with `activeTab.val = "<tab title>"`, and subscribe to the changes of active tab via [`van.derive`](https://vanjs.org/tutorial#api-derive).
@@ -222,6 +317,8 @@ board.remove()
222317

223318
#### Examples
224319

320+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/message?file=%2Fsrc%2Fmain.ts%3A1%2C1).
321+
225322
```ts
226323
const board = new MessageBoard({top: "20px"})
227324

@@ -237,8 +334,6 @@ const example3 = () => {
237334
document.addEventListener("keydown", e => e.key === "Escape" && (closed.val = true))
238335
```
239336

240-
You can live preview the examples with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/message?file=/src/main.ts).
241-
242337
#### Property Reference
243338

244339
Message board properties:
@@ -274,6 +369,8 @@ Tooltip({...props}) => <The created tooltip element>
274369

275370
#### Examples
276371

372+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tooltip?file=%2Fsrc%2Fmain.ts%3A1%2C1).
373+
277374
```ts
278375
const tooltip1Show = van.state(false)
279376
const tooltip2Show = van.state(false)
@@ -301,8 +398,6 @@ van.add(document.body,
301398
)
302399
```
303400

304-
You can live preview the examples with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/tooltip?file=/src/main.ts:1,1).
305-
306401
Note that the lines:
307402

308403
```ts
@@ -340,15 +435,15 @@ Toggle({...props}) => <The created toggle switch>
340435

341436
#### Example
342437

438+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/toggle?file=%2Fsrc%2Fmain.ts%3A1%2C1).
439+
343440
```ts
344441
van.add(document.body, Toggle({
345442
size: 2,
346443
onColor: "#4CAF50"
347444
}))
348445
```
349446

350-
You can live preview the example with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/toggle?file=%2Fsrc%2Fmain.ts%3A1%2C1).
351-
352447
#### Property Reference
353448

354449
* `on`: Type `boolean | State<boolean>`. Default `false`. Optional. A boolean or a boolean-typed `State` object to indicate the status of the toggle. If a `State` object is specified, you can turn on/off the toggle via the specified `State` object with `on.val = <true|false>`, and subscribe to the status change of the toggle via [`van.derive`](https://vanjs.org/tutorial#api-derive).
@@ -379,6 +474,8 @@ The `options` parameter is a `string[]` for all the options.
379474

380475
#### Example
381476

477+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/option-group?file=%2Fsrc%2Fmain.ts%3A1%2C1).
478+
382479
```ts
383480
const selected = van.state("")
384481
const options = ["Water", "Coffee", "Juice"]
@@ -391,8 +488,6 @@ van.add(document.body,
391488
)
392489
```
393490

394-
You can live preview the example with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/option-group?file=%2Fsrc%2Fmain.ts%3A1%2C1).
395-
396491
#### Property Reference
397492

398493
* `selected`: Type `State<string>`. Required. A `State` object for the currently selected option. You can change the selected option with `selected.val = <option string>`, and subscribe to the selection change via [`van.derive`](https://vanjs.org/tutorial#api-derive).
@@ -419,6 +514,8 @@ Banner({...props}, ...children) => <The created banner element>
419514

420515
#### Examples
421516

517+
Preview with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/banner?file=%2Fsrc%2Fmain.ts%3A1%2C1).
518+
422519
```ts
423520
van.add(document.body,
424521
h2("Sticky Banner"),
@@ -434,8 +531,6 @@ van.add(document.body,
434531
)
435532
```
436533

437-
You can live preview the examples with [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/van/tree/main/components/examples/banner?file=/src/main.ts:1,1).
438-
439534
#### Property Reference
440535

441536
* `backgroundColor`: Type `string`. Default `#fff1a8`. Optional. The background color of the banner.
@@ -488,7 +583,7 @@ van.add(document.body, FloatingWindow(
488583
))
489584
```
490585

491-
Window with customized close button:
586+
Close button with custom appearance:
492587

493588
```ts
494589
van.add(document.body, FloatingWindow(
@@ -502,7 +597,7 @@ van.add(document.body, FloatingWindow(
502597
))
503598
```
504599

505-
Window with `Tabs` and custom close button:
600+
Window with `Tabs`:
506601

507602
```ts
508603
const closed = van.state(false)

components/dist/van-ui.d.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1-
import { ChildDom, State, ValidChildDomValue } from "vanjs-core";
1+
import { ChildDom, State, TagFunc, ValidChildDomValue } from "vanjs-core";
22
export type CSSPropertyBag = Record<string, string | number>;
33
export type CSSStyles = Record<string, CSSPropertyBag>;
4+
interface AwaitProps<Value> {
5+
value: Promise<Value>;
6+
container?: TagFunc<Element>;
7+
Loading?: () => ValidChildDomValue;
8+
Error?: (reason: Error) => ValidChildDomValue;
9+
}
10+
export type AwaitState<Value> = {
11+
status: "pending";
12+
} | {
13+
status: "fulfilled";
14+
value: Value;
15+
} | {
16+
status: "rejected";
17+
value: Error;
18+
};
19+
export declare const Await: <T>({ value, container, Loading, Error }: AwaitProps<T>, children: (data: T) => ValidChildDomValue) => Element;
420
export interface ModalProps {
521
readonly closed: State<boolean>;
622
readonly backgroundColor?: string;
@@ -135,19 +151,4 @@ export interface FloatingWindowProps {
135151
}
136152
export declare const topMostZIndex: () => number;
137153
export declare const FloatingWindow: ({ title, closed, x, y, width, height, closeCross, customStacking, zIndex, disableMove, disableResize, headerColor, windowClass, windowStyleOverrides, headerClass, headerStyleOverrides, childrenContainerClass, childrenContainerStyleOverrides, crossClass, crossStyleOverrides, crossHoverClass, crossHoverStyleOverrides, }: FloatingWindowProps, ...children: readonly ChildDom[]) => () => HTMLDivElement | null;
138-
interface AwaitProps<Value> {
139-
value: Promise<Value>;
140-
Loading?: () => ValidChildDomValue;
141-
Error?: (reason: Error) => ValidChildDomValue;
142-
}
143-
export type AwaitState<Value> = {
144-
status: 'pending';
145-
} | {
146-
value: Value;
147-
status: 'fulfilled';
148-
} | {
149-
value: Error;
150-
status: 'rejected';
151-
};
152-
export declare const Await: <T>({ value, Loading, Error }: AwaitProps<T>, children: (data: T) => ValidChildDomValue) => () => ValidChildDomValue;
153154
export {};

components/dist/van-ui.js

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ const toStyleSheet = (styles) => {
99
};
1010
const stateProto = Object.getPrototypeOf(van.state(null));
1111
const stateOf = (v) => (Object.getPrototypeOf(v ?? 0) === stateProto ? v : van.state(v));
12+
export const Await = ({ value, container = div, Loading, Error }, children) => {
13+
const data = van.state({ status: "pending" });
14+
value
15+
.then(result => data.val = { status: "fulfilled", value: result })
16+
.catch(err => data.val = { status: "rejected", value: err });
17+
return container(() => data.val.status === "pending" ? Loading?.() ?? "" :
18+
data.val.status === "rejected" ? Error?.(data.val.value) :
19+
children(data.val.value));
20+
};
1221
export const Modal = ({ closed, backgroundColor = "rgba(0,0,0,.5)", blurBackground = false, backgroundClass = "", backgroundStyleOverrides = {}, modalClass = "", modalStyleOverrides = {}, }, ...children) => {
1322
const backgroundStyle = {
1423
display: "flex",
@@ -427,28 +436,3 @@ export const FloatingWindow = ({ title, closed = van.state(false), x = 100, y =
427436
style: toStyleStr(childrenContainerStyleOverrides)
428437
}, children));
429438
};
430-
export const Await = ({ value, Loading, Error }, children) => {
431-
const data = van.state({ status: 'pending' });
432-
const resolve = (promise) => {
433-
data.val = { status: 'pending' };
434-
promise
435-
.then((result) => {
436-
data.val = {
437-
value: result,
438-
status: 'fulfilled',
439-
};
440-
})
441-
.catch((err) => {
442-
data.val = {
443-
value: err,
444-
status: 'rejected',
445-
};
446-
});
447-
};
448-
van.derive(() => resolve(value));
449-
return () => data.val.status === 'pending'
450-
? Loading?.()
451-
: data.val.status === 'rejected'
452-
? Error?.(data.val.value)
453-
: children(data.val.value);
454-
};

components/dist/van-ui.nomodule.js

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
};
1010
const stateProto = Object.getPrototypeOf(van.state(null));
1111
const stateOf = (v) => (Object.getPrototypeOf(v ?? 0) === stateProto ? v : van.state(v));
12+
window.Await = ({ value, container = div, Loading, Error }, children) => {
13+
const data = van.state({ status: "pending" });
14+
value
15+
.then(result => data.val = { status: "fulfilled", value: result })
16+
.catch(err => data.val = { status: "rejected", value: err });
17+
return container(() => data.val.status === "pending" ? Loading?.() ?? "" :
18+
data.val.status === "rejected" ? Error?.(data.val.value) :
19+
children(data.val.value));
20+
};
1221
window.Modal = ({ closed, backgroundColor = "rgba(0,0,0,.5)", blurBackground = false, backgroundClass = "", backgroundStyleOverrides = {}, modalClass = "", modalStyleOverrides = {}, }, ...children) => {
1322
const backgroundStyle = {
1423
display: "flex",
@@ -427,29 +436,4 @@
427436
style: toStyleStr(childrenContainerStyleOverrides)
428437
}, children));
429438
};
430-
window.Await = ({ value, Loading, Error }, children) => {
431-
const data = van.state({ status: 'pending' });
432-
const resolve = (promise) => {
433-
data.val = { status: 'pending' };
434-
promise
435-
.then((result) => {
436-
data.val = {
437-
value: result,
438-
status: 'fulfilled',
439-
};
440-
})
441-
.catch((err) => {
442-
data.val = {
443-
value: err,
444-
status: 'rejected',
445-
};
446-
});
447-
};
448-
van.derive(() => resolve(value));
449-
return () => data.val.status === 'pending'
450-
? Loading?.()
451-
: data.val.status === 'rejected'
452-
? Error?.(data.val.value)
453-
: children(data.val.value);
454-
};
455439
}

0 commit comments

Comments
 (0)