@yuneta/gobj-ui 3.0.0 → 5.0.0
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 +335 -6
- package/dist/gobj-ui.cjs.js +10930 -5027
- package/dist/gobj-ui.es.js +10948 -5080
- package/index.js +45 -1
- package/package.json +11 -11
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.js +112 -53
- package/src/c_yui_gobj_tree_js.js +58 -36
- package/src/c_yui_json.css +139 -0
- package/src/c_yui_json.js +928 -0
- package/src/c_yui_json_graph.js +110 -20
- package/src/c_yui_map.js +19 -37
- package/src/c_yui_nav.js +6 -9
- package/src/c_yui_period.css +184 -0
- package/src/c_yui_period.js +1441 -0
- package/src/c_yui_shell.css +33 -0
- package/src/c_yui_shell.js +672 -111
- package/src/c_yui_treedb_graph.js +639 -37
- package/src/c_yui_treedb_schema.js +478 -0
- package/src/c_yui_treedb_topic_with_form.css +7 -42
- package/src/c_yui_treedb_topic_with_form.js +152 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.js +180 -97
- package/src/c_yui_window_manager.js +38 -4
- package/src/json_view_helpers.js +214 -0
- package/src/json_view_helpers.test.js +135 -0
- package/src/route_map_model.js +317 -0
- package/src/route_map_model.test.js +209 -0
- package/src/route_resolver.js +24 -1
- package/src/route_resolver.test.js +40 -1
- package/src/shell_modals.js +117 -39
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +67 -0
- package/src/yui_dev.js +130 -8
- package/src/yui_frontend_view.js +106 -0
- package/src/yui_icons.css +62 -0
- package/src/yui_inputs.css +8 -3
- package/src/yui_inputs.js +51 -8
- package/src/yui_tabulator_i18n.js +128 -0
- package/src/yui_theme.js +147 -0
- package/src/yui_time.js +666 -0
- package/src/yui_time.test.js +330 -0
- package/src/yui_toolbar.js +86 -43
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_tabulator_i18n.js
|
|
3
|
+
*
|
|
4
|
+
* The strings TABULATOR renders on its own — the paginator ("Page
|
|
5
|
+
* Size", "First", "Prev", "Next", "Last"), the placeholder ("No Data
|
|
6
|
+
* Available"), the loading/error notices — through the app's i18n.
|
|
7
|
+
*
|
|
8
|
+
* They are not built by any gclass, so nothing here or in an app ever
|
|
9
|
+
* passed them through t(): a table sat in English inside an otherwise
|
|
10
|
+
* Spanish view, and a language switch did not touch it.
|
|
11
|
+
*
|
|
12
|
+
* Two calls:
|
|
13
|
+
*
|
|
14
|
+
* new Tabulator($el, {..., ...yui_tabulator_lang(t)});
|
|
15
|
+
* yui_tabulator_relocalize(table, t); // on a language change
|
|
16
|
+
*
|
|
17
|
+
* Every key carries an English `defaultValue`, so an app that does not
|
|
18
|
+
* define it renders exactly what Tabulator rendered before — the
|
|
19
|
+
* keys are an OPPORTUNITY to translate, never a requirement.
|
|
20
|
+
*
|
|
21
|
+
* Copyright (c) 2026, ArtGins.
|
|
22
|
+
* All Rights Reserved.
|
|
23
|
+
***********************************************************************/
|
|
24
|
+
import {log_warning} from "@yuneta/gobj-js";
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/* Tabulator re-renders the parts it owns only when the locale NAME changes:
|
|
28
|
+
* setLocale() with the name already in force is a no-op, so re-registering
|
|
29
|
+
* fresh strings under "default" left the paginator in the old language. Each
|
|
30
|
+
* application of a language therefore gets its own name. */
|
|
31
|
+
let __lang_seq__ = 0;
|
|
32
|
+
|
|
33
|
+
function next_lang_name()
|
|
34
|
+
{
|
|
35
|
+
return `yui-${++__lang_seq__}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/***************************************************************
|
|
39
|
+
* Tabulator's own strings, in the CURRENT language.
|
|
40
|
+
*
|
|
41
|
+
* ONE lang dict (not one per locale): the app owns the locales and hands us
|
|
42
|
+
* only its live `t`, so a language change rebuilds the dict and re-applies
|
|
43
|
+
* it under a fresh name (yui_tabulator_relocalize).
|
|
44
|
+
***************************************************************/
|
|
45
|
+
function yui_tabulator_lang(t)
|
|
46
|
+
{
|
|
47
|
+
let name = next_lang_name();
|
|
48
|
+
let langs = {};
|
|
49
|
+
langs[name] = tabulator_strings(t);
|
|
50
|
+
return {
|
|
51
|
+
locale: name,
|
|
52
|
+
langs: langs
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function tabulator_strings(t)
|
|
57
|
+
{
|
|
58
|
+
let tr = (key, def) => t(key, {defaultValue: def});
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
data: {
|
|
62
|
+
loading: tr("loading", "Loading"),
|
|
63
|
+
error: tr("error", "Error")
|
|
64
|
+
},
|
|
65
|
+
pagination: {
|
|
66
|
+
page_size: tr("page size", "Page Size"),
|
|
67
|
+
page_title: tr("show page", "Show Page"),
|
|
68
|
+
first: tr("first", "First"),
|
|
69
|
+
first_title: tr("first page", "First Page"),
|
|
70
|
+
last: tr("last", "Last"),
|
|
71
|
+
last_title: tr("last page", "Last Page"),
|
|
72
|
+
prev: tr("prev", "Prev"),
|
|
73
|
+
prev_title: tr("prev page", "Prev Page"),
|
|
74
|
+
next: tr("next", "Next"),
|
|
75
|
+
next_title: tr("next page", "Next Page"),
|
|
76
|
+
all: tr("all", "All")
|
|
77
|
+
},
|
|
78
|
+
headerFilters: {
|
|
79
|
+
default: tr("filter column", "filter column...")
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/***************************************************************
|
|
85
|
+
* Put an EXISTING table in the current language: rebuild the strings and
|
|
86
|
+
* re-apply the locale, which is what makes Tabulator re-render the parts
|
|
87
|
+
* it owns (the paginator above all — it is drawn once, at build).
|
|
88
|
+
*
|
|
89
|
+
* Silent no-op on a table that is gone: a language switch races nothing,
|
|
90
|
+
* but a view torn down mid-switch must not log a failure it cannot act on.
|
|
91
|
+
***************************************************************/
|
|
92
|
+
function yui_tabulator_relocalize(table, t)
|
|
93
|
+
{
|
|
94
|
+
if(!table) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
let name = next_lang_name();
|
|
99
|
+
let strings = tabulator_strings(t);
|
|
100
|
+
|
|
101
|
+
/* Tabulator DEEP-CLONES options.langs into its localize module when
|
|
102
|
+
* the table is built, and never looks at the option again: writing a
|
|
103
|
+
* new language there and calling setLocale() only earned a
|
|
104
|
+
* "Matching locale not found, using default: yui-5" — and the
|
|
105
|
+
* paginator it was meant to translate stayed in the old language.
|
|
106
|
+
* Install it where the module actually reads it. The option is still
|
|
107
|
+
* written, so a table rebuilt from its options keeps the language. */
|
|
108
|
+
table.options.langs = table.options.langs || {};
|
|
109
|
+
table.options.langs[name] = strings;
|
|
110
|
+
|
|
111
|
+
let localize = table.modules && table.modules.localize;
|
|
112
|
+
if(localize && typeof localize.installLang === "function") {
|
|
113
|
+
localize.installLang(name, strings);
|
|
114
|
+
} else {
|
|
115
|
+
log_warning(`yui_tabulator_relocalize: no localize module: ` +
|
|
116
|
+
`Tabulator's own chrome stays in the old language`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
table.setLocale(name); /* a NEW name: this is what re-renders */
|
|
120
|
+
} catch(e) {
|
|
121
|
+
log_warning(`yui_tabulator_relocalize: table gone: ${e}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
yui_tabulator_lang,
|
|
127
|
+
yui_tabulator_relocalize,
|
|
128
|
+
};
|
package/src/yui_theme.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_theme.js
|
|
3
|
+
*
|
|
4
|
+
* The app's light/dark theme, and how a gclass follows it.
|
|
5
|
+
*
|
|
6
|
+
* The theme lives in ONE place: the `data-theme` attribute of
|
|
7
|
+
* <html> (the Bulma convention, set by the shell's theme toggle).
|
|
8
|
+
* Reading it is the only way to know the current theme; there is
|
|
9
|
+
* no service to ask.
|
|
10
|
+
*
|
|
11
|
+
* Components used to ask a legacy C_YUI_MAIN "__yui_main__"
|
|
12
|
+
* service instead — read its `theme` attr, subscribe to its
|
|
13
|
+
* EV_THEME. That path was retired: nothing ever WROTE that attr,
|
|
14
|
+
* so it answered "light" for the life of the app, and no shell
|
|
15
|
+
* published EV_THEME. C_YUI_SHELL has no such service at all.
|
|
16
|
+
*
|
|
17
|
+
* A theme change is a DOM mutation — an OS notification — so it
|
|
18
|
+
* enters the machine as an event: yui_watch_theme() translates
|
|
19
|
+
* the mutation into EV_THEME and the gclass's action does the
|
|
20
|
+
* restyling. Nothing happens in the observer callback itself.
|
|
21
|
+
*
|
|
22
|
+
* Copyright (c) 2026, ArtGins.
|
|
23
|
+
* All Rights Reserved.
|
|
24
|
+
***********************************************************************/
|
|
25
|
+
import {
|
|
26
|
+
gobj_send_event,
|
|
27
|
+
gobj_is_destroying,
|
|
28
|
+
} from "@yuneta/gobj-js";
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/************************************************************
|
|
32
|
+
* The active theme: "dark" | "light".
|
|
33
|
+
*
|
|
34
|
+
* <html data-theme> when the app sets it, else the OS
|
|
35
|
+
* preference, else light.
|
|
36
|
+
************************************************************/
|
|
37
|
+
function yui_theme_now()
|
|
38
|
+
{
|
|
39
|
+
if(typeof document === "undefined") {
|
|
40
|
+
return "light";
|
|
41
|
+
}
|
|
42
|
+
let attr = document.documentElement.getAttribute("data-theme");
|
|
43
|
+
if(attr === "dark" || attr === "light") {
|
|
44
|
+
return attr;
|
|
45
|
+
}
|
|
46
|
+
if(typeof window !== "undefined" && window.matchMedia) {
|
|
47
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
48
|
+
? "dark" : "light";
|
|
49
|
+
}
|
|
50
|
+
return "light";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/************************************************************
|
|
54
|
+
* True when the app renders dark.
|
|
55
|
+
************************************************************/
|
|
56
|
+
function yui_is_dark()
|
|
57
|
+
{
|
|
58
|
+
return yui_theme_now() === "dark";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/************************************************************
|
|
62
|
+
* Follow the theme: watch <html data-theme> and send `event`
|
|
63
|
+
* (default EV_THEME, kw {theme}) to `gobj` on every change,
|
|
64
|
+
* so the gclass restyles in its ACTION — the observer only
|
|
65
|
+
* translates the notification.
|
|
66
|
+
*
|
|
67
|
+
* The gclass must declare `event` in its FSM, and must
|
|
68
|
+
* disconnect the returned observer in mt_destroy:
|
|
69
|
+
*
|
|
70
|
+
* priv.theme_observer = yui_watch_theme(gobj);
|
|
71
|
+
* ...
|
|
72
|
+
* if(priv.theme_observer) {
|
|
73
|
+
* priv.theme_observer.disconnect();
|
|
74
|
+
* priv.theme_observer = null;
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* Watches BOTH sources: <html data-theme> (a MutationObserver)
|
|
78
|
+
* and the OS preference (matchMedia prefers-color-scheme, which
|
|
79
|
+
* is the live source while the attribute is absent — the
|
|
80
|
+
* "system" theme).
|
|
81
|
+
*
|
|
82
|
+
* Returns a handle with disconnect(), or null where there is
|
|
83
|
+
* no DOM.
|
|
84
|
+
************************************************************/
|
|
85
|
+
function yui_watch_theme(gobj, event = "EV_THEME")
|
|
86
|
+
{
|
|
87
|
+
if(typeof MutationObserver === "undefined" || typeof document === "undefined") {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let $html = document.documentElement;
|
|
92
|
+
let last = yui_theme_now();
|
|
93
|
+
|
|
94
|
+
let notify = function() {
|
|
95
|
+
let theme = yui_theme_now();
|
|
96
|
+
/* data-theme can be rewritten with the same value (a re-render,
|
|
97
|
+
* another observer); only a real change is an event. */
|
|
98
|
+
if(theme === last) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
last = theme;
|
|
102
|
+
if(gobj_is_destroying(gobj)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
gobj_send_event(gobj, event, {theme: theme}, gobj);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let mo = new MutationObserver(notify);
|
|
109
|
+
mo.observe($html, {attributes: true, attributeFilter: ["data-theme"]});
|
|
110
|
+
|
|
111
|
+
/* The "system" theme: with data-theme ABSENT, yui_theme_now()
|
|
112
|
+
* answers from the OS preference (and Bulma follows it via
|
|
113
|
+
* prefers-color-scheme), so an OS auto-switch (sunset) IS a theme
|
|
114
|
+
* change — without this listener the CSS would flip while every
|
|
115
|
+
* canvas kept its old palette, the exact bug class the attribute
|
|
116
|
+
* observer kills. notify() dedupes, so when the app pins
|
|
117
|
+
* data-theme this listener never fires an event (the attribute
|
|
118
|
+
* wins inside yui_theme_now). */
|
|
119
|
+
let mql = null;
|
|
120
|
+
if(typeof window !== "undefined" && window.matchMedia) {
|
|
121
|
+
mql = window.matchMedia("(prefers-color-scheme: dark)");
|
|
122
|
+
if(typeof mql.addEventListener === "function") {
|
|
123
|
+
mql.addEventListener("change", notify);
|
|
124
|
+
} else {
|
|
125
|
+
mql = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* One handle, one disconnect() — same contract as a bare
|
|
130
|
+
* MutationObserver, covering both sources. */
|
|
131
|
+
return {
|
|
132
|
+
disconnect: function() {
|
|
133
|
+
mo.disconnect();
|
|
134
|
+
if(mql) {
|
|
135
|
+
mql.removeEventListener("change", notify);
|
|
136
|
+
mql = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
yui_theme_now,
|
|
145
|
+
yui_is_dark,
|
|
146
|
+
yui_watch_theme,
|
|
147
|
+
};
|