@monimesl/dot-cli 0.1.0-alpha.3 → 0.1.0-alpha.30

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 (159) hide show
  1. package/README.md +91 -17
  2. package/dist/cli.js +776 -199
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-z7M4FLjf.js → server-DMnWDdJ6.js} +702 -280
  5. package/dist/server-DMnWDdJ6.js.map +1 -0
  6. package/dist/templates/showcase/.env +3 -0
  7. package/dist/templates/showcase/dot.ts +60 -0
  8. package/dist/templates/showcase/pages/basics/bars/controller.ts +47 -0
  9. package/dist/templates/showcase/pages/basics/bars/state.ts +7 -0
  10. package/dist/templates/showcase/pages/basics/bars/view.dml +36 -0
  11. package/dist/templates/showcase/pages/basics/bindings/controller.ts +15 -0
  12. package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
  13. package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
  14. package/dist/templates/showcase/pages/basics/colors/controller.ts +112 -0
  15. package/dist/templates/showcase/pages/basics/colors/palette.ts +52 -0
  16. package/dist/templates/showcase/pages/basics/colors/state.ts +17 -0
  17. package/dist/templates/showcase/pages/basics/colors/view.dml +142 -0
  18. package/dist/templates/showcase/pages/basics/concert/controller.ts +16 -0
  19. package/dist/templates/showcase/pages/basics/concert/state.ts +7 -0
  20. package/dist/templates/showcase/pages/basics/concert/view.dml +20 -0
  21. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +18 -0
  22. package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
  23. package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
  24. package/dist/templates/showcase/pages/basics/events/controller.ts +43 -0
  25. package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
  26. package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
  27. package/dist/templates/showcase/pages/basics/hooks/controller.ts +43 -0
  28. package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
  29. package/dist/templates/showcase/pages/basics/hooks/view.dml +37 -0
  30. package/dist/templates/showcase/pages/basics/navigation/controller.ts +41 -0
  31. package/dist/templates/showcase/pages/basics/navigation/state.ts +11 -0
  32. package/dist/templates/showcase/pages/basics/navigation/view.dml +48 -0
  33. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +18 -0
  34. package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
  35. package/dist/templates/showcase/pages/basics/pick-genre/view.dml +10 -0
  36. package/dist/templates/showcase/pages/basics/share/concerts.ts +31 -0
  37. package/dist/templates/showcase/pages/basics/share/controller.ts +12 -0
  38. package/dist/templates/showcase/pages/basics/share/state.ts +5 -0
  39. package/dist/templates/showcase/pages/basics/share/view.dml +21 -0
  40. package/dist/{template/pages → templates/showcase/pages/basics}/state/controller.ts +1 -0
  41. package/dist/{template/pages → templates/showcase/pages/basics}/state/state.ts +1 -1
  42. package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
  43. package/dist/templates/showcase/pages/components/carousel/controller.ts +41 -0
  44. package/dist/templates/showcase/pages/components/carousel/state.ts +77 -0
  45. package/dist/templates/showcase/pages/components/carousel/view.dml +42 -0
  46. package/dist/templates/showcase/pages/components/feedback/controller.ts +32 -0
  47. package/dist/templates/showcase/pages/components/feedback/state.ts +41 -0
  48. package/dist/templates/showcase/pages/components/feedback/view.dml +116 -0
  49. package/dist/templates/showcase/pages/components/forms/controller.ts +72 -0
  50. package/dist/templates/showcase/pages/components/forms/state.ts +37 -0
  51. package/dist/templates/showcase/pages/components/forms/view.dml +97 -0
  52. package/dist/templates/showcase/pages/components/icons/controller.ts +15 -0
  53. package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
  54. package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
  55. package/dist/templates/showcase/pages/components/images/controller.ts +44 -0
  56. package/dist/{template/pages → templates/showcase/pages/components}/images/state.ts +3 -0
  57. package/dist/templates/showcase/pages/components/images/view.dml +83 -0
  58. package/dist/templates/showcase/pages/components/layout/controller.ts +61 -0
  59. package/dist/templates/showcase/pages/components/layout/state.ts +34 -0
  60. package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
  61. package/dist/templates/showcase/pages/components/lists/controller.ts +18 -0
  62. package/dist/templates/showcase/pages/components/lists/state.ts +130 -0
  63. package/dist/templates/showcase/pages/components/lists/view.dml +88 -0
  64. package/dist/templates/showcase/pages/components/money/controller.ts +45 -0
  65. package/dist/templates/showcase/pages/components/money/state.ts +13 -0
  66. package/dist/templates/showcase/pages/components/money/view.dml +54 -0
  67. package/dist/templates/showcase/pages/components/reading/controller.ts +6 -0
  68. package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
  69. package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
  70. package/dist/templates/showcase/pages/components/sheets/controller.ts +36 -0
  71. package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
  72. package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
  73. package/dist/templates/showcase/pages/components/styles/controller.ts +6 -0
  74. package/dist/templates/showcase/pages/components/styles/state.ts +3 -0
  75. package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
  76. package/dist/templates/showcase/pages/components/tabs/controller.ts +41 -0
  77. package/dist/templates/showcase/pages/components/tabs/state.ts +127 -0
  78. package/dist/templates/showcase/pages/components/tabs/view.dml +57 -0
  79. package/dist/templates/showcase/pages/components/text/controller.ts +10 -0
  80. package/dist/templates/showcase/pages/components/text/state.ts +3 -0
  81. package/dist/templates/showcase/pages/components/text/view.dml +95 -0
  82. package/dist/templates/showcase/pages/dash/account/controller.ts +32 -0
  83. package/dist/templates/showcase/pages/dash/account/state.ts +9 -0
  84. package/dist/templates/showcase/pages/dash/account/view.dml +33 -0
  85. package/dist/templates/showcase/pages/dash/cache/controller.ts +50 -0
  86. package/dist/templates/showcase/pages/dash/cache/state.ts +17 -0
  87. package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
  88. package/dist/templates/showcase/pages/dash/calls/controller.ts +67 -0
  89. package/dist/templates/showcase/pages/dash/calls/state.ts +9 -0
  90. package/dist/templates/showcase/pages/dash/calls/view.dml +69 -0
  91. package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
  92. package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
  93. package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
  94. package/dist/templates/showcase/pages/dash/crypto/controller.ts +38 -0
  95. package/dist/templates/showcase/pages/dash/crypto/state.ts +10 -0
  96. package/dist/templates/showcase/pages/dash/crypto/view.dml +48 -0
  97. package/dist/templates/showcase/pages/dash/files/controller.ts +71 -0
  98. package/dist/templates/showcase/pages/dash/files/state.ts +23 -0
  99. package/dist/templates/showcase/pages/dash/files/view.dml +78 -0
  100. package/dist/templates/showcase/pages/dash/media/controller.ts +65 -0
  101. package/dist/templates/showcase/pages/dash/media/state.ts +12 -0
  102. package/dist/templates/showcase/pages/dash/media/view.dml +53 -0
  103. package/dist/templates/showcase/pages/dash/network/controller.ts +215 -0
  104. package/dist/templates/showcase/pages/dash/network/state.ts +35 -0
  105. package/dist/templates/showcase/pages/dash/network/view.dml +108 -0
  106. package/dist/templates/showcase/pages/dash/secrets/controller.ts +44 -0
  107. package/dist/templates/showcase/pages/dash/secrets/state.ts +5 -0
  108. package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
  109. package/dist/templates/showcase/pages/dash/system/controller.ts +89 -0
  110. package/dist/templates/showcase/pages/dash/system/state.ts +13 -0
  111. package/dist/templates/showcase/pages/dash/system/view.dml +96 -0
  112. package/dist/templates/showcase/pages/dash/ui/controller.ts +70 -0
  113. package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
  114. package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
  115. package/dist/{template → templates/showcase}/pages/index/controller.ts +1 -1
  116. package/dist/templates/showcase/pages/index/state.ts +237 -0
  117. package/dist/templates/showcase/pages/index/view.dml +23 -0
  118. package/dist/templates/showcase/statics/logo.png +0 -0
  119. package/dist/{template → templates/starter}/.env +1 -1
  120. package/dist/templates/starter/dot.ts +3 -0
  121. package/dist/templates/starter/pages/index/controller.ts +9 -0
  122. package/dist/templates/starter/pages/index/state.ts +4 -0
  123. package/dist/templates/starter/pages/index/view.dml +16 -0
  124. package/package.json +8 -7
  125. package/dist/server-z7M4FLjf.js.map +0 -1
  126. package/dist/template/dot.ts +0 -19
  127. package/dist/template/pages/device/controller.ts +0 -70
  128. package/dist/template/pages/device/state.ts +0 -7
  129. package/dist/template/pages/device/view.dml +0 -44
  130. package/dist/template/pages/feedback/controller.ts +0 -16
  131. package/dist/template/pages/feedback/state.ts +0 -7
  132. package/dist/template/pages/feedback/view.dml +0 -87
  133. package/dist/template/pages/files/controller.ts +0 -47
  134. package/dist/template/pages/files/state.ts +0 -11
  135. package/dist/template/pages/files/view.dml +0 -35
  136. package/dist/template/pages/forms/controller.ts +0 -44
  137. package/dist/template/pages/forms/state.ts +0 -15
  138. package/dist/template/pages/forms/view.dml +0 -39
  139. package/dist/template/pages/images/controller.ts +0 -15
  140. package/dist/template/pages/images/view.dml +0 -38
  141. package/dist/template/pages/index/state.ts +0 -98
  142. package/dist/template/pages/index/view.dml +0 -20
  143. package/dist/template/pages/layout/controller.ts +0 -23
  144. package/dist/template/pages/layout/state.ts +0 -12
  145. package/dist/template/pages/layout/view.dml +0 -63
  146. package/dist/template/pages/lists/controller.ts +0 -36
  147. package/dist/template/pages/lists/state.ts +0 -96
  148. package/dist/template/pages/lists/view.dml +0 -59
  149. package/dist/template/pages/navigation/controller.ts +0 -42
  150. package/dist/template/pages/navigation/state.ts +0 -13
  151. package/dist/template/pages/navigation/view.dml +0 -24
  152. package/dist/template/pages/network/controller.ts +0 -107
  153. package/dist/template/pages/network/state.ts +0 -15
  154. package/dist/template/pages/network/view.dml +0 -56
  155. package/dist/template/pages/state/view.dml +0 -33
  156. package/dist/template/pages/text/controller.ts +0 -10
  157. package/dist/template/pages/text/state.ts +0 -7
  158. package/dist/template/pages/text/view.dml +0 -49
  159. /package/dist/{template → templates/starter}/statics/logo.png +0 -0
