@monimesl/dot-cli 0.1.0-alpha.9 → 1.0.0-beta.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.
Files changed (143) hide show
  1. package/README.md +72 -59
  2. package/dist/cli.js +530 -339
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-BfPAnsSf.js → server-DMnWDdJ6.js} +570 -478
  5. package/dist/server-DMnWDdJ6.js.map +1 -0
  6. package/dist/templates/showcase/dot.ts +53 -16
  7. package/dist/templates/showcase/pages/basics/bars/controller.ts +47 -0
  8. package/dist/templates/showcase/pages/basics/bars/state.ts +7 -0
  9. package/dist/templates/showcase/pages/basics/bars/view.dml +36 -0
  10. package/dist/templates/showcase/pages/{text → basics/bindings}/controller.ts +5 -0
  11. package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
  12. package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
  13. package/dist/templates/showcase/pages/basics/colors/controller.ts +112 -0
  14. package/dist/templates/showcase/pages/basics/colors/palette.ts +52 -0
  15. package/dist/templates/showcase/pages/basics/colors/state.ts +17 -0
  16. package/dist/templates/showcase/pages/basics/colors/view.dml +142 -0
  17. package/dist/templates/showcase/pages/basics/concert/controller.ts +16 -0
  18. package/dist/templates/showcase/pages/basics/concert/state.ts +7 -0
  19. package/dist/templates/showcase/pages/basics/concert/view.dml +20 -0
  20. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +18 -0
  21. package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
  22. package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
  23. package/dist/templates/showcase/pages/basics/events/controller.ts +43 -0
  24. package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
  25. package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
  26. package/dist/templates/showcase/pages/basics/hooks/controller.ts +43 -0
  27. package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
  28. package/dist/templates/showcase/pages/basics/hooks/view.dml +37 -0
  29. package/dist/templates/showcase/pages/basics/navigation/controller.ts +41 -0
  30. package/dist/templates/showcase/pages/basics/navigation/state.ts +11 -0
  31. package/dist/templates/showcase/pages/basics/navigation/view.dml +48 -0
  32. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +18 -0
  33. package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
  34. package/dist/templates/showcase/pages/basics/pick-genre/view.dml +10 -0
  35. package/dist/templates/showcase/pages/basics/share/concerts.ts +31 -0
  36. package/dist/templates/showcase/pages/basics/share/controller.ts +12 -0
  37. package/dist/templates/showcase/pages/basics/share/state.ts +5 -0
  38. package/dist/templates/showcase/pages/basics/share/view.dml +21 -0
  39. package/dist/templates/showcase/pages/{state → basics/state}/controller.ts +1 -0
  40. package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
  41. package/dist/templates/showcase/pages/{carousel → components/carousel}/controller.ts +2 -1
  42. package/dist/templates/showcase/pages/{carousel → components/carousel}/state.ts +5 -5
  43. package/dist/templates/showcase/pages/{carousel → components/carousel}/view.dml +7 -5
  44. package/dist/templates/showcase/pages/{feedback → components/feedback}/controller.ts +13 -2
  45. package/dist/templates/showcase/pages/components/feedback/state.ts +41 -0
  46. package/dist/templates/showcase/pages/{feedback → components/feedback}/view.dml +38 -9
  47. package/dist/templates/showcase/pages/{forms → components/forms}/controller.ts +23 -19
  48. package/dist/templates/showcase/pages/{forms → components/forms}/state.ts +6 -3
  49. package/dist/templates/showcase/pages/components/forms/view.dml +97 -0
  50. package/dist/templates/showcase/pages/components/icons/controller.ts +15 -0
  51. package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
  52. package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
  53. package/dist/templates/showcase/pages/{images → components/images}/controller.ts +4 -1
  54. package/dist/templates/showcase/pages/components/images/view.dml +83 -0
  55. package/dist/templates/showcase/pages/{layout → components/layout}/controller.ts +3 -2
  56. package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
  57. package/dist/templates/showcase/pages/components/lists/controller.ts +18 -0
  58. package/dist/templates/showcase/pages/{lists → components/lists}/state.ts +43 -30
  59. package/dist/templates/showcase/pages/components/lists/view.dml +88 -0
  60. package/dist/templates/showcase/pages/{payments → components/money}/controller.ts +7 -8
  61. package/dist/templates/showcase/pages/components/money/view.dml +54 -0
  62. package/dist/templates/showcase/pages/components/reading/controller.ts +6 -0
  63. package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
  64. package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
  65. package/dist/templates/showcase/pages/{lists → components/sheets}/controller.ts +10 -11
  66. package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
  67. package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
  68. package/dist/templates/showcase/pages/components/styles/controller.ts +6 -0
  69. package/dist/templates/showcase/pages/components/styles/state.ts +3 -0
  70. package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
  71. package/dist/templates/showcase/pages/components/tabs/controller.ts +41 -0
  72. package/dist/templates/showcase/pages/{filters → components/tabs}/state.ts +32 -23
  73. package/dist/templates/showcase/pages/components/tabs/view.dml +57 -0
  74. package/dist/templates/showcase/pages/components/text/controller.ts +10 -0
  75. package/dist/templates/showcase/pages/components/text/state.ts +3 -0
  76. package/dist/templates/showcase/pages/components/text/view.dml +95 -0
  77. package/dist/templates/showcase/pages/dash/account/controller.ts +32 -0
  78. package/dist/templates/showcase/pages/dash/account/state.ts +9 -0
  79. package/dist/templates/showcase/pages/dash/account/view.dml +33 -0
  80. package/dist/templates/showcase/pages/{cache → dash/cache}/controller.ts +9 -4
  81. package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
  82. package/dist/templates/showcase/pages/dash/calls/controller.ts +67 -0
  83. package/dist/templates/showcase/pages/dash/calls/state.ts +9 -0
  84. package/dist/templates/showcase/pages/dash/calls/view.dml +69 -0
  85. package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
  86. package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
  87. package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
  88. package/dist/templates/showcase/pages/dash/crypto/controller.ts +38 -0
  89. package/dist/templates/showcase/pages/dash/crypto/state.ts +10 -0
  90. package/dist/templates/showcase/pages/dash/crypto/view.dml +48 -0
  91. package/dist/templates/showcase/pages/{files → dash/files}/controller.ts +7 -2
  92. package/dist/templates/showcase/pages/{files → dash/files}/state.ts +4 -0
  93. package/dist/templates/showcase/pages/dash/files/view.dml +78 -0
  94. package/dist/templates/showcase/pages/dash/media/controller.ts +65 -0
  95. package/dist/templates/showcase/pages/dash/media/state.ts +12 -0
  96. package/dist/templates/showcase/pages/dash/media/view.dml +53 -0
  97. package/dist/templates/showcase/pages/{network → dash/network}/controller.ts +84 -20
  98. package/dist/templates/showcase/pages/{network → dash/network}/state.ts +14 -1
  99. package/dist/templates/showcase/pages/dash/network/view.dml +108 -0
  100. package/dist/templates/showcase/pages/dash/payments/cases.ts +445 -0
  101. package/dist/templates/showcase/pages/dash/payments/controller.ts +149 -0
  102. package/dist/templates/showcase/pages/dash/payments/state.ts +33 -0
  103. package/dist/templates/showcase/pages/dash/payments/view.dml +71 -0
  104. package/dist/templates/showcase/pages/{secrets → dash/secrets}/controller.ts +2 -1
  105. package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
  106. package/dist/templates/showcase/pages/dash/system/controller.ts +89 -0
  107. package/dist/templates/showcase/pages/dash/system/state.ts +13 -0
  108. package/dist/templates/showcase/pages/dash/system/view.dml +96 -0
  109. package/dist/templates/showcase/pages/dash/ui/controller.ts +70 -0
  110. package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
  111. package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
  112. package/dist/templates/showcase/pages/index/controller.ts +1 -1
  113. package/dist/templates/showcase/pages/index/state.ts +175 -60
  114. package/dist/templates/showcase/pages/index/view.dml +6 -3
  115. package/package.json +6 -6
  116. package/dist/server-BfPAnsSf.js.map +0 -1
  117. package/dist/templates/showcase/pages/cache/view.dml +0 -36
  118. package/dist/templates/showcase/pages/device/controller.ts +0 -80
  119. package/dist/templates/showcase/pages/device/state.ts +0 -7
  120. package/dist/templates/showcase/pages/device/view.dml +0 -49
  121. package/dist/templates/showcase/pages/feedback/state.ts +0 -8
  122. package/dist/templates/showcase/pages/files/view.dml +0 -50
  123. package/dist/templates/showcase/pages/filters/controller.ts +0 -27
  124. package/dist/templates/showcase/pages/filters/view.dml +0 -44
  125. package/dist/templates/showcase/pages/forms/view.dml +0 -49
  126. package/dist/templates/showcase/pages/images/view.dml +0 -53
  127. package/dist/templates/showcase/pages/layout/view.dml +0 -103
  128. package/dist/templates/showcase/pages/lists/view.dml +0 -59
  129. package/dist/templates/showcase/pages/navigation/controller.ts +0 -65
  130. package/dist/templates/showcase/pages/navigation/state.ts +0 -17
  131. package/dist/templates/showcase/pages/navigation/view.dml +0 -34
  132. package/dist/templates/showcase/pages/network/view.dml +0 -67
  133. package/dist/templates/showcase/pages/payments/view.dml +0 -45
  134. package/dist/templates/showcase/pages/secrets/view.dml +0 -24
  135. package/dist/templates/showcase/pages/state/view.dml +0 -33
  136. package/dist/templates/showcase/pages/text/state.ts +0 -8
  137. package/dist/templates/showcase/pages/text/view.dml +0 -58
  138. /package/dist/templates/showcase/pages/{state → basics/state}/state.ts +0 -0
  139. /package/dist/templates/showcase/pages/{images → components/images}/state.ts +0 -0
  140. /package/dist/templates/showcase/pages/{layout → components/layout}/state.ts +0 -0
  141. /package/dist/templates/showcase/pages/{payments → components/money}/state.ts +0 -0
  142. /package/dist/templates/showcase/pages/{cache → dash/cache}/state.ts +0 -0
  143. /package/dist/templates/showcase/pages/{secrets → dash/secrets}/state.ts +0 -0
