@yuneta/gobj-ui 5.2.0 → 5.3.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 +165 -0
- package/dist/gobj-ui.cjs.js +386 -43
- package/dist/gobj-ui.es.js +520 -177
- package/package.json +1 -1
- package/src/c_yui_json.css +23 -3
- package/src/c_yui_json.js +17 -3
- package/src/c_yui_nav.js +63 -3
- package/src/c_yui_node.css +77 -0
- package/src/c_yui_node.js +1567 -0
- package/src/c_yui_shell.css +18 -0
- package/src/c_yui_shell.js +206 -4
- package/src/c_yui_treedb_schema.js +1 -0
- package/src/c_yui_window.js +8 -3
- package/src/node_tree_model.js +463 -0
- package/src/node_tree_model.test.js +282 -0
- package/src/route_map_model.js +48 -0
- package/src/route_map_model.test.js +70 -0
- package/src/route_resolver.js +11 -0
- package/src/shell_route_map.css +36 -2
- package/src/shell_route_map.js +194 -45
- package/src/yui_dev.js +3 -3
- package/src/yui_icons.css +13 -0
package/src/shell_route_map.js
CHANGED
|
@@ -40,6 +40,16 @@ import i18next from "i18next";
|
|
|
40
40
|
|
|
41
41
|
const WIN_NAME = "shell-route-map-window";
|
|
42
42
|
|
|
43
|
+
/* Reference rows — a route's second and third entry point: a drawer
|
|
44
|
+
* entry, a toolbar SHORTCUT. Shown by default: they are one line
|
|
45
|
+
* each, and they are exactly what someone auditing the navigation
|
|
46
|
+
* came to see ("which quick links does this app define?"). What was
|
|
47
|
+
* ever noisy is the repeated SUBTREE, and that is gone for good
|
|
48
|
+
* (dedupe_subtrees) rather than hidden behind a switch. The toggle
|
|
49
|
+
* stays for reading the bare structure, and remembers the choice for
|
|
50
|
+
* this page. */
|
|
51
|
+
let __show_refs__ = true;
|
|
52
|
+
|
|
43
53
|
/* Modal fallback currently open (the window flavour is a service and
|
|
44
54
|
* is found by name; the modal is not, so it is tracked here to make
|
|
45
55
|
* the second call a TOGGLE for both flavours). */
|
|
@@ -81,6 +91,16 @@ function render_node(node)
|
|
|
81
91
|
if(node.event) {
|
|
82
92
|
row.push(["span", {class: "ROUTEMAP_EVENT"}, node.event]);
|
|
83
93
|
}
|
|
94
|
+
/* A reference: this route's structure is drawn under whichever
|
|
95
|
+
* surface owns it (the model's dedupe keeps one subtree per
|
|
96
|
+
* route). Still a live link — it just does not repeat the
|
|
97
|
+
* branch. */
|
|
98
|
+
if(node.ref) {
|
|
99
|
+
row.push(["span", {class: "ROUTEMAP_REF", i18n: "shown above",
|
|
100
|
+
title: "shown above", "data-i18n-title": "shown above"},
|
|
101
|
+
"shown above"]);
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
/* "You are here" — the node whose route best matches the current
|
|
85
105
|
* one (marked by the model; at most one). */
|
|
86
106
|
if(node.current) {
|
|
@@ -88,7 +108,8 @@ function render_node(node)
|
|
|
88
108
|
"you are here"]);
|
|
89
109
|
}
|
|
90
110
|
|
|
91
|
-
let row_class = node.current ? " ROUTEMAP_CURRENT" : ""
|
|
111
|
+
let row_class = (node.current ? " ROUTEMAP_CURRENT" : "") +
|
|
112
|
+
(node.ref ? " ROUTEMAP_IS_REF" : "");
|
|
92
113
|
let $row;
|
|
93
114
|
if(node.route) {
|
|
94
115
|
$row = createElement2(
|
|
@@ -113,29 +134,64 @@ function render_node(node)
|
|
|
113
134
|
* descendant matches (keep the ancestor path). Empty `q` shows all.
|
|
114
135
|
* Matches against the visible row text (name + route + event), so it
|
|
115
136
|
* honours the current translation. Returns whether the <li> is shown.
|
|
137
|
+
*
|
|
138
|
+
* `show_refs` off hides the reference rows — the second and third
|
|
139
|
+
* place a route is reachable from, which carry no structure. They are
|
|
140
|
+
* hidden by DEFAULT: in an app with a drawer and an account menu they
|
|
141
|
+
* are a third of the map, and what a reader comes here for is the
|
|
142
|
+
* structure. A ref stays hidden even inside a matched subtree — an
|
|
143
|
+
* ancestor match must not smuggle it back in — and a group left with
|
|
144
|
+
* nothing but refs collapses with them, since a group is only ever
|
|
145
|
+
* shown by what it contains.
|
|
116
146
|
***************************************************************/
|
|
117
|
-
function filter_li($li, q, ancestor_match)
|
|
147
|
+
function filter_li($li, q, ancestor_match, show_refs)
|
|
118
148
|
{
|
|
119
149
|
let $row = $li.querySelector(":scope > .ROUTEMAP_ROW");
|
|
120
150
|
let text = $row ? ($row.textContent || "").toLowerCase() : "";
|
|
151
|
+
let hidden_ref = !show_refs &&
|
|
152
|
+
!!($row && $row.classList.contains("ROUTEMAP_IS_REF"));
|
|
121
153
|
let self_match = q === "" || text.indexOf(q) >= 0;
|
|
122
|
-
let show_all = ancestor_match || self_match;
|
|
154
|
+
let show_all = (ancestor_match || self_match) && !hidden_ref;
|
|
123
155
|
|
|
124
156
|
let any_child = false;
|
|
125
157
|
let $ul = $li.querySelector(":scope > ul");
|
|
126
158
|
if($ul) {
|
|
127
159
|
let kids = $ul.children;
|
|
128
160
|
for(let i = 0; i < kids.length; i++) {
|
|
129
|
-
if(filter_li(kids[i], q, show_all)) {
|
|
161
|
+
if(filter_li(kids[i], q, show_all, show_refs)) {
|
|
162
|
+
any_child = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let is_group = !!($row && $row.classList.contains("ROUTEMAP_STRUCT"));
|
|
168
|
+
let had_children = !!($ul && $ul.children.length);
|
|
169
|
+
|
|
170
|
+
/* Hiding references removes NOISE, never a navigation surface. A
|
|
171
|
+
* whole menu whose entries are all references — a drawer that
|
|
172
|
+
* reaches the same routes as the primary nav — would otherwise
|
|
173
|
+
* vanish heading and all, and the map would be quietly lying about
|
|
174
|
+
* what the app has. So when hiding would empty a group, its
|
|
175
|
+
* entries are shown anyway: one line each, still without the
|
|
176
|
+
* repeated subtree, which is the part that was actually noisy. */
|
|
177
|
+
if(is_group && had_children && !any_child && !show_refs) {
|
|
178
|
+
let kids = $ul.children;
|
|
179
|
+
for(let i = 0; i < kids.length; i++) {
|
|
180
|
+
if(filter_li(kids[i], q, show_all, true)) {
|
|
130
181
|
any_child = true;
|
|
131
182
|
}
|
|
132
183
|
}
|
|
133
184
|
}
|
|
134
185
|
|
|
135
|
-
|
|
186
|
+
/* A group is only ever shown by what it contains. While a search
|
|
187
|
+
* is running its own heading may still match — a query that finds
|
|
188
|
+
* a name must not come back empty. */
|
|
189
|
+
let visible = (is_group && had_children)
|
|
190
|
+
? (any_child || (q !== "" && self_match))
|
|
191
|
+
: ((show_all || any_child) && !hidden_ref);
|
|
136
192
|
$li.style.display = visible ? "" : "none";
|
|
137
193
|
if($row) {
|
|
138
|
-
$row.classList.toggle("ROUTEMAP_MATCH", q !== "" && self_match);
|
|
194
|
+
$row.classList.toggle("ROUTEMAP_MATCH", q !== "" && self_match && visible);
|
|
139
195
|
}
|
|
140
196
|
return visible;
|
|
141
197
|
}
|
|
@@ -147,13 +203,23 @@ function filter_li($li, q, ancestor_match)
|
|
|
147
203
|
* on a resting-route change. `on_jump()` closes the host for the
|
|
148
204
|
* one click that navigates nowhere (the current route).
|
|
149
205
|
***************************************************************/
|
|
150
|
-
function build_body(shell, t
|
|
206
|
+
function build_body(shell, t)
|
|
151
207
|
{
|
|
152
208
|
let map = yui_shell_nav_map(shell);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
209
|
+
|
|
210
|
+
/* STRUCTURE FIRST. The nav leads, then the toolbar, then the
|
|
211
|
+
* route-table leftovers. The toolbar used to come first, which
|
|
212
|
+
* put a shortcut ("Go to Cards", an account-menu entry) above the
|
|
213
|
+
* section it points at: with one subtree per route the shortcut
|
|
214
|
+
* renders as a reference, so the first /cards a reader met had no
|
|
215
|
+
* tree under it and the real one was thirty rows down. It also
|
|
216
|
+
* made the reference's own "shown above" a lie — the owner was
|
|
217
|
+
* BELOW it. Leading with the structure fixes both. */
|
|
218
|
+
let children = [].concat(map.nav);
|
|
219
|
+
if(Array.isArray(map.toolbar) && map.toolbar.length) {
|
|
220
|
+
children.push({label: "toolbar", icon: "", route: "", event: "",
|
|
221
|
+
kind: "group", children: map.toolbar});
|
|
222
|
+
}
|
|
157
223
|
/* Routes declared only in the route table (config.shell.routes)
|
|
158
224
|
* that no menu/toolbar item points at — root "/", URL-only action
|
|
159
225
|
* routes. Rendered last, as their own group. */
|
|
@@ -197,15 +263,33 @@ function build_body(shell, t, on_jump)
|
|
|
197
263
|
["span", {class: "ROUTEMAP_COUNT is-size-7 has-text-grey is-hidden"},
|
|
198
264
|
[$count_n, ["span", {i18n: "matches"}, "matches"]]]
|
|
199
265
|
);
|
|
266
|
+
/* References are hidden by default and revealed by this toggle.
|
|
267
|
+
* Remembered for the page session (not localStorage): it is a
|
|
268
|
+
* reading preference of the panel, not a position (ROUTING.md §3),
|
|
269
|
+
* and it costs nothing to be back at the useful default tomorrow. */
|
|
270
|
+
let $refs_input = createElement2(
|
|
271
|
+
["input", {class: "ROUTEMAP_REFS_INPUT", type: "checkbox"}]
|
|
272
|
+
);
|
|
273
|
+
if(__show_refs__) {
|
|
274
|
+
$refs_input.checked = true;
|
|
275
|
+
}
|
|
276
|
+
let $refs_toggle = createElement2(
|
|
277
|
+
["label", {class: "checkbox is-size-7 ROUTEMAP_REFS_TOGGLE",
|
|
278
|
+
title: "show references", "data-i18n-title": "show references"},
|
|
279
|
+
[$refs_input, ["span", {class: "ml-1", i18n: "show references"},
|
|
280
|
+
"show references"]]]
|
|
281
|
+
);
|
|
282
|
+
|
|
200
283
|
let $search_row = createElement2(
|
|
201
|
-
["div", {class: "ROUTEMAP_SEARCH_ROW mb-2"},
|
|
284
|
+
["div", {class: "ROUTEMAP_SEARCH_ROW mb-2"},
|
|
285
|
+
[$search_ctrl, $count, $refs_toggle]]
|
|
202
286
|
);
|
|
203
287
|
|
|
204
|
-
|
|
288
|
+
let apply_filter = function() {
|
|
205
289
|
let q = ($search.value || "").trim().toLowerCase();
|
|
206
290
|
let $root_li = $tree.querySelector(".ROUTEMAP_ROOT > li");
|
|
207
291
|
if($root_li) {
|
|
208
|
-
filter_li($root_li, q, false);
|
|
292
|
+
filter_li($root_li, q, false, __show_refs__);
|
|
209
293
|
}
|
|
210
294
|
if(q === "") {
|
|
211
295
|
$count.classList.add("is-hidden");
|
|
@@ -213,8 +297,24 @@ function build_body(shell, t, on_jump)
|
|
|
213
297
|
$count_n.textContent = $tree.querySelectorAll(".ROUTEMAP_MATCH").length;
|
|
214
298
|
$count.classList.remove("is-hidden");
|
|
215
299
|
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
$search.addEventListener("input", apply_filter);
|
|
303
|
+
$refs_input.addEventListener("change", function() {
|
|
304
|
+
__show_refs__ = !!$refs_input.checked;
|
|
305
|
+
apply_filter();
|
|
216
306
|
});
|
|
217
307
|
|
|
308
|
+
/* Nothing to toggle in an app where every route is reachable from
|
|
309
|
+
* exactly one place. */
|
|
310
|
+
if($tree.querySelectorAll(".ROUTEMAP_IS_REF").length === 0) {
|
|
311
|
+
$refs_toggle.classList.add("is-hidden");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* Apply the default (references hidden) before the panel is shown,
|
|
315
|
+
* so it is never painted once with them and once without. */
|
|
316
|
+
apply_filter();
|
|
317
|
+
|
|
218
318
|
let $body = createElement2(
|
|
219
319
|
["div", {class: "C_YUI_SHELL_ROUTEMAP ROUTEMAP_BODY"}, [
|
|
220
320
|
["p", {class: "ROUTEMAP_HINT is-size-7 mb-2", i18n: "site map hint"},
|
|
@@ -251,34 +351,85 @@ function build_body(shell, t, on_jump)
|
|
|
251
351
|
});
|
|
252
352
|
}
|
|
253
353
|
|
|
254
|
-
/* A route link jumps there NATIVELY (browser push + hashchange
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* is ALREADY on navigates nowhere: close the host instead.
|
|
354
|
+
/* A route link jumps there NATIVELY (browser push + hashchange, no
|
|
355
|
+
* preventDefault) and the map STAYS OPEN — every row, not just the
|
|
356
|
+
* subpath ones. It is a navigation panel: the user opens it to
|
|
357
|
+
* move around, and having half the rows close it under them made
|
|
358
|
+
* the same gesture mean two different things. The window is
|
|
359
|
+
* either dock-managed or registered with `keep_on_navigate`, so
|
|
360
|
+
* the shell's transient-overlay drain leaves it alone.
|
|
262
361
|
* Action nodes (no route) are documentation only and do not fire. */
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
362
|
+
|
|
363
|
+
/* "You are here" has to follow the url now that the panel outlives
|
|
364
|
+
* the navigation, or it would keep pointing at where the user was
|
|
365
|
+
* when they opened it. Self-detaching: the panel's DOM is owned by
|
|
366
|
+
* the window/modal that hosts it, and this helper has no hook into
|
|
367
|
+
* their teardown — once the body leaves the document the listener
|
|
368
|
+
* retires itself instead of leaking one per open. */
|
|
369
|
+
let on_hash = function() {
|
|
370
|
+
if(typeof document === "undefined" || !$body.isConnected) {
|
|
371
|
+
if(typeof window !== "undefined") {
|
|
372
|
+
window.removeEventListener("hashchange", on_hash);
|
|
373
|
+
}
|
|
267
374
|
return;
|
|
268
375
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
on_jump();
|
|
275
|
-
}
|
|
276
|
-
});
|
|
376
|
+
mark_current_row($body, window.location.hash, t);
|
|
377
|
+
};
|
|
378
|
+
if(typeof window !== "undefined") {
|
|
379
|
+
window.addEventListener("hashchange", on_hash);
|
|
380
|
+
}
|
|
277
381
|
|
|
278
382
|
refresh_language($body, t);
|
|
279
383
|
return $body;
|
|
280
384
|
}
|
|
281
385
|
|
|
386
|
+
/***************************************************************
|
|
387
|
+
* Move the "you are here" mark to the row that best matches
|
|
388
|
+
* `hash`: an exact route wins, else the LONGEST declared route
|
|
389
|
+
* that is a path-prefix of it (the base view of a deep subpath
|
|
390
|
+
* position). Same rule as the model's mark_current(), applied
|
|
391
|
+
* to the rendered rows so the open panel stays truthful.
|
|
392
|
+
***************************************************************/
|
|
393
|
+
function mark_current_row($body, hash, t)
|
|
394
|
+
{
|
|
395
|
+
let route = String(hash || "").replace(/^#/, "");
|
|
396
|
+
let $best = null;
|
|
397
|
+
let best_len = -1;
|
|
398
|
+
|
|
399
|
+
for(let $a of $body.querySelectorAll(".ROUTEMAP_LINK")) {
|
|
400
|
+
let r = String($a.getAttribute("href") || "").replace(/^#/, "");
|
|
401
|
+
if(!r) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
if(r === route) {
|
|
405
|
+
$best = $a;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
if(route.indexOf(r + "/") === 0 && r.length > best_len) {
|
|
409
|
+
$best = $a;
|
|
410
|
+
best_len = r.length;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
for(let $marked of $body.querySelectorAll(".ROUTEMAP_CURRENT")) {
|
|
415
|
+
$marked.classList.remove("ROUTEMAP_CURRENT");
|
|
416
|
+
}
|
|
417
|
+
if(!$best) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
$best.classList.add("ROUTEMAP_CURRENT");
|
|
421
|
+
|
|
422
|
+
/* One badge, moved — never a second one left behind on the row the
|
|
423
|
+
* user came from. */
|
|
424
|
+
let $badge = $body.querySelector(".ROUTEMAP_HERE");
|
|
425
|
+
if(!$badge) {
|
|
426
|
+
$badge = createElement2(
|
|
427
|
+
["span", {class: "ROUTEMAP_HERE", i18n: "you are here"},
|
|
428
|
+
t ? t("you are here") : "you are here"]);
|
|
429
|
+
}
|
|
430
|
+
$best.appendChild($badge);
|
|
431
|
+
}
|
|
432
|
+
|
|
282
433
|
|
|
283
434
|
/***************************************************************
|
|
284
435
|
* Show the site map. Toggles: a second call closes the open one.
|
|
@@ -305,11 +456,7 @@ export function yui_shell_show_route_map(shell, opts)
|
|
|
305
456
|
/* Preferred: a resizable, maximisable floating window. */
|
|
306
457
|
if(gclass_find_by_name("C_YUI_WINDOW") !== null) {
|
|
307
458
|
let win_ref = {gobj: null};
|
|
308
|
-
let $body = build_body(shell, t
|
|
309
|
-
if(win_ref.gobj && is_gobj(win_ref.gobj)) {
|
|
310
|
-
gobj_send_event(win_ref.gobj, "EV_CLOSE_WINDOW", {}, shell);
|
|
311
|
-
}
|
|
312
|
-
});
|
|
459
|
+
let $body = build_body(shell, t);
|
|
313
460
|
let $parent = yui_shell_popup_layer(shell) ||
|
|
314
461
|
(typeof document !== "undefined" &&
|
|
315
462
|
document.getElementById("top-layer")) || null;
|
|
@@ -328,7 +475,13 @@ export function yui_shell_show_route_map(shell, opts)
|
|
|
328
475
|
title: "site map",
|
|
329
476
|
icon: "yi-bars",
|
|
330
477
|
body: $body,
|
|
331
|
-
|
|
478
|
+
/* A workspace surface, not a thing floating over one view:
|
|
479
|
+
* it joins the dock when the app has one (minimise, restore,
|
|
480
|
+
* focus like any other window). Without a dock it stays a
|
|
481
|
+
* floating overlay, and `keep_on_navigate` gives it the same
|
|
482
|
+
* survives-navigation behaviour there. */
|
|
483
|
+
manager: gobj_find_service("__window_manager__", false),
|
|
484
|
+
keep_on_navigate: true
|
|
332
485
|
}, shell);
|
|
333
486
|
if(!win) {
|
|
334
487
|
log_error("C_YUI_SHELL: cannot create the site-map window");
|
|
@@ -342,11 +495,7 @@ export function yui_shell_show_route_map(shell, opts)
|
|
|
342
495
|
|
|
343
496
|
/* Fallback: a modal (no C_YUI_WINDOW registered). */
|
|
344
497
|
let modal_ref = {modal: null};
|
|
345
|
-
let $body = build_body(shell, t
|
|
346
|
-
if(modal_ref.modal && typeof modal_ref.modal.close === "function") {
|
|
347
|
-
modal_ref.modal.close();
|
|
348
|
-
}
|
|
349
|
-
});
|
|
498
|
+
let $body = build_body(shell, t);
|
|
350
499
|
modal_ref.modal = yui_shell_show_modal(shell, $body, {
|
|
351
500
|
dialog: true,
|
|
352
501
|
logical_class: "ROUTEMAP_SHEET",
|
package/src/yui_dev.js
CHANGED
|
@@ -715,7 +715,7 @@ function render_full(e)
|
|
|
715
715
|
let payload = full_sections(e.kw ? e.kw : e.jn);
|
|
716
716
|
let text;
|
|
717
717
|
try {
|
|
718
|
-
text = JSON.stringify(payload, null,
|
|
718
|
+
text = JSON.stringify(payload, null, 4);
|
|
719
719
|
} catch(err) {
|
|
720
720
|
text = String(payload);
|
|
721
721
|
}
|
|
@@ -1021,7 +1021,7 @@ function info_log(level, msg, hora)
|
|
|
1021
1021
|
let text;
|
|
1022
1022
|
if(lvl === "json") {
|
|
1023
1023
|
try {
|
|
1024
|
-
text = JSON.stringify(msg, null,
|
|
1024
|
+
text = JSON.stringify(msg, null, 4);
|
|
1025
1025
|
} catch(e) {
|
|
1026
1026
|
text = String(msg);
|
|
1027
1027
|
}
|
|
@@ -1248,7 +1248,7 @@ function traffic_to_text()
|
|
|
1248
1248
|
out.push(head);
|
|
1249
1249
|
let payload = e.kw ? e.kw : e.jn;
|
|
1250
1250
|
try {
|
|
1251
|
-
out.push(JSON.stringify(payload, null,
|
|
1251
|
+
out.push(JSON.stringify(payload, null, 4));
|
|
1252
1252
|
} catch(err) {
|
|
1253
1253
|
out.push(String(payload));
|
|
1254
1254
|
}
|
package/src/yui_icons.css
CHANGED
|
@@ -336,3 +336,16 @@
|
|
|
336
336
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath d='M267.5 440.6c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-320c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4l-192 160L64 241l0-145c0-17.7-14.3-32-32-32S0 78.3 0 96L0 416c0 17.7 14.3 32 32 32s32-14.3 32-32l0-145 11.5 9.6 192 160z'/%3E%3C/svg%3E");
|
|
337
337
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath d='M267.5 440.6c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-320c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4l-192 160L64 241l0-145c0-17.7-14.3-32-32-32S0 78.3 0 96L0 416c0 17.7 14.3 32 32 32s32-14.3 32-32l0-145 11.5 9.6 192 160z'/%3E%3C/svg%3E");
|
|
338
338
|
}
|
|
339
|
+
|
|
340
|
+
/* yi-bolt / yi-droplet: hand-drawn masks. Added for the node-tree
|
|
341
|
+
* demo, but they earn their place in the set on their own: energy and
|
|
342
|
+
* water are two of the meter families the AMR apps read. */
|
|
343
|
+
.yi-bolt::before {
|
|
344
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M300 0 60 288l140 0-50 224 240-288-140 0z'/%3E%3C/svg%3E");
|
|
345
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M300 0 60 288l140 0-50 224 240-288-140 0z'/%3E%3C/svg%3E");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.yi-droplet::before {
|
|
349
|
+
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath d='M192 0C192 0 48 176 48 304a144 144 0 1 0 288 0C336 176 192 0 192 0z'/%3E%3C/svg%3E");
|
|
350
|
+
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath d='M192 0C192 0 48 176 48 304a144 144 0 1 0 288 0C336 176 192 0 192 0z'/%3E%3C/svg%3E");
|
|
351
|
+
}
|