@yuneta/gobj-ui 7.23.142 → 7.23.143
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/README.md +26 -0
- package/dist/gobj-ui.cjs.js +665 -213
- package/dist/gobj-ui.es.js +664 -214
- package/index.js +2 -0
- package/package.json +1 -1
- package/src/c_yui_json_pad.js +454 -0
- package/src/c_yui_treedb_topic_with_form.js +14 -0
- package/src/yui_json_pad.js +109 -0
package/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export { register_c_yui_map } from "./src/c_yui_map.js";
|
|
|
23
23
|
export { register_c_yui_uplot } from "./src/c_yui_uplot.js";
|
|
24
24
|
export { register_c_yui_json_graph } from "./src/c_yui_json_graph.js";
|
|
25
25
|
export { register_c_yui_json } from "./src/c_yui_json.js";
|
|
26
|
+
export { register_c_yui_json_pad } from "./src/c_yui_json_pad.js";
|
|
26
27
|
export { register_c_yui_gclass } from "./src/c_yui_gclass.js";
|
|
27
28
|
export { register_c_yui_fsm_graph } from "./src/c_yui_fsm_graph.js";
|
|
28
29
|
export { register_c_yui_gobj_tree_js } from "./src/c_yui_gobj_tree_js.js";
|
|
@@ -129,6 +130,7 @@ export { yui_theme_now, yui_is_dark, yui_watch_theme } from "./src/yui_theme.js"
|
|
|
129
130
|
export { attach_clear, refresh_clear } from "./src/yui_inputs.js";
|
|
130
131
|
export { info_traffic, setup_dev, build_dev_panel, apply_dev_traces, dev_window_was_open } from "./src/yui_dev.js";
|
|
131
132
|
export { setup_frontend_view } from "./src/yui_frontend_view.js";
|
|
133
|
+
export { setup_json_pad } from "./src/yui_json_pad.js";
|
|
132
134
|
|
|
133
135
|
/*
|
|
134
136
|
* CSS - import these in your main entry point
|
package/package.json
CHANGED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* c_yui_json_pad.js
|
|
3
|
+
*
|
|
4
|
+
* C_YUI_JSON_PAD -- a pad to paste JSON from OUTSIDE and read it
|
|
5
|
+
* with the library's own viewer.
|
|
6
|
+
*
|
|
7
|
+
* The viewer (C_YUI_JSON: the lazy tree, the raw text and the
|
|
8
|
+
* graph) only ever showed what an app fetched. A JSON that came
|
|
9
|
+
* from anywhere else -- a log line, a config file, an answer copied
|
|
10
|
+
* from a terminal -- had to be read in whatever the operator had
|
|
11
|
+
* at hand. This is the blank page for it: paste, and read it the
|
|
12
|
+
* way every other document of the app is read.
|
|
13
|
+
*
|
|
14
|
+
* DOM (UPPER_SNAKE logical classes):
|
|
15
|
+
*
|
|
16
|
+
* JSON_PAD the root
|
|
17
|
+
* JSON_PAD_BAR the text area and its two buttons
|
|
18
|
+
* JSON_PAD_INPUT where the text goes
|
|
19
|
+
* JSON_PAD_VIEW read what is in the text area
|
|
20
|
+
* JSON_PAD_CLEAR empty the pad
|
|
21
|
+
* JSON_PAD_ERROR "invalid JSON" and the parser's reason
|
|
22
|
+
* JSON_PAD_BODY the C_YUI_JSON child
|
|
23
|
+
*
|
|
24
|
+
* Every way in is an event:
|
|
25
|
+
*
|
|
26
|
+
* EV_PASTE {text} a paste ANYWHERE in the pad replaces the
|
|
27
|
+
* document and shows it at once -- there
|
|
28
|
+
* is one document here, not a text to edit
|
|
29
|
+
* EV_VIEW the text area as it is (typed, or edited
|
|
30
|
+
* after a paste); also Ctrl+Enter
|
|
31
|
+
* EV_CLEAR back to the blank pad
|
|
32
|
+
*
|
|
33
|
+
* FSM: ST_EMPTY (nothing shown) and ST_SHOWING. EV_EXPAND_PATH,
|
|
34
|
+
* which the viewer publishes when a `__collapsed__` sentinel is
|
|
35
|
+
* opened, only exists in ST_SHOWING, and is answered with
|
|
36
|
+
* EV_SUBTREE_ERROR: a pasted document has no backend to fetch the
|
|
37
|
+
* rest from -- the source truncated it, and the pad says so.
|
|
38
|
+
*
|
|
39
|
+
* Hosted by setup_json_pad() (yui_json_pad.js) in a C_YUI_WINDOW;
|
|
40
|
+
* any host can mount `$container` itself.
|
|
41
|
+
*
|
|
42
|
+
* Copyright (c) 2026, ArtGins.
|
|
43
|
+
* All Rights Reserved.
|
|
44
|
+
***********************************************************************/
|
|
45
|
+
import {
|
|
46
|
+
SDATA,
|
|
47
|
+
SDATA_END,
|
|
48
|
+
data_type_t,
|
|
49
|
+
gclass_create,
|
|
50
|
+
gclass_find_by_name,
|
|
51
|
+
log_error,
|
|
52
|
+
log_warning,
|
|
53
|
+
gobj_create_pure_child,
|
|
54
|
+
gobj_read_pointer_attr,
|
|
55
|
+
gobj_read_attr,
|
|
56
|
+
gobj_write_attr,
|
|
57
|
+
gobj_subscribe_event,
|
|
58
|
+
gobj_send_event,
|
|
59
|
+
gobj_change_state,
|
|
60
|
+
gobj_start,
|
|
61
|
+
gobj_stop,
|
|
62
|
+
gobj_is_running,
|
|
63
|
+
gobj_short_name,
|
|
64
|
+
gobj_parent,
|
|
65
|
+
createElement2,
|
|
66
|
+
} from "@yuneta/gobj-js";
|
|
67
|
+
|
|
68
|
+
import {register_c_yui_json} from "./c_yui_json.js";
|
|
69
|
+
import {yui_toolbar_icon} from "./yui_toolbar.js";
|
|
70
|
+
|
|
71
|
+
import {t} from "i18next";
|
|
72
|
+
|
|
73
|
+
/***************************************************************
|
|
74
|
+
* Constants
|
|
75
|
+
***************************************************************/
|
|
76
|
+
const GCLASS_NAME = "C_YUI_JSON_PAD";
|
|
77
|
+
|
|
78
|
+
/***************************************************************
|
|
79
|
+
* Data
|
|
80
|
+
***************************************************************/
|
|
81
|
+
const attrs_table = [
|
|
82
|
+
SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
|
|
83
|
+
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTMLElement root, mounted by the host"),
|
|
84
|
+
SDATA_END()
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
let PRIVATE_DATA = {
|
|
88
|
+
$input: null, /* the text area */
|
|
89
|
+
$error: null, /* the "invalid JSON" line */
|
|
90
|
+
$reason: null, /* ...and the parser's reason inside it */
|
|
91
|
+
gobj_json: null, /* the C_YUI_JSON child */
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let __gclass__ = null;
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/******************************
|
|
100
|
+
* Framework Methods
|
|
101
|
+
******************************/
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/***************************************************************
|
|
107
|
+
* Framework Method: Create
|
|
108
|
+
***************************************************************/
|
|
109
|
+
function mt_create(gobj)
|
|
110
|
+
{
|
|
111
|
+
let priv = gobj.priv;
|
|
112
|
+
|
|
113
|
+
/*
|
|
114
|
+
* CHILD subscription model
|
|
115
|
+
*/
|
|
116
|
+
let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
117
|
+
if(!subscriber) {
|
|
118
|
+
subscriber = gobj_parent(gobj);
|
|
119
|
+
}
|
|
120
|
+
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
121
|
+
|
|
122
|
+
build_ui(gobj);
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* The viewer, as a pure child: it publishes EV_EXPAND_PATH to its
|
|
126
|
+
* parent (this gobj), which declares it in ST_SHOWING.
|
|
127
|
+
*/
|
|
128
|
+
priv.gobj_json = gobj_create_pure_child("json_pad_view", "C_YUI_JSON", {}, gobj);
|
|
129
|
+
let $view = gobj_read_attr(priv.gobj_json, "$container");
|
|
130
|
+
if(!$view) {
|
|
131
|
+
log_error(`${gobj_short_name(gobj)}: C_YUI_JSON without $container`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
gobj_read_attr(gobj, "$container").querySelector(".JSON_PAD_BODY").appendChild($view);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/***************************************************************
|
|
138
|
+
* Framework Method: Start
|
|
139
|
+
***************************************************************/
|
|
140
|
+
function mt_start(gobj)
|
|
141
|
+
{
|
|
142
|
+
let priv = gobj.priv;
|
|
143
|
+
|
|
144
|
+
if(priv.gobj_json) {
|
|
145
|
+
gobj_start(priv.gobj_json);
|
|
146
|
+
}
|
|
147
|
+
/* A blank pad is waiting for a paste: the caret goes where it lands. */
|
|
148
|
+
if(priv.$input) {
|
|
149
|
+
priv.$input.focus();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/***************************************************************
|
|
154
|
+
* Framework Method: Stop
|
|
155
|
+
***************************************************************/
|
|
156
|
+
function mt_stop(gobj)
|
|
157
|
+
{
|
|
158
|
+
let priv = gobj.priv;
|
|
159
|
+
|
|
160
|
+
if(priv.gobj_json && gobj_is_running(priv.gobj_json)) {
|
|
161
|
+
gobj_stop(priv.gobj_json);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/***************************************************************
|
|
166
|
+
* Framework Method: Destroy
|
|
167
|
+
***************************************************************/
|
|
168
|
+
function mt_destroy(gobj)
|
|
169
|
+
{
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
/***************************
|
|
176
|
+
* Local Methods
|
|
177
|
+
***************************/
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
/***************************************************************
|
|
183
|
+
* The pad: the text area and its buttons, the error line, and
|
|
184
|
+
* the body the viewer is mounted in.
|
|
185
|
+
***************************************************************/
|
|
186
|
+
function build_ui(gobj)
|
|
187
|
+
{
|
|
188
|
+
let priv = gobj.priv;
|
|
189
|
+
|
|
190
|
+
let button = (cls, icon, key, event_name) => {
|
|
191
|
+
return ['button', {class: `${cls} button`, type: 'button',
|
|
192
|
+
title: t(key), 'data-i18n-title': key,
|
|
193
|
+
'aria-label': t(key), 'data-i18n-aria-label': key},
|
|
194
|
+
[
|
|
195
|
+
yui_toolbar_icon(icon),
|
|
196
|
+
['span', {style: 'padding-left:5px;', i18n: key}, key]
|
|
197
|
+
],
|
|
198
|
+
{
|
|
199
|
+
click: (evt) => {
|
|
200
|
+
evt.stopPropagation();
|
|
201
|
+
gobj_send_event(gobj, event_name, {}, gobj);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
priv.$input = createElement2(
|
|
208
|
+
['textarea', {class: 'JSON_PAD_INPUT textarea', rows: 3, spellcheck: 'false',
|
|
209
|
+
placeholder: t('paste json here'), 'data-i18n-placeholder': 'paste json here',
|
|
210
|
+
title: t('paste json here'), 'data-i18n-title': 'paste json here',
|
|
211
|
+
'aria-label': t('paste json here'), 'data-i18n-aria-label': 'paste json here',
|
|
212
|
+
style: 'font-family:monospace; resize:vertical; min-height:4.5em;'}, '', {
|
|
213
|
+
keydown: (evt) => {
|
|
214
|
+
if((evt.ctrlKey || evt.metaKey) && evt.key === 'Enter') {
|
|
215
|
+
evt.preventDefault();
|
|
216
|
+
gobj_send_event(gobj, "EV_VIEW", {}, gobj);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}]
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
priv.$reason = createElement2(
|
|
223
|
+
['span', {class: 'JSON_PAD_ERROR_REASON', style: 'margin-left:.5em; font-family:monospace;'}, '']
|
|
224
|
+
);
|
|
225
|
+
priv.$error = createElement2(
|
|
226
|
+
['div', {class: 'JSON_PAD_ERROR is-hidden has-text-danger is-size-7', role: 'alert'}, [
|
|
227
|
+
['span', {class: 'JSON_PAD_ERROR_LABEL', style: 'font-weight:600;', i18n: 'invalid json'},
|
|
228
|
+
'invalid json'],
|
|
229
|
+
priv.$reason
|
|
230
|
+
]]
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
let $container = createElement2(
|
|
234
|
+
['div', {class: 'C_YUI_JSON_PAD JSON_PAD',
|
|
235
|
+
style: 'height:100%; display:flex; flex-direction:column; gap:.5rem; padding:.5rem;'}, [
|
|
236
|
+
['div', {class: 'JSON_PAD_BAR',
|
|
237
|
+
style: 'display:flex; gap:.5rem; align-items:flex-start;'}, [
|
|
238
|
+
['div', {style: 'flex:1 1 auto; min-width:0;'}, [priv.$input]],
|
|
239
|
+
['div', {style: 'flex:0 0 auto; display:flex; flex-direction:column; gap:.5rem;'}, [
|
|
240
|
+
button('JSON_PAD_VIEW', 'yi-eye', 'view', 'EV_VIEW'),
|
|
241
|
+
button('JSON_PAD_CLEAR', 'yi-broom', 'clear', 'EV_CLEAR'),
|
|
242
|
+
]]
|
|
243
|
+
]],
|
|
244
|
+
priv.$error,
|
|
245
|
+
['div', {class: 'JSON_PAD_BODY',
|
|
246
|
+
style: 'flex:1 1 auto; min-height:0; display:flex; flex-direction:column;'}, []]
|
|
247
|
+
], {
|
|
248
|
+
/* A paste anywhere in the pad is the document: it replaces
|
|
249
|
+
* what the text area held instead of landing at the caret. */
|
|
250
|
+
paste: (evt) => {
|
|
251
|
+
let text = evt.clipboardData? evt.clipboardData.getData("text") : "";
|
|
252
|
+
if(!text) {
|
|
253
|
+
return; /* an image or nothing: the browser's business */
|
|
254
|
+
}
|
|
255
|
+
evt.preventDefault();
|
|
256
|
+
gobj_send_event(gobj, "EV_PASTE", {text: text}, gobj);
|
|
257
|
+
}
|
|
258
|
+
}]
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
gobj_write_attr(gobj, "$container", $container);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/***************************************************************
|
|
265
|
+
* Read `text` as JSON and show it, or say why it is not.
|
|
266
|
+
***************************************************************/
|
|
267
|
+
function show_text(gobj, text)
|
|
268
|
+
{
|
|
269
|
+
let priv = gobj.priv;
|
|
270
|
+
let src = String(text || "").trim();
|
|
271
|
+
|
|
272
|
+
if(!src) {
|
|
273
|
+
clear_pad(gobj, false);
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
let doc;
|
|
278
|
+
try {
|
|
279
|
+
doc = JSON.parse(src);
|
|
280
|
+
} catch(e) {
|
|
281
|
+
/* The reader's text, not a fault of the app: said on the pad, and
|
|
282
|
+
* a warning for whoever reads the console. */
|
|
283
|
+
log_warning(`${gobj_short_name(gobj)}: the pasted text is not JSON: ${e.message}`);
|
|
284
|
+
priv.$reason.textContent = e.message;
|
|
285
|
+
priv.$error.classList.remove('is-hidden');
|
|
286
|
+
return -1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
priv.$error.classList.add('is-hidden');
|
|
290
|
+
priv.$reason.textContent = "";
|
|
291
|
+
gobj_send_event(priv.gobj_json, "EV_SET_JSON", {json: doc}, gobj);
|
|
292
|
+
gobj_change_state(gobj, "ST_SHOWING");
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/***************************************************************
|
|
297
|
+
* Back to the blank pad; `with_text` empties the text area too.
|
|
298
|
+
***************************************************************/
|
|
299
|
+
function clear_pad(gobj, with_text)
|
|
300
|
+
{
|
|
301
|
+
let priv = gobj.priv;
|
|
302
|
+
|
|
303
|
+
if(with_text) {
|
|
304
|
+
priv.$input.value = "";
|
|
305
|
+
}
|
|
306
|
+
priv.$error.classList.add('is-hidden');
|
|
307
|
+
priv.$reason.textContent = "";
|
|
308
|
+
gobj_send_event(priv.gobj_json, "EV_SET_JSON", {json: null}, gobj);
|
|
309
|
+
gobj_change_state(gobj, "ST_EMPTY");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
/***************************
|
|
316
|
+
* Actions
|
|
317
|
+
***************************/
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
/***************************************************************
|
|
323
|
+
* A paste: it becomes the text area and the document at once.
|
|
324
|
+
***************************************************************/
|
|
325
|
+
function ac_paste(gobj, event, kw, src)
|
|
326
|
+
{
|
|
327
|
+
let priv = gobj.priv;
|
|
328
|
+
let text = (kw && typeof kw.text === "string")? kw.text : "";
|
|
329
|
+
|
|
330
|
+
priv.$input.value = text;
|
|
331
|
+
return show_text(gobj, text);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/***************************************************************
|
|
335
|
+
* The text area as it is now.
|
|
336
|
+
***************************************************************/
|
|
337
|
+
function ac_view(gobj, event, kw, src)
|
|
338
|
+
{
|
|
339
|
+
let priv = gobj.priv;
|
|
340
|
+
return show_text(gobj, priv.$input.value);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/***************************************************************
|
|
344
|
+
* The blank pad again.
|
|
345
|
+
***************************************************************/
|
|
346
|
+
function ac_clear(gobj, event, kw, src)
|
|
347
|
+
{
|
|
348
|
+
let priv = gobj.priv;
|
|
349
|
+
|
|
350
|
+
clear_pad(gobj, true);
|
|
351
|
+
priv.$input.focus();
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/***************************************************************
|
|
356
|
+
* The viewer asks for a `__collapsed__` subtree. A pasted
|
|
357
|
+
* document has no backend to ask for it: the SOURCE truncated
|
|
358
|
+
* it, and that is the answer.
|
|
359
|
+
***************************************************************/
|
|
360
|
+
function ac_expand_path(gobj, event, kw, src)
|
|
361
|
+
{
|
|
362
|
+
let priv = gobj.priv;
|
|
363
|
+
|
|
364
|
+
gobj_send_event(priv.gobj_json, "EV_SUBTREE_ERROR", {
|
|
365
|
+
path: (kw && kw.path) || "",
|
|
366
|
+
error: t("collapsed in the source")
|
|
367
|
+
}, gobj);
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/***************************************************************
|
|
372
|
+
* FSM
|
|
373
|
+
***************************************************************/
|
|
374
|
+
/*---------------------------------------------*
|
|
375
|
+
* Global methods table
|
|
376
|
+
*---------------------------------------------*/
|
|
377
|
+
const gmt = {
|
|
378
|
+
mt_create: mt_create,
|
|
379
|
+
mt_start: mt_start,
|
|
380
|
+
mt_stop: mt_stop,
|
|
381
|
+
mt_destroy: mt_destroy
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/***************************************************************
|
|
385
|
+
* Create the GClass
|
|
386
|
+
***************************************************************/
|
|
387
|
+
function create_gclass(gclass_name)
|
|
388
|
+
{
|
|
389
|
+
if(__gclass__) {
|
|
390
|
+
log_error(`GClass ALREADY created: ${gclass_name}`);
|
|
391
|
+
return -1;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/*---------------------------------------------*
|
|
395
|
+
* States
|
|
396
|
+
*---------------------------------------------*/
|
|
397
|
+
const states = [
|
|
398
|
+
["ST_EMPTY", [
|
|
399
|
+
["EV_PASTE", ac_paste, null],
|
|
400
|
+
["EV_VIEW", ac_view, null],
|
|
401
|
+
["EV_CLEAR", ac_clear, null],
|
|
402
|
+
]],
|
|
403
|
+
["ST_SHOWING", [
|
|
404
|
+
["EV_PASTE", ac_paste, null],
|
|
405
|
+
["EV_VIEW", ac_view, null],
|
|
406
|
+
["EV_CLEAR", ac_clear, null],
|
|
407
|
+
["EV_EXPAND_PATH", ac_expand_path, null],
|
|
408
|
+
]]
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
/*---------------------------------------------*
|
|
412
|
+
* Events
|
|
413
|
+
*---------------------------------------------*/
|
|
414
|
+
const event_types = [
|
|
415
|
+
["EV_PASTE", 0],
|
|
416
|
+
["EV_VIEW", 0],
|
|
417
|
+
["EV_CLEAR", 0],
|
|
418
|
+
["EV_EXPAND_PATH", 0],
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
__gclass__ = gclass_create(
|
|
422
|
+
gclass_name,
|
|
423
|
+
event_types,
|
|
424
|
+
states,
|
|
425
|
+
gmt,
|
|
426
|
+
0, // lmt,
|
|
427
|
+
attrs_table,
|
|
428
|
+
PRIVATE_DATA,
|
|
429
|
+
0, // authz_table,
|
|
430
|
+
0, // command_table,
|
|
431
|
+
0, // s_user_trace_level
|
|
432
|
+
0 // gclass_flag
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
if(!__gclass__) {
|
|
436
|
+
return -1;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/***************************************************************
|
|
443
|
+
* Register GClass
|
|
444
|
+
***************************************************************/
|
|
445
|
+
function register_c_yui_json_pad()
|
|
446
|
+
{
|
|
447
|
+
/* the viewer it hosts, if the app did not register it */
|
|
448
|
+
if(!gclass_find_by_name("C_YUI_JSON", false)) {
|
|
449
|
+
register_c_yui_json();
|
|
450
|
+
}
|
|
451
|
+
return create_gclass(GCLASS_NAME);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export { register_c_yui_json_pad };
|
|
@@ -1522,6 +1522,20 @@ function create_tabulator(gobj)
|
|
|
1522
1522
|
* had once opened the table. Only a real change is written. */
|
|
1523
1523
|
gobj.priv._page_size = tabulator_settings.paginationSize;
|
|
1524
1524
|
|
|
1525
|
+
/* Normalized records are read by their KEY: a topic table opens
|
|
1526
|
+
* sorted by `id` (the pkey), ascending and natural (`d2` before
|
|
1527
|
+
* `d10`) -- unless the app set its own order, or the table pages
|
|
1528
|
+
* from the backend, where a sort in the browser would reorder one
|
|
1529
|
+
* page and say nothing about the others. */
|
|
1530
|
+
let pkey_col = columns.find((c) => c && c.field === pkey);
|
|
1531
|
+
if(pkey_col && !tabulator_settings.initialSort &&
|
|
1532
|
+
!gobj_read_bool_attr(gobj, "with_remote_paging")) {
|
|
1533
|
+
if(!pkey_col.sorter) {
|
|
1534
|
+
pkey_col.sorter = "alphanum";
|
|
1535
|
+
}
|
|
1536
|
+
tabulator_settings.initialSort = [{column: pkey, dir: "asc"}];
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1525
1539
|
Object.assign(tabulator_settings, {
|
|
1526
1540
|
index: pkey,
|
|
1527
1541
|
columns: columns,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_json_pad.js
|
|
3
|
+
*
|
|
4
|
+
* JSON viewer -- a blank C_YUI_JSON_PAD in a floating window,
|
|
5
|
+
* to paste JSON from outside and read it with the library's own
|
|
6
|
+
* viewer. Peer of the frontend view (yui_frontend_view.js), and
|
|
7
|
+
* opened the same way: an entry of the account menu.
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026, ArtGins.
|
|
10
|
+
* All Rights Reserved.
|
|
11
|
+
***********************************************************************/
|
|
12
|
+
import {
|
|
13
|
+
createElement2,
|
|
14
|
+
gclass_find_by_name,
|
|
15
|
+
gobj_create_service,
|
|
16
|
+
gobj_create_pure_child,
|
|
17
|
+
gobj_find_service,
|
|
18
|
+
gobj_read_attr,
|
|
19
|
+
gobj_start_tree,
|
|
20
|
+
log_error,
|
|
21
|
+
} from "@yuneta/gobj-js";
|
|
22
|
+
|
|
23
|
+
import {register_c_yui_json_pad} from "./c_yui_json_pad.js";
|
|
24
|
+
|
|
25
|
+
/* Service name of the window. The host toggles the entry by looking
|
|
26
|
+
* it up, exactly as it does with "Frontend-View-Window". */
|
|
27
|
+
const WIN_NAME = "Json-Viewer-Window";
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/******************************
|
|
31
|
+
* Public API
|
|
32
|
+
******************************/
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/************************************************************
|
|
36
|
+
* Open a blank JSON pad (C_YUI_JSON_PAD) inside a non-modal
|
|
37
|
+
* C_YUI_WINDOW, the same shape as the frontend view.
|
|
38
|
+
*
|
|
39
|
+
* Self-contained: it registers C_YUI_JSON_PAD (and the
|
|
40
|
+
* C_YUI_JSON it hosts) if the app did not, so an app gets the
|
|
41
|
+
* viewer with one menu entry and one action.
|
|
42
|
+
*
|
|
43
|
+
* Returns the window gobj, or null.
|
|
44
|
+
************************************************************/
|
|
45
|
+
function setup_json_pad(self)
|
|
46
|
+
{
|
|
47
|
+
if(gobj_find_service(WIN_NAME, false)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if(!gclass_find_by_name("C_YUI_JSON_PAD", false)) {
|
|
51
|
+
register_c_yui_json_pad();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* The pad is created AFTER the window, as a pure child of it, so
|
|
55
|
+
* every teardown path (the ✕, or the host destroying the window to
|
|
56
|
+
* toggle the entry off) takes the pad down with it -- which is why
|
|
57
|
+
* the body is a placeholder: C_YUI_WINDOW builds its UI in
|
|
58
|
+
* mt_create, before the pad exists. */
|
|
59
|
+
let $body = createElement2(
|
|
60
|
+
["div", {class: "YJSONPAD_BODY",
|
|
61
|
+
style: "height:100%; display:flex; flex-direction:column;"}, []]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
let win = gobj_create_service(
|
|
65
|
+
WIN_NAME,
|
|
66
|
+
"C_YUI_WINDOW",
|
|
67
|
+
{
|
|
68
|
+
$parent: (typeof document !== "undefined" &&
|
|
69
|
+
document.getElementById("top-layer")) || null,
|
|
70
|
+
subscriber: null,
|
|
71
|
+
modal: false,
|
|
72
|
+
showMax: true,
|
|
73
|
+
showFooter: false,
|
|
74
|
+
resizable: true,
|
|
75
|
+
center: false,
|
|
76
|
+
auto_save_size_and_position: true,
|
|
77
|
+
width: 900,
|
|
78
|
+
height: 640,
|
|
79
|
+
logical_class: "JSON_VIEWER_WINDOW",
|
|
80
|
+
title: "json viewer",
|
|
81
|
+
icon: "yi-code",
|
|
82
|
+
body: $body,
|
|
83
|
+
/* `|| null`: an undefined attr value logs "attr undefined:
|
|
84
|
+
* manager" in an app without a window manager. */
|
|
85
|
+
manager: gobj_find_service("__window_manager__", false) || null,
|
|
86
|
+
},
|
|
87
|
+
self
|
|
88
|
+
);
|
|
89
|
+
if(!win) {
|
|
90
|
+
log_error("yui_json_pad: cannot create the json-viewer window");
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* The window is mounted by its mt_create, so $body is live DOM here:
|
|
95
|
+
* the viewer's graph measures its canvas when it is first shown. */
|
|
96
|
+
let pad = gobj_create_pure_child("json_pad", "C_YUI_JSON_PAD", {}, win);
|
|
97
|
+
let $pad = pad? gobj_read_attr(pad, "$container") : null;
|
|
98
|
+
if(!$pad) {
|
|
99
|
+
log_error("yui_json_pad: C_YUI_JSON_PAD without $container");
|
|
100
|
+
return win;
|
|
101
|
+
}
|
|
102
|
+
$body.appendChild($pad);
|
|
103
|
+
|
|
104
|
+
gobj_start_tree(win);
|
|
105
|
+
|
|
106
|
+
return win;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {setup_json_pad};
|