jsbox-cview 1.0.0 → 1.1.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/dist/components/alert/input-alert.js +39 -0
  2. package/dist/components/alert/login-alert.js +45 -0
  3. package/dist/components/alert/plain-alert.js +25 -0
  4. package/dist/components/alert/uialert.js +89 -0
  5. package/dist/components/artificial-flowlayout.js +258 -0
  6. package/dist/components/base.js +43 -0
  7. package/dist/components/custom-navigation-bar.js +519 -0
  8. package/dist/components/dialogs/dialog-sheet.js +67 -0
  9. package/dist/components/dialogs/form-dialog.js +24 -0
  10. package/dist/components/dialogs/list-dialog.js +87 -0
  11. package/dist/components/dialogs/text-dialog.js +31 -0
  12. package/dist/components/dynamic-itemsize-matrix.js +129 -0
  13. package/dist/components/dynamic-preference-listview.js +557 -0
  14. package/dist/components/dynamic-rowheight-list.js +44 -0
  15. package/dist/components/enhanced-imageview.js +114 -0
  16. package/dist/components/image-pager.js +157 -0
  17. package/dist/components/page-control.js +76 -0
  18. package/dist/components/pageviewer-titlebar.js +143 -0
  19. package/dist/components/pageviewer.js +96 -0
  20. package/dist/components/rotating-view.js +102 -0
  21. package/dist/components/searchbar.js +322 -0
  22. package/dist/components/sheet.js +82 -0
  23. package/dist/components/single-views.js +429 -0
  24. package/dist/components/spinners/loading-double-rings.js +104 -0
  25. package/dist/components/spinners/loading-dual-ring.js +82 -0
  26. package/dist/components/spinners/loading-wedges.js +104 -0
  27. package/dist/components/spinners/spinner-androidstyle.js +248 -0
  28. package/dist/components/static-preference-listview.js +798 -0
  29. package/dist/components/symbol-button.js +79 -0
  30. package/dist/components/tabbar.js +357 -0
  31. package/dist/controller/base-controller.js +178 -0
  32. package/dist/controller/controller-router.js +68 -0
  33. package/dist/controller/pageviewer-controller.js +63 -0
  34. package/dist/controller/presented-page-controller.js +48 -0
  35. package/dist/controller/splitview-controller.js +252 -0
  36. package/dist/controller/tabbar-controller.js +74 -0
  37. package/dist/index.js +58 -0
  38. package/dist/test.js +1 -0
  39. package/dist/utils/colors.js +15 -0
  40. package/dist/utils/cvid.js +28 -0
  41. package/dist/utils/l10n.js +44 -0
  42. package/dist/utils/path.js +107 -0
  43. package/dist/utils/rect.js +72 -0
  44. package/dist/utils/uitools.js +95 -0
  45. package/index.ts +42 -0
  46. package/package.json +4 -3
  47. package/tsconfig.json +5 -3
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * # CView InputAlert
4
+ *
5
+ * 显示一个输入框提示
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.inputAlert = void 0;
9
+ const uialert_1 = require("./uialert");
10
+ const l10n_1 = require("../../utils/l10n");
11
+ function inputAlert({ title = "", message = "", text = "", placeholder, type = 0, secure = false, cancelText = (0, l10n_1.l10n)("CANCEL"), confirmText = (0, l10n_1.l10n)("OK") }) {
12
+ return new Promise((resolve, reject) => {
13
+ const alertVC = new uialert_1.UIAlertController(title, message, uialert_1.UIAlertControllerStyle.Alert);
14
+ alertVC.addTextField({
15
+ placeholder,
16
+ text,
17
+ type,
18
+ secure,
19
+ events: {
20
+ shouldReturn: () => {
21
+ const input = alertVC.getText(0);
22
+ const isValid = input.length > 0;
23
+ return isValid;
24
+ }
25
+ }
26
+ });
27
+ alertVC.addAction(new uialert_1.UIAlertAction(cancelText, uialert_1.UIAlertActionStyle.Destructive, cancelEvent));
28
+ alertVC.addAction(new uialert_1.UIAlertAction(confirmText, uialert_1.UIAlertActionStyle.Default, confirmEvent));
29
+ alertVC.present();
30
+ function confirmEvent() {
31
+ const input = alertVC.getText(0);
32
+ resolve(input);
33
+ }
34
+ function cancelEvent() {
35
+ reject("cancel");
36
+ }
37
+ });
38
+ }
39
+ exports.inputAlert = inputAlert;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * # CView LoginAlert
4
+ *
5
+ * 显示一个登录输入框提示
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.loginAlert = void 0;
9
+ const uialert_1 = require("./uialert");
10
+ const l10n_1 = require("../../utils/l10n");
11
+ function loginAlert({ title = "", message = "", placeholder1 = "", placeholder2 = "", cancelText = (0, l10n_1.l10n)("CANCEL"), confirmText = (0, l10n_1.l10n)("OK") } = {}) {
12
+ return new Promise((resolve, reject) => {
13
+ const alertVC = new uialert_1.UIAlertController(title, message, uialert_1.UIAlertControllerStyle.Alert);
14
+ alertVC.addTextField({
15
+ placeholder: placeholder1
16
+ });
17
+ alertVC.addTextField({
18
+ placeholder: placeholder2,
19
+ secure: true,
20
+ events: {
21
+ shouldReturn: () => {
22
+ const username = alertVC.getText(0);
23
+ const password = alertVC.getText(1);
24
+ const isValid = username.length > 0 && password.length > 0;
25
+ return isValid;
26
+ }
27
+ }
28
+ });
29
+ alertVC.addAction(new uialert_1.UIAlertAction(cancelText, uialert_1.UIAlertActionStyle.Destructive, cancelEvent));
30
+ alertVC.addAction(new uialert_1.UIAlertAction(confirmText, uialert_1.UIAlertActionStyle.Default, confirmEvent));
31
+ alertVC.present();
32
+ function confirmEvent() {
33
+ const username = alertVC.getText(0);
34
+ const password = alertVC.getText(1);
35
+ resolve({
36
+ username,
37
+ password
38
+ });
39
+ }
40
+ function cancelEvent() {
41
+ reject("cancel");
42
+ }
43
+ });
44
+ }
45
+ exports.loginAlert = loginAlert;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * # CView PlainAlert
4
+ *
5
+ * 显示一个文字提示
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.plainAlert = void 0;
9
+ const uialert_1 = require("./uialert");
10
+ const l10n_1 = require("../../utils/l10n");
11
+ function plainAlert({ title = "", message = "", cancelText = (0, l10n_1.l10n)("CANCEL"), confirmText = (0, l10n_1.l10n)("OK") } = {}) {
12
+ return new Promise((resolve, reject) => {
13
+ const alertVC = new uialert_1.UIAlertController(title, message, uialert_1.UIAlertControllerStyle.Alert);
14
+ alertVC.addAction(new uialert_1.UIAlertAction(cancelText, uialert_1.UIAlertActionStyle.Destructive, cancelEvent));
15
+ alertVC.addAction(new uialert_1.UIAlertAction(confirmText, uialert_1.UIAlertActionStyle.Default, confirmEvent));
16
+ alertVC.present();
17
+ function confirmEvent() {
18
+ resolve("ok");
19
+ }
20
+ function cancelEvent() {
21
+ reject("cancel");
22
+ }
23
+ });
24
+ }
25
+ exports.plainAlert = plainAlert;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * Alert的基础类
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UIAlertController = exports.UIAlertAction = exports.UIAlertControllerStyle = exports.UIAlertActionStyle = void 0;
7
+ exports.UIAlertActionStyle = {
8
+ Default: 0,
9
+ Cancel: 1,
10
+ Destructive: 2
11
+ };
12
+ exports.UIAlertControllerStyle = {
13
+ ActionSheet: 0,
14
+ Alert: 1
15
+ };
16
+ class UIAlertAction {
17
+ constructor(title, style = exports.UIAlertActionStyle.Default, handler) {
18
+ this.title = title;
19
+ this.style = style;
20
+ this.instance = $objc("UIAlertAction").$actionWithTitle_style_handler(title, style, $block("void, UIAlertAction *", () => {
21
+ if (handler) {
22
+ handler(this);
23
+ }
24
+ }));
25
+ }
26
+ }
27
+ exports.UIAlertAction = UIAlertAction;
28
+ class UIAlertController {
29
+ constructor(title, message, style = exports.UIAlertControllerStyle.ActionSheet) {
30
+ this.title = title;
31
+ this.message = message;
32
+ this.style = style;
33
+ this.instance = $objc("UIAlertController").$alertControllerWithTitle_message_preferredStyle(title, message, style);
34
+ }
35
+ addAction(action) {
36
+ this.instance.$addAction(action.instance);
37
+ }
38
+ addTextField(options) {
39
+ this.instance.$addTextFieldWithConfigurationHandler($block("void, UITextField *", (textField) => {
40
+ textField.$setClearButtonMode(1);
41
+ if (options.type) {
42
+ textField.$setKeyboardType(options.type);
43
+ }
44
+ if (options.placeholder) {
45
+ textField.$setPlaceholder(options.placeholder);
46
+ }
47
+ if (options.text) {
48
+ textField.$setText(options.text);
49
+ }
50
+ if (options.textColor) {
51
+ textField.$setTextColor(options.textColor.ocValue());
52
+ }
53
+ if (options.font) {
54
+ textField.$setFont(options.font.ocValue());
55
+ }
56
+ if (options.align) {
57
+ textField.$setTextAlignment(options.align);
58
+ }
59
+ if (options.secure) {
60
+ textField.$setSecureTextEntry(true);
61
+ }
62
+ if (options.events) {
63
+ const events = options.events;
64
+ textField.$setDelegate($delegate({
65
+ type: "UITextFieldDelegate",
66
+ events: {
67
+ "textFieldShouldReturn:": (textField) => {
68
+ if (events.shouldReturn) {
69
+ return events.shouldReturn();
70
+ }
71
+ else {
72
+ return true;
73
+ }
74
+ }
75
+ }
76
+ }));
77
+ }
78
+ }));
79
+ }
80
+ getText(index) {
81
+ const textField = this.instance.$textFields().$objectAtIndex(index);
82
+ const text = textField.$text();
83
+ return text.jsValue();
84
+ }
85
+ present() {
86
+ this.instance.$show();
87
+ }
88
+ }
89
+ exports.UIAlertController = UIAlertController;
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ /**
3
+ * # cview ArtificialFlowlayout
4
+ *
5
+ * 仿制的 flowlayout
6
+ * 通过在右侧插入空白的 item,从而作出假象的左对齐。
7
+ *
8
+ * 已知问题:
9
+ *
10
+ * - 当某一行中所占用的总宽度恰好使得右侧的剩余宽度在 1 spacing 到 2 spacing 之间,此时无法插入空白 item,这一行的 spacing 将会被拉宽
11
+ * - 不可以依赖 indexPath,请将原数据和此 CView 的数据分离,通过 data 中加入标记的方法来定位的原数据
12
+ *
13
+ * !!! layout 当中必须有关于 height 的约束 !!!
14
+ *
15
+ * - columns 不定,item 宽度不相等但高度固定,spacing 固定,左对齐,自动换行
16
+ * - 不可滚动,会自动调整自身的高度,因而拥有方法 heightToWidth(width: number): number 用于事前确定其应有的高度
17
+ * 事件 heightChanged: (cview, height) => void 用于高度变更时回调
18
+ * - 此控件不能直接指定 props.data,而是需要指定 sections,并且每个 item 都需要改为{data: any, width: number} 其中 data 代表原 item 的内容,width 代表其应有的宽度
19
+ * - 如果 item.width > frame.width - 2 spacing, 那么生成的对应 item 将单独占用一行,其宽度为 frame.width - 2 spacing
20
+ * -
21
+ *
22
+ * 特别参数
23
+ *
24
+ * - sections: {title: string, items: {data: any, width: number}[]}[] 即使只有单个 section 也必须用多 sections 的写法
25
+ * 此参数可以在 cview.view 生成后重新写入
26
+ *
27
+ * props:
28
+ *
29
+ * - itemHeight 默认为 40
30
+ * - spacing 默认为 5
31
+ * - scrollEnabled 固定为 false
32
+ *
33
+ * 除了 props: data 和 events: itemSize 不可用,其他均和 matrix 一致
34
+ *
35
+ * methods
36
+ *
37
+ * - heightToWidth(width: number): number
38
+ * - getSectionHeights(width: number): number[]
39
+ * - reload()
40
+ */
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.ArtificialFlowlayout = void 0;
43
+ const base_1 = require("./base");
44
+ const single_views_1 = require("./single-views");
45
+ const cvid_1 = require("../utils/cvid");
46
+ class ArtificialFlowlayout extends base_1.Base {
47
+ /**
48
+ * 创建一个人工流式布局实例。
49
+ * @param sections - 此控件不能直接指定 props.data,而是需要指定 sections,
50
+ * 并且每个 item 都需要改为{data: any, width: number} 其中 data 代表原 item 的内容,width 代表其应有的宽度
51
+ * @param props - 矩阵的属性。不可指定columns,可以指定spacing,itemHeight,template。scrollEnabled固定为false。
52
+ * @param layout - 视图的布局函数。
53
+ * @param events - 事件处理程序的对象。
54
+ * - didSelect: (cview, indexPath, data): void - 其中indexPath是原sections的indexPath,data是原item的内容。
55
+ * - didLongPress: (cview, indexPath, data): void
56
+ * - heightChanged: (cview, height): void
57
+ *
58
+ * @method set sections - 设置 sections。
59
+ * @method get sections - 获取 sections。
60
+ * @method heightToWidth - 根据宽度计算高度.
61
+ *
62
+ */
63
+ constructor({ sections, props, layout, events }) {
64
+ super();
65
+ this._sections = sections;
66
+ this._innerSections = [];
67
+ this._props = Object.assign(Object.assign({ spacing: 5, itemHeight: 40 }, props), { scrollEnabled: false, columns: undefined });
68
+ this._cellRootViewId = cvid_1.cvid.newId;
69
+ this._width = 300;
70
+ this._height = 0;
71
+ this._props.template = this._addCellIdForTemplate(this._props.template);
72
+ this._matrix = new single_views_1.Matrix({
73
+ props: Object.assign(Object.assign({}, this._props), { itemHeight: undefined }),
74
+ layout: $layout.fill,
75
+ events: {
76
+ itemSize: (sender, indexPath) => {
77
+ const item = this._innerSections[indexPath.section].items[indexPath.item];
78
+ return $size(Math.max(item.width, 0), this._props.itemHeight || 40);
79
+ },
80
+ didSelect: (sender, indexPath, data) => {
81
+ const item = this._innerSections[indexPath.section].items[indexPath.item];
82
+ if (item.blank)
83
+ return;
84
+ const realIndexPath = $indexPath(indexPath.section, item.realIndex);
85
+ if (events.didSelect)
86
+ events.didSelect(this, realIndexPath, data);
87
+ },
88
+ didLongPress: (sender, indexPath, data) => {
89
+ const item = this._innerSections[indexPath.section].items[indexPath.item];
90
+ if (item.blank)
91
+ return;
92
+ const realIndexPath = $indexPath(indexPath.section, item.realIndex);
93
+ if (events.didSelect)
94
+ events.didSelect(this, realIndexPath, data);
95
+ }
96
+ }
97
+ });
98
+ this._defineView = () => {
99
+ return {
100
+ type: "view",
101
+ props: {
102
+ id: this.id
103
+ },
104
+ layout: layout,
105
+ events: {
106
+ layoutSubviews: sender => {
107
+ sender.relayout();
108
+ this._width = sender.frame.width;
109
+ this._innerSections = this._addBlankItem(this._sections, this._width, this._props.spacing || 5);
110
+ const height = this._calculateSectionsHeight(this._innerSections, this._width, this._props.spacing || 5, this._props.itemHeight || 40);
111
+ this.view.updateLayout((make, view) => make.height.equalTo(height));
112
+ this._props.data = this._innerSections.map(n => ({
113
+ title: n.title,
114
+ items: n.items.map(n => n.data)
115
+ }));
116
+ this._matrix.view.data = this._props.data;
117
+ if (height !== this._height && events.heightChanged)
118
+ events.heightChanged(this, height);
119
+ this._height = height;
120
+ }
121
+ },
122
+ views: [this._matrix.definition]
123
+ };
124
+ };
125
+ }
126
+ set sections(sections) {
127
+ this._sections = sections;
128
+ this._innerSections = this._addBlankItem(this._sections, this._width, this._props.spacing || 5);
129
+ this._props.data = this._innerSections.map(n => ({
130
+ title: n.title,
131
+ items: n.items.map(n => n.data)
132
+ }));
133
+ this._matrix.view.data = this._props.data;
134
+ }
135
+ get sections() {
136
+ return this._sections;
137
+ }
138
+ /**
139
+ * 为每个 section 的 items 添加空白占位的 item。
140
+ * @param sections - 原始数据的数组,每个元素包含一个标题和一组项目。
141
+ * @param containerWidth - 容器的宽度。
142
+ * @param spacing - 项目之间的间距。
143
+ * @returns 带有空白占位的 item 的数组。
144
+ */
145
+ _addBlankItem(sections, containerWidth, spacing) {
146
+ return sections.map((section) => {
147
+ let lineWidth = spacing; // 当前行已使用的宽度
148
+ const newItems = [];
149
+ let index = 0;
150
+ for (const item of section.items) {
151
+ const itemWidthWithSpacing = item.width + spacing;
152
+ // 检查是否需要换行:当前行宽度 + 项目宽度 + 间距 > 容器宽度
153
+ if (lineWidth + itemWidthWithSpacing > containerWidth && lineWidth > spacing) {
154
+ // 插入空白项目填充剩余宽度
155
+ const blankWidth = containerWidth - lineWidth + spacing; // 计算空白项目宽度
156
+ if (blankWidth > 0) { // 防止插入宽度为负的空白项目
157
+ const blankItemData = {};
158
+ blankItemData[this._cellRootViewId] = { hidden: true };
159
+ newItems.push({
160
+ data: blankItemData,
161
+ width: blankWidth,
162
+ blank: true,
163
+ realIndex: -1
164
+ });
165
+ // 然后换行,重置lineWidth
166
+ lineWidth = spacing + itemWidthWithSpacing;
167
+ newItems.push({
168
+ data: item.data,
169
+ width: item.width,
170
+ blank: false,
171
+ realIndex: index
172
+ });
173
+ }
174
+ else { // 当剩余宽度恰好大于spacing但不超过2倍spacing时,需要直接换行,但这行的spacing会被稍微拉长
175
+ // 直接换行,重置lineWidth
176
+ lineWidth = spacing + itemWidthWithSpacing;
177
+ newItems.push({
178
+ data: item.data,
179
+ width: item.width,
180
+ blank: false,
181
+ realIndex: index
182
+ });
183
+ }
184
+ }
185
+ else {
186
+ newItems.push({
187
+ data: item.data,
188
+ width: item.width,
189
+ blank: false,
190
+ realIndex: index
191
+ });
192
+ lineWidth += itemWidthWithSpacing;
193
+ }
194
+ index++;
195
+ }
196
+ return {
197
+ title: section.title,
198
+ items: newItems,
199
+ };
200
+ });
201
+ }
202
+ /**
203
+ * 计算 sections 的高度。
204
+ * @param sections - 带有空白占位的 item 的数组。
205
+ * @param containerWidth - 容器的宽度。
206
+ * @param spacing - 项目之间的间距。
207
+ * @param itemHeight - 项目的高度。
208
+ * @returns 计算得到的高度值。
209
+ */
210
+ _calculateSectionsHeight(sections, containerWidth, spacing, itemHeight) {
211
+ let height = spacing; // 当前行已使用的高度
212
+ const lineHeight = itemHeight + spacing; // 每行的高度,包括项目高度和间距
213
+ for (const section of sections) {
214
+ let lineWidth = 0; // 当前行已使用的宽度
215
+ for (const item of section.items) {
216
+ const itemWidthWithSpacing = item.width + spacing;
217
+ // 检查是否需要换行:当前行宽度 + 项目宽度 + 间距 > 容器宽度
218
+ if (lineWidth + itemWidthWithSpacing > containerWidth && lineWidth > spacing) {
219
+ // 换行,重置lineWidth
220
+ lineWidth = spacing + itemWidthWithSpacing;
221
+ height += lineHeight;
222
+ }
223
+ else {
224
+ lineWidth += itemWidthWithSpacing;
225
+ }
226
+ }
227
+ }
228
+ return height;
229
+ }
230
+ _addCellIdForTemplate(template) {
231
+ if (!template || typeof template !== "object")
232
+ throw new Error("Invalid template");
233
+ const topProps = template.props;
234
+ if (topProps && topProps.id)
235
+ delete topProps.id;
236
+ return {
237
+ props: {},
238
+ views: [
239
+ {
240
+ type: "view",
241
+ props: Object.assign(Object.assign({}, topProps), { id: this._cellRootViewId }),
242
+ layout: $layout.fill,
243
+ views: template.views
244
+ }
245
+ ]
246
+ };
247
+ }
248
+ /**
249
+ * 根据宽度计算高度。
250
+ * @param width - 宽度值。
251
+ * @returns 计算得到的高度值。
252
+ */
253
+ heightToWidth(width) {
254
+ const innerSections = this._addBlankItem(this._sections, width, this._props.spacing || 5);
255
+ return this._calculateSectionsHeight(innerSections, width, this._props.spacing || 5, this._props.itemHeight || 40);
256
+ }
257
+ }
258
+ exports.ArtificialFlowlayout = ArtificialFlowlayout;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /**
3
+ * # Base<T extends AllUIView, R extends UiTypes.AllViewOptions>
4
+ * 组件 Base 基类,用于创建自定义组件。CView 的所有子类都应该继承自 Base。
5
+ *
6
+ * ## 属性
7
+ * - id: string 组件的唯一 id
8
+ * - view: T 通过自动创建的唯一 id 进行查找,获得对应的 UIView 对象
9
+ * - definition: R 获得对应的 view 的定义
10
+ *
11
+ * ## 方法
12
+ * - add(view: UiTypes.AllViewOptions | Base<any, any>): void 添加子 view。其中 `view` 参数可以是 CView,或 view 的定义
13
+ *
14
+ * ## 说明
15
+ * 视图是 CView 框架的重点,其名称的含义为“组件化视图”。设计目的是:
16
+ * - 通过组件化的方式,将JSBox view的定义和实例绑定,使用起来更加方便
17
+ * - 可以便利地创建自定义组件
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.Base = void 0;
21
+ const cvid_1 = require("../utils/cvid");
22
+ class Base {
23
+ constructor() {
24
+ this.id = cvid_1.cvid.newId;
25
+ }
26
+ get definition() {
27
+ return this._defineView();
28
+ }
29
+ get view() {
30
+ if (!this._view)
31
+ this._view = $(this.id);
32
+ return this._view;
33
+ }
34
+ add(view) {
35
+ if (view instanceof Base) {
36
+ this.view.add(view.definition);
37
+ }
38
+ else {
39
+ this.view.add(view);
40
+ }
41
+ }
42
+ }
43
+ exports.Base = Base;