jsbox-cview 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/components/alert/input-alert.ts +73 -0
  4. package/components/alert/login-alert.ts +75 -0
  5. package/components/alert/plain-alert.ts +49 -0
  6. package/components/alert/uialert.ts +110 -0
  7. package/components/artificial-flowlayout.ts +321 -0
  8. package/components/base.ts +47 -0
  9. package/components/custom-navigation-bar.ts +570 -0
  10. package/components/dialogs/dialog-sheet.ts +87 -0
  11. package/components/dialogs/form-dialog.ts +23 -0
  12. package/components/dialogs/list-dialog.ts +87 -0
  13. package/components/dialogs/text-dialog.ts +34 -0
  14. package/components/dynamic-itemsize-matrix.ts +190 -0
  15. package/components/dynamic-preference-listview.ts +691 -0
  16. package/components/dynamic-rowheight-list.ts +62 -0
  17. package/components/enhanced-imageview.ts +128 -0
  18. package/components/image-pager.ts +177 -0
  19. package/components/page-control.ts +91 -0
  20. package/components/pageviewer-titlebar.ts +170 -0
  21. package/components/pageviewer.ts +124 -0
  22. package/components/rotating-view.ts +126 -0
  23. package/components/searchbar.ts +373 -0
  24. package/components/sheet.ts +113 -0
  25. package/components/single-views.ts +828 -0
  26. package/components/spinners/loading-double-rings.ts +121 -0
  27. package/components/spinners/loading-dual-ring.ts +90 -0
  28. package/components/spinners/loading-wedges.ts +112 -0
  29. package/components/spinners/spinner-androidstyle.ts +264 -0
  30. package/components/static-preference-listview.ts +991 -0
  31. package/components/symbol-button.ts +105 -0
  32. package/components/tabbar.ts +451 -0
  33. package/controller/base-controller.ts +216 -0
  34. package/controller/controller-router.ts +74 -0
  35. package/controller/pageviewer-controller.ts +86 -0
  36. package/controller/presented-page-controller.ts +57 -0
  37. package/controller/splitview-controller.ts +323 -0
  38. package/controller/tabbar-controller.ts +99 -0
  39. package/package.json +23 -0
  40. package/test.ts +0 -0
  41. package/tsconfig.json +121 -0
  42. package/utils/colors.ts +13 -0
  43. package/utils/cvid.ts +34 -0
  44. package/utils/l10n.ts +42 -0
  45. package/utils/path.ts +100 -0
  46. package/utils/rect.ts +67 -0
  47. package/utils/uitools.ts +117 -0
