@monimesl/dot-cli 0.1.0-alpha.2 → 0.1.0-alpha.3
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/cli.js +151 -35
- package/dist/cli.js.map +1 -1
- package/dist/server-z7M4FLjf.js +10886 -0
- package/dist/server-z7M4FLjf.js.map +1 -0
- package/dist/template/.env +3 -3
- package/dist/template/dot.ts +14 -1
- package/dist/template/pages/device/controller.ts +70 -0
- package/dist/template/pages/device/state.ts +7 -0
- package/dist/template/pages/device/view.dml +44 -0
- package/dist/template/pages/feedback/controller.ts +16 -0
- package/dist/template/pages/feedback/state.ts +7 -0
- package/dist/template/pages/feedback/view.dml +87 -0
- package/dist/template/pages/files/controller.ts +47 -0
- package/dist/template/pages/files/state.ts +11 -0
- package/dist/template/pages/files/view.dml +35 -0
- package/dist/template/pages/forms/controller.ts +44 -0
- package/dist/template/pages/forms/state.ts +15 -0
- package/dist/template/pages/forms/view.dml +39 -0
- package/dist/template/pages/images/controller.ts +15 -0
- package/dist/template/pages/images/state.ts +7 -0
- package/dist/template/pages/images/view.dml +38 -0
- package/dist/template/pages/index/controller.ts +2 -59
- package/dist/template/pages/index/state.ts +93 -12
- package/dist/template/pages/index/view.dml +17 -63
- package/dist/template/pages/layout/controller.ts +23 -0
- package/dist/template/pages/layout/state.ts +12 -0
- package/dist/template/pages/layout/view.dml +63 -0
- package/dist/template/pages/lists/controller.ts +36 -0
- package/dist/template/pages/lists/state.ts +96 -0
- package/dist/template/pages/lists/view.dml +59 -0
- package/dist/template/pages/navigation/controller.ts +42 -0
- package/dist/template/pages/navigation/state.ts +13 -0
- package/dist/template/pages/navigation/view.dml +24 -0
- package/dist/template/pages/network/controller.ts +107 -0
- package/dist/template/pages/network/state.ts +15 -0
- package/dist/template/pages/network/view.dml +56 -0
- package/dist/template/pages/state/controller.ts +22 -0
- package/dist/template/pages/state/state.ts +10 -0
- package/dist/template/pages/state/view.dml +33 -0
- package/dist/template/pages/text/controller.ts +10 -0
- package/dist/template/pages/text/state.ts +7 -0
- package/dist/template/pages/text/view.dml +49 -0
- package/package.json +10 -6
- package/dist/template/pages/help/tips/controller.ts +0 -30
- package/dist/template/pages/help/tips/state.ts +0 -11
- package/dist/template/pages/help/tips/view.dml +0 -15
|
@@ -1,17 +1,98 @@
|
|
|
1
|
-
export interface
|
|
2
|
-
|
|
1
|
+
export interface Feature {
|
|
2
|
+
page: PageId;
|
|
3
|
+
title: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
icon: string;
|
|
3
6
|
}
|
|
4
7
|
|
|
5
|
-
export
|
|
8
|
+
export interface Section {
|
|
9
|
+
title: string;
|
|
10
|
+
features: Feature[];
|
|
11
|
+
}
|
|
6
12
|
|
|
7
13
|
export default {
|
|
8
|
-
title: '
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
title: 'Showcase',
|
|
15
|
+
sections: [
|
|
16
|
+
{
|
|
17
|
+
title: 'Views',
|
|
18
|
+
features: [
|
|
19
|
+
{
|
|
20
|
+
page: 'layout',
|
|
21
|
+
title: 'Layout',
|
|
22
|
+
summary: 'Rows, columns, gaps, alignment and cards',
|
|
23
|
+
icon: 'grid',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
page: 'text',
|
|
27
|
+
title: 'Text and bindings',
|
|
28
|
+
summary: 'Text styles and tones, and values from state',
|
|
29
|
+
icon: 'edit',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
page: 'images',
|
|
33
|
+
title: 'Images',
|
|
34
|
+
summary: 'Pictures from statics/, their shapes and events',
|
|
35
|
+
icon: 'image',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
page: 'lists',
|
|
39
|
+
title: 'Lists and icons',
|
|
40
|
+
summary: 'Rows to tap, sheets of actions, badges and icons',
|
|
41
|
+
icon: 'list',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
page: 'forms',
|
|
45
|
+
title: 'Forms',
|
|
46
|
+
summary: 'Fields, choices, switches, and a footer to send them',
|
|
47
|
+
icon: 'check',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
page: 'feedback',
|
|
51
|
+
title: 'Feedback',
|
|
52
|
+
summary: 'Skeletons, toasts, spinners, progress and notices',
|
|
53
|
+
icon: 'bell',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: 'Logic',
|
|
59
|
+
features: [
|
|
60
|
+
{
|
|
61
|
+
page: 'state',
|
|
62
|
+
title: 'State, mutations and actions',
|
|
63
|
+
summary: 'A counter, conditions and lists',
|
|
64
|
+
icon: 'refresh',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
page: 'navigation',
|
|
68
|
+
title: 'Navigation and hooks',
|
|
69
|
+
summary: 'The page stack, and when each hook runs',
|
|
70
|
+
icon: 'arrow-right',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: 'Dash',
|
|
76
|
+
features: [
|
|
77
|
+
{
|
|
78
|
+
page: 'device',
|
|
79
|
+
title: 'Alerts and device',
|
|
80
|
+
summary: 'Native alerts, the clipboard, sharing, delays and the torch',
|
|
81
|
+
icon: 'mobile',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
page: 'network',
|
|
85
|
+
title: 'Network',
|
|
86
|
+
summary: 'Requests, JSON, downloads and uploads',
|
|
87
|
+
icon: 'wifi',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
page: 'files',
|
|
91
|
+
title: 'Files',
|
|
92
|
+
summary: "The dot's own files, kept between runs",
|
|
93
|
+
icon: 'file',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
] as Section[],
|
|
17
98
|
};
|
|
@@ -1,66 +1,20 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<Text variant="caption">Logo loaded at {{ logoSize }}</Text>
|
|
9
|
-
</If>
|
|
10
|
-
<ElseIf condition="{{ logoStatus === 'failed' }}">
|
|
11
|
-
<Text variant="caption">Logo failed to load</Text>
|
|
12
|
-
</ElseIf>
|
|
13
|
-
<Else>
|
|
14
|
-
<Text variant="caption">Loading logo…</Text>
|
|
15
|
-
</Else>
|
|
16
|
-
</View>
|
|
17
|
-
</View>
|
|
18
|
-
|
|
19
|
-
<View variant="card">
|
|
20
|
-
<Text variant="title">{{ count }} / {{ limit }}</Text>
|
|
21
|
-
<If condition="{{ count >= limit }}">
|
|
22
|
-
<Text>Limit reached</Text>
|
|
23
|
-
</If>
|
|
24
|
-
<ElseIf condition="{{ count > 0 }}">
|
|
25
|
-
<Text>Keep tapping</Text>
|
|
26
|
-
</ElseIf>
|
|
27
|
-
<Else>
|
|
28
|
-
<Text>Tap to start 🚀</Text>
|
|
29
|
-
</Else>
|
|
30
|
-
<View direction="row">
|
|
31
|
-
<Button onClick="increment" disabled="{{ count >= limit }}">Add one</Button>
|
|
32
|
-
<Button variant="secondary" onClick="reset" disabled="{{ count === 0 }}">Reset</Button>
|
|
33
|
-
<Button variant="secondary" onClick="fillUp" disabled="{{ count >= limit }}">Fill up</Button>
|
|
34
|
-
</View>
|
|
35
|
-
</View>
|
|
36
|
-
|
|
37
|
-
<View variant="card" gap="small">
|
|
38
|
-
<Text variant="caption">Name your counter</Text>
|
|
39
|
-
<Input label="Counter name" placeholder="Dot Counter" value="{{ title }}" maxLength="24" onChange="rename" />
|
|
40
|
-
</View>
|
|
41
|
-
|
|
42
|
-
<View variant="card" gap="small">
|
|
43
|
-
<Text variant="caption">Dash</Text>
|
|
44
|
-
<View direction="row">
|
|
45
|
-
<Button variant="secondary" onClick="sayHello">Say hello</Button>
|
|
46
|
-
<Button variant="secondary" onClick="vibrate">Vibrate</Button>
|
|
47
|
-
<Button variant="secondary" onClick="toggleTorch">{{ torch === 'on' ? 'Torch off' : 'Torch on' }}</Button>
|
|
1
|
+
<View>
|
|
2
|
+
<View direction="row" align="center" gap="large">
|
|
3
|
+
<Image src="statics/logo.png" alt="Dot logo" width="48" height="48" />
|
|
4
|
+
<View gap="none">
|
|
5
|
+
<Text variant="title">{{ title }}</Text>
|
|
6
|
+
<Text variant="caption" tone="muted">What a dot can do, one page each</Text>
|
|
7
|
+
</View>
|
|
48
8
|
</View>
|
|
49
|
-
<If condition="{{ dashNote }}">
|
|
50
|
-
<Text variant="caption">{{ dashNote }}</Text>
|
|
51
|
-
</If>
|
|
52
|
-
</View>
|
|
53
9
|
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
10
|
+
<For each="{{ sections }}" item="section" key="title">
|
|
11
|
+
<View gap="small">
|
|
12
|
+
<Text variant="caption" tone="muted">{{ section.title }}</Text>
|
|
13
|
+
<List>
|
|
14
|
+
<For each="{{ section.features }}" item="feature" key="page">
|
|
15
|
+
<ListItem icon="{{ feature.icon }}" title="{{ feature.title }}" subtitle="{{ feature.summary }}" data-page="{{ feature.page }}" onTap="open" />
|
|
16
|
+
</For>
|
|
17
|
+
</List>
|
|
18
|
+
</View>
|
|
19
|
+
</For>
|
|
57
20
|
</View>
|
|
58
|
-
|
|
59
|
-
<If condition="{{ taps.length > 0 }}">
|
|
60
|
-
<View variant="card" gap="small">
|
|
61
|
-
<Text variant="caption">History</Text>
|
|
62
|
-
<For each="{{ taps }}" index="i" item="tap" key="id">
|
|
63
|
-
<Text>{{ i + 1 }}. Tap #{{ tap.id }}</Text>
|
|
64
|
-
</For>
|
|
65
|
-
</View>
|
|
66
|
-
</If>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Align, Direction, Justify, Space } from './state';
|
|
2
|
+
import state from './state';
|
|
3
|
+
|
|
4
|
+
const DIRECTIONS: Direction[] = ['row', 'column'];
|
|
5
|
+
const JUSTIFIES: Justify[] = ['start', 'center', 'end', 'between'];
|
|
6
|
+
const ALIGNS: Align[] = ['stretch', 'start', 'center', 'end'];
|
|
7
|
+
const SPACES: Space[] = ['none', 'small', 'medium', 'large'];
|
|
8
|
+
|
|
9
|
+
export default definePage({
|
|
10
|
+
initialState: state,
|
|
11
|
+
mutations: {
|
|
12
|
+
nextDirection: (state) => ({ direction: after(DIRECTIONS, state.direction) }),
|
|
13
|
+
nextJustify: (state) => ({ justify: after(JUSTIFIES, state.justify) }),
|
|
14
|
+
nextAlign: (state) => ({ align: after(ALIGNS, state.align) }),
|
|
15
|
+
nextGap: (state) => ({ gap: after(SPACES, state.gap) }),
|
|
16
|
+
toggleWrap: (state) => ({ wrap: !state.wrap }),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function after<Value>(values: Value[], current: Value): Value {
|
|
21
|
+
const next = (values.indexOf(current) + 1) % values.length;
|
|
22
|
+
return values[next]!;
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Direction = 'row' | 'column';
|
|
2
|
+
export type Justify = 'start' | 'center' | 'end' | 'between';
|
|
3
|
+
export type Align = 'stretch' | 'start' | 'center' | 'end';
|
|
4
|
+
export type Space = 'none' | 'small' | 'medium' | 'large';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
direction: 'row' as Direction,
|
|
8
|
+
justify: 'start' as Justify,
|
|
9
|
+
align: 'center' as Align,
|
|
10
|
+
gap: 'small' as Space,
|
|
11
|
+
wrap: true,
|
|
12
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<View>
|
|
2
|
+
<Text variant="title">Layout</Text>
|
|
3
|
+
<Text>A View lays out what it holds in a column, or in a row. Change the one below.</Text>
|
|
4
|
+
|
|
5
|
+
<View variant="card" direction="{{ direction }}" justify="{{ justify }}" align="{{ align }}" gap="{{ gap }}" wrap="{{ wrap }}">
|
|
6
|
+
<Button variant="secondary">One</Button>
|
|
7
|
+
<Button variant="secondary">Two</Button>
|
|
8
|
+
<Text variant="title">Three</Text>
|
|
9
|
+
<Button variant="secondary">Four</Button>
|
|
10
|
+
<Text variant="caption" tone="muted">Five</Text>
|
|
11
|
+
</View>
|
|
12
|
+
|
|
13
|
+
<View gap="small">
|
|
14
|
+
<View direction="row" align="center">
|
|
15
|
+
<Text>direction="{{ direction }}"</Text>
|
|
16
|
+
<View grow />
|
|
17
|
+
<Button variant="secondary" onClick="nextDirection">Change</Button>
|
|
18
|
+
</View>
|
|
19
|
+
<View direction="row" align="center">
|
|
20
|
+
<Text>justify="{{ justify }}"</Text>
|
|
21
|
+
<View grow />
|
|
22
|
+
<Button variant="secondary" onClick="nextJustify">Change</Button>
|
|
23
|
+
</View>
|
|
24
|
+
<View direction="row" align="center">
|
|
25
|
+
<Text>align="{{ align }}"</Text>
|
|
26
|
+
<View grow />
|
|
27
|
+
<Button variant="secondary" onClick="nextAlign">Change</Button>
|
|
28
|
+
</View>
|
|
29
|
+
<View direction="row" align="center">
|
|
30
|
+
<Text>gap="{{ gap }}"</Text>
|
|
31
|
+
<View grow />
|
|
32
|
+
<Button variant="secondary" onClick="nextGap">Change</Button>
|
|
33
|
+
</View>
|
|
34
|
+
<View direction="row" align="center">
|
|
35
|
+
<Text>wrap="{{ wrap }}"</Text>
|
|
36
|
+
<View grow />
|
|
37
|
+
<Button variant="secondary" onClick="toggleWrap">Change</Button>
|
|
38
|
+
</View>
|
|
39
|
+
</View>
|
|
40
|
+
|
|
41
|
+
<Text variant="caption" tone="muted">grow takes the space that's left</Text>
|
|
42
|
+
<View direction="row" gap="small">
|
|
43
|
+
<View variant="card" grow>
|
|
44
|
+
<Text>grow</Text>
|
|
45
|
+
</View>
|
|
46
|
+
<View variant="card">
|
|
47
|
+
<Text>fixed</Text>
|
|
48
|
+
</View>
|
|
49
|
+
</View>
|
|
50
|
+
|
|
51
|
+
<Text variant="caption" tone="muted">padding, on every side or just one</Text>
|
|
52
|
+
<View direction="row" gap="small">
|
|
53
|
+
<View variant="card" padding="small">
|
|
54
|
+
<Text>small</Text>
|
|
55
|
+
</View>
|
|
56
|
+
<View variant="card" padding="large">
|
|
57
|
+
<Text>large</Text>
|
|
58
|
+
</View>
|
|
59
|
+
<View variant="card" padding="small" paddingStart="large">
|
|
60
|
+
<Text>start</Text>
|
|
61
|
+
</View>
|
|
62
|
+
</View>
|
|
63
|
+
</View>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import state from './state';
|
|
2
|
+
|
|
3
|
+
export default definePage({
|
|
4
|
+
initialState: state,
|
|
5
|
+
mutations: {
|
|
6
|
+
choose: (state, event: DotEvent<unknown, { id: string }>) => {
|
|
7
|
+
const account = state.accounts.find((candidate) => candidate.id === event.data.id);
|
|
8
|
+
return { chosen: account?.name ?? '' };
|
|
9
|
+
},
|
|
10
|
+
openPayment: (state, event: DotEvent<unknown, { id: string }>) => ({
|
|
11
|
+
selected: state.payments.find((payment) => payment.id === event.data.id) ?? null,
|
|
12
|
+
}),
|
|
13
|
+
closeSheet: () => ({ selected: null }),
|
|
14
|
+
clearToast: () => ({ toast: '' }),
|
|
15
|
+
},
|
|
16
|
+
actions: {
|
|
17
|
+
async copyReference(state) {
|
|
18
|
+
if (!state.selected) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
await this.dash.system.copyText(state.selected.reference);
|
|
22
|
+
this.setState({ selected: null, toast: 'Reference copied' });
|
|
23
|
+
},
|
|
24
|
+
async shareReceipt(state) {
|
|
25
|
+
if (!state.selected) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { title, amount, status, reference } = state.selected;
|
|
29
|
+
this.setState({ selected: null });
|
|
30
|
+
await this.dash.system.share({
|
|
31
|
+
title: `${title} receipt`,
|
|
32
|
+
text: `${title}: ${amount}, ${status}. Reference ${reference}.`,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface Account {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
number: string;
|
|
5
|
+
balance: string;
|
|
6
|
+
icon: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Payment {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
date: string;
|
|
13
|
+
amount: string;
|
|
14
|
+
status: string;
|
|
15
|
+
tone: string;
|
|
16
|
+
reference: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
accounts: [
|
|
21
|
+
{
|
|
22
|
+
id: 'savings',
|
|
23
|
+
name: 'Savings',
|
|
24
|
+
number: '•••• 2041',
|
|
25
|
+
balance: 'Le 1,250,000',
|
|
26
|
+
icon: 'bank',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'wallet',
|
|
30
|
+
name: 'Mobile wallet',
|
|
31
|
+
number: '076 •••• 21',
|
|
32
|
+
balance: 'Le 84,500',
|
|
33
|
+
icon: 'mobile',
|
|
34
|
+
},
|
|
35
|
+
{ id: 'card', name: 'Debit card', number: '•••• 7730', balance: 'Le 310,000', icon: 'card' },
|
|
36
|
+
] as Account[],
|
|
37
|
+
payments: [
|
|
38
|
+
{
|
|
39
|
+
id: 'p1',
|
|
40
|
+
title: 'Electricity',
|
|
41
|
+
date: 'Today',
|
|
42
|
+
amount: 'Le 50,000',
|
|
43
|
+
status: 'Paid',
|
|
44
|
+
tone: 'success',
|
|
45
|
+
reference: 'EDSA-58213',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'p2',
|
|
49
|
+
title: 'Airtime',
|
|
50
|
+
date: 'Yesterday',
|
|
51
|
+
amount: 'Le 10,000',
|
|
52
|
+
status: 'Pending',
|
|
53
|
+
tone: 'warning',
|
|
54
|
+
reference: 'AIR-11907',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'p3',
|
|
58
|
+
title: 'Water',
|
|
59
|
+
date: '12 September',
|
|
60
|
+
amount: 'Le 35,000',
|
|
61
|
+
status: 'Failed',
|
|
62
|
+
tone: 'danger',
|
|
63
|
+
reference: 'GVWC-40022',
|
|
64
|
+
},
|
|
65
|
+
] as Payment[],
|
|
66
|
+
chosen: '',
|
|
67
|
+
// The payment whose actions the sheet shows.
|
|
68
|
+
selected: null as Payment | null,
|
|
69
|
+
toast: '',
|
|
70
|
+
icons: [
|
|
71
|
+
'home',
|
|
72
|
+
'search',
|
|
73
|
+
'bell',
|
|
74
|
+
'user',
|
|
75
|
+
'settings',
|
|
76
|
+
'wallet',
|
|
77
|
+
'bank',
|
|
78
|
+
'card',
|
|
79
|
+
'cash',
|
|
80
|
+
'send',
|
|
81
|
+
'receipt',
|
|
82
|
+
'qr-code',
|
|
83
|
+
'bolt',
|
|
84
|
+
'water',
|
|
85
|
+
'phone',
|
|
86
|
+
'mail',
|
|
87
|
+
'calendar',
|
|
88
|
+
'clock',
|
|
89
|
+
'location',
|
|
90
|
+
'lock',
|
|
91
|
+
'share',
|
|
92
|
+
'copy',
|
|
93
|
+
'download',
|
|
94
|
+
'star',
|
|
95
|
+
],
|
|
96
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<View>
|
|
2
|
+
<Text variant="title">Lists and icons</Text>
|
|
3
|
+
<Text>A List draws rows in a card. A row shows an icon or a picture, a value or anything at its end, and a chevron when it can be tapped.</Text>
|
|
4
|
+
|
|
5
|
+
<View gap="small">
|
|
6
|
+
<Text variant="caption" tone="muted">Rows know which one was tapped from their data- attributes</Text>
|
|
7
|
+
<List>
|
|
8
|
+
<For each="{{ accounts }}" item="account" key="id">
|
|
9
|
+
<ListItem icon="{{ account.icon }}" title="{{ account.name }}" subtitle="{{ account.number }}" value="{{ account.balance }}" data-id="{{ account.id }}" onTap="choose" />
|
|
10
|
+
</For>
|
|
11
|
+
</List>
|
|
12
|
+
<If condition="{{ chosen }}">
|
|
13
|
+
<Text>You chose {{ chosen }}</Text>
|
|
14
|
+
</If>
|
|
15
|
+
</View>
|
|
16
|
+
|
|
17
|
+
<View gap="small">
|
|
18
|
+
<Text variant="caption" tone="muted">A badge at the end of each row; tap one for its actions</Text>
|
|
19
|
+
<List>
|
|
20
|
+
<For each="{{ payments }}" item="payment" key="id">
|
|
21
|
+
<ListItem title="{{ payment.title }}" subtitle="{{ payment.date }} · {{ payment.amount }}" data-id="{{ payment.id }}" onTap="openPayment">
|
|
22
|
+
<Badge tone="{{ payment.tone }}">{{ payment.status }}</Badge>
|
|
23
|
+
</ListItem>
|
|
24
|
+
</For>
|
|
25
|
+
</List>
|
|
26
|
+
</View>
|
|
27
|
+
|
|
28
|
+
<View gap="small">
|
|
29
|
+
<Text variant="caption" tone="muted">Icons, by name, in any tone</Text>
|
|
30
|
+
<View variant="card" direction="row" wrap gap="large">
|
|
31
|
+
<For each="{{ icons }}" item="icon" key="*this">
|
|
32
|
+
<Icon name="{{ icon }}" />
|
|
33
|
+
</For>
|
|
34
|
+
</View>
|
|
35
|
+
<View variant="card" direction="row" wrap gap="large" align="center">
|
|
36
|
+
<Icon name="info" tone="info" size="small" />
|
|
37
|
+
<Icon name="success" tone="success" />
|
|
38
|
+
<Icon name="warning" tone="warning" size="large" />
|
|
39
|
+
<Icon name="error" tone="danger" size="large" />
|
|
40
|
+
<Icon name="star" tone="muted" />
|
|
41
|
+
</View>
|
|
42
|
+
</View>
|
|
43
|
+
|
|
44
|
+
<View gap="small">
|
|
45
|
+
<Text variant="caption" tone="muted">A divider between things</Text>
|
|
46
|
+
<View variant="card">
|
|
47
|
+
<Text>Above the line</Text>
|
|
48
|
+
<Divider />
|
|
49
|
+
<Text>Below the line</Text>
|
|
50
|
+
</View>
|
|
51
|
+
</View>
|
|
52
|
+
|
|
53
|
+
<Sheet open="{{ selected !== null }}" title="{{ selected.title }} · {{ selected.amount }}" onClose="closeSheet">
|
|
54
|
+
<Button variant="secondary" onClick="copyReference">Copy reference {{ selected.reference }}</Button>
|
|
55
|
+
<Button variant="secondary" onClick="shareReceipt">Share receipt</Button>
|
|
56
|
+
<Button onClick="closeSheet">Cancel</Button>
|
|
57
|
+
</Sheet>
|
|
58
|
+
<Toast message="{{ toast }}" tone="success" onHide="clearToast" />
|
|
59
|
+
</View>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { HookRun } from './state';
|
|
2
|
+
import state from './state';
|
|
3
|
+
|
|
4
|
+
export default definePage({
|
|
5
|
+
initialState: state,
|
|
6
|
+
hooks: {
|
|
7
|
+
// Options come from the page that opened this one, as strings.
|
|
8
|
+
onLoad(options) {
|
|
9
|
+
const depth = Number(options.depth ?? '1') + 1;
|
|
10
|
+
const from = options.trail ?? 'Home';
|
|
11
|
+
this.setState({ depth, from, trail: `${from} › ${depth}` });
|
|
12
|
+
this.setState((current) => ({ hooks: ran(current.hooks, 'onLoad') }));
|
|
13
|
+
},
|
|
14
|
+
onShow() {
|
|
15
|
+
this.setState((current) => ({ hooks: ran(current.hooks, 'onShow') }));
|
|
16
|
+
},
|
|
17
|
+
onHide() {
|
|
18
|
+
this.setState((current) => ({ hooks: ran(current.hooks, 'onHide') }));
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
actions: {
|
|
22
|
+
openAnother(state) {
|
|
23
|
+
this.navigation.push('navigation', { depth: String(state.depth), trail: state.trail });
|
|
24
|
+
},
|
|
25
|
+
replaceThis(state) {
|
|
26
|
+
this.navigation.replace('navigation', { depth: String(state.depth - 1), trail: state.from });
|
|
27
|
+
},
|
|
28
|
+
back() {
|
|
29
|
+
this.navigation.back();
|
|
30
|
+
},
|
|
31
|
+
backTwo() {
|
|
32
|
+
this.navigation.back(2);
|
|
33
|
+
},
|
|
34
|
+
home() {
|
|
35
|
+
this.navigation.reset('index');
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function ran(hooks: readonly HookRun[], hook: string): HookRun[] {
|
|
41
|
+
return [...hooks, { id: hooks.length + 1, hook }];
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<View>
|
|
2
|
+
<Text variant="caption" tone="muted">{{ trail }}</Text>
|
|
3
|
+
<Text variant="title">Navigation and hooks</Text>
|
|
4
|
+
<Text>Each page opens on top of the one before it; back returns to the one below. A dot has room for {{ maxDepth }} pages at once.</Text>
|
|
5
|
+
|
|
6
|
+
<View variant="card" gap="small">
|
|
7
|
+
<Text>This is page {{ depth }} of {{ maxDepth }}</Text>
|
|
8
|
+
<View direction="row" wrap gap="small">
|
|
9
|
+
<Button onClick="openAnother" disabled="{{ depth >= maxDepth }}">Open another</Button>
|
|
10
|
+
<Button variant="secondary" onClick="replaceThis">Replace this one</Button>
|
|
11
|
+
<Button variant="secondary" onClick="back">Back</Button>
|
|
12
|
+
<Button variant="secondary" onClick="backTwo" disabled="{{ depth < 3 }}">Back two</Button>
|
|
13
|
+
<Button variant="secondary" onClick="home">Start over</Button>
|
|
14
|
+
</View>
|
|
15
|
+
</View>
|
|
16
|
+
|
|
17
|
+
<Text variant="caption" tone="muted">The hooks this page ran, in order</Text>
|
|
18
|
+
<View variant="card" gap="small">
|
|
19
|
+
<For each="{{ hooks }}" item="run" key="id">
|
|
20
|
+
<Text>{{ run.id }}. {{ run.hook }}</Text>
|
|
21
|
+
</For>
|
|
22
|
+
<Text variant="caption" tone="muted">Open another page and come back: this one runs onHide, then onShow.</Text>
|
|
23
|
+
</View>
|
|
24
|
+
</View>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import state from './state';
|
|
2
|
+
|
|
3
|
+
const PICTURE = 'pictures/octocat.png';
|
|
4
|
+
|
|
5
|
+
interface GitHubUser {
|
|
6
|
+
login: string;
|
|
7
|
+
name: string | null;
|
|
8
|
+
followers: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Echo {
|
|
12
|
+
json: unknown;
|
|
13
|
+
files: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default definePage({
|
|
17
|
+
initialState: state,
|
|
18
|
+
actions: {
|
|
19
|
+
async fetchZen() {
|
|
20
|
+
try {
|
|
21
|
+
const response = await this.dash.network.http.request({
|
|
22
|
+
url: 'https://api.github.com/zen',
|
|
23
|
+
});
|
|
24
|
+
this.setState({ zen: response.ok ? response.body : `GitHub answered ${response.status}` });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
this.setState({ note: (error as Error).message });
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async fetchProfile() {
|
|
30
|
+
try {
|
|
31
|
+
const { body } = await this.dash.network.http.request<GitHubUser>(
|
|
32
|
+
{ url: 'https://api.github.com/users/octocat', as: 'json' },
|
|
33
|
+
{ timeout: 10_000 },
|
|
34
|
+
);
|
|
35
|
+
this.setState({
|
|
36
|
+
profile: { login: body.login, name: body.name ?? body.login, followers: body.followers },
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
this.setState({ note: (error as Error).message });
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
async sendJson() {
|
|
43
|
+
try {
|
|
44
|
+
const { body } = await this.dash.network.http.request<Echo>({
|
|
45
|
+
url: 'https://httpbin.org/post',
|
|
46
|
+
method: 'POST',
|
|
47
|
+
body: { item: 'rice', count: 2 },
|
|
48
|
+
as: 'json',
|
|
49
|
+
});
|
|
50
|
+
this.setState({ echo: JSON.stringify(body.json) });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
this.setState({ note: (error as Error).message });
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async download() {
|
|
56
|
+
try {
|
|
57
|
+
const response = await this.dash.network.http.download({
|
|
58
|
+
url: 'https://avatars.githubusercontent.com/u/583231?s=128',
|
|
59
|
+
name: 'octocat.png',
|
|
60
|
+
});
|
|
61
|
+
if (response.ok) {
|
|
62
|
+
const { id, url, size } = response.file;
|
|
63
|
+
this.setState({ picture: { id, url, size }, kept: false });
|
|
64
|
+
} else {
|
|
65
|
+
this.setState({ note: `GitHub answered ${response.status}` });
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.setState({ note: (error as Error).message });
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
async upload(state) {
|
|
72
|
+
try {
|
|
73
|
+
const { body } = await this.dash.network.http.upload<Echo>({
|
|
74
|
+
url: 'https://httpbin.org/post',
|
|
75
|
+
fields: { title: 'Octocat' },
|
|
76
|
+
files: [{ field: 'picture', file: state.picture.id }],
|
|
77
|
+
as: 'json',
|
|
78
|
+
});
|
|
79
|
+
this.setState({ uploaded: Object.keys(body.files).join(', ') });
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.setState({ note: (error as Error).message });
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
async sharePicture(state) {
|
|
85
|
+
try {
|
|
86
|
+
await this.dash.system.share({ file: state.picture.id });
|
|
87
|
+
} catch (error) {
|
|
88
|
+
this.setState({ note: (error as Error).message });
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
async keep(state) {
|
|
92
|
+
try {
|
|
93
|
+
await this.dash.file.save(state.picture.id, PICTURE, { overwrite: true });
|
|
94
|
+
this.setState({ kept: true });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
this.setState({ note: (error as Error).message });
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
async tryUnlistedHost() {
|
|
100
|
+
try {
|
|
101
|
+
await this.dash.network.http.request({ url: 'https://example.com' });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
this.setState({ note: (error as Error).message });
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
});
|