@yuneta/gobj-ui 7.26.38 → 7.26.40
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 +36 -0
- package/dist/gobj-ui.cjs.js +93 -37
- package/dist/gobj-ui.es.js +93 -37
- package/package.json +1 -1
- package/src/c_yui_form.js +26 -3
- package/src/c_yui_uplot.js +8 -58
- package/src/form_close_asks_for_edits.wiring.test.js +197 -0
- package/src/uplot_ink.test.js +105 -0
- package/src/yui_uplot_ink.js +94 -0
package/README.md
CHANGED
|
@@ -3394,6 +3394,42 @@ the middle, and **a toolbar left with a single group is centred**. An unknown
|
|
|
3394
3394
|
name is reported, not silently dropped: a typo would otherwise remove the save
|
|
3395
3395
|
button with no trace of why.
|
|
3396
3396
|
|
|
3397
|
+
### Charts — the ink of the axes, on both schemes
|
|
3398
|
+
|
|
3399
|
+
uPlot is built for a white page: its axis labels are black, its grid 7% black.
|
|
3400
|
+
`C_YUI_UPLOT` gives its axes the ink of the theme -- both of them: uPlot always
|
|
3401
|
+
draws an X and a Y axis, and until 7.26.40 the Y axis kept uPlot's black, which
|
|
3402
|
+
no one could read on the dark scheme. A uPlot built by the app itself takes the
|
|
3403
|
+
same axes from `yui_uplot_axes()`, and redraws on a change of the theme
|
|
3404
|
+
(`yui_watch_theme(gobj)` sends `EV_THEME`; its action calls `chart.redraw()`):
|
|
3405
|
+
|
|
3406
|
+
```js
|
|
3407
|
+
import {yui_uplot_axes} from "@yuneta/gobj-ui/src/yui_uplot_ink.js";
|
|
3408
|
+
|
|
3409
|
+
new uPlot({width: 600, height: 300, series: [{}, {label: "temperature"}],
|
|
3410
|
+
axes: yui_uplot_axes([{}])}, data, $el);
|
|
3411
|
+
```
|
|
3412
|
+
|
|
3413
|
+
### `C_YUI_FORM` — what closing it asks about
|
|
3414
|
+
|
|
3415
|
+
`changes` says the form is not what is stored: the reader changed it, or the
|
|
3416
|
+
record lacks a field that has a default, and the form shows the default. The
|
|
3417
|
+
save lights with it. **`edited`** (7.26.39+) says the READER changed it --
|
|
3418
|
+
typed, cleared, pasted -- since it was loaded, saved or undone, and it is the
|
|
3419
|
+
only thing a close asks about: the form answers `EV_WINDOW_TO_CLOSE` with
|
|
3420
|
+
`abort_close` and the warning *"all changes will be lost"* when `edited` is
|
|
3421
|
+
true. A record that lacks a defaulted field opens changed and closes without
|
|
3422
|
+
a question; until 7.26.39 it asked on every close, about nothing the reader
|
|
3423
|
+
would lose.
|
|
3424
|
+
|
|
3425
|
+
```js
|
|
3426
|
+
const kw = {abort_close: false};
|
|
3427
|
+
gobj_send_event(form, "EV_WINDOW_TO_CLOSE", kw, gobj);
|
|
3428
|
+
if(kw.abort_close) {
|
|
3429
|
+
// the reader changed it: ask kw.warning before closing
|
|
3430
|
+
}
|
|
3431
|
+
```
|
|
3432
|
+
|
|
3397
3433
|
### `C_YUI_FORM` — which field is editable, and the one `writable` does not govern
|
|
3398
3434
|
|
|
3399
3435
|
A form opened to LOOK at a record has no editable field: that is a property of
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -9483,6 +9483,7 @@ var attrs_table$17 = [
|
|
|
9483
9483
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "form_id", 0, "", "Form identifier"),
|
|
9484
9484
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "initialized", 0, false, "Indicates if it is initialized"),
|
|
9485
9485
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "changes", 0, false, "Indicates if changes have been made"),
|
|
9486
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "edited", 0, false, "The reader changed the form (typed, cleared, pasted) since it was loaded, saved or undone: what closing it would lose. A field the record lacks, filled with its default, is a change of the form (`changes`) and not of the reader"),
|
|
9486
9487
|
(0, _yuneta_gobj_js.SDATA_END)()
|
|
9487
9488
|
];
|
|
9488
9489
|
var PRIVATE_DATA$17 = {
|
|
@@ -9576,6 +9577,7 @@ function set_changed_stated(gobj, changed) {
|
|
|
9576
9577
|
});
|
|
9577
9578
|
} else {
|
|
9578
9579
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "changes", false);
|
|
9580
|
+
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "edited", false);
|
|
9579
9581
|
enable($button_save, false, OFF_COLOR);
|
|
9580
9582
|
enable($button_undo, false, OFF_COLOR);
|
|
9581
9583
|
if (gobj.priv.rearm_timer) clearTimeout(gobj.priv.rearm_timer);
|
|
@@ -9599,6 +9601,16 @@ function set_changed_stated(gobj, changed) {
|
|
|
9599
9601
|
}
|
|
9600
9602
|
}
|
|
9601
9603
|
/************************************************************
|
|
9604
|
+
* A change the READER made -- typed, cleared, pasted: what
|
|
9605
|
+
* closing the form would lose (ac_window_to_close). Clearing
|
|
9606
|
+
* and pasting wrote their values and marked nothing, so a form
|
|
9607
|
+
* cleared or pasted into kept its save off.
|
|
9608
|
+
************************************************************/
|
|
9609
|
+
function set_edited_by_reader(gobj) {
|
|
9610
|
+
set_changed_stated(gobj, true);
|
|
9611
|
+
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "edited", true);
|
|
9612
|
+
}
|
|
9613
|
+
/************************************************************
|
|
9602
9614
|
*
|
|
9603
9615
|
************************************************************/
|
|
9604
9616
|
async function readClipboard$1(gobj) {
|
|
@@ -11980,10 +11992,16 @@ function form_value_2_treedb_value(gobj, field_desc, value) {
|
|
|
11980
11992
|
* Actions
|
|
11981
11993
|
***************************/
|
|
11982
11994
|
/************************************************************
|
|
11983
|
-
* From Window parent, is going to close
|
|
11995
|
+
* From Window parent, is going to close.
|
|
11996
|
+
*
|
|
11997
|
+
* It asks only for what the READER changed (`edited`). A record
|
|
11998
|
+
* that lacks a field opens CHANGED -- the default fills it, and
|
|
11999
|
+
* the save lights to store it -- but the reader did nothing, and
|
|
12000
|
+
* the question asked of every such record, on every close, was
|
|
12001
|
+
* about nothing they would lose.
|
|
11984
12002
|
************************************************************/
|
|
11985
12003
|
function ac_window_to_close(gobj, event, kw, src) {
|
|
11986
|
-
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "
|
|
12004
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "edited")) {
|
|
11987
12005
|
kw.abort_close = true;
|
|
11988
12006
|
kw.warning = "all changes will be lost";
|
|
11989
12007
|
}
|
|
@@ -11999,6 +12017,7 @@ function ac_load_record(gobj, event, kw, src) {
|
|
|
11999
12017
|
let modified = set_form_values(gobj, (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "template"), $form, kw);
|
|
12000
12018
|
apply_form_mode(gobj, $form);
|
|
12001
12019
|
set_changed_stated(gobj, modified);
|
|
12020
|
+
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "edited", false);
|
|
12002
12021
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "initialized", true);
|
|
12003
12022
|
return 0;
|
|
12004
12023
|
}
|
|
@@ -12042,6 +12061,7 @@ function ac_clear_record(gobj, event, kw, src) {
|
|
|
12042
12061
|
let $form = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container").querySelector("form");
|
|
12043
12062
|
clear_validation(gobj, $form);
|
|
12044
12063
|
clear_data(gobj, $form);
|
|
12064
|
+
set_edited_by_reader(gobj);
|
|
12045
12065
|
return 0;
|
|
12046
12066
|
}
|
|
12047
12067
|
/************************************************************
|
|
@@ -12120,7 +12140,7 @@ function ac_add_table_row(gobj, event, kw, src) {
|
|
|
12120
12140
|
* Internal event
|
|
12121
12141
|
************************************************************/
|
|
12122
12142
|
function ac_record_changed(gobj, event, kw, src) {
|
|
12123
|
-
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "initialized"))
|
|
12143
|
+
if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "initialized")) set_edited_by_reader(gobj);
|
|
12124
12144
|
return 0;
|
|
12125
12145
|
}
|
|
12126
12146
|
/************************************************************
|
|
@@ -12143,6 +12163,7 @@ function ac_paste_record(gobj, event, kw, src) {
|
|
|
12143
12163
|
if ((0, _yuneta_gobj_js.is_object)(record)) {
|
|
12144
12164
|
(0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "initialized", true);
|
|
12145
12165
|
set_form_values(gobj, (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "template"), $form, record);
|
|
12166
|
+
set_edited_by_reader(gobj);
|
|
12146
12167
|
}
|
|
12147
12168
|
return 0;
|
|
12148
12169
|
}
|
|
@@ -13375,41 +13396,58 @@ function register_c_yui_map() {
|
|
|
13375
13396
|
return create_gclass$16(GCLASS_NAME$16);
|
|
13376
13397
|
}
|
|
13377
13398
|
//#endregion
|
|
13378
|
-
//#region src/
|
|
13399
|
+
//#region src/yui_uplot_ink.js
|
|
13379
13400
|
/***********************************************************************
|
|
13380
|
-
*
|
|
13401
|
+
* yui_uplot_ink.js
|
|
13381
13402
|
*
|
|
13382
|
-
*
|
|
13403
|
+
* The ink of a uPlot chart, on both schemes, and its palette.
|
|
13383
13404
|
*
|
|
13384
|
-
*
|
|
13405
|
+
* uPlot is built for a white page: its axis labels are `#000`
|
|
13406
|
+
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
13407
|
+
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
13408
|
+
* not there. The inks here are handed to uPlot as FUNCTIONS: it
|
|
13409
|
+
* calls them when it draws, so the value can never be stale --
|
|
13410
|
+
* what goes stale is the canvas, and a change of the theme has to
|
|
13411
|
+
* ask for a redraw (yui_watch_theme() turns it into an event).
|
|
13412
|
+
*
|
|
13413
|
+
* uPlot ALWAYS draws a Y axis: its setDefaults() makes axes[1]
|
|
13414
|
+
* whether it was given or not, with its own black. So the axes are
|
|
13415
|
+
* padded to two before they are inked. C_YUI_UPLOT's default is
|
|
13416
|
+
* `[{}]`, the X axis alone, and until 7.26.40 every chart of the
|
|
13417
|
+
* library had a Y axis whose values could not be read in the dark.
|
|
13418
|
+
*
|
|
13419
|
+
* C_YUI_UPLOT uses it, and so can an app that builds a uPlot of its
|
|
13420
|
+
* own:
|
|
13421
|
+
*
|
|
13422
|
+
* import {yui_uplot_axes} from "@yuneta/gobj-ui/src/yui_uplot_ink.js";
|
|
13423
|
+
*
|
|
13424
|
+
* const chart = new uPlot({
|
|
13425
|
+
* width: 600, height: 300,
|
|
13426
|
+
* series: [{}, {label: "temperature"}],
|
|
13427
|
+
* axes: yui_uplot_axes([{}]), // X and Y, with the ink of the theme
|
|
13428
|
+
* }, data, $el);
|
|
13429
|
+
*
|
|
13430
|
+
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
13431
|
+
* (worst 3.39 on white, 3.71 on near-black), ordered so the first
|
|
13432
|
+
* two are the pair that survives colour blindness -- blue and
|
|
13433
|
+
* orange, never red and green as the opening pair.
|
|
13434
|
+
*
|
|
13435
|
+
* Copyright (c) 2026, ArtGins.
|
|
13385
13436
|
* All Rights Reserved.
|
|
13386
13437
|
***********************************************************************/
|
|
13387
|
-
|
|
13388
|
-
* The ink of
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
13392
|
-
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
13393
|
-
* not there. And the series were CSS colour names: `blue` is
|
|
13394
|
-
* 2.11:1 on the dark ground, `orange` 1.97 on the light one, so
|
|
13395
|
-
* whichever scheme you were in, one of the lines was a rumour.
|
|
13396
|
-
*
|
|
13397
|
-
* These are given to uPlot as FUNCTIONS: it calls them when it
|
|
13398
|
-
* draws, so the value can never be stale -- what goes stale is
|
|
13399
|
-
* the canvas, and that is what EV_THEME redraws.
|
|
13400
|
-
*
|
|
13401
|
-
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
13402
|
-
* (worst 3.39 on white, 3.71 on near-black), ordered so the
|
|
13403
|
-
* first two are the pair that survives colour blindness -- blue
|
|
13404
|
-
* and orange, never red and green as the opening pair.
|
|
13405
|
-
***************************************************************/
|
|
13406
|
-
function axis_ink() {
|
|
13438
|
+
/************************************************************
|
|
13439
|
+
* The ink of the labels of an axis.
|
|
13440
|
+
************************************************************/
|
|
13441
|
+
function yui_uplot_axis_ink() {
|
|
13407
13442
|
return yui_is_dark() ? "#c7cdd8" : "#2e333d";
|
|
13408
13443
|
}
|
|
13409
|
-
|
|
13444
|
+
/************************************************************
|
|
13445
|
+
* The ink of the grid and of the ticks.
|
|
13446
|
+
************************************************************/
|
|
13447
|
+
function yui_uplot_grid_ink() {
|
|
13410
13448
|
return yui_is_dark() ? "rgba(199,205,216,0.16)" : "rgba(46,51,61,0.12)";
|
|
13411
13449
|
}
|
|
13412
|
-
var
|
|
13450
|
+
var YUI_UPLOT_PALETTE = [
|
|
13413
13451
|
{
|
|
13414
13452
|
stroke: "#2f6fd0",
|
|
13415
13453
|
fill: "rgba(47,111,208,0.10)"
|
|
@@ -13435,15 +13473,33 @@ var CHART_PALETTE = [
|
|
|
13435
13473
|
fill: "rgba(209,52,75,0.10)"
|
|
13436
13474
|
}
|
|
13437
13475
|
];
|
|
13438
|
-
|
|
13439
|
-
|
|
13476
|
+
/************************************************************
|
|
13477
|
+
* The axes the host asked for -- at least the X and the Y,
|
|
13478
|
+
* the two uPlot always draws -- with the ink it did not ask
|
|
13479
|
+
* about. A stroke, a grid or a ticks the host gave is kept.
|
|
13480
|
+
* Answers new objects: the host's are not touched.
|
|
13481
|
+
************************************************************/
|
|
13482
|
+
function yui_uplot_axes(axes) {
|
|
13483
|
+
let list = Array.isArray(axes) ? axes.slice() : [axes || {}];
|
|
13484
|
+
while (list.length < 2) list.push({});
|
|
13485
|
+
return list.map((ax) => {
|
|
13440
13486
|
let a = Object.assign({}, ax || {});
|
|
13441
|
-
if (a.stroke === void 0) a.stroke =
|
|
13442
|
-
a.grid = Object.assign({ stroke:
|
|
13443
|
-
a.ticks = Object.assign({ stroke:
|
|
13487
|
+
if (a.stroke === void 0) a.stroke = yui_uplot_axis_ink;
|
|
13488
|
+
a.grid = Object.assign({ stroke: yui_uplot_grid_ink }, a.grid || {});
|
|
13489
|
+
a.ticks = Object.assign({ stroke: yui_uplot_grid_ink }, a.ticks || {});
|
|
13444
13490
|
return a;
|
|
13445
13491
|
});
|
|
13446
13492
|
}
|
|
13493
|
+
//#endregion
|
|
13494
|
+
//#region src/c_yui_uplot.js
|
|
13495
|
+
/***********************************************************************
|
|
13496
|
+
* c_yui_uplot.js
|
|
13497
|
+
*
|
|
13498
|
+
* Graphics, Charts with uPlot
|
|
13499
|
+
*
|
|
13500
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
13501
|
+
* All Rights Reserved.
|
|
13502
|
+
***********************************************************************/
|
|
13447
13503
|
/***************************************************************
|
|
13448
13504
|
* Constants
|
|
13449
13505
|
***************************************************************/
|
|
@@ -13461,7 +13517,7 @@ var attrs_table$15 = [
|
|
|
13461
13517
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "axes", 0, [{}], "Chart axes"),
|
|
13462
13518
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "scales", 0, {}, "Chart scales"),
|
|
13463
13519
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "data", 0, [[]], "Chart data"),
|
|
13464
|
-
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "colors", 0,
|
|
13520
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "colors", 0, YUI_UPLOT_PALETTE, "Series colours, in order. The default palette clears 3:1 on both schemes and opens with the colour-blind-safe pair (blue, orange)"),
|
|
13465
13521
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for the chart"),
|
|
13466
13522
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "own_container", 0, false, "If true, container is gobj-managed"),
|
|
13467
13523
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "uplot", 0, null, "uPlot instance"),
|
|
@@ -13528,7 +13584,7 @@ function build_ui$14(gobj) {
|
|
|
13528
13584
|
width: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "width"),
|
|
13529
13585
|
height: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "height"),
|
|
13530
13586
|
series: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "series"),
|
|
13531
|
-
axes:
|
|
13587
|
+
axes: yui_uplot_axes((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "axes")),
|
|
13532
13588
|
scales: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "scales")
|
|
13533
13589
|
};
|
|
13534
13590
|
let uplot$2 = new uplot.default(uPlot_options, (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "data"), $container);
|
|
@@ -13629,7 +13685,7 @@ function ac_add_serie(gobj, event, kw, src) {
|
|
|
13629
13685
|
}
|
|
13630
13686
|
}
|
|
13631
13687
|
let colors = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "colors");
|
|
13632
|
-
if (!Array.isArray(colors) || !colors.length) colors =
|
|
13688
|
+
if (!Array.isArray(colors) || !colors.length) colors = YUI_UPLOT_PALETTE;
|
|
13633
13689
|
let color = colors[Math.max(0, idx - 1) % colors.length];
|
|
13634
13690
|
if (kw.stroke === void 0) kw.stroke = color.stroke;
|
|
13635
13691
|
if (kw.fill === void 0) kw.fill = color.fill;
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -9478,6 +9478,7 @@ var attrs_table$17 = [
|
|
|
9478
9478
|
SDATA(data_type_t.DTP_STRING, "form_id", 0, "", "Form identifier"),
|
|
9479
9479
|
SDATA(data_type_t.DTP_BOOLEAN, "initialized", 0, false, "Indicates if it is initialized"),
|
|
9480
9480
|
SDATA(data_type_t.DTP_BOOLEAN, "changes", 0, false, "Indicates if changes have been made"),
|
|
9481
|
+
SDATA(data_type_t.DTP_BOOLEAN, "edited", 0, false, "The reader changed the form (typed, cleared, pasted) since it was loaded, saved or undone: what closing it would lose. A field the record lacks, filled with its default, is a change of the form (`changes`) and not of the reader"),
|
|
9481
9482
|
SDATA_END()
|
|
9482
9483
|
];
|
|
9483
9484
|
var PRIVATE_DATA$17 = {
|
|
@@ -9571,6 +9572,7 @@ function set_changed_stated(gobj, changed) {
|
|
|
9571
9572
|
});
|
|
9572
9573
|
} else {
|
|
9573
9574
|
gobj_write_bool_attr(gobj, "changes", false);
|
|
9575
|
+
gobj_write_bool_attr(gobj, "edited", false);
|
|
9574
9576
|
enable($button_save, false, OFF_COLOR);
|
|
9575
9577
|
enable($button_undo, false, OFF_COLOR);
|
|
9576
9578
|
if (gobj.priv.rearm_timer) clearTimeout(gobj.priv.rearm_timer);
|
|
@@ -9594,6 +9596,16 @@ function set_changed_stated(gobj, changed) {
|
|
|
9594
9596
|
}
|
|
9595
9597
|
}
|
|
9596
9598
|
/************************************************************
|
|
9599
|
+
* A change the READER made -- typed, cleared, pasted: what
|
|
9600
|
+
* closing the form would lose (ac_window_to_close). Clearing
|
|
9601
|
+
* and pasting wrote their values and marked nothing, so a form
|
|
9602
|
+
* cleared or pasted into kept its save off.
|
|
9603
|
+
************************************************************/
|
|
9604
|
+
function set_edited_by_reader(gobj) {
|
|
9605
|
+
set_changed_stated(gobj, true);
|
|
9606
|
+
gobj_write_bool_attr(gobj, "edited", true);
|
|
9607
|
+
}
|
|
9608
|
+
/************************************************************
|
|
9597
9609
|
*
|
|
9598
9610
|
************************************************************/
|
|
9599
9611
|
async function readClipboard$1(gobj) {
|
|
@@ -11975,10 +11987,16 @@ function form_value_2_treedb_value(gobj, field_desc, value) {
|
|
|
11975
11987
|
* Actions
|
|
11976
11988
|
***************************/
|
|
11977
11989
|
/************************************************************
|
|
11978
|
-
* From Window parent, is going to close
|
|
11990
|
+
* From Window parent, is going to close.
|
|
11991
|
+
*
|
|
11992
|
+
* It asks only for what the READER changed (`edited`). A record
|
|
11993
|
+
* that lacks a field opens CHANGED -- the default fills it, and
|
|
11994
|
+
* the save lights to store it -- but the reader did nothing, and
|
|
11995
|
+
* the question asked of every such record, on every close, was
|
|
11996
|
+
* about nothing they would lose.
|
|
11979
11997
|
************************************************************/
|
|
11980
11998
|
function ac_window_to_close(gobj, event, kw, src) {
|
|
11981
|
-
if (gobj_read_bool_attr(gobj, "
|
|
11999
|
+
if (gobj_read_bool_attr(gobj, "edited")) {
|
|
11982
12000
|
kw.abort_close = true;
|
|
11983
12001
|
kw.warning = "all changes will be lost";
|
|
11984
12002
|
}
|
|
@@ -11994,6 +12012,7 @@ function ac_load_record(gobj, event, kw, src) {
|
|
|
11994
12012
|
let modified = set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, kw);
|
|
11995
12013
|
apply_form_mode(gobj, $form);
|
|
11996
12014
|
set_changed_stated(gobj, modified);
|
|
12015
|
+
gobj_write_bool_attr(gobj, "edited", false);
|
|
11997
12016
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
11998
12017
|
return 0;
|
|
11999
12018
|
}
|
|
@@ -12037,6 +12056,7 @@ function ac_clear_record(gobj, event, kw, src) {
|
|
|
12037
12056
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
12038
12057
|
clear_validation(gobj, $form);
|
|
12039
12058
|
clear_data(gobj, $form);
|
|
12059
|
+
set_edited_by_reader(gobj);
|
|
12040
12060
|
return 0;
|
|
12041
12061
|
}
|
|
12042
12062
|
/************************************************************
|
|
@@ -12115,7 +12135,7 @@ function ac_add_table_row(gobj, event, kw, src) {
|
|
|
12115
12135
|
* Internal event
|
|
12116
12136
|
************************************************************/
|
|
12117
12137
|
function ac_record_changed(gobj, event, kw, src) {
|
|
12118
|
-
if (gobj_read_bool_attr(gobj, "initialized"))
|
|
12138
|
+
if (gobj_read_bool_attr(gobj, "initialized")) set_edited_by_reader(gobj);
|
|
12119
12139
|
return 0;
|
|
12120
12140
|
}
|
|
12121
12141
|
/************************************************************
|
|
@@ -12138,6 +12158,7 @@ function ac_paste_record(gobj, event, kw, src) {
|
|
|
12138
12158
|
if (is_object(record)) {
|
|
12139
12159
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
12140
12160
|
set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, record);
|
|
12161
|
+
set_edited_by_reader(gobj);
|
|
12141
12162
|
}
|
|
12142
12163
|
return 0;
|
|
12143
12164
|
}
|
|
@@ -13370,41 +13391,58 @@ function register_c_yui_map() {
|
|
|
13370
13391
|
return create_gclass$16(GCLASS_NAME$16);
|
|
13371
13392
|
}
|
|
13372
13393
|
//#endregion
|
|
13373
|
-
//#region src/
|
|
13394
|
+
//#region src/yui_uplot_ink.js
|
|
13374
13395
|
/***********************************************************************
|
|
13375
|
-
*
|
|
13396
|
+
* yui_uplot_ink.js
|
|
13376
13397
|
*
|
|
13377
|
-
*
|
|
13398
|
+
* The ink of a uPlot chart, on both schemes, and its palette.
|
|
13378
13399
|
*
|
|
13379
|
-
*
|
|
13400
|
+
* uPlot is built for a white page: its axis labels are `#000`
|
|
13401
|
+
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
13402
|
+
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
13403
|
+
* not there. The inks here are handed to uPlot as FUNCTIONS: it
|
|
13404
|
+
* calls them when it draws, so the value can never be stale --
|
|
13405
|
+
* what goes stale is the canvas, and a change of the theme has to
|
|
13406
|
+
* ask for a redraw (yui_watch_theme() turns it into an event).
|
|
13407
|
+
*
|
|
13408
|
+
* uPlot ALWAYS draws a Y axis: its setDefaults() makes axes[1]
|
|
13409
|
+
* whether it was given or not, with its own black. So the axes are
|
|
13410
|
+
* padded to two before they are inked. C_YUI_UPLOT's default is
|
|
13411
|
+
* `[{}]`, the X axis alone, and until 7.26.40 every chart of the
|
|
13412
|
+
* library had a Y axis whose values could not be read in the dark.
|
|
13413
|
+
*
|
|
13414
|
+
* C_YUI_UPLOT uses it, and so can an app that builds a uPlot of its
|
|
13415
|
+
* own:
|
|
13416
|
+
*
|
|
13417
|
+
* import {yui_uplot_axes} from "@yuneta/gobj-ui/src/yui_uplot_ink.js";
|
|
13418
|
+
*
|
|
13419
|
+
* const chart = new uPlot({
|
|
13420
|
+
* width: 600, height: 300,
|
|
13421
|
+
* series: [{}, {label: "temperature"}],
|
|
13422
|
+
* axes: yui_uplot_axes([{}]), // X and Y, with the ink of the theme
|
|
13423
|
+
* }, data, $el);
|
|
13424
|
+
*
|
|
13425
|
+
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
13426
|
+
* (worst 3.39 on white, 3.71 on near-black), ordered so the first
|
|
13427
|
+
* two are the pair that survives colour blindness -- blue and
|
|
13428
|
+
* orange, never red and green as the opening pair.
|
|
13429
|
+
*
|
|
13430
|
+
* Copyright (c) 2026, ArtGins.
|
|
13380
13431
|
* All Rights Reserved.
|
|
13381
13432
|
***********************************************************************/
|
|
13382
|
-
|
|
13383
|
-
* The ink of
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
13387
|
-
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
13388
|
-
* not there. And the series were CSS colour names: `blue` is
|
|
13389
|
-
* 2.11:1 on the dark ground, `orange` 1.97 on the light one, so
|
|
13390
|
-
* whichever scheme you were in, one of the lines was a rumour.
|
|
13391
|
-
*
|
|
13392
|
-
* These are given to uPlot as FUNCTIONS: it calls them when it
|
|
13393
|
-
* draws, so the value can never be stale -- what goes stale is
|
|
13394
|
-
* the canvas, and that is what EV_THEME redraws.
|
|
13395
|
-
*
|
|
13396
|
-
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
13397
|
-
* (worst 3.39 on white, 3.71 on near-black), ordered so the
|
|
13398
|
-
* first two are the pair that survives colour blindness -- blue
|
|
13399
|
-
* and orange, never red and green as the opening pair.
|
|
13400
|
-
***************************************************************/
|
|
13401
|
-
function axis_ink() {
|
|
13433
|
+
/************************************************************
|
|
13434
|
+
* The ink of the labels of an axis.
|
|
13435
|
+
************************************************************/
|
|
13436
|
+
function yui_uplot_axis_ink() {
|
|
13402
13437
|
return yui_is_dark() ? "#c7cdd8" : "#2e333d";
|
|
13403
13438
|
}
|
|
13404
|
-
|
|
13439
|
+
/************************************************************
|
|
13440
|
+
* The ink of the grid and of the ticks.
|
|
13441
|
+
************************************************************/
|
|
13442
|
+
function yui_uplot_grid_ink() {
|
|
13405
13443
|
return yui_is_dark() ? "rgba(199,205,216,0.16)" : "rgba(46,51,61,0.12)";
|
|
13406
13444
|
}
|
|
13407
|
-
var
|
|
13445
|
+
var YUI_UPLOT_PALETTE = [
|
|
13408
13446
|
{
|
|
13409
13447
|
stroke: "#2f6fd0",
|
|
13410
13448
|
fill: "rgba(47,111,208,0.10)"
|
|
@@ -13430,15 +13468,33 @@ var CHART_PALETTE = [
|
|
|
13430
13468
|
fill: "rgba(209,52,75,0.10)"
|
|
13431
13469
|
}
|
|
13432
13470
|
];
|
|
13433
|
-
|
|
13434
|
-
|
|
13471
|
+
/************************************************************
|
|
13472
|
+
* The axes the host asked for -- at least the X and the Y,
|
|
13473
|
+
* the two uPlot always draws -- with the ink it did not ask
|
|
13474
|
+
* about. A stroke, a grid or a ticks the host gave is kept.
|
|
13475
|
+
* Answers new objects: the host's are not touched.
|
|
13476
|
+
************************************************************/
|
|
13477
|
+
function yui_uplot_axes(axes) {
|
|
13478
|
+
let list = Array.isArray(axes) ? axes.slice() : [axes || {}];
|
|
13479
|
+
while (list.length < 2) list.push({});
|
|
13480
|
+
return list.map((ax) => {
|
|
13435
13481
|
let a = Object.assign({}, ax || {});
|
|
13436
|
-
if (a.stroke === void 0) a.stroke =
|
|
13437
|
-
a.grid = Object.assign({ stroke:
|
|
13438
|
-
a.ticks = Object.assign({ stroke:
|
|
13482
|
+
if (a.stroke === void 0) a.stroke = yui_uplot_axis_ink;
|
|
13483
|
+
a.grid = Object.assign({ stroke: yui_uplot_grid_ink }, a.grid || {});
|
|
13484
|
+
a.ticks = Object.assign({ stroke: yui_uplot_grid_ink }, a.ticks || {});
|
|
13439
13485
|
return a;
|
|
13440
13486
|
});
|
|
13441
13487
|
}
|
|
13488
|
+
//#endregion
|
|
13489
|
+
//#region src/c_yui_uplot.js
|
|
13490
|
+
/***********************************************************************
|
|
13491
|
+
* c_yui_uplot.js
|
|
13492
|
+
*
|
|
13493
|
+
* Graphics, Charts with uPlot
|
|
13494
|
+
*
|
|
13495
|
+
* Copyright (c) 2025-2026, ArtGins.
|
|
13496
|
+
* All Rights Reserved.
|
|
13497
|
+
***********************************************************************/
|
|
13442
13498
|
/***************************************************************
|
|
13443
13499
|
* Constants
|
|
13444
13500
|
***************************************************************/
|
|
@@ -13456,7 +13512,7 @@ var attrs_table$15 = [
|
|
|
13456
13512
|
SDATA(data_type_t.DTP_JSON, "axes", 0, [{}], "Chart axes"),
|
|
13457
13513
|
SDATA(data_type_t.DTP_JSON, "scales", 0, {}, "Chart scales"),
|
|
13458
13514
|
SDATA(data_type_t.DTP_JSON, "data", 0, [[]], "Chart data"),
|
|
13459
|
-
SDATA(data_type_t.DTP_JSON, "colors", 0,
|
|
13515
|
+
SDATA(data_type_t.DTP_JSON, "colors", 0, YUI_UPLOT_PALETTE, "Series colours, in order. The default palette clears 3:1 on both schemes and opens with the colour-blind-safe pair (blue, orange)"),
|
|
13460
13516
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for the chart"),
|
|
13461
13517
|
SDATA(data_type_t.DTP_BOOLEAN, "own_container", 0, false, "If true, container is gobj-managed"),
|
|
13462
13518
|
SDATA(data_type_t.DTP_POINTER, "uplot", 0, null, "uPlot instance"),
|
|
@@ -13523,7 +13579,7 @@ function build_ui$14(gobj) {
|
|
|
13523
13579
|
width: gobj_read_attr(gobj, "width"),
|
|
13524
13580
|
height: gobj_read_attr(gobj, "height"),
|
|
13525
13581
|
series: gobj_read_attr(gobj, "series"),
|
|
13526
|
-
axes:
|
|
13582
|
+
axes: yui_uplot_axes(gobj_read_attr(gobj, "axes")),
|
|
13527
13583
|
scales: gobj_read_attr(gobj, "scales")
|
|
13528
13584
|
};
|
|
13529
13585
|
let uplot = new uPlot(uPlot_options, gobj_read_attr(gobj, "data"), $container);
|
|
@@ -13624,7 +13680,7 @@ function ac_add_serie(gobj, event, kw, src) {
|
|
|
13624
13680
|
}
|
|
13625
13681
|
}
|
|
13626
13682
|
let colors = gobj_read_attr(gobj, "colors");
|
|
13627
|
-
if (!Array.isArray(colors) || !colors.length) colors =
|
|
13683
|
+
if (!Array.isArray(colors) || !colors.length) colors = YUI_UPLOT_PALETTE;
|
|
13628
13684
|
let color = colors[Math.max(0, idx - 1) % colors.length];
|
|
13629
13685
|
if (kw.stroke === void 0) kw.stroke = color.stroke;
|
|
13630
13686
|
if (kw.fill === void 0) kw.fill = color.fill;
|
package/package.json
CHANGED
package/src/c_yui_form.js
CHANGED
|
@@ -167,6 +167,7 @@ SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Container eleme
|
|
|
167
167
|
SDATA(data_type_t.DTP_STRING, "form_id", 0, "", "Form identifier"),
|
|
168
168
|
SDATA(data_type_t.DTP_BOOLEAN, "initialized", 0, false, "Indicates if it is initialized"),
|
|
169
169
|
SDATA(data_type_t.DTP_BOOLEAN, "changes", 0, false, "Indicates if changes have been made"),
|
|
170
|
+
SDATA(data_type_t.DTP_BOOLEAN, "edited", 0, false, "The reader changed the form (typed, cleared, pasted) since it was loaded, saved or undone: what closing it would lose. A field the record lacks, filled with its default, is a change of the form (`changes`) and not of the reader"),
|
|
170
171
|
SDATA_END()
|
|
171
172
|
];
|
|
172
173
|
|
|
@@ -349,6 +350,7 @@ function set_changed_stated(gobj, changed)
|
|
|
349
350
|
* Data not changed, disable buttons to save/undo
|
|
350
351
|
*/
|
|
351
352
|
gobj_write_bool_attr(gobj, "changes", false);
|
|
353
|
+
gobj_write_bool_attr(gobj, "edited", false);
|
|
352
354
|
|
|
353
355
|
enable($button_save, false, OFF_COLOR);
|
|
354
356
|
enable($button_undo, false, OFF_COLOR);
|
|
@@ -390,6 +392,18 @@ function set_changed_stated(gobj, changed)
|
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
394
|
|
|
395
|
+
/************************************************************
|
|
396
|
+
* A change the READER made -- typed, cleared, pasted: what
|
|
397
|
+
* closing the form would lose (ac_window_to_close). Clearing
|
|
398
|
+
* and pasting wrote their values and marked nothing, so a form
|
|
399
|
+
* cleared or pasted into kept its save off.
|
|
400
|
+
************************************************************/
|
|
401
|
+
function set_edited_by_reader(gobj)
|
|
402
|
+
{
|
|
403
|
+
set_changed_stated(gobj, true);
|
|
404
|
+
gobj_write_bool_attr(gobj, "edited", true);
|
|
405
|
+
}
|
|
406
|
+
|
|
393
407
|
/************************************************************
|
|
394
408
|
*
|
|
395
409
|
************************************************************/
|
|
@@ -3606,11 +3620,17 @@ function form_value_2_treedb_value(gobj, field_desc, value)
|
|
|
3606
3620
|
|
|
3607
3621
|
|
|
3608
3622
|
/************************************************************
|
|
3609
|
-
* From Window parent, is going to close
|
|
3623
|
+
* From Window parent, is going to close.
|
|
3624
|
+
*
|
|
3625
|
+
* It asks only for what the READER changed (`edited`). A record
|
|
3626
|
+
* that lacks a field opens CHANGED -- the default fills it, and
|
|
3627
|
+
* the save lights to store it -- but the reader did nothing, and
|
|
3628
|
+
* the question asked of every such record, on every close, was
|
|
3629
|
+
* about nothing they would lose.
|
|
3610
3630
|
************************************************************/
|
|
3611
3631
|
function ac_window_to_close(gobj, event, kw, src)
|
|
3612
3632
|
{
|
|
3613
|
-
if(gobj_read_bool_attr(gobj, "
|
|
3633
|
+
if(gobj_read_bool_attr(gobj, "edited")) {
|
|
3614
3634
|
kw.abort_close = true; // Don't close the window until fields repaired
|
|
3615
3635
|
/* An i18n KEY, not a sentence: the dialog renders its message with
|
|
3616
3636
|
* `i18n: message`. The English sentence that was here was a key
|
|
@@ -3634,6 +3654,7 @@ function ac_load_record(gobj, event, kw, src)
|
|
|
3634
3654
|
let modified = set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, kw);
|
|
3635
3655
|
apply_form_mode(gobj, $form);
|
|
3636
3656
|
set_changed_stated(gobj, modified);
|
|
3657
|
+
gobj_write_bool_attr(gobj, "edited", false);
|
|
3637
3658
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
3638
3659
|
|
|
3639
3660
|
return 0;
|
|
@@ -3707,6 +3728,7 @@ function ac_clear_record(gobj, event, kw, src)
|
|
|
3707
3728
|
let $form = $container.querySelector('form');
|
|
3708
3729
|
clear_validation(gobj, $form);
|
|
3709
3730
|
clear_data(gobj, $form);
|
|
3731
|
+
set_edited_by_reader(gobj);
|
|
3710
3732
|
return 0;
|
|
3711
3733
|
}
|
|
3712
3734
|
|
|
@@ -3820,7 +3842,7 @@ function ac_add_table_row(gobj, event, kw, src)
|
|
|
3820
3842
|
function ac_record_changed(gobj, event, kw, src)
|
|
3821
3843
|
{
|
|
3822
3844
|
if(gobj_read_bool_attr(gobj, "initialized")) {
|
|
3823
|
-
|
|
3845
|
+
set_edited_by_reader(gobj);
|
|
3824
3846
|
}
|
|
3825
3847
|
return 0;
|
|
3826
3848
|
}
|
|
@@ -3854,6 +3876,7 @@ function ac_paste_record(gobj, event, kw, src)
|
|
|
3854
3876
|
if(is_object(record)) {
|
|
3855
3877
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
3856
3878
|
set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, record);
|
|
3879
|
+
set_edited_by_reader(gobj);
|
|
3857
3880
|
}
|
|
3858
3881
|
return 0;
|
|
3859
3882
|
}
|
package/src/c_yui_uplot.js
CHANGED
|
@@ -30,62 +30,12 @@ import {
|
|
|
30
30
|
import uPlot from "uplot";
|
|
31
31
|
import "uplot/dist/uPlot.min.css";
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import {yui_watch_theme} from "./yui_theme.js";
|
|
34
|
+
/* The ink of the axes -- the X and the Y -- and the palette of the
|
|
35
|
+
* series, on both schemes: see yui_uplot_ink.js. */
|
|
36
|
+
import {yui_uplot_axes, YUI_UPLOT_PALETTE} from "./yui_uplot_ink.js";
|
|
34
37
|
|
|
35
38
|
|
|
36
|
-
/***************************************************************
|
|
37
|
-
* The ink of a chart, and its palette.
|
|
38
|
-
*
|
|
39
|
-
* uPlot is built for a white page: its axis labels are `#000`
|
|
40
|
-
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
41
|
-
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
42
|
-
* not there. And the series were CSS colour names: `blue` is
|
|
43
|
-
* 2.11:1 on the dark ground, `orange` 1.97 on the light one, so
|
|
44
|
-
* whichever scheme you were in, one of the lines was a rumour.
|
|
45
|
-
*
|
|
46
|
-
* These are given to uPlot as FUNCTIONS: it calls them when it
|
|
47
|
-
* draws, so the value can never be stale -- what goes stale is
|
|
48
|
-
* the canvas, and that is what EV_THEME redraws.
|
|
49
|
-
*
|
|
50
|
-
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
51
|
-
* (worst 3.39 on white, 3.71 on near-black), ordered so the
|
|
52
|
-
* first two are the pair that survives colour blindness -- blue
|
|
53
|
-
* and orange, never red and green as the opening pair.
|
|
54
|
-
***************************************************************/
|
|
55
|
-
function axis_ink()
|
|
56
|
-
{
|
|
57
|
-
return yui_is_dark()? "#c7cdd8" : "#2e333d";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function grid_ink()
|
|
61
|
-
{
|
|
62
|
-
return yui_is_dark()? "rgba(199,205,216,0.16)" : "rgba(46,51,61,0.12)";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const CHART_PALETTE = [
|
|
66
|
-
{stroke: "#2f6fd0", fill: "rgba(47,111,208,0.10)"}, /* 4.88 / 3.71 */
|
|
67
|
-
{stroke: "#b8620a", fill: "rgba(184,98,10,0.10)"}, /* 4.38 / 4.13 */
|
|
68
|
-
{stroke: "#2f8f4e", fill: "rgba(47,143,78,0.10)"}, /* 4.07 / 4.45 */
|
|
69
|
-
{stroke: "#8b5cf6", fill: "rgba(139,92,246,0.10)"}, /* 4.23 / 4.28 */
|
|
70
|
-
{stroke: "#0e9aa7", fill: "rgba(14,154,167,0.10)"}, /* 3.39 / 5.35 */
|
|
71
|
-
{stroke: "#d1344b", fill: "rgba(209,52,75,0.10)"}, /* 4.88 / 3.71 */
|
|
72
|
-
];
|
|
73
|
-
|
|
74
|
-
/* The axes the host asked for, with the ink it did not ask about. */
|
|
75
|
-
function themed_axes(axes)
|
|
76
|
-
{
|
|
77
|
-
let list = Array.isArray(axes)? axes : [axes || {}];
|
|
78
|
-
return list.map((ax) => {
|
|
79
|
-
let a = Object.assign({}, ax || {});
|
|
80
|
-
if(a.stroke === undefined) {
|
|
81
|
-
a.stroke = axis_ink;
|
|
82
|
-
}
|
|
83
|
-
a.grid = Object.assign({stroke: grid_ink}, a.grid || {});
|
|
84
|
-
a.ticks = Object.assign({stroke: grid_ink}, a.ticks || {});
|
|
85
|
-
return a;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
39
|
/***************************************************************
|
|
90
40
|
* Constants
|
|
91
41
|
***************************************************************/
|
|
@@ -104,7 +54,7 @@ SDATA(data_type_t.DTP_JSON, "series", 0, [{}], "Data series"),
|
|
|
104
54
|
SDATA(data_type_t.DTP_JSON, "axes", 0, [{}], "Chart axes"),
|
|
105
55
|
SDATA(data_type_t.DTP_JSON, "scales", 0, {}, "Chart scales"),
|
|
106
56
|
SDATA(data_type_t.DTP_JSON, "data", 0, [[]], "Chart data"),
|
|
107
|
-
SDATA(data_type_t.DTP_JSON, "colors", 0,
|
|
57
|
+
SDATA(data_type_t.DTP_JSON, "colors", 0, YUI_UPLOT_PALETTE,
|
|
108
58
|
"Series colours, in order. The default palette clears 3:1 on both schemes and opens with the colour-blind-safe pair (blue, orange)"),
|
|
109
59
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for the chart"),
|
|
110
60
|
SDATA(data_type_t.DTP_BOOLEAN, "own_container", 0, false, "If true, container is gobj-managed"),
|
|
@@ -138,7 +88,7 @@ function mt_create(gobj)
|
|
|
138
88
|
build_ui(gobj);
|
|
139
89
|
|
|
140
90
|
/* A canvas does not restyle itself: the ink is read when uPlot
|
|
141
|
-
* DRAWS (
|
|
91
|
+
* DRAWS (yui_uplot_ink.js), so a theme change only has to reach the
|
|
142
92
|
* machine and ask for a redraw. Same wiring as the graphs. */
|
|
143
93
|
gobj.priv.theme_observer = yui_watch_theme(gobj);
|
|
144
94
|
|
|
@@ -220,7 +170,7 @@ function build_ui(gobj)
|
|
|
220
170
|
width: gobj_read_attr(gobj, "width"),
|
|
221
171
|
height: gobj_read_attr(gobj, "height"),
|
|
222
172
|
series: gobj_read_attr(gobj, "series"),
|
|
223
|
-
axes:
|
|
173
|
+
axes: yui_uplot_axes(gobj_read_attr(gobj, "axes")),
|
|
224
174
|
scales: gobj_read_attr(gobj, "scales"),
|
|
225
175
|
};
|
|
226
176
|
let uplot = new uPlot(
|
|
@@ -444,7 +394,7 @@ function ac_add_serie(gobj, event, kw, src)
|
|
|
444
394
|
}
|
|
445
395
|
let colors = gobj_read_attr(gobj, "colors");
|
|
446
396
|
if(!Array.isArray(colors) || !colors.length) {
|
|
447
|
-
colors =
|
|
397
|
+
colors = YUI_UPLOT_PALETTE;
|
|
448
398
|
}
|
|
449
399
|
/* `idx` counts uPlot's series, and series[0] is the X AXIS -- so
|
|
450
400
|
* the first line drawn was taking colours[1] and the palette's
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* form_close_asks_for_edits.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* Closing a C_YUI_FORM asks "all changes will be lost" only for what
|
|
5
|
+
* the READER changed. A field the record lacks is filled with its
|
|
6
|
+
* default when the record is loaded: the form then differs from what
|
|
7
|
+
* is stored, and its save lights (`changes`) -- but the reader did
|
|
8
|
+
* nothing, and the close asked all the same. In estadodelaire that
|
|
9
|
+
* was every device whose alarm colours a save had wiped (16 of 19),
|
|
10
|
+
* on every close of its settings. What the reader does -- types,
|
|
11
|
+
* clears, pastes -- is `edited`, and that is what the close asks
|
|
12
|
+
* about.
|
|
13
|
+
*
|
|
14
|
+
* Copyright (c) 2026, ArtGins.
|
|
15
|
+
* All Rights Reserved.
|
|
16
|
+
***********************************************************************/
|
|
17
|
+
import {describe, test, expect, beforeAll, beforeEach} from "vitest";
|
|
18
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
19
|
+
|
|
20
|
+
install_dom_double();
|
|
21
|
+
|
|
22
|
+
if(typeof globalThis.ResizeObserver === "undefined") {
|
|
23
|
+
globalThis.ResizeObserver = class {
|
|
24
|
+
observe() {}
|
|
25
|
+
unobserve() {}
|
|
26
|
+
disconnect() {}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
SDATA_END, gclass_create,
|
|
32
|
+
gobj_start_up, gobj_create_yuno, gobj_create_pure_child,
|
|
33
|
+
gobj_start, gobj_send_event, gobj_read_attr,
|
|
34
|
+
set_log_callback,
|
|
35
|
+
} = await import("@yuneta/gobj-js");
|
|
36
|
+
const {register_c_yui_form} = await import("./c_yui_form.js");
|
|
37
|
+
|
|
38
|
+
const logged = [];
|
|
39
|
+
let yuno = null;
|
|
40
|
+
|
|
41
|
+
/* The settings template of an estadodelaire device type, cut down: a
|
|
42
|
+
* field with a default beside a sub-form of three. */
|
|
43
|
+
const TEMPLATE = {
|
|
44
|
+
template_version: {
|
|
45
|
+
type: "integer",
|
|
46
|
+
default: 7,
|
|
47
|
+
flag: ["hidden", "persistent"]
|
|
48
|
+
},
|
|
49
|
+
alarm_colors: {
|
|
50
|
+
type: "object",
|
|
51
|
+
flag: ["writable", "persistent", "template"],
|
|
52
|
+
template: {
|
|
53
|
+
active_not_validated: {
|
|
54
|
+
type: "string",
|
|
55
|
+
default: "red",
|
|
56
|
+
flag: ["writable", "persistent", "color"]
|
|
57
|
+
},
|
|
58
|
+
active_validated: {
|
|
59
|
+
type: "string",
|
|
60
|
+
default: "#e5a50a",
|
|
61
|
+
flag: ["writable", "persistent", "color"]
|
|
62
|
+
},
|
|
63
|
+
not_active_not_validated: {
|
|
64
|
+
type: "string",
|
|
65
|
+
default: "Fuchsia",
|
|
66
|
+
flag: ["writable", "persistent", "color"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const COMPLETE = {
|
|
73
|
+
template_version: 7,
|
|
74
|
+
alarm_colors: {
|
|
75
|
+
active_not_validated: "#ff0000",
|
|
76
|
+
active_validated: "#e5a50a",
|
|
77
|
+
not_active_not_validated: "#ff00ff"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
beforeAll(async () => {
|
|
82
|
+
const i18next = (await import("i18next")).default;
|
|
83
|
+
await i18next.init({lng: "en", resources: {}});
|
|
84
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
85
|
+
set_log_callback((level, msg) => {
|
|
86
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
87
|
+
});
|
|
88
|
+
gclass_create("C_TEST_CLOSE_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
89
|
+
gclass_create(
|
|
90
|
+
"C_TEST_CLOSE_APP",
|
|
91
|
+
[["EV_SAVE_RECORD", 0]],
|
|
92
|
+
[["ST_IDLE", [
|
|
93
|
+
["EV_SAVE_RECORD", () => 0, null],
|
|
94
|
+
]]],
|
|
95
|
+
{}, 0, [SDATA_END()], {}, 0, 0, 0, 0
|
|
96
|
+
);
|
|
97
|
+
register_c_yui_form();
|
|
98
|
+
yuno = gobj_create_yuno("form_close_yuno", "C_TEST_CLOSE_HOST", {});
|
|
99
|
+
gobj_start(yuno);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
logged.length = 0;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
let seq = 0;
|
|
107
|
+
|
|
108
|
+
function build(record)
|
|
109
|
+
{
|
|
110
|
+
const app = gobj_create_pure_child(`capp${++seq}`, "C_TEST_CLOSE_APP", {}, yuno);
|
|
111
|
+
gobj_start(app);
|
|
112
|
+
const form = gobj_create_pure_child(`cform${seq}`, "C_YUI_FORM", {
|
|
113
|
+
template: TEMPLATE,
|
|
114
|
+
record: record,
|
|
115
|
+
editable: true,
|
|
116
|
+
}, app);
|
|
117
|
+
gobj_start(form);
|
|
118
|
+
return form;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* What a window or a sheet asks the form before it closes. */
|
|
122
|
+
function close_asks(form)
|
|
123
|
+
{
|
|
124
|
+
const kw = {abort_close: false};
|
|
125
|
+
gobj_send_event(form, "EV_WINDOW_TO_CLOSE", kw, form);
|
|
126
|
+
return kw.abort_close ? (kw.warning || "(no warning)") : "";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function errors()
|
|
130
|
+
{
|
|
131
|
+
return logged.filter(l => /error/i.test(l.level) || /ERROR/.test(l.msg));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
describe("the close of a form asks for what the reader changed", () => {
|
|
135
|
+
test("a record that lacks a defaulted field opens changed, and closes without asking", () => {
|
|
136
|
+
const form = build({template_version: 7, alarm_colors: null});
|
|
137
|
+
expect(gobj_read_attr(form, "changes")).toBe(true);
|
|
138
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
139
|
+
expect(close_asks(form)).toBe("");
|
|
140
|
+
expect(errors()).toEqual([]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("a change of the reader makes it ask", () => {
|
|
144
|
+
const form = build(COMPLETE);
|
|
145
|
+
expect(gobj_read_attr(form, "changes")).toBe(false);
|
|
146
|
+
expect(close_asks(form)).toBe("");
|
|
147
|
+
|
|
148
|
+
gobj_send_event(form, "EV_RECORD_CHANGED", {}, form);
|
|
149
|
+
expect(gobj_read_attr(form, "changes")).toBe(true);
|
|
150
|
+
expect(gobj_read_attr(form, "edited")).toBe(true);
|
|
151
|
+
expect(close_asks(form)).toBe("all changes will be lost");
|
|
152
|
+
expect(errors()).toEqual([]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("an undo, a save or a record loaded end what the close would lose", () => {
|
|
156
|
+
const form = build(COMPLETE);
|
|
157
|
+
|
|
158
|
+
gobj_send_event(form, "EV_RECORD_CHANGED", {}, form);
|
|
159
|
+
gobj_send_event(form, "EV_UNDO_RECORD", {}, form);
|
|
160
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
161
|
+
expect(close_asks(form)).toBe("");
|
|
162
|
+
|
|
163
|
+
gobj_send_event(form, "EV_RECORD_CHANGED", {}, form);
|
|
164
|
+
gobj_send_event(form, "EV_SAVE_RECORD", {}, form);
|
|
165
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
166
|
+
expect(close_asks(form)).toBe("");
|
|
167
|
+
|
|
168
|
+
gobj_send_event(form, "EV_RECORD_CHANGED", {}, form);
|
|
169
|
+
gobj_send_event(form, "EV_LOAD_RECORD", {template_version: 7, alarm_colors: null}, form);
|
|
170
|
+
expect(gobj_read_attr(form, "changes")).toBe(true); // the defaults again
|
|
171
|
+
expect(gobj_read_attr(form, "edited")).toBe(false);
|
|
172
|
+
expect(close_asks(form)).toBe("");
|
|
173
|
+
expect(errors()).toEqual([]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("clearing and pasting are changes of the reader", () => {
|
|
177
|
+
const cleared = build(COMPLETE);
|
|
178
|
+
gobj_send_event(cleared, "EV_CLEAR_RECORD", {}, cleared);
|
|
179
|
+
expect(gobj_read_attr(cleared, "changes")).toBe(true);
|
|
180
|
+
expect(gobj_read_attr(cleared, "edited")).toBe(true);
|
|
181
|
+
expect(close_asks(cleared)).toBe("all changes will be lost");
|
|
182
|
+
|
|
183
|
+
const pasted = build(COMPLETE);
|
|
184
|
+
gobj_send_event(pasted, "EV_PASTE_RECORD", {record: {
|
|
185
|
+
template_version: 7,
|
|
186
|
+
alarm_colors: {
|
|
187
|
+
active_not_validated: "#00ff00",
|
|
188
|
+
active_validated: "#e5a50a",
|
|
189
|
+
not_active_not_validated: "#ff00ff"
|
|
190
|
+
}
|
|
191
|
+
}}, pasted);
|
|
192
|
+
expect(gobj_read_attr(pasted, "changes")).toBe(true);
|
|
193
|
+
expect(gobj_read_attr(pasted, "edited")).toBe(true);
|
|
194
|
+
expect(close_asks(pasted)).toBe("all changes will be lost");
|
|
195
|
+
expect(errors()).toEqual([]);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* uplot_ink.test.js
|
|
3
|
+
*
|
|
4
|
+
* The axes of a chart carry the ink of the theme -- the Y axis
|
|
5
|
+
* too. uPlot always draws one (its setDefaults() makes axes[1]
|
|
6
|
+
* whether it was given or not, with its own black), and
|
|
7
|
+
* C_YUI_UPLOT inked only the axes it was handed: by default one,
|
|
8
|
+
* the X. In the dark scheme the values of every Y axis of the
|
|
9
|
+
* library were black on near-black (a user's report, the cards of
|
|
10
|
+
* estadodelaire).
|
|
11
|
+
*
|
|
12
|
+
* Copyright (c) 2026, ArtGins.
|
|
13
|
+
* All Rights Reserved.
|
|
14
|
+
***********************************************************************/
|
|
15
|
+
import {describe, test, expect, beforeAll, beforeEach, afterEach, vi} from "vitest";
|
|
16
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
17
|
+
|
|
18
|
+
install_dom_double();
|
|
19
|
+
|
|
20
|
+
/* What C_YUI_UPLOT hands to uPlot, kept by the stand-in. */
|
|
21
|
+
let given = null;
|
|
22
|
+
vi.mock("uplot", () => ({
|
|
23
|
+
default: class {
|
|
24
|
+
constructor(opts) {
|
|
25
|
+
given = opts;
|
|
26
|
+
this.series = [{}];
|
|
27
|
+
}
|
|
28
|
+
setSize() {}
|
|
29
|
+
addSeries() {}
|
|
30
|
+
redraw() {}
|
|
31
|
+
destroy() {}
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
gobj_start_up, gobj_create_yuno, gobj_create_pure_child, gclass_create,
|
|
37
|
+
set_log_callback, SDATA_END,
|
|
38
|
+
} = await import("@yuneta/gobj-js");
|
|
39
|
+
const {register_c_yui_uplot} = await import("./c_yui_uplot.js");
|
|
40
|
+
const {
|
|
41
|
+
yui_uplot_axes, yui_uplot_axis_ink, yui_uplot_grid_ink,
|
|
42
|
+
} = await import("./yui_uplot_ink.js");
|
|
43
|
+
|
|
44
|
+
const logged = [];
|
|
45
|
+
let yuno = null;
|
|
46
|
+
|
|
47
|
+
beforeAll(() => {
|
|
48
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
49
|
+
set_log_callback((level, msg) => {
|
|
50
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
51
|
+
});
|
|
52
|
+
gclass_create("C_TEST_INK_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
|
|
53
|
+
register_c_yui_uplot();
|
|
54
|
+
yuno = gobj_create_yuno("ink_yuno", "C_TEST_INK_HOST", {});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
logged.length = 0;
|
|
59
|
+
given = null;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
document.documentElement.removeAttribute("data-theme");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("yui_uplot_axes()", () => {
|
|
67
|
+
test("gives the X and the Y axes, the two uPlot draws", () => {
|
|
68
|
+
expect(yui_uplot_axes([{}]).length).toBe(2);
|
|
69
|
+
expect(yui_uplot_axes(undefined).length).toBe(2);
|
|
70
|
+
expect(yui_uplot_axes([]).length).toBe(2);
|
|
71
|
+
expect(yui_uplot_axes([{}, {}, {scale: "%"}]).length).toBe(3);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("inks each one with the theme, read when uPlot draws", () => {
|
|
75
|
+
const axes = yui_uplot_axes([{}]);
|
|
76
|
+
for(const a of axes) {
|
|
77
|
+
expect(a.stroke).toBe(yui_uplot_axis_ink);
|
|
78
|
+
expect(a.grid.stroke).toBe(yui_uplot_grid_ink);
|
|
79
|
+
expect(a.ticks.stroke).toBe(yui_uplot_grid_ink);
|
|
80
|
+
}
|
|
81
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
82
|
+
expect(axes[1].stroke()).toBe("#2e333d");
|
|
83
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
84
|
+
expect(axes[1].stroke()).toBe("#c7cdd8");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("keeps what the host gave, and does not touch its objects", () => {
|
|
88
|
+
const mine = [{stroke: "red", grid: {show: false}}];
|
|
89
|
+
const axes = yui_uplot_axes(mine);
|
|
90
|
+
expect(axes[0].stroke).toBe("red");
|
|
91
|
+
expect(axes[0].grid).toEqual({stroke: yui_uplot_grid_ink, show: false});
|
|
92
|
+
expect(mine).toEqual([{stroke: "red", grid: {show: false}}]);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("C_YUI_UPLOT", () => {
|
|
97
|
+
test("hands uPlot a Y axis with the ink of the theme", () => {
|
|
98
|
+
gobj_create_pure_child("ink_chart", "C_YUI_UPLOT", {}, yuno);
|
|
99
|
+
expect(given).toBeTruthy();
|
|
100
|
+
expect(given.axes.length).toBe(2);
|
|
101
|
+
expect(given.axes[1].stroke).toBe(yui_uplot_axis_ink);
|
|
102
|
+
expect(given.axes[1].grid.stroke).toBe(yui_uplot_grid_ink);
|
|
103
|
+
expect(logged.filter((l) => /error/i.test(l.level))).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* yui_uplot_ink.js
|
|
3
|
+
*
|
|
4
|
+
* The ink of a uPlot chart, on both schemes, and its palette.
|
|
5
|
+
*
|
|
6
|
+
* uPlot is built for a white page: its axis labels are `#000`
|
|
7
|
+
* and its grid `rgba(0,0,0,0.07)`, which on the dark scheme are
|
|
8
|
+
* 1.16:1 and 1.04 -- an axis nobody can read and a grid that is
|
|
9
|
+
* not there. The inks here are handed to uPlot as FUNCTIONS: it
|
|
10
|
+
* calls them when it draws, so the value can never be stale --
|
|
11
|
+
* what goes stale is the canvas, and a change of the theme has to
|
|
12
|
+
* ask for a redraw (yui_watch_theme() turns it into an event).
|
|
13
|
+
*
|
|
14
|
+
* uPlot ALWAYS draws a Y axis: its setDefaults() makes axes[1]
|
|
15
|
+
* whether it was given or not, with its own black. So the axes are
|
|
16
|
+
* padded to two before they are inked. C_YUI_UPLOT's default is
|
|
17
|
+
* `[{}]`, the X axis alone, and until 7.26.40 every chart of the
|
|
18
|
+
* library had a Y axis whose values could not be read in the dark.
|
|
19
|
+
*
|
|
20
|
+
* C_YUI_UPLOT uses it, and so can an app that builds a uPlot of its
|
|
21
|
+
* own:
|
|
22
|
+
*
|
|
23
|
+
* import {yui_uplot_axes} from "@yuneta/gobj-ui/src/yui_uplot_ink.js";
|
|
24
|
+
*
|
|
25
|
+
* const chart = new uPlot({
|
|
26
|
+
* width: 600, height: 300,
|
|
27
|
+
* series: [{}, {label: "temperature"}],
|
|
28
|
+
* axes: yui_uplot_axes([{}]), // X and Y, with the ink of the theme
|
|
29
|
+
* }, data, $el);
|
|
30
|
+
*
|
|
31
|
+
* The palette is six colours that clear 3:1 on BOTH grounds
|
|
32
|
+
* (worst 3.39 on white, 3.71 on near-black), ordered so the first
|
|
33
|
+
* two are the pair that survives colour blindness -- blue and
|
|
34
|
+
* orange, never red and green as the opening pair.
|
|
35
|
+
*
|
|
36
|
+
* Copyright (c) 2026, ArtGins.
|
|
37
|
+
* All Rights Reserved.
|
|
38
|
+
***********************************************************************/
|
|
39
|
+
import {yui_is_dark} from "./yui_theme.js";
|
|
40
|
+
|
|
41
|
+
/************************************************************
|
|
42
|
+
* The ink of the labels of an axis.
|
|
43
|
+
************************************************************/
|
|
44
|
+
function yui_uplot_axis_ink()
|
|
45
|
+
{
|
|
46
|
+
return yui_is_dark()? "#c7cdd8" : "#2e333d";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/************************************************************
|
|
50
|
+
* The ink of the grid and of the ticks.
|
|
51
|
+
************************************************************/
|
|
52
|
+
function yui_uplot_grid_ink()
|
|
53
|
+
{
|
|
54
|
+
return yui_is_dark()? "rgba(199,205,216,0.16)" : "rgba(46,51,61,0.12)";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const YUI_UPLOT_PALETTE = [
|
|
58
|
+
{stroke: "#2f6fd0", fill: "rgba(47,111,208,0.10)"}, /* 4.88 / 3.71 */
|
|
59
|
+
{stroke: "#b8620a", fill: "rgba(184,98,10,0.10)"}, /* 4.38 / 4.13 */
|
|
60
|
+
{stroke: "#2f8f4e", fill: "rgba(47,143,78,0.10)"}, /* 4.07 / 4.45 */
|
|
61
|
+
{stroke: "#8b5cf6", fill: "rgba(139,92,246,0.10)"}, /* 4.23 / 4.28 */
|
|
62
|
+
{stroke: "#0e9aa7", fill: "rgba(14,154,167,0.10)"}, /* 3.39 / 5.35 */
|
|
63
|
+
{stroke: "#d1344b", fill: "rgba(209,52,75,0.10)"}, /* 4.88 / 3.71 */
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
/************************************************************
|
|
67
|
+
* The axes the host asked for -- at least the X and the Y,
|
|
68
|
+
* the two uPlot always draws -- with the ink it did not ask
|
|
69
|
+
* about. A stroke, a grid or a ticks the host gave is kept.
|
|
70
|
+
* Answers new objects: the host's are not touched.
|
|
71
|
+
************************************************************/
|
|
72
|
+
function yui_uplot_axes(axes)
|
|
73
|
+
{
|
|
74
|
+
let list = Array.isArray(axes)? axes.slice() : [axes || {}];
|
|
75
|
+
while(list.length < 2) {
|
|
76
|
+
list.push({});
|
|
77
|
+
}
|
|
78
|
+
return list.map((ax) => {
|
|
79
|
+
let a = Object.assign({}, ax || {});
|
|
80
|
+
if(a.stroke === undefined) {
|
|
81
|
+
a.stroke = yui_uplot_axis_ink;
|
|
82
|
+
}
|
|
83
|
+
a.grid = Object.assign({stroke: yui_uplot_grid_ink}, a.grid || {});
|
|
84
|
+
a.ticks = Object.assign({stroke: yui_uplot_grid_ink}, a.ticks || {});
|
|
85
|
+
return a;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
yui_uplot_axes,
|
|
91
|
+
yui_uplot_axis_ink,
|
|
92
|
+
yui_uplot_grid_ink,
|
|
93
|
+
YUI_UPLOT_PALETTE,
|
|
94
|
+
};
|