clickgo 3.1.4-dev13 → 3.1.6-dev15

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 (96) hide show
  1. package/README.md +7 -7
  2. package/dist/app/demo/app.js +29 -3
  3. package/dist/app/demo/config.json +22 -3
  4. package/dist/app/demo/form/control/box/box.js +66 -0
  5. package/dist/app/demo/form/control/box/box.xml +18 -0
  6. package/dist/app/demo/form/control/button/button.js +24 -1
  7. package/dist/app/demo/form/control/check/check.js +24 -1
  8. package/dist/app/demo/form/control/dialog/dialog.js +24 -1
  9. package/dist/app/demo/form/control/file/file.js +24 -1
  10. package/dist/app/demo/form/control/flow/flow.js +24 -1
  11. package/dist/app/demo/form/control/form/form.js +24 -1
  12. package/dist/app/demo/form/control/layout/layout.js +57 -0
  13. package/dist/app/demo/form/control/layout/layout.xml +16 -0
  14. package/dist/app/demo/form/control/list/list.js +24 -1
  15. package/dist/app/demo/form/control/list/list.xml +8 -2
  16. package/dist/app/demo/form/control/marquee/marquee.js +24 -2
  17. package/dist/app/demo/form/control/marquee/marquee.xml +2 -5
  18. package/dist/app/demo/form/control/menu/menu.js +24 -1
  19. package/dist/app/demo/form/control/monaco/monaco.js +24 -1
  20. package/dist/app/demo/form/control/nav/nav.js +52 -0
  21. package/dist/app/demo/form/control/nav/nav.xml +43 -0
  22. package/dist/app/demo/form/control/panel/panel.js +67 -0
  23. package/dist/app/demo/form/control/panel/panel.xml +11 -0
  24. package/dist/app/demo/form/control/panel/test1.js +58 -0
  25. package/dist/app/demo/form/control/panel/test1.xml +16 -0
  26. package/dist/app/demo/form/control/panel/test2.xml +3 -0
  27. package/dist/app/demo/form/control/property/property.js +24 -1
  28. package/dist/app/demo/form/control/radio/radio.js +24 -1
  29. package/dist/app/demo/form/control/scroll/scroll.js +25 -1
  30. package/dist/app/demo/form/control/scroll/scroll.xml +5 -2
  31. package/dist/app/demo/form/control/select/select.js +24 -1
  32. package/dist/app/demo/form/control/tab/tab.js +24 -1
  33. package/dist/app/demo/form/control/table/table.js +164 -0
  34. package/dist/app/demo/form/control/table/table.xml +35 -0
  35. package/dist/app/demo/form/control/text/text.js +25 -1
  36. package/dist/app/demo/form/control/text/text.xml +1 -1
  37. package/dist/app/demo/form/control/vflow/vflow.js +24 -1
  38. package/dist/app/demo/form/event/form/form.js +24 -1
  39. package/dist/app/demo/form/event/other/other.js +24 -1
  40. package/dist/app/demo/form/event/screen/screen.js +24 -1
  41. package/dist/app/demo/form/event/task/task.js +24 -1
  42. package/dist/app/demo/form/main.js +130 -84
  43. package/dist/app/demo/form/main.xml +5 -0
  44. package/dist/app/demo/form/method/aform/aform.js +29 -15
  45. package/dist/app/demo/form/method/aform/aform.xml +0 -1
  46. package/dist/app/demo/form/method/aform/sd.js +25 -5
  47. package/dist/app/demo/form/method/core/core.js +24 -1
  48. package/dist/app/demo/form/method/dom/dom.js +48 -2
  49. package/dist/app/demo/form/method/dom/dom.xml +11 -0
  50. package/dist/app/demo/form/method/form/form.js +40 -7
  51. package/dist/app/demo/form/method/form/form.xml +3 -0
  52. package/dist/app/demo/form/method/{aform → form}/test.xml +0 -0
  53. package/dist/app/demo/form/method/fs/fs.js +139 -8
  54. package/dist/app/demo/form/method/fs/fs.xml +11 -1
  55. package/dist/app/demo/form/method/fs/text.js +24 -1
  56. package/dist/app/demo/form/method/native/native.js +24 -1
  57. package/dist/app/demo/form/method/system/system.js +24 -1
  58. package/dist/app/demo/form/method/task/task.js +31 -4
  59. package/dist/app/demo/form/method/task/task.xml +6 -1
  60. package/dist/app/demo/form/method/theme/theme.js +24 -1
  61. package/dist/app/demo/form/method/tool/tool.js +38 -1
  62. package/dist/app/demo/form/method/tool/tool.xml +1 -0
  63. package/dist/app/demo/form/method/zip/zip.js +30 -7
  64. package/dist/app/task/app.js +29 -3
  65. package/dist/app/task/form/bar/bar.js +24 -1
  66. package/dist/clickgo.js +33 -10
  67. package/dist/control/box.cgc +0 -0
  68. package/dist/control/common.cgc +0 -0
  69. package/dist/control/form.cgc +0 -0
  70. package/dist/control/monaco.cgc +0 -0
  71. package/dist/control/nav.cgc +0 -0
  72. package/dist/control/property.cgc +0 -0
  73. package/dist/control/table.cgc +0 -0
  74. package/dist/control/task.cgc +0 -0
  75. package/dist/global.css +1 -1
  76. package/dist/lib/control.js +53 -12
  77. package/dist/lib/control.ts +25 -5
  78. package/dist/lib/core.js +44 -50
  79. package/dist/lib/core.ts +18 -48
  80. package/dist/lib/dom.js +322 -108
  81. package/dist/lib/dom.ts +394 -127
  82. package/dist/lib/form.js +590 -226
  83. package/dist/lib/form.ts +706 -267
  84. package/dist/lib/fs.js +485 -224
  85. package/dist/lib/fs.ts +493 -287
  86. package/dist/lib/native.js +24 -1
  87. package/dist/lib/task.js +159 -139
  88. package/dist/lib/task.ts +148 -130
  89. package/dist/lib/theme.js +27 -4
  90. package/dist/lib/tool.js +57 -2
  91. package/dist/lib/tool.ts +68 -1
  92. package/dist/lib/zip.js +29 -3
  93. package/dist/lib/zip.ts +1 -1
  94. package/dist/theme/familiar.cgt +0 -0
  95. package/package.json +5 -7
  96. package/types/index.d.ts +51 -70
