@monimesl/dot-cli 1.0.0-beta.0 → 1.0.0-beta.10

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 (98) hide show
  1. package/README.md +22 -9
  2. package/dist/cli.js +24 -14
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-DMnWDdJ6.js → server-BhnO9_Zu.js} +56 -7
  5. package/dist/server-BhnO9_Zu.js.map +1 -0
  6. package/dist/templates/showcase/.env +2 -2
  7. package/dist/templates/showcase/dot.ts +22 -2
  8. package/dist/templates/showcase/pages/basics/bars/controller.ts +2 -2
  9. package/dist/templates/showcase/pages/basics/bars/view.dml +2 -1
  10. package/dist/templates/showcase/pages/basics/bindings/state.ts +2 -2
  11. package/dist/templates/showcase/pages/basics/bindings/view.dml +2 -2
  12. package/dist/templates/showcase/pages/basics/colors/controller.ts +1 -1
  13. package/dist/templates/showcase/pages/basics/concert/view.dml +1 -1
  14. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +1 -1
  15. package/dist/templates/showcase/pages/basics/control-flow/view.dml +1 -1
  16. package/dist/templates/showcase/pages/basics/events/controller.ts +1 -1
  17. package/dist/templates/showcase/pages/basics/events/view.dml +1 -1
  18. package/dist/templates/showcase/pages/basics/hooks/controller.ts +2 -1
  19. package/dist/templates/showcase/pages/basics/hooks/view.dml +2 -2
  20. package/dist/templates/showcase/pages/basics/navigation/controller.ts +1 -0
  21. package/dist/templates/showcase/pages/basics/navigation/view.dml +1 -1
  22. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +3 -3
  23. package/dist/templates/showcase/pages/basics/share/view.dml +2 -2
  24. package/dist/templates/showcase/pages/basics/state/controller.ts +9 -0
  25. package/dist/templates/showcase/pages/basics/state/state.ts +3 -0
  26. package/dist/templates/showcase/pages/basics/theme/controller.ts +28 -0
  27. package/dist/templates/showcase/pages/basics/theme/state.ts +4 -0
  28. package/dist/templates/showcase/pages/basics/theme/view.dml +50 -0
  29. package/dist/templates/showcase/pages/components/capture/controller.ts +25 -0
  30. package/dist/templates/showcase/pages/components/capture/state.ts +7 -0
  31. package/dist/templates/showcase/pages/components/capture/view.dml +32 -0
  32. package/dist/templates/showcase/pages/components/carousel/view.dml +2 -2
  33. package/dist/templates/showcase/pages/components/feedback/controller.ts +2 -1
  34. package/dist/templates/showcase/pages/components/feedback/state.ts +2 -2
  35. package/dist/templates/showcase/pages/components/feedback/view.dml +2 -2
  36. package/dist/templates/showcase/pages/components/forms/controller.ts +2 -2
  37. package/dist/templates/showcase/pages/components/forms/state.ts +1 -1
  38. package/dist/templates/showcase/pages/components/forms/view.dml +6 -5
  39. package/dist/templates/showcase/pages/components/icons/controller.ts +1 -0
  40. package/dist/templates/showcase/pages/components/icons/state.ts +1 -1
  41. package/dist/templates/showcase/pages/components/images/controller.ts +1 -1
  42. package/dist/templates/showcase/pages/components/images/state.ts +1 -1
  43. package/dist/templates/showcase/pages/components/images/view.dml +4 -4
  44. package/dist/templates/showcase/pages/components/layout/controller.ts +5 -1
  45. package/dist/templates/showcase/pages/components/layout/state.ts +2 -0
  46. package/dist/templates/showcase/pages/components/layout/view.dml +44 -6
  47. package/dist/templates/showcase/pages/components/lists/view.dml +12 -7
  48. package/dist/templates/showcase/pages/components/money/controller.ts +1 -1
  49. package/dist/templates/showcase/pages/components/money/view.dml +2 -2
  50. package/dist/templates/showcase/pages/components/pinned/controller.ts +10 -0
  51. package/dist/templates/showcase/pages/components/pinned/state.ts +25 -0
  52. package/dist/templates/showcase/pages/components/pinned/view.dml +42 -0
  53. package/dist/templates/showcase/pages/components/reading/state.ts +2 -2
  54. package/dist/templates/showcase/pages/components/sheets/controller.ts +1 -1
  55. package/dist/templates/showcase/pages/components/sheets/view.dml +1 -1
  56. package/dist/templates/showcase/pages/components/styles/view.dml +39 -4
  57. package/dist/templates/showcase/pages/components/tabs/state.ts +2 -2
  58. package/dist/templates/showcase/pages/components/tabs/view.dml +6 -5
  59. package/dist/templates/showcase/pages/components/text/view.dml +4 -4
  60. package/dist/templates/showcase/pages/dash/account/controller.ts +3 -0
  61. package/dist/templates/showcase/pages/dash/account/view.dml +2 -2
  62. package/dist/templates/showcase/pages/dash/cache/controller.ts +45 -16
  63. package/dist/templates/showcase/pages/dash/cache/state.ts +1 -0
  64. package/dist/templates/showcase/pages/dash/cache/view.dml +4 -0
  65. package/dist/templates/showcase/pages/dash/calls/controller.ts +3 -2
  66. package/dist/templates/showcase/pages/dash/contacts/controller.ts +1 -0
  67. package/dist/templates/showcase/pages/dash/crypto/controller.ts +2 -0
  68. package/dist/templates/showcase/pages/dash/crypto/view.dml +1 -1
  69. package/dist/templates/showcase/pages/dash/files/controller.ts +67 -32
  70. package/dist/templates/showcase/pages/dash/files/state.ts +1 -0
  71. package/dist/templates/showcase/pages/dash/files/view.dml +5 -1
  72. package/dist/templates/showcase/pages/dash/media/controller.ts +7 -1
  73. package/dist/templates/showcase/pages/dash/media/state.ts +2 -0
  74. package/dist/templates/showcase/pages/dash/network/controller.ts +5 -1
  75. package/dist/templates/showcase/pages/dash/network/view.dml +1 -1
  76. package/dist/templates/showcase/pages/dash/payments/cases.ts +40 -22
  77. package/dist/templates/showcase/pages/dash/payments/controller.ts +27 -3
  78. package/dist/templates/showcase/pages/dash/payments/state.ts +2 -0
  79. package/dist/templates/showcase/pages/dash/payments/view.dml +3 -1
  80. package/dist/templates/showcase/pages/dash/reminders/controller.ts +73 -0
  81. package/dist/templates/showcase/pages/dash/reminders/state.ts +6 -0
  82. package/dist/templates/showcase/pages/dash/reminders/view.dml +34 -0
  83. package/dist/templates/showcase/pages/dash/secrets/controller.ts +37 -14
  84. package/dist/templates/showcase/pages/dash/secrets/state.ts +1 -0
  85. package/dist/templates/showcase/pages/dash/secrets/view.dml +4 -0
  86. package/dist/templates/showcase/pages/dash/system/controller.ts +5 -0
  87. package/dist/templates/showcase/pages/dash/system/view.dml +4 -4
  88. package/dist/templates/showcase/pages/dash/ui/controller.ts +6 -1
  89. package/dist/templates/showcase/pages/index/controller.ts +1 -0
  90. package/dist/templates/showcase/pages/index/state.ts +30 -6
  91. package/dist/templates/showcase/statics/icons/ticket-stub.svg +1 -0
  92. package/dist/templates/starter/.env +2 -2
  93. package/dist/templates/starter/dot.ts +1 -0
  94. package/dist/templates/starter/pages/index/controller.ts +2 -0
  95. package/dist/templates/starter/pages/index/state.ts +2 -0
  96. package/dist/templates/starter/pages/index/view.dml +2 -1
  97. package/package.json +6 -6
  98. package/dist/server-DMnWDdJ6.js.map +0 -1
