@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
package/src/c_yui_json_graph.js
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
} from '@antv/g6';
|
|
60
60
|
|
|
61
61
|
import { ensure_drag_canvas_patch } from "./g6_drag_canvas_touch.js";
|
|
62
|
+
import { yui_is_dark, yui_theme_now, yui_watch_theme } from "./yui_theme.js";
|
|
62
63
|
|
|
63
64
|
/***************************************************************
|
|
64
65
|
* Constants
|
|
@@ -91,6 +92,7 @@ let PRIVATE_DATA = {
|
|
|
91
92
|
graph: null,
|
|
92
93
|
node_counter: 0,
|
|
93
94
|
theme: null,
|
|
95
|
+
theme_observer: null, // MutationObserver on <html data-theme>
|
|
94
96
|
resize_observer: null, // ResizeObserver on the canvas mount element
|
|
95
97
|
_resize_raf: 0, // rAF id debouncing resize -> EV_RESIZE
|
|
96
98
|
};
|
|
@@ -111,11 +113,55 @@ const TYPE_COLORS = {
|
|
|
111
113
|
dict: "#4C0099", // purple
|
|
112
114
|
};
|
|
113
115
|
|
|
116
|
+
/***************************************************************
|
|
117
|
+
* Group (card) colours. `fill` is the light-theme card; `tint`
|
|
118
|
+
* is the hue that fill hints at, used to derive the dark card —
|
|
119
|
+
* dict and list share a stroke, so the tint is what keeps them
|
|
120
|
+
* apart at a glance (with the dashed/solid border).
|
|
121
|
+
***************************************************************/
|
|
114
122
|
const GROUP_COLORS = {
|
|
115
|
-
dict: {fill: "#FBFBFB", stroke: "#006658"},
|
|
116
|
-
list: {fill: "#fffbd1", stroke: "#006658"},
|
|
123
|
+
dict: {fill: "#FBFBFB", tint: "#006658", stroke: "#006658"},
|
|
124
|
+
list: {fill: "#fffbd1", tint: "#ffd54a", stroke: "#006658"},
|
|
117
125
|
};
|
|
118
126
|
|
|
127
|
+
/***************************************************************
|
|
128
|
+
* Soft, theme-aware card palette, same visual language as the
|
|
129
|
+
* gobj-tree's role_card_style(): tinted fill + group-colour
|
|
130
|
+
* border, lifted off the near-black canvas on dark (a faint tint
|
|
131
|
+
* on the canvas colour makes the cards invisible).
|
|
132
|
+
*
|
|
133
|
+
* The header bar is always the border colour, so its text flips:
|
|
134
|
+
* white on the dark-teal light bar, near-black on the brightened
|
|
135
|
+
* dark one.
|
|
136
|
+
***************************************************************/
|
|
137
|
+
function json_card_style(group, dark)
|
|
138
|
+
{
|
|
139
|
+
return {
|
|
140
|
+
bg: dark
|
|
141
|
+
? `color-mix(in srgb, ${group.tint} 30%, #2c3542)`
|
|
142
|
+
: group.fill,
|
|
143
|
+
border: dark
|
|
144
|
+
? `color-mix(in srgb, ${group.stroke} 85%, #ffffff)`
|
|
145
|
+
: group.stroke,
|
|
146
|
+
header_fg: dark ? "#12181f" : "#ffffff",
|
|
147
|
+
key: dark ? "#c9cfd8" : "#1A1A1A",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/***************************************************************
|
|
152
|
+
* A scalar's colour by type. The palette is tuned for a light
|
|
153
|
+
* card; on a dark one every one of them (dark green #006000,
|
|
154
|
+
* blue #475ED0, …) sinks into the background, so brighten it.
|
|
155
|
+
***************************************************************/
|
|
156
|
+
function type_color(type, dark)
|
|
157
|
+
{
|
|
158
|
+
let color = TYPE_COLORS[type] || (dark ? "#e8eaed" : "black");
|
|
159
|
+
if(!dark) {
|
|
160
|
+
return color;
|
|
161
|
+
}
|
|
162
|
+
return `color-mix(in srgb, ${color} 55%, #ffffff)`;
|
|
163
|
+
}
|
|
164
|
+
|
|
119
165
|
|
|
120
166
|
|
|
121
167
|
|
|
@@ -146,10 +192,14 @@ function mt_create(gobj)
|
|
|
146
192
|
}
|
|
147
193
|
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
148
194
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
195
|
+
/* Follow the app theme. This used to read a legacy C_YUI_MAIN
|
|
196
|
+
* "__yui_main__" service's `theme` attr — and looked it up with
|
|
197
|
+
* verbose=true, so under C_YUI_SHELL (which has no such service) it
|
|
198
|
+
* logged "gobj service not found: __yui_main__" on every mount. It
|
|
199
|
+
* never watched either, so the theme was read once. Watch
|
|
200
|
+
* <html data-theme> and restyle in ac_theme. */
|
|
201
|
+
priv.theme = yui_theme_now();
|
|
202
|
+
priv.theme_observer = yui_watch_theme(gobj);
|
|
153
203
|
|
|
154
204
|
build_ui(gobj);
|
|
155
205
|
|
|
@@ -184,6 +234,10 @@ function mt_destroy(gobj)
|
|
|
184
234
|
{
|
|
185
235
|
let priv = gobj.priv;
|
|
186
236
|
|
|
237
|
+
if(priv.theme_observer) {
|
|
238
|
+
priv.theme_observer.disconnect();
|
|
239
|
+
priv.theme_observer = null;
|
|
240
|
+
}
|
|
187
241
|
if(priv.resize_observer) {
|
|
188
242
|
priv.resize_observer.disconnect();
|
|
189
243
|
priv.resize_observer = null;
|
|
@@ -385,9 +439,10 @@ function build_graph(gobj)
|
|
|
385
439
|
/************************************************************
|
|
386
440
|
* Build cell value HTML (matching old mx_json_viewer colors)
|
|
387
441
|
************************************************************/
|
|
388
|
-
function build_cell_html(key, value, type)
|
|
442
|
+
function build_cell_html(key, value, type, dark)
|
|
389
443
|
{
|
|
390
|
-
let color =
|
|
444
|
+
let color = type_color(type, dark);
|
|
445
|
+
let key_color = json_card_style(GROUP_COLORS.dict, dark).key;
|
|
391
446
|
let display_value = "";
|
|
392
447
|
|
|
393
448
|
switch(type) {
|
|
@@ -414,7 +469,7 @@ function build_cell_html(key, value, type)
|
|
|
414
469
|
break;
|
|
415
470
|
}
|
|
416
471
|
|
|
417
|
-
return `<span style="color
|
|
472
|
+
return `<span style="color:${key_color}">• ${escapeHtml(String(key))}: </span><span style="color:${color}">${display_value}</span>`;
|
|
418
473
|
}
|
|
419
474
|
|
|
420
475
|
/************************************************************
|
|
@@ -481,8 +536,11 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
481
536
|
}
|
|
482
537
|
|
|
483
538
|
/*
|
|
484
|
-
* Build HTML content for this group
|
|
539
|
+
* Build HTML content for this group.
|
|
540
|
+
* The theme is read live as the card is drawn (like the gobj-tree's
|
|
541
|
+
* cards): ac_theme rebuilds on a switch, so this is always current.
|
|
485
542
|
*/
|
|
543
|
+
let dark = yui_is_dark();
|
|
486
544
|
let lines = [];
|
|
487
545
|
let pending_complex = [];
|
|
488
546
|
|
|
@@ -490,7 +548,7 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
490
548
|
|
|
491
549
|
for(let [key, value] of entries) {
|
|
492
550
|
let type = get_json_type(value);
|
|
493
|
-
let html = build_cell_html(key, value, type);
|
|
551
|
+
let html = build_cell_html(key, value, type, dark);
|
|
494
552
|
lines.push(html);
|
|
495
553
|
|
|
496
554
|
if((is_object(value) && json_object_size(value) > 0) ||
|
|
@@ -500,7 +558,9 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
500
558
|
}
|
|
501
559
|
|
|
502
560
|
let label = get_group_label(path) || (is_dict ? "{}" : "[]");
|
|
503
|
-
let colors =
|
|
561
|
+
let colors = json_card_style(
|
|
562
|
+
is_dict ? GROUP_COLORS.dict : GROUP_COLORS.list, dark
|
|
563
|
+
);
|
|
504
564
|
|
|
505
565
|
/*
|
|
506
566
|
* Build node HTML content
|
|
@@ -514,15 +574,15 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
514
574
|
let node_html = `
|
|
515
575
|
<div style="
|
|
516
576
|
min-width: ${min_width}px;
|
|
517
|
-
background: ${colors.
|
|
518
|
-
border: 1px ${is_dict ? 'dashed' : 'solid'} ${colors.
|
|
577
|
+
background: ${colors.bg};
|
|
578
|
+
border: 1px ${is_dict ? 'dashed' : 'solid'} ${colors.border};
|
|
519
579
|
border-radius: ${is_dict ? '6px' : '0'};
|
|
520
580
|
opacity: 0.9;
|
|
521
581
|
overflow: hidden;
|
|
522
582
|
">
|
|
523
583
|
<div style="
|
|
524
|
-
background: ${colors.
|
|
525
|
-
color:
|
|
584
|
+
background: ${colors.border};
|
|
585
|
+
color: ${colors.header_fg};
|
|
526
586
|
font-weight: bold;
|
|
527
587
|
font-size: 12px;
|
|
528
588
|
padding: 3px 8px;
|
|
@@ -555,7 +615,7 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id)
|
|
|
555
615
|
source: parent_id,
|
|
556
616
|
target: group_id,
|
|
557
617
|
style: {
|
|
558
|
-
stroke: colors.
|
|
618
|
+
stroke: colors.border,
|
|
559
619
|
lineWidth: 1,
|
|
560
620
|
}
|
|
561
621
|
});
|
|
@@ -608,23 +668,34 @@ function load_json(gobj)
|
|
|
608
668
|
|
|
609
669
|
build_json_nodes(gobj, path, json, nodes, edges, null);
|
|
610
670
|
|
|
671
|
+
let preserve_view = !!priv.pending_preserve_view;
|
|
672
|
+
priv.pending_preserve_view = false;
|
|
673
|
+
|
|
611
674
|
if(nodes.length > 0) {
|
|
612
675
|
graph.setData({nodes: nodes, edges: edges});
|
|
613
676
|
graph.render().then(() => {
|
|
614
|
-
|
|
677
|
+
if(!preserve_view) {
|
|
678
|
+
graph.fitView();
|
|
679
|
+
}
|
|
615
680
|
});
|
|
616
681
|
}
|
|
617
682
|
}
|
|
618
683
|
|
|
619
684
|
/************************************************************
|
|
620
|
-
* Clear and reload
|
|
685
|
+
* Clear and reload.
|
|
686
|
+
* {preserve_view: true}
|
|
687
|
+
* keep the current zoom and translate (no fitView) —
|
|
688
|
+
* a restyle (theme change) must not move the camera.
|
|
689
|
+
* Default: fit the whole graph to the viewport.
|
|
621
690
|
************************************************************/
|
|
622
|
-
function refresh_json(gobj)
|
|
691
|
+
function refresh_json(gobj, opts)
|
|
623
692
|
{
|
|
624
693
|
let priv = gobj.priv;
|
|
625
694
|
let graph = priv.graph;
|
|
695
|
+
opts = opts || {};
|
|
626
696
|
|
|
627
697
|
if(graph) {
|
|
698
|
+
priv.pending_preserve_view = !!opts.preserve_view;
|
|
628
699
|
graph.clear();
|
|
629
700
|
load_json(gobj);
|
|
630
701
|
}
|
|
@@ -789,6 +860,23 @@ function ac_refresh(gobj, event, kw, src)
|
|
|
789
860
|
return 0;
|
|
790
861
|
}
|
|
791
862
|
|
|
863
|
+
/************************************************************
|
|
864
|
+
* {theme: "dark"|"light"} — the app switched theme.
|
|
865
|
+
************************************************************/
|
|
866
|
+
function ac_theme(gobj, event, kw, src)
|
|
867
|
+
{
|
|
868
|
+
let priv = gobj.priv;
|
|
869
|
+
|
|
870
|
+
priv.theme = kw.theme || yui_theme_now();
|
|
871
|
+
if(priv.graph) {
|
|
872
|
+
priv.graph.setTheme(priv.theme);
|
|
873
|
+
}
|
|
874
|
+
/* A restyle, not new data: keep the user's zoom/pan. */
|
|
875
|
+
refresh_json(gobj, {preserve_view: true});
|
|
876
|
+
|
|
877
|
+
return 0;
|
|
878
|
+
}
|
|
879
|
+
|
|
792
880
|
/************************************************************
|
|
793
881
|
*
|
|
794
882
|
************************************************************/
|
|
@@ -965,6 +1053,7 @@ function create_gclass(gclass_name)
|
|
|
965
1053
|
["ST_IDLE", [
|
|
966
1054
|
["EV_LOAD_DATA", ac_load_data, null],
|
|
967
1055
|
["EV_REFRESH", ac_refresh, null],
|
|
1056
|
+
["EV_THEME", ac_theme, null],
|
|
968
1057
|
["EV_ZOOM_IN", ac_zoom_in, null],
|
|
969
1058
|
["EV_ZOOM_OUT", ac_zoom_out, null],
|
|
970
1059
|
["EV_ZOOM_RESET", ac_zoom_reset, null],
|
|
@@ -982,6 +1071,7 @@ function create_gclass(gclass_name)
|
|
|
982
1071
|
const event_types = [
|
|
983
1072
|
["EV_LOAD_DATA", 0],
|
|
984
1073
|
["EV_REFRESH", 0],
|
|
1074
|
+
["EV_THEME", 0],
|
|
985
1075
|
["EV_ZOOM_IN", 0],
|
|
986
1076
|
["EV_ZOOM_OUT", 0],
|
|
987
1077
|
["EV_ZOOM_RESET", 0],
|
package/src/c_yui_map.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
gobj_read_bool_attr,
|
|
28
28
|
gobj_find_service,
|
|
29
29
|
gobj_create_service,
|
|
30
|
+
gobj_start,
|
|
30
31
|
gobj_name,
|
|
31
32
|
} from "@yuneta/gobj-js";
|
|
32
33
|
|
|
@@ -205,24 +206,6 @@ function mt_create(gobj)
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
/***************************************************************
|
|
209
|
-
* Framework Method: Start
|
|
210
|
-
***************************************************************/
|
|
211
|
-
function mt_start(gobj)
|
|
212
|
-
{
|
|
213
|
-
let __yui_main__ = gobj_find_service("__yui_main__", true);
|
|
214
|
-
if(__yui_main__) {
|
|
215
|
-
gobj_subscribe_event(__yui_main__, "EV_RESIZE", {}, gobj);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/***************************************************************
|
|
220
|
-
* Framework Method: Stop
|
|
221
|
-
***************************************************************/
|
|
222
|
-
function mt_stop(gobj)
|
|
223
|
-
{
|
|
224
|
-
}
|
|
225
|
-
|
|
226
209
|
/***************************************************************
|
|
227
210
|
* Framework Method: Destroy
|
|
228
211
|
***************************************************************/
|
|
@@ -590,10 +573,25 @@ function onClick(gobj, e)
|
|
|
590
573
|
center: true,
|
|
591
574
|
showMax: false,
|
|
592
575
|
content_size: true,
|
|
576
|
+
/* The marker's own name: this window is one marker's
|
|
577
|
+
* detail, and several can be open at once, so the bar
|
|
578
|
+
* must say WHICH. It is DATA, so it travels in
|
|
579
|
+
* title_prefix (never translated) — in `title` it
|
|
580
|
+
* would get a data-i18n and a marker named like a
|
|
581
|
+
* locale key ("status") would render translated. */
|
|
582
|
+
title_prefix: gobj_name(gobj_service),
|
|
583
|
+
title: "",
|
|
584
|
+
icon: "yi-location-dot",
|
|
585
|
+
logical_class: "MAP_MARKER_WINDOW",
|
|
593
586
|
body: popupContent
|
|
594
587
|
},
|
|
595
588
|
gobj
|
|
596
589
|
);
|
|
590
|
+
if(!gobj_window) {
|
|
591
|
+
log_error(`${gobj_name(gobj)}: cannot create the marker window`);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
gobj_start(gobj_window);
|
|
597
595
|
|
|
598
596
|
} else {
|
|
599
597
|
// Initialize a popup
|
|
@@ -823,18 +821,6 @@ function ac_hide(gobj, event, kw, src)
|
|
|
823
821
|
return 0;
|
|
824
822
|
}
|
|
825
823
|
|
|
826
|
-
/************************************************************
|
|
827
|
-
*
|
|
828
|
-
************************************************************/
|
|
829
|
-
function ac_resize(gobj, event, kw, src)
|
|
830
|
-
{
|
|
831
|
-
if(!gobj_read_bool_attr(gobj, "dimensions_with_parent_observer")) {
|
|
832
|
-
// TODO
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
return 0;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
824
|
|
|
839
825
|
|
|
840
826
|
|
|
@@ -850,8 +836,6 @@ function ac_resize(gobj, event, kw, src)
|
|
|
850
836
|
*---------------------------------------------*/
|
|
851
837
|
const gmt = {
|
|
852
838
|
mt_create: mt_create,
|
|
853
|
-
mt_start: mt_start,
|
|
854
|
-
mt_stop: mt_stop,
|
|
855
839
|
mt_destroy: mt_destroy
|
|
856
840
|
};
|
|
857
841
|
|
|
@@ -876,8 +860,7 @@ function create_gclass(gclass_name)
|
|
|
876
860
|
["EV_REFRESH", ac_refresh, null],
|
|
877
861
|
["EV_SELECT", ac_select, null],
|
|
878
862
|
["EV_SHOW", ac_show, null],
|
|
879
|
-
["EV_HIDE", ac_hide, null]
|
|
880
|
-
["EV_RESIZE", ac_resize, null]
|
|
863
|
+
["EV_HIDE", ac_hide, null]
|
|
881
864
|
]]
|
|
882
865
|
];
|
|
883
866
|
|
|
@@ -891,8 +874,7 @@ function create_gclass(gclass_name)
|
|
|
891
874
|
["EV_REFRESH", 0],
|
|
892
875
|
["EV_SELECT", 0],
|
|
893
876
|
["EV_SHOW", 0],
|
|
894
|
-
["EV_HIDE", 0]
|
|
895
|
-
["EV_RESIZE", 0]
|
|
877
|
+
["EV_HIDE", 0]
|
|
896
878
|
];
|
|
897
879
|
|
|
898
880
|
__gclass__ = gclass_create(
|
package/src/c_yui_nav.js
CHANGED
|
@@ -107,14 +107,15 @@ SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "backbar layo
|
|
|
107
107
|
SDATA(data_type_t.DTP_POINTER, "shell", 0, null, "C_YUI_SHELL gobj (for navigation calls)"),
|
|
108
108
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTMLElement of this nav"),
|
|
109
109
|
SDATA(data_type_t.DTP_STRING, "active_route", 0, "", "Currently active route"),
|
|
110
|
-
SDATA(data_type_t.DTP_POINTER, "priv", 0, null, "Private runtime state (click listener, etc.)"),
|
|
111
110
|
SDATA_END()
|
|
112
111
|
];
|
|
113
112
|
|
|
114
113
|
/* Monotonic id generator for ARIA pairs (aria-controls etc.). */
|
|
115
114
|
let __nav_aria_seq__ = 0;
|
|
116
115
|
|
|
117
|
-
let PRIVATE_DATA = {
|
|
116
|
+
let PRIVATE_DATA = {
|
|
117
|
+
click_handler: null
|
|
118
|
+
};
|
|
118
119
|
let __gclass__ = null;
|
|
119
120
|
|
|
120
121
|
|
|
@@ -141,9 +142,6 @@ function mt_create(gobj)
|
|
|
141
142
|
}
|
|
142
143
|
gobj_subscribe_event(gobj, null, {}, subscriber);
|
|
143
144
|
|
|
144
|
-
gobj_write_attr(gobj, "priv", {
|
|
145
|
-
click_handler: null
|
|
146
|
-
});
|
|
147
145
|
build_ui(gobj);
|
|
148
146
|
}
|
|
149
147
|
|
|
@@ -177,7 +175,7 @@ function mt_stop(gobj)
|
|
|
177
175
|
function mt_destroy(gobj)
|
|
178
176
|
{
|
|
179
177
|
let $c = gobj_read_attr(gobj, "$container");
|
|
180
|
-
let priv =
|
|
178
|
+
let priv = gobj.priv;
|
|
181
179
|
if($c && priv && priv.click_handler) {
|
|
182
180
|
$c.removeEventListener("click", priv.click_handler);
|
|
183
181
|
}
|
|
@@ -185,7 +183,6 @@ function mt_destroy(gobj)
|
|
|
185
183
|
$c.parentNode.removeChild($c);
|
|
186
184
|
}
|
|
187
185
|
gobj_write_attr(gobj, "$container", null);
|
|
188
|
-
gobj_write_attr(gobj, "priv", null);
|
|
189
186
|
}
|
|
190
187
|
|
|
191
188
|
|
|
@@ -267,7 +264,7 @@ function build_ui(gobj)
|
|
|
267
264
|
function rebuild(gobj)
|
|
268
265
|
{
|
|
269
266
|
let $old = gobj_read_attr(gobj, "$container");
|
|
270
|
-
let priv =
|
|
267
|
+
let priv = gobj.priv;
|
|
271
268
|
let parent = $old ? $old.parentNode : null;
|
|
272
269
|
let next = $old ? $old.nextSibling : null;
|
|
273
270
|
let was_hidden = !!($old && $old.classList.contains("is-hidden"));
|
|
@@ -718,7 +715,7 @@ function item_iconbar(gobj, it, opts)
|
|
|
718
715
|
|
|
719
716
|
function wire_clicks(gobj, $container)
|
|
720
717
|
{
|
|
721
|
-
let priv =
|
|
718
|
+
let priv = gobj.priv;
|
|
722
719
|
|
|
723
720
|
let handler = ev => {
|
|
724
721
|
let target = ev.target;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* c_yui_period.css
|
|
3
|
+
* The date navigator (C_YUI_PERIOD).
|
|
4
|
+
*
|
|
5
|
+
* Imported by c_yui_period.js, so it rides the component: a consumer that
|
|
6
|
+
* imports the gclass gets its styles, and one that does not, does not ship
|
|
7
|
+
* them.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.YUI_PERIOD {
|
|
11
|
+
max-width: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The granularities are a SEGMENTED control: one line of joined pills.
|
|
16
|
+
* When they do not fit — a phone, or an app that declared ten of them —
|
|
17
|
+
* the strip SCROLLS sideways. Wrapping would break the joined look mid-row,
|
|
18
|
+
* and letting it overflow would push the last modes out of the card (it did:
|
|
19
|
+
* "Custom" was cut off by the dialog's edge, unreachable).
|
|
20
|
+
*/
|
|
21
|
+
.YUI_PERIOD_MODES_ROW {
|
|
22
|
+
max-width: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Two classes deep, again: Bulma's `.buttons` sets `justify-content:
|
|
26
|
+
* flex-start` and ships after this file, so a single-class rule of equal
|
|
27
|
+
* specificity loses — the strip stayed pinned to the left of the arrows it
|
|
28
|
+
* belongs to. When it does not fit it scrolls, and then it fills the row
|
|
29
|
+
* anyway. */
|
|
30
|
+
/* `safe center`, not plain `center`: a CENTRED flex row that overflows spills
|
|
31
|
+
* out of BOTH ends, and `scrollLeft` cannot go negative — so on a phone the
|
|
32
|
+
* first granularities were painted off the left edge and NO swipe could reach
|
|
33
|
+
* them (at 360px the browser exposed 28px of the 57px of overflow, and "All"
|
|
34
|
+
* sat at -28px). The `safe` keyword keeps the strip centred while it fits and
|
|
35
|
+
* falls back to start-alignment the moment it does not. */
|
|
36
|
+
.YUI_PERIOD .YUI_PERIOD_MODES {
|
|
37
|
+
flex: 1 1 auto;
|
|
38
|
+
min-width: 0;
|
|
39
|
+
overflow-x: auto;
|
|
40
|
+
scrollbar-width: thin;
|
|
41
|
+
justify-content: safe center;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.YUI_PERIOD_MODES::-webkit-scrollbar {
|
|
45
|
+
height: 4px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* The "there is more" hint: a 4px scrollbar is invisible to a thumb, so the
|
|
50
|
+
* edge that hides content fades out instead. The classes are toggled from
|
|
51
|
+
* update_modes_fade() (scroll + ResizeObserver); a strip that fits gets none.
|
|
52
|
+
*/
|
|
53
|
+
.YUI_PERIOD_MODES.yui-period-fade-right {
|
|
54
|
+
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent);
|
|
55
|
+
mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.YUI_PERIOD_MODES.yui-period-fade-left {
|
|
59
|
+
-webkit-mask-image: linear-gradient(to right, transparent, #000 2rem);
|
|
60
|
+
mask-image: linear-gradient(to right, transparent, #000 2rem);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.YUI_PERIOD_MODES.yui-period-fade-left.yui-period-fade-right {
|
|
64
|
+
-webkit-mask-image: linear-gradient(to right,
|
|
65
|
+
transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
|
|
66
|
+
mask-image: linear-gradient(to right,
|
|
67
|
+
transparent, #000 2rem, #000 calc(100% - 2rem), transparent);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Bulma's .buttons gives every child a margin-bottom, for the rows it
|
|
71
|
+
* expects to wrap. This one never wraps, so that margin is just a gap. */
|
|
72
|
+
.YUI_PERIOD_MODES .button {
|
|
73
|
+
margin-bottom: 0;
|
|
74
|
+
white-space: nowrap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* The overflow menu lives OUTSIDE the scrolling strip on purpose: a dropdown
|
|
79
|
+
* inside an `overflow-x` container is clipped by it, so its items would open
|
|
80
|
+
* into a 4px-tall scrollbar.
|
|
81
|
+
*/
|
|
82
|
+
.YUI_PERIOD_MORE {
|
|
83
|
+
flex: 0 0 auto;
|
|
84
|
+
margin-left: 0.25rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* The navigator is the LOUDEST thing in the block: it is what the user
|
|
89
|
+
* actually operates ("show me yesterday"), and at Bulma's default button size
|
|
90
|
+
* it read as chrome. Big targets, a big label — a phone thumb hits them
|
|
91
|
+
* without aiming.
|
|
92
|
+
*/
|
|
93
|
+
/* Two classes deep on purpose: Bulma's `.button` sets its own font-size and
|
|
94
|
+
* ships AFTER this file, so a single-class rule of equal specificity loses
|
|
95
|
+
* to it — the label stayed at 16px and read as chrome. */
|
|
96
|
+
.YUI_PERIOD .YUI_PERIOD_NAV .button {
|
|
97
|
+
height: auto;
|
|
98
|
+
min-width: 2.75rem;
|
|
99
|
+
padding: 0.5rem 0.75rem;
|
|
100
|
+
font-size: 1.25rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* THREE classes deep, and the reason is the rule right above: the label is a
|
|
104
|
+
* `.button` INSIDE the navigator, so `.YUI_PERIOD .YUI_PERIOD_NAV .button`
|
|
105
|
+
* outranked a two-class label rule and pinned it to the arrows' 1.25rem — the
|
|
106
|
+
* row rendered flat, with nothing standing out, which is the opposite of what
|
|
107
|
+
* it is for. */
|
|
108
|
+
.YUI_PERIOD .YUI_PERIOD_NAV .YUI_PERIOD_LABEL {
|
|
109
|
+
font-size: 1.35rem;
|
|
110
|
+
font-weight: 700;
|
|
111
|
+
min-width: 9rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.YUI_PERIOD_LABEL .YUI_PERIOD_LABEL_TEXT {
|
|
115
|
+
margin-left: 0.4rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* The calendar glyph is an affordance, not a headline: quieter and smaller
|
|
119
|
+
* than the label text it decorates. */
|
|
120
|
+
.YUI_PERIOD_LABEL .YUI_PERIOD_LABEL_ICON {
|
|
121
|
+
font-size: 1rem;
|
|
122
|
+
opacity: 0.55;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* A name is for the user; these two timestamps are what the query carries.
|
|
126
|
+
* Quiet, but always there — nobody should have to guess what "Week 27"
|
|
127
|
+
* resolves to. */
|
|
128
|
+
.YUI_PERIOD_RESOLVED {
|
|
129
|
+
font-size: 0.75rem;
|
|
130
|
+
opacity: 0.7;
|
|
131
|
+
margin-top: 0.15rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Disabled, not gone (see repaint): the row keeps its place so the card does
|
|
135
|
+
* not re-flow under the cursor on every mode change. */
|
|
136
|
+
.YUI_PERIOD_NAV.yui-period-nav-off {
|
|
137
|
+
opacity: 0.45;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* The calendar hangs off the navigator, centred under the label.
|
|
141
|
+
* `display: flex` lives HERE and not as Bulma's `.is-flex` helper: that one
|
|
142
|
+
* is `!important` and would beat `.is-hidden` (also `!important`), keeping
|
|
143
|
+
* the arrows on screen in the modes that have no bucket to walk. */
|
|
144
|
+
.YUI_PERIOD_NAV {
|
|
145
|
+
position: relative;
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.YUI_PERIOD_CALENDAR {
|
|
152
|
+
position: absolute;
|
|
153
|
+
top: 100%;
|
|
154
|
+
left: 50%;
|
|
155
|
+
transform: translateX(-50%);
|
|
156
|
+
z-index: 40;
|
|
157
|
+
min-width: 17rem;
|
|
158
|
+
max-width: 95vw;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.YUI_PERIOD_CAL_DAY,
|
|
162
|
+
.YUI_PERIOD_CAL_MONTH,
|
|
163
|
+
.YUI_PERIOD_CAL_YEAR,
|
|
164
|
+
.YUI_PERIOD_CAL_WEEKNUM {
|
|
165
|
+
padding-left: 0;
|
|
166
|
+
padding-right: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* The bucket a click would pick, while hovering: a quiet inset ring — the
|
|
170
|
+
* solid fill stays reserved for the bucket that IS selected.
|
|
171
|
+
* Three classes deep: Bulma's `.button.is-ghost` sets `box-shadow: none` at
|
|
172
|
+
* (0,2,0) and ships after this file, so a two-class rule loses to it.
|
|
173
|
+
* currentColor, so the ring follows the cell (link-blue, grey on the
|
|
174
|
+
* neighbouring months) and both themes for free. */
|
|
175
|
+
.YUI_PERIOD .YUI_PERIOD_CALENDAR .yui-period-preview {
|
|
176
|
+
box-shadow: inset 0 0 0 1px currentColor;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* No transitions (framework rule): a granularity switches, it does not
|
|
180
|
+
* glide. */
|
|
181
|
+
.YUI_PERIOD .button,
|
|
182
|
+
.YUI_PERIOD_CALENDAR {
|
|
183
|
+
transition: none !important;
|
|
184
|
+
}
|