@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.
Files changed (56) hide show
  1. package/README.md +331 -11
  2. package/dist/gobj-ui.cjs.js +21386 -17919
  3. package/dist/gobj-ui.es.js +26059 -22613
  4. package/index.js +45 -9
  5. package/package.json +4 -4
  6. package/src/c_g6_nodes_tree.js +128 -40
  7. package/src/c_yui_form.css +9 -0
  8. package/src/c_yui_form.js +113 -53
  9. package/src/c_yui_gobj_tree_js.js +58 -36
  10. package/src/c_yui_json.css +139 -0
  11. package/src/c_yui_json.js +928 -0
  12. package/src/c_yui_json_graph.js +110 -20
  13. package/src/c_yui_map.js +18 -36
  14. package/src/c_yui_nav.js +6 -9
  15. package/src/c_yui_period.css +184 -0
  16. package/src/c_yui_period.js +1441 -0
  17. package/src/c_yui_shell.css +120 -0
  18. package/src/c_yui_shell.js +672 -111
  19. package/src/c_yui_treedb_graph.js +639 -37
  20. package/src/c_yui_treedb_schema.js +478 -0
  21. package/src/c_yui_treedb_topic_with_form.css +18 -0
  22. package/src/c_yui_treedb_topic_with_form.js +153 -103
  23. package/src/c_yui_treedb_topics.css +73 -0
  24. package/src/c_yui_treedb_topics.js +1070 -29
  25. package/src/c_yui_window.css +22 -0
  26. package/src/c_yui_window.js +182 -97
  27. package/src/c_yui_window_manager.js +38 -4
  28. package/src/json_view_helpers.js +214 -0
  29. package/src/json_view_helpers.test.js +135 -0
  30. package/src/route_map_model.js +317 -0
  31. package/src/route_map_model.test.js +209 -0
  32. package/src/route_resolver.js +24 -1
  33. package/src/route_resolver.test.js +40 -1
  34. package/src/shell_modals.js +162 -52
  35. package/src/shell_route_map.css +225 -0
  36. package/src/shell_route_map.js +363 -0
  37. package/src/tabulator.css +245 -0
  38. package/src/yui_dev.js +130 -8
  39. package/src/yui_frontend_view.js +106 -0
  40. package/src/yui_icons.css +62 -0
  41. package/src/yui_inputs.css +8 -3
  42. package/src/yui_inputs.js +51 -8
  43. package/src/yui_tabulator_i18n.js +128 -0
  44. package/src/yui_theme.js +147 -0
  45. package/src/yui_time.js +666 -0
  46. package/src/yui_time.test.js +330 -0
  47. package/src/yui_toolbar.css +28 -0
  48. package/src/yui_toolbar.js +86 -43
  49. package/src/c_yui_main.css +0 -501
  50. package/src/c_yui_main.js +0 -1623
  51. package/src/c_yui_routing.css +0 -18
  52. package/src/c_yui_routing.js +0 -837
  53. package/src/c_yui_tabs.js +0 -487
  54. package/src/themes.js +0 -215
  55. package/src/ytable.css +0 -63
  56. 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
+