@@ -1,3 +1,3 @@
1
- # Public configuration for the dot. Anyone with the dot can read these variables: never put a
2
- # secret here. Read them in the service as `env.GREETING`.
1
+ # Public settings for the dot. Code reads each one from `env`, such as `env.GREETING`.
2
+ # They're built into the dot, so anyone who has it can read them. Never put a secret here.
3
3
  GREETING=Welcome to Salone Sounds. This message comes from the dot's .env file.
@@ -3,7 +3,7 @@ import { logger } from '@monimesl/dot-runtime';
3
3
  import { swatchColors } from './pages/basics/colors/palette';
4
4
 
5
5
  export default defineDot({
6
- // Folders under pages/, as the home page lists them. The first opens when the dot starts.
6
+ // Each page is a folder under pages/, here in the home page's order. The dot opens on the first.
7
7
  pages: [
8
8
  'index',
9
9
  // How a page works.
@@ -16,6 +16,7 @@ export default defineDot({
16
16
  'basics/hooks',
17
17
  'basics/bars',
18
18
  'basics/colors',
19
+ 'basics/theme',
19
20
  'basics/share',
20
21
  'basics/concert',
21
22
  // What a view can show.
@@ -28,6 +29,8 @@ export default defineDot({
28
29
  'components/carousel',
29
30
  'components/lists',
30
31
  'components/tabs',
32
+ 'components/pinned',
33
+ 'components/capture',
31
34
  'components/forms',
32
35
  'components/money',
33
36
  'components/feedback',
@@ -37,6 +40,7 @@ export default defineDot({
37
40
  'dash/system',
38
41
  'dash/media',
39
42
  'dash/contacts',
43
+ 'dash/reminders',
40
44
  'dash/account',
41
45
  'dash/payments',
42
46
  'dash/crypto',
@@ -46,16 +50,32 @@ export default defineDot({
46
50
  'dash/secrets',
47
51
  'dash/calls',
48
52
  ],
49
- // The showcase's own colours, used by name on the Styles page, and the Page colours swatches.
53
+ // How every page looks. The Theme page explains each setting.
50
54
  theme: {
55
+ // Turn one of these on to see it on every page:
56
+ // font: 'rounded',
57
+ // headingFont: 'serif',
58
+ // headingWeight: 'medium',
59
+ // textSize: 'large',
60
+ // shape: 'rounded',
61
+ // density: 'compact',
62
+ // elevation: 'raised',
63
+ // components: { Button: { size: 'large' }, View: { variants: { card: { border: 'none' } } } },
64
+ // The showcase's own colours, used by name on the Styles page, and the Page colours swatches.
51
65
  colors: {
52
66
  custom: {
53
67
  gold: { light: '#f5c542', dark: '#d4a72c' },
54
68
  // Dark text that reads on gold in both light and dark.
55
69
  ink: { light: '#1c1c1e', dark: '#1c1c1e' },
70
+ amber: { light: '#e8952a', dark: '#f0a63a' },
56
71
  ...swatchColors(),
57
72
  },
58
73
  },
74
+ // The showcase's own icon, an SVG in statics/, used by name like Dot's icons.
75
+ icons: { 'ticket-stub': 'statics/icons/ticket-stub.svg' },
76
+ // A gradient between two of the theme's colours, for a View's background.
77
+ gradients: { goldenHour: { from: 'gold', to: 'amber', direction: 'diagonal' } },
59
78
  },
79
+ // logger() writes each page load, event, navigation and state change to the console.
60
80
  plugins: [logger()],
61
81
  });
@@ -24,8 +24,8 @@ export default definePage({
24
24
  },
25
25
  togglePainted(state) {
26
26
  const painted = !state.painted;
27
- // A pale sky by day, the night over Freetown by dark. Undefined puts the theme's back. Cards,
28
- // fields and quieter text follow the background, unless the page sets those too.
27
+ // A pale sky in light mode, the night over Freetown in dark. Undefined puts the theme's back.
28
+ // Cards, fields and quieter text follow the background, unless the page sets those too.
29
29
  if (painted) {
30
30
  this.setColors({ background: { light: '#E3F2FD', dark: '#0B1A2E' } });
31
31
  } else {
@@ -16,12 +16,13 @@
16
16
  <Button variant="secondary" onClick="toggleColors">{{ colored ? 'Plain bar' : 'Green bar' }}</Button>
17
17
  <Button variant="secondary" onClick="toggleNavigationBar">{{ navigationBarHidden ? 'Show the bar' : 'Hide the bar' }}</Button>
18
18
  </View>
19
+ <Text variant="caption" tone="muted">A title can be up to 60 characters. With the bar hidden, Dash still shows a capsule at the top right with the dot's menu, so keep that corner free.</Text>
19
20
  </View>
20
21
  </View>
21
22
 
22
23
  <View gap="small">
23
24
  <Text variant="subtitle">setColors</Text>
24
- <Text variant="caption" tone="muted">configs.colors or setColors give a page its own colours, for light and dark, such as its background. Dash paints the background behind the page too, makes cards and fields that sit on it, and picks text, bar and status bar colours that read on it. The Page colours page has more.</Text>
25
+ <Text variant="caption" tone="muted">configs.colors or setColors give a page its own colours, for light and dark, such as its background. Dash picks text and bar colours that read on it. The Page colours page has more.</Text>
25
26
  <View variant="card">
26
27
  <Button variant="secondary" onClick="togglePainted">{{ painted ? 'Theme background' : 'Sky background' }}</Button>
27
28
  </View>
@@ -3,11 +3,11 @@ export default {
3
3
  syntax: '{{ name }}',
4
4
  name: 'Mariama',
5
5
  stageName: null as string | null,
6
- // Le, in the leone since 2022, when it dropped three zeros.
6
+ // In leones (SLE), the new leone of 2022.
7
7
  price: 150,
8
8
  tickets: 4,
9
9
  genres: ['kriofusion', 'palm-wine', 'gumbe'],
10
- // state.ts runs in the service, so it can read the .env file's values too.
10
+ // A binding reads only the page's state, so a value from .env goes in it first.
11
11
  greeting: env.GREETING,
12
12
  // Worked out in the controller, since a binding can't call a function.
13
13
  shout: 'MARIAMA',
@@ -13,7 +13,7 @@
13
13
 
14
14
  <View gap="small">
15
15
  <Text variant="subtitle">Expressions</Text>
16
- <Text variant="caption" tone="muted">Maths, comparisons, ? : and ?? work. Calling a function doesn't: work the value out in the controller, as shout is here.</Text>
16
+ <Text variant="caption" tone="muted">Maths, comparisons, ? : and ?? work. A binding can't call a function, so work the value out in the controller, as shout is here.</Text>
17
17
  <View variant="card" gap="small">
18
18
  <Text>{{ tickets }} {{ tickets === 1 ? 'ticket' : 'tickets' }} at Le {{ price }} each: Le {{ tickets * price }}</Text>
19
19
  <Text>Stage name: {{ stageName ?? 'none yet' }}</Text>
@@ -33,7 +33,7 @@
33
33
 
34
34
  <View gap="small">
35
35
  <Text variant="subtitle">env</Text>
36
- <Text variant="caption" tone="muted">Values in the dot's .env file. Anyone with the dot can read them, so never put a secret there.</Text>
36
+ <Text variant="caption" tone="muted">Values in the dot's .env file, copied into state.ts to show here. Anyone with the dot can read them, so never put a secret there.</Text>
37
37
  <View variant="card">
38
38
  <Text>{{ greeting }}</Text>
39
39
  </View>
@@ -31,7 +31,7 @@ export default definePage({
31
31
  },
32
32
  useCustom(state) {
33
33
  const light = state.customLight.trim();
34
- // One colour is enough: it's used in dark too.
34
+ // One colour is enough. It's used in dark mode too.
35
35
  const dark = state.customDark.trim() || light;
36
36
  if (!HEX_COLOR.test(light) || !HEX_COLOR.test(dark)) {
37
37
  this.setState({ customError: 'Write a colour as # and six digits, such as #FFF3E0' });
@@ -10,7 +10,7 @@
10
10
  <Notice tone="info">Opened for {{ buyer }}. buyer isn't in share's options, so a shared link leaves it out.</Notice>
11
11
  </If>
12
12
  <Else>
13
- <Notice tone="success">Opened from a link: it carried the concert, and nothing else.</Notice>
13
+ <Notice tone="success">Opened from a link. It carried the concert and nothing else.</Notice>
14
14
  </Else>
15
15
  <Text variant="caption" tone="muted">Tap ••• then Share. The link opens this concert.</Text>
16
16
  </If>
@@ -12,7 +12,7 @@ export default definePage({
12
12
  }),
13
13
  restock: (state) => ({ shows: state.shows.map((show) => ({ ...show, left: 20 })) }),
14
14
  toggleSignedIn: (state) => ({ signedIn: !state.signedIn }),
15
- // Changes the list's order. The key keeps each row's place right.
15
+ // Changes the list's order. The For's key lets each row move with its item.
16
16
  shuffle: (state) => ({ genres: [...state.genres.slice(1), state.genres[0]!] }),
17
17
  },
18
18
  });
@@ -18,7 +18,7 @@
18
18
 
19
19
  <View gap="small">
20
20
  <Text variant="subtitle">For, with ElseIf inside</Text>
21
- <Text variant="caption" tone="muted">item names each show. key gives each a lasting id, so its row stays the same row when the list changes. ElseIf checks another condition when the one above is false.</Text>
21
+ <Text variant="caption" tone="muted">item names each show. key names the field that tells the shows apart, so each row stays with its show when the list changes. ElseIf checks another condition when the one above is false.</Text>
22
22
  <View variant="card" gap="medium">
23
23
  <For each="{{ shows }}" item="show" key="id">
24
24
  <View direction="row" align="center" gap="small">
@@ -14,7 +14,7 @@ export default definePage({
14
14
  initialState: state,
15
15
  configs: { navigationBar: { title: 'Events' } },
16
16
  mutations: {
17
- // No detail: a tap is all there is to know.
17
+ // A tap has no detail. It only says the tap happened.
18
18
  countTap: (state, event: DotEvent) => ({
19
19
  taps: state.taps + 1,
20
20
  ...describe('countTap', event),
@@ -19,7 +19,7 @@
19
19
 
20
20
  <View gap="small">
21
21
  <Text variant="subtitle">Taps</Text>
22
- <Text variant="caption" tone="muted">Button raises click. View, ListItem, Link and Chip raise tap.</Text>
22
+ <Text variant="caption" tone="muted">A Button raises click, so it takes onClick. View, ListItem, Link and Chip raise tap, so they take onTap.</Text>
23
23
  <View variant="card" direction="row" align="center" gap="small">
24
24
  <Button variant="secondary" onClick="countTap">Tap me</Button>
25
25
  <View variant="card" grow onTap="countTap">
@@ -16,7 +16,8 @@ export default definePage({
16
16
  onLoad(options) {
17
17
  this.setState({ lastClosed, runs: ran([], `onLoad ${JSON.stringify(options)}`) });
18
18
  },
19
- // Runs each time the page comes into view: after onLoad, and when a page above it closes.
19
+ // Runs each time the page comes into view: after onLoad, when a page above it closes, and when
20
+ // the dot comes back from the background.
20
21
  onShow() {
21
22
  this.setState((current) => ({ runs: ran(current.runs, 'onShow') }));
22
23
  },
@@ -9,7 +9,7 @@
9
9
  <Text>{{ run.id }}. {{ run.hook }}</Text>
10
10
  </For>
11
11
  </View>
12
- <Text variant="caption" tone="muted">Open another page, then come back: this page runs onHide, then onShow.</Text>
12
+ <Text variant="caption" tone="muted">Open another page, then come back. This page runs onHide, then onShow.</Text>
13
13
  <Button variant="secondary" onClick="openAnother">Open another page</Button>
14
14
  </View>
15
15
 
@@ -32,6 +32,6 @@
32
32
 
33
33
  <View gap="small">
34
34
  <Text variant="subtitle">The dot's hooks</Text>
35
- <Text variant="caption" tone="muted">dot.ts can have hooks too: onLaunch runs once when the dot starts, and onError gets any error a page doesn't catch.</Text>
35
+ <Text variant="caption" tone="muted">dot.ts can have hooks too. onLaunch runs once when the dot starts. onError gets any error a page doesn't catch.</Text>
36
36
  </View>
37
37
  </View>
@@ -11,6 +11,7 @@ export default definePage({
11
11
  this.setState({ depth, from, trail: `${from} › ${depth}`, options: JSON.stringify(options) });
12
12
  },
13
13
  },
14
+ // Only actions have this.navigation, so only they open and close pages.
14
15
  actions: {
15
16
  openAnother(state) {
16
17
  this.navigation.push('basics/navigation', { depth: String(state.depth), trail: state.trail });
@@ -22,7 +22,7 @@
22
22
 
23
23
  <View gap="small">
24
24
  <Text variant="subtitle">back</Text>
25
- <Text variant="caption" tone="muted">Closes this page, or as many as you ask. On the first page, it closes the dot.</Text>
25
+ <Text variant="caption" tone="muted">Closes this page, or as many as you ask. It never closes the first page. To close the dot, call this.dash.dot.close().</Text>
26
26
  <View direction="row" gap="small">
27
27
  <Button variant="secondary" onClick="back">Back</Button>
28
28
  <Button variant="secondary" onClick="backTwo" disabled="{{ depth < 3 }}">Back two</Button>
@@ -5,9 +5,9 @@ export default definePage({
5
5
  initialState: state,
6
6
  configs: {
7
7
  navigationBar: { title: 'Choose a genre' },
8
- // This page's own background: a warm Salone sunset by day, the evening after it by dark. Dash
9
- // paints it behind the page, makes the list's cards to match, and picks text and bar colours
10
- // that read on it. Going back shows the page below in its own colours again.
8
+ // This page's own background, a warm Salone sunset in light mode and the evening after it in
9
+ // dark. Dash paints it behind the page, makes the list's cards to match, and picks text and bar
10
+ // colours that read on it. Going back shows the page below in its own colours again.
11
11
  colors: { background: { light: '#FFF3E0', dark: '#2B1A0E' } },
12
12
  },
13
13
  actions: {
@@ -1,10 +1,10 @@
1
1
  <View>
2
2
  <Text variant="title">Share links</Text>
3
- <Text>Dash's ••• menu has Share. It shares a link to the dot, which opens its first page. A page can let the link open it instead, with some of its options.</Text>
3
+ <Text>Dash's menu, ••• (⋮ on Android), has Share. It shares a link to the dot, which opens its first page. A page can let the link open it instead, with some of its options.</Text>
4
4
 
5
5
  <View gap="small">
6
6
  <Text variant="subtitle">definePage({ share })</Text>
7
- <Text variant="caption" tone="muted">share: { options: ['concert'] } lets the link open the page with its concert option. Options not on the list stay out of the link, so nothing private goes in it. Write share out in place: the build reads it.</Text>
7
+ <Text variant="caption" tone="muted">share: { options: ['concert'] } lets the link open the page with its concert option. Options not on the list stay out of the link, so nothing private goes in it. Write share out in place, as the build reads it without running the code.</Text>
8
8
  <Text variant="caption" tone="muted">Open a concert, then tap ••• and Share.</Text>
9
9
  <List>
10
10
  <For each="{{ concerts }}" item="concert" key="id">
@@ -2,21 +2,30 @@ import state from './state';
2
2
 
3
3
  export default definePage({
4
4
  initialState: state,
5
+ // What Dash's bars show on this page. The Bars page has more.
5
6
  configs: { navigationBar: { title: 'State' } },
7
+ // A mutation gets the state and returns only the fields that change. It can't wait, so it can't
8
+ // call Dash.
6
9
  mutations: {
7
10
  increment: (state) => {
8
11
  const count = state.count + 1;
12
+ // State is read-only, so make a new list rather than change the old one.
9
13
  return { count, taps: [{ id: count }, ...state.taps] };
10
14
  },
11
15
  reset: () => ({ count: 0, taps: [] }),
12
16
  },
17
+ // An action can wait, call Dash, open pages and run other handlers. It changes the state with
18
+ // this.setState or a mutation.
13
19
  actions: {
14
20
  fillUp() {
21
+ // this.state is the latest state. The state an action gets is from when it started, so it
22
+ // wouldn't see the cheers this loop adds.
15
23
  while (this.state.count < this.state.limit) {
16
24
  this.mutations.increment();
17
25
  }
18
26
  },
19
27
  undo() {
28
+ // setState takes the fields to change, or a function that gets the latest state.
20
29
  this.setState((current) => ({ count: current.count - 1, taps: current.taps.slice(1) }));
21
30
  },
22
31
  },
@@ -2,9 +2,12 @@ export interface Tap {
2
2
  id: number;
3
3
  }
4
4
 
5
+ // State is plain data, as in JSON: text, numbers, true and false, null, lists and objects. Each
6
+ // open copy of the page gets its own. For "no value", use null, as setState skips undefined fields.
5
7
  export default {
6
8
  title: 'cheers for an encore',
7
9
  count: 0,
8
10
  limit: 5,
11
+ // `as Tap[]` tells TypeScript what the empty list will hold.
9
12
  taps: [] as Tap[],
10
13
  };
@@ -0,0 +1,28 @@
1
+ import state from './state';
2
+
3
+ // Sierra Leone's green and blue, darker in light mode so text in them reads on white.
4
+ const FLAG_TONES = {
5
+ success: { light: '#178a33', dark: '#4cd471' },
6
+ info: { light: '#0060a8', dark: '#5aa9ff' },
7
+ } as const;
8
+
9
+ export default definePage({
10
+ initialState: state,
11
+ configs: { navigationBar: { title: 'Theme' } },
12
+ actions: {
13
+ // A page can't change the theme, but setColors changes its colours, tones included. Undefined
14
+ // puts the theme's own back.
15
+ setFlagTones(_state, event: CheckedEvent) {
16
+ const flagTones = event.detail.checked;
17
+ this.setState({ flagTones });
18
+ if (flagTones) {
19
+ this.setColors(FLAG_TONES);
20
+ } else {
21
+ this.setColors({
22
+ success: { light: undefined, dark: undefined },
23
+ info: { light: undefined, dark: undefined },
24
+ });
25
+ }
26
+ },
27
+ },
28
+ });
@@ -0,0 +1,4 @@
1
+ export default {
2
+ // Whether the page uses the flag's green and blue for its tones, in place of the theme's.
3
+ flagTones: false,
4
+ };
@@ -0,0 +1,50 @@
1
+ <View>
2
+ <Text variant="title">Theme</Text>
3
+ <Text>A dot has no CSS. The theme in dot.ts sets how every page looks, and a page can't change it while it runs. To see a setting below, turn it on in dot.ts, where a commented line has each one, then reload.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">colors</Text>
7
+ <Text variant="caption" tone="muted">primary, background, surface, text, muted, border and subtle, and the tones info, success, warning and danger, each for light and dark. A colour left out is Dash's. A page can change its own with setColors, as this switch does for two tones.</Text>
8
+ <View variant="card" gap="small">
9
+ <View direction="row" gap="small" wrap>
10
+ <Button tone="info">Details</Button>
11
+ <Button tone="success">Pay SLE 120</Button>
12
+ <Button variant="secondary" tone="danger">Refund</Button>
13
+ </View>
14
+ <View direction="row" gap="small" wrap>
15
+ <Badge tone="info">New</Badge>
16
+ <Badge tone="success">Paid</Badge>
17
+ <Badge tone="warning">Due Friday</Badge>
18
+ <Badge tone="danger">Failed</Badge>
19
+ </View>
20
+ <Switch label="Use the flag's green and blue" checked="{{ flagTones }}" onChange="setFlagTones" />
21
+ </View>
22
+ </View>
23
+
24
+ <View gap="small">
25
+ <Text variant="subtitle">shape, density and elevation</Text>
26
+ <Text variant="caption" tone="muted">shape is how round corners are: square, subtle, soft (the default) or rounded. density is how much room gaps and padding take: compact, regular (the default) or comfortable. elevation: "raised" gives every card and list a soft shadow in place of its hairline.</Text>
27
+ </View>
28
+
29
+ <View gap="small">
30
+ <Text variant="subtitle">font, headingFont, headingWeight and textSize</Text>
31
+ <Text variant="caption" tone="muted">font is one of the phone's own fonts, so nothing is downloaded: system (the default), rounded, serif or mono. headingFont and headingWeight give titles their own. textSize, small, regular or large, makes text a tenth smaller or larger, except what's typed into fields, amounts and codes.</Text>
32
+ </View>
33
+
34
+ <View gap="small">
35
+ <Text variant="subtitle">components</Text>
36
+ <Text variant="caption" tone="muted">Default props for every use of a component, or for one of its variants, so a dot sets its look once. Button: { size: "large" } makes every Button large. View: { variants: { card: { elevation: "low" } } } gives only cards a shadow. A prop in the markup still wins.</Text>
37
+ </View>
38
+
39
+ <View gap="small">
40
+ <Text variant="subtitle">icons and gradients</Text>
41
+ <Text variant="caption" tone="muted">icons names SVGs in statics/, such as ticket-stub here. They work wherever Dot's icons do, and take the icon's tone. The build refuses an SVG with scripts or links, so export it from your design tool as plain shapes. gradients names a gradient between two of the theme's colours, for a View's background, such as goldenHour.</Text>
42
+ <View background="goldenHour" radius="large" padding="large" gap="small">
43
+ <View direction="row" align="center" gap="small">
44
+ <Icon name="ticket-stub" tone="ink" />
45
+ <Text weight="bold" tone="ink">Freetown Jazz Night</Text>
46
+ </View>
47
+ <Text variant="title" tone="ink">2 tickets</Text>
48
+ </View>
49
+ </View>
50
+ </View>
@@ -0,0 +1,25 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ configs: { navigationBar: { title: 'Pictures of a View' } },
6
+ actions: {
7
+ async capture() {
8
+ this.setState({ capturing: true, note: '' });
9
+ try {
10
+ const picture = await this.captureView('ticket');
11
+ this.setState({ picture, note: `${Math.round(picture.size / 1024)} KB` });
12
+ } catch (error) {
13
+ this.setState({ note: (error as Error).message });
14
+ } finally {
15
+ this.setState({ capturing: false });
16
+ }
17
+ },
18
+ async share() {
19
+ const { picture } = this.state;
20
+ if (picture) {
21
+ await this.dash.system.share({ file: picture });
22
+ }
23
+ },
24
+ },
25
+ });
@@ -0,0 +1,7 @@
1
+ import type { DotFile } from '@monimesl/dot-runtime';
2
+
3
+ export default {
4
+ capturing: false,
5
+ picture: null as DotFile | null,
6
+ note: '',
7
+ };
@@ -0,0 +1,32 @@
1
+ <View>
2
+ <Text variant="title">Pictures of a View</Text>
3
+ <Text>Give a View an id, and this.captureView makes a PNG of it, such as a ticket to share. It draws the whole View, even past the bottom of the screen.</Text>
4
+
5
+ <View id="ticket" variant="card" gap="medium">
6
+ <View direction="row" justify="between" align="center">
7
+ <Text variant="subtitle">Lumley Beach Sundown</Text>
8
+ <Icon name="ticket-stub" tone="gold" />
9
+ </View>
10
+ <Divider />
11
+ <DetailRow label="Name" value="Mariama Kamara" />
12
+ <DetailRow label="Where" value="Lumley Beach, Freetown" />
13
+ <DetailRow label="When" value="Friday, 6 pm" />
14
+ <DetailRow label="Paid" value="Le 300" />
15
+ <View align="center">
16
+ <QRCode value="SALONE-SOUNDS-4471" />
17
+ </View>
18
+ </View>
19
+
20
+ <Button loading="{{ capturing }}" onClick="capture">Make a picture</Button>
21
+ <If condition="{{ note }}">
22
+ <Text variant="caption" tone="muted">{{ note }}</Text>
23
+ </If>
24
+
25
+ <If condition="{{ picture }}">
26
+ <View gap="medium">
27
+ <Text variant="caption" tone="muted">captureView gives back a file, as a download does. Show it with an Image, or pass it to dash.system.share. A new picture of a View replaces the last one. A picture from another site shows only if that site allows CORS.</Text>
28
+ <Image src="{{ picture.url }}" alt="The ticket as a picture" />
29
+ <Button variant="secondary" onClick="share">Share the picture</Button>
30
+ </View>
31
+ </If>
32
+ </View>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <View gap="small">
6
6
  <Text variant="subtitle">autoplay, loop and effect</Text>
7
- <Text variant="caption" tone="muted">Gen Z artists. The slides move on their own every 3 seconds, start over at the end, and pause when you touch them. effect="scale" shrinks the slides beside the one in view.</Text>
7
+ <Text variant="caption" tone="muted">Gen Z artists. autoplay moves the slides every interval, here 3000 milliseconds, and pauses while you touch them. loop goes on from the last slide to the first. effect="scale" shrinks the slides beside the one in view.</Text>
8
8
  <Carousel autoplay loop interval="3000" gap="medium" effect="scale" indicator="dots">
9
9
  <For each="{{ artists }}" item="artist" key="id">
10
10
  <View variant="card" align="center" gap="medium">
@@ -23,7 +23,7 @@
23
23
 
24
24
  <View gap="small">
25
25
  <Text variant="subtitle">index and onChange</Text>
26
- <Text variant="caption" tone="muted">How Salone music grew, from palm-wine to Gen Z. The page sets the slide with index, and onChange tells it when you swipe. The buttons move the slides, so indicator="none" hides the dots.</Text>
26
+ <Text variant="caption" tone="muted">How Salone music grew, from palm-wine to Gen Z. index sets the slide. When you swipe, onChange sends the new slide's index. The buttons move the slides here, so indicator="none" hides the dots.</Text>
27
27
  <Carousel index="{{ chapter }}" indicator="none" effect="slide" gap="small" padding="small" onChange="showChapter">
28
28
  <For each="{{ chapters }}" item="card" key="title">
29
29
  <View variant="card" align="center" gap="medium">
@@ -8,7 +8,8 @@ export default definePage({
8
8
  restart: () => ({ progress: 0 }),
9
9
  toggleBusy: (state) => ({ busy: !state.busy }),
10
10
  toggleLoaded: (state) => ({ loaded: !state.loaded }),
11
- // A toast lasts its default time unless the button gives one, in milliseconds.
11
+ // A button's data-duration, in milliseconds, is how long its Toast stays. Without one, the
12
+ // Toast stays for its default time.
12
13
  showToast: (
13
14
  _state,
14
15
  event: DataEvent<{ message: string; tone: string; duration?: string }>,
@@ -6,9 +6,9 @@ export interface PaymentStep {
6
6
  status: StepStatus;
7
7
  }
8
8
 
9
- // Sending Le 500 to Fatmata Conteh in Bo.
9
+ // Sending SLE 500 to Fatmata Conteh in Bo.
10
10
  const PAYMENT = [
11
- { title: 'Paid', text: 'Le 500 from your wallet' },
11
+ { title: 'Paid', text: 'SLE 500 from your wallet' },
12
12
  { title: 'Sent', text: 'On its way to Bo' },
13
13
  { title: 'Received', text: 'By Fatmata Conteh' },
14
14
  ];
@@ -14,7 +14,7 @@
14
14
 
15
15
  <View gap="small">
16
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>
17
+ <Text variant="caption" tone="muted">Shows the shape of what's loading, as lines of text, a circle or a box.</Text>
18
18
  <View variant="card">
19
19
  <If condition="{{ loaded }}">
20
20
  <View direction="row" align="center" gap="large">
@@ -65,7 +65,7 @@
65
65
  <Text variant="subtitle">A busy Button</Text>
66
66
  <Text variant="caption" tone="muted">loading shows a spinner, and the button ignores taps.</Text>
67
67
  <View variant="card">
68
- <Button loading="{{ busy }}">{{ busy ? 'Paying…' : 'Pay Le 300 for two tickets' }}</Button>
68
+ <Button loading="{{ busy }}">{{ busy ? 'Paying…' : 'Pay SLE 300 for two tickets' }}</Button>
69
69
  <Switch label="Busy" checked="{{ busy }}" onChange="toggleBusy" />
70
70
  </View>
71
71
  </View>
@@ -3,10 +3,10 @@ import { errorMessage } from '@monimesl/dot-runtime';
3
3
  import type { TicketType } from './state';
4
4
  import state, { SHOWS, TICKETS } from './state';
5
5
 
6
- // 2 tickets of a Le 1,200 type -> "Le 2,400"
6
+ // 2 tickets of an SLE 1,200 type -> "SLE 2,400"
7
7
  function total(tickets: number, ticket: TicketType): string {
8
8
  const leones = tickets * TICKETS[ticket].price;
9
- return `Le ${leones.toLocaleString('en-US')}`;
9
+ return `SLE ${leones.toLocaleString('en-US')}`;
10
10
  }
11
11
 
12
12
  export default definePage({
@@ -18,7 +18,7 @@ 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 ${2 * TICKETS.regular.price}`,
21
+ total: `SLE ${2 * TICKETS.regular.price}`,
22
22
  name: '',
23
23
  email: '',
24
24
  phone: '',
@@ -9,13 +9,14 @@
9
9
  <Option value="freetown" label="Salone Sounds Live · Freetown" />
10
10
  <Option value="bo" label="Palm-wine evening · Bo" />
11
11
  <Option value="kenema" label="Gospel night · Kenema" />
12
+ <Option value="makeni" label="Sold out · Makeni" disabled />
12
13
  </Select>
13
14
  </View>
14
15
  </View>
15
16
 
16
17
  <View gap="small">
17
18
  <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
+ <Text variant="caption" tone="muted">type sets the keyboard, such as email, phone, number or password. prefix and suffix sit inside the field. error shows under it, in place of hint. onSubmit comes when the person presses return, here in the promo code.</Text>
19
20
  <View variant="card">
20
21
  <Input label="Name" placeholder="Mariama Kamara" value="{{ name }}" maxLength="40" onChange="setName" />
21
22
  <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" />
@@ -37,9 +38,9 @@
37
38
  <Text variant="caption" tone="muted">One choice from a few. A Radio, or the whole group, can be disabled.</Text>
38
39
  <View variant="card">
39
40
  <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" />
41
+ <Radio value="regular" label="Regular · SLE 150" />
42
+ <Radio value="vip" label="VIP, near the stage · SLE 400" />
43
+ <Radio value="table" label="Table for four · SLE 1,200" />
43
44
  <Radio value="backstage" label="Backstage · sold out" disabled />
44
45
  </RadioGroup>
45
46
  </View>
@@ -90,7 +91,7 @@
90
91
  <Notice tone="danger" title="Not booked">{{ failure }}</Notice>
91
92
  </If>
92
93
 
93
- <Text variant="caption" tone="muted">A Footer keeps its buttons at the bottom of the screen.</Text>
94
+ <Text variant="caption" tone="muted">A Footer keeps its buttons at the bottom of the screen, on a bar from side to side. Put it last in the page's View.</Text>
94
95
  <Footer>
95
96
  <Button onClick="send" loading="{{ sending }}" disabled="{{ !agreed }}">Buy tickets</Button>
96
97
  </Footer>
@@ -4,6 +4,7 @@ export default definePage({
4
4
  initialState: state,
5
5
  configs: { navigationBar: { title: 'Icons' } },
6
6
  mutations: {
7
+ // The Toast raises onHide once it goes. Clearing its message lets the same one show again.
7
8
  clearToast: () => ({ toast: '' }),
8
9
  },
9
10
  actions: {