@@ -0,0 +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`.
3
+ GREETING=Welcome to Salone Sounds. This message comes from the dot's .env file.
@@ -0,0 +1,60 @@
1
+ import { logger } from '@monimesl/dot-runtime';
2
+
3
+ import { swatchColors } from './pages/basics/colors/palette';
4
+
5
+ export default defineDot({
6
+ // Folders under pages/, as the home page lists them. The first opens when the dot starts.
7
+ pages: [
8
+ 'index',
9
+ // How a page works.
10
+ 'basics/state',
11
+ 'basics/bindings',
12
+ 'basics/control-flow',
13
+ 'basics/events',
14
+ 'basics/navigation',
15
+ 'basics/pick-genre',
16
+ 'basics/hooks',
17
+ 'basics/bars',
18
+ 'basics/colors',
19
+ 'basics/share',
20
+ 'basics/concert',
21
+ // What a view can show.
22
+ 'components/layout',
23
+ 'components/styles',
24
+ 'components/text',
25
+ 'components/icons',
26
+ 'components/reading',
27
+ 'components/images',
28
+ 'components/carousel',
29
+ 'components/lists',
30
+ 'components/tabs',
31
+ 'components/forms',
32
+ 'components/money',
33
+ 'components/feedback',
34
+ 'components/sheets',
35
+ // The phone's features, through this.dash.
36
+ 'dash/ui',
37
+ 'dash/system',
38
+ 'dash/media',
39
+ 'dash/contacts',
40
+ 'dash/account',
41
+ 'dash/crypto',
42
+ 'dash/network',
43
+ 'dash/files',
44
+ 'dash/cache',
45
+ 'dash/secrets',
46
+ 'dash/calls',
47
+ ],
48
+ // The showcase's own colours, used by name on the Styles page, and the Page colours swatches.
49
+ theme: {
50
+ colors: {
51
+ custom: {
52
+ gold: { light: '#f5c542', dark: '#d4a72c' },
53
+ // Dark text that reads on gold in both light and dark.
54
+ ink: { light: '#1c1c1e', dark: '#1c1c1e' },
55
+ ...swatchColors(),
56
+ },
57
+ },
58
+ },
59
+ plugins: [logger()],
60
+ });
@@ -0,0 +1,47 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ // What Dash's bars show when the page opens, on top of the configs in dot.ts.
6
+ configs: { navigationBar: { title: 'Bars' } },
7
+ mutations: {
8
+ setTitle: (_state, event: ValueEvent) => ({ title: event.detail.value }),
9
+ },
10
+ actions: {
11
+ // While the page runs, setNavigationBar changes what configs set.
12
+ showTitle(state) {
13
+ this.setNavigationBar({ title: state.title || undefined });
14
+ },
15
+ toggleColors(state) {
16
+ const colored = !state.colored;
17
+ // Undefined puts Dash's own colours back. Without a textColor, Dash picks text that reads on
18
+ // the bar.
19
+ this.setNavigationBar({
20
+ // The green of Sierra Leone's flag.
21
+ backgroundColor: colored ? '#1EB53A' : undefined,
22
+ });
23
+ this.setState({ colored });
24
+ },
25
+ togglePainted(state) {
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.
29
+ if (painted) {
30
+ this.setColors({ background: { light: '#E3F2FD', dark: '#0B1A2E' } });
31
+ } else {
32
+ this.setColors({ background: { light: undefined, dark: undefined } });
33
+ }
34
+ this.setState({ painted });
35
+ },
36
+ toggleNavigationBar(state) {
37
+ const navigationBarHidden = !state.navigationBarHidden;
38
+ this.setNavigationBar({ hidden: navigationBarHidden });
39
+ this.setState({ navigationBarHidden });
40
+ },
41
+ toggleTabBar(state) {
42
+ const tabBarHidden = !state.tabBarHidden;
43
+ this.setTabBar({ hidden: tabBarHidden });
44
+ this.setState({ tabBarHidden });
45
+ },
46
+ },
47
+ });
@@ -0,0 +1,7 @@
1
+ export default {
2
+ title: 'Bars',
3
+ colored: false,
4
+ painted: false,
5
+ navigationBarHidden: false,
6
+ tabBarHidden: false,
7
+ };
@@ -0,0 +1,36 @@
1
+ <View>
2
+ <Text variant="title">Dash's bars</Text>
3
+ <Text>Dash shows a navigation bar above the dot and a tab bar below it. Each page can change them. Going back shows the page below with its own bars, and leaving the dot brings back Dash's.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">configs</Text>
7
+ <Text variant="caption" tone="muted">definePage({ configs }) sets the bars when the page opens. This page's title is "Bars". defineDot({ configs }) sets them for every page.</Text>
8
+ </View>
9
+
10
+ <View gap="small">
11
+ <Text variant="subtitle">setNavigationBar</Text>
12
+ <View variant="card" gap="small">
13
+ <Input label="Title" placeholder="Bars" value="{{ title }}" maxLength="30" onChange="setTitle" onSubmit="showTitle" />
14
+ <View direction="row" wrap gap="small">
15
+ <Button variant="secondary" onClick="showTitle">Use this title</Button>
16
+ <Button variant="secondary" onClick="toggleColors">{{ colored ? 'Plain bar' : 'Green bar' }}</Button>
17
+ <Button variant="secondary" onClick="toggleNavigationBar">{{ navigationBarHidden ? 'Show the bar' : 'Hide the bar' }}</Button>
18
+ </View>
19
+ </View>
20
+ </View>
21
+
22
+ <View gap="small">
23
+ <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
+ <View variant="card">
26
+ <Button variant="secondary" onClick="togglePainted">{{ painted ? 'Theme background' : 'Sky background' }}</Button>
27
+ </View>
28
+ </View>
29
+
30
+ <View gap="small">
31
+ <Text variant="subtitle">setTabBar</Text>
32
+ <View variant="card">
33
+ <Button variant="secondary" onClick="toggleTabBar">{{ tabBarHidden ? 'Show the tab bar' : 'Hide the tab bar' }}</Button>
34
+ </View>
35
+ </View>
36
+ </View>
@@ -0,0 +1,15 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ configs: { navigationBar: { title: 'Bindings' } },
6
+ mutations: {
7
+ addTicket: (state) => ({ tickets: state.tickets + 1 }),
8
+ removeTicket: (state) => ({ tickets: Math.max(0, state.tickets - 1) }),
9
+ toggleStageName: (state) => ({ stageName: state.stageName === null ? 'Lady M' : null }),
10
+ switchName: (state) => {
11
+ const name = state.name === 'Mariama' ? 'Fatmata' : 'Mariama';
12
+ return { name, shout: name.toUpperCase() };
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,14 @@
1
+ export default {
2
+ // Kept in state, so the view shows the braces rather than reading them as a binding.
3
+ syntax: '{{ name }}',
4
+ name: 'Mariama',
5
+ stageName: null as string | null,
6
+ // Le, in the leone since 2022, when it dropped three zeros.
7
+ price: 150,
8
+ tickets: 4,
9
+ genres: ['kriofusion', 'palm-wine', 'gumbe'],
10
+ // state.ts runs in the service, so it can read the .env file's values too.
11
+ greeting: env.GREETING,
12
+ // Worked out in the controller, since a binding can't call a function.
13
+ shout: 'MARIAMA',
14
+ };
@@ -0,0 +1,41 @@
1
+ <View>
2
+ <Text variant="title">Bindings</Text>
3
+ <Text>Write {{ syntax }} in view.dml to show a value from the page's state. The view changes when the state does.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">Values</Text>
7
+ <View variant="card" gap="small">
8
+ <Text>Hello, {{ name }}</Text>
9
+ <Text>{{ genres.length }} favourite genres. The first is {{ genres[0] }}.</Text>
10
+ <Button variant="secondary" onClick="switchName">Change the name</Button>
11
+ </View>
12
+ </View>
13
+
14
+ <View gap="small">
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>
17
+ <View variant="card" gap="small">
18
+ <Text>{{ tickets }} {{ tickets === 1 ? 'ticket' : 'tickets' }} at Le {{ price }} each: Le {{ tickets * price }}</Text>
19
+ <Text>Stage name: {{ stageName ?? 'none yet' }}</Text>
20
+ <Text>Shouted: {{ shout }}!</Text>
21
+ </View>
22
+ </View>
23
+
24
+ <View gap="small">
25
+ <Text variant="subtitle">In attributes</Text>
26
+ <Text variant="caption" tone="muted">A binding can set any attribute, such as disabled, or the text on a button.</Text>
27
+ <View variant="card" direction="row" wrap gap="small">
28
+ <Button variant="secondary" onClick="addTicket">Add a ticket</Button>
29
+ <Button variant="secondary" onClick="removeTicket" disabled="{{ tickets === 0 }}">Remove one</Button>
30
+ <Button variant="secondary" onClick="toggleStageName">{{ stageName === null ? 'Give a stage name' : 'Drop the stage name' }}</Button>
31
+ </View>
32
+ </View>
33
+
34
+ <View gap="small">
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>
37
+ <View variant="card">
38
+ <Text>{{ greeting }}</Text>
39
+ </View>
40
+ </View>
41
+ </View>
@@ -0,0 +1,112 @@
1
+ import type { HexColor, PageColors, SchemeColor } from '@monimesl/dot-runtime';
2
+
3
+ import type { Swatch } from './palette';
4
+ import { BACKGROUNDS, BAR_COLORS, OWN_SURFACES, PRIMARIES } from './palette';
5
+ import state from './state';
6
+
7
+ const HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
8
+ // Undefined for light and dark puts the theme's colour back.
9
+ const THEME: SchemeColor = { light: undefined, dark: undefined };
10
+
11
+ interface Choices {
12
+ background: string;
13
+ primary: string;
14
+ surfaces: string;
15
+ customLight: string;
16
+ customDark: string;
17
+ }
18
+
19
+ export default definePage({
20
+ initialState: state,
21
+ configs: { navigationBar: { title: 'Page colours' } },
22
+ mutations: {
23
+ setCustomLight: (_state, event: ValueEvent) => ({ customLight: event.detail.value }),
24
+ setCustomDark: (_state, event: ValueEvent) => ({ customDark: event.detail.value }),
25
+ },
26
+ actions: {
27
+ chooseBackground(state, event: DataEvent<{ name: string }>) {
28
+ const choices = { ...state, background: event.data.name };
29
+ this.setColors(colorsFor(choices));
30
+ this.setState({ background: choices.background, code: codeFor(choices) });
31
+ },
32
+ useCustom(state) {
33
+ const light = state.customLight.trim();
34
+ // One colour is enough: it's used in dark too.
35
+ const dark = state.customDark.trim() || light;
36
+ if (!HEX_COLOR.test(light) || !HEX_COLOR.test(dark)) {
37
+ this.setState({ customError: 'Write a colour as # and six digits, such as #FFF3E0' });
38
+ return;
39
+ }
40
+ const choices = { ...state, background: 'custom', customLight: light, customDark: dark };
41
+ this.setColors(colorsFor(choices));
42
+ this.setState({ background: 'custom', customError: '', code: codeFor(choices) });
43
+ },
44
+ choosePrimary(state, event: DataEvent<{ name: string }>) {
45
+ const choices = { ...state, primary: event.data.name };
46
+ this.setColors(colorsFor(choices));
47
+ this.setState({ primary: choices.primary, code: codeFor(choices) });
48
+ },
49
+ chooseSurfaces(state, event: ValueEvent) {
50
+ const choices = { ...state, surfaces: event.detail.value };
51
+ this.setColors(colorsFor(choices));
52
+ this.setState({ surfaces: choices.surfaces, code: codeFor(choices) });
53
+ },
54
+ chooseNavigationBar(_state, event: DataEvent<{ name: string }>) {
55
+ const color = BAR_COLORS.find((swatch) => swatch.name === event.data.name);
56
+ // Without a textColor, Dash picks text that reads on the bar.
57
+ this.setNavigationBar({ backgroundColor: color?.light });
58
+ this.setState({ navigationBar: event.data.name });
59
+ },
60
+ reset(state) {
61
+ const choices = { ...state, background: '', primary: '', surfaces: 'derived' };
62
+ this.setColors(colorsFor(choices));
63
+ this.setNavigationBar({ backgroundColor: undefined });
64
+ this.setState({
65
+ background: '',
66
+ primary: '',
67
+ surfaces: 'derived',
68
+ navigationBar: '',
69
+ customError: '',
70
+ code: codeFor(choices),
71
+ });
72
+ },
73
+ },
74
+ });
75
+
76
+ // Every colour the page can change here, so one put back to the theme's is cleared too.
77
+ function colorsFor(choices: Choices): PageColors {
78
+ const own = choices.surfaces === 'own';
79
+ return {
80
+ background: backgroundFor(choices),
81
+ primary: swatchFor(PRIMARIES, choices.primary),
82
+ surface: own ? OWN_SURFACES.surface : THEME,
83
+ muted: own ? OWN_SURFACES.muted : THEME,
84
+ border: own ? OWN_SURFACES.border : THEME,
85
+ };
86
+ }
87
+
88
+ function backgroundFor(choices: Choices): SchemeColor {
89
+ if (choices.background === 'custom') {
90
+ return { light: choices.customLight as HexColor, dark: choices.customDark as HexColor };
91
+ }
92
+ return swatchFor(BACKGROUNDS, choices.background);
93
+ }
94
+
95
+ function swatchFor(swatches: readonly Swatch[], name: string): SchemeColor {
96
+ const swatch = swatches.find((candidate) => candidate.name === name);
97
+ if (!swatch) {
98
+ return THEME;
99
+ }
100
+ return { light: swatch.light, dark: swatch.dark };
101
+ }
102
+
103
+ // The colours chosen, one line each, as they go in a setColors call.
104
+ function codeFor(choices: Choices): string[] {
105
+ const lines: string[] = [];
106
+ for (const [name, color] of Object.entries(colorsFor(choices))) {
107
+ if (color?.light) {
108
+ lines.push(`${name}: { light: '${color.light}', dark: '${color.dark}' },`);
109
+ }
110
+ }
111
+ return lines;
112
+ }
@@ -0,0 +1,52 @@
1
+ import type { HexColor, SchemeColor } from '@monimesl/dot-runtime';
2
+
3
+ // Colours the Page colours page offers, each for light and dark. dot.ts names them in the theme too,
4
+ // so the page can show a swatch of each.
5
+ export interface Swatch {
6
+ name: string;
7
+ label: string;
8
+ light: HexColor;
9
+ dark: HexColor;
10
+ }
11
+
12
+ export const BACKGROUNDS: Swatch[] = [
13
+ { name: 'sky', label: 'Freetown sky', light: '#E3F2FD', dark: '#0B1A2E' },
14
+ { name: 'sand', label: 'Lumley Beach', light: '#FBF1DC', dark: '#2A2416' },
15
+ { name: 'palm', label: 'Tacugama', light: '#E6F4EA', dark: '#0F2417' },
16
+ { name: 'sunset', label: 'Bureh sunset', light: '#FFF3E0', dark: '#2B1A0E' },
17
+ { name: 'lagoon', label: 'Tokeh lagoon', light: '#DDF3F2', dark: '#0B2426' },
18
+ { name: 'kola', label: 'Kola nut', light: '#F6E3E1', dark: '#2A1312' },
19
+ // Strong colours, where Dash turns the text and bars light.
20
+ { name: 'flagGreen', label: 'Flag green', light: '#1EB53A', dark: '#12832A' },
21
+ { name: 'atlantic', label: 'Atlantic', light: '#0072C6', dark: '#0A3D66' },
22
+ ];
23
+
24
+ export const PRIMARIES: Swatch[] = [
25
+ { name: 'flagBlue', label: 'Flag blue', light: '#0072C6', dark: '#4DA3E8' },
26
+ { name: 'kolaRed', label: 'Kola red', light: '#B23A2B', dark: '#E0705F' },
27
+ { name: 'palmGreen', label: 'Palm green', light: '#1E7A3A', dark: '#5CC27A' },
28
+ { name: 'diamond', label: 'Diamond gold', light: '#9A6B00', dark: '#F5C542' },
29
+ ];
30
+
31
+ // Bars take one colour, for light and dark alike.
32
+ export const BAR_COLORS: Swatch[] = [
33
+ { name: 'flagGreen', label: 'Flag green', light: '#1EB53A', dark: '#1EB53A' },
34
+ { name: 'atlantic', label: 'Atlantic', light: '#0072C6', dark: '#0072C6' },
35
+ { name: 'ink', label: 'Ink', light: '#1C1C1E', dark: '#1C1C1E' },
36
+ ];
37
+
38
+ // What a page might set for its cards, quiet fills and lines, in place of what Dash works out.
39
+ export const OWN_SURFACES: Record<'surface' | 'muted' | 'border', SchemeColor> = {
40
+ surface: { light: '#FFFFFF', dark: '#16181B' },
41
+ muted: { light: '#EEF1F4', dark: '#25282C' },
42
+ border: { light: '#C9D1D9', dark: '#3A3F45' },
43
+ };
44
+
45
+ // The theme's custom colours for the swatches, by name.
46
+ export function swatchColors(): Record<string, SchemeColor> {
47
+ const colors: Record<string, SchemeColor> = {};
48
+ for (const swatch of [...BACKGROUNDS, ...PRIMARIES]) {
49
+ colors[swatch.name] = { light: swatch.light, dark: swatch.dark };
50
+ }
51
+ return colors;
52
+ }
@@ -0,0 +1,17 @@
1
+ import { BACKGROUNDS, PRIMARIES } from './palette';
2
+
3
+ export default {
4
+ backgrounds: BACKGROUNDS,
5
+ primaries: PRIMARIES,
6
+ // The swatch chosen for each, or '' for the theme's own.
7
+ background: '',
8
+ primary: '',
9
+ navigationBar: '',
10
+ // 'derived' lets Dash work out cards and fields from the background. 'own' sets them.
11
+ surfaces: 'derived',
12
+ customLight: '',
13
+ customDark: '',
14
+ customError: '',
15
+ // The colours the choices add up to, as lines of a setColors call.
16
+ code: [] as string[],
17
+ };
@@ -0,0 +1,142 @@
1
+ <View>
2
+ <Text variant="title">Page colours</Text>
3
+ <Text>A page can have its own colours, for light and dark, on top of the theme's. Pick some and watch the page follow. Dash paints the background behind the page, and picks text, bar and status bar colours that read on it. Try each with the phone in dark mode too.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">background</Text>
7
+ <Text variant="caption" tone="muted">Each swatch has a light and a dark colour. On the strong ones, Dash turns the text and the status bar light.</Text>
8
+ <View variant="card">
9
+ <Grid columns="3" gap="medium">
10
+ <View align="center" gap="small" data-name="" onTap="chooseBackground">
11
+ <View width="52" height="52" radius="full" background="muted" border="{{ background === '' ? 'strong' : 'subtle' }}" justify="center" align="center">
12
+ <Icon name="refresh" tone="muted" />
13
+ </View>
14
+ <Text variant="caption" align="center" weight="{{ background === '' ? 'bold' : 'regular' }}">Theme's</Text>
15
+ </View>
16
+ <For each="{{ backgrounds }}" item="swatch" key="name">
17
+ <View align="center" gap="small" data-name="{{ swatch.name }}" onTap="chooseBackground">
18
+ <View width="52" height="52" radius="full" background="{{ swatch.name }}" border="{{ background === swatch.name ? 'strong' : 'subtle' }}" />
19
+ <Text variant="caption" align="center" maxLines="2" weight="{{ background === swatch.name ? 'bold' : 'regular' }}">{{ swatch.label }}</Text>
20
+ </View>
21
+ </For>
22
+ </Grid>
23
+ </View>
24
+ </View>
25
+
26
+ <View gap="small">
27
+ <Text variant="subtitle">Your own background</Text>
28
+ <Text variant="caption" tone="muted">Any colour as # and six digits. Leave dark empty to use the light one in dark too. If text can't be read on it, dotctl dev warns you.</Text>
29
+ <View variant="card" gap="small">
30
+ <View direction="row" gap="small">
31
+ <View grow>
32
+ <Input label="Light" placeholder="#FFF3E0" value="{{ customLight }}" maxLength="7" onChange="setCustomLight" onSubmit="useCustom" />
33
+ </View>
34
+ <View grow>
35
+ <Input label="Dark" placeholder="#2B1A0E" value="{{ customDark }}" maxLength="7" onChange="setCustomDark" onSubmit="useCustom" />
36
+ </View>
37
+ </View>
38
+ <If condition="{{ customError }}">
39
+ <Text variant="caption" tone="danger">{{ customError }}</Text>
40
+ </If>
41
+ <Button variant="secondary" onClick="useCustom" disabled="{{ !customLight }}">Use this background</Button>
42
+ </View>
43
+ </View>
44
+
45
+ <View gap="small">
46
+ <Text variant="subtitle">primary</Text>
47
+ <Text variant="caption" tone="muted">Buttons, switches, links and selected chips. Text on it, with tone="onPrimary", turns dark or light to read.</Text>
48
+ <View variant="card">
49
+ <Grid columns="5" gap="small">
50
+ <View align="center" gap="small" data-name="" onTap="choosePrimary">
51
+ <View width="40" height="40" radius="full" background="muted" border="{{ primary === '' ? 'strong' : 'subtle' }}" justify="center" align="center">
52
+ <Icon name="refresh" size="small" tone="muted" />
53
+ </View>
54
+ <Text variant="caption" align="center" weight="{{ primary === '' ? 'bold' : 'regular' }}">Theme's</Text>
55
+ </View>
56
+ <For each="{{ primaries }}" item="swatch" key="name">
57
+ <View align="center" gap="small" data-name="{{ swatch.name }}" onTap="choosePrimary">
58
+ <View width="40" height="40" radius="full" background="{{ swatch.name }}" border="{{ primary === swatch.name ? 'strong' : 'none' }}" />
59
+ <Text variant="caption" align="center" maxLines="2" weight="{{ primary === swatch.name ? 'bold' : 'regular' }}">{{ swatch.label }}</Text>
60
+ </View>
61
+ </For>
62
+ </Grid>
63
+ </View>
64
+ </View>
65
+
66
+ <View gap="small">
67
+ <Text variant="subtitle">surface, muted and border</Text>
68
+ <Text variant="caption" tone="muted">Left out, Dash works out cards, fields, quiet fills and lines from the background, so they sit well on it. A page can set them itself instead.</Text>
69
+ <Tabs variant="segmented" value="{{ surfaces }}" onChange="chooseSurfaces">
70
+ <Tab value="derived" label="Worked out" />
71
+ <Tab value="own" label="The page's own" />
72
+ </Tabs>
73
+ </View>
74
+
75
+ <View gap="small">
76
+ <Text variant="subtitle">The navigation bar</Text>
77
+ <Text variant="caption" tone="muted">setNavigationBar gives the bar its own colour. Without a textColor, Dash picks text that reads on it.</Text>
78
+ <View direction="row" gap="small" wrap>
79
+ <Chip label="Dash's" selected="{{ navigationBar === '' }}" data-name="" onTap="chooseNavigationBar" />
80
+ <Chip label="Flag green" selected="{{ navigationBar === 'flagGreen' }}" data-name="flagGreen" onTap="chooseNavigationBar" />
81
+ <Chip label="Atlantic" selected="{{ navigationBar === 'atlantic' }}" data-name="atlantic" onTap="chooseNavigationBar" />
82
+ <Chip label="Ink" selected="{{ navigationBar === 'ink' }}" data-name="ink" onTap="chooseNavigationBar" />
83
+ </View>
84
+ </View>
85
+
86
+ <View gap="small">
87
+ <Text variant="subtitle">What follows</Text>
88
+ <Text variant="caption" tone="muted">Everything on the page takes its colours from these, so nothing here sets one.</Text>
89
+ <View variant="card" gap="small">
90
+ <View direction="row" align="center" gap="medium">
91
+ <Avatar name="Emmerson Bockarie" />
92
+ <View gap="none" grow>
93
+ <Text weight="bold">Emmerson</Text>
94
+ <Text variant="caption" tone="muted">Kill Me Pikin · 2008</Text>
95
+ </View>
96
+ <Badge tone="success">New</Badge>
97
+ </View>
98
+ <Input label="Your name" placeholder="Aminata Kamara" />
99
+ <Switch label="Tell me about new songs" checked />
100
+ <ProgressBar value="60" />
101
+ <View direction="row" gap="small" wrap>
102
+ <Chip label="Afropop" selected />
103
+ <Chip label="Gumbe" />
104
+ <Chip label="Palm-wine" />
105
+ </View>
106
+ <View direction="row" gap="small" wrap>
107
+ <Button>Play</Button>
108
+ <Button variant="secondary">Add to a playlist</Button>
109
+ </View>
110
+ <Text>Read about <Link href="https://en.wikipedia.org/wiki/Music_of_Sierra_Leone">the music of Sierra Leone</Link>.</Text>
111
+ </View>
112
+ <List>
113
+ <ListItem icon="music" title="Ballot Paper" subtitle="Emmerson" value="4:12" />
114
+ <ListItem icon="music" title="Salone" subtitle="Drizilik" value="3:48" />
115
+ </List>
116
+ <View background="muted" radius="medium" padding="medium">
117
+ <Text variant="caption">A quiet fill, such as for a note</Text>
118
+ </View>
119
+ <View background="primary" radius="medium" padding="medium">
120
+ <Text tone="onPrimary" weight="medium">On the primary colour</Text>
121
+ </View>
122
+ </View>
123
+
124
+ <View gap="small">
125
+ <Text variant="subtitle">The call</Text>
126
+ <Text variant="caption" tone="muted">What you picked, as a dot would write it. configs.colors in definePage takes the same.</Text>
127
+ <View variant="card" gap="none">
128
+ <If condition="{{ code.length > 0 }}">
129
+ <Text selectable>this.setColors({</Text>
130
+ <For each="{{ code }}" item="line" key="*this">
131
+ <Text paddingStart="large" selectable>{{ line }}</Text>
132
+ </For>
133
+ <Text selectable>});</Text>
134
+ </If>
135
+ <Else>
136
+ <Text tone="muted">The theme's own colours. Pick some above to see the call.</Text>
137
+ </Else>
138
+ </View>
139
+ </View>
140
+
141
+ <Button variant="secondary" onClick="reset">Put the theme's colours back</Button>
142
+ </View>
@@ -0,0 +1,16 @@
1
+ import { CONCERTS } from '../share/concerts';
2
+ import state from './state';
3
+
4
+ export default definePage({
5
+ initialState: state,
6
+ configs: { navigationBar: { title: 'Concert' } },
7
+ // Dash's Share opens this page, with its concert option and no other.
8
+ share: { options: ['concert'] },
9
+ hooks: {
10
+ // From a link, the options can be anything, so look the concert up rather than trust it.
11
+ onLoad(options) {
12
+ const concert = CONCERTS.find((candidate) => candidate.id === options.concert) ?? null;
13
+ this.setState({ concert, buyer: options.buyer ?? '' });
14
+ },
15
+ },
16
+ });
@@ -0,0 +1,7 @@
1
+ import type { Concert } from '../share/concerts';
2
+
3
+ export default {
4
+ concert: null as Concert | null,
5
+ // Who the page was opened for. A shared link never carries it.
6
+ buyer: '',
7
+ };
@@ -0,0 +1,20 @@
1
+ <View>
2
+ <If condition="{{ concert }}">
3
+ <Text variant="title">{{ concert.title }}</Text>
4
+ <View variant="card" gap="small">
5
+ <DetailRow label="Where" value="{{ concert.place }}" />
6
+ <DetailRow label="When" value="{{ concert.date }}" />
7
+ <DetailRow label="From" value="{{ concert.price }}" />
8
+ </View>
9
+ <If condition="{{ buyer }}">
10
+ <Notice tone="info">Opened for {{ buyer }}. buyer isn't in share's options, so a shared link leaves it out.</Notice>
11
+ </If>
12
+ <Else>
13
+ <Notice tone="success">Opened from a link: it carried the concert, and nothing else.</Notice>
14
+ </Else>
15
+ <Text variant="caption" tone="muted">Tap ••• then Share. The link opens this concert.</Text>
16
+ </If>
17
+ <Else>
18
+ <EmptyState icon="search" title="No such concert" text="The link may be old, or written by hand." />
19
+ </Else>
20
+ </View>
@@ -0,0 +1,18 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ configs: { navigationBar: { title: 'If and For' } },
6
+ mutations: {
7
+ // Sells five tickets for the show the button belongs to.
8
+ sell: (state, event: DataEvent<{ id: string }>) => ({
9
+ shows: state.shows.map((show) =>
10
+ show.id === event.data.id ? { ...show, left: Math.max(0, show.left - 5) } : show,
11
+ ),
12
+ }),
13
+ restock: (state) => ({ shows: state.shows.map((show) => ({ ...show, left: 20 })) }),
14
+ toggleSignedIn: (state) => ({ signedIn: !state.signedIn }),
15
+ // Changes the list's order. The key keeps each row's place right.
16
+ shuffle: (state) => ({ genres: [...state.genres.slice(1), state.genres[0]!] }),
17
+ },
18
+ });
@@ -0,0 +1,16 @@
1
+ export interface Show {
2
+ id: string;
3
+ title: string;
4
+ city: string;
5
+ left: number;
6
+ }
7
+
8
+ export default {
9
+ shows: [
10
+ { id: 'freetown', title: 'Salone Sounds Live', city: 'Freetown', left: 120 },
11
+ { id: 'bo', title: 'Palm-wine evening', city: 'Bo', left: 6 },
12
+ { id: 'kenema', title: 'Gospel night', city: 'Kenema', left: 1 },
13
+ ] as Show[],
14
+ genres: ['Palm-wine', 'Gumbe', 'Milo jazz', 'Afropop', 'Kriofusion'],
15
+ signedIn: false,
16
+ };