@yuneta/gobj-ui 7.26.19 → 7.26.21
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/dist/gobj-ui.cjs.js +20 -2
- package/dist/gobj-ui.es.js +20 -2
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +20 -0
- package/src/g6_long_press_clock.test.js +51 -0
- package/src/g6_touch_gestures.js +3 -2
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -13590,7 +13590,7 @@ function install_long_press_contextmenu(graph, options) {
|
|
|
13590
13590
|
pending = {
|
|
13591
13591
|
x,
|
|
13592
13592
|
y,
|
|
13593
|
-
t0:
|
|
13593
|
+
t0: performance.now(),
|
|
13594
13594
|
event: Object.assign({}, event),
|
|
13595
13595
|
tick: setTimeout(announce, 500)
|
|
13596
13596
|
};
|
|
@@ -13616,7 +13616,7 @@ function install_long_press_contextmenu(graph, options) {
|
|
|
13616
13616
|
let y = event.client?.y;
|
|
13617
13617
|
if (x === void 0) x = press.x;
|
|
13618
13618
|
if (y === void 0) y = press.y;
|
|
13619
|
-
if (classify_press(press, x, y,
|
|
13619
|
+
if (classify_press(press, x, y, performance.now()) !== "long") return;
|
|
13620
13620
|
fire(press.event);
|
|
13621
13621
|
};
|
|
13622
13622
|
let on_touch_cancel = () => {
|
|
@@ -74386,6 +74386,16 @@ function mt_destroy$1(gobj) {
|
|
|
74386
74386
|
/***************************
|
|
74387
74387
|
* Local Methods
|
|
74388
74388
|
***************************/
|
|
74389
|
+
/************************************************************
|
|
74390
|
+
* THE name rule of timeranger2 and treedb
|
|
74391
|
+
* (tranger2_name_is_identifier(), SDK 7.26.9): a topic or a column
|
|
74392
|
+
* name is a directory and a key, so it cannot be "." or "..", nor hold
|
|
74393
|
+
* '/' or the path delimiter '`'. Said here, before the backend answers
|
|
74394
|
+
* a generic "cannot create topic".
|
|
74395
|
+
************************************************************/
|
|
74396
|
+
function name_breaks_the_rule(name) {
|
|
74397
|
+
return name === "." || name === ".." || name.includes("/") || name.includes("`");
|
|
74398
|
+
}
|
|
74389
74399
|
/***************************************************************
|
|
74390
74400
|
* The frame: a toolbar that never changes place, a notice line
|
|
74391
74401
|
* for what the whole view has to say, and one body the screens
|
|
@@ -77898,6 +77908,10 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77898
77908
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t: i18next.t });
|
|
77899
77909
|
return -1;
|
|
77900
77910
|
}
|
|
77911
|
+
if (kw.creating && name_breaks_the_rule(String(values.value).trim())) {
|
|
77912
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", { t: i18next.t });
|
|
77913
|
+
return -1;
|
|
77914
|
+
}
|
|
77901
77915
|
if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
77902
77916
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t: i18next.t });
|
|
77903
77917
|
return -1;
|
|
@@ -78152,6 +78166,10 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78152
78166
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t: i18next.t });
|
|
78153
78167
|
return -1;
|
|
78154
78168
|
}
|
|
78169
|
+
if (name_breaks_the_rule(name)) {
|
|
78170
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", { t: i18next.t });
|
|
78171
|
+
return -1;
|
|
78172
|
+
}
|
|
78155
78173
|
if (find_topic(priv.model, priv.treedb_id, name)) {
|
|
78156
78174
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t: i18next.t });
|
|
78157
78175
|
return -1;
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -13585,7 +13585,7 @@ function install_long_press_contextmenu(graph, options) {
|
|
|
13585
13585
|
pending = {
|
|
13586
13586
|
x,
|
|
13587
13587
|
y,
|
|
13588
|
-
t0:
|
|
13588
|
+
t0: performance.now(),
|
|
13589
13589
|
event: Object.assign({}, event),
|
|
13590
13590
|
tick: setTimeout(announce, 500)
|
|
13591
13591
|
};
|
|
@@ -13611,7 +13611,7 @@ function install_long_press_contextmenu(graph, options) {
|
|
|
13611
13611
|
let y = event.client?.y;
|
|
13612
13612
|
if (x === void 0) x = press.x;
|
|
13613
13613
|
if (y === void 0) y = press.y;
|
|
13614
|
-
if (classify_press(press, x, y,
|
|
13614
|
+
if (classify_press(press, x, y, performance.now()) !== "long") return;
|
|
13615
13615
|
fire(press.event);
|
|
13616
13616
|
};
|
|
13617
13617
|
let on_touch_cancel = () => {
|
|
@@ -74381,6 +74381,16 @@ function mt_destroy$1(gobj) {
|
|
|
74381
74381
|
/***************************
|
|
74382
74382
|
* Local Methods
|
|
74383
74383
|
***************************/
|
|
74384
|
+
/************************************************************
|
|
74385
|
+
* THE name rule of timeranger2 and treedb
|
|
74386
|
+
* (tranger2_name_is_identifier(), SDK 7.26.9): a topic or a column
|
|
74387
|
+
* name is a directory and a key, so it cannot be "." or "..", nor hold
|
|
74388
|
+
* '/' or the path delimiter '`'. Said here, before the backend answers
|
|
74389
|
+
* a generic "cannot create topic".
|
|
74390
|
+
************************************************************/
|
|
74391
|
+
function name_breaks_the_rule(name) {
|
|
74392
|
+
return name === "." || name === ".." || name.includes("/") || name.includes("`");
|
|
74393
|
+
}
|
|
74384
74394
|
/***************************************************************
|
|
74385
74395
|
* The frame: a toolbar that never changes place, a notice line
|
|
74386
74396
|
* for what the whole view has to say, and one body the screens
|
|
@@ -77893,6 +77903,10 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77893
77903
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t });
|
|
77894
77904
|
return -1;
|
|
77895
77905
|
}
|
|
77906
|
+
if (kw.creating && name_breaks_the_rule(String(values.value).trim())) {
|
|
77907
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", { t });
|
|
77908
|
+
return -1;
|
|
77909
|
+
}
|
|
77896
77910
|
if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
77897
77911
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t });
|
|
77898
77912
|
return -1;
|
|
@@ -78147,6 +78161,10 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78147
78161
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t });
|
|
78148
78162
|
return -1;
|
|
78149
78163
|
}
|
|
78164
|
+
if (name_breaks_the_rule(name)) {
|
|
78165
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", { t });
|
|
78166
|
+
return -1;
|
|
78167
|
+
}
|
|
78150
78168
|
if (find_topic(priv.model, priv.treedb_id, name)) {
|
|
78151
78169
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t });
|
|
78152
78170
|
return -1;
|
package/package.json
CHANGED
|
@@ -361,6 +361,18 @@ function mt_destroy(gobj)
|
|
|
361
361
|
|
|
362
362
|
|
|
363
363
|
|
|
364
|
+
/************************************************************
|
|
365
|
+
* THE name rule of timeranger2 and treedb
|
|
366
|
+
* (tranger2_name_is_identifier(), SDK 7.26.9): a topic or a column
|
|
367
|
+
* name is a directory and a key, so it cannot be "." or "..", nor hold
|
|
368
|
+
* '/' or the path delimiter '`'. Said here, before the backend answers
|
|
369
|
+
* a generic "cannot create topic".
|
|
370
|
+
************************************************************/
|
|
371
|
+
function name_breaks_the_rule(name)
|
|
372
|
+
{
|
|
373
|
+
return name === "." || name === ".." || name.includes("/") || name.includes("`");
|
|
374
|
+
}
|
|
375
|
+
|
|
364
376
|
/***************************************************************
|
|
365
377
|
* The frame: a toolbar that never changes place, a notice line
|
|
366
378
|
* for what the whole view has to say, and one body the screens
|
|
@@ -4026,6 +4038,10 @@ function ac_save_column(gobj, event, kw, src)
|
|
|
4026
4038
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", {t: t});
|
|
4027
4039
|
return -1;
|
|
4028
4040
|
}
|
|
4041
|
+
if(kw.creating && name_breaks_the_rule(String(values.value).trim())) {
|
|
4042
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", {t: t});
|
|
4043
|
+
return -1;
|
|
4044
|
+
}
|
|
4029
4045
|
if(kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
4030
4046
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", {t: t});
|
|
4031
4047
|
return -1;
|
|
@@ -4333,6 +4349,10 @@ function ac_save_topic(gobj, event, kw, src)
|
|
|
4333
4349
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", {t: t});
|
|
4334
4350
|
return -1;
|
|
4335
4351
|
}
|
|
4352
|
+
if(name_breaks_the_rule(name)) {
|
|
4353
|
+
yui_shell_show_error(yui_shell_of(gobj), "a name cannot be . or .., nor hold / or `", {t: t});
|
|
4354
|
+
return -1;
|
|
4355
|
+
}
|
|
4336
4356
|
if(find_topic(priv.model, priv.treedb_id, name)) {
|
|
4337
4357
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", {t: t});
|
|
4338
4358
|
return -1;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* g6_long_press_clock.test.js
|
|
3
|
+
*
|
|
4
|
+
* The long press of the G6 graphs is measured on a MONOTONIC clock
|
|
5
|
+
* (yunovatios REV-CLOUD.md V11.4 / V12.4). press_arbiter.test.js
|
|
6
|
+
* injects the time into classify_press(), so it passes whatever
|
|
7
|
+
* clock the handler reads; this drives the handler itself, with the
|
|
8
|
+
* wall clock set back an hour in the middle of the press -- an NTP
|
|
9
|
+
* step -- while the monotonic one moves on as a finger does.
|
|
10
|
+
*
|
|
11
|
+
* Copyright (c) 2026, ArtGins.
|
|
12
|
+
* All Rights Reserved.
|
|
13
|
+
***********************************************************************/
|
|
14
|
+
import {describe, test, expect, vi, afterEach} from "vitest";
|
|
15
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
16
|
+
|
|
17
|
+
install_dom_double();
|
|
18
|
+
|
|
19
|
+
const {CommonEvent} = await import("@antv/g6");
|
|
20
|
+
const {install_long_press_contextmenu} = await import("./g6_touch_gestures.js");
|
|
21
|
+
const {LONG_PRESS_MS} = await import("./press_arbiter.js");
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("the long press of a G6 graph and the clocks", () => {
|
|
28
|
+
test("a wall clock set back mid-press does not turn a long press into a tap", () => {
|
|
29
|
+
const handlers = {};
|
|
30
|
+
const emitted = [];
|
|
31
|
+
const graph = {
|
|
32
|
+
on(name, fn) { handlers[name] = fn; },
|
|
33
|
+
off() {},
|
|
34
|
+
emit(name) { emitted.push(name); },
|
|
35
|
+
};
|
|
36
|
+
let mono = 1000;
|
|
37
|
+
let wall = 1_800_000_000_000;
|
|
38
|
+
vi.spyOn(performance, "now").mockImplementation(() => mono);
|
|
39
|
+
vi.spyOn(Date, "now").mockImplementation(() => wall);
|
|
40
|
+
|
|
41
|
+
install_long_press_contextmenu(graph, {});
|
|
42
|
+
handlers[CommonEvent.POINTER_DOWN]({
|
|
43
|
+
pointerType: "touch", client: {x: 10, y: 10}, targetType: "node"
|
|
44
|
+
});
|
|
45
|
+
mono += LONG_PRESS_MS + 100; /* the finger stays long enough */
|
|
46
|
+
wall -= 3600 * 1000; /* and the wall clock steps back */
|
|
47
|
+
handlers[CommonEvent.POINTER_UP]({client: {x: 10, y: 10}});
|
|
48
|
+
|
|
49
|
+
expect(emitted).toContain(CommonEvent.CONTEXT_MENU);
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/g6_touch_gestures.js
CHANGED
|
@@ -461,7 +461,8 @@ export function install_long_press_contextmenu(graph, options)
|
|
|
461
461
|
pending = {
|
|
462
462
|
x: x,
|
|
463
463
|
y: y,
|
|
464
|
-
|
|
464
|
+
/* A monotonic clock: the wall clock may step mid-press. */
|
|
465
|
+
t0: performance.now(),
|
|
465
466
|
event: Object.assign({}, event),
|
|
466
467
|
tick: setTimeout(announce, LONG_PRESS_MS),
|
|
467
468
|
};
|
|
@@ -505,7 +506,7 @@ export function install_long_press_contextmenu(graph, options)
|
|
|
505
506
|
if(y === undefined) {
|
|
506
507
|
y = press.y;
|
|
507
508
|
}
|
|
508
|
-
if(classify_press(press, x, y,
|
|
509
|
+
if(classify_press(press, x, y, performance.now()) !== "long") {
|
|
509
510
|
return; /* a drag, or a tap: both are already served */
|
|
510
511
|
}
|
|
511
512
|
fire(press.event);
|