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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,72 +15,380 @@
15
15
  }
16
16
  return registry;
17
17
  }
18
- const builtinComponents = {
19
- Button: defineComponent({
20
- name: "Button",
21
- props: {
22
- variant: ["primary", "secondary"],
23
- disabled: "boolean"
24
- },
25
- events: ["click"],
26
- children: "text"
27
- }),
28
- Image: defineComponent({
29
- name: "Image",
30
- props: {
31
- src: "string",
32
- alt: "string",
33
- width: "number",
34
- height: "number"
35
- },
36
- events: ["load", "error"],
37
- children: "none"
38
- }),
39
- Input: defineComponent({
40
- name: "Input",
41
- props: {
42
- value: "string",
43
- placeholder: "string",
44
- label: "string",
45
- type: [
46
- "text",
47
- "number",
48
- "email",
49
- "phone",
50
- "password"
51
- ],
52
- maxLength: "number",
53
- disabled: "boolean"
54
- },
55
- events: ["change", "submit"],
56
- children: "none"
57
- }),
58
- Text: defineComponent({
59
- name: "Text",
60
- props: { variant: [
18
+ const Button = defineComponent({
19
+ name: "Button",
20
+ props: {
21
+ variant: ["primary", "secondary"],
22
+ disabled: "boolean",
23
+ loading: "boolean"
24
+ },
25
+ events: ["click"],
26
+ children: "text"
27
+ });
28
+ const Divider = defineComponent({
29
+ name: "Divider",
30
+ children: "none"
31
+ });
32
+ const SIZES = [
33
+ "small",
34
+ "medium",
35
+ "large"
36
+ ];
37
+ const TONES = [
38
+ "default",
39
+ "muted",
40
+ "info",
41
+ "success",
42
+ "warning",
43
+ "danger"
44
+ ];
45
+ const STATUS_TONES = [
46
+ "default",
47
+ "info",
48
+ "success",
49
+ "warning",
50
+ "danger"
51
+ ];
52
+ const ICON_NAMES = [
53
+ "arrow-down",
54
+ "arrow-left",
55
+ "arrow-right",
56
+ "arrow-up",
57
+ "bank",
58
+ "bell",
59
+ "bolt",
60
+ "calendar",
61
+ "card",
62
+ "cart",
63
+ "cash",
64
+ "check",
65
+ "chevron-down",
66
+ "chevron-left",
67
+ "chevron-right",
68
+ "chevron-up",
69
+ "clock",
70
+ "close",
71
+ "copy",
72
+ "download",
73
+ "edit",
74
+ "error",
75
+ "eye",
76
+ "eye-off",
77
+ "file",
78
+ "gift",
79
+ "grid",
80
+ "heart",
81
+ "help",
82
+ "history",
83
+ "home",
84
+ "image",
85
+ "info",
86
+ "list",
87
+ "location",
88
+ "lock",
89
+ "logout",
90
+ "mail",
91
+ "minus",
92
+ "mobile",
93
+ "more",
94
+ "phone",
95
+ "plus",
96
+ "qr-code",
97
+ "receipt",
98
+ "refresh",
99
+ "search",
100
+ "send",
101
+ "settings",
102
+ "share",
103
+ "star",
104
+ "success",
105
+ "trash",
106
+ "upload",
107
+ "user",
108
+ "users",
109
+ "wallet",
110
+ "warning",
111
+ "water",
112
+ "wifi"
113
+ ];
114
+ const Spinner = defineComponent({
115
+ name: "Spinner",
116
+ props: { size: SIZES },
117
+ children: "none"
118
+ });
119
+ const ProgressBar = defineComponent({
120
+ name: "ProgressBar",
121
+ props: { value: "number" },
122
+ children: "none"
123
+ });
124
+ const Notice = defineComponent({
125
+ name: "Notice",
126
+ props: {
127
+ tone: STATUS_TONES,
128
+ title: "string"
129
+ },
130
+ children: "text"
131
+ });
132
+ const Badge = defineComponent({
133
+ name: "Badge",
134
+ props: { tone: STATUS_TONES },
135
+ children: "text"
136
+ });
137
+ const Footer = defineComponent({
138
+ name: "Footer",
139
+ children: "elements"
140
+ });
141
+ const Icon = defineComponent({
142
+ name: "Icon",
143
+ props: {
144
+ name: ICON_NAMES,
145
+ size: SIZES,
146
+ tone: TONES
147
+ },
148
+ children: "none",
149
+ inline: true
150
+ });
151
+ const Image = defineComponent({
152
+ name: "Image",
153
+ props: {
154
+ src: "string",
155
+ alt: "string",
156
+ width: "number",
157
+ height: "number",
158
+ fit: ["cover", "contain"],
159
+ shape: [
160
+ "rounded",
161
+ "square",
162
+ "circle"
163
+ ]
164
+ },
165
+ events: ["load", "error"],
166
+ children: "none"
167
+ });
168
+ const Input = defineComponent({
169
+ name: "Input",
170
+ props: {
171
+ value: "string",
172
+ placeholder: "string",
173
+ label: "string",
174
+ hint: "string",
175
+ error: "string",
176
+ prefix: "string",
177
+ suffix: "string",
178
+ type: [
179
+ "text",
180
+ "number",
181
+ "email",
182
+ "phone",
183
+ "password"
184
+ ],
185
+ maxLength: "number",
186
+ disabled: "boolean"
187
+ },
188
+ events: ["change", "submit"],
189
+ children: "none"
190
+ });
191
+ const List = defineComponent({
192
+ name: "List",
193
+ children: "elements"
194
+ });
195
+ const ListItem = defineComponent({
196
+ name: "ListItem",
197
+ props: {
198
+ title: "string",
199
+ subtitle: "string",
200
+ value: "string",
201
+ icon: ICON_NAMES,
202
+ image: "string"
203
+ },
204
+ events: ["tap"],
205
+ children: "elements"
206
+ });
207
+ const Sheet = defineComponent({
208
+ name: "Sheet",
209
+ props: {
210
+ open: "boolean",
211
+ title: "string"
212
+ },
213
+ events: ["close"],
214
+ children: "elements"
215
+ });
216
+ const Toast = defineComponent({
217
+ name: "Toast",
218
+ props: {
219
+ message: "string",
220
+ tone: STATUS_TONES,
221
+ duration: "number"
222
+ },
223
+ events: ["hide"],
224
+ children: "none"
225
+ });
226
+ const Select = defineComponent({
227
+ name: "Select",
228
+ props: {
229
+ value: "string",
230
+ label: "string",
231
+ placeholder: "string",
232
+ hint: "string",
233
+ error: "string",
234
+ disabled: "boolean"
235
+ },
236
+ events: ["change"],
237
+ children: "elements"
238
+ });
239
+ const Option = defineComponent({
240
+ name: "Option",
241
+ props: {
242
+ value: "string",
243
+ label: "string"
244
+ },
245
+ children: "none"
246
+ });
247
+ const Skeleton = defineComponent({
248
+ name: "Skeleton",
249
+ props: {
250
+ shape: [
251
+ "text",
252
+ "circle",
253
+ "box"
254
+ ],
255
+ lines: "number",
256
+ height: "number"
257
+ },
258
+ children: "none"
259
+ });
260
+ const Text = defineComponent({
261
+ name: "Text",
262
+ props: {
263
+ variant: [
61
264
  "title",
62
265
  "body",
63
266
  "caption"
64
- ] },
65
- children: "text",
66
- inline: true
67
- }),
267
+ ],
268
+ tone: TONES,
269
+ weight: [
270
+ "regular",
271
+ "medium",
272
+ "bold"
273
+ ],
274
+ align: [
275
+ "start",
276
+ "center",
277
+ "end"
278
+ ],
279
+ maxLines: "number"
280
+ },
281
+ children: "text",
282
+ inline: true
283
+ });
284
+ const TextArea = defineComponent({
285
+ name: "TextArea",
286
+ props: {
287
+ value: "string",
288
+ placeholder: "string",
289
+ label: "string",
290
+ hint: "string",
291
+ error: "string",
292
+ rows: "number",
293
+ maxLength: "number",
294
+ disabled: "boolean"
295
+ },
296
+ events: ["change"],
297
+ children: "none"
298
+ });
299
+ const Switch = defineComponent({
300
+ name: "Switch",
301
+ props: {
302
+ checked: "boolean",
303
+ label: "string",
304
+ disabled: "boolean"
305
+ },
306
+ events: ["change"],
307
+ children: "none"
308
+ });
309
+ const Checkbox = defineComponent({
310
+ name: "Checkbox",
311
+ props: {
312
+ checked: "boolean",
313
+ label: "string",
314
+ disabled: "boolean"
315
+ },
316
+ events: ["change"],
317
+ children: "none"
318
+ });
319
+ const RadioGroup = defineComponent({
320
+ name: "RadioGroup",
321
+ props: {
322
+ value: "string",
323
+ label: "string",
324
+ disabled: "boolean"
325
+ },
326
+ events: ["change"],
327
+ children: "elements"
328
+ });
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
+ const builtinComponents = {
345
+ Badge,
346
+ Button,
347
+ Checkbox,
348
+ Divider,
349
+ Footer,
350
+ Icon,
351
+ Image,
352
+ Input,
353
+ List,
354
+ ListItem,
355
+ Notice,
356
+ Option,
357
+ ProgressBar,
358
+ Radio,
359
+ RadioGroup,
360
+ Select,
361
+ Sheet,
362
+ Skeleton,
363
+ Spinner,
364
+ Switch,
365
+ Text,
366
+ TextArea,
367
+ Toast,
68
368
  View: defineComponent({
69
369
  name: "View",
70
370
  props: {
371
+ gap: SPACE,
71
372
  direction: ["column", "row"],
72
- gap: [
73
- "none",
74
- "small",
75
- "medium",
76
- "large"
77
- ],
78
373
  align: [
79
374
  "stretch",
80
375
  "start",
81
376
  "center",
82
377
  "end"
83
378
  ],
379
+ justify: [
380
+ "start",
381
+ "center",
382
+ "end",
383
+ "between"
384
+ ],
385
+ wrap: "boolean",
386
+ grow: "boolean",
387
+ padding: SPACE,
388
+ paddingTop: SPACE,
389
+ paddingStart: SPACE,
390
+ paddingEnd: SPACE,
391
+ paddingBottom: SPACE,
84
392
  variant: ["plain", "card"]
85
393
  },
86
394
  events: ["tap"],
@@ -96,6 +404,8 @@
96
404
  this.code = code;
97
405
  }
98
406
  };
407
+ const LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
408
+ new RegExp(`^(?:\\*\\.${LABEL}(?:\\.${LABEL})+|${LABEL}(?:\\.${LABEL})*)$`);
99
409
  const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
100
410
  function majorVersion(version) {
101
411
  return parseVersion(version).major;
@@ -436,10 +746,24 @@
436
746
  //#region src/paths.ts
437
747
  const CLIENT_PATH = "/__dot/";
438
748
  const EVENTS_PATH = `${CLIENT_PATH}events`;
749
+ const HTTP_PATH = `${CLIENT_PATH}network/http/`;
750
+ const FILE_PATH = `${CLIENT_PATH}file/`;
439
751
  const DOT_PATH = "/dot/";
440
752
  const WEBVIEW_URL = `${DOT_PATH}ui/index.html`;
441
753
  const MANIFEST_URL = `${DOT_PATH}manifest.json`;
442
754
  //#endregion
755
+ //#region src/client/dash/dev-server.ts
756
+ async function callDevServer(path, params, signal) {
757
+ const answer = await (await fetch(path, {
758
+ method: "POST",
759
+ headers: { "Content-Type": "application/json" },
760
+ body: JSON.stringify(params),
761
+ signal
762
+ })).json();
763
+ if ("error" in answer) throw new HostError(answer.error.code, answer.error.message);
764
+ return answer.result;
765
+ }
766
+ //#endregion
443
767
  //#region src/client/dash/system.ts
444
768
  const SHAKE_DURATION_MS = 300;
445
769
  const SHAKE = [
@@ -458,6 +782,30 @@
458
782
  phone.dataset.torch = state;
459
783
  note(`torch: ${state}`);
460
784
  }
785
+ async function copyText(note, text) {
786
+ try {
787
+ await navigator.clipboard.writeText(text);
788
+ } catch (error) {
789
+ throw new HostError("failed", `The browser kept the clipboard: ${error.message}`);
790
+ }
791
+ note(`copied: ${text}`);
792
+ }
793
+ function share(note, params) {
794
+ const parts = [
795
+ params.text,
796
+ params.url,
797
+ params.file && `file ${params.file}`
798
+ ].filter(Boolean);
799
+ if (parts.length === 0) throw new HostError("invalid", "Give share a text, a url or a file");
800
+ note(`shared: ${parts.join(" · ")}`);
801
+ }
802
+ function delay(milliseconds, signal) {
803
+ if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new HostError("invalid", `A delay is a number of milliseconds, 0 or more, not ${milliseconds}`);
804
+ return new Promise((resolve) => {
805
+ const timer = setTimeout(resolve, milliseconds);
806
+ signal.addEventListener("abort", () => clearTimeout(timer));
807
+ });
808
+ }
461
809
  //#endregion
462
810
  //#region src/client/dom.ts
463
811
  function element(selector) {
@@ -506,13 +854,39 @@
506
854
  }
507
855
  //#endregion
508
856
  //#region src/client/dash/index.ts
857
+ const HTTP_METHODS = [
858
+ "request",
859
+ "download",
860
+ "upload"
861
+ ];
862
+ const FILE_OPERATIONS = [
863
+ "read",
864
+ "write",
865
+ "stat",
866
+ "list",
867
+ "createFolder",
868
+ "move",
869
+ "copy",
870
+ "remove",
871
+ "save",
872
+ "open",
873
+ "usage"
874
+ ];
509
875
  function dashHandlers(phone, note) {
510
876
  return {
511
877
  "dash.ui.alert": ({ params, signal }) => alert(phone, params, signal),
512
878
  "dash.system.vibrate": ({ params }) => vibrate(phone, note, params),
513
- "dash.system.torch": ({ params }) => torch(phone, note, params.state)
879
+ "dash.system.torch": ({ params }) => torch(phone, note, params.state),
880
+ "dash.system.copyText": ({ params }) => copyText(note, params.text),
881
+ "dash.system.share": ({ params }) => share(note, params),
882
+ "dash.system.delay": ({ params, signal }) => delay(params.milliseconds, signal),
883
+ ...devServerHandlers("dash.network.http", HTTP_PATH, HTTP_METHODS),
884
+ ...devServerHandlers("dash.file", FILE_PATH, FILE_OPERATIONS)
514
885
  };
515
886
  }
887
+ function devServerHandlers(group, path, names) {
888
+ return Object.fromEntries(names.map((name) => [`${group}.${name}`, ({ params, signal }) => callDevServer(`${path}${name}`, params, signal)]));
889
+ }
516
890
  //#endregion
517
891
  //#region src/client/dash/permissions.ts
518
892
  const PERMISSION_SUMMARIES = { account: "see your Dash account: your name, and your email or phone" };