package/dist/lib/dom.ts CHANGED
@@ -20,7 +20,7 @@ import * as core from './core';
20
20
  import * as tool from './tool';
21
21
 
22
22
  /** --- style list 的 div --- */
23
- const topClass: string[] = ['#cg-form-list', '#cg-pop-list', '#cg-notify', '#cg-simpletask', '#cg-launcher'];
23
+ const topClass: string[] = ['#cg-form-list', '#cg-pop-list', '#cg-notify', '#cg-simpletask', '#cg-launcher', '#cg-confirm'];
24
24
  function classUnfold(after?: string, out: string[] = []): string {
25
25
  const arr: string[] = [];
26
26
  for (const name of topClass) {
@@ -45,6 +45,14 @@ ${classUnfold('*')}, ${classUnfold('*::after')}, ${classUnfold('*::before')} {bo
45
45
  ${classUnfold()}, ${classUnfold('input')}, ${classUnfold('textarea')} {font-family: "Lucida Sans Unicode", "Helvetica Neue","Helvetica","PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Noto Sans CJK","Source Han Sans","WenQuanYi Micro Hei","Microsoft YaHei",sans-serif; font-size: 12px; line-height: 1; -webkit-font-smoothing: antialiased;}
46
46
  </style>`);
47
47
 
48
+ /**
49
+ * --- 判断一个元素是否还存在于页面当中 ---
50
+ * @param el 要判断的元素
51
+ */
52
+ export function inPage(el: HTMLElement): boolean {
53
+ return document.body.contains(el);
54
+ }
55
+
48
56
  /** --- 全局 cursor 设置的 style 标签 --- */
49
57
  let globalCursorStyle: HTMLStyleElement;
50
58
  /**
@@ -93,7 +101,7 @@ export function hasTouchButMouse(e: MouseEvent | TouchEvent | PointerEvent): boo
93
101
  }
94
102
 
95
103
  /**
96
- * --- 创建任务时连同一起创建的 style 标签 ---
104
+ * --- 创建任务时连同一起创建的 style 标签,App 模式下无效 ---
97
105
  * @param taskId 任务 id
98
106
  */
99
107
  export function createToStyleList(taskId: number): void {
@@ -101,7 +109,7 @@ export function createToStyleList(taskId: number): void {
101
109
  }
102
110
 
103
111
  /**
104
- * --- 任务结束时需要移除 task 的所有 style ---
112
+ * --- 任务结束时需要移除 task 的所有 style,App 模式下无效 ---
105
113
  * @param taskId 任务 id
106
114
  */
107
115
  export function removeFromStyleList(taskId: number): void {
@@ -109,13 +117,14 @@ export function removeFromStyleList(taskId: number): void {
109
117
  }
110
118
 
111
119
  /**
112
- * --- 将 style 内容写入 dom ---
120
+ * --- 将 style 内容写入 dom,App 模式下无效 ---
113
121
  * @param taskId 当前任务 ID
114
122
  * @param style 样式内容
115
123
  * @param type 插入的类型
116
- * @param formId 当前窗体 ID(global 下可空,theme 下为主题唯一标识符)
124
+ * @param formId 当前窗体 ID(global 下可空,theme 下为主题唯一标识符,control 下为控件名)
125
+ * @param panelId 若是 panel 中创建的则需要指定 panelId,仅 type 为 form 有效
117
126
  */
118
- export function pushStyle(taskId: number, style: string, type: 'global' | 'theme' | 'control' | 'form' = 'global', formId: number | string = 0): void {
127
+ export function pushStyle(taskId: number, style: string, type: 'global' | 'theme' | 'control' | 'form' = 'global', formId: number | string = 0, panelId?: number): void {
119
128
  const el = document.querySelector(`#cg-style-task${taskId} > .cg-style-${type}`);
120
129
  if (!el) {
121
130
  return;
@@ -127,17 +136,19 @@ export function pushStyle(taskId: number, style: string, type: 'global' | 'theme
127
136
  el.insertAdjacentHTML('beforeend', `<style data-name="${formId}">${style}</style>`);
128
137
  }
129
138
  else {
130
- el.insertAdjacentHTML('beforeend', `<style class="cg-style-form${formId}">${style}</style>`);
139
+ // --- form ---
140
+ el.insertAdjacentHTML('beforeend', `<style class="cg-style-form${formId}" data-panel="${panelId ? panelId.toString() : ''}">${style}</style>`);
131
141
  }
132
142
  }
133
143
 
134
144
  /**
135
- * --- 移除 style 样式 dom ---
145
+ * --- 移除 style 样式 dom,App 模式下无效 ---
136
146
  * @param taskId 要移除的任务 ID
137
147
  * @param type 移除的类型
138
148
  * @param formId 要移除的窗体 ID
149
+ * @param panelId type 为 form 模式下若不指定则当前 form 包含 panel 的样式都会被移除
139
150
  */
140
- export function removeStyle(taskId: number, type: 'global' | 'theme' | 'control' | 'form' = 'global', formId: number | string = 0): void {
151
+ export function removeStyle(taskId: number, type: 'global' | 'theme' | 'control' | 'form' = 'global', formId: number | string = 0, panelId?: number): void {
141
152
  const styleTask = document.getElementById('cg-style-task' + taskId.toString());
142
153
  if (!styleTask) {
143
154
  return;
@@ -165,7 +176,8 @@ export function removeStyle(taskId: number, type: 'global' | 'theme' | 'control'
165
176
  }
166
177
  }
167
178
  else {
168
- const elist = styleTask.querySelectorAll('.cg-style-form' + formId.toString());
179
+ // --- form ---
180
+ const elist = styleTask.querySelectorAll('.cg-style-form' + formId.toString() + (panelId ? '[data-panel="' + panelId.toString() + '"]' : ''));
169
181
  for (let i = 0; i < elist.length; ++i) {
170
182
  elist.item(i).remove();
171
183
  }
@@ -188,6 +200,24 @@ export function getStyleCount(taskId: number, type: 'theme' | 'control' | 'form'
188
200
  /** --- 被监视中的元素 --- */
189
201
  const watchSizeList: Record<string, types.IWatchSizeItem> = {};
190
202
 
203
+ /**
204
+ * --- 获取当前 watch size 中的元素总数 ---
205
+ * @param taskId 留空则获取全部总数 ---
206
+ */
207
+ export function getWatchSizeCount(taskId?: number): number {
208
+ if (!taskId) {
209
+ return Object.keys(watchSizeList).length;
210
+ }
211
+ let count = 0;
212
+ for (const id in watchSizeList) {
213
+ if (watchSizeList[id].taskId !== taskId) {
214
+ continue;
215
+ }
216
+ ++count;
217
+ }
218
+ return count;
219
+ }
220
+
191
221
  /** --- 监视元素的 data-cg-roindex --- */
192
222
  let watchSizeIndex: number = 0;
193
223
 
@@ -195,7 +225,7 @@ let watchSizeIndex: number = 0;
195
225
  const resizeObserver = new ResizeObserver(function(entries): void {
196
226
  for (const entrie of entries) {
197
227
  const el = entrie.target as HTMLElement;
198
- if (!el.offsetParent) {
228
+ if (!document.body.contains(el)) {
199
229
  resizeObserver.unobserve(el);
200
230
  if (watchSizeList[el.dataset.cgRoindex!]) {
201
231
  delete watchSizeList[el.dataset.cgRoindex!];
@@ -218,7 +248,7 @@ const resizeObserver = new ResizeObserver(function(entries): void {
218
248
  });
219
249
 
220
250
  /**
221
- * --- 添加监视 Element 对象大小,元素移除后自动停止监视(浏览器原生效果) ---
251
+ * --- 添加监视 Element 对象大小,元素移除后自动停止监视(浏览器原生效果),已经监视中的不会再次监视 ---
222
252
  * @param el 要监视的大小
223
253
  * @param cb 回调函数
224
254
  * @param immediate 立刻先执行一次回调
@@ -307,6 +337,24 @@ export function clearWatchSize(taskId: number): void {
307
337
  /** --- 监视 dom 变动中的元素 */
308
338
  const watchList: Record<string, types.IWatchItem> = {};
309
339
 
340
+ /**
341
+ * --- 获取当前 watch 中的元素总数 ---
342
+ * @param taskId 留空则获取全部总数 ---
343
+ */
344
+ export function getWatchCount(taskId?: number): number {
345
+ if (!taskId) {
346
+ return Object.keys(watchList).length;
347
+ }
348
+ let count = 0;
349
+ for (const id in watchList) {
350
+ if (watchList[id].taskId !== taskId) {
351
+ continue;
352
+ }
353
+ ++count;
354
+ }
355
+ return count;
356
+ }
357
+
310
358
  /** --- 监视元素的 data-cg-moindex --- */
311
359
  let watchIndex: number = 0;
312
360
 
@@ -317,7 +365,7 @@ let watchIndex: number = 0;
317
365
  * @param mode 监听模式
318
366
  * @param taskId 归属到一个任务里可留空,App 模式下无效
319
367
  */
320
- export function watch(el: HTMLElement, cb: (mutations: MutationRecord[]) => void | Promise<void>, mode: 'child' | 'childsub' | 'style' | 'default' = 'default', immediate: boolean = false, taskId?: number): boolean {
368
+ export function watch(el: HTMLElement, cb: (mutations: MutationRecord[]) => void | Promise<void>, mode: 'child' | 'childsub' | 'style' | 'text' | 'default' = 'default', immediate: boolean = false, taskId?: number): boolean {
321
369
  if (isWatch(el)) {
322
370
  return false;
323
371
  }
@@ -358,11 +406,8 @@ export function watch(el: HTMLElement, cb: (mutations: MutationRecord[]) => void
358
406
  };
359
407
  break;
360
408
  }
361
- case 'default': {
409
+ case 'text': {
362
410
  moi = {
363
- 'attributeFilter': ['style', 'class'],
364
- 'attributeOldValue': true,
365
- 'attributes': true,
366
411
  'characterData': true,
367
412
  'childList': true,
368
413
  'subtree': true
@@ -370,11 +415,18 @@ export function watch(el: HTMLElement, cb: (mutations: MutationRecord[]) => void
370
415
  break;
371
416
  }
372
417
  default: {
373
- moi = mode;
418
+ moi = {
419
+ 'attributeFilter': ['style', 'class'],
420
+ 'attributeOldValue': true,
421
+ 'attributes': true,
422
+ 'characterData': true,
423
+ 'childList': true,
424
+ 'subtree': true
425
+ };
374
426
  }
375
427
  }
376
428
  const mo = new MutationObserver((mutations) => {
377
- if (!el.offsetParent) {
429
+ if (!document.body.contains(el)) {
378
430
  mo.disconnect();
379
431
  if (watchList[index]) {
380
432
  delete watchList[index];
@@ -456,18 +508,58 @@ export function clearWatch(taskId: number): void {
456
508
  }
457
509
  }
458
510
 
511
+ // ----------------------
512
+ // --- watch cg timer ---
513
+ // ----------------------
514
+
515
+ // --- watch 和 watchSize 依靠 cg 去清除元素已经消失但还占用 map 的情况 ---
516
+ // --- style 和 property 因为是我们自己实现的,随时就能知道元素是否已经被移除了 ---
517
+
518
+ const watchCgTimerHandler = function(): void {
519
+ for (const index in watchSizeList) {
520
+ const item = watchSizeList[index];
521
+ if (document.body.contains(item.el)) {
522
+ continue;
523
+ }
524
+ delete watchSizeList[index];
525
+ }
526
+ for (const index in watchList) {
527
+ const item = watchList[index];
528
+ if (document.body.contains(item.el)) {
529
+ continue;
530
+ }
531
+ delete watchList[index];
532
+ }
533
+ window.setTimeout(watchCgTimerHandler, 1000 * 60 * 7);
534
+ };
535
+ watchCgTimerHandler();
536
+
459
537
  // ------------------
460
538
  // --- watchStyle ---
461
539
  // ------------------
462
540
 
463
- const watchStyleList: Record<string, Record<string, {
541
+ interface IWatchStyleItem {
464
542
  'el': HTMLElement;
465
543
  'sd': CSSStyleDeclaration;
466
544
  'names': Record<string, {
467
545
  'val': string;
468
546
  'cb': Array<(name: string, value: string, old: string) => void>;
469
547
  }>;
470
- }>> = {};
548
+ }
549
+
550
+ const watchStyleList: Record<
551
+ /** --- formId --- */
552
+ string,
553
+ Record<
554
+ /** --- panelId 或 default --- */
555
+ string,
556
+ Record<
557
+ /** --- index 值 --- */
558
+ string,
559
+ IWatchStyleItem
560
+ >
561
+ >
562
+ > = {};
471
563
 
472
564
  /** --- 监视元素的 data-cg-styleindex --- */
473
565
  let watchStyleIndex: number = 0;
@@ -494,13 +586,14 @@ export function watchStyle(
494
586
  return;
495
587
  }
496
588
  const formId = formWrap.dataset.formId!;
497
- if (!watchStyleList[formId]) {
498
- watchStyleList[formId] = {};
499
- }
589
+ // --- 获取监视标签的所属 panel ---
590
+ const panelWrap = findParentByData(el, 'panel-id');
591
+ const panelId = panelWrap ? panelWrap.dataset.panelId! : 'default';
592
+ /** --- 监视 index 值 --- */
500
593
  const index = el.dataset.cgStyleindex;
501
594
  if (index) {
502
- const item = watchStyleList[formId][index];
503
595
  // --- 已经有监听了 ---
596
+ const item = watchStyleList[formId][panelId][index];
504
597
  for (const n of name) {
505
598
  if (!item.names[n]) {
506
599
  item.names[n] = {
@@ -517,14 +610,21 @@ export function watchStyle(
517
610
  }
518
611
  return;
519
612
  }
613
+ // --- 创建 object ---
614
+ if (!watchStyleList[formId]) {
615
+ watchStyleList[formId] = {};
616
+ }
617
+ if (!watchStyleList[formId][panelId]) {
618
+ watchStyleList[formId][panelId] = {};
619
+ }
520
620
  // --- 创建监听 ---
521
621
  const sd = getComputedStyle(el);
522
- watchStyleList[formId][watchStyleIndex] = {
622
+ watchStyleList[formId][panelId][watchStyleIndex] = {
523
623
  'el': el,
524
624
  'sd': sd,
525
625
  'names': {}
526
626
  };
527
- const item = watchStyleList[formId][watchStyleIndex];
627
+ const item = watchStyleList[formId][panelId][watchStyleIndex];
528
628
  for (const n of name) {
529
629
  item.names[n] = {
530
630
  'val': (item.sd as any)[n],
@@ -538,41 +638,6 @@ export function watchStyle(
538
638
  ++watchStyleIndex;
539
639
  }
540
640
 
541
- /** --- watch style 的 timer --- */
542
- let watchStyleTimer = 0;
543
- const watchStyleHandler = function(): void {
544
- // --- 为什么要判断 form.getFocus 存在否,因为 form 类可能还没加载出来,这个函数就已经开始执行了 ---
545
- if (form.getFocus) {
546
- // --- 只判断和执行活跃中的窗体的监听事件 ---
547
- const formId: number | null = form.getFocus();
548
- if (formId && watchStyleList[formId]) {
549
- for (const index in watchStyleList[formId]) {
550
- const item = watchStyleList[formId][index];
551
- if (!item.el.offsetParent) {
552
- delete watchStyleList[formId][index];
553
- if (!Object.keys(watchStyleList[formId]).length) {
554
- delete watchStyleList[formId];
555
- }
556
- continue;
557
- }
558
- // --- 执行 cb ---
559
- for (const name in item.names) {
560
- if ((item.sd as any)[name] === item.names[name].val) {
561
- continue;
562
- }
563
- const old = item.names[name].val;
564
- item.names[name].val = (item.sd as any)[name];
565
- for (const cb of item.names[name].cb) {
566
- cb(name, (item.sd as any)[name], old);
567
- }
568
- }
569
- }
570
- }
571
- }
572
- watchStyleTimer = requestAnimationFrame(watchStyleHandler);
573
- };
574
- watchStyleHandler();
575
-
576
641
  /**
577
642
  * --- 检测一个标签是否正在被 watchStyle ---
578
643
  * @param el 要检测的标签
@@ -584,14 +649,26 @@ export function isWatchStyle(el: HTMLElement): boolean {
584
649
  /**
585
650
  * --- 清除某个窗体的所有 watch style 监视,App 模式下无效 ---
586
651
  * @param formId 窗体 id
652
+ * @param panelId 若指定则只清除当前窗体的某个 panel 的 watch
587
653
  */
588
- export function clearWatchStyle(formId: number | string): void {
654
+ export function clearWatchStyle(formId: number | string, panelId?: number): void {
589
655
  if (!watchStyleList[formId]) {
590
656
  return;
591
657
  }
592
- for (const index in watchStyleList[formId]) {
593
- const item = watchStyleList[formId][index];
594
- item.el.removeAttribute('data-cg-styleindex');
658
+ for (const panel in watchStyleList[formId]) {
659
+ if (panelId) {
660
+ if (panel !== panelId.toString()) {
661
+ continue;
662
+ }
663
+ }
664
+ for (const index in watchStyleList[formId][panel]) {
665
+ const item = watchStyleList[formId][panel][index];
666
+ item.el.removeAttribute('data-cg-styleindex');
667
+ }
668
+ delete watchStyleList[formId][panel];
669
+ }
670
+ if (Object.keys(watchStyleList[formId]).length) {
671
+ return;
595
672
  }
596
673
  delete watchStyleList[formId];
597
674
  }
@@ -600,16 +677,30 @@ export function clearWatchStyle(formId: number | string): void {
600
677
  // --- watchProperty ---
601
678
  // ---------------------
602
679
 
603
- /**
604
- * --- 监听中的标签对象,对应 formId -> 数组列表 ---
605
- */
606
- const watchPropertyObjects: Record<string, Record<string, {
680
+ interface IWatchPropertyItem {
607
681
  'el': HTMLElement;
608
682
  'names': Record<string, {
609
683
  'val': string;
610
684
  'cb': Array<(name: string, value: string) => void>;
611
685
  }>;
612
- }>> = {};
686
+ }
687
+
688
+ /**
689
+ * --- 监听中的标签对象,对应 formId -> 数组列表 ---
690
+ */
691
+ const watchPropertyObjects: Record<
692
+ /** --- formId --- */
693
+ string,
694
+ Record<
695
+ /** --- panelId 或 default --- */
696
+ string,
697
+ Record<
698
+ /** --- index 值 --- */
699
+ string,
700
+ IWatchPropertyItem
701
+ >
702
+ >
703
+ > = {};
613
704
 
614
705
  /** --- 监视元素的 data-cg-propertyindex --- */
615
706
  let watchPropertyIndex: number = 0;
@@ -636,13 +727,14 @@ export function watchProperty(
636
727
  return;
637
728
  }
638
729
  const formId = formWrap.dataset.formId!;
639
- if (!watchPropertyObjects[formId]) {
640
- watchPropertyObjects[formId] = {};
641
- }
730
+ // --- 获取监视标签的所属 panel ---
731
+ const panelWrap = findParentByData(el, 'panel-id');
732
+ const panelId = panelWrap ? panelWrap.dataset.panelId! : 'default';
733
+ /** --- 监视 index 值 --- */
642
734
  const index = el.dataset.cgPropertyindex;
643
735
  if (index) {
644
- const item = watchPropertyObjects[formId][index];
645
736
  // --- 已经有监听了 ---
737
+ const item = watchPropertyObjects[formId][panelId][index];
646
738
  for (const n of name) {
647
739
  if (!item.names[n]) {
648
740
  item.names[n] = {
@@ -659,12 +751,19 @@ export function watchProperty(
659
751
  }
660
752
  return;
661
753
  }
754
+ // --- 创建 object ---
755
+ if (!watchPropertyObjects[formId]) {
756
+ watchPropertyObjects[formId] = {};
757
+ }
758
+ if (!watchPropertyObjects[formId][panelId]) {
759
+ watchPropertyObjects[formId][panelId] = {};
760
+ }
662
761
  // --- 创建监听 ---
663
- watchPropertyObjects[formId][watchPropertyIndex] = {
762
+ watchPropertyObjects[formId][panelId][watchPropertyIndex] = {
664
763
  'el': el,
665
764
  'names': {}
666
765
  };
667
- const item = watchPropertyObjects[formId][watchPropertyIndex];
766
+ const item = watchPropertyObjects[formId][panelId][watchPropertyIndex];
668
767
  for (const n of name) {
669
768
  item.names[n] = {
670
769
  'val': (item.el as any)[n],
@@ -678,40 +777,6 @@ export function watchProperty(
678
777
  ++watchPropertyIndex;
679
778
  }
680
779
 
681
- /** --- watch property 的 timer --- */
682
- let watchPropertyTimer = 0;
683
- const watchPropertyHandler = function(): void {
684
- // --- 为什么要判断 form.getFocus 存在否,因为 form 类可能还没加载出来,这个函数就已经开始执行了 ---
685
- if (form.getFocus) {
686
- // --- 只判断和执行活跃中的窗体的监听事件 ---
687
- const formId: number | null = form.getFocus();
688
- if (formId && watchPropertyObjects[formId]) {
689
- for (const index in watchPropertyObjects[formId]) {
690
- const item = watchPropertyObjects[formId][index];
691
- if (!item.el.offsetParent) {
692
- delete watchPropertyObjects[formId][index];
693
- if (!Object.keys(watchPropertyObjects[formId]).length) {
694
- delete watchPropertyObjects[formId];
695
- }
696
- continue;
697
- }
698
- // --- 执行 cb ---
699
- for (const name in item.names) {
700
- if ((item.el as any)[name] === item.names[name].val) {
701
- continue;
702
- }
703
- item.names[name].val = (item.el as any)[name];
704
- for (const cb of item.names[name].cb) {
705
- cb(name, (item.el as any)[name]);
706
- }
707
- }
708
- }
709
- }
710
- }
711
- watchPropertyTimer = requestAnimationFrame(watchPropertyHandler);
712
- };
713
- watchPropertyHandler();
714
-
715
780
  /**
716
781
  * --- 检测一个标签是否正在被 watchProperty ---
717
782
  * @param el 要检测的标签
@@ -723,18 +788,204 @@ export function isWatchProperty(el: HTMLElement): boolean {
723
788
  /**
724
789
  * --- 清除某个窗体的所有 watch property 监视,虽然窗体结束后相关监视永远不会再被执行,但是会形成冗余,App 模式下无效 ---
725
790
  * @param formId 窗体 id
791
+ * @param panelId 若指定则只清除当前窗体的某个 panel 的 watch
726
792
  */
727
- export function clearWatchProperty(formId: number | string): void {
793
+ export function clearWatchProperty(formId: number | string, panelId?: number): void {
728
794
  if (!watchPropertyObjects[formId]) {
729
795
  return;
730
796
  }
731
- for (const index in watchPropertyObjects[formId]) {
732
- const item = watchPropertyObjects[formId][index];
733
- item.el.removeAttribute('data-cg-propertyindex');
797
+ for (const panel in watchPropertyObjects[formId]) {
798
+ if (panelId) {
799
+ if (panel !== panelId.toString()) {
800
+ continue;
801
+ }
802
+ }
803
+ for (const index in watchPropertyObjects[formId][panel]) {
804
+ const item = watchPropertyObjects[formId][panel][index];
805
+ item.el.removeAttribute('data-cg-propertyindex');
806
+ }
807
+ delete watchPropertyObjects[formId][panel];
808
+ }
809
+ if (Object.keys(watchPropertyObjects[formId]).length) {
810
+ return;
734
811
  }
735
812
  delete watchPropertyObjects[formId];
736
813
  }
737
814
 
815
+ // -------------------
816
+ // --- watch timer ---
817
+ // -------------------
818
+
819
+ export function getWatchInfo(): types.IGetWatchInfoResult {
820
+ const rtn: types.IGetWatchInfoResult = {
821
+ 'formId': 0,
822
+ 'default': {},
823
+ 'panels': {}
824
+ };
825
+ const formId: number | null = form.getFocus();
826
+ if (!formId) {
827
+ return rtn;
828
+ }
829
+ rtn.formId = formId;
830
+ const panelIds = form.getActivePanel(formId);
831
+ const handler = (item: {
832
+ 'el': HTMLElement;
833
+ 'names': Record<string, any>;
834
+ }, type: 'style' | 'property', panelId?: string): void => {
835
+ if (panelId) {
836
+ if (!rtn.panels[panelId]) {
837
+ rtn.panels[panelId] = {};
838
+ }
839
+ }
840
+ const ritem = panelId ? rtn.panels[panelId] : rtn.default;
841
+ /** --- 控件名 --- */
842
+ const cname = item.el.dataset.cgControl ?? findParentByData(item.el, 'cg-control')?.dataset.cgControl ?? 'unknown';
843
+ if (!ritem[cname]) {
844
+ ritem[cname] = {
845
+ 'style': {
846
+ 'count': 0,
847
+ 'list': []
848
+ },
849
+ 'property': {
850
+ 'count': 0,
851
+ 'list': []
852
+ }
853
+ };
854
+ }
855
+ ++ritem[cname][type].count;
856
+ for (const name in item.names) {
857
+ if (ritem[cname][type].list.includes(name)) {
858
+ continue;
859
+ }
860
+ ritem[cname][type].list.push(name);
861
+ }
862
+ };
863
+ // --- 先执行窗体默认的 ---
864
+ if (watchStyleList[formId].default) {
865
+ for (const index in watchStyleList[formId].default) {
866
+ handler(watchStyleList[formId].default[index], 'style');
867
+ }
868
+ }
869
+ // --- 再执行活跃的 panel 的 ---
870
+ for (const id of panelIds) {
871
+ if (watchStyleList[formId][id]) {
872
+ for (const index in watchStyleList[formId][id]) {
873
+ handler(watchStyleList[formId][id][index], 'style', id.toString());
874
+ }
875
+ }
876
+ }
877
+ // --- 先执行窗体默认的 ---
878
+ if (watchPropertyObjects[formId].default) {
879
+ for (const index in watchPropertyObjects[formId].default) {
880
+ handler(watchPropertyObjects[formId].default[index], 'property');
881
+ }
882
+ }
883
+ // --- 再执行活跃的 panel 的 ---
884
+ for (const id of panelIds) {
885
+ if (watchPropertyObjects[formId][id]) {
886
+ for (const index in watchPropertyObjects[formId][id]) {
887
+ handler(watchPropertyObjects[formId][id][index], 'property', id.toString());
888
+ }
889
+ }
890
+ }
891
+ return rtn;
892
+ }
893
+
894
+ /** --- watch style 的 timer --- */
895
+ let watchTimer = 0;
896
+ const watchTimerHandler = function(): void {
897
+ // --- 为什么要判断 form.getFocus 存在否,因为 form 类可能还没加载出来,这个函数就已经开始执行了 ---
898
+ if (form.getFocus) {
899
+ /** --- --- */
900
+ const formId: number | null = form.getFocus();
901
+ if (formId) {
902
+ /** --- 活跃的 panel --- */
903
+ const panelIds = form.getActivePanel(formId);
904
+ if (watchStyleList[formId]) {
905
+ // --- style ---
906
+ const handler = (item: IWatchStyleItem, panelId: string, index: string): void => {
907
+ if (!document.body.contains(item.el)) {
908
+ delete watchStyleList[formId][panelId][index];
909
+ if (!Object.keys(watchStyleList[formId][panelId]).length) {
910
+ delete watchStyleList[formId][panelId];
911
+ }
912
+ if (!Object.keys(watchStyleList[formId]).length) {
913
+ delete watchStyleList[formId];
914
+ }
915
+ return;
916
+ }
917
+ // --- 执行 cb ---
918
+ for (const name in item.names) {
919
+ if ((item.sd as any)[name] === item.names[name].val) {
920
+ continue;
921
+ }
922
+ const old = item.names[name].val;
923
+ item.names[name].val = (item.sd as any)[name];
924
+ for (const cb of item.names[name].cb) {
925
+ cb(name, (item.sd as any)[name], old);
926
+ }
927
+ }
928
+ };
929
+ // --- 先执行窗体默认的 ---
930
+ if (watchStyleList[formId].default) {
931
+ for (const index in watchStyleList[formId].default) {
932
+ handler(watchStyleList[formId].default[index], 'default', index);
933
+ }
934
+ }
935
+ // --- 再执行活跃的 panel 的 ---
936
+ for (const id of panelIds) {
937
+ if (watchStyleList[formId][id]) {
938
+ for (const index in watchStyleList[formId][id]) {
939
+ handler(watchStyleList[formId][id][index], id.toString(), index);
940
+ }
941
+ }
942
+ }
943
+ }
944
+ if (watchPropertyObjects[formId]) {
945
+ // --- property ---
946
+ const handler = (item: IWatchPropertyItem, panelId: string, index: string): void => {
947
+ if (!document.body.contains(item.el)) {
948
+ delete watchPropertyObjects[formId][panelId][index];
949
+ if (!Object.keys(watchPropertyObjects[formId][panelId]).length) {
950
+ delete watchPropertyObjects[formId][panelId];
951
+ }
952
+ if (!Object.keys(watchPropertyObjects[formId]).length) {
953
+ delete watchPropertyObjects[formId];
954
+ }
955
+ return;
956
+ }
957
+ // --- 执行 cb ---
958
+ for (const name in item.names) {
959
+ if ((item.el as any)[name] === item.names[name].val) {
960
+ continue;
961
+ }
962
+ item.names[name].val = (item.el as any)[name];
963
+ for (const cb of item.names[name].cb) {
964
+ cb(name, (item.el as any)[name]);
965
+ }
966
+ }
967
+ };
968
+ // --- 先执行窗体默认的 ---
969
+ if (watchPropertyObjects[formId].default) {
970
+ for (const index in watchPropertyObjects[formId].default) {
971
+ handler(watchPropertyObjects[formId].default[index], 'default', index);
972
+ }
973
+ }
974
+ // --- 再执行活跃的 panel 的 ---
975
+ for (const id of panelIds) {
976
+ if (watchPropertyObjects[formId][id]) {
977
+ for (const index in watchPropertyObjects[formId][id]) {
978
+ handler(watchPropertyObjects[formId][id][index], id.toString(), index);
979
+ }
980
+ }
981
+ }
982
+ }
983
+ }
984
+ }
985
+ watchTimer = requestAnimationFrame(watchTimerHandler);
986
+ };
987
+ watchTimerHandler();
988
+
738
989
  /**
739
990
  * --- 鼠标/手指没移动时,click 才生效 ---
740
991
  * @param e 事件对象
@@ -780,7 +1031,7 @@ export function bindDown(oe: MouseEvent | TouchEvent, opt: types.IBindDownOption
780
1031
  let end: ((e: MouseEvent | TouchEvent) => void) | undefined = undefined;
781
1032
  const move = function(e: MouseEvent | TouchEvent): void {
782
1033
  // --- 虽然上层已经有 preventDefault 了,但是有可能 e.target 会被注销,这样就响应不到上层的 preventDefault 事件,所以要在这里再加一个 ---
783
- if ((!e.target || !(e.target as HTMLElement).offsetParent) && e.cancelable) {
1034
+ if (!e.target || !document.body.contains(e.target as HTMLElement) && e.cancelable) {
784
1035
  e.preventDefault();
785
1036
  }
786
1037
  /** --- 本次的移动方向 --- */
@@ -1356,7 +1607,8 @@ export function bindDrag(e: MouseEvent | TouchEvent, opt: { 'el': HTMLElement; '
1356
1607
  export const is = clickgo.vue.reactive({
1357
1608
  'move': false,
1358
1609
  'shift': false,
1359
- 'ctrl': false
1610
+ 'ctrl': false,
1611
+ 'meta': false
1360
1612
  });
1361
1613
 
1362
1614
  window.addEventListener('keydown', function(e: KeyboardEvent) {
@@ -1369,6 +1621,10 @@ window.addEventListener('keydown', function(e: KeyboardEvent) {
1369
1621
  is.ctrl = true;
1370
1622
  break;
1371
1623
  }
1624
+ case 'Meta': {
1625
+ is.meta = true;
1626
+ break;
1627
+ }
1372
1628
  }
1373
1629
  });
1374
1630
  window.addEventListener('keyup', function(e: KeyboardEvent) {
@@ -1381,6 +1637,10 @@ window.addEventListener('keyup', function(e: KeyboardEvent) {
1381
1637
  is.ctrl = false;
1382
1638
  break;
1383
1639
  }
1640
+ case 'Meta': {
1641
+ is.meta = false;
1642
+ break;
1643
+ }
1384
1644
  }
1385
1645
  });
1386
1646
 
@@ -1399,7 +1659,7 @@ export function bindMove(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions
1399
1659
  };
1400
1660
  }
1401
1661
  is.move = true;
1402
- setGlobalCursor(getComputedStyle(e.target as Element).cursor);
1662
+ setGlobalCursor(opt.cursor ? opt.cursor : getComputedStyle(e.target as Element).cursor);
1403
1663
  /** --- 上一次的 x 坐标 --- */
1404
1664
  let tx: number = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
1405
1665
  /** --- 上一次的 y 坐标 --- */
@@ -1414,7 +1674,7 @@ export function bindMove(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions
1414
1674
  /** --- 拖动限定区域底部 --- */
1415
1675
  bottom: number;
1416
1676
  if (opt.areaObject) {
1417
- if (!(opt.areaObject instanceof HTMLElement)) {
1677
+ if (!(opt.areaObject instanceof Element)) {
1418
1678
  opt.areaObject = opt.areaObject.$el;
1419
1679
  }
1420
1680
  const areaRect = opt.areaObject.getBoundingClientRect();
@@ -1474,7 +1734,7 @@ export function bindMove(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions
1474
1734
  }
1475
1735
  // --- 限定拖动对象,限定后整体对象将无法拖动出边界 ---
1476
1736
  if (opt.object) {
1477
- if (!(opt.object instanceof HTMLElement)) {
1737
+ if (!(opt.object instanceof Element)) {
1478
1738
  opt.object = opt.object.$el;
1479
1739
  }
1480
1740
  const rect = opt.object.getBoundingClientRect();
@@ -1756,7 +2016,7 @@ export function bindResize(e: MouseEvent | TouchEvent, opt: types.IBindResizeOpt
1756
2016
  if (!opt.object) {
1757
2017
  return;
1758
2018
  }
1759
- if (!(opt.object instanceof HTMLElement)) {
2019
+ if (!(opt.object instanceof Element)) {
1760
2020
  opt.object = opt.object.$el;
1761
2021
  }
1762
2022
  const objectRect = opt.object.getBoundingClientRect();
@@ -1836,8 +2096,9 @@ export function bindResize(e: MouseEvent | TouchEvent, opt: types.IBindResizeOpt
1836
2096
  * --- 通过 data 名查找上层所有标签是否存在 ---
1837
2097
  * @param el 当前标签
1838
2098
  * @param name 要查找的 data 名
2099
+ * @param value data 对应的值,留空则代表只要匹配了名就可以
1839
2100
  */
1840
- export function findParentByData(el: HTMLElement, name: string): HTMLElement | null {
2101
+ export function findParentByData(el: HTMLElement, name: string, value?: string): HTMLElement | null {
1841
2102
  let parent = el.parentNode as HTMLElement;
1842
2103
  while (parent) {
1843
2104
  if (!parent.tagName) {
@@ -1846,7 +2107,15 @@ export function findParentByData(el: HTMLElement, name: string): HTMLElement | n
1846
2107
  if (parent.tagName.toLowerCase() === 'body') {
1847
2108
  break;
1848
2109
  }
1849
- if (parent.getAttribute('data-' + name) !== null) {
2110
+ const v = parent.getAttribute('data-' + name);
2111
+ if (v !== null) {
2112
+ if (value) {
2113
+ if (value === v) {
2114
+ return parent;
2115
+ }
2116
+ // --- value 不匹配 ---
2117
+ continue;
2118
+ }
1850
2119
  return parent;
1851
2120
  }
1852
2121
  parent = parent.parentNode as HTMLElement;
@@ -1934,12 +2203,10 @@ export function fullscreen(): boolean {
1934
2203
  document.addEventListener('visibilitychange', function() {
1935
2204
  if (document.hidden) {
1936
2205
  // --- 隐藏 ---
1937
- cancelAnimationFrame(watchStyleTimer);
1938
- cancelAnimationFrame(watchPropertyTimer);
2206
+ cancelAnimationFrame(watchTimer);
1939
2207
  }
1940
2208
  else {
1941
2209
  // --- 显示 ---
1942
- watchStyleTimer = requestAnimationFrame(watchStyleHandler);
1943
- watchPropertyTimer = requestAnimationFrame(watchPropertyHandler);
2210
+ watchTimer = requestAnimationFrame(watchTimerHandler);
1944
2211
  }
1945
2212
  });