@monimesl/dot-simulator 0.1.0-alpha.3 → 0.1.0-alpha.4

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.
@@ -15,6 +15,21 @@
15
15
  }
16
16
  return registry;
17
17
  }
18
+ const AmountInput = defineComponent({
19
+ name: "AmountInput",
20
+ props: {
21
+ value: "number",
22
+ currency: "string",
23
+ decimals: "number",
24
+ label: "string",
25
+ placeholder: "string",
26
+ hint: "string",
27
+ error: "string",
28
+ disabled: "boolean"
29
+ },
30
+ events: ["change"],
31
+ children: "none"
32
+ });
18
33
  const Button = defineComponent({
19
34
  name: "Button",
20
35
  props: {
@@ -25,15 +40,17 @@
25
40
  events: ["click"],
26
41
  children: "text"
27
42
  });
28
- const Divider = defineComponent({
29
- name: "Divider",
30
- children: "none"
31
- });
32
43
  const SIZES = [
33
44
  "small",
34
45
  "medium",
35
46
  "large"
36
47
  ];
48
+ const SPACES = [
49
+ "none",
50
+ "small",
51
+ "medium",
52
+ "large"
53
+ ];
37
54
  const TONES = [
38
55
  "default",
39
56
  "muted",
@@ -111,6 +128,58 @@
111
128
  "water",
112
129
  "wifi"
113
130
  ];
131
+ const Carousel = defineComponent({
132
+ name: "Carousel",
133
+ props: {
134
+ autoplay: "boolean",
135
+ interval: "number",
136
+ loop: "boolean",
137
+ indicator: ["dots", "none"],
138
+ gap: SPACES,
139
+ effect: ["slide", "scale"],
140
+ index: "number"
141
+ },
142
+ events: ["change"],
143
+ children: "elements"
144
+ });
145
+ const Chip = defineComponent({
146
+ name: "Chip",
147
+ props: {
148
+ label: "string",
149
+ icon: ICON_NAMES,
150
+ selected: "boolean",
151
+ disabled: "boolean"
152
+ },
153
+ events: ["tap"],
154
+ children: "none"
155
+ });
156
+ const CodeInput = defineComponent({
157
+ name: "CodeInput",
158
+ props: {
159
+ value: "string",
160
+ length: "number",
161
+ label: "string",
162
+ hint: "string",
163
+ error: "string",
164
+ secure: "boolean",
165
+ disabled: "boolean"
166
+ },
167
+ events: ["change", "complete"],
168
+ children: "none"
169
+ });
170
+ const Divider = defineComponent({
171
+ name: "Divider",
172
+ children: "none"
173
+ });
174
+ const EmptyState = defineComponent({
175
+ name: "EmptyState",
176
+ props: {
177
+ icon: ICON_NAMES,
178
+ title: "string",
179
+ text: "string"
180
+ },
181
+ children: "elements"
182
+ });
114
183
  const Spinner = defineComponent({
115
184
  name: "Spinner",
116
185
  props: { size: SIZES },
@@ -138,6 +207,25 @@
138
207
  name: "Footer",
139
208
  children: "elements"
140
209
  });
210
+ const Grid = defineComponent({
211
+ name: "Grid",
212
+ props: {
213
+ columns: "number",
214
+ gap: SPACES
215
+ },
216
+ children: "elements"
217
+ });
218
+ const GridItem = defineComponent({
219
+ name: "GridItem",
220
+ props: {
221
+ label: "string",
222
+ icon: ICON_NAMES,
223
+ image: "string",
224
+ badge: "string"
225
+ },
226
+ events: ["tap"],
227
+ children: "none"
228
+ });
141
229
  const Icon = defineComponent({
142
230
  name: "Icon",
143
231
  props: {
@@ -223,6 +311,15 @@
223
311
  events: ["hide"],
224
312
  children: "none"
225
313
  });
314
+ const QRCode = defineComponent({
315
+ name: "QRCode",
316
+ props: {
317
+ value: "string",
318
+ size: "number",
319
+ label: "string"
320
+ },
321
+ children: "none"
322
+ });
226
323
  const Select = defineComponent({
227
324
  name: "Select",
228
325
  props: {
@@ -257,6 +354,24 @@
257
354
  },
258
355
  children: "none"
259
356
  });
357
+ const Tabs = defineComponent({
358
+ name: "Tabs",
359
+ props: {
360
+ value: "string",
361
+ variant: ["underline", "segmented"]
362
+ },
363
+ events: ["change"],
364
+ children: "elements"
365
+ });
366
+ const Tab = defineComponent({
367
+ name: "Tab",
368
+ props: {
369
+ value: "string",
370
+ label: "string",
371
+ badge: "number"
372
+ },
373
+ children: "none"
374
+ });
260
375
  const Text = defineComponent({
261
376
  name: "Text",
262
377
  props: {
@@ -326,27 +441,19 @@
326
441
  events: ["change"],
327
442
  children: "elements"
328
443
  });
329
- const Radio = defineComponent({
330
- name: "Radio",
331
- props: {
332
- value: "string",
333
- label: "string",
334
- disabled: "boolean"
335
- },
336
- children: "none"
337
- });
338
- const SPACE = [
339
- "none",
340
- "small",
341
- "medium",
342
- "large"
343
- ];
344
444
  const builtinComponents = {
445
+ AmountInput,
345
446
  Badge,
346
447
  Button,
448
+ Carousel,
347
449
  Checkbox,
450
+ Chip,
451
+ CodeInput,
348
452
  Divider,
453
+ EmptyState,
349
454
  Footer,
455
+ Grid,
456
+ GridItem,
350
457
  Icon,
351
458
  Image,
352
459
  Input,
@@ -355,20 +462,31 @@
355
462
  Notice,
356
463
  Option,
357
464
  ProgressBar,
358
- Radio,
465
+ QRCode,
466
+ Radio: defineComponent({
467
+ name: "Radio",
468
+ props: {
469
+ value: "string",
470
+ label: "string",
471
+ disabled: "boolean"
472
+ },
473
+ children: "none"
474
+ }),
359
475
  RadioGroup,
360
476
  Select,
361
477
  Sheet,
362
478
  Skeleton,
363
479
  Spinner,
364
480
  Switch,
481
+ Tab,
482
+ Tabs,
365
483
  Text,
366
484
  TextArea,
367
485
  Toast,
368
486
  View: defineComponent({
369
487
  name: "View",
370
488
  props: {
371
- gap: SPACE,
489
+ gap: SPACES,
372
490
  direction: ["column", "row"],
373
491
  align: [
374
492
  "stretch",
@@ -384,11 +502,11 @@
384
502
  ],
385
503
  wrap: "boolean",
386
504
  grow: "boolean",
387
- padding: SPACE,
388
- paddingTop: SPACE,
389
- paddingStart: SPACE,
390
- paddingEnd: SPACE,
391
- paddingBottom: SPACE,
505
+ padding: SPACES,
506
+ paddingTop: SPACES,
507
+ paddingStart: SPACES,
508
+ paddingEnd: SPACES,
509
+ paddingBottom: SPACES,
392
510
  variant: ["plain", "card"]
393
511
  },
394
512
  events: ["tap"],
@@ -647,7 +765,7 @@
647
765
  };
648
766
  //#endregion
649
767
  //#region src/host.ts
650
- function startHost({ logic, ui, manifest, dashVersion, minRuntimeVersion, systemContext, handlers = {}, askPermission, observe }) {
768
+ function startHost({ logic, ui, manifest, dashVersion, minRuntimeVersion, systemContext, handlers = {}, askPermission, observe, restorePages = [], onBarsChange }) {
651
769
  checkCompatibility(manifest, {
652
770
  dashVersion,
653
771
  minRuntimeVersion
@@ -667,6 +785,18 @@
667
785
  ui.post(message);
668
786
  logic.post(message);
669
787
  };
788
+ const barsByInstance = /* @__PURE__ */ new Map();
789
+ const showBars = () => {
790
+ const top = stack.top;
791
+ const bars = top && barsByInstance.get(top.instanceId);
792
+ onBarsChange?.({
793
+ title: bars?.navigationBar.title ?? manifest.name,
794
+ navigationBarHidden: bars?.navigationBar.hidden ?? false,
795
+ navigationBarColor: bars?.navigationBar.backgroundColor,
796
+ navigationBarTextColor: bars?.navigationBar.textColor,
797
+ tabBarHidden: bars?.tabBar.hidden ?? false
798
+ }, stack.pages);
799
+ };
670
800
  const syncStack = () => {
671
801
  const message = {
672
802
  type: "syncStack",
@@ -676,6 +806,9 @@
676
806
  }
677
807
  };
678
808
  broadcast(message);
809
+ const open = new Set(stack.pages.map((entry) => entry.instanceId));
810
+ for (const instanceId of barsByInstance.keys()) if (!open.has(instanceId)) barsByInstance.delete(instanceId);
811
+ showBars();
679
812
  };
680
813
  const syncSystemContext = () => {
681
814
  broadcast({
@@ -691,13 +824,40 @@
691
824
  if (stack.top !== previousTop) calls.dismissUI();
692
825
  return true;
693
826
  };
827
+ const openFirstPages = () => {
828
+ const [first, ...rest] = restorePages.filter((entry) => manifest.pages.includes(entry.page));
829
+ if (!first) {
830
+ navigate({
831
+ action: "reset",
832
+ page: manifest.entry,
833
+ options: {}
834
+ });
835
+ return;
836
+ }
837
+ stack.apply({
838
+ action: "reset",
839
+ page: first.page,
840
+ options: first.options
841
+ });
842
+ for (const entry of rest) stack.apply({
843
+ action: "push",
844
+ page: entry.page,
845
+ options: entry.options
846
+ });
847
+ syncStack();
848
+ };
694
849
  const navigateFromPage = ({ from, ...navigation }) => {
695
850
  if (from === stack.top?.instanceId) navigate(navigation);
696
851
  };
697
852
  const hostMessageHandlers = /* @__PURE__ */ new Map([
698
853
  ["navigate", (payload) => navigateFromPage(payload)],
699
854
  ["hostCall", (payload) => calls.run(payload)],
700
- ["hostCancel", (payload) => calls.cancel(payload.id)]
855
+ ["hostCancel", (payload) => calls.cancel(payload.id)],
856
+ ["pageBars", (payload) => {
857
+ const { instanceId, ...bars } = payload;
858
+ barsByInstance.set(instanceId, bars);
859
+ showBars();
860
+ }]
701
861
  ]);
702
862
  const stopService = logic.listen((raw) => {
703
863
  const message = raw;
@@ -716,14 +876,13 @@
716
876
  logic.post(message);
717
877
  if (message.type === "connect") {
718
878
  syncSystemContext();
719
- navigate({
720
- action: "reset",
721
- page: manifest.entry,
722
- options: {}
723
- });
879
+ openFirstPages();
724
880
  }
725
881
  });
726
882
  return {
883
+ get pages() {
884
+ return stack.pages;
885
+ },
727
886
  back: () => navigate({
728
887
  action: "back",
729
888
  delta: 1
@@ -941,10 +1100,15 @@
941
1100
  const errorBanner = element("#error");
942
1101
  const phone = element(".phone");
943
1102
  const schemeSelect = element("#scheme");
1103
+ const navigationBar = element(".navigation-bar");
1104
+ const navigationTitle = element("#navigation-title");
1105
+ const navigationBack = element("#navigation-back");
1106
+ const tabBar = element(".tab-bar");
944
1107
  const browserDark = matchMedia("(prefers-color-scheme: dark)");
945
1108
  let runningApp = null;
946
1109
  let launchCount = 0;
947
- async function launch() {
1110
+ let hotReloads = 0;
1111
+ async function launch(restorePages = []) {
948
1112
  stopRunningApp();
949
1113
  launchCount += 1;
950
1114
  const thisLaunch = launchCount;
@@ -973,7 +1137,9 @@
973
1137
  systemContext: systemContext(),
974
1138
  handlers: dashHandlers(phone, note),
975
1139
  askPermission: (request, signal) => askPermission(phone, note, request, signal),
976
- observe: record
1140
+ observe: record,
1141
+ restorePages,
1142
+ onBarsChange: showBars
977
1143
  });
978
1144
  worker.addEventListener("error", (event) => showError(`Service crashed: ${event.message}`));
979
1145
  screen.src = WEBVIEW_URL;
@@ -988,6 +1154,18 @@
988
1154
  runningApp.worker.terminate();
989
1155
  runningApp = null;
990
1156
  }
1157
+ function showBars(bars, pages) {
1158
+ navigationBar.hidden = bars.navigationBarHidden;
1159
+ navigationTitle.textContent = bars.title;
1160
+ navigationBack.hidden = pages.length < 2;
1161
+ setBarColor("--bar", bars.navigationBarColor);
1162
+ setBarColor("--bar-text", bars.navigationBarTextColor);
1163
+ tabBar.hidden = bars.tabBarHidden;
1164
+ }
1165
+ function setBarColor(property, color) {
1166
+ if (color) phone.style.setProperty(property, color);
1167
+ else phone.style.removeProperty(property);
1168
+ }
991
1169
  function goBack() {
992
1170
  if (runningApp && !runningApp.host.back()) note("back on the first page: a device would close the dot");
993
1171
  }
@@ -1015,11 +1193,25 @@
1015
1193
  while (log.childElementCount > MAX_LOG_ENTRIES) log.firstElementChild?.remove();
1016
1194
  log.scrollTop = log.scrollHeight;
1017
1195
  }
1018
- function syncLaunch() {
1019
- launch().catch((error) => {
1196
+ function syncLaunch(restorePages = []) {
1197
+ launch(restorePages).catch((error) => {
1020
1198
  showError(`Could not start the dot: ${String(error)}`);
1021
1199
  });
1022
1200
  }
1201
+ function relaunch() {
1202
+ syncLaunch(runningApp?.host.pages ?? []);
1203
+ }
1204
+ function hotReload() {
1205
+ if (!runningApp) {
1206
+ syncLaunch();
1207
+ return;
1208
+ }
1209
+ hotReloads += 1;
1210
+ const reload = { dotHotReload: hotReloads };
1211
+ runningApp.worker.postMessage(reload);
1212
+ screen.contentWindow?.postMessage(reload, location.origin);
1213
+ note("dot updated in place");
1214
+ }
1023
1215
  function showError(message) {
1024
1216
  errorBanner.hidden = message === null;
1025
1217
  errorBanner.textContent = message ?? "";
@@ -1045,11 +1237,16 @@
1045
1237
  const events = new EventSource(EVENTS_PATH);
1046
1238
  events.addEventListener("reload", () => {
1047
1239
  showError(null);
1048
- syncLaunch();
1240
+ relaunch();
1241
+ });
1242
+ events.addEventListener("hot-reload", () => {
1243
+ showError(null);
1244
+ hotReload();
1049
1245
  });
1050
1246
  events.addEventListener("build-error", (event) => showError(String(event.data)));
1051
- element("#reload").addEventListener("click", syncLaunch);
1247
+ element("#reload").addEventListener("click", () => syncLaunch());
1052
1248
  element("#back").addEventListener("click", goBack);
1249
+ navigationBack.addEventListener("click", goBack);
1053
1250
  element("#clear").addEventListener("click", () => log.replaceChildren());
1054
1251
  schemeSelect.addEventListener("change", () => changeScheme(schemeSelect.value));
1055
1252
  browserDark.addEventListener("change", () => changeScheme(browserScheme()));