@yuneta/gobj-ui 7.23.43 → 7.23.44
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 +680 -655
- package/dist/gobj-ui.es.js +680 -655
- package/package.json +1 -1
- package/src/c_yui_form.js +6 -5
- package/src/json_view_helpers.js +23 -7
- package/src/json_view_helpers.test.js +33 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -6040,173 +6040,460 @@ function yui_watch_theme(gobj, event = "EV_THEME") {
|
|
|
6040
6040
|
}
|
|
6041
6041
|
} };
|
|
6042
6042
|
}
|
|
6043
|
-
//#endregion
|
|
6044
|
-
//#region src/c_yui_form.js
|
|
6045
|
-
/***********************************************************************
|
|
6046
|
-
* c_yui_form.js
|
|
6047
|
-
*
|
|
6048
|
-
* Form (in Window or not)
|
|
6049
|
-
*
|
|
6050
|
-
* Copyright (c) 2024-2026, ArtGins.
|
|
6051
|
-
* All Rights Reserved.
|
|
6052
|
-
***********************************************************************/
|
|
6053
|
-
/***************************************************************
|
|
6054
|
-
* Constants
|
|
6055
|
-
***************************************************************/
|
|
6056
|
-
var GCLASS_NAME$16 = "C_YUI_FORM";
|
|
6057
|
-
/***************************************************************
|
|
6058
|
-
* Data
|
|
6059
|
-
***************************************************************/
|
|
6060
|
-
var attrs_table$16 = [
|
|
6061
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of publishing messages"),
|
|
6062
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else do it manually"),
|
|
6063
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "template", 0, "<div>You must supply a template for form</div>", "Template for form, object -> TEMPLATE, array -> DESC"),
|
|
6064
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "record", 0, "{}", "Data to form"),
|
|
6065
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "placeholders", 0, "{}", "Placeholder values for form fields"),
|
|
6066
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "fkey_options", 0, "{}", "Options for fkey fields: {topic_name: [ids or {id} records]}, read at build time"),
|
|
6067
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "form_mode", 0, "", "'create' (pkey editable+required) or 'update' (pkey readonly); empty = no pkey handling"),
|
|
6068
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "render_mode", 0, "exec", "'exec' interprets structured cols (template->sub-form, table->grid, coordinates->picker); 'edit' shows them as raw JSON editors (record-editing, e.g. the treedb topic form)"),
|
|
6069
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "pkey", 0, "id", "Primary key field name, used by form_mode"),
|
|
6070
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "topic_name", 0, "", "Treedb topic name: labels use the i18n cascade '<topic>.<col>' -> '<col>' -> header (like table headers)"),
|
|
6071
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Default is editable, set false to readonly"),
|
|
6072
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "selectable", 0, true, "Rows selectable with 'click' (BAD with editable=true)"),
|
|
6073
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
6074
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
|
|
6075
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
|
|
6076
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "toolbar", 0, [
|
|
6077
|
-
"save",
|
|
6078
|
-
"undo",
|
|
6079
|
-
"clear",
|
|
6080
|
-
"copy",
|
|
6081
|
-
"paste"
|
|
6082
|
-
], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
|
|
6083
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
6084
|
-
layout: "fitDataFill",
|
|
6085
|
-
validationMode: "highlight",
|
|
6086
|
-
columnDefaults: { resizable: true },
|
|
6087
|
-
maxHeight: "100%"
|
|
6088
|
-
}, "Default settings for Tabulator"),
|
|
6089
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Container element"),
|
|
6090
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "form_id", 0, "", "Form identifier"),
|
|
6091
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "initialized", 0, false, "Indicates if it is initialized"),
|
|
6092
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "changes", 0, false, "Indicates if changes have been made"),
|
|
6093
|
-
(0, _yuneta_gobj_js.SDATA_END)()
|
|
6094
|
-
];
|
|
6095
|
-
var PRIVATE_DATA$16 = {};
|
|
6096
|
-
var __gclass__$16 = null;
|
|
6097
|
-
/***************************
|
|
6098
|
-
* Framework Methods
|
|
6099
|
-
***************************/
|
|
6100
6043
|
/************************************************************
|
|
6101
|
-
*
|
|
6044
|
+
* JSON type discriminator (viewer vocabulary)
|
|
6102
6045
|
************************************************************/
|
|
6103
|
-
function
|
|
6104
|
-
|
|
6105
|
-
if (
|
|
6106
|
-
(
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6046
|
+
function json_type(value) {
|
|
6047
|
+
if (value === null) return "null";
|
|
6048
|
+
if (Array.isArray(value)) return "array";
|
|
6049
|
+
switch (typeof value) {
|
|
6050
|
+
case "string": return "string";
|
|
6051
|
+
case "number": return "number";
|
|
6052
|
+
case "boolean": return "boolean";
|
|
6053
|
+
case "object": return "object";
|
|
6054
|
+
default: return "unknown";
|
|
6055
|
+
}
|
|
6110
6056
|
}
|
|
6111
6057
|
/************************************************************
|
|
6112
|
-
*
|
|
6113
|
-
*
|
|
6114
|
-
*
|
|
6058
|
+
* Detect a kw_collapse() sentinel.
|
|
6059
|
+
*
|
|
6060
|
+
* The kernel replaces an over-limit dict with:
|
|
6061
|
+
* { "__collapsed__": { "path": ..., "size": N } }
|
|
6062
|
+
* and an over-limit array with:
|
|
6063
|
+
* [ { "__collapsed__": { "path": ..., "size": N } } ]
|
|
6064
|
+
*
|
|
6065
|
+
* Returns { size, path, is_array } or null.
|
|
6115
6066
|
************************************************************/
|
|
6116
|
-
function
|
|
6117
|
-
|
|
6067
|
+
function is_collapsed(value) {
|
|
6068
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
6069
|
+
let keys = Object.keys(value);
|
|
6070
|
+
if (keys.length === 1 && keys[0] === "__collapsed__") {
|
|
6071
|
+
let c = value.__collapsed__ || {};
|
|
6072
|
+
return {
|
|
6073
|
+
size: c.size,
|
|
6074
|
+
path: c.path,
|
|
6075
|
+
is_array: false
|
|
6076
|
+
};
|
|
6077
|
+
}
|
|
6078
|
+
return null;
|
|
6079
|
+
}
|
|
6080
|
+
if (Array.isArray(value) && value.length === 1) {
|
|
6081
|
+
let e = value[0];
|
|
6082
|
+
if (e && typeof e === "object" && !Array.isArray(e) && Object.keys(e).length === 1 && e.__collapsed__) {
|
|
6083
|
+
let c = e.__collapsed__ || {};
|
|
6084
|
+
return {
|
|
6085
|
+
size: c.size,
|
|
6086
|
+
path: c.path,
|
|
6087
|
+
is_array: true
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
return null;
|
|
6118
6092
|
}
|
|
6119
6093
|
/************************************************************
|
|
6120
|
-
*
|
|
6094
|
+
* Join / split absolute path segments (kernel convention)
|
|
6121
6095
|
************************************************************/
|
|
6122
|
-
function
|
|
6123
|
-
|
|
6124
|
-
|
|
6096
|
+
function seg_join(segments) {
|
|
6097
|
+
return segments.map(function(s) {
|
|
6098
|
+
return String(s);
|
|
6099
|
+
}).join("`");
|
|
6100
|
+
}
|
|
6101
|
+
function seg_split(path) {
|
|
6102
|
+
if (path === "" || path === null || path === void 0) return [];
|
|
6103
|
+
return path.split("`");
|
|
6125
6104
|
}
|
|
6126
6105
|
/************************************************************
|
|
6127
|
-
*
|
|
6106
|
+
* Walk a JSON tree by absolute segments; undefined if the
|
|
6107
|
+
* path does not resolve.
|
|
6128
6108
|
************************************************************/
|
|
6129
|
-
function
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6109
|
+
function get_by_segments(root, segments) {
|
|
6110
|
+
let v = root;
|
|
6111
|
+
for (let seg of segments) {
|
|
6112
|
+
if (v === null || v === void 0) return;
|
|
6113
|
+
if (Array.isArray(v)) v = v[Number(seg)];
|
|
6114
|
+
else if (typeof v === "object") v = v[seg];
|
|
6115
|
+
else return;
|
|
6116
|
+
}
|
|
6117
|
+
return v;
|
|
6118
|
+
}
|
|
6133
6119
|
/************************************************************
|
|
6120
|
+
* Set a value at absolute segments (mutates root).
|
|
6134
6121
|
*
|
|
6122
|
+
* segments == [] means "replace the whole root" — the caller
|
|
6123
|
+
* must use the RETURNED value in that case (a primitive/array
|
|
6124
|
+
* root cannot be mutated in place).
|
|
6135
6125
|
************************************************************/
|
|
6136
|
-
function
|
|
6137
|
-
|
|
6138
|
-
|
|
6126
|
+
function set_by_segments(root, segments, new_value) {
|
|
6127
|
+
if (segments.length === 0) return new_value;
|
|
6128
|
+
let parent = get_by_segments(root, segments.slice(0, -1));
|
|
6129
|
+
if (parent === null || parent === void 0 || typeof parent !== "object") return root;
|
|
6130
|
+
let last = segments[segments.length - 1];
|
|
6131
|
+
if (Array.isArray(parent)) parent[Number(last)] = new_value;
|
|
6132
|
+
else parent[last] = new_value;
|
|
6133
|
+
return root;
|
|
6139
6134
|
}
|
|
6140
6135
|
/************************************************************
|
|
6136
|
+
* Does `value` (or any loaded descendant / its own key)
|
|
6137
|
+
* contain the lower-cased search term?
|
|
6141
6138
|
*
|
|
6139
|
+
* Collapsed (not-yet-loaded) subtrees can't be searched, so
|
|
6140
|
+
* they never match on content — only their key can match,
|
|
6141
|
+
* which the renderer checks separately.
|
|
6142
6142
|
************************************************************/
|
|
6143
|
-
function
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", true);
|
|
6158
|
-
enable($button_save, true, "MediumSeaGreen");
|
|
6159
|
-
enable($button_undo, true, "Magenta");
|
|
6160
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
6161
|
-
if (isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
6162
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
6163
|
-
if (isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
6164
|
-
});
|
|
6165
|
-
});
|
|
6166
|
-
} else {
|
|
6167
|
-
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", false);
|
|
6168
|
-
enable($button_save, false, OFF_COLOR);
|
|
6169
|
-
enable($button_undo, false, OFF_COLOR);
|
|
6170
|
-
setTimeout(function() {
|
|
6171
|
-
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
6172
|
-
if (isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
6173
|
-
$table.tabulator.on("dataChanged", function(data) {
|
|
6174
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6175
|
-
});
|
|
6176
|
-
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
6177
|
-
if (isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
6178
|
-
$table_nested.tabulator.on("dataChanged", function(data) {
|
|
6179
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6180
|
-
});
|
|
6181
|
-
});
|
|
6182
|
-
});
|
|
6183
|
-
}, 200);
|
|
6143
|
+
function subtree_matches(value, term) {
|
|
6144
|
+
if (!term) return true;
|
|
6145
|
+
if (is_collapsed(value)) return false;
|
|
6146
|
+
let type = json_type(value);
|
|
6147
|
+
if (type === "object") {
|
|
6148
|
+
for (let [k, v] of Object.entries(value)) {
|
|
6149
|
+
if (String(k).toLowerCase().includes(term)) return true;
|
|
6150
|
+
if (subtree_matches(v, term)) return true;
|
|
6151
|
+
}
|
|
6152
|
+
return false;
|
|
6153
|
+
}
|
|
6154
|
+
if (type === "array") {
|
|
6155
|
+
for (let v of value) if (subtree_matches(v, term)) return true;
|
|
6156
|
+
return false;
|
|
6184
6157
|
}
|
|
6158
|
+
return String(value).toLowerCase().includes(term);
|
|
6185
6159
|
}
|
|
6186
6160
|
/************************************************************
|
|
6161
|
+
* Fields whose numeric value is an epoch timestamp.
|
|
6187
6162
|
*
|
|
6163
|
+
* THE ONE LIST. C_YUI_FORM's jsoneditor takes its
|
|
6164
|
+
* `timestampTag` from here: it used to carry its own copy,
|
|
6165
|
+
* and a copy is how the two stopped agreeing.
|
|
6166
|
+
*
|
|
6167
|
+
* TWO SPELLINGS, AND LAS DOS SON DEL BACKEND. timeranger2
|
|
6168
|
+
* writes `from_t`/`from_tm` in the match_cond of a query and
|
|
6169
|
+
* `fr_t`/`fr_tm` in the metadata of a topic and of a file --
|
|
6170
|
+
* which is what a Raw JSON of a treedb actually shows. With
|
|
6171
|
+
* only the long pair listed, `to_t` got its date and the
|
|
6172
|
+
* `fr_t` right above it did not, which reads as a broken
|
|
6173
|
+
* annotator rather than a missing name.
|
|
6174
|
+
************************************************************/
|
|
6175
|
+
var TIME_FIELDS = [
|
|
6176
|
+
"__t__",
|
|
6177
|
+
"__tm__",
|
|
6178
|
+
"t",
|
|
6179
|
+
"tm",
|
|
6180
|
+
"time",
|
|
6181
|
+
"from_t",
|
|
6182
|
+
"to_t",
|
|
6183
|
+
"from_tm",
|
|
6184
|
+
"to_tm",
|
|
6185
|
+
"fr_t",
|
|
6186
|
+
"fr_tm",
|
|
6187
|
+
"t_input",
|
|
6188
|
+
"t_output"
|
|
6189
|
+
];
|
|
6190
|
+
function is_time_field(field) {
|
|
6191
|
+
return TIME_FIELDS.indexOf(field) >= 0;
|
|
6192
|
+
}
|
|
6193
|
+
/************************************************************
|
|
6194
|
+
* Format an epoch value as a local wall-clock string.
|
|
6195
|
+
* Accepts seconds or milliseconds; returns null when the
|
|
6196
|
+
* value is not a usable positive timestamp (0 == "unset").
|
|
6188
6197
|
************************************************************/
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
(0, _yuneta_gobj_js.log_error)(`Failed to read clipboard contents: ${error}`);
|
|
6196
|
-
}
|
|
6198
|
+
function format_epoch(value) {
|
|
6199
|
+
if (typeof value !== "number" || !isFinite(value) || value <= 0) return null;
|
|
6200
|
+
let ms = Math.abs(value) < 0xe8d4a51000 ? value * 1e3 : value;
|
|
6201
|
+
let d = new Date(ms);
|
|
6202
|
+
if (isNaN(d.getTime())) return null;
|
|
6203
|
+
return d.toLocaleString();
|
|
6197
6204
|
}
|
|
6198
6205
|
/************************************************************
|
|
6199
|
-
*
|
|
6206
|
+
* The text view's payload: the document as text, indented
|
|
6207
|
+
* four characters, cut at `max_chars`.
|
|
6200
6208
|
*
|
|
6201
|
-
*
|
|
6202
|
-
*
|
|
6203
|
-
*
|
|
6204
|
-
* always had, so nothing moves for a caller that says nothing.
|
|
6209
|
+
* Returns {text, capped}. `capped` is what the caller
|
|
6210
|
+
* announces under the dump — a cut that is not announced
|
|
6211
|
+
* reads as a document that ends there.
|
|
6205
6212
|
************************************************************/
|
|
6206
|
-
function
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6213
|
+
function json_text_dump(root, max_chars) {
|
|
6214
|
+
let text;
|
|
6215
|
+
try {
|
|
6216
|
+
text = JSON.stringify(root, null, 4);
|
|
6217
|
+
} catch (e) {
|
|
6218
|
+
return {
|
|
6219
|
+
text: "",
|
|
6220
|
+
capped: false,
|
|
6221
|
+
error: String(e)
|
|
6222
|
+
};
|
|
6223
|
+
}
|
|
6224
|
+
if (text === void 0) return {
|
|
6225
|
+
text: "",
|
|
6226
|
+
capped: false,
|
|
6227
|
+
error: "not representable as JSON"
|
|
6228
|
+
};
|
|
6229
|
+
if (typeof max_chars === "number" && max_chars > 0 && text.length > max_chars) return {
|
|
6230
|
+
text: text.slice(0, max_chars),
|
|
6231
|
+
capped: true
|
|
6232
|
+
};
|
|
6233
|
+
return {
|
|
6234
|
+
text,
|
|
6235
|
+
capped: false
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6238
|
+
/************************************************************
|
|
6239
|
+
* Which view a freshly mounted viewer opens on.
|
|
6240
|
+
*
|
|
6241
|
+
* Three answers, in order of authority: what the HOST asked
|
|
6242
|
+
* for, what the READER chose last time, and `fallback`.
|
|
6243
|
+
*
|
|
6244
|
+
* The host wins because a viewer mounted to show a graph has
|
|
6245
|
+
* to show one. The memory comes next, because reopening a
|
|
6246
|
+
* document in the view you just left is the whole point of
|
|
6247
|
+
* remembering it. `fallback` is the floor.
|
|
6248
|
+
*
|
|
6249
|
+
* Anything not in `modes` is treated as unsaid, which covers
|
|
6250
|
+
* an absent value, an empty one, and a stored string from an
|
|
6251
|
+
* older release that named a view no longer offered.
|
|
6252
|
+
************************************************************/
|
|
6253
|
+
function pick_view_mode(asked, remembered, modes, fallback) {
|
|
6254
|
+
let known = (m) => Array.isArray(modes) && modes.indexOf(m) >= 0;
|
|
6255
|
+
if (known(asked)) return asked;
|
|
6256
|
+
if (known(remembered)) return remembered;
|
|
6257
|
+
return fallback;
|
|
6258
|
+
}
|
|
6259
|
+
/************************************************************
|
|
6260
|
+
* The short label a container row carries beside its size.
|
|
6261
|
+
*
|
|
6262
|
+
* An array of dicts that carry an `id` is a list of RECORDS,
|
|
6263
|
+
* and a row that says only `2: {15}` makes you open all
|
|
6264
|
+
* fifteen fields of every one of them to find out WHICH
|
|
6265
|
+
* record it is. The id is the one field that answers that,
|
|
6266
|
+
* so the row says it without being opened.
|
|
6267
|
+
*
|
|
6268
|
+
* Only a SCALAR id, and only when it is really there: an `id`
|
|
6269
|
+
* that is itself a dict or a list is not a name, it is more
|
|
6270
|
+
* document, and printing `[object Object]` beside the size
|
|
6271
|
+
* would be worse than printing nothing.
|
|
6272
|
+
*
|
|
6273
|
+
* Cut at `max_chars`, because this rides on a row whose job
|
|
6274
|
+
* is to stay one line.
|
|
6275
|
+
************************************************************/
|
|
6276
|
+
function container_label(value, max_chars) {
|
|
6277
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return "";
|
|
6278
|
+
if (!Object.prototype.hasOwnProperty.call(value, "id")) return "";
|
|
6279
|
+
let id = value.id;
|
|
6280
|
+
let t = typeof id;
|
|
6281
|
+
if (t !== "string" && t !== "number" && t !== "boolean") return "";
|
|
6282
|
+
let text = String(id).trim();
|
|
6283
|
+
if (text === "") return "";
|
|
6284
|
+
let cap = typeof max_chars === "number" && max_chars > 0 ? max_chars : 0;
|
|
6285
|
+
if (cap && text.length > cap) return text.slice(0, cap) + "…";
|
|
6286
|
+
return text;
|
|
6287
|
+
}
|
|
6288
|
+
/************************************************************
|
|
6289
|
+
* A dict, in the JSON sense: not an array and not null.
|
|
6290
|
+
************************************************************/
|
|
6291
|
+
function is_plain_object(v) {
|
|
6292
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
6293
|
+
}
|
|
6294
|
+
/************************************************************
|
|
6295
|
+
* Does this container hold ONLY other containers?
|
|
6296
|
+
*
|
|
6297
|
+
* Such a value is not a thing, it is a LIST of things -- and a
|
|
6298
|
+
* list is fully described by the row that names it in its
|
|
6299
|
+
* parent, plus the edges to what it holds. Drawing it as a
|
|
6300
|
+
* card of its own put a node in the middle of the graph with no
|
|
6301
|
+
* data in it, saying nothing the parent's row did not already
|
|
6302
|
+
* say, and pushed everything below it one level down.
|
|
6303
|
+
*
|
|
6304
|
+
* An EMPTY container inside counts as content: `{0}` is
|
|
6305
|
+
* something the card has to show, and there is no child card
|
|
6306
|
+
* to show it instead.
|
|
6307
|
+
************************************************************/
|
|
6308
|
+
function is_pure_collection(value) {
|
|
6309
|
+
let entries = is_plain_object(value) ? Object.entries(value) : Array.isArray(value) ? value.map((v, i) => [i, v]) : [];
|
|
6310
|
+
if (entries.length === 0) return false;
|
|
6311
|
+
for (let [, v] of entries) if (!has_branch(v)) return false;
|
|
6312
|
+
return true;
|
|
6313
|
+
}
|
|
6314
|
+
/************************************************************
|
|
6315
|
+
* A value that gets a card, or children, of its own.
|
|
6316
|
+
************************************************************/
|
|
6317
|
+
function has_branch(value) {
|
|
6318
|
+
return is_plain_object(value) && Object.keys(value).length > 0 || Array.isArray(value) && value.length > 0;
|
|
6319
|
+
}
|
|
6320
|
+
/************************************************************
|
|
6321
|
+
* How many branches a container has, which is what the fold
|
|
6322
|
+
* chip counts.
|
|
6323
|
+
************************************************************/
|
|
6324
|
+
function count_branches(value) {
|
|
6325
|
+
let entries = is_plain_object(value) ? Object.values(value) : Array.isArray(value) ? value : [];
|
|
6326
|
+
let n = 0;
|
|
6327
|
+
for (let v of entries) if (has_branch(v)) n++;
|
|
6328
|
+
return n;
|
|
6329
|
+
}
|
|
6330
|
+
//#endregion
|
|
6331
|
+
//#region src/c_yui_form.js
|
|
6332
|
+
/***********************************************************************
|
|
6333
|
+
* c_yui_form.js
|
|
6334
|
+
*
|
|
6335
|
+
* Form (in Window or not)
|
|
6336
|
+
*
|
|
6337
|
+
* Copyright (c) 2024-2026, ArtGins.
|
|
6338
|
+
* All Rights Reserved.
|
|
6339
|
+
***********************************************************************/
|
|
6340
|
+
/***************************************************************
|
|
6341
|
+
* Constants
|
|
6342
|
+
***************************************************************/
|
|
6343
|
+
var GCLASS_NAME$16 = "C_YUI_FORM";
|
|
6344
|
+
/***************************************************************
|
|
6345
|
+
* Data
|
|
6346
|
+
***************************************************************/
|
|
6347
|
+
var attrs_table$16 = [
|
|
6348
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of publishing messages"),
|
|
6349
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else do it manually"),
|
|
6350
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "template", 0, "<div>You must supply a template for form</div>", "Template for form, object -> TEMPLATE, array -> DESC"),
|
|
6351
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "record", 0, "{}", "Data to form"),
|
|
6352
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "placeholders", 0, "{}", "Placeholder values for form fields"),
|
|
6353
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "fkey_options", 0, "{}", "Options for fkey fields: {topic_name: [ids or {id} records]}, read at build time"),
|
|
6354
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "form_mode", 0, "", "'create' (pkey editable+required) or 'update' (pkey readonly); empty = no pkey handling"),
|
|
6355
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "render_mode", 0, "exec", "'exec' interprets structured cols (template->sub-form, table->grid, coordinates->picker); 'edit' shows them as raw JSON editors (record-editing, e.g. the treedb topic form)"),
|
|
6356
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "pkey", 0, "id", "Primary key field name, used by form_mode"),
|
|
6357
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "topic_name", 0, "", "Treedb topic name: labels use the i18n cascade '<topic>.<col>' -> '<col>' -> header (like table headers)"),
|
|
6358
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Default is editable, set false to readonly"),
|
|
6359
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "selectable", 0, true, "Rows selectable with 'click' (BAD with editable=true)"),
|
|
6360
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
6361
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
|
|
6362
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
|
|
6363
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "toolbar", 0, [
|
|
6364
|
+
"save",
|
|
6365
|
+
"undo",
|
|
6366
|
+
"clear",
|
|
6367
|
+
"copy",
|
|
6368
|
+
"paste"
|
|
6369
|
+
], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
|
|
6370
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
6371
|
+
layout: "fitDataFill",
|
|
6372
|
+
validationMode: "highlight",
|
|
6373
|
+
columnDefaults: { resizable: true },
|
|
6374
|
+
maxHeight: "100%"
|
|
6375
|
+
}, "Default settings for Tabulator"),
|
|
6376
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Container element"),
|
|
6377
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "form_id", 0, "", "Form identifier"),
|
|
6378
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "initialized", 0, false, "Indicates if it is initialized"),
|
|
6379
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "changes", 0, false, "Indicates if changes have been made"),
|
|
6380
|
+
(0, _yuneta_gobj_js.SDATA_END)()
|
|
6381
|
+
];
|
|
6382
|
+
var PRIVATE_DATA$16 = {};
|
|
6383
|
+
var __gclass__$16 = null;
|
|
6384
|
+
/***************************
|
|
6385
|
+
* Framework Methods
|
|
6386
|
+
***************************/
|
|
6387
|
+
/************************************************************
|
|
6388
|
+
* Framework Method create
|
|
6389
|
+
************************************************************/
|
|
6390
|
+
function mt_create$16(gobj) {
|
|
6391
|
+
let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
|
|
6392
|
+
if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
|
|
6393
|
+
(0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
|
|
6394
|
+
let name = (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj));
|
|
6395
|
+
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "form_id", "form" + name);
|
|
6396
|
+
build_ui$14(gobj);
|
|
6397
|
+
}
|
|
6398
|
+
/************************************************************
|
|
6399
|
+
* Framework Method destroy
|
|
6400
|
+
* In this point, all children
|
|
6401
|
+
* and subscriptions are already deleted.
|
|
6402
|
+
************************************************************/
|
|
6403
|
+
function mt_destroy$16(gobj) {
|
|
6404
|
+
destroy_ui$10(gobj);
|
|
6405
|
+
}
|
|
6406
|
+
/************************************************************
|
|
6407
|
+
* Framework Method start
|
|
6408
|
+
************************************************************/
|
|
6409
|
+
function mt_start$15(gobj) {
|
|
6410
|
+
let record = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "record");
|
|
6411
|
+
if (record) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_LOAD_RECORD", record, gobj);
|
|
6412
|
+
}
|
|
6413
|
+
/************************************************************
|
|
6414
|
+
* Framework Method stop
|
|
6415
|
+
************************************************************/
|
|
6416
|
+
function mt_stop$15(gobj) {}
|
|
6417
|
+
/***************************
|
|
6418
|
+
* Local Methods
|
|
6419
|
+
***************************/
|
|
6420
|
+
/************************************************************
|
|
6421
|
+
*
|
|
6422
|
+
************************************************************/
|
|
6423
|
+
function isEventSet(table, event) {
|
|
6424
|
+
let eventListeners = table.externalEvents.events;
|
|
6425
|
+
return eventListeners && eventListeners[event] && eventListeners[event].length > 0;
|
|
6426
|
+
}
|
|
6427
|
+
/************************************************************
|
|
6428
|
+
*
|
|
6429
|
+
************************************************************/
|
|
6430
|
+
function set_changed_stated(gobj, changed) {
|
|
6431
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
6432
|
+
let $form = $container.querySelector("form");
|
|
6433
|
+
const enable = ($btn, on, color) => {
|
|
6434
|
+
if (!$btn) return;
|
|
6435
|
+
if (on) $btn.removeAttribute("disabled");
|
|
6436
|
+
else $btn.setAttribute("disabled", true);
|
|
6437
|
+
const $i = $btn.querySelector("span i");
|
|
6438
|
+
if ($i) $i.style.color = color;
|
|
6439
|
+
};
|
|
6440
|
+
const OFF_COLOR = "hsl(var(--bulma-button-h), var(--bulma-button-s), var(--bulma-button-color-l))";
|
|
6441
|
+
let $button_save = $container.querySelector(".button-save");
|
|
6442
|
+
let $button_undo = $container.querySelector(".button-undo");
|
|
6443
|
+
if (changed) {
|
|
6444
|
+
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", true);
|
|
6445
|
+
enable($button_save, true, "MediumSeaGreen");
|
|
6446
|
+
enable($button_undo, true, "Magenta");
|
|
6447
|
+
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
6448
|
+
if (isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
6449
|
+
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
6450
|
+
if (isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
6451
|
+
});
|
|
6452
|
+
});
|
|
6453
|
+
} else {
|
|
6454
|
+
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", false);
|
|
6455
|
+
enable($button_save, false, OFF_COLOR);
|
|
6456
|
+
enable($button_undo, false, OFF_COLOR);
|
|
6457
|
+
setTimeout(function() {
|
|
6458
|
+
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
6459
|
+
if (isEventSet($table.tabulator, "dataChanged")) $table.tabulator.off("dataChanged");
|
|
6460
|
+
$table.tabulator.on("dataChanged", function(data) {
|
|
6461
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6462
|
+
});
|
|
6463
|
+
$table.querySelectorAll(".yui-tabulator-table-nested").forEach(($table_nested) => {
|
|
6464
|
+
if (isEventSet($table_nested.tabulator, "dataChanged")) $table_nested.tabulator.off("dataChanged");
|
|
6465
|
+
$table_nested.tabulator.on("dataChanged", function(data) {
|
|
6466
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6467
|
+
});
|
|
6468
|
+
});
|
|
6469
|
+
});
|
|
6470
|
+
}, 200);
|
|
6471
|
+
}
|
|
6472
|
+
}
|
|
6473
|
+
/************************************************************
|
|
6474
|
+
*
|
|
6475
|
+
************************************************************/
|
|
6476
|
+
async function readClipboard$1(gobj) {
|
|
6477
|
+
try {
|
|
6478
|
+
const text = await navigator.clipboard.readText();
|
|
6479
|
+
let kw = JSON.parse(text);
|
|
6480
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_PASTE_RECORD", kw, gobj);
|
|
6481
|
+
} catch (error) {
|
|
6482
|
+
(0, _yuneta_gobj_js.log_error)(`Failed to read clipboard contents: ${error}`);
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
/************************************************************
|
|
6486
|
+
* Bottom toolbar, from the `toolbar` attr.
|
|
6487
|
+
*
|
|
6488
|
+
* The five buttons it can show, and their order, are the
|
|
6489
|
+
* caller's: a dialog with a single action asks for ["save"],
|
|
6490
|
+
* and [] leaves no toolbar at all. Default is the five it
|
|
6491
|
+
* always had, so nothing moves for a caller that says nothing.
|
|
6492
|
+
************************************************************/
|
|
6493
|
+
function toolbar_button$1(gobj, name) {
|
|
6494
|
+
switch (name) {
|
|
6495
|
+
case "save": return [
|
|
6496
|
+
"button",
|
|
6210
6497
|
{
|
|
6211
6498
|
class: "button p-1 button-save",
|
|
6212
6499
|
title: "save",
|
|
@@ -7168,7 +7455,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
7168
7455
|
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
7169
7456
|
},
|
|
7170
7457
|
timestampTag: function({ field, value, path }) {
|
|
7171
|
-
return field
|
|
7458
|
+
return is_time_field(field);
|
|
7172
7459
|
}
|
|
7173
7460
|
}
|
|
7174
7461
|
});
|
|
@@ -10038,559 +10325,297 @@ var ZoomCanvasPinch = class extends _antv_g6.ZoomCanvas {
|
|
|
10038
10325
|
}
|
|
10039
10326
|
let dx = touches[0].clientX - touches[1].clientX;
|
|
10040
10327
|
let dy = touches[0].clientY - touches[1].clientY;
|
|
10041
|
-
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
10042
|
-
if (dist <= 0) return;
|
|
10043
|
-
if (this._pinch_dist0 === 0) {
|
|
10044
|
-
this._pinch_dist0 = dist;
|
|
10045
|
-
this._pinch_zoom0 = graph.getZoom();
|
|
10046
|
-
return;
|
|
10047
|
-
}
|
|
10048
|
-
this._pinch_to(dist / this._pinch_dist0, touches, event);
|
|
10049
|
-
};
|
|
10050
|
-
state.container.addEventListener("touchmove", this._on_touch_move, { passive: true });
|
|
10051
|
-
}
|
|
10052
|
-
/************************************************************
|
|
10053
|
-
* Zoom to `scale` times the zoom the pinch started at,
|
|
10054
|
-
* about the midpoint between the two fingers.
|
|
10055
|
-
*
|
|
10056
|
-
* ABSOLUTE against the start of the gesture, where G6's
|
|
10057
|
-
* own pinch feeds `zoom()` an increment per frame: an
|
|
10058
|
-
* increment drifts away from the fingers over a long
|
|
10059
|
-
* gesture, and cannot come back when they close again.
|
|
10060
|
-
************************************************************/
|
|
10061
|
-
_pinch_to(scale, touches, event) {
|
|
10062
|
-
if (!this.validate(event)) return;
|
|
10063
|
-
let graph = this.context.graph;
|
|
10064
|
-
let state = touch_state_of(graph);
|
|
10065
|
-
let origin = null;
|
|
10066
|
-
let rect = state.container?.getBoundingClientRect();
|
|
10067
|
-
if (rect) origin = [(touches[0].clientX + touches[1].clientX) / 2 - rect.left, (touches[0].clientY + touches[1].clientY) / 2 - rect.top];
|
|
10068
|
-
graph.zoomTo(this._pinch_zoom0 * scale, false, origin);
|
|
10069
|
-
this.options.onFinish?.();
|
|
10070
|
-
}
|
|
10071
|
-
destroy() {
|
|
10072
|
-
if (this._on_touch_move) {
|
|
10073
|
-
__touch_state__.get(this.context.graph)?.container?.removeEventListener("touchmove", this._on_touch_move);
|
|
10074
|
-
this._on_touch_move = null;
|
|
10075
|
-
}
|
|
10076
|
-
super.destroy();
|
|
10077
|
-
}
|
|
10078
|
-
};
|
|
10079
|
-
/************************************************************
|
|
10080
|
-
* Register once, over the built-in `zoom-canvas`, so every
|
|
10081
|
-
* graph (tree, json, treedb, editor) pinches without
|
|
10082
|
-
* touching its behaviors list.
|
|
10083
|
-
************************************************************/
|
|
10084
|
-
var __zoom_canvas_patched__ = false;
|
|
10085
|
-
function ensure_pinch_zoom_patch() {
|
|
10086
|
-
if (__zoom_canvas_patched__) return;
|
|
10087
|
-
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.BEHAVIOR, "zoom-canvas", ZoomCanvasPinch);
|
|
10088
|
-
__zoom_canvas_patched__ = true;
|
|
10089
|
-
}
|
|
10090
|
-
/***************************
|
|
10091
|
-
* Long press
|
|
10092
|
-
***************************/
|
|
10093
|
-
/************************************************************
|
|
10094
|
-
* Is the click being dispatched right now the tail of a long
|
|
10095
|
-
* press? Answers true ONCE, to the first asker.
|
|
10096
|
-
*
|
|
10097
|
-
* @antv/g does not take `click` from the DOM either: it makes
|
|
10098
|
-
* one out of the pointerdown/pointerup pair itself, inside
|
|
10099
|
-
* the same `onPointerUp` that just fed us the release. So
|
|
10100
|
-
* the press that opened the menu goes on to click whatever it
|
|
10101
|
-
* opened the menu ON -- selecting the node, or worse, dropping
|
|
10102
|
-
* a selection of several -- unless the handler asks.
|
|
10103
|
-
************************************************************/
|
|
10104
|
-
function consume_long_press_click(graph) {
|
|
10105
|
-
if (!graph) return false;
|
|
10106
|
-
let state = __touch_state__.get(graph);
|
|
10107
|
-
if (!state || !state.long_press_click) return false;
|
|
10108
|
-
state.long_press_click = false;
|
|
10109
|
-
return true;
|
|
10110
|
-
}
|
|
10111
|
-
/************************************************************
|
|
10112
|
-
* Make a long press open the context menu.
|
|
10113
|
-
*
|
|
10114
|
-
* Returns the function that uninstalls it.
|
|
10115
|
-
*
|
|
10116
|
-
* The menu the press opens is whatever the `contextmenu`
|
|
10117
|
-
* plugin builds: we re-emit G6's own forwarded event under
|
|
10118
|
-
* the name the plugin listens for, so `getItems(e)` and
|
|
10119
|
-
* `enable(e)` see the same object a right-click would give
|
|
10120
|
-
* them -- same `target`, same `targetType`, same `client`.
|
|
10121
|
-
*
|
|
10122
|
-
* `options.enable(event)` decides, at the DOWN, whether this
|
|
10123
|
-
* press is even a candidate. Pass the plugin's own predicate:
|
|
10124
|
-
* a press on the empty canvas would otherwise arm a menu the
|
|
10125
|
-
* plugin then refuses to show, and swallow the click that
|
|
10126
|
-
* would have cleared the selection.
|
|
10127
|
-
************************************************************/
|
|
10128
|
-
function install_long_press_contextmenu(graph, options) {
|
|
10129
|
-
if (!graph) return () => {};
|
|
10130
|
-
let enable = options?.enable;
|
|
10131
|
-
let state = touch_state_of(graph);
|
|
10132
|
-
let pending = null;
|
|
10133
|
-
let disarm = () => {
|
|
10134
|
-
if (pending) {
|
|
10135
|
-
clearTimeout(pending.tick);
|
|
10136
|
-
pending = null;
|
|
10137
|
-
}
|
|
10138
|
-
};
|
|
10139
|
-
let announce = () => {
|
|
10140
|
-
if (!pending) return;
|
|
10141
|
-
if (state.multi) return;
|
|
10142
|
-
navigator.vibrate?.(15);
|
|
10143
|
-
};
|
|
10144
|
-
let fire = (event) => {
|
|
10145
|
-
state.long_press_click = true;
|
|
10146
|
-
setTimeout(() => {
|
|
10147
|
-
state.long_press_click = false;
|
|
10148
|
-
}, 0);
|
|
10149
|
-
let swallow = (evt) => {
|
|
10150
|
-
evt.stopPropagation();
|
|
10151
|
-
document.removeEventListener("click", swallow, true);
|
|
10152
|
-
};
|
|
10153
|
-
document.addEventListener("click", swallow, true);
|
|
10154
|
-
setTimeout(() => {
|
|
10155
|
-
document.removeEventListener("click", swallow, true);
|
|
10156
|
-
}, 1e3);
|
|
10157
|
-
graph.emit(`${event.targetType}:${_antv_g6.CommonEvent.CONTEXT_MENU}`, event);
|
|
10158
|
-
graph.emit(_antv_g6.CommonEvent.CONTEXT_MENU, event);
|
|
10159
|
-
};
|
|
10160
|
-
let on_down = (event) => {
|
|
10161
|
-
disarm();
|
|
10162
|
-
if (event.pointerType !== "touch") return;
|
|
10163
|
-
if (state.count > 1 || state.multi) return;
|
|
10164
|
-
if (enable && !enable(event)) return;
|
|
10165
|
-
let x = event.client?.x;
|
|
10166
|
-
let y = event.client?.y;
|
|
10167
|
-
if (x === void 0 || y === void 0) return;
|
|
10168
|
-
pending = {
|
|
10169
|
-
x,
|
|
10170
|
-
y,
|
|
10171
|
-
t0: Date.now(),
|
|
10172
|
-
event: Object.assign({}, event),
|
|
10173
|
-
tick: setTimeout(announce, 500)
|
|
10174
|
-
};
|
|
10175
|
-
};
|
|
10176
|
-
let on_move = (event) => {
|
|
10177
|
-
if (!pending) return;
|
|
10178
|
-
if (state.count > 1 || state.multi) {
|
|
10179
|
-
disarm();
|
|
10180
|
-
return;
|
|
10181
|
-
}
|
|
10182
|
-
let x = event.client?.x;
|
|
10183
|
-
let y = event.client?.y;
|
|
10184
|
-
if (x === void 0 || y === void 0) return;
|
|
10185
|
-
if (press_moved(pending, x, y)) disarm();
|
|
10186
|
-
};
|
|
10187
|
-
let on_up = (event) => {
|
|
10188
|
-
let press = pending;
|
|
10189
|
-
if (press) clearTimeout(press.tick);
|
|
10190
|
-
pending = null;
|
|
10191
|
-
if (!press) return;
|
|
10192
|
-
if (state.multi) return;
|
|
10193
|
-
let x = event.client?.x;
|
|
10194
|
-
let y = event.client?.y;
|
|
10195
|
-
if (x === void 0) x = press.x;
|
|
10196
|
-
if (y === void 0) y = press.y;
|
|
10197
|
-
if (classify_press(press, x, y, Date.now()) !== "long") return;
|
|
10198
|
-
fire(press.event);
|
|
10199
|
-
};
|
|
10200
|
-
let on_touch_cancel = () => {
|
|
10201
|
-
disarm();
|
|
10202
|
-
};
|
|
10203
|
-
state.container?.addEventListener("touchcancel", on_touch_cancel);
|
|
10204
|
-
graph.on(_antv_g6.CommonEvent.POINTER_DOWN, on_down);
|
|
10205
|
-
graph.on(_antv_g6.CommonEvent.POINTER_MOVE, on_move);
|
|
10206
|
-
graph.on(_antv_g6.CommonEvent.POINTER_UP, on_up);
|
|
10207
|
-
return () => {
|
|
10208
|
-
disarm();
|
|
10209
|
-
state.container?.removeEventListener("touchcancel", on_touch_cancel);
|
|
10210
|
-
graph.off(_antv_g6.CommonEvent.POINTER_DOWN, on_down);
|
|
10211
|
-
graph.off(_antv_g6.CommonEvent.POINTER_MOVE, on_move);
|
|
10212
|
-
graph.off(_antv_g6.CommonEvent.POINTER_UP, on_up);
|
|
10213
|
-
};
|
|
10214
|
-
}
|
|
10215
|
-
//#endregion
|
|
10216
|
-
//#region src/g6_drag_canvas_touch.js
|
|
10217
|
-
/***********************************************************************
|
|
10218
|
-
* g6_drag_canvas_touch.js
|
|
10219
|
-
*
|
|
10220
|
-
* Drop-in replacement for G6 v5 built-in 'drag-canvas'.
|
|
10221
|
-
*
|
|
10222
|
-
* Why this exists
|
|
10223
|
-
* ----------------
|
|
10224
|
-
* G6 v5.1.0 drag-canvas computes the pan delta as:
|
|
10225
|
-
*
|
|
10226
|
-
* const x = event.movement?.x ?? event.dx;
|
|
10227
|
-
*
|
|
10228
|
-
* but @antv/g always attaches a `movement` Point to every
|
|
10229
|
-
* federated pointer event (g-lite: `this.movement = new
|
|
10230
|
-
* Point()`), and fills it from the native
|
|
10231
|
-
* `PointerEvent.movementX/Y` (g-lite: `event.movement.x =
|
|
10232
|
-
* nativeEvent.movementX`). So `event.movement?.x` is never
|
|
10233
|
-
* nullish and the `?? event.dx` fallback never runs.
|
|
10234
|
-
*
|
|
10235
|
-
* Native `movementX/Y`:
|
|
10236
|
-
* - on touch pointers most mobile browsers leave it at 0
|
|
10237
|
-
* (it is a mouse / pointer-lock feature), so the canvas
|
|
10238
|
-
* barely pans on mobile;
|
|
10239
|
-
* - on desktop it is skewed by OS pointer acceleration and
|
|
10240
|
-
* devicePixelRatio, so the graph lags the cursor.
|
|
10241
|
-
*
|
|
10242
|
-
* The fix tracks `event.viewport` (the federated pointer
|
|
10243
|
-
* position already mapped to canvas/viewport space by
|
|
10244
|
-
* @antv/g, the exact units `translateBy` expects) and pans
|
|
10245
|
-
* by its frame-to-frame delta. This is:
|
|
10246
|
-
* - reliable on mouse AND touch (derived from clientX/Y,
|
|
10247
|
-
* never from native movementX);
|
|
10248
|
-
* - correct for ANY canvas scale (it is post-`getScale()`,
|
|
10249
|
-
* so a configured-vs-displayed size mismatch can no
|
|
10250
|
-
* longer make panning over- or under-shoot the cursor);
|
|
10251
|
-
* - the same source G6's own `zoom-canvas` uses for its
|
|
10252
|
-
* origin (`event.viewport`).
|
|
10253
|
-
* The subclass reuses all of the built-in clamp/cursor logic
|
|
10254
|
-
* and only swaps the delta source.
|
|
10255
|
-
*
|
|
10256
|
-
* Registered globally over the built-in 'drag-canvas' id so
|
|
10257
|
-
* every graph (tree, json, treedb, editor) is fixed without
|
|
10258
|
-
* touching each consumer's behaviors list.
|
|
10259
|
-
*
|
|
10260
|
-
* It also has to keep the ONE guard the base class has and
|
|
10261
|
-
* this replacement dropped: G6's `onDrag` refuses to pan
|
|
10262
|
-
* while a pinch is in progress. See `is_pinching` in
|
|
10263
|
-
* `g6_touch_gestures.js`, which is where the pinch that
|
|
10264
|
-
* matters is now recognised.
|
|
10265
|
-
*
|
|
10266
|
-
* Copyright (c) 2026, ArtGins.
|
|
10267
|
-
* All Rights Reserved.
|
|
10268
|
-
***********************************************************************/
|
|
10269
|
-
/************************************************************
|
|
10270
|
-
* Custom drag-canvas: same behavior, reliable delta source
|
|
10271
|
-
************************************************************/
|
|
10272
|
-
var DragCanvasTouch = class extends _antv_g6.DragCanvas {
|
|
10273
|
-
constructor(context, options) {
|
|
10274
|
-
super(context, options);
|
|
10275
|
-
this._lastVP = null;
|
|
10276
|
-
this._onDragTouch = (event) => {
|
|
10277
|
-
if (!this.isDragging) {
|
|
10278
|
-
this._lastVP = null;
|
|
10279
|
-
return;
|
|
10280
|
-
}
|
|
10281
|
-
if (is_pinching(this.context.graph)) {
|
|
10282
|
-
this._lastVP = null;
|
|
10283
|
-
return;
|
|
10284
|
-
}
|
|
10285
|
-
let vp = event.viewport;
|
|
10286
|
-
if (!vp || typeof vp.x !== "number" || typeof vp.y !== "number") return;
|
|
10287
|
-
if (this._lastVP === null) {
|
|
10288
|
-
this._lastVP = {
|
|
10289
|
-
x: vp.x,
|
|
10290
|
-
y: vp.y
|
|
10291
|
-
};
|
|
10328
|
+
let dist = Math.sqrt(dx * dx + dy * dy);
|
|
10329
|
+
if (dist <= 0) return;
|
|
10330
|
+
if (this._pinch_dist0 === 0) {
|
|
10331
|
+
this._pinch_dist0 = dist;
|
|
10332
|
+
this._pinch_zoom0 = graph.getZoom();
|
|
10292
10333
|
return;
|
|
10293
10334
|
}
|
|
10294
|
-
|
|
10295
|
-
let y = vp.y - this._lastVP.y;
|
|
10296
|
-
this._lastVP.x = vp.x;
|
|
10297
|
-
this._lastVP.y = vp.y;
|
|
10298
|
-
if (x !== 0 || y !== 0) this.translate([x, y], false);
|
|
10299
|
-
};
|
|
10300
|
-
this._onDragEndReset = () => {
|
|
10301
|
-
this._lastVP = null;
|
|
10335
|
+
this._pinch_to(dist / this._pinch_dist0, touches, event);
|
|
10302
10336
|
};
|
|
10303
|
-
|
|
10304
|
-
graph.off(_antv_g6.CommonEvent.DRAG, this.onDrag);
|
|
10305
|
-
graph.on(_antv_g6.CommonEvent.DRAG, this._onDragTouch);
|
|
10306
|
-
graph.on(_antv_g6.CommonEvent.DRAG_END, this._onDragEndReset);
|
|
10337
|
+
state.container.addEventListener("touchmove", this._on_touch_move, { passive: true });
|
|
10307
10338
|
}
|
|
10308
|
-
|
|
10309
|
-
|
|
10339
|
+
/************************************************************
|
|
10340
|
+
* Zoom to `scale` times the zoom the pinch started at,
|
|
10341
|
+
* about the midpoint between the two fingers.
|
|
10342
|
+
*
|
|
10343
|
+
* ABSOLUTE against the start of the gesture, where G6's
|
|
10344
|
+
* own pinch feeds `zoom()` an increment per frame: an
|
|
10345
|
+
* increment drifts away from the fingers over a long
|
|
10346
|
+
* gesture, and cannot come back when they close again.
|
|
10347
|
+
************************************************************/
|
|
10348
|
+
_pinch_to(scale, touches, event) {
|
|
10349
|
+
if (!this.validate(event)) return;
|
|
10310
10350
|
let graph = this.context.graph;
|
|
10311
|
-
graph
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
graph.
|
|
10351
|
+
let state = touch_state_of(graph);
|
|
10352
|
+
let origin = null;
|
|
10353
|
+
let rect = state.container?.getBoundingClientRect();
|
|
10354
|
+
if (rect) origin = [(touches[0].clientX + touches[1].clientX) / 2 - rect.left, (touches[0].clientY + touches[1].clientY) / 2 - rect.top];
|
|
10355
|
+
graph.zoomTo(this._pinch_zoom0 * scale, false, origin);
|
|
10356
|
+
this.options.onFinish?.();
|
|
10316
10357
|
}
|
|
10317
10358
|
destroy() {
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10359
|
+
if (this._on_touch_move) {
|
|
10360
|
+
__touch_state__.get(this.context.graph)?.container?.removeEventListener("touchmove", this._on_touch_move);
|
|
10361
|
+
this._on_touch_move = null;
|
|
10362
|
+
}
|
|
10321
10363
|
super.destroy();
|
|
10322
10364
|
}
|
|
10323
10365
|
};
|
|
10324
10366
|
/************************************************************
|
|
10325
|
-
*
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
function ensure_drag_canvas_patch() {
|
|
10329
|
-
if (__drag_canvas_patched__) return;
|
|
10330
|
-
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.BEHAVIOR, "drag-canvas", DragCanvasTouch);
|
|
10331
|
-
__drag_canvas_patched__ = true;
|
|
10332
|
-
}
|
|
10333
|
-
/************************************************************
|
|
10334
|
-
* JSON type discriminator (viewer vocabulary)
|
|
10367
|
+
* Register once, over the built-in `zoom-canvas`, so every
|
|
10368
|
+
* graph (tree, json, treedb, editor) pinches without
|
|
10369
|
+
* touching its behaviors list.
|
|
10335
10370
|
************************************************************/
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
if (
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
case "number": return "number";
|
|
10342
|
-
case "boolean": return "boolean";
|
|
10343
|
-
case "object": return "object";
|
|
10344
|
-
default: return "unknown";
|
|
10345
|
-
}
|
|
10371
|
+
var __zoom_canvas_patched__ = false;
|
|
10372
|
+
function ensure_pinch_zoom_patch() {
|
|
10373
|
+
if (__zoom_canvas_patched__) return;
|
|
10374
|
+
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.BEHAVIOR, "zoom-canvas", ZoomCanvasPinch);
|
|
10375
|
+
__zoom_canvas_patched__ = true;
|
|
10346
10376
|
}
|
|
10377
|
+
/***************************
|
|
10378
|
+
* Long press
|
|
10379
|
+
***************************/
|
|
10347
10380
|
/************************************************************
|
|
10348
|
-
*
|
|
10349
|
-
*
|
|
10350
|
-
* The kernel replaces an over-limit dict with:
|
|
10351
|
-
* { "__collapsed__": { "path": ..., "size": N } }
|
|
10352
|
-
* and an over-limit array with:
|
|
10353
|
-
* [ { "__collapsed__": { "path": ..., "size": N } } ]
|
|
10381
|
+
* Is the click being dispatched right now the tail of a long
|
|
10382
|
+
* press? Answers true ONCE, to the first asker.
|
|
10354
10383
|
*
|
|
10355
|
-
*
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
let c = value.__collapsed__ || {};
|
|
10362
|
-
return {
|
|
10363
|
-
size: c.size,
|
|
10364
|
-
path: c.path,
|
|
10365
|
-
is_array: false
|
|
10366
|
-
};
|
|
10367
|
-
}
|
|
10368
|
-
return null;
|
|
10369
|
-
}
|
|
10370
|
-
if (Array.isArray(value) && value.length === 1) {
|
|
10371
|
-
let e = value[0];
|
|
10372
|
-
if (e && typeof e === "object" && !Array.isArray(e) && Object.keys(e).length === 1 && e.__collapsed__) {
|
|
10373
|
-
let c = e.__collapsed__ || {};
|
|
10374
|
-
return {
|
|
10375
|
-
size: c.size,
|
|
10376
|
-
path: c.path,
|
|
10377
|
-
is_array: true
|
|
10378
|
-
};
|
|
10379
|
-
}
|
|
10380
|
-
}
|
|
10381
|
-
return null;
|
|
10382
|
-
}
|
|
10383
|
-
/************************************************************
|
|
10384
|
-
* Join / split absolute path segments (kernel convention)
|
|
10385
|
-
************************************************************/
|
|
10386
|
-
function seg_join(segments) {
|
|
10387
|
-
return segments.map(function(s) {
|
|
10388
|
-
return String(s);
|
|
10389
|
-
}).join("`");
|
|
10390
|
-
}
|
|
10391
|
-
function seg_split(path) {
|
|
10392
|
-
if (path === "" || path === null || path === void 0) return [];
|
|
10393
|
-
return path.split("`");
|
|
10394
|
-
}
|
|
10395
|
-
/************************************************************
|
|
10396
|
-
* Walk a JSON tree by absolute segments; undefined if the
|
|
10397
|
-
* path does not resolve.
|
|
10384
|
+
* @antv/g does not take `click` from the DOM either: it makes
|
|
10385
|
+
* one out of the pointerdown/pointerup pair itself, inside
|
|
10386
|
+
* the same `onPointerUp` that just fed us the release. So
|
|
10387
|
+
* the press that opened the menu goes on to click whatever it
|
|
10388
|
+
* opened the menu ON -- selecting the node, or worse, dropping
|
|
10389
|
+
* a selection of several -- unless the handler asks.
|
|
10398
10390
|
************************************************************/
|
|
10399
|
-
function
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
else return;
|
|
10406
|
-
}
|
|
10407
|
-
return v;
|
|
10391
|
+
function consume_long_press_click(graph) {
|
|
10392
|
+
if (!graph) return false;
|
|
10393
|
+
let state = __touch_state__.get(graph);
|
|
10394
|
+
if (!state || !state.long_press_click) return false;
|
|
10395
|
+
state.long_press_click = false;
|
|
10396
|
+
return true;
|
|
10408
10397
|
}
|
|
10409
10398
|
/************************************************************
|
|
10410
|
-
*
|
|
10399
|
+
* Make a long press open the context menu.
|
|
10411
10400
|
*
|
|
10412
|
-
*
|
|
10413
|
-
* must use the RETURNED value in that case (a primitive/array
|
|
10414
|
-
* root cannot be mutated in place).
|
|
10415
|
-
************************************************************/
|
|
10416
|
-
function set_by_segments(root, segments, new_value) {
|
|
10417
|
-
if (segments.length === 0) return new_value;
|
|
10418
|
-
let parent = get_by_segments(root, segments.slice(0, -1));
|
|
10419
|
-
if (parent === null || parent === void 0 || typeof parent !== "object") return root;
|
|
10420
|
-
let last = segments[segments.length - 1];
|
|
10421
|
-
if (Array.isArray(parent)) parent[Number(last)] = new_value;
|
|
10422
|
-
else parent[last] = new_value;
|
|
10423
|
-
return root;
|
|
10424
|
-
}
|
|
10425
|
-
/************************************************************
|
|
10426
|
-
* Does `value` (or any loaded descendant / its own key)
|
|
10427
|
-
* contain the lower-cased search term?
|
|
10401
|
+
* Returns the function that uninstalls it.
|
|
10428
10402
|
*
|
|
10429
|
-
*
|
|
10430
|
-
*
|
|
10431
|
-
*
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
if (!term) return true;
|
|
10435
|
-
if (is_collapsed(value)) return false;
|
|
10436
|
-
let type = json_type(value);
|
|
10437
|
-
if (type === "object") {
|
|
10438
|
-
for (let [k, v] of Object.entries(value)) {
|
|
10439
|
-
if (String(k).toLowerCase().includes(term)) return true;
|
|
10440
|
-
if (subtree_matches(v, term)) return true;
|
|
10441
|
-
}
|
|
10442
|
-
return false;
|
|
10443
|
-
}
|
|
10444
|
-
if (type === "array") {
|
|
10445
|
-
for (let v of value) if (subtree_matches(v, term)) return true;
|
|
10446
|
-
return false;
|
|
10447
|
-
}
|
|
10448
|
-
return String(value).toLowerCase().includes(term);
|
|
10449
|
-
}
|
|
10450
|
-
/************************************************************
|
|
10451
|
-
* Fields whose numeric value is an epoch timestamp.
|
|
10452
|
-
* Mirrors the timestampTag list wired into C_YUI_FORM's
|
|
10453
|
-
* jsoneditor control, so the two viewers agree.
|
|
10454
|
-
************************************************************/
|
|
10455
|
-
function is_time_field(field) {
|
|
10456
|
-
return field === "__t__" || field === "__tm__" || field === "tm" || field === "t" || field === "time" || field === "from_t" || field === "to_t" || field === "t_input" || field === "t_output" || field === "from_tm" || field === "to_tm";
|
|
10457
|
-
}
|
|
10458
|
-
/************************************************************
|
|
10459
|
-
* Format an epoch value as a local wall-clock string.
|
|
10460
|
-
* Accepts seconds or milliseconds; returns null when the
|
|
10461
|
-
* value is not a usable positive timestamp (0 == "unset").
|
|
10462
|
-
************************************************************/
|
|
10463
|
-
function format_epoch(value) {
|
|
10464
|
-
if (typeof value !== "number" || !isFinite(value) || value <= 0) return null;
|
|
10465
|
-
let ms = Math.abs(value) < 0xe8d4a51000 ? value * 1e3 : value;
|
|
10466
|
-
let d = new Date(ms);
|
|
10467
|
-
if (isNaN(d.getTime())) return null;
|
|
10468
|
-
return d.toLocaleString();
|
|
10469
|
-
}
|
|
10470
|
-
/************************************************************
|
|
10471
|
-
* The text view's payload: the document as text, indented
|
|
10472
|
-
* four characters, cut at `max_chars`.
|
|
10403
|
+
* The menu the press opens is whatever the `contextmenu`
|
|
10404
|
+
* plugin builds: we re-emit G6's own forwarded event under
|
|
10405
|
+
* the name the plugin listens for, so `getItems(e)` and
|
|
10406
|
+
* `enable(e)` see the same object a right-click would give
|
|
10407
|
+
* them -- same `target`, same `targetType`, same `client`.
|
|
10473
10408
|
*
|
|
10474
|
-
*
|
|
10475
|
-
*
|
|
10476
|
-
*
|
|
10409
|
+
* `options.enable(event)` decides, at the DOWN, whether this
|
|
10410
|
+
* press is even a candidate. Pass the plugin's own predicate:
|
|
10411
|
+
* a press on the empty canvas would otherwise arm a menu the
|
|
10412
|
+
* plugin then refuses to show, and swallow the click that
|
|
10413
|
+
* would have cleared the selection.
|
|
10477
10414
|
************************************************************/
|
|
10478
|
-
function
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10415
|
+
function install_long_press_contextmenu(graph, options) {
|
|
10416
|
+
if (!graph) return () => {};
|
|
10417
|
+
let enable = options?.enable;
|
|
10418
|
+
let state = touch_state_of(graph);
|
|
10419
|
+
let pending = null;
|
|
10420
|
+
let disarm = () => {
|
|
10421
|
+
if (pending) {
|
|
10422
|
+
clearTimeout(pending.tick);
|
|
10423
|
+
pending = null;
|
|
10424
|
+
}
|
|
10425
|
+
};
|
|
10426
|
+
let announce = () => {
|
|
10427
|
+
if (!pending) return;
|
|
10428
|
+
if (state.multi) return;
|
|
10429
|
+
navigator.vibrate?.(15);
|
|
10430
|
+
};
|
|
10431
|
+
let fire = (event) => {
|
|
10432
|
+
state.long_press_click = true;
|
|
10433
|
+
setTimeout(() => {
|
|
10434
|
+
state.long_press_click = false;
|
|
10435
|
+
}, 0);
|
|
10436
|
+
let swallow = (evt) => {
|
|
10437
|
+
evt.stopPropagation();
|
|
10438
|
+
document.removeEventListener("click", swallow, true);
|
|
10487
10439
|
};
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10440
|
+
document.addEventListener("click", swallow, true);
|
|
10441
|
+
setTimeout(() => {
|
|
10442
|
+
document.removeEventListener("click", swallow, true);
|
|
10443
|
+
}, 1e3);
|
|
10444
|
+
graph.emit(`${event.targetType}:${_antv_g6.CommonEvent.CONTEXT_MENU}`, event);
|
|
10445
|
+
graph.emit(_antv_g6.CommonEvent.CONTEXT_MENU, event);
|
|
10493
10446
|
};
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10447
|
+
let on_down = (event) => {
|
|
10448
|
+
disarm();
|
|
10449
|
+
if (event.pointerType !== "touch") return;
|
|
10450
|
+
if (state.count > 1 || state.multi) return;
|
|
10451
|
+
if (enable && !enable(event)) return;
|
|
10452
|
+
let x = event.client?.x;
|
|
10453
|
+
let y = event.client?.y;
|
|
10454
|
+
if (x === void 0 || y === void 0) return;
|
|
10455
|
+
pending = {
|
|
10456
|
+
x,
|
|
10457
|
+
y,
|
|
10458
|
+
t0: Date.now(),
|
|
10459
|
+
event: Object.assign({}, event),
|
|
10460
|
+
tick: setTimeout(announce, 500)
|
|
10461
|
+
};
|
|
10497
10462
|
};
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10463
|
+
let on_move = (event) => {
|
|
10464
|
+
if (!pending) return;
|
|
10465
|
+
if (state.count > 1 || state.multi) {
|
|
10466
|
+
disarm();
|
|
10467
|
+
return;
|
|
10468
|
+
}
|
|
10469
|
+
let x = event.client?.x;
|
|
10470
|
+
let y = event.client?.y;
|
|
10471
|
+
if (x === void 0 || y === void 0) return;
|
|
10472
|
+
if (press_moved(pending, x, y)) disarm();
|
|
10473
|
+
};
|
|
10474
|
+
let on_up = (event) => {
|
|
10475
|
+
let press = pending;
|
|
10476
|
+
if (press) clearTimeout(press.tick);
|
|
10477
|
+
pending = null;
|
|
10478
|
+
if (!press) return;
|
|
10479
|
+
if (state.multi) return;
|
|
10480
|
+
let x = event.client?.x;
|
|
10481
|
+
let y = event.client?.y;
|
|
10482
|
+
if (x === void 0) x = press.x;
|
|
10483
|
+
if (y === void 0) y = press.y;
|
|
10484
|
+
if (classify_press(press, x, y, Date.now()) !== "long") return;
|
|
10485
|
+
fire(press.event);
|
|
10486
|
+
};
|
|
10487
|
+
let on_touch_cancel = () => {
|
|
10488
|
+
disarm();
|
|
10489
|
+
};
|
|
10490
|
+
state.container?.addEventListener("touchcancel", on_touch_cancel);
|
|
10491
|
+
graph.on(_antv_g6.CommonEvent.POINTER_DOWN, on_down);
|
|
10492
|
+
graph.on(_antv_g6.CommonEvent.POINTER_MOVE, on_move);
|
|
10493
|
+
graph.on(_antv_g6.CommonEvent.POINTER_UP, on_up);
|
|
10494
|
+
return () => {
|
|
10495
|
+
disarm();
|
|
10496
|
+
state.container?.removeEventListener("touchcancel", on_touch_cancel);
|
|
10497
|
+
graph.off(_antv_g6.CommonEvent.POINTER_DOWN, on_down);
|
|
10498
|
+
graph.off(_antv_g6.CommonEvent.POINTER_MOVE, on_move);
|
|
10499
|
+
graph.off(_antv_g6.CommonEvent.POINTER_UP, on_up);
|
|
10501
10500
|
};
|
|
10502
10501
|
}
|
|
10503
|
-
|
|
10504
|
-
|
|
10502
|
+
//#endregion
|
|
10503
|
+
//#region src/g6_drag_canvas_touch.js
|
|
10504
|
+
/***********************************************************************
|
|
10505
|
+
* g6_drag_canvas_touch.js
|
|
10505
10506
|
*
|
|
10506
|
-
*
|
|
10507
|
-
* for, what the READER chose last time, and `fallback`.
|
|
10507
|
+
* Drop-in replacement for G6 v5 built-in 'drag-canvas'.
|
|
10508
10508
|
*
|
|
10509
|
-
*
|
|
10510
|
-
*
|
|
10511
|
-
*
|
|
10512
|
-
* remembering it. `fallback` is the floor.
|
|
10509
|
+
* Why this exists
|
|
10510
|
+
* ----------------
|
|
10511
|
+
* G6 v5.1.0 drag-canvas computes the pan delta as:
|
|
10513
10512
|
*
|
|
10514
|
-
*
|
|
10515
|
-
* an absent value, an empty one, and a stored string from an
|
|
10516
|
-
* older release that named a view no longer offered.
|
|
10517
|
-
************************************************************/
|
|
10518
|
-
function pick_view_mode(asked, remembered, modes, fallback) {
|
|
10519
|
-
let known = (m) => Array.isArray(modes) && modes.indexOf(m) >= 0;
|
|
10520
|
-
if (known(asked)) return asked;
|
|
10521
|
-
if (known(remembered)) return remembered;
|
|
10522
|
-
return fallback;
|
|
10523
|
-
}
|
|
10524
|
-
/************************************************************
|
|
10525
|
-
* The short label a container row carries beside its size.
|
|
10513
|
+
* const x = event.movement?.x ?? event.dx;
|
|
10526
10514
|
*
|
|
10527
|
-
*
|
|
10528
|
-
*
|
|
10529
|
-
*
|
|
10530
|
-
*
|
|
10531
|
-
*
|
|
10515
|
+
* but @antv/g always attaches a `movement` Point to every
|
|
10516
|
+
* federated pointer event (g-lite: `this.movement = new
|
|
10517
|
+
* Point()`), and fills it from the native
|
|
10518
|
+
* `PointerEvent.movementX/Y` (g-lite: `event.movement.x =
|
|
10519
|
+
* nativeEvent.movementX`). So `event.movement?.x` is never
|
|
10520
|
+
* nullish and the `?? event.dx` fallback never runs.
|
|
10532
10521
|
*
|
|
10533
|
-
*
|
|
10534
|
-
*
|
|
10535
|
-
*
|
|
10536
|
-
*
|
|
10522
|
+
* Native `movementX/Y`:
|
|
10523
|
+
* - on touch pointers most mobile browsers leave it at 0
|
|
10524
|
+
* (it is a mouse / pointer-lock feature), so the canvas
|
|
10525
|
+
* barely pans on mobile;
|
|
10526
|
+
* - on desktop it is skewed by OS pointer acceleration and
|
|
10527
|
+
* devicePixelRatio, so the graph lags the cursor.
|
|
10537
10528
|
*
|
|
10538
|
-
*
|
|
10539
|
-
*
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
return text;
|
|
10552
|
-
}
|
|
10553
|
-
/************************************************************
|
|
10554
|
-
* A dict, in the JSON sense: not an array and not null.
|
|
10555
|
-
************************************************************/
|
|
10556
|
-
function is_plain_object(v) {
|
|
10557
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
10558
|
-
}
|
|
10559
|
-
/************************************************************
|
|
10560
|
-
* Does this container hold ONLY other containers?
|
|
10529
|
+
* The fix tracks `event.viewport` (the federated pointer
|
|
10530
|
+
* position already mapped to canvas/viewport space by
|
|
10531
|
+
* @antv/g, the exact units `translateBy` expects) and pans
|
|
10532
|
+
* by its frame-to-frame delta. This is:
|
|
10533
|
+
* - reliable on mouse AND touch (derived from clientX/Y,
|
|
10534
|
+
* never from native movementX);
|
|
10535
|
+
* - correct for ANY canvas scale (it is post-`getScale()`,
|
|
10536
|
+
* so a configured-vs-displayed size mismatch can no
|
|
10537
|
+
* longer make panning over- or under-shoot the cursor);
|
|
10538
|
+
* - the same source G6's own `zoom-canvas` uses for its
|
|
10539
|
+
* origin (`event.viewport`).
|
|
10540
|
+
* The subclass reuses all of the built-in clamp/cursor logic
|
|
10541
|
+
* and only swaps the delta source.
|
|
10561
10542
|
*
|
|
10562
|
-
*
|
|
10563
|
-
*
|
|
10564
|
-
*
|
|
10565
|
-
* card of its own put a node in the middle of the graph with no
|
|
10566
|
-
* data in it, saying nothing the parent's row did not already
|
|
10567
|
-
* say, and pushed everything below it one level down.
|
|
10543
|
+
* Registered globally over the built-in 'drag-canvas' id so
|
|
10544
|
+
* every graph (tree, json, treedb, editor) is fixed without
|
|
10545
|
+
* touching each consumer's behaviors list.
|
|
10568
10546
|
*
|
|
10569
|
-
*
|
|
10570
|
-
*
|
|
10571
|
-
*
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
}
|
|
10547
|
+
* It also has to keep the ONE guard the base class has and
|
|
10548
|
+
* this replacement dropped: G6's `onDrag` refuses to pan
|
|
10549
|
+
* while a pinch is in progress. See `is_pinching` in
|
|
10550
|
+
* `g6_touch_gestures.js`, which is where the pinch that
|
|
10551
|
+
* matters is now recognised.
|
|
10552
|
+
*
|
|
10553
|
+
* Copyright (c) 2026, ArtGins.
|
|
10554
|
+
* All Rights Reserved.
|
|
10555
|
+
***********************************************************************/
|
|
10579
10556
|
/************************************************************
|
|
10580
|
-
*
|
|
10557
|
+
* Custom drag-canvas: same behavior, reliable delta source
|
|
10581
10558
|
************************************************************/
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10559
|
+
var DragCanvasTouch = class extends _antv_g6.DragCanvas {
|
|
10560
|
+
constructor(context, options) {
|
|
10561
|
+
super(context, options);
|
|
10562
|
+
this._lastVP = null;
|
|
10563
|
+
this._onDragTouch = (event) => {
|
|
10564
|
+
if (!this.isDragging) {
|
|
10565
|
+
this._lastVP = null;
|
|
10566
|
+
return;
|
|
10567
|
+
}
|
|
10568
|
+
if (is_pinching(this.context.graph)) {
|
|
10569
|
+
this._lastVP = null;
|
|
10570
|
+
return;
|
|
10571
|
+
}
|
|
10572
|
+
let vp = event.viewport;
|
|
10573
|
+
if (!vp || typeof vp.x !== "number" || typeof vp.y !== "number") return;
|
|
10574
|
+
if (this._lastVP === null) {
|
|
10575
|
+
this._lastVP = {
|
|
10576
|
+
x: vp.x,
|
|
10577
|
+
y: vp.y
|
|
10578
|
+
};
|
|
10579
|
+
return;
|
|
10580
|
+
}
|
|
10581
|
+
let x = vp.x - this._lastVP.x;
|
|
10582
|
+
let y = vp.y - this._lastVP.y;
|
|
10583
|
+
this._lastVP.x = vp.x;
|
|
10584
|
+
this._lastVP.y = vp.y;
|
|
10585
|
+
if (x !== 0 || y !== 0) this.translate([x, y], false);
|
|
10586
|
+
};
|
|
10587
|
+
this._onDragEndReset = () => {
|
|
10588
|
+
this._lastVP = null;
|
|
10589
|
+
};
|
|
10590
|
+
let graph = this.context.graph;
|
|
10591
|
+
graph.off(_antv_g6.CommonEvent.DRAG, this.onDrag);
|
|
10592
|
+
graph.on(_antv_g6.CommonEvent.DRAG, this._onDragTouch);
|
|
10593
|
+
graph.on(_antv_g6.CommonEvent.DRAG_END, this._onDragEndReset);
|
|
10594
|
+
}
|
|
10595
|
+
update(options) {
|
|
10596
|
+
super.update(options);
|
|
10597
|
+
let graph = this.context.graph;
|
|
10598
|
+
graph.off(_antv_g6.CommonEvent.DRAG, this.onDrag);
|
|
10599
|
+
graph.off(_antv_g6.CommonEvent.DRAG, this._onDragTouch);
|
|
10600
|
+
graph.off(_antv_g6.CommonEvent.DRAG_END, this._onDragEndReset);
|
|
10601
|
+
graph.on(_antv_g6.CommonEvent.DRAG, this._onDragTouch);
|
|
10602
|
+
graph.on(_antv_g6.CommonEvent.DRAG_END, this._onDragEndReset);
|
|
10603
|
+
}
|
|
10604
|
+
destroy() {
|
|
10605
|
+
let graph = this.context.graph;
|
|
10606
|
+
graph.off(_antv_g6.CommonEvent.DRAG, this._onDragTouch);
|
|
10607
|
+
graph.off(_antv_g6.CommonEvent.DRAG_END, this._onDragEndReset);
|
|
10608
|
+
super.destroy();
|
|
10609
|
+
}
|
|
10610
|
+
};
|
|
10585
10611
|
/************************************************************
|
|
10586
|
-
*
|
|
10587
|
-
* chip counts.
|
|
10612
|
+
* Register once, replacing the built-in 'drag-canvas'
|
|
10588
10613
|
************************************************************/
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10614
|
+
var __drag_canvas_patched__ = false;
|
|
10615
|
+
function ensure_drag_canvas_patch() {
|
|
10616
|
+
if (__drag_canvas_patched__) return;
|
|
10617
|
+
(0, _antv_g6.register)(_antv_g6.ExtensionCategory.BEHAVIOR, "drag-canvas", DragCanvasTouch);
|
|
10618
|
+
__drag_canvas_patched__ = true;
|
|
10594
10619
|
}
|
|
10595
10620
|
//#endregion
|
|
10596
10621
|
//#region src/c_yui_json_graph.js
|