clickgo 6.3.0 → 6.4.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clickgo",
3
- "version": "6.3.0",
3
+ "version": "6.4.1",
4
4
  "description": "Background interface, software interface, mobile phone APP interface operation library.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -0,0 +1,93 @@
1
+ // Run after TypeScript compilation: node --experimental-vm-modules test/dock-lifecycle.mjs
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { SourceTextModule, SyntheticModule } from 'node:vm';
5
+
6
+ const sizeWatches = [];
7
+ const sizeUnwatches = [];
8
+
9
+ class AbstractControl {
10
+ constructor(rootForm, height) {
11
+ this.rootForm = rootForm;
12
+ this.refs = {
13
+ 'body': { 'clientHeight': height }
14
+ };
15
+ }
16
+
17
+ watch(name, callback, options) {
18
+ if ((name === 'expanded') && options?.immediate) {
19
+ callback();
20
+ }
21
+ }
22
+
23
+ async nextTick() {}
24
+
25
+ propBoolean(name) {
26
+ return Boolean(this.props[name]);
27
+ }
28
+
29
+ emit() {}
30
+ }
31
+
32
+ const clickgo = new SyntheticModule(['control', 'dom'], function() {
33
+ this.setExport('control', { AbstractControl });
34
+ this.setExport('dom', {
35
+ watchSizeMulti(current, element, handler, immediate) {
36
+ sizeWatches.push({ current, element, handler });
37
+ if (immediate) {
38
+ handler();
39
+ }
40
+ return true;
41
+ },
42
+ unwatchSizeMulti(current, element, handler) {
43
+ sizeUnwatches.push({ current, element, handler });
44
+ },
45
+ watchSize(current, element, handler, immediate) {
46
+ if (immediate) {
47
+ handler();
48
+ }
49
+ return true;
50
+ }
51
+ });
52
+ });
53
+ const mod = new SourceTextModule(await readFile(new URL('../dist/sources/control/dock/code.js', import.meta.url), 'utf8'));
54
+ await mod.link(() => clickgo);
55
+ await mod.evaluate();
56
+ const Dock = mod.namespace.default;
57
+
58
+ const formA = { 'element': { 'isConnected': true, 'offsetWidth': 500 } };
59
+ const formB = { 'element': { 'isConnected': true, 'offsetWidth': 800 } };
60
+ const dockA1 = new Dock(formA, 300);
61
+ const dockA2 = new Dock(formA, 400);
62
+ const dockB = new Dock(formB, 500);
63
+
64
+ await dockA1.onMounted();
65
+ await dockA2.onMounted();
66
+ await dockB.onMounted();
67
+
68
+ assert.notEqual(dockA1.access, dockA2.access);
69
+ assert.notEqual(dockA1.access.formSizeWatch, dockA2.access.formSizeWatch);
70
+ assert.equal(dockA1.narrow, true);
71
+ assert.equal(dockA2.narrow, true);
72
+ assert.equal(dockB.narrow, false);
73
+ assert.equal(dockA1.floatAreaHeight, 300);
74
+ assert.equal(dockA2.floatAreaHeight, 400);
75
+ assert.equal(dockB.floatAreaHeight, 500);
76
+
77
+ dockB.toggleFloat(1);
78
+ dockA1.toggleFloat(2);
79
+ dockA2.toggleFloat(3);
80
+ assert.equal(dockA1.floatGroup, -1);
81
+ assert.equal(dockA2.floatGroup, 3);
82
+ assert.equal(dockB.floatGroup, 1);
83
+
84
+ const dockA1Watch = dockA1.access.formSizeWatch;
85
+ dockA1.onUnmounted();
86
+ assert.equal(dockA1.access.formSizeWatch, null);
87
+ assert.equal(sizeUnwatches.length, 1);
88
+ assert.equal(sizeUnwatches[0].current, dockA1);
89
+ assert.equal(sizeUnwatches[0].element, formA.element);
90
+ assert.equal(sizeUnwatches[0].handler, dockA1Watch.handler);
91
+ assert.equal(sizeWatches.length, 3);
92
+
93
+ console.log('Dock access state, Form isolation and size-watch cleanup checks passed.');
@@ -0,0 +1,42 @@
1
+ // Run after TypeScript compilation: node test/tool-clone.mjs
2
+ import assert from 'node:assert/strict';
3
+ import { clone } from '../dist/lib/tool.js';
4
+
5
+ const shared = { 'value': 1 };
6
+ const source = {
7
+ 'map': new Map(),
8
+ 'set': new Set(),
9
+ shared,
10
+ 'alias': shared,
11
+ 'date': new Date('2026-09-20T00:00:00.000Z'),
12
+ };
13
+ source.self = source;
14
+ source.map.set(shared, source);
15
+ source.set.add(shared);
16
+ source.set.add(source);
17
+
18
+ const first = clone(source);
19
+ const second = clone(source);
20
+
21
+ assert.notEqual(first, source);
22
+ assert.notEqual(first, second);
23
+ assert(first.map instanceof Map);
24
+ assert(first.set instanceof Set);
25
+ assert.notEqual(first.map, source.map);
26
+ assert.notEqual(first.map, second.map);
27
+ assert.notEqual(first.set, source.set);
28
+ assert.notEqual(first.set, second.set);
29
+ assert.equal(first.self, first);
30
+ assert.equal(first.shared, first.alias);
31
+ assert.notEqual(first.shared, shared);
32
+ assert.equal(first.map.get(first.shared), first);
33
+ assert(first.set.has(first.shared));
34
+ assert(first.set.has(first));
35
+ assert(first.date instanceof Date);
36
+ assert.notEqual(first.date, source.date);
37
+ assert.equal(first.date.getTime(), source.date.getTime());
38
+
39
+ first.map.set('only-first', true);
40
+ assert.equal(second.map.has('only-first'), false);
41
+
42
+ console.log('Tool clone Map/Set, circular-reference and instance-isolation checks passed.');
@@ -0,0 +1,96 @@
1
+ // Run: node --experimental-vm-modules test/web-drag-mask.mjs
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { SourceTextModule, SyntheticModule } from 'node:vm';
5
+ import { JSDOM } from 'jsdom';
6
+ import ts from 'typescript';
7
+
8
+ const dom = new JSDOM('<!doctype html><html><head></head><body></body></html>');
9
+ for (const name of ['window', 'document', 'Element', 'SVGElement', 'Node', 'HTMLElement']) {
10
+ globalThis[name] = dom.window[name];
11
+ }
12
+ globalThis.getComputedStyle = dom.window.getComputedStyle.bind(dom.window);
13
+ const { createApp, compile, reactive, nextTick } = await import('vue');
14
+ const pointer = await import('@litert/pointer');
15
+ const state = reactive({ move: false });
16
+ const clickgo = new SyntheticModule(['control', 'dom'], function() {
17
+ this.setExport('control', { AbstractControl: class {} });
18
+ this.setExport('dom', { is: state });
19
+ });
20
+ const source = await readFile(new URL('../dist/sources/control/web/code.ts', import.meta.url), 'utf8');
21
+ const web = new SourceTextModule(ts.transpileModule(source, {
22
+ compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
23
+ }).outputText);
24
+ await web.link(() => clickgo);
25
+ await web.evaluate();
26
+ const control = new web.namespace.default();
27
+
28
+ // Exercise the actual launcher hooks with the installed Pointer library.
29
+ const launcher = await readFile(new URL('../dist/clickgo.ts', import.meta.url), 'utf8');
30
+ const hookSource = launcher.match(/ modules\.pointer\.addMoveHook\('down',[\s\S]*? modules\.pointer\.addMoveHook\('up',[\s\S]*? \}\);/)[0];
31
+ const hooks = new SourceTextModule(`import { pointer, is } from 'bridge';
32
+ const modules = { pointer }; const lDom = { is }; ${hookSource}`);
33
+ const bridge = new SyntheticModule(['pointer', 'is'], function() {
34
+ this.setExport('pointer', pointer);
35
+ this.setExport('is', state);
36
+ });
37
+ await hooks.link(() => bridge);
38
+ await hooks.evaluate();
39
+
40
+ const template = await readFile(new URL('../dist/sources/control/web/layout.html', import.meta.url), 'utf8');
41
+ const host = document.createElement('div');
42
+ document.body.append(host);
43
+ const app = createApp({
44
+ render: compile(template),
45
+ data: () => ({ src: 'about:blank' }),
46
+ computed: { showMask: () => control.showMask },
47
+ });
48
+ app.mount(host);
49
+ const iframe = host.querySelector('iframe');
50
+ const mask = host.querySelector('.mask');
51
+ assert.equal(iframe.getAttribute('src'), 'about:blank');
52
+ assert.equal(mask.style.display, 'none');
53
+
54
+ function event(type, x, y) {
55
+ const e = new dom.window.Event(type, { bubbles: true, cancelable: true });
56
+ Object.assign(e, { clientX: x, clientY: y, pointerId: 1, pointerType: 'mouse', button: 0 });
57
+ return e;
58
+ }
59
+
60
+ for (const operation of ['move', 'resize']) {
61
+ for (const finish of ['pointerup', 'pointercancel']) {
62
+ let updates = 0;
63
+ const handle = document.createElement('div');
64
+ document.body.append(handle);
65
+ handle.addEventListener('pointerdown', e => {
66
+ if (operation === 'resize') {
67
+ pointer.resize(e, {
68
+ border: 'rb', objectLeft: 0, objectTop: 0,
69
+ objectWidth: 400, objectHeight: 300,
70
+ move: () => { ++updates; },
71
+ });
72
+ }
73
+ else {
74
+ pointer.move(e, { move: () => { ++updates; } });
75
+ }
76
+ });
77
+ handle.dispatchEvent(event('pointerdown', 400, 300));
78
+ await nextTick();
79
+ assert.equal(state.move, true);
80
+ assert.notEqual(mask.style.display, 'none');
81
+ mask.dispatchEvent(event('pointermove', 350, 250));
82
+ mask.dispatchEvent(event('pointermove', 320, 230));
83
+ assert.equal(updates, 2);
84
+ mask.dispatchEvent(event(finish, 320, 230));
85
+ await nextTick();
86
+ assert.equal(state.move, false);
87
+ assert.equal(mask.style.display, 'none');
88
+ mask.dispatchEvent(event('pointermove', 300, 200));
89
+ assert.equal(updates, 2);
90
+ assert.equal(host.querySelector('iframe'), iframe);
91
+ handle.remove();
92
+ }
93
+ }
94
+ app.unmount();
95
+ dom.window.close();
96
+ console.log('WEB drag/resize mask, pointerup/cancel cleanup and iframe preservation checks passed.');