@@ -1,7 +1,8 @@
1
- import state from './state';
1
+ import state, { paymentSteps } from './state';
2
2
 
3
3
  export default definePage({
4
4
  initialState: state,
5
+ configs: { navigationBar: { title: 'Feedback' } },
5
6
  mutations: {
6
7
  advance: (state) => ({ progress: Math.min(100, state.progress + 20) }),
7
8
  restart: () => ({ progress: 0 }),
@@ -10,12 +11,22 @@ export default definePage({
10
11
  // A toast lasts its default time unless the button gives one, in milliseconds.
11
12
  showToast: (
12
13
  _state,
13
- event: DotEvent<unknown, { message: string; tone: string; duration?: string }>,
14
+ event: DataEvent<{ message: string; tone: string; duration?: string }>,
14
15
  ) => ({
15
16
  toast: event.data.message,
16
17
  toastTone: event.data.tone,
17
18
  toastDuration: event.data.duration ? Number(event.data.duration) : undefined,
18
19
  }),
19
20
  clearToast: () => ({ toast: '' }),
21
+ hideTip: () => ({ tipShown: false }),
22
+ advancePayment: (state) => {
23
+ const paymentStage = state.paymentStage + 1;
24
+ return { paymentStage, payment: paymentSteps(paymentStage) };
25
+ },
26
+ failPayment: (state) => ({
27
+ paymentFailed: true,
28
+ payment: paymentSteps(state.paymentStage, true),
29
+ }),
30
+ restartPayment: () => ({ paymentStage: 0, paymentFailed: false, payment: paymentSteps(0) }),
20
31
  },
21
32
  });
@@ -0,0 +1,41 @@
1
+ export type StepStatus = 'done' | 'current' | 'upcoming' | 'failed';
2
+
3
+ export interface PaymentStep {
4
+ title: string;
5
+ text: string;
6
+ status: StepStatus;
7
+ }
8
+
9
+ // Sending Le 500 to Fatmata Conteh in Bo.
10
+ const PAYMENT = [
11
+ { title: 'Paid', text: 'Le 500 from your wallet' },
12
+ { title: 'Sent', text: 'On its way to Bo' },
13
+ { title: 'Received', text: 'By Fatmata Conteh' },
14
+ ];
15
+
16
+ // The payment's steps when `stage` of them are done. The next one is under way, or failed.
17
+ export function paymentSteps(stage: number, failed = false): PaymentStep[] {
18
+ return PAYMENT.map((step, index) => {
19
+ let status: StepStatus = 'upcoming';
20
+ if (index < stage) {
21
+ status = 'done';
22
+ } else if (index === stage) {
23
+ status = failed ? 'failed' : 'current';
24
+ }
25
+ return { ...step, status };
26
+ });
27
+ }
28
+
29
+ export default {
30
+ progress: 40,
31
+ busy: false,
32
+ loaded: false,
33
+ toast: '',
34
+ toastTone: 'default',
35
+ toastDuration: undefined as number | undefined,
36
+ tipShown: true,
37
+ // How many steps are done, and whether the next one failed, which ends the payment.
38
+ paymentStage: 0,
39
+ paymentFailed: false,
40
+ payment: paymentSteps(0),
41
+ };
@@ -3,7 +3,8 @@
3
3
  <Text>Show that something is loading, how far it has gone, and how it ended.</Text>
4
4
 
5
5
  <View gap="small">
6
- <Text variant="caption" tone="muted">Spinners, while something loads</Text>
6
+ <Text variant="subtitle">Spinner</Text>
7
+ <Text variant="caption" tone="muted">While something loads, in three sizes.</Text>
7
8
  <View variant="card" direction="row" gap="large" align="center">
8
9
  <Spinner size="small" />
9
10
  <Spinner />
@@ -12,7 +13,8 @@
12
13
  </View>
13
14
 
14
15
  <View gap="small">
15
- <Text variant="caption" tone="muted">Skeletons show the shape of what is loading</Text>
16
+ <Text variant="subtitle">Skeleton</Text>
17
+ <Text variant="caption" tone="muted">Shows the shape of what is loading: lines of text, a circle or a box.</Text>
16
18
  <View variant="card">
17
19
  <If condition="{{ loaded }}">
18
20
  <View direction="row" align="center" gap="large">
@@ -27,7 +29,7 @@
27
29
  <Else>
28
30
  <View direction="row" align="center" gap="large">
29
31
  <Skeleton shape="circle" />
30
- <Skeleton lines="2" />
32
+ <Skeleton shape="text" lines="2" />
31
33
  </View>
32
34
  <Skeleton shape="box" height="80" />
33
35
  </Else>
@@ -36,15 +38,19 @@
36
38
  </View>
37
39
 
38
40
  <View gap="small">
39
- <Text variant="caption" tone="muted">Toasts disappear on their own. The offline one stays for 6 seconds.</Text>
40
- <View direction="row" gap="small">
41
+ <Text variant="subtitle">Toast</Text>
42
+ <Text variant="caption" tone="muted">A short message over the page that goes on its own. The offline one stays for 6 seconds.</Text>
43
+ <View direction="row" wrap gap="small">
41
44
  <Button variant="secondary" data-message="Added to your library" data-tone="success" onClick="showToast">Add a song</Button>
42
45
  <Button variant="secondary" data-message="You're offline. Only downloads will play." data-tone="warning" data-duration="6000" onClick="showToast">Go offline</Button>
46
+ <Button variant="secondary" data-message="A new song from Boii" data-tone="info" onClick="showToast">New song</Button>
47
+ <Button variant="secondary" data-message="That song can't play here" data-tone="danger" onClick="showToast">Fail</Button>
43
48
  </View>
44
49
  </View>
45
50
 
46
51
  <View gap="small">
47
- <Text variant="caption" tone="muted">A progress bar for an album download, from 0 to 100</Text>
52
+ <Text variant="subtitle">ProgressBar</Text>
53
+ <Text variant="caption" tone="muted">How far something has gone, from 0 to 100, such as an album download.</Text>
48
54
  <View variant="card">
49
55
  <ProgressBar value="{{ progress }}" />
50
56
  <Text>{{ progress }}% downloaded</Text>
@@ -56,7 +62,8 @@
56
62
  </View>
57
63
 
58
64
  <View gap="small">
59
- <Text variant="caption" tone="muted">A busy button ignores taps</Text>
65
+ <Text variant="subtitle">A busy Button</Text>
66
+ <Text variant="caption" tone="muted">loading shows a spinner, and the button ignores taps.</Text>
60
67
  <View variant="card">
61
68
  <Button loading="{{ busy }}">{{ busy ? 'Paying…' : 'Pay Le 300 for two tickets' }}</Button>
62
69
  <Switch label="Busy" checked="{{ busy }}" onChange="toggleBusy" />
@@ -64,7 +71,11 @@
64
71
  </View>
65
72
 
66
73
  <View gap="small">
67
- <Text variant="caption" tone="muted">Notices sit inside the page</Text>
74
+ <Text variant="subtitle">Notice</Text>
75
+ <Text variant="caption" tone="muted">A message that stays in the page, in a tone. With onClose it shows a close button, and the page hides it.</Text>
76
+ <If condition="{{ tipShown }}">
77
+ <Notice tone="info" title="Tip" onClose="hideTip">Hold a song to add it to a playlist.</Notice>
78
+ </If>
68
79
  <Notice title="Heads up">Doors open at 7 pm, and the music starts at 8.</Notice>
69
80
  <Notice tone="info" title="New">Tickets for gospel night in Kenema are on sale.</Notice>
70
81
  <Notice tone="success" title="Booked">Two tickets for Salone Sounds Live.</Notice>
@@ -73,7 +84,25 @@
73
84
  </View>
74
85
 
75
86
  <View gap="small">
76
- <Text variant="caption" tone="muted">Badges show a status</Text>
87
+ <Text variant="subtitle">Steps</Text>
88
+ <Text variant="caption" tone="muted">What happened and what comes next, such as a payment's progress. Each Step is done, current, upcoming or failed.</Text>
89
+ <View variant="card">
90
+ <Steps>
91
+ <For each="{{ payment }}" item="step" key="title">
92
+ <Step title="{{ step.title }}" text="{{ step.text }}" status="{{ step.status }}" />
93
+ </For>
94
+ </Steps>
95
+ <View direction="row" gap="small">
96
+ <Button variant="secondary" onClick="advancePayment" disabled="{{ paymentFailed || paymentStage === payment.length }}">Next step</Button>
97
+ <Button variant="secondary" onClick="failPayment" disabled="{{ paymentFailed || paymentStage === payment.length }}">Fail it</Button>
98
+ <Button variant="secondary" onClick="restartPayment" disabled="{{ paymentStage === 0 }}">Start over</Button>
99
+ </View>
100
+ </View>
101
+ </View>
102
+
103
+ <View gap="small">
104
+ <Text variant="subtitle">Badge</Text>
105
+ <Text variant="caption" tone="muted">A short status.</Text>
77
106
  <View direction="row" wrap gap="small">
78
107
  <Badge>Demo</Badge>
79
108
  <Badge tone="info">New release</Badge>
@@ -1,45 +1,47 @@
1
+ import { errorMessage } from '@monimesl/dot-runtime';
2
+
1
3
  import type { TicketType } from './state';
2
4
  import state, { SHOWS, TICKETS } from './state';
3
5
 
4
- type Typed = DotEvent<{ value: string }>;
5
-
6
- // "2" tickets of a Le 1,200 type -> "Le 2,400"
7
- function total(tickets: string, ticket: TicketType): string {
8
- const leones = (Number(tickets) || 0) * TICKETS[ticket].price;
6
+ // 2 tickets of a Le 1,200 type -> "Le 2,400"
7
+ function total(tickets: number, ticket: TicketType): string {
8
+ const leones = tickets * TICKETS[ticket].price;
9
9
  return `Le ${leones.toLocaleString('en-US')}`;
10
10
  }
11
- type Toggled = DotEvent<{ checked: boolean }>;
12
11
 
13
12
  export default definePage({
14
13
  initialState: state,
14
+ configs: { navigationBar: { title: 'Forms' } },
15
15
  mutations: {
16
- setShow: (_state, event: Typed) => ({
16
+ setShow: (_state, event: ValueEvent) => ({
17
17
  show: event.detail.value,
18
18
  showName: SHOWS[event.detail.value] ?? 'Not chosen yet',
19
19
  }),
20
- setName: (_state, event: Typed) => ({ name: event.detail.value }),
21
- setEmail: (_state, event: Typed) => ({ email: event.detail.value }),
22
- setPhone: (_state, event: Typed) => ({ phone: event.detail.value }),
23
- setTickets: (state, event: Typed) => ({
20
+ setName: (_state, event: ValueEvent) => ({ name: event.detail.value }),
21
+ setEmail: (_state, event: ValueEvent) => ({ email: event.detail.value }),
22
+ setPhone: (_state, event: ValueEvent) => ({ phone: event.detail.value }),
23
+ setTickets: (state, event: ValueEvent<number>) => ({
24
24
  tickets: event.detail.value,
25
25
  total: total(event.detail.value, state.ticket),
26
26
  }),
27
- setShoutOut: (_state, event: Typed) => ({ shoutOut: event.detail.value }),
28
- setTicket: (state, event: DotEvent<{ value: TicketType }>) => ({
27
+ setShoutOut: (_state, event: ValueEvent) => ({ shoutOut: event.detail.value }),
28
+ setPromo: (_state, event: ValueEvent) => ({ promo: event.detail.value }),
29
+ setChairs: (_state, event: ValueEvent<number>) => ({ chairs: event.detail.value }),
30
+ setTip: (_state, event: ValueEvent<number>) => ({ tip: event.detail.value }),
31
+ setTicket: (state, event: ValueEvent<TicketType>) => ({
29
32
  ticket: event.detail.value,
30
33
  ticketName: TICKETS[event.detail.value].name,
31
34
  total: total(state.tickets, event.detail.value),
32
35
  }),
33
- setETickets: (_state, event: Toggled) => ({ eTickets: event.detail.checked }),
34
- setAgreed: (_state, event: Toggled) => ({ agreed: event.detail.checked }),
36
+ setETickets: (_state, event: CheckedEvent) => ({ eTickets: event.detail.checked }),
37
+ setAgreed: (_state, event: CheckedEvent) => ({ agreed: event.detail.checked }),
35
38
  },
36
39
  actions: {
37
40
  async send(state) {
38
- const count = Number(state.tickets);
41
+ const count = state.tickets;
39
42
  const errors = {
40
43
  show: state.show ? '' : 'Choose a show',
41
44
  phone: state.phone.length < 8 ? 'Enter a phone number, such as 76 123456' : '',
42
- tickets: count >= 1 && count <= 10 ? '' : 'Buy from 1 to 10 tickets',
43
45
  shoutOut: state.shoutOut.includes('http')
44
46
  ? 'Links are not read out. Write words only.'
45
47
  : '',
@@ -60,9 +62,11 @@ export default definePage({
60
62
  sent: `${count} ${count === 1 ? 'ticket' : 'tickets'}, sent to +232 ${phone}`,
61
63
  });
62
64
  } catch (error) {
63
- this.setState({ failure: (error as Error).message });
65
+ this.setState({ failure: errorMessage(error) });
66
+ } finally {
67
+ // However the request ends, the button stops spinning and takes taps again.
68
+ this.setState({ sending: false });
64
69
  }
65
- this.setState({ sending: false });
66
70
  },
67
71
  },
68
72
  });
@@ -18,16 +18,19 @@ export default {
18
18
  // The order, as the summary shows it.
19
19
  showName: 'Not chosen yet',
20
20
  ticketName: TICKETS.regular.name,
21
- total: 'Le 0',
21
+ total: `Le ${2 * TICKETS.regular.price}`,
22
22
  name: '',
23
23
  email: '',
24
24
  phone: '',
25
- tickets: '',
25
+ tickets: 2,
26
+ promo: '',
27
+ chairs: 2,
28
+ tip: 20,
26
29
  shoutOut: '',
27
30
  ticket: 'regular' as TicketType,
28
31
  eTickets: true,
29
32
  agreed: false,
30
- errors: { show: '', phone: '', tickets: '', shoutOut: '' },
33
+ errors: { show: '', phone: '', shoutOut: '' },
31
34
  sending: false,
32
35
  sent: '',
33
36
  failure: '',
@@ -0,0 +1,97 @@
1
+ <View>
2
+ <Text variant="title">Forms</Text>
3
+ <Text>Buy tickets for a show. Each field sends onChange as you use it, and the page keeps its value in state. The page checks the form when you send it.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">Select</Text>
7
+ <View variant="card">
8
+ <Select label="Show" placeholder="Choose a show" hint="Every show has the same prices" value="{{ show }}" error="{{ errors.show }}" disabled="{{ sending }}" onChange="setShow">
9
+ <Option value="freetown" label="Salone Sounds Live · Freetown" />
10
+ <Option value="bo" label="Palm-wine evening · Bo" />
11
+ <Option value="kenema" label="Gospel night · Kenema" />
12
+ </Select>
13
+ </View>
14
+ </View>
15
+
16
+ <View gap="small">
17
+ <Text variant="subtitle">Input</Text>
18
+ <Text variant="caption" tone="muted">type sets the keyboard: text, email, phone, number or password. prefix and suffix sit inside the field. error shows under it.</Text>
19
+ <View variant="card">
20
+ <Input label="Name" placeholder="Mariama Kamara" value="{{ name }}" maxLength="40" onChange="setName" />
21
+ <Input label="Email" type="email" placeholder="mariama@example.com" hint="{{ eTickets ? 'For the e-tickets' : 'Turn on e-tickets below to use it' }}" value="{{ email }}" disabled="{{ !eTickets }}" onChange="setEmail" />
22
+ <Input label="Phone" type="phone" prefix="+232" placeholder="76 123456" value="{{ phone }}" error="{{ errors.phone }}" onChange="setPhone" />
23
+ <Input label="Promo code" type="password" placeholder="Hidden as you type" value="{{ promo }}" onChange="setPromo" onSubmit="send" />
24
+ </View>
25
+ </View>
26
+
27
+ <View gap="small">
28
+ <Text variant="subtitle">Stepper</Text>
29
+ <Text variant="caption" tone="muted">A count changed one at a time. min and max keep it in range, so there's nothing to check.</Text>
30
+ <View variant="card">
31
+ <Stepper label="Tickets" min="1" max="10" hint="At most 10 for each person" value="{{ tickets }}" onChange="setTickets" />
32
+ </View>
33
+ </View>
34
+
35
+ <View gap="small">
36
+ <Text variant="subtitle">RadioGroup and Radio</Text>
37
+ <Text variant="caption" tone="muted">One choice from a few. A Radio, or the whole group, can be disabled.</Text>
38
+ <View variant="card">
39
+ <RadioGroup label="Ticket" value="{{ ticket }}" disabled="{{ sending }}" onChange="setTicket">
40
+ <Radio value="regular" label="Regular · Le 150" />
41
+ <Radio value="vip" label="VIP, near the stage · Le 400" />
42
+ <Radio value="table" label="Table for four · Le 1,200" />
43
+ <Radio value="backstage" label="Backstage · sold out" disabled />
44
+ </RadioGroup>
45
+ </View>
46
+ </View>
47
+
48
+ <View gap="small">
49
+ <Text variant="subtitle">TextArea</Text>
50
+ <View variant="card">
51
+ <TextArea label="Shout-out" placeholder="A message for the band, read out on the night" rows="3" maxLength="140" hint="{{ ticket === 'regular' ? 'Shout-outs come with VIP and table tickets' : shoutOut.length + ' of 140' }}" value="{{ shoutOut }}" error="{{ errors.shoutOut }}" disabled="{{ ticket === 'regular' }}" onChange="setShoutOut" />
52
+ </View>
53
+ </View>
54
+
55
+ <View gap="small">
56
+ <Text variant="subtitle">Slider</Text>
57
+ <Text variant="caption" tone="muted">A number between min and max, in steps. showValue shows it beside the label.</Text>
58
+ <View variant="card">
59
+ <Slider label="Chairs near the stage" min="0" max="10" showValue value="{{ chairs }}" disabled="{{ ticket === 'regular' }}" hint="{{ ticket === 'regular' ? 'Chairs come with VIP and table tickets' : '' }}" onChange="setChairs" />
60
+ <Slider label="Tip for the band, in leones" min="0" max="100" step="10" showValue value="{{ tip }}" onChange="setTip" />
61
+ </View>
62
+ </View>
63
+
64
+ <View gap="small">
65
+ <Text variant="subtitle">Switch and Checkbox</Text>
66
+ <View variant="card">
67
+ <Switch label="Email me the e-tickets" checked="{{ eTickets }}" onChange="setETickets" />
68
+ <Switch label="Text me on the night" disabled />
69
+ <Checkbox label="I agree to the terms" checked="{{ agreed }}" onChange="setAgreed" />
70
+ <Checkbox label="I'm over 18" checked disabled />
71
+ </View>
72
+ </View>
73
+
74
+ <View gap="small">
75
+ <Text variant="subtitle">Your order</Text>
76
+ <View variant="card" gap="none">
77
+ <DetailRow label="Show" value="{{ showName }}" />
78
+ <DetailRow label="Ticket" value="{{ ticketName }}" />
79
+ <DetailRow label="How many" value="{{ tickets }}" />
80
+ <Divider />
81
+ <DetailRow label="Total" value="{{ total }}" weight="bold" paddingTop="medium" />
82
+ <DetailRow label="Paid" value="{{ sent ? 'Yes' : 'Not yet' }}" tone="{{ sent ? 'success' : 'warning' }}" />
83
+ </View>
84
+ </View>
85
+
86
+ <If condition="{{ sent }}">
87
+ <Notice tone="success" title="Booked">{{ sent }}</Notice>
88
+ </If>
89
+ <If condition="{{ failure }}">
90
+ <Notice tone="danger" title="Not booked">{{ failure }}</Notice>
91
+ </If>
92
+
93
+ <Text variant="caption" tone="muted">A Footer keeps its buttons at the bottom of the screen.</Text>
94
+ <Footer>
95
+ <Button onClick="send" loading="{{ sending }}" disabled="{{ !agreed }}">Buy tickets</Button>
96
+ </Footer>
97
+ </View>
@@ -0,0 +1,15 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ configs: { navigationBar: { title: 'Icons' } },
6
+ mutations: {
7
+ clearToast: () => ({ toast: '' }),
8
+ },
9
+ actions: {
10
+ async copyName(_state, event: DataEvent<{ name: string }>) {
11
+ await this.dash.system.copyText(event.data.name);
12
+ this.setState({ toast: `Copied "${event.data.name}"` });
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,75 @@
1
+ export default {
2
+ // Every icon name, as Icon's name, and ListItem's, Chip's, GridItem's and EmptyState's icon take.
3
+ names: [
4
+ 'arrow-down',
5
+ 'arrow-left',
6
+ 'arrow-right',
7
+ 'arrow-up',
8
+ 'bank',
9
+ 'bell',
10
+ 'bolt',
11
+ 'calendar',
12
+ 'card',
13
+ 'cart',
14
+ 'cash',
15
+ 'check',
16
+ 'chevron-down',
17
+ 'chevron-left',
18
+ 'chevron-right',
19
+ 'chevron-up',
20
+ 'clock',
21
+ 'close',
22
+ 'copy',
23
+ 'download',
24
+ 'edit',
25
+ 'error',
26
+ 'external-link',
27
+ 'eye',
28
+ 'eye-off',
29
+ 'file',
30
+ 'gift',
31
+ 'globe',
32
+ 'grid',
33
+ 'headphones',
34
+ 'heart',
35
+ 'help',
36
+ 'history',
37
+ 'home',
38
+ 'image',
39
+ 'info',
40
+ 'link',
41
+ 'list',
42
+ 'location',
43
+ 'lock',
44
+ 'logout',
45
+ 'mail',
46
+ 'microphone',
47
+ 'minus',
48
+ 'mobile',
49
+ 'more',
50
+ 'music',
51
+ 'phone',
52
+ 'playlist',
53
+ 'plus',
54
+ 'qr-code',
55
+ 'receipt',
56
+ 'refresh',
57
+ 'search',
58
+ 'send',
59
+ 'settings',
60
+ 'share',
61
+ 'star',
62
+ 'success',
63
+ 'ticket',
64
+ 'trash',
65
+ 'upload',
66
+ 'user',
67
+ 'users',
68
+ 'video',
69
+ 'wallet',
70
+ 'warning',
71
+ 'water',
72
+ 'wifi',
73
+ ],
74
+ toast: '',
75
+ };
@@ -0,0 +1,36 @@
1
+ <View>
2
+ <Text variant="title">Icons</Text>
3
+ <Text>Icon shows a small picture by its name. Lists, chips, grids and empty states take the same names.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">size and tone</Text>
7
+ <View variant="card" direction="row" wrap gap="large" align="center">
8
+ <Icon name="music" size="small" />
9
+ <Icon name="music" size="medium" />
10
+ <Icon name="music" size="large" />
11
+ <Icon name="info" tone="info" />
12
+ <Icon name="success" tone="success" />
13
+ <Icon name="warning" tone="warning" />
14
+ <Icon name="error" tone="danger" />
15
+ <Icon name="star" tone="muted" />
16
+ <Icon name="heart" tone="default" />
17
+ </View>
18
+ </View>
19
+
20
+ <View gap="small">
21
+ <Text variant="subtitle">Every name</Text>
22
+ <Text variant="caption" tone="muted">Tap one to copy its name.</Text>
23
+ <View variant="card">
24
+ <Grid columns="3" gap="medium">
25
+ <For each="{{ names }}" item="name" key="*this">
26
+ <View align="center" gap="small" padding="small" data-name="{{ name }}" onTap="copyName">
27
+ <Icon name="{{ name }}" />
28
+ <Text variant="caption" tone="muted" align="center">{{ name }}</Text>
29
+ </View>
30
+ </For>
31
+ </Grid>
32
+ </View>
33
+ </View>
34
+
35
+ <Toast message="{{ toast }}" tone="success" onHide="clearToast" />
36
+ </View>
@@ -1,3 +1,5 @@
1
+ import { errorMessage } from '@monimesl/dot-runtime';
2
+
1
3
  import state from './state';
2
4
 
3
5
  // AUDACITY's id in Apple's catalog; its cover's address can change, so the page looks it up.
@@ -9,6 +11,7 @@ interface Lookup {
9
11
 
10
12
  export default definePage({
11
13
  initialState: state,
14
+ configs: { navigationBar: { title: 'Images' } },
12
15
  hooks: {
13
16
  async onLoad() {
14
17
  try {
@@ -24,7 +27,7 @@ export default definePage({
24
27
  : { coverNote: "Apple's catalog has no cover for it" },
25
28
  );
26
29
  } catch (error) {
27
- this.setState({ coverNote: (error as Error).message });
30
+ this.setState({ coverNote: errorMessage(error) });
28
31
  }
29
32
  },
30
33
  },
@@ -0,0 +1,83 @@
1
+ <View>
2
+ <Text variant="title">Images</Text>
3
+ <Text>Image shows a picture from the dot's statics/ folder or from the web. Give it alt text, which screen readers say aloud.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">From statics/</Text>
7
+ <Text variant="caption" tone="muted">These pictures come with the dot, and the build checks each one exists.</Text>
8
+ <View variant="card" direction="row" align="end" gap="large">
9
+ <Image src="statics/logo.png" alt="Small logo" width="24" height="24" />
10
+ <Image src="statics/logo.png" alt="Medium logo" width="48" height="48" />
11
+ <Image src="statics/logo.png" alt="Large logo" width="96" height="96" />
12
+ </View>
13
+ </View>
14
+
15
+ <View gap="small">
16
+ <Text variant="subtitle">From the web</Text>
17
+ <Text variant="caption" tone="muted">The cover of Drizilik's album AUDACITY. With aspect="1:1" and no width, it fills the card.</Text>
18
+ <View variant="card" gap="small">
19
+ <If condition="{{ cover }}">
20
+ <Image src="{{ cover }}" alt="The cover of AUDACITY, by Drizilik" aspect="1:1" />
21
+ <Text weight="medium">AUDACITY</Text>
22
+ <Text variant="caption" tone="muted">Drizilik raps mostly in Krio, in a style called Kriofusion. He won Best Male Artist at the 2026 SLeDU awards.</Text>
23
+ </If>
24
+ <ElseIf condition="{{ coverNote }}">
25
+ <Text variant="caption" tone="muted">{{ coverNote }}</Text>
26
+ </ElseIf>
27
+ <Else>
28
+ <Skeleton shape="box" height="280" />
29
+ </Else>
30
+ </View>
31
+ </View>
32
+
33
+ <View gap="small">
34
+ <Text variant="subtitle">shape and fit</Text>
35
+ <Text variant="caption" tone="muted">Rounded, square or circle. fit="contain" shows the whole picture; cover, the default, fills the box.</Text>
36
+ <View variant="card" direction="row" align="center" gap="large">
37
+ <Image src="statics/logo.png" alt="Rounded" width="56" height="56" shape="rounded" />
38
+ <Image src="statics/logo.png" alt="Square" width="56" height="56" shape="square" />
39
+ <Image src="statics/logo.png" alt="Circle" width="56" height="56" shape="circle" />
40
+ <Image src="statics/logo.png" alt="Contained" width="96" height="56" fit="contain" />
41
+ <Image src="statics/logo.png" alt="Covered" width="96" height="56" fit="cover" />
42
+ </View>
43
+ </View>
44
+
45
+ <View gap="small">
46
+ <Text variant="subtitle">onLoad</Text>
47
+ <Text variant="caption" tone="muted">Tells the page the picture's own size once it loads. The size on screen comes from state here.</Text>
48
+ <View variant="card" gap="small">
49
+ <Image src="statics/logo.png" alt="Logo" width="{{ size }}" height="{{ size }}" onLoad="logoLoaded" />
50
+ <Text>{{ size }} × {{ size }} on screen, {{ loaded }} in the file</Text>
51
+ <View direction="row" gap="small">
52
+ <Button variant="secondary" onClick="shrink" disabled="{{ size <= 16 }}">Smaller</Button>
53
+ <Button variant="secondary" onClick="grow" disabled="{{ size >= 128 }}">Bigger</Button>
54
+ </View>
55
+ </View>
56
+ </View>
57
+
58
+ <View gap="small">
59
+ <Text variant="subtitle">onError</Text>
60
+ <Text variant="caption" tone="muted">Tells the page why a picture didn't load.</Text>
61
+ <View variant="card">
62
+ <If condition="{{ failure }}">
63
+ <Text>{{ failure }}</Text>
64
+ </If>
65
+ <Else>
66
+ <Image src="{{ missing }}" alt="" width="48" height="48" onError="missingFailed" />
67
+ </Else>
68
+ </View>
69
+ </View>
70
+
71
+ <View gap="small">
72
+ <Text variant="subtitle">Avatar</Text>
73
+ <Text variant="caption" tone="muted">A person's photo, round. Without one, or when it fails to load, their initials show.</Text>
74
+ <View variant="card" direction="row" align="center" gap="large">
75
+ <Avatar name="Salone Sounds" image="statics/logo.png" size="large" />
76
+ <Avatar name="Mariama Kamara" size="large" />
77
+ <Avatar name="Fatmata Conteh" />
78
+ <Avatar name="Boii" size="small" />
79
+ <Avatar name="Kao Denero" image="{{ missing }}" />
80
+ </View>
81
+ <Text variant="caption" tone="muted">The last one's picture is missing, so it shows KD.</Text>
82
+ </View>
83
+ </View>
@@ -12,6 +12,7 @@ interface Lookup {
12
12
 
13
13
  export default definePage({
14
14
  initialState: state,
15
+ configs: { navigationBar: { title: 'Layout' } },
15
16
  hooks: {
16
17
  // The shelf's covers come from Apple's catalog.
17
18
  async onLoad() {
@@ -44,10 +45,10 @@ export default definePage({
44
45
  nextAlign: (state) => ({ align: after(ALIGNS, state.align) }),
45
46
  nextGap: (state) => ({ gap: after(SPACES, state.gap) }),
46
47
  toggleWrap: (state) => ({ wrap: !state.wrap }),
47
- openSection: (_state, event: DotEvent<unknown, { name: string }>) => ({
48
+ openSection: (_state, event: DataEvent<{ name: string }>) => ({
48
49
  section: event.data.name,
49
50
  }),
50
- showFirst: (_state, event: DotEvent<{ index: number }>) => ({ first: event.detail.index }),
51
+ showFirst: (_state, event: IndexEvent) => ({ first: event.detail.index }),
51
52
  shelfStart: () => ({ first: 0 }),
52
53
  shelfEnd: (state) => ({ first: state.releases.length - 1 }),
53
54
  playSong: (state) => ({ playing: !state.playing }),