@yuneta/gobj-ui 2.6.0 → 4.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 +331 -11
- package/dist/gobj-ui.cjs.js +21386 -17919
- package/dist/gobj-ui.es.js +26059 -22613
- package/index.js +45 -9
- package/package.json +4 -4
- package/src/c_g6_nodes_tree.js +128 -40
- package/src/c_yui_form.css +9 -0
- package/src/c_yui_form.js +113 -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 +18 -36
- 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 +120 -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 +18 -0
- package/src/c_yui_treedb_topic_with_form.js +153 -103
- package/src/c_yui_treedb_topics.css +73 -0
- package/src/c_yui_treedb_topics.js +1070 -29
- package/src/c_yui_window.css +22 -0
- package/src/c_yui_window.js +182 -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 +162 -52
- package/src/shell_route_map.css +225 -0
- package/src/shell_route_map.js +363 -0
- package/src/tabulator.css +245 -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.css +28 -0
- package/src/yui_toolbar.js +86 -43
- package/src/c_yui_main.css +0 -501
- package/src/c_yui_main.js +0 -1623
- package/src/c_yui_routing.css +0 -18
- package/src/c_yui_routing.js +0 -837
- package/src/c_yui_tabs.js +0 -487
- package/src/themes.js +0 -215
- package/src/ytable.css +0 -63
- package/src/ytable.js +0 -505
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* shell_route_map.js
|
|
3
|
+
*
|
|
4
|
+
* "Site map" viewer for a C_YUI_SHELL app: renders the WHOLE
|
|
5
|
+
* navigation surface — the toolbar (including the account menu),
|
|
6
|
+
* the primary menu, its live dynamic tabs, and each view's
|
|
7
|
+
* declared sub-routes (topics / info / schema / focus topics) —
|
|
8
|
+
* as a printable, clickable tree, in DECLARATION order (see
|
|
9
|
+
* ROUTING.md). Meant to double as the app's basic documentation.
|
|
10
|
+
* Hosted in a resizable C_YUI_WINDOW (a modal is the fallback when
|
|
11
|
+
* C_YUI_WINDOW is not registered).
|
|
12
|
+
*
|
|
13
|
+
* Usage (e.g. from an "Account → Site map" menu action):
|
|
14
|
+
* import {yui_shell_show_route_map} from
|
|
15
|
+
* "@yuneta/gobj-ui/src/shell_route_map.js";
|
|
16
|
+
* yui_shell_show_route_map(shell, {t});
|
|
17
|
+
*
|
|
18
|
+
* Copyright (c) 2026, ArtGins.
|
|
19
|
+
* All Rights Reserved.
|
|
20
|
+
***********************************************************************/
|
|
21
|
+
import "./shell_route_map.css";
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
createElement2,
|
|
25
|
+
refresh_language,
|
|
26
|
+
gobj_create_service,
|
|
27
|
+
gobj_find_service,
|
|
28
|
+
gobj_start,
|
|
29
|
+
gobj_send_event,
|
|
30
|
+
is_gobj,
|
|
31
|
+
gclass_find_by_name,
|
|
32
|
+
log_error,
|
|
33
|
+
} from "@yuneta/gobj-js";
|
|
34
|
+
|
|
35
|
+
import {yui_shell_show_modal, yui_shell_popup_layer} from "./shell_modals.js";
|
|
36
|
+
import {yui_shell_nav_map} from "./c_yui_shell.js";
|
|
37
|
+
import {attach_clear} from "./yui_inputs.js";
|
|
38
|
+
|
|
39
|
+
import i18next from "i18next";
|
|
40
|
+
|
|
41
|
+
const WIN_NAME = "shell-route-map-window";
|
|
42
|
+
|
|
43
|
+
/* Modal fallback currently open (the window flavour is a service and
|
|
44
|
+
* is found by name; the modal is not, so it is tracked here to make
|
|
45
|
+
* the second call a TOGGLE for both flavours). */
|
|
46
|
+
let __open_modal__ = null;
|
|
47
|
+
|
|
48
|
+
/* Bring the "you are here" row into view once the map is mounted. */
|
|
49
|
+
function scroll_to_current($body)
|
|
50
|
+
{
|
|
51
|
+
let $cur = $body.querySelector(".ROUTEMAP_CURRENT");
|
|
52
|
+
if($cur && typeof $cur.scrollIntoView === "function") {
|
|
53
|
+
$cur.scrollIntoView({block: "center"});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/***************************************************************
|
|
59
|
+
* Render one nav node as an <li> (with a nested <ul> for children),
|
|
60
|
+
* preserving the given order. A node with a `route` is a live hash
|
|
61
|
+
* link; a node with an `event` is an action (shown, not fired); a
|
|
62
|
+
* structural/group node is plain text.
|
|
63
|
+
***************************************************************/
|
|
64
|
+
function render_node(node)
|
|
65
|
+
{
|
|
66
|
+
let row = [];
|
|
67
|
+
if(node.icon && /^yi-[a-z0-9-]+$/.test(node.icon)) {
|
|
68
|
+
row.push(["span", {class: "icon ROUTEMAP_ICON"},
|
|
69
|
+
[["i", {class: node.icon}]]]);
|
|
70
|
+
}
|
|
71
|
+
row.push(["span", {class: "ROUTEMAP_NAME", i18n: node.label}, node.label]);
|
|
72
|
+
/* Documentation columns: route + implementing gclass + action event. */
|
|
73
|
+
if(node.route) {
|
|
74
|
+
row.push(["code", {class: "ROUTEMAP_ROUTE"}, node.route]);
|
|
75
|
+
}
|
|
76
|
+
if(node.gclass) {
|
|
77
|
+
row.push(["span", {class: "ROUTEMAP_GCLASS",
|
|
78
|
+
title: "gclass", "data-i18n-title": "gclass"},
|
|
79
|
+
node.gclass]);
|
|
80
|
+
}
|
|
81
|
+
if(node.event) {
|
|
82
|
+
row.push(["span", {class: "ROUTEMAP_EVENT"}, node.event]);
|
|
83
|
+
}
|
|
84
|
+
/* "You are here" — the node whose route best matches the current
|
|
85
|
+
* one (marked by the model; at most one). */
|
|
86
|
+
if(node.current) {
|
|
87
|
+
row.push(["span", {class: "ROUTEMAP_HERE", i18n: "you are here"},
|
|
88
|
+
"you are here"]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let row_class = node.current ? " ROUTEMAP_CURRENT" : "";
|
|
92
|
+
let $row;
|
|
93
|
+
if(node.route) {
|
|
94
|
+
$row = createElement2(
|
|
95
|
+
["a", {class: "ROUTEMAP_LINK ROUTEMAP_ROW" + row_class,
|
|
96
|
+
href: "#" + node.route,
|
|
97
|
+
title: node.route}, row]);
|
|
98
|
+
} else {
|
|
99
|
+
$row = createElement2(
|
|
100
|
+
["span", {class: "ROUTEMAP_ROW ROUTEMAP_STRUCT" + row_class}, row]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let kids = (node.children && node.children.length)
|
|
104
|
+
? [createElement2(["ul", {class: "ROUTEMAP_UL"},
|
|
105
|
+
node.children.map(render_node)])]
|
|
106
|
+
: [];
|
|
107
|
+
return createElement2(["li", {class: "ROUTEMAP_LI"}, [$row].concat(kids)]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/***************************************************************
|
|
111
|
+
* Filter a rendered <li>: visible when its own row matches `q`, when
|
|
112
|
+
* an ancestor matched (show the whole subtree of a match), or when a
|
|
113
|
+
* descendant matches (keep the ancestor path). Empty `q` shows all.
|
|
114
|
+
* Matches against the visible row text (name + route + event), so it
|
|
115
|
+
* honours the current translation. Returns whether the <li> is shown.
|
|
116
|
+
***************************************************************/
|
|
117
|
+
function filter_li($li, q, ancestor_match)
|
|
118
|
+
{
|
|
119
|
+
let $row = $li.querySelector(":scope > .ROUTEMAP_ROW");
|
|
120
|
+
let text = $row ? ($row.textContent || "").toLowerCase() : "";
|
|
121
|
+
let self_match = q === "" || text.indexOf(q) >= 0;
|
|
122
|
+
let show_all = ancestor_match || self_match;
|
|
123
|
+
|
|
124
|
+
let any_child = false;
|
|
125
|
+
let $ul = $li.querySelector(":scope > ul");
|
|
126
|
+
if($ul) {
|
|
127
|
+
let kids = $ul.children;
|
|
128
|
+
for(let i = 0; i < kids.length; i++) {
|
|
129
|
+
if(filter_li(kids[i], q, show_all)) {
|
|
130
|
+
any_child = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let visible = show_all || any_child;
|
|
136
|
+
$li.style.display = visible ? "" : "none";
|
|
137
|
+
if($row) {
|
|
138
|
+
$row.classList.toggle("ROUTEMAP_MATCH", q !== "" && self_match);
|
|
139
|
+
}
|
|
140
|
+
return visible;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/***************************************************************
|
|
144
|
+
* Build the site-map body (tree + hint + print), and wire the
|
|
145
|
+
* print button and the link-jump behaviour. Links navigate
|
|
146
|
+
* natively; the shell's transient-overlay drain closes the host
|
|
147
|
+
* on a resting-route change. `on_jump()` closes the host for the
|
|
148
|
+
* one click that navigates nowhere (the current route).
|
|
149
|
+
***************************************************************/
|
|
150
|
+
function build_body(shell, t, on_jump)
|
|
151
|
+
{
|
|
152
|
+
let map = yui_shell_nav_map(shell);
|
|
153
|
+
let children = [
|
|
154
|
+
{label: "toolbar", icon: "", route: "", event: "",
|
|
155
|
+
kind: "group", children: map.toolbar}
|
|
156
|
+
].concat(map.nav);
|
|
157
|
+
/* Routes declared only in the route table (config.shell.routes)
|
|
158
|
+
* that no menu/toolbar item points at — root "/", URL-only action
|
|
159
|
+
* routes. Rendered last, as their own group. */
|
|
160
|
+
if(Array.isArray(map.other) && map.other.length) {
|
|
161
|
+
children.push({label: "other routes", icon: "", route: "", event: "",
|
|
162
|
+
kind: "group", children: map.other});
|
|
163
|
+
}
|
|
164
|
+
let root = {
|
|
165
|
+
label: map.brand.label || "app",
|
|
166
|
+
icon: "",
|
|
167
|
+
route: map.brand.route || "",
|
|
168
|
+
event: "",
|
|
169
|
+
current: !!map.brand.current,
|
|
170
|
+
children: children
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
let $tree = createElement2(
|
|
174
|
+
["div", {class: "ROUTEMAP_TREE"}, [
|
|
175
|
+
createElement2(["ul", {class: "ROUTEMAP_UL ROUTEMAP_ROOT"},
|
|
176
|
+
[render_node(root)]])
|
|
177
|
+
]]
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
/* Search filter: matching nodes plus their ancestor path (and the
|
|
181
|
+
* matched node's whole subtree) stay visible; the rest collapse. */
|
|
182
|
+
let $search = createElement2(
|
|
183
|
+
["input", {class: "input is-small ROUTEMAP_SEARCH", type: "text",
|
|
184
|
+
placeholder: t("filter", {defaultValue: "Filter…"}),
|
|
185
|
+
"data-i18n-placeholder": "filter",
|
|
186
|
+
"aria-label": t("filter", {defaultValue: "Filter"}),
|
|
187
|
+
"data-i18n-aria-label": "filter"}]
|
|
188
|
+
);
|
|
189
|
+
let $search_ctrl = createElement2(
|
|
190
|
+
["div", {class: "control ROUTEMAP_SEARCH_CTRL"}, [$search]]
|
|
191
|
+
);
|
|
192
|
+
attach_clear($search_ctrl, $search);
|
|
193
|
+
|
|
194
|
+
/* Live match counter next to the filter. */
|
|
195
|
+
let $count_n = createElement2(["span", {class: "ROUTEMAP_COUNT_N"}, ""]);
|
|
196
|
+
let $count = createElement2(
|
|
197
|
+
["span", {class: "ROUTEMAP_COUNT is-size-7 has-text-grey is-hidden"},
|
|
198
|
+
[$count_n, ["span", {i18n: "matches"}, "matches"]]]
|
|
199
|
+
);
|
|
200
|
+
let $search_row = createElement2(
|
|
201
|
+
["div", {class: "ROUTEMAP_SEARCH_ROW mb-2"}, [$search_ctrl, $count]]
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
$search.addEventListener("input", function() {
|
|
205
|
+
let q = ($search.value || "").trim().toLowerCase();
|
|
206
|
+
let $root_li = $tree.querySelector(".ROUTEMAP_ROOT > li");
|
|
207
|
+
if($root_li) {
|
|
208
|
+
filter_li($root_li, q, false);
|
|
209
|
+
}
|
|
210
|
+
if(q === "") {
|
|
211
|
+
$count.classList.add("is-hidden");
|
|
212
|
+
} else {
|
|
213
|
+
$count_n.textContent = $tree.querySelectorAll(".ROUTEMAP_MATCH").length;
|
|
214
|
+
$count.classList.remove("is-hidden");
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
let $body = createElement2(
|
|
219
|
+
["div", {class: "C_YUI_SHELL_ROUTEMAP ROUTEMAP_BODY"}, [
|
|
220
|
+
["p", {class: "ROUTEMAP_HINT is-size-7 mb-2", i18n: "site map hint"},
|
|
221
|
+
t("site map hint", {defaultValue:
|
|
222
|
+
"Every reachable position of the app is a URL. Click to jump."})],
|
|
223
|
+
$search_row,
|
|
224
|
+
$tree,
|
|
225
|
+
["div", {class: "ROUTEMAP_ACTIONS"}, [
|
|
226
|
+
["button", {class: "button is-small ROUTEMAP_PRINT",
|
|
227
|
+
i18n: "print"}, t("print", {defaultValue: "Print"})]
|
|
228
|
+
]]
|
|
229
|
+
]]
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
/* Print only the tree: clone it into an off-screen print area so
|
|
233
|
+
* the @media print rules can hide everything else, regardless of
|
|
234
|
+
* where the window/modal is mounted. */
|
|
235
|
+
let $print = $body.querySelector(".ROUTEMAP_PRINT");
|
|
236
|
+
if($print) {
|
|
237
|
+
$print.addEventListener("click", function() {
|
|
238
|
+
let $area = document.createElement("div");
|
|
239
|
+
$area.className = "routemap-print-area";
|
|
240
|
+
$area.appendChild($tree.cloneNode(true));
|
|
241
|
+
document.body.appendChild($area);
|
|
242
|
+
document.body.classList.add("routemap-printing");
|
|
243
|
+
try {
|
|
244
|
+
window.print();
|
|
245
|
+
} finally {
|
|
246
|
+
document.body.classList.remove("routemap-printing");
|
|
247
|
+
if($area.parentNode) {
|
|
248
|
+
$area.parentNode.removeChild($area);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* A route link jumps there NATIVELY (browser push + hashchange —
|
|
255
|
+
* no preventDefault): a resting-route change then closes this
|
|
256
|
+
* window/modal through the shell's transient-overlay drain, which
|
|
257
|
+
* is deterministic (the old close-then-deferred-navigate raced the
|
|
258
|
+
* dismissal's history.back() and could land back where it
|
|
259
|
+
* started). A subpath or action-route jump keeps the map open —
|
|
260
|
+
* it doubles as a navigation panel. Clicking the route the user
|
|
261
|
+
* is ALREADY on navigates nowhere: close the host instead.
|
|
262
|
+
* Action nodes (no route) are documentation only and do not fire. */
|
|
263
|
+
$body.addEventListener("click", function(ev) {
|
|
264
|
+
let $link = ev.target && ev.target.closest &&
|
|
265
|
+
ev.target.closest(".ROUTEMAP_LINK");
|
|
266
|
+
if(!$link) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
let href = $link.getAttribute("href");
|
|
270
|
+
if(href && typeof window !== "undefined" &&
|
|
271
|
+
window.location.hash === href &&
|
|
272
|
+
typeof on_jump === "function") {
|
|
273
|
+
ev.preventDefault();
|
|
274
|
+
on_jump();
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
refresh_language($body, t);
|
|
279
|
+
return $body;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
/***************************************************************
|
|
284
|
+
* Show the site map. Toggles: a second call closes the open one.
|
|
285
|
+
***************************************************************/
|
|
286
|
+
export function yui_shell_show_route_map(shell, opts)
|
|
287
|
+
{
|
|
288
|
+
let t = (opts && opts.t) || i18next.t.bind(i18next);
|
|
289
|
+
|
|
290
|
+
/* Toggle: an open site-map window/modal → close it. */
|
|
291
|
+
let existing = gobj_find_service(WIN_NAME, false);
|
|
292
|
+
if(existing && is_gobj(existing)) {
|
|
293
|
+
gobj_send_event(existing, "EV_CLOSE_WINDOW", {}, shell);
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
if(__open_modal__) {
|
|
297
|
+
let m = __open_modal__;
|
|
298
|
+
__open_modal__ = null;
|
|
299
|
+
if(typeof m.close === "function") {
|
|
300
|
+
m.close();
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Preferred: a resizable, maximisable floating window. */
|
|
306
|
+
if(gclass_find_by_name("C_YUI_WINDOW") !== null) {
|
|
307
|
+
let win_ref = {gobj: null};
|
|
308
|
+
let $body = build_body(shell, t, function() {
|
|
309
|
+
if(win_ref.gobj && is_gobj(win_ref.gobj)) {
|
|
310
|
+
gobj_send_event(win_ref.gobj, "EV_CLOSE_WINDOW", {}, shell);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
let $parent = yui_shell_popup_layer(shell) ||
|
|
314
|
+
(typeof document !== "undefined" &&
|
|
315
|
+
document.getElementById("top-layer")) || null;
|
|
316
|
+
let win = gobj_create_service(WIN_NAME, "C_YUI_WINDOW", {
|
|
317
|
+
$parent: $parent,
|
|
318
|
+
subscriber: null,
|
|
319
|
+
modal: false,
|
|
320
|
+
showMax: true,
|
|
321
|
+
showFooter: false,
|
|
322
|
+
resizable: true,
|
|
323
|
+
center: true,
|
|
324
|
+
auto_save_size_and_position: true,
|
|
325
|
+
width: 780,
|
|
326
|
+
height: 640,
|
|
327
|
+
logical_class: "ROUTEMAP_WINDOW",
|
|
328
|
+
title: "site map",
|
|
329
|
+
icon: "yi-bars",
|
|
330
|
+
body: $body,
|
|
331
|
+
manager: null
|
|
332
|
+
}, shell);
|
|
333
|
+
if(!win) {
|
|
334
|
+
log_error("C_YUI_SHELL: cannot create the site-map window");
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
win_ref.gobj = win;
|
|
338
|
+
gobj_start(win);
|
|
339
|
+
scroll_to_current($body);
|
|
340
|
+
return win;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Fallback: a modal (no C_YUI_WINDOW registered). */
|
|
344
|
+
let modal_ref = {modal: null};
|
|
345
|
+
let $body = build_body(shell, t, function() {
|
|
346
|
+
if(modal_ref.modal && typeof modal_ref.modal.close === "function") {
|
|
347
|
+
modal_ref.modal.close();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
modal_ref.modal = yui_shell_show_modal(shell, $body, {
|
|
351
|
+
dialog: true,
|
|
352
|
+
logical_class: "ROUTEMAP_SHEET",
|
|
353
|
+
/* The KEY, not t(key): the modal renders `title` with
|
|
354
|
+
* data-i18n, so a pre-translated string would become the
|
|
355
|
+
* "key" and never re-translate. */
|
|
356
|
+
title: "site map",
|
|
357
|
+
t: t,
|
|
358
|
+
on_close: function() { __open_modal__ = null; }
|
|
359
|
+
});
|
|
360
|
+
__open_modal__ = modal_ref.modal;
|
|
361
|
+
scroll_to_current($body);
|
|
362
|
+
return modal_ref.modal;
|
|
363
|
+
}
|
package/src/tabulator.css
CHANGED
|
@@ -51,3 +51,248 @@
|
|
|
51
51
|
[data-theme=dark] .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after {
|
|
52
52
|
background: #ffffff !important;
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
/* Moved from c_yui_main.css (2.6.1): generic Tabulator theming
|
|
56
|
+
* (column separator, frozen columns, striping/hover, dark + system themes).
|
|
57
|
+
* Self-contained here so every consumer gets it without the
|
|
58
|
+
* legacy stack's stylesheet. */
|
|
59
|
+
|
|
60
|
+
/*********************************************************
|
|
61
|
+
* Tabulator
|
|
62
|
+
*********************************************************/
|
|
63
|
+
|
|
64
|
+
/* Visible column separator via pseudo-element on the right-side resize handle only.
|
|
65
|
+
.prev handles (left-side) are excluded so hidden columns (e.g. Select Row when
|
|
66
|
+
not in edit mode) don't leave a spurious line at the start of the table. */
|
|
67
|
+
.tabulator .tabulator-col-resize-handle:not(.prev)::after {
|
|
68
|
+
content: "";
|
|
69
|
+
position: absolute;
|
|
70
|
+
top: 0;
|
|
71
|
+
bottom: 0;
|
|
72
|
+
left: 50%;
|
|
73
|
+
width: 1px;
|
|
74
|
+
background-color: var(--bulma-border);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.tabulator-row .tabulator-cell.tabulator-frozen {
|
|
78
|
+
background-color: var(--bulma-background) !important;
|
|
79
|
+
}
|
|
80
|
+
.tabulator .tabulator-header .tabulator-frozen {
|
|
81
|
+
background-color: var(--bulma-background) !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Tabulator light theme — alternating row backgrounds
|
|
86
|
+
* scheme-main-ter (#f5f5f5) gives visible contrast against white odd rows.
|
|
87
|
+
* !important needed to override the tabulator_bulma.css theme.
|
|
88
|
+
*/
|
|
89
|
+
.tabulator .tabulator-row,
|
|
90
|
+
.tabulator .tabulator-row.tabulator-row-odd {
|
|
91
|
+
background-color: var(--bulma-scheme-main, #fff) !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.tabulator .tabulator-row.tabulator-row-even {
|
|
95
|
+
background-color: var(--bulma-scheme-main-ter, #f5f5f5) !important;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.tabulator .tabulator-row.tabulator-selectable:hover {
|
|
99
|
+
background-color: var(--bulma-background, #ebebeb) !important;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ====================================================================
|
|
103
|
+
* Opt-out of the whole-row hover wash + pointer cursor. For tables that
|
|
104
|
+
* select ONLY through the checkbox column (selectableRows:"highlight"),
|
|
105
|
+
* the hover highlight reads as a selection. Add `yui-no-row-hover` on the
|
|
106
|
+
* .tabulator element: unselected rows stay visually static on hover; a
|
|
107
|
+
* checkbox-selected row keeps its `.tabulator-selected` colour (the
|
|
108
|
+
* :not() guard leaves it alone). Colours mirror the striping rules so
|
|
109
|
+
* hover === the row's own background in every theme.
|
|
110
|
+
* ==================================================================== */
|
|
111
|
+
.tabulator.yui-no-row-hover .tabulator-row.tabulator-selectable:hover {
|
|
112
|
+
cursor: default !important;
|
|
113
|
+
}
|
|
114
|
+
.tabulator.yui-no-row-hover
|
|
115
|
+
.tabulator-row.tabulator-row-odd.tabulator-selectable:not(.tabulator-selected):hover {
|
|
116
|
+
background-color: var(--bulma-scheme-main, #fff) !important;
|
|
117
|
+
color: var(--bulma-body-color) !important;
|
|
118
|
+
}
|
|
119
|
+
.tabulator.yui-no-row-hover
|
|
120
|
+
.tabulator-row.tabulator-row-even.tabulator-selectable:not(.tabulator-selected):hover {
|
|
121
|
+
background-color: var(--bulma-scheme-main-ter, #f5f5f5) !important;
|
|
122
|
+
color: var(--bulma-body-color) !important;
|
|
123
|
+
}
|
|
124
|
+
[data-theme=dark] .tabulator.yui-no-row-hover
|
|
125
|
+
.tabulator-row.tabulator-row-odd.tabulator-selectable:not(.tabulator-selected):hover {
|
|
126
|
+
background-color: var(--bulma-body-background-color) !important;
|
|
127
|
+
color: var(--bulma-body-color) !important;
|
|
128
|
+
}
|
|
129
|
+
[data-theme=dark] .tabulator.yui-no-row-hover
|
|
130
|
+
.tabulator-row.tabulator-row-even.tabulator-selectable:not(.tabulator-selected):hover {
|
|
131
|
+
background-color: var(--bulma-background) !important;
|
|
132
|
+
color: var(--bulma-body-color) !important;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/*
|
|
136
|
+
* Tabulator dark theme
|
|
137
|
+
* Activated by html[data-theme=dark] (set by the app's theme switcher)
|
|
138
|
+
*/
|
|
139
|
+
[data-theme=dark] .tabulator {
|
|
140
|
+
background-color: var(--bulma-body-background-color);
|
|
141
|
+
border-color: var(--bulma-border);
|
|
142
|
+
color: var(--bulma-body-color);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
[data-theme=dark] .tabulator .tabulator-header,
|
|
146
|
+
[data-theme=dark] .tabulator .tabulator-header .tabulator-col {
|
|
147
|
+
background-color: var(--bulma-background);
|
|
148
|
+
border-color: var(--bulma-border);
|
|
149
|
+
color: var(--bulma-text);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
[data-theme=dark] .tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover {
|
|
153
|
+
background-color: var(--bulma-background-bis, var(--bulma-background));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
[data-theme=dark] .tabulator-row,
|
|
157
|
+
[data-theme=dark] .tabulator-row.tabulator-row-odd {
|
|
158
|
+
background-color: var(--bulma-body-background-color) !important;
|
|
159
|
+
color: var(--bulma-body-color);
|
|
160
|
+
border-color: var(--bulma-border);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
[data-theme=dark] .tabulator-row.tabulator-row-even {
|
|
164
|
+
background-color: var(--bulma-background) !important;
|
|
165
|
+
color: var(--bulma-body-color);
|
|
166
|
+
border-color: var(--bulma-border);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[data-theme=dark] .tabulator-row .tabulator-cell {
|
|
170
|
+
border-color: var(--bulma-border);
|
|
171
|
+
color: var(--bulma-body-color);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[data-theme=dark] .tabulator-row.tabulator-selectable:hover {
|
|
175
|
+
background-color: var(--bulma-background) !important;
|
|
176
|
+
color: var(--bulma-text) !important;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/*
|
|
180
|
+
* The CELL EDITOR in dark theme.
|
|
181
|
+
*
|
|
182
|
+
* Tabulator gives its editor input no colour of its own, so it inherits the
|
|
183
|
+
* browser default: BLACK text on the dark cell — the value disappears the
|
|
184
|
+
* moment you click into it, and comes back when the field loses focus. Same
|
|
185
|
+
* for a header-filter input and for a <select>.
|
|
186
|
+
*/
|
|
187
|
+
[data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing input,
|
|
188
|
+
[data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing select,
|
|
189
|
+
[data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing textarea,
|
|
190
|
+
[data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter input,
|
|
191
|
+
[data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter select {
|
|
192
|
+
background-color: var(--bulma-scheme-main, #16181d);
|
|
193
|
+
color: var(--bulma-body-color, #e6e6e6) !important;
|
|
194
|
+
caret-color: var(--bulma-body-color, #e6e6e6);
|
|
195
|
+
border-color: var(--bulma-border);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
[data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing input::placeholder,
|
|
199
|
+
[data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter input::placeholder {
|
|
200
|
+
color: var(--bulma-text-weak, #8a8f98);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[data-theme=dark] .tabulator-row.tabulator-selected {
|
|
204
|
+
background-color: var(--bulma-link) !important;
|
|
205
|
+
color: var(--bulma-link-invert) !important;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
[data-theme=dark] .tabulator-row.tabulator-selected .tabulator-cell {
|
|
209
|
+
color: var(--bulma-link-invert) !important;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
[data-theme=dark] .tabulator .tabulator-footer {
|
|
213
|
+
background-color: var(--bulma-background);
|
|
214
|
+
border-color: var(--bulma-border);
|
|
215
|
+
color: var(--bulma-text);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[data-theme=dark] .tabulator .tabulator-footer .tabulator-page {
|
|
219
|
+
background-color: transparent;
|
|
220
|
+
color: var(--bulma-text);
|
|
221
|
+
border-color: var(--bulma-border);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
[data-theme=dark] .tabulator .tabulator-footer .tabulator-page:not(.disabled):hover {
|
|
225
|
+
background-color: var(--bulma-background);
|
|
226
|
+
color: var(--bulma-text);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
[data-theme=dark] .tabulator .tabulator-footer .tabulator-page.active {
|
|
230
|
+
background-color: var(--bulma-link);
|
|
231
|
+
color: var(--bulma-link-invert);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
[data-theme=dark] .tabulator-row .tabulator-cell.tabulator-frozen {
|
|
235
|
+
background-color: var(--bulma-background) !important;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* system theme: follow OS preference */
|
|
239
|
+
@media (prefers-color-scheme: dark) {
|
|
240
|
+
[data-theme=system] .tabulator {
|
|
241
|
+
background-color: var(--bulma-body-background-color);
|
|
242
|
+
border-color: var(--bulma-border);
|
|
243
|
+
color: var(--bulma-body-color);
|
|
244
|
+
}
|
|
245
|
+
[data-theme=system] .tabulator .tabulator-header,
|
|
246
|
+
[data-theme=system] .tabulator .tabulator-header .tabulator-col {
|
|
247
|
+
background-color: var(--bulma-background);
|
|
248
|
+
border-color: var(--bulma-border);
|
|
249
|
+
color: var(--bulma-text);
|
|
250
|
+
}
|
|
251
|
+
[data-theme=system] .tabulator-row,
|
|
252
|
+
[data-theme=system] .tabulator-row.tabulator-row-odd {
|
|
253
|
+
background-color: var(--bulma-body-background-color) !important;
|
|
254
|
+
color: var(--bulma-body-color);
|
|
255
|
+
border-color: var(--bulma-border);
|
|
256
|
+
}
|
|
257
|
+
[data-theme=system] .tabulator-row.tabulator-row-even {
|
|
258
|
+
background-color: var(--bulma-background) !important;
|
|
259
|
+
color: var(--bulma-body-color);
|
|
260
|
+
border-color: var(--bulma-border);
|
|
261
|
+
}
|
|
262
|
+
[data-theme=system] .tabulator-row .tabulator-cell {
|
|
263
|
+
border-color: var(--bulma-border);
|
|
264
|
+
color: var(--bulma-body-color);
|
|
265
|
+
}
|
|
266
|
+
[data-theme=system] .tabulator-row.tabulator-selectable:hover {
|
|
267
|
+
background-color: var(--bulma-background) !important;
|
|
268
|
+
}
|
|
269
|
+
[data-theme=system] .tabulator.yui-no-row-hover
|
|
270
|
+
.tabulator-row.tabulator-row-odd.tabulator-selectable:not(.tabulator-selected):hover {
|
|
271
|
+
background-color: var(--bulma-body-background-color) !important;
|
|
272
|
+
color: var(--bulma-body-color) !important;
|
|
273
|
+
}
|
|
274
|
+
[data-theme=system] .tabulator.yui-no-row-hover
|
|
275
|
+
.tabulator-row.tabulator-row-even.tabulator-selectable:not(.tabulator-selected):hover {
|
|
276
|
+
background-color: var(--bulma-background) !important;
|
|
277
|
+
color: var(--bulma-body-color) !important;
|
|
278
|
+
}
|
|
279
|
+
[data-theme=system] .tabulator-row.tabulator-selected {
|
|
280
|
+
background-color: var(--bulma-link) !important;
|
|
281
|
+
color: var(--bulma-link-invert) !important;
|
|
282
|
+
}
|
|
283
|
+
[data-theme=system] .tabulator .tabulator-footer {
|
|
284
|
+
background-color: var(--bulma-background);
|
|
285
|
+
border-color: var(--bulma-border);
|
|
286
|
+
color: var(--bulma-text);
|
|
287
|
+
}
|
|
288
|
+
[data-theme=system] .tabulator .tabulator-footer .tabulator-page {
|
|
289
|
+
background-color: transparent;
|
|
290
|
+
color: var(--bulma-text);
|
|
291
|
+
border-color: var(--bulma-border);
|
|
292
|
+
}
|
|
293
|
+
[data-theme=system] .tabulator .tabulator-footer .tabulator-page.active {
|
|
294
|
+
background-color: var(--bulma-link);
|
|
295
|
+
color: var(--bulma-link-invert);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|