@@ -0,0 +1,991 @@
1
+ /**
2
+ * # cview PreferenceListView_static
3
+ *
4
+ * 便捷的设置列表实现. 其所有 cell 均为静态 cell, 可以同时使用 list 控件的 props(除了 template, data)和 events(除了 didSelect), 同时具有独特方法 set(key, value), 以及独特方法 changed
5
+ *
6
+ * sections 为 Array, 里面的 section 定义:
7
+ *
8
+ * - title?: string 标题.
9
+ * - rows: {type: string}[] 内容
10
+ *
11
+ * row定义:
12
+ *
13
+ * - 通用:
14
+ *
15
+ * - type: string 类型. 包括'string', 'number', 'integer', 'stepper','boolean', 'slider', 'list', 'tab','info', 'link', 'action'
16
+ * - key?: string 键. 如没有则不会返回其值.
17
+ * - title?: string 标题
18
+ * - value?: any 在下面专项里详解.
19
+ * - titleColor?: $color = $color("primaryText") 标题颜色
20
+ *
21
+ * - string:
22
+ *
23
+ * - value?: string
24
+ * - placeholder?: string
25
+ * - textColor?: $color = $color("primaryText")
26
+ *
27
+ * - number, integer:
28
+ *
29
+ * - value?: number
30
+ * - placeholder?: string
31
+ * - textColor?: $color = $color("primaryText")
32
+ * - min?: number 最小值
33
+ * - max?: number 最大值
34
+ *
35
+ * - stepper:
36
+ *
37
+ * - value?: number
38
+ * - placeholder?: string
39
+ * - min?: number 最小值
40
+ * - max?: number 最大值
41
+ *
42
+ * - boolean:
43
+ *
44
+ * - value?: boolean
45
+ * - onColor?: $color = $color("#34C85A")
46
+ * - thumbColor
47
+ *
48
+ * - slider:
49
+ *
50
+ * - value?: number 即 slider.value
51
+ * - decimal?: number = 1 精度
52
+ * - min?: number
53
+ * - max?: number
54
+ * - minColor?: $color = $color("systemLink")
55
+ * - maxColor?: $color
56
+ * - thumbColor?: $color
57
+ *
58
+ * - list:
59
+ *
60
+ * - value?: number 即 index, -1 时为不选
61
+ * - items?: string[]
62
+ *
63
+ * - tab:
64
+ *
65
+ * - value?: number 即 index, -1 时为不选
66
+ * - items?: string[]
67
+ *
68
+ * - info:
69
+ *
70
+ * - value?: string
71
+ *
72
+ * - link:
73
+ *
74
+ * - value?: string url
75
+ *
76
+ * - action:
77
+ *
78
+ * - value?: function 点击后会执行的函数
79
+ * - destructive?: boolean = false 是否为危险动作,若是则为红色
80
+ *
81
+ * Methods:
82
+ *
83
+ * - set(key, value) 设定 key 对应的 value
84
+ * - cview.values 获取全部的 values
85
+ *
86
+ * Events:
87
+ *
88
+ * - changed: values => {}
89
+ */
90
+
91
+ import { Base } from "./base";
92
+ import { getTextWidth } from "../utils/uitools";
93
+
94
+ type PreferenceCellTypes = "string" | "number" | "integer" | "stepper" | "boolean" | "slider" | "list" | "tab" | "info" | "link" | "action";
95
+
96
+ export interface PreferenceSection {
97
+ title: string;
98
+ rows: PrefsRow[]
99
+ }
100
+
101
+ type PrefsRow = PrefsRowString | PrefsRowNumber | PrefsRowInteger | PrefsRowStepper | PrefsRowBoolean | PrefsRowSlider | PrefsRowList | PrefsRowTab | PrefsRowInfo | PrefsRowLink | PrefsRowAction;
102
+
103
+ interface PrefsRowBase {
104
+ type: PreferenceCellTypes;
105
+ key?: string;
106
+ title?: string;
107
+ titleColor?: UIColor;
108
+ changedEvent?: () => void;
109
+ }
110
+
111
+ interface PrefsRowString extends PrefsRowBase {
112
+ type: "string";
113
+ value?: string;
114
+ placeholder?: string;
115
+ textColor?: UIColor;
116
+ }
117
+
118
+ interface PrefsRowNumber extends PrefsRowBase {
119
+ type: "number";
120
+ value?: number;
121
+ placeholder?: string;
122
+ textColor?: UIColor;
123
+ min?: number;
124
+ max?: number;
125
+ }
126
+
127
+ interface PrefsRowInteger extends PrefsRowBase {
128
+ type: "integer";
129
+ value?: number;
130
+ placeholder?: string;
131
+ textColor?: UIColor;
132
+ min?: number;
133
+ max?: number;
134
+ }
135
+
136
+ interface PrefsRowStepper extends PrefsRowBase {
137
+ type: "stepper";
138
+ value?: number;
139
+ min?: number;
140
+ max?: number;
141
+ }
142
+
143
+ interface PrefsRowBoolean extends PrefsRowBase {
144
+ type: "boolean";
145
+ value?: boolean;
146
+ onColor?: UIColor;
147
+ thumbColor?: UIColor;
148
+ }
149
+
150
+ interface PrefsRowSlider extends PrefsRowBase {
151
+ type: "slider";
152
+ value?: number;
153
+ min?: number;
154
+ max?: number;
155
+ decimal?: number;
156
+ minColor?: UIColor;
157
+ maxColor?: UIColor;
158
+ thumbColor?: UIColor;
159
+ }
160
+
161
+ interface PrefsRowList extends PrefsRowBase {
162
+ type: "list";
163
+ value?: number;
164
+ items: string[];
165
+ }
166
+
167
+ interface PrefsRowTab extends PrefsRowBase {
168
+ type: "tab";
169
+ value?: number;
170
+ items: string[];
171
+ }
172
+
173
+ interface PrefsRowInfo extends PrefsRowBase {
174
+ type: "info";
175
+ value?: string;
176
+ }
177
+
178
+ interface PrefsRowLink extends PrefsRowBase {
179
+ type: "link";
180
+ value?: string;
181
+ }
182
+
183
+ interface PrefsRowAction extends PrefsRowBase {
184
+ type: "action";
185
+ value?: () => void;
186
+ destructive?: boolean;
187
+ }
188
+
189
+ type PreferenceValues = { [key: string]: any };
190
+
191
+ type AllCells = StringCell | NumberCell | IntegerCell | StepperCell | BooleanCell | SliderCell | ListCell | TabCell | InfoCell | LinkCell | ActionCell;
192
+
193
+ abstract class Cell extends Base<UIView, UiTypes.ViewOptions> {
194
+ abstract _type: string;
195
+ _key?: string;
196
+ _title?: string;
197
+ _value?: any;
198
+ _values: PreferenceValues;
199
+ _titleColor: UIColor;
200
+ _changedEvent?: () => void;
201
+ _defineView: () => UiTypes.ViewOptions;
202
+ constructor(
203
+ {
204
+ key,
205
+ title,
206
+ value,
207
+ titleColor = $color("primaryText"),
208
+ changedEvent
209
+ }: {
210
+ key?: string;
211
+ title?: string;
212
+ value?: any;
213
+ titleColor?: UIColor;
214
+ changedEvent?: () => void;
215
+ }, values: PreferenceValues
216
+ ) {
217
+ super();
218
+ this._key = key;
219
+ this._title = title;
220
+ this._value = value;
221
+ this._titleColor = titleColor;
222
+ this._changedEvent = changedEvent;
223
+ this._values = values;
224
+ const selectableTypes = [
225
+ "string",
226
+ "number",
227
+ "integer",
228
+ "stepper",
229
+ "list",
230
+ "link",
231
+ "action"
232
+ ];
233
+ this._defineView = () => {
234
+ return {
235
+ type: "view",
236
+ props: {
237
+ selectable: selectableTypes.includes(this._type),
238
+ id: this.id
239
+ },
240
+ layout: $layout.fill,
241
+ views: [this._defineTitleView(), this._defineValueView()]
242
+ };
243
+ }
244
+ }
245
+
246
+ set value(value) {
247
+ if (this._handleValue) value = this._handleValue(value);
248
+ if (this._key) this._values[this._key] = value;
249
+ this._value = value;
250
+ }
251
+
252
+ get value() {
253
+ return this._value;
254
+ }
255
+
256
+ get type() {
257
+ return this._type;
258
+ }
259
+
260
+ get key() {
261
+ return this._key;
262
+ }
263
+
264
+ abstract _handleValue(value: any): any;
265
+
266
+ abstract _defineValueView(): UiTypes.AllViewOptions;
267
+
268
+ _defineTitleView(): UiTypes.LabelOptions {
269
+ return {
270
+ type: "label",
271
+ props: {
272
+ id: "title",
273
+ text: this._title,
274
+ textColor: this._titleColor,
275
+ font: $font(17)
276
+ },
277
+ layout: (make, view) => {
278
+ make.centerY.equalTo(view.super);
279
+ make.width.equalTo(getTextWidth(this._title || ""));
280
+ make.left.inset(15);
281
+ }
282
+ };
283
+ }
284
+ }
285
+
286
+ abstract class BaseStringCell extends Cell {
287
+ abstract _type: string;
288
+ _placeholder?: string;
289
+ _textColor?: UIColor;
290
+ constructor(props: PrefsRowString | PrefsRowNumber | PrefsRowInteger, values: PreferenceValues) {
291
+ super(props, values);
292
+ const { placeholder, textColor } = props;
293
+ this._placeholder = placeholder;
294
+ this._textColor = textColor;
295
+ }
296
+
297
+ _defineValueView(): UiTypes.AllViewOptions {
298
+ return {
299
+ type: "view",
300
+ props: {},
301
+ layout: (make, view) => {
302
+ make.top.bottom.inset(0);
303
+ make.left.equalTo(view.prev.right).inset(10);
304
+ make.right.inset(15);
305
+ },
306
+ views: [
307
+ {
308
+ type: "image",
309
+ props: {
310
+ symbol: "chevron.right",
311
+ tintColor: $color("lightGray", "darkGray"),
312
+ contentMode: 1
313
+ },
314
+ layout: (make, view) => {
315
+ make.centerY.equalTo(view.super);
316
+ make.size.equalTo($size(17, 17));
317
+ make.right.inset(0);
318
+ }
319
+ },
320
+ {
321
+ type: "label",
322
+ props: {
323
+ id: "label",
324
+ text: this._handleText(this._value)?.toString(),
325
+ align: $align.right,
326
+ font: $font(17),
327
+ textColor: this._textColor,
328
+ bgcolor: $color("clear"),
329
+ userInteractionEnabled: false
330
+ },
331
+ layout: (make, view) => {
332
+ make.centerY.equalTo(view.super);
333
+ make.left.inset(0);
334
+ make.right.equalTo(view.prev.left).inset(5);
335
+ }
336
+ }
337
+ ]
338
+ };
339
+ }
340
+
341
+ _handleValue(text: string) {
342
+ const result = this._handleText(text);
343
+ const label = this.view.get("label") as UILabelView;
344
+ if (result === undefined) label.text = "";
345
+ else label.text = result.toString();
346
+ return result;
347
+ }
348
+
349
+ abstract _handleText(text: string): string | number | undefined;
350
+ }
351
+
352
+ class StringCell extends BaseStringCell {
353
+ readonly _type = "string"
354
+ constructor(props: PrefsRowString, values: PreferenceValues) {
355
+ super(props, values);
356
+ }
357
+
358
+ _handleText(text: string) {
359
+ return text;
360
+ }
361
+ }
362
+
363
+ class NumberCell extends BaseStringCell {
364
+ readonly _type = "number";
365
+ _min?: number;
366
+ _max?: number;
367
+ constructor(props: PrefsRowNumber, values: PreferenceValues) {
368
+ super(props, values);
369
+ const { min, max } = props;
370
+ this._min = min;
371
+ this._max = max;
372
+ }
373
+
374
+ _handleText(text: string): number | undefined {
375
+ if (!text) return;
376
+ const result = parseFloat(text);
377
+ if (isNaN(result)) return;
378
+ if (this._min !== undefined && result < this._min) return;
379
+ if (this._max !== undefined && result > this._max) return;
380
+ return result;
381
+ }
382
+ }
383
+
384
+ class IntegerCell extends BaseStringCell {
385
+ readonly _type = "integer";
386
+ _min: number;
387
+ _max?: number;
388
+ constructor(props: PrefsRowInteger, values: PreferenceValues) {
389
+ super(props, values);
390
+ const { min, max } = props;
391
+ this._min = min || 0;
392
+ this._max = max;
393
+ }
394
+
395
+ _handleText(text: string): number | undefined {
396
+ if (!text) return;
397
+ const result = parseInt(text);
398
+ if (isNaN(result)) return;
399
+ if (this._min !== undefined && result < this._min) return;
400
+ if (this._max !== undefined && result > this._max) return;
401
+ return result;
402
+ }
403
+ }
404
+
405
+ class StepperCell extends Cell {
406
+ readonly _type = "stepper";
407
+ _max?: number;
408
+ _min: number;
409
+ constructor(props: PrefsRowStepper, values: PreferenceValues) {
410
+ super(props, values);
411
+ const { max, min } = props;
412
+ this._max = max;
413
+ this._min = min || 0;
414
+ }
415
+
416
+ _defineValueView(): UiTypes.ViewOptions {
417
+ return {
418
+ type: "view",
419
+ props: {},
420
+ views: [
421
+ {
422
+ type: "stepper",
423
+ props: {
424
+ id: "stepper",
425
+ value: this._value || this._min,
426
+ max: this._max,
427
+ min: this._min
428
+ },
429
+ layout: (make, view) => {
430
+ make.centerY.equalTo(view.super);
431
+ make.right.inset(0);
432
+ },
433
+ events: {
434
+ changed: sender => {
435
+ this.value = sender.value;
436
+ if (this._changedEvent) this._changedEvent();
437
+ }
438
+ }
439
+ },
440
+ {
441
+ type: "label",
442
+ props: {
443
+ id: "label",
444
+ text: this._value || this._min,
445
+ align: $align.right
446
+ },
447
+ layout: (make, view) => {
448
+ make.top.bottom.inset(0);
449
+ make.right.equalTo(view.prev.left).inset(10);
450
+ make.width.equalTo(30);
451
+ }
452
+ }
453
+ ],
454
+ layout: (make, view) => {
455
+ make.top.bottom.inset(0);
456
+ make.left.equalTo(view.prev.right).inset(10);
457
+ make.right.inset(15);
458
+ }
459
+ };
460
+ }
461
+
462
+ _handleValue(num: number) {
463
+ if (isNaN(num)) num = this._min;
464
+ if (this._min !== undefined && num < this._min) num = this._min;
465
+ if (this._max !== undefined && num > this._max) num = this._max;
466
+ const label = this.view.get("label") as UILabelView;
467
+ label.text = num.toString();
468
+ const stepper = this.view.get("stepper") as UIStepperView;
469
+ stepper.value = num;
470
+ return num;
471
+ }
472
+ }
473
+
474
+ class BooleanCell extends Cell {
475
+ readonly _type = "boolean";
476
+ _onColor: UIColor;
477
+ _thumbColor?: UIColor;
478
+ constructor(props: PrefsRowBoolean, values: PreferenceValues) {
479
+ super(props, values);
480
+ const { onColor = $color("#34C85A"), thumbColor } = props;
481
+ this._onColor = onColor;
482
+ this._thumbColor = thumbColor;
483
+ }
484
+
485
+ _defineValueView(): UiTypes.SwitchOptions {
486
+ return {
487
+ type: "switch",
488
+ props: {
489
+ id: "switch",
490
+ on: this._value,
491
+ onColor: this._onColor,
492
+ thumbColor: this._thumbColor
493
+ },
494
+ layout: (make, view) => {
495
+ make.size.equalTo($size(51, 31));
496
+ make.centerY.equalTo(view.super);
497
+ make.right.inset(15);
498
+ },
499
+ events: {
500
+ changed: sender => {
501
+ this.value = sender.on;
502
+ if (this._changedEvent) this._changedEvent();
503
+ }
504
+ }
505
+ };
506
+ }
507
+
508
+ _handleValue(bool: boolean) {
509
+ const switchView = this.view.get("switch") as UISwitchView;
510
+ switchView.on = bool;
511
+ return bool;
512
+ }
513
+ }
514
+
515
+ class SliderCell extends Cell {
516
+ readonly _type = "slider";
517
+ _decimal: number;
518
+ _min: number;
519
+ _max: number;
520
+ _minColor: UIColor;
521
+ _maxColor?: UIColor;
522
+ _thumbColor?: UIColor;
523
+ constructor(props: PrefsRowSlider, values: PreferenceValues) {
524
+ super(props, values);
525
+ const {
526
+ decimal = 1,
527
+ min = 0,
528
+ max = 1,
529
+ minColor = $color("systemLink"),
530
+ maxColor,
531
+ thumbColor
532
+ } = props;
533
+ this._decimal = decimal;
534
+ this._min = min;
535
+ this._max = max;
536
+ this._minColor = minColor;
537
+ this._maxColor = maxColor;
538
+ this._thumbColor = thumbColor;
539
+ }
540
+
541
+ _defineValueView(): UiTypes.ViewOptions {
542
+ return {
543
+ type: "view",
544
+ props: {},
545
+ views: [
546
+ {
547
+ type: "label",
548
+ props: {
549
+ id: "label",
550
+ text: this._value.toFixed(this._decimal),
551
+ align: $align.center
552
+ },
553
+ layout: (make, view) => {
554
+ make.top.right.bottom.inset(0);
555
+ make.width.equalTo(44);
556
+ }
557
+ },
558
+ {
559
+ type: "slider",
560
+ props: {
561
+ id: "slider",
562
+ value: this._value,
563
+ max: this._max,
564
+ min: this._min,
565
+ minColor: this._minColor,
566
+ maxColor: this._maxColor,
567
+ thumbColor: this._thumbColor,
568
+ continuous: true
569
+ },
570
+ layout: (make, view) => {
571
+ make.top.left.bottom.inset(0);
572
+ make.right.equalTo(view.prev.left);
573
+ },
574
+ events: {
575
+ changed: sender => {
576
+ const adjustedValue = parseFloat(
577
+ sender.value.toFixed(this._decimal)
578
+ );
579
+ const label = sender.prev as UILabelView;
580
+ label.text = adjustedValue.toString();
581
+ if (this._key) {
582
+ this._values[this._key] = adjustedValue;
583
+ this._value = adjustedValue;
584
+ }
585
+ },
586
+ touchesEnded: sender => {
587
+ const adjustedValue = parseFloat(
588
+ sender.value.toFixed(this._decimal)
589
+ );
590
+ this.value = adjustedValue;
591
+ if (this._changedEvent) this._changedEvent();
592
+ }
593
+ }
594
+ }
595
+ ],
596
+ layout: (make, view) => {
597
+ make.top.bottom.inset(0);
598
+ make.left.lessThanOrEqualTo(view.prev.right).inset(10).priority(999);
599
+ make.width.lessThanOrEqualTo(250);
600
+ make.right.inset(15);
601
+ }
602
+ };
603
+ }
604
+
605
+ _handleValue(num: number) {
606
+ if (isNaN(num)) num = this._min;
607
+ if (this._min !== undefined && num < this._min) num = this._min;
608
+ if (this._max !== undefined && num > this._max) num = this._max;
609
+ const adjustedValue = parseFloat(num.toFixed(this._decimal));
610
+ const label = this.view.get("label") as UILabelView;
611
+ label.text = adjustedValue.toString();
612
+ const slider = this.view.get("slider") as UISliderView;
613
+ slider.value = adjustedValue;
614
+ return adjustedValue;
615
+ }
616
+ }
617
+
618
+ class ListCell extends Cell {
619
+ readonly _type = "list";
620
+ _items: string[];
621
+ constructor(props: PrefsRowList, values: PreferenceValues) {
622
+ super(props, values);
623
+ const { items } = props;
624
+ this._items = items;
625
+ }
626
+
627
+ _defineValueView(): UiTypes.ViewOptions {
628
+ return {
629
+ type: "view",
630
+ props: {},
631
+ layout: (make, view) => {
632
+ make.top.bottom.inset(0);
633
+ make.left.equalTo(view.prev.right).inset(10);
634
+ make.right.inset(15);
635
+ },
636
+ views: [
637
+ {
638
+ type: "image",
639
+ props: {
640
+ symbol: "chevron.right",
641
+ tintColor: $color("lightGray", "darkGray"),
642
+ contentMode: 1
643
+ },
644
+ layout: (make, view) => {
645
+ make.centerY.equalTo(view.super);
646
+ make.size.equalTo($size(17, 17));
647
+ make.right.inset(0);
648
+ }
649
+ },
650
+ {
651
+ type: "label",
652
+ props: {
653
+ id: "label",
654
+ text: this._items[this._value],
655
+ textColor: $color("secondaryText"),
656
+ align: $align.right
657
+ },
658
+ layout: (make, view) => {
659
+ make.centerY.equalTo(view.super);
660
+ make.left.inset(0);
661
+ make.right.equalTo(view.prev.left).inset(5);
662
+ }
663
+ }
664
+ ]
665
+ };
666
+ }
667
+
668
+ _handleValue(num: number) {
669
+ const label = this.view.get("label") as UILabelView;
670
+ label.text = this._items[num];
671
+ return num;
672
+ }
673
+ }
674
+
675
+ class TabCell extends Cell {
676
+ readonly _type = "tab";
677
+ _items: string[];
678
+ constructor(props: PrefsRowTab, values: PreferenceValues) {
679
+ super(props, values);
680
+ const { items, value = -1 } = props;
681
+ this._items = items;
682
+ this._value = value;
683
+ }
684
+
685
+ _defineValueView(): UiTypes.TabOptions {
686
+ return {
687
+ type: "tab",
688
+ props: {
689
+ id: "tab",
690
+ items: this._items,
691
+ index: this._value
692
+ },
693
+ layout: (make, view) => {
694
+ make.centerY.equalTo(view.super);
695
+ make.height.equalTo(34);
696
+ make.left.lessThanOrEqualTo(view.prev.right).inset(10).priority(999);
697
+ make.width.lessThanOrEqualTo(250);
698
+ make.right.inset(15);
699
+ },
700
+ events: {
701
+ changed: sender => {
702
+ this.value = sender.index;
703
+ if (this._changedEvent) this._changedEvent();
704
+ }
705
+ }
706
+ };
707
+ }
708
+
709
+ _handleValue(num: number) {
710
+ const tab = this.view.get("tab") as UITabView;
711
+ tab.index = num;
712
+ return num;
713
+ }
714
+ }
715
+
716
+ class InfoCell extends Cell {
717
+ readonly _type = "info";
718
+ constructor(props: PrefsRowInfo, values: PreferenceValues) {
719
+ super(props, values);
720
+ }
721
+
722
+ _defineValueView(): UiTypes.LabelOptions {
723
+ return {
724
+ type: "label",
725
+ props: {
726
+ id: "label",
727
+ text: this._value,
728
+ textColor: $color("secondaryText"),
729
+ align: $align.right
730
+ },
731
+ layout: (make, view) => {
732
+ make.top.bottom.inset(0);
733
+ make.left.equalTo(view.prev.right).inset(10);
734
+ make.right.inset(15);
735
+ }
736
+ };
737
+ }
738
+
739
+ _handleValue(text: string) {
740
+ const label = this.view.get("label") as UILabelView;
741
+ label.text = text;
742
+ return text;
743
+ }
744
+ }
745
+
746
+ class LinkCell extends Cell {
747
+ readonly _type = "link";
748
+ constructor(props: PrefsRowLink, values: PreferenceValues) {
749
+ super(props, values);
750
+ }
751
+
752
+ _defineValueView(): UiTypes.LabelOptions {
753
+ return {
754
+ type: "label",
755
+ props: {
756
+ id: "label",
757
+ styledText: {
758
+ text: this._value,
759
+ font: $font(17),
760
+ styles: [
761
+ {
762
+ range: $range(0, this._value.length),
763
+ link: this._value
764
+ }
765
+ ]
766
+ },
767
+ align: $align.right
768
+ },
769
+ layout: (make, view) => {
770
+ make.top.bottom.inset(0);
771
+ make.left.equalTo(view.prev.right).inset(10);
772
+ make.right.inset(15);
773
+ }
774
+ };
775
+ }
776
+
777
+ _handleValue(text: string) {
778
+ const label = this.view.get("label") as UILabelView;
779
+ label.styledText = {
780
+ text,
781
+ font: $font(17),
782
+ styles: [
783
+ {
784
+ range: $range(0, text.length),
785
+ link: text
786
+ }
787
+ ]
788
+ };
789
+ return text;
790
+ }
791
+ }
792
+
793
+ class ActionCell extends Cell {
794
+ readonly _type = "action";
795
+ _destructive: boolean;
796
+ constructor(props: PrefsRowAction, values: PreferenceValues) {
797
+ super(props, values);
798
+ const { destructive = false } = props;
799
+ this._destructive = destructive;
800
+ this._values = values;
801
+ }
802
+
803
+ _defineTitleView(): UiTypes.LabelOptions {
804
+ return {
805
+ type: "label",
806
+ props: {},
807
+ layout: make => make.width.equalTo(0)
808
+ };
809
+ }
810
+
811
+ _defineValueView(): UiTypes.LabelOptions {
812
+ return {
813
+ type: "label",
814
+ props: {
815
+ text: this._title,
816
+ textColor: this._destructive ? $color("red") : $color("systemLink")
817
+ },
818
+ layout: (make, view) => {
819
+ make.top.bottom.inset(0);
820
+ make.left.equalTo(view.prev.left);
821
+ make.left.right.inset(15);
822
+ }
823
+ };
824
+ }
825
+
826
+ _handleValue() {
827
+ return;
828
+ }
829
+ }
830
+
831
+ export class PreferenceListView extends Base<UIListView, UiTypes.ListOptions> {
832
+ _defineView: () => UiTypes.ListOptions;
833
+ _sections: PreferenceSection[];
834
+ _props: Partial<UiTypes.ListProps>;
835
+ _layout?: (make: MASConstraintMaker, view: UIListView) => void;
836
+ _values: PreferenceValues;
837
+ _cells: {
838
+ title: string;
839
+ rows: AllCells[];
840
+ }[];
841
+
842
+ constructor({ sections, props = {}, layout, events = {} }: {
843
+ sections: PreferenceSection[];
844
+ props?: Partial<UiTypes.ListProps>;
845
+ layout?: (make: MASConstraintMaker, view: UIListView) => void;
846
+ events?: {
847
+ changed?: (values: { [key: string]: any }) => void;
848
+ };
849
+ }) {
850
+ super();
851
+ this._sections = sections;
852
+ this._values = {};
853
+ const excludedTypes = ["action", "info", "link"];
854
+ sections.forEach(section => {
855
+ section.rows.forEach(row => {
856
+ if (row.key && !excludedTypes.includes(row.type)) {
857
+ this._values[row.key] = row.value;
858
+ }
859
+ });
860
+ });
861
+ this._props = props;
862
+ this._layout = layout;
863
+ this._cells = this._sections.map(section => ({
864
+ title: section.title,
865
+ rows: section.rows.map(props => {
866
+ if (events.changed)
867
+ props.changedEvent = () => {
868
+ if (events.changed) events.changed(this.values);
869
+ };
870
+ return this._createCell(props);
871
+ })
872
+ }));
873
+ this._defineView = () => {
874
+
875
+ return {
876
+ type: "list",
877
+ props: {
878
+ style: 2,
879
+ ...this._props,
880
+ id: this.id,
881
+ data: this._cells.map(section => ({
882
+ title: section.title,
883
+ rows: section.rows.map(cell => cell.definition)
884
+ }))
885
+ },
886
+ layout: this._layout,
887
+ events: {
888
+ didSelect: (sender, indexPath, data) => {
889
+ const cell = this._cells[indexPath.section].rows[indexPath.row];
890
+ switch (cell._type) {
891
+ case "string": {
892
+ $input.text({
893
+ type: $kbType.default,
894
+ placeholder: cell._placeholder,
895
+ handler: text => {
896
+ cell.value = text;
897
+ if (cell._changedEvent) cell._changedEvent();
898
+ }
899
+ });
900
+ break;
901
+ }
902
+ case "number": {
903
+ $input.text({
904
+ type: $kbType.decimal,
905
+ placeholder: cell._placeholder,
906
+ handler: text => {
907
+ cell.value = parseFloat(text);
908
+ if (cell._changedEvent) cell._changedEvent();
909
+ }
910
+ });
911
+ break;
912
+ }
913
+ case "integer": {
914
+ $input.text({
915
+ type: $kbType.number,
916
+ placeholder: cell._placeholder,
917
+ handler: text => {
918
+ cell.value = parseInt(text);
919
+ if (cell._changedEvent) cell._changedEvent();
920
+ }
921
+ });
922
+ break;
923
+ }
924
+ case "list": {
925
+ $ui.menu({
926
+ items: cell._items,
927
+ handler: (title, index) => {
928
+ cell.value = index;
929
+ if (cell._changedEvent) cell._changedEvent();
930
+ }
931
+ });
932
+ break;
933
+ }
934
+ case "link": {
935
+ $safari.open({ url: cell.value });
936
+ break;
937
+ }
938
+ case "action": {
939
+ cell.value();
940
+ break;
941
+ }
942
+ default:
943
+ break;
944
+ }
945
+ }
946
+ }
947
+ };
948
+ }
949
+ }
950
+
951
+ private _createCell(props: PrefsRow) {
952
+ switch (props.type) {
953
+ case "string":
954
+ return new StringCell(props, this._values);
955
+ case "number":
956
+ return new NumberCell(props, this._values);
957
+ case "integer":
958
+ return new IntegerCell(props, this._values);
959
+ case "stepper":
960
+ return new StepperCell(props, this._values);
961
+ case "boolean":
962
+ return new BooleanCell(props, this._values);
963
+ case "slider":
964
+ return new SliderCell(props, this._values);
965
+ case "list":
966
+ return new ListCell(props, this._values);
967
+ case "tab":
968
+ return new TabCell(props, this._values);
969
+ case "info":
970
+ return new InfoCell(props, this._values);
971
+ case "link":
972
+ return new LinkCell(props, this._values);
973
+ case "action":
974
+ return new ActionCell(props, this._values);
975
+ default:
976
+ throw new Error("Invalid cell type");
977
+ }
978
+ }
979
+
980
+ get values() {
981
+ return this._values;
982
+ }
983
+
984
+ set(key: string, value: any) {
985
+ this._cells.forEach(section => {
986
+ section.rows.forEach(row => {
987
+ if (row.key === key) row.value = value;
988
+ });
989
+ });
990
+ }
991
+ }