@yuneta/gobj-ui 5.6.0 → 5.7.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/dist/gobj-ui.cjs.js +232 -145
- package/dist/gobj-ui.es.js +232 -145
- package/package.json +2 -2
- package/src/c_yui_form.js +100 -75
- package/src/form_toolbar_plan.js +59 -0
- package/src/form_toolbar_plan.test.js +54 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -890,7 +890,7 @@ function mt_start$15(gobj) {
|
|
|
890
890
|
if (typeof shell_cfg.remember_section_position === "boolean") (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "remember_section_position", shell_cfg.remember_section_position);
|
|
891
891
|
build_item_index(gobj, config);
|
|
892
892
|
instantiate_menus(gobj, config);
|
|
893
|
-
build_toolbar(gobj, config);
|
|
893
|
+
build_toolbar$1(gobj, config);
|
|
894
894
|
preinstantiate_eager_views(gobj);
|
|
895
895
|
if ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "use_hash")) {
|
|
896
896
|
priv.hash_handler = () => {
|
|
@@ -1616,7 +1616,7 @@ function safe_id(s) {
|
|
|
1616
1616
|
* Per-item `show_on` is honoured: each rendered item is wrapped with
|
|
1617
1617
|
* the same Bulma-helper logic as zones.
|
|
1618
1618
|
************************************************************/
|
|
1619
|
-
function build_toolbar(gobj, config) {
|
|
1619
|
+
function build_toolbar$1(gobj, config) {
|
|
1620
1620
|
let tb = config && config.toolbar;
|
|
1621
1621
|
if (!tb || !(0, _yuneta_gobj_js.is_array)(tb.items)) return;
|
|
1622
1622
|
let priv = gobj.priv;
|
|
@@ -5760,6 +5760,59 @@ function refresh_clear($input) {
|
|
|
5760
5760
|
$btn.classList.toggle("is-visible", clear_visible($input));
|
|
5761
5761
|
}
|
|
5762
5762
|
//#endregion
|
|
5763
|
+
//#region src/form_toolbar_plan.js
|
|
5764
|
+
/***********************************************************************
|
|
5765
|
+
* form_toolbar_plan.js
|
|
5766
|
+
*
|
|
5767
|
+
* Which buttons the C_YUI_FORM toolbar shows, and where.
|
|
5768
|
+
*
|
|
5769
|
+
* Pure, so it can be tested without a DOM: the gclass turns the
|
|
5770
|
+
* plan into elements, this decides the plan.
|
|
5771
|
+
*
|
|
5772
|
+
* Copyright (c) 2026, ArtGins.
|
|
5773
|
+
* All Rights Reserved.
|
|
5774
|
+
***********************************************************************/
|
|
5775
|
+
var LEFT_BUTTONS = [
|
|
5776
|
+
"save",
|
|
5777
|
+
"undo",
|
|
5778
|
+
"clear"
|
|
5779
|
+
];
|
|
5780
|
+
var RIGHT_BUTTONS = ["copy", "paste"];
|
|
5781
|
+
var DEFAULT_TOOLBAR = [
|
|
5782
|
+
"save",
|
|
5783
|
+
"undo",
|
|
5784
|
+
"clear",
|
|
5785
|
+
"copy",
|
|
5786
|
+
"paste"
|
|
5787
|
+
];
|
|
5788
|
+
/***************************************************************
|
|
5789
|
+
* plan_toolbar(wanted) -> {left, right, unknown}
|
|
5790
|
+
*
|
|
5791
|
+
* wanted an array of button names, in the order asked for.
|
|
5792
|
+
* Anything that is not an array means "the default
|
|
5793
|
+
* five", so a caller that sets nothing keeps exactly
|
|
5794
|
+
* the toolbar this gclass always had.
|
|
5795
|
+
* [] means no toolbar at all.
|
|
5796
|
+
*
|
|
5797
|
+
* unknown names that match no button. They are reported and
|
|
5798
|
+
* NOT silently dropped: a typo in the list would
|
|
5799
|
+
* otherwise remove the save button with no trace.
|
|
5800
|
+
***************************************************************/
|
|
5801
|
+
function plan_toolbar(wanted) {
|
|
5802
|
+
if (!Array.isArray(wanted)) wanted = DEFAULT_TOOLBAR;
|
|
5803
|
+
let left = [];
|
|
5804
|
+
let right = [];
|
|
5805
|
+
let unknown = [];
|
|
5806
|
+
for (let name of wanted) if (LEFT_BUTTONS.indexOf(name) >= 0) left.push(name);
|
|
5807
|
+
else if (RIGHT_BUTTONS.indexOf(name) >= 0) right.push(name);
|
|
5808
|
+
else unknown.push(name);
|
|
5809
|
+
return {
|
|
5810
|
+
left,
|
|
5811
|
+
right,
|
|
5812
|
+
unknown
|
|
5813
|
+
};
|
|
5814
|
+
}
|
|
5815
|
+
//#endregion
|
|
5763
5816
|
//#region src/yui_theme.js
|
|
5764
5817
|
/***********************************************************************
|
|
5765
5818
|
* yui_theme.js
|
|
@@ -5891,6 +5944,13 @@ var attrs_table$13 = [
|
|
|
5891
5944
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
5892
5945
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
|
|
5893
5946
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
|
|
5947
|
+
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "toolbar", 0, [
|
|
5948
|
+
"save",
|
|
5949
|
+
"undo",
|
|
5950
|
+
"clear",
|
|
5951
|
+
"copy",
|
|
5952
|
+
"paste"
|
|
5953
|
+
], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
|
|
5894
5954
|
(0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
5895
5955
|
layout: "fitDataFill",
|
|
5896
5956
|
validationMode: "highlight",
|
|
@@ -6001,6 +6061,175 @@ async function readClipboard$1(gobj) {
|
|
|
6001
6061
|
}
|
|
6002
6062
|
}
|
|
6003
6063
|
/************************************************************
|
|
6064
|
+
* Bottom toolbar, from the `toolbar` attr.
|
|
6065
|
+
*
|
|
6066
|
+
* The five buttons it can show, and their order, are the
|
|
6067
|
+
* caller's: a dialog with a single action asks for ["save"],
|
|
6068
|
+
* and [] leaves no toolbar at all. Default is the five it
|
|
6069
|
+
* always had, so nothing moves for a caller that says nothing.
|
|
6070
|
+
************************************************************/
|
|
6071
|
+
function toolbar_button(gobj, name) {
|
|
6072
|
+
switch (name) {
|
|
6073
|
+
case "save": return [
|
|
6074
|
+
"button",
|
|
6075
|
+
{
|
|
6076
|
+
class: "button p-1 button-save",
|
|
6077
|
+
title: "save",
|
|
6078
|
+
"aria-label": "save",
|
|
6079
|
+
disabled: true
|
|
6080
|
+
},
|
|
6081
|
+
[[
|
|
6082
|
+
"span",
|
|
6083
|
+
{ class: "icon m-0" },
|
|
6084
|
+
"<i class=\"yi-floppy-disk\"></i>"
|
|
6085
|
+
], [
|
|
6086
|
+
"span",
|
|
6087
|
+
{
|
|
6088
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6089
|
+
i18n: "save"
|
|
6090
|
+
},
|
|
6091
|
+
"save"
|
|
6092
|
+
]],
|
|
6093
|
+
{ click: function(evt) {
|
|
6094
|
+
evt.stopPropagation();
|
|
6095
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
6096
|
+
} }
|
|
6097
|
+
];
|
|
6098
|
+
case "undo": return [
|
|
6099
|
+
"button",
|
|
6100
|
+
{
|
|
6101
|
+
class: "button p-1 button-undo",
|
|
6102
|
+
title: "undo",
|
|
6103
|
+
"aria-label": "undo",
|
|
6104
|
+
disabled: true
|
|
6105
|
+
},
|
|
6106
|
+
[[
|
|
6107
|
+
"span",
|
|
6108
|
+
{ class: "icon m-0" },
|
|
6109
|
+
"<i class=\"yi-arrow-rotate-left\"></i>"
|
|
6110
|
+
], [
|
|
6111
|
+
"span",
|
|
6112
|
+
{
|
|
6113
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6114
|
+
i18n: "undo"
|
|
6115
|
+
},
|
|
6116
|
+
"undo"
|
|
6117
|
+
]],
|
|
6118
|
+
{ click: function(evt) {
|
|
6119
|
+
evt.stopPropagation();
|
|
6120
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
6121
|
+
} }
|
|
6122
|
+
];
|
|
6123
|
+
case "clear": return [
|
|
6124
|
+
"button",
|
|
6125
|
+
{
|
|
6126
|
+
class: "button p-1",
|
|
6127
|
+
title: "clear",
|
|
6128
|
+
"aria-label": "clear"
|
|
6129
|
+
},
|
|
6130
|
+
[[
|
|
6131
|
+
"span",
|
|
6132
|
+
{ class: "icon m-0" },
|
|
6133
|
+
"<i class=\"yi-broom-wide\"></i>"
|
|
6134
|
+
], [
|
|
6135
|
+
"span",
|
|
6136
|
+
{
|
|
6137
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6138
|
+
i18n: "clear"
|
|
6139
|
+
},
|
|
6140
|
+
"clear"
|
|
6141
|
+
]],
|
|
6142
|
+
{ click: function(evt) {
|
|
6143
|
+
evt.stopPropagation();
|
|
6144
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
6145
|
+
} }
|
|
6146
|
+
];
|
|
6147
|
+
case "copy": return [
|
|
6148
|
+
"button",
|
|
6149
|
+
{
|
|
6150
|
+
class: "button p-1",
|
|
6151
|
+
title: "copy",
|
|
6152
|
+
"aria-label": "copy"
|
|
6153
|
+
},
|
|
6154
|
+
[[
|
|
6155
|
+
"span",
|
|
6156
|
+
{ class: "icon m-0" },
|
|
6157
|
+
"<i class=\"yi-copy\"></i>"
|
|
6158
|
+
], [
|
|
6159
|
+
"span",
|
|
6160
|
+
{
|
|
6161
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6162
|
+
i18n: "copy"
|
|
6163
|
+
},
|
|
6164
|
+
"copy"
|
|
6165
|
+
]],
|
|
6166
|
+
{ click: function(evt) {
|
|
6167
|
+
evt.stopPropagation();
|
|
6168
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
6169
|
+
} }
|
|
6170
|
+
];
|
|
6171
|
+
case "paste": return [
|
|
6172
|
+
"button",
|
|
6173
|
+
{
|
|
6174
|
+
class: "button p-1",
|
|
6175
|
+
title: "paste",
|
|
6176
|
+
"aria-label": "paste"
|
|
6177
|
+
},
|
|
6178
|
+
[[
|
|
6179
|
+
"span",
|
|
6180
|
+
{ class: "icon m-0" },
|
|
6181
|
+
"<i class=\"yi-paste\"></i>"
|
|
6182
|
+
], [
|
|
6183
|
+
"span",
|
|
6184
|
+
{
|
|
6185
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6186
|
+
i18n: "paste"
|
|
6187
|
+
},
|
|
6188
|
+
"paste"
|
|
6189
|
+
]],
|
|
6190
|
+
{ click: async function(evt) {
|
|
6191
|
+
evt.stopPropagation();
|
|
6192
|
+
await readClipboard$1(gobj);
|
|
6193
|
+
} }
|
|
6194
|
+
];
|
|
6195
|
+
default: return null;
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
function build_toolbar(gobj) {
|
|
6199
|
+
const plan = plan_toolbar((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "toolbar"));
|
|
6200
|
+
for (let name of plan.unknown) (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: unknown toolbar button '${name}'`);
|
|
6201
|
+
if (!plan.left.length && !plan.right.length) return [
|
|
6202
|
+
"span",
|
|
6203
|
+
{
|
|
6204
|
+
class: "yui-toolbar-form-empty",
|
|
6205
|
+
style: "display:none;"
|
|
6206
|
+
},
|
|
6207
|
+
""
|
|
6208
|
+
];
|
|
6209
|
+
return [
|
|
6210
|
+
"div",
|
|
6211
|
+
{
|
|
6212
|
+
class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
|
|
6213
|
+
style: "width:100%; flex-wrap:wrap; row-gap:6px;"
|
|
6214
|
+
},
|
|
6215
|
+
[[
|
|
6216
|
+
"div",
|
|
6217
|
+
{
|
|
6218
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6219
|
+
style: "column-gap:6px;"
|
|
6220
|
+
},
|
|
6221
|
+
plan.left.map((n) => toolbar_button(gobj, n))
|
|
6222
|
+
], [
|
|
6223
|
+
"div",
|
|
6224
|
+
{
|
|
6225
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6226
|
+
style: "column-gap:6px;"
|
|
6227
|
+
},
|
|
6228
|
+
plan.right.map((n) => toolbar_button(gobj, n))
|
|
6229
|
+
]]
|
|
6230
|
+
];
|
|
6231
|
+
}
|
|
6232
|
+
/************************************************************
|
|
6004
6233
|
* Build UI
|
|
6005
6234
|
************************************************************/
|
|
6006
6235
|
function build_ui$11(gobj) {
|
|
@@ -6017,149 +6246,7 @@ function build_ui$11(gobj) {
|
|
|
6017
6246
|
style: "overflow-y:auto; min-height:0;"
|
|
6018
6247
|
},
|
|
6019
6248
|
build_form(gobj)
|
|
6020
|
-
],
|
|
6021
|
-
"div",
|
|
6022
|
-
{
|
|
6023
|
-
class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
|
|
6024
|
-
style: "width:100%; flex-wrap:wrap; row-gap:6px;"
|
|
6025
|
-
},
|
|
6026
|
-
[[
|
|
6027
|
-
"div",
|
|
6028
|
-
{
|
|
6029
|
-
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6030
|
-
style: "column-gap:6px;"
|
|
6031
|
-
},
|
|
6032
|
-
[
|
|
6033
|
-
[
|
|
6034
|
-
"button",
|
|
6035
|
-
{
|
|
6036
|
-
class: "button p-1 button-save",
|
|
6037
|
-
title: "save",
|
|
6038
|
-
"aria-label": "save",
|
|
6039
|
-
disabled: true
|
|
6040
|
-
},
|
|
6041
|
-
[[
|
|
6042
|
-
"span",
|
|
6043
|
-
{ class: "icon m-0" },
|
|
6044
|
-
"<i class=\"yi-floppy-disk\"></i>"
|
|
6045
|
-
], [
|
|
6046
|
-
"span",
|
|
6047
|
-
{
|
|
6048
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6049
|
-
i18n: "save"
|
|
6050
|
-
},
|
|
6051
|
-
"save"
|
|
6052
|
-
]],
|
|
6053
|
-
{ click: function(evt) {
|
|
6054
|
-
evt.stopPropagation();
|
|
6055
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
6056
|
-
} }
|
|
6057
|
-
],
|
|
6058
|
-
[
|
|
6059
|
-
"button",
|
|
6060
|
-
{
|
|
6061
|
-
class: "button p-1 button-undo",
|
|
6062
|
-
title: "undo",
|
|
6063
|
-
"aria-label": "undo",
|
|
6064
|
-
disabled: true
|
|
6065
|
-
},
|
|
6066
|
-
[[
|
|
6067
|
-
"span",
|
|
6068
|
-
{ class: "icon m-0" },
|
|
6069
|
-
"<i class=\"yi-arrow-rotate-left\"></i>"
|
|
6070
|
-
], [
|
|
6071
|
-
"span",
|
|
6072
|
-
{
|
|
6073
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6074
|
-
i18n: "undo"
|
|
6075
|
-
},
|
|
6076
|
-
"undo"
|
|
6077
|
-
]],
|
|
6078
|
-
{ click: function(evt) {
|
|
6079
|
-
evt.stopPropagation();
|
|
6080
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
6081
|
-
} }
|
|
6082
|
-
],
|
|
6083
|
-
[
|
|
6084
|
-
"button",
|
|
6085
|
-
{
|
|
6086
|
-
class: "button p-1",
|
|
6087
|
-
title: "clear",
|
|
6088
|
-
"aria-label": "clear"
|
|
6089
|
-
},
|
|
6090
|
-
[[
|
|
6091
|
-
"span",
|
|
6092
|
-
{ class: "icon m-0" },
|
|
6093
|
-
"<i class=\"yi-broom-wide\"></i>"
|
|
6094
|
-
], [
|
|
6095
|
-
"span",
|
|
6096
|
-
{
|
|
6097
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6098
|
-
i18n: "clear"
|
|
6099
|
-
},
|
|
6100
|
-
"clear"
|
|
6101
|
-
]],
|
|
6102
|
-
{ click: function(evt) {
|
|
6103
|
-
evt.stopPropagation();
|
|
6104
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
6105
|
-
} }
|
|
6106
|
-
]
|
|
6107
|
-
]
|
|
6108
|
-
], [
|
|
6109
|
-
"div",
|
|
6110
|
-
{
|
|
6111
|
-
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6112
|
-
style: "column-gap:6px;"
|
|
6113
|
-
},
|
|
6114
|
-
[[
|
|
6115
|
-
"button",
|
|
6116
|
-
{
|
|
6117
|
-
class: "button p-1",
|
|
6118
|
-
title: "copy",
|
|
6119
|
-
"aria-label": "copy"
|
|
6120
|
-
},
|
|
6121
|
-
[[
|
|
6122
|
-
"span",
|
|
6123
|
-
{ class: "icon m-0" },
|
|
6124
|
-
"<i class=\"yi-copy\"></i>"
|
|
6125
|
-
], [
|
|
6126
|
-
"span",
|
|
6127
|
-
{
|
|
6128
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6129
|
-
i18n: "copy"
|
|
6130
|
-
},
|
|
6131
|
-
"copy"
|
|
6132
|
-
]],
|
|
6133
|
-
{ click: function(evt) {
|
|
6134
|
-
evt.stopPropagation();
|
|
6135
|
-
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
6136
|
-
} }
|
|
6137
|
-
], [
|
|
6138
|
-
"button",
|
|
6139
|
-
{
|
|
6140
|
-
class: "button p-1",
|
|
6141
|
-
title: "paste",
|
|
6142
|
-
"aria-label": "paste"
|
|
6143
|
-
},
|
|
6144
|
-
[[
|
|
6145
|
-
"span",
|
|
6146
|
-
{ class: "icon m-0" },
|
|
6147
|
-
"<i class=\"yi-paste\"></i>"
|
|
6148
|
-
], [
|
|
6149
|
-
"span",
|
|
6150
|
-
{
|
|
6151
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6152
|
-
i18n: "paste"
|
|
6153
|
-
},
|
|
6154
|
-
"paste"
|
|
6155
|
-
]],
|
|
6156
|
-
{ click: async function(evt) {
|
|
6157
|
-
evt.stopPropagation();
|
|
6158
|
-
await readClipboard$1(gobj);
|
|
6159
|
-
} }
|
|
6160
|
-
]]
|
|
6161
|
-
]]
|
|
6162
|
-
]]
|
|
6249
|
+
], build_toolbar(gobj)]
|
|
6163
6250
|
]);
|
|
6164
6251
|
(0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
|
|
6165
6252
|
apply_form_mode(gobj, $container.querySelector("form"));
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -885,7 +885,7 @@ function mt_start$15(gobj) {
|
|
|
885
885
|
if (typeof shell_cfg.remember_section_position === "boolean") gobj_write_attr(gobj, "remember_section_position", shell_cfg.remember_section_position);
|
|
886
886
|
build_item_index(gobj, config);
|
|
887
887
|
instantiate_menus(gobj, config);
|
|
888
|
-
build_toolbar(gobj, config);
|
|
888
|
+
build_toolbar$1(gobj, config);
|
|
889
889
|
preinstantiate_eager_views(gobj);
|
|
890
890
|
if (gobj_read_attr(gobj, "use_hash")) {
|
|
891
891
|
priv.hash_handler = () => {
|
|
@@ -1611,7 +1611,7 @@ function safe_id(s) {
|
|
|
1611
1611
|
* Per-item `show_on` is honoured: each rendered item is wrapped with
|
|
1612
1612
|
* the same Bulma-helper logic as zones.
|
|
1613
1613
|
************************************************************/
|
|
1614
|
-
function build_toolbar(gobj, config) {
|
|
1614
|
+
function build_toolbar$1(gobj, config) {
|
|
1615
1615
|
let tb = config && config.toolbar;
|
|
1616
1616
|
if (!tb || !is_array(tb.items)) return;
|
|
1617
1617
|
let priv = gobj.priv;
|
|
@@ -5755,6 +5755,59 @@ function refresh_clear($input) {
|
|
|
5755
5755
|
$btn.classList.toggle("is-visible", clear_visible($input));
|
|
5756
5756
|
}
|
|
5757
5757
|
//#endregion
|
|
5758
|
+
//#region src/form_toolbar_plan.js
|
|
5759
|
+
/***********************************************************************
|
|
5760
|
+
* form_toolbar_plan.js
|
|
5761
|
+
*
|
|
5762
|
+
* Which buttons the C_YUI_FORM toolbar shows, and where.
|
|
5763
|
+
*
|
|
5764
|
+
* Pure, so it can be tested without a DOM: the gclass turns the
|
|
5765
|
+
* plan into elements, this decides the plan.
|
|
5766
|
+
*
|
|
5767
|
+
* Copyright (c) 2026, ArtGins.
|
|
5768
|
+
* All Rights Reserved.
|
|
5769
|
+
***********************************************************************/
|
|
5770
|
+
var LEFT_BUTTONS = [
|
|
5771
|
+
"save",
|
|
5772
|
+
"undo",
|
|
5773
|
+
"clear"
|
|
5774
|
+
];
|
|
5775
|
+
var RIGHT_BUTTONS = ["copy", "paste"];
|
|
5776
|
+
var DEFAULT_TOOLBAR = [
|
|
5777
|
+
"save",
|
|
5778
|
+
"undo",
|
|
5779
|
+
"clear",
|
|
5780
|
+
"copy",
|
|
5781
|
+
"paste"
|
|
5782
|
+
];
|
|
5783
|
+
/***************************************************************
|
|
5784
|
+
* plan_toolbar(wanted) -> {left, right, unknown}
|
|
5785
|
+
*
|
|
5786
|
+
* wanted an array of button names, in the order asked for.
|
|
5787
|
+
* Anything that is not an array means "the default
|
|
5788
|
+
* five", so a caller that sets nothing keeps exactly
|
|
5789
|
+
* the toolbar this gclass always had.
|
|
5790
|
+
* [] means no toolbar at all.
|
|
5791
|
+
*
|
|
5792
|
+
* unknown names that match no button. They are reported and
|
|
5793
|
+
* NOT silently dropped: a typo in the list would
|
|
5794
|
+
* otherwise remove the save button with no trace.
|
|
5795
|
+
***************************************************************/
|
|
5796
|
+
function plan_toolbar(wanted) {
|
|
5797
|
+
if (!Array.isArray(wanted)) wanted = DEFAULT_TOOLBAR;
|
|
5798
|
+
let left = [];
|
|
5799
|
+
let right = [];
|
|
5800
|
+
let unknown = [];
|
|
5801
|
+
for (let name of wanted) if (LEFT_BUTTONS.indexOf(name) >= 0) left.push(name);
|
|
5802
|
+
else if (RIGHT_BUTTONS.indexOf(name) >= 0) right.push(name);
|
|
5803
|
+
else unknown.push(name);
|
|
5804
|
+
return {
|
|
5805
|
+
left,
|
|
5806
|
+
right,
|
|
5807
|
+
unknown
|
|
5808
|
+
};
|
|
5809
|
+
}
|
|
5810
|
+
//#endregion
|
|
5758
5811
|
//#region src/yui_theme.js
|
|
5759
5812
|
/***********************************************************************
|
|
5760
5813
|
* yui_theme.js
|
|
@@ -5886,6 +5939,13 @@ var attrs_table$13 = [
|
|
|
5886
5939
|
SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
5887
5940
|
SDATA(data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
|
|
5888
5941
|
SDATA(data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
|
|
5942
|
+
SDATA(data_type_t.DTP_JSON, "toolbar", 0, [
|
|
5943
|
+
"save",
|
|
5944
|
+
"undo",
|
|
5945
|
+
"clear",
|
|
5946
|
+
"copy",
|
|
5947
|
+
"paste"
|
|
5948
|
+
], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
|
|
5889
5949
|
SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
|
|
5890
5950
|
layout: "fitDataFill",
|
|
5891
5951
|
validationMode: "highlight",
|
|
@@ -5996,6 +6056,175 @@ async function readClipboard$1(gobj) {
|
|
|
5996
6056
|
}
|
|
5997
6057
|
}
|
|
5998
6058
|
/************************************************************
|
|
6059
|
+
* Bottom toolbar, from the `toolbar` attr.
|
|
6060
|
+
*
|
|
6061
|
+
* The five buttons it can show, and their order, are the
|
|
6062
|
+
* caller's: a dialog with a single action asks for ["save"],
|
|
6063
|
+
* and [] leaves no toolbar at all. Default is the five it
|
|
6064
|
+
* always had, so nothing moves for a caller that says nothing.
|
|
6065
|
+
************************************************************/
|
|
6066
|
+
function toolbar_button(gobj, name) {
|
|
6067
|
+
switch (name) {
|
|
6068
|
+
case "save": return [
|
|
6069
|
+
"button",
|
|
6070
|
+
{
|
|
6071
|
+
class: "button p-1 button-save",
|
|
6072
|
+
title: "save",
|
|
6073
|
+
"aria-label": "save",
|
|
6074
|
+
disabled: true
|
|
6075
|
+
},
|
|
6076
|
+
[[
|
|
6077
|
+
"span",
|
|
6078
|
+
{ class: "icon m-0" },
|
|
6079
|
+
"<i class=\"yi-floppy-disk\"></i>"
|
|
6080
|
+
], [
|
|
6081
|
+
"span",
|
|
6082
|
+
{
|
|
6083
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6084
|
+
i18n: "save"
|
|
6085
|
+
},
|
|
6086
|
+
"save"
|
|
6087
|
+
]],
|
|
6088
|
+
{ click: function(evt) {
|
|
6089
|
+
evt.stopPropagation();
|
|
6090
|
+
gobj_send_event(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
6091
|
+
} }
|
|
6092
|
+
];
|
|
6093
|
+
case "undo": return [
|
|
6094
|
+
"button",
|
|
6095
|
+
{
|
|
6096
|
+
class: "button p-1 button-undo",
|
|
6097
|
+
title: "undo",
|
|
6098
|
+
"aria-label": "undo",
|
|
6099
|
+
disabled: true
|
|
6100
|
+
},
|
|
6101
|
+
[[
|
|
6102
|
+
"span",
|
|
6103
|
+
{ class: "icon m-0" },
|
|
6104
|
+
"<i class=\"yi-arrow-rotate-left\"></i>"
|
|
6105
|
+
], [
|
|
6106
|
+
"span",
|
|
6107
|
+
{
|
|
6108
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6109
|
+
i18n: "undo"
|
|
6110
|
+
},
|
|
6111
|
+
"undo"
|
|
6112
|
+
]],
|
|
6113
|
+
{ click: function(evt) {
|
|
6114
|
+
evt.stopPropagation();
|
|
6115
|
+
gobj_send_event(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
6116
|
+
} }
|
|
6117
|
+
];
|
|
6118
|
+
case "clear": return [
|
|
6119
|
+
"button",
|
|
6120
|
+
{
|
|
6121
|
+
class: "button p-1",
|
|
6122
|
+
title: "clear",
|
|
6123
|
+
"aria-label": "clear"
|
|
6124
|
+
},
|
|
6125
|
+
[[
|
|
6126
|
+
"span",
|
|
6127
|
+
{ class: "icon m-0" },
|
|
6128
|
+
"<i class=\"yi-broom-wide\"></i>"
|
|
6129
|
+
], [
|
|
6130
|
+
"span",
|
|
6131
|
+
{
|
|
6132
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6133
|
+
i18n: "clear"
|
|
6134
|
+
},
|
|
6135
|
+
"clear"
|
|
6136
|
+
]],
|
|
6137
|
+
{ click: function(evt) {
|
|
6138
|
+
evt.stopPropagation();
|
|
6139
|
+
gobj_send_event(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
6140
|
+
} }
|
|
6141
|
+
];
|
|
6142
|
+
case "copy": return [
|
|
6143
|
+
"button",
|
|
6144
|
+
{
|
|
6145
|
+
class: "button p-1",
|
|
6146
|
+
title: "copy",
|
|
6147
|
+
"aria-label": "copy"
|
|
6148
|
+
},
|
|
6149
|
+
[[
|
|
6150
|
+
"span",
|
|
6151
|
+
{ class: "icon m-0" },
|
|
6152
|
+
"<i class=\"yi-copy\"></i>"
|
|
6153
|
+
], [
|
|
6154
|
+
"span",
|
|
6155
|
+
{
|
|
6156
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6157
|
+
i18n: "copy"
|
|
6158
|
+
},
|
|
6159
|
+
"copy"
|
|
6160
|
+
]],
|
|
6161
|
+
{ click: function(evt) {
|
|
6162
|
+
evt.stopPropagation();
|
|
6163
|
+
gobj_send_event(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
6164
|
+
} }
|
|
6165
|
+
];
|
|
6166
|
+
case "paste": return [
|
|
6167
|
+
"button",
|
|
6168
|
+
{
|
|
6169
|
+
class: "button p-1",
|
|
6170
|
+
title: "paste",
|
|
6171
|
+
"aria-label": "paste"
|
|
6172
|
+
},
|
|
6173
|
+
[[
|
|
6174
|
+
"span",
|
|
6175
|
+
{ class: "icon m-0" },
|
|
6176
|
+
"<i class=\"yi-paste\"></i>"
|
|
6177
|
+
], [
|
|
6178
|
+
"span",
|
|
6179
|
+
{
|
|
6180
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
6181
|
+
i18n: "paste"
|
|
6182
|
+
},
|
|
6183
|
+
"paste"
|
|
6184
|
+
]],
|
|
6185
|
+
{ click: async function(evt) {
|
|
6186
|
+
evt.stopPropagation();
|
|
6187
|
+
await readClipboard$1(gobj);
|
|
6188
|
+
} }
|
|
6189
|
+
];
|
|
6190
|
+
default: return null;
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6193
|
+
function build_toolbar(gobj) {
|
|
6194
|
+
const plan = plan_toolbar(gobj_read_attr(gobj, "toolbar"));
|
|
6195
|
+
for (let name of plan.unknown) log_warning(`${gobj_short_name(gobj)}: unknown toolbar button '${name}'`);
|
|
6196
|
+
if (!plan.left.length && !plan.right.length) return [
|
|
6197
|
+
"span",
|
|
6198
|
+
{
|
|
6199
|
+
class: "yui-toolbar-form-empty",
|
|
6200
|
+
style: "display:none;"
|
|
6201
|
+
},
|
|
6202
|
+
""
|
|
6203
|
+
];
|
|
6204
|
+
return [
|
|
6205
|
+
"div",
|
|
6206
|
+
{
|
|
6207
|
+
class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
|
|
6208
|
+
style: "width:100%; flex-wrap:wrap; row-gap:6px;"
|
|
6209
|
+
},
|
|
6210
|
+
[[
|
|
6211
|
+
"div",
|
|
6212
|
+
{
|
|
6213
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6214
|
+
style: "column-gap:6px;"
|
|
6215
|
+
},
|
|
6216
|
+
plan.left.map((n) => toolbar_button(gobj, n))
|
|
6217
|
+
], [
|
|
6218
|
+
"div",
|
|
6219
|
+
{
|
|
6220
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6221
|
+
style: "column-gap:6px;"
|
|
6222
|
+
},
|
|
6223
|
+
plan.right.map((n) => toolbar_button(gobj, n))
|
|
6224
|
+
]]
|
|
6225
|
+
];
|
|
6226
|
+
}
|
|
6227
|
+
/************************************************************
|
|
5999
6228
|
* Build UI
|
|
6000
6229
|
************************************************************/
|
|
6001
6230
|
function build_ui$11(gobj) {
|
|
@@ -6012,149 +6241,7 @@ function build_ui$11(gobj) {
|
|
|
6012
6241
|
style: "overflow-y:auto; min-height:0;"
|
|
6013
6242
|
},
|
|
6014
6243
|
build_form(gobj)
|
|
6015
|
-
],
|
|
6016
|
-
"div",
|
|
6017
|
-
{
|
|
6018
|
-
class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
|
|
6019
|
-
style: "width:100%; flex-wrap:wrap; row-gap:6px;"
|
|
6020
|
-
},
|
|
6021
|
-
[[
|
|
6022
|
-
"div",
|
|
6023
|
-
{
|
|
6024
|
-
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6025
|
-
style: "column-gap:6px;"
|
|
6026
|
-
},
|
|
6027
|
-
[
|
|
6028
|
-
[
|
|
6029
|
-
"button",
|
|
6030
|
-
{
|
|
6031
|
-
class: "button p-1 button-save",
|
|
6032
|
-
title: "save",
|
|
6033
|
-
"aria-label": "save",
|
|
6034
|
-
disabled: true
|
|
6035
|
-
},
|
|
6036
|
-
[[
|
|
6037
|
-
"span",
|
|
6038
|
-
{ class: "icon m-0" },
|
|
6039
|
-
"<i class=\"yi-floppy-disk\"></i>"
|
|
6040
|
-
], [
|
|
6041
|
-
"span",
|
|
6042
|
-
{
|
|
6043
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6044
|
-
i18n: "save"
|
|
6045
|
-
},
|
|
6046
|
-
"save"
|
|
6047
|
-
]],
|
|
6048
|
-
{ click: function(evt) {
|
|
6049
|
-
evt.stopPropagation();
|
|
6050
|
-
gobj_send_event(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
6051
|
-
} }
|
|
6052
|
-
],
|
|
6053
|
-
[
|
|
6054
|
-
"button",
|
|
6055
|
-
{
|
|
6056
|
-
class: "button p-1 button-undo",
|
|
6057
|
-
title: "undo",
|
|
6058
|
-
"aria-label": "undo",
|
|
6059
|
-
disabled: true
|
|
6060
|
-
},
|
|
6061
|
-
[[
|
|
6062
|
-
"span",
|
|
6063
|
-
{ class: "icon m-0" },
|
|
6064
|
-
"<i class=\"yi-arrow-rotate-left\"></i>"
|
|
6065
|
-
], [
|
|
6066
|
-
"span",
|
|
6067
|
-
{
|
|
6068
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6069
|
-
i18n: "undo"
|
|
6070
|
-
},
|
|
6071
|
-
"undo"
|
|
6072
|
-
]],
|
|
6073
|
-
{ click: function(evt) {
|
|
6074
|
-
evt.stopPropagation();
|
|
6075
|
-
gobj_send_event(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
6076
|
-
} }
|
|
6077
|
-
],
|
|
6078
|
-
[
|
|
6079
|
-
"button",
|
|
6080
|
-
{
|
|
6081
|
-
class: "button p-1",
|
|
6082
|
-
title: "clear",
|
|
6083
|
-
"aria-label": "clear"
|
|
6084
|
-
},
|
|
6085
|
-
[[
|
|
6086
|
-
"span",
|
|
6087
|
-
{ class: "icon m-0" },
|
|
6088
|
-
"<i class=\"yi-broom-wide\"></i>"
|
|
6089
|
-
], [
|
|
6090
|
-
"span",
|
|
6091
|
-
{
|
|
6092
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6093
|
-
i18n: "clear"
|
|
6094
|
-
},
|
|
6095
|
-
"clear"
|
|
6096
|
-
]],
|
|
6097
|
-
{ click: function(evt) {
|
|
6098
|
-
evt.stopPropagation();
|
|
6099
|
-
gobj_send_event(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
6100
|
-
} }
|
|
6101
|
-
]
|
|
6102
|
-
]
|
|
6103
|
-
], [
|
|
6104
|
-
"div",
|
|
6105
|
-
{
|
|
6106
|
-
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
6107
|
-
style: "column-gap:6px;"
|
|
6108
|
-
},
|
|
6109
|
-
[[
|
|
6110
|
-
"button",
|
|
6111
|
-
{
|
|
6112
|
-
class: "button p-1",
|
|
6113
|
-
title: "copy",
|
|
6114
|
-
"aria-label": "copy"
|
|
6115
|
-
},
|
|
6116
|
-
[[
|
|
6117
|
-
"span",
|
|
6118
|
-
{ class: "icon m-0" },
|
|
6119
|
-
"<i class=\"yi-copy\"></i>"
|
|
6120
|
-
], [
|
|
6121
|
-
"span",
|
|
6122
|
-
{
|
|
6123
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6124
|
-
i18n: "copy"
|
|
6125
|
-
},
|
|
6126
|
-
"copy"
|
|
6127
|
-
]],
|
|
6128
|
-
{ click: function(evt) {
|
|
6129
|
-
evt.stopPropagation();
|
|
6130
|
-
gobj_send_event(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
6131
|
-
} }
|
|
6132
|
-
], [
|
|
6133
|
-
"button",
|
|
6134
|
-
{
|
|
6135
|
-
class: "button p-1",
|
|
6136
|
-
title: "paste",
|
|
6137
|
-
"aria-label": "paste"
|
|
6138
|
-
},
|
|
6139
|
-
[[
|
|
6140
|
-
"span",
|
|
6141
|
-
{ class: "icon m-0" },
|
|
6142
|
-
"<i class=\"yi-paste\"></i>"
|
|
6143
|
-
], [
|
|
6144
|
-
"span",
|
|
6145
|
-
{
|
|
6146
|
-
class: "is-hidden-mobile pl-1 pr-1",
|
|
6147
|
-
i18n: "paste"
|
|
6148
|
-
},
|
|
6149
|
-
"paste"
|
|
6150
|
-
]],
|
|
6151
|
-
{ click: async function(evt) {
|
|
6152
|
-
evt.stopPropagation();
|
|
6153
|
-
await readClipboard$1(gobj);
|
|
6154
|
-
} }
|
|
6155
|
-
]]
|
|
6156
|
-
]]
|
|
6157
|
-
]]
|
|
6244
|
+
], build_toolbar(gobj)]
|
|
6158
6245
|
]);
|
|
6159
6246
|
gobj_write_attr(gobj, "$container", $container);
|
|
6160
6247
|
apply_form_mode(gobj, $container.querySelector("form"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuneta/gobj-ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/gobj-ui.cjs.js",
|
|
6
6
|
"module": "dist/gobj-ui.es.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@yuneta/gobj-js": "^7.8.7",
|
|
39
39
|
"bulma": "^1.0.4",
|
|
40
40
|
"i18next": "^26.3.6",
|
|
41
|
-
"maplibre-gl": "^6.
|
|
41
|
+
"maplibre-gl": "^6.1.0",
|
|
42
42
|
"tabulator-tables": "^6.5.2",
|
|
43
43
|
"tom-select": "^2.6.2",
|
|
44
44
|
"uplot": "^1.6.32",
|
package/src/c_yui_form.js
CHANGED
|
@@ -57,6 +57,7 @@ import { createJSONEditor } from 'vanilla-jsoneditor';
|
|
|
57
57
|
import "vanilla-jsoneditor/themes/jse-theme-dark.css";
|
|
58
58
|
import "./c_yui_form.css";
|
|
59
59
|
import {attach_clear, refresh_clear} from "./yui_inputs.js";
|
|
60
|
+
import {plan_toolbar} from "./form_toolbar_plan.js";
|
|
60
61
|
/* Read at field-build time only — deliberately NOT watched: the hosting
|
|
61
62
|
* dialog rebuilds the form on every open, and re-rendering a form under
|
|
62
63
|
* the user mid-edit would throw away what they typed. */
|
|
@@ -96,6 +97,20 @@ SDATA(data_type_t.DTP_BOOLEAN, "selectable", 0, true, "Rows selectable
|
|
|
96
97
|
SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
97
98
|
SDATA(data_type_t.DTP_BOOLEAN, "with_rownum", 0, false, "Print first column with row number"),
|
|
98
99
|
SDATA(data_type_t.DTP_BOOLEAN, "with_delete_row_btn", 0, false, "Print last column with delete row button"),
|
|
100
|
+
/*
|
|
101
|
+
* Which buttons the bottom toolbar shows, and in which order. The
|
|
102
|
+
* default is the five it has always shown, so nothing moves for a
|
|
103
|
+
* caller that does not set it.
|
|
104
|
+
*
|
|
105
|
+
* It exists because the toolbar was hardcoded: a three-field sign-up
|
|
106
|
+
* dialog with ONE action still got save + undo + clear + copy + paste,
|
|
107
|
+
* and the only way out was to not use this gclass at all. `[]` hides
|
|
108
|
+
* the toolbar entirely, for a caller that supplies its own buttons.
|
|
109
|
+
*
|
|
110
|
+
* Unknown names are ignored, with a warning: a typo must not silently
|
|
111
|
+
* drop the save button.
|
|
112
|
+
*/
|
|
113
|
+
SDATA(data_type_t.DTP_JSON, "toolbar", 0, ["save", "undo", "clear", "copy", "paste"], "Toolbar buttons to show, in order: save, undo, clear, copy, paste. [] hides the toolbar"),
|
|
99
114
|
/*
|
|
100
115
|
* Defaults for tabulator
|
|
101
116
|
*/
|
|
@@ -283,6 +298,90 @@ async function readClipboard(gobj)
|
|
|
283
298
|
}
|
|
284
299
|
}
|
|
285
300
|
|
|
301
|
+
/************************************************************
|
|
302
|
+
* Bottom toolbar, from the `toolbar` attr.
|
|
303
|
+
*
|
|
304
|
+
* The five buttons it can show, and their order, are the
|
|
305
|
+
* caller's: a dialog with a single action asks for ["save"],
|
|
306
|
+
* and [] leaves no toolbar at all. Default is the five it
|
|
307
|
+
* always had, so nothing moves for a caller that says nothing.
|
|
308
|
+
************************************************************/
|
|
309
|
+
function toolbar_button(gobj, name)
|
|
310
|
+
{
|
|
311
|
+
switch(name) {
|
|
312
|
+
case "save":
|
|
313
|
+
return ['button', {class: 'button p-1 button-save', title: 'save', 'aria-label': 'save', disabled: true}, [
|
|
314
|
+
['span', {class: 'icon m-0'}, '<i class="yi-floppy-disk"></i>'],
|
|
315
|
+
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'save'}, 'save']
|
|
316
|
+
], {
|
|
317
|
+
click: function(evt) {
|
|
318
|
+
evt.stopPropagation();
|
|
319
|
+
gobj_send_event(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
320
|
+
}
|
|
321
|
+
}];
|
|
322
|
+
case "undo":
|
|
323
|
+
return ['button', {class: 'button p-1 button-undo', title: 'undo', 'aria-label': 'undo', disabled: true}, [
|
|
324
|
+
['span', {class: 'icon m-0'}, '<i class="yi-arrow-rotate-left"></i>'],
|
|
325
|
+
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'undo'}, 'undo']
|
|
326
|
+
], {
|
|
327
|
+
click: function(evt) {
|
|
328
|
+
evt.stopPropagation();
|
|
329
|
+
gobj_send_event(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
330
|
+
}
|
|
331
|
+
}];
|
|
332
|
+
case "clear":
|
|
333
|
+
return ['button', {class: 'button p-1', title: 'clear', 'aria-label': 'clear'}, [
|
|
334
|
+
['span', {class: 'icon m-0'}, '<i class="yi-broom-wide"></i>'],
|
|
335
|
+
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'clear'}, 'clear']
|
|
336
|
+
], {
|
|
337
|
+
click: function(evt) {
|
|
338
|
+
evt.stopPropagation();
|
|
339
|
+
gobj_send_event(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
340
|
+
}
|
|
341
|
+
}];
|
|
342
|
+
case "copy":
|
|
343
|
+
return ['button', {class: 'button p-1', title: 'copy', 'aria-label': 'copy'}, [
|
|
344
|
+
['span', {class: 'icon m-0'}, '<i class="yi-copy"></i>'],
|
|
345
|
+
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'copy'}, 'copy']
|
|
346
|
+
], {click: function(evt) {
|
|
347
|
+
evt.stopPropagation();
|
|
348
|
+
gobj_send_event(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
349
|
+
}
|
|
350
|
+
}];
|
|
351
|
+
case "paste":
|
|
352
|
+
return ['button', {class: 'button p-1', title: 'paste', 'aria-label': 'paste'}, [
|
|
353
|
+
['span', {class: 'icon m-0'}, '<i class="yi-paste"></i>'],
|
|
354
|
+
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'paste'}, 'paste']
|
|
355
|
+
], {click: async function(evt) {
|
|
356
|
+
evt.stopPropagation();
|
|
357
|
+
await readClipboard(gobj);
|
|
358
|
+
}
|
|
359
|
+
}];
|
|
360
|
+
default:
|
|
361
|
+
return null; /* plan_toolbar() already warned */
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function build_toolbar(gobj)
|
|
366
|
+
{
|
|
367
|
+
const plan = plan_toolbar(gobj_read_attr(gobj, "toolbar"));
|
|
368
|
+
|
|
369
|
+
for(let name of plan.unknown) {
|
|
370
|
+
/* A typo must not silently drop the save button. */
|
|
371
|
+
log_warning(`${gobj_short_name(gobj)}: unknown toolbar button '${name}'`);
|
|
372
|
+
}
|
|
373
|
+
if(!plan.left.length && !plan.right.length) {
|
|
374
|
+
return ['span', {class: 'yui-toolbar-form-empty', style: 'display:none;'}, ''];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return ['div', {class: 'yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center', style: 'width:100%; flex-wrap:wrap; row-gap:6px;'}, [
|
|
378
|
+
['div', {class: 'p-1 is-flex is-flex-direction-row is-align-items-center', style:'column-gap:6px;'},
|
|
379
|
+
plan.left.map((n) => toolbar_button(gobj, n))],
|
|
380
|
+
['div', {class: 'p-1 is-flex is-flex-direction-row is-align-items-center', style:'column-gap:6px;'},
|
|
381
|
+
plan.right.map((n) => toolbar_button(gobj, n))]
|
|
382
|
+
]];
|
|
383
|
+
}
|
|
384
|
+
|
|
286
385
|
/************************************************************
|
|
287
386
|
* Build UI
|
|
288
387
|
************************************************************/
|
|
@@ -314,81 +413,7 @@ function build_ui(gobj)
|
|
|
314
413
|
/*----------------------------*
|
|
315
414
|
* Bottom toolbar
|
|
316
415
|
*----------------------------*/
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
/*----------------------------*
|
|
320
|
-
* Left buttons
|
|
321
|
-
*----------------------------*/
|
|
322
|
-
['div', {class: 'p-1 is-flex is-flex-direction-row is-align-items-center', style:'column-gap:6px;'}, [
|
|
323
|
-
/*----------------------------*
|
|
324
|
-
* Save
|
|
325
|
-
*----------------------------*/
|
|
326
|
-
['button', {class: 'button p-1 button-save', title: 'save', 'aria-label': 'save', disabled: true}, [
|
|
327
|
-
['span', {class: 'icon m-0'}, '<i class="yi-floppy-disk"></i>'],
|
|
328
|
-
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'save'}, 'save']
|
|
329
|
-
], {
|
|
330
|
-
click: function(evt) {
|
|
331
|
-
evt.stopPropagation();
|
|
332
|
-
gobj_send_event(gobj, "EV_SAVE_RECORD", {}, gobj);
|
|
333
|
-
}
|
|
334
|
-
}],
|
|
335
|
-
|
|
336
|
-
/*----------------------------*
|
|
337
|
-
* Undo
|
|
338
|
-
*----------------------------*/
|
|
339
|
-
['button', {class: 'button p-1 button-undo', title: 'undo', 'aria-label': 'undo', disabled: true}, [
|
|
340
|
-
['span', {class: 'icon m-0'}, '<i class="yi-arrow-rotate-left"></i>'],
|
|
341
|
-
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'undo'}, 'undo']
|
|
342
|
-
], {
|
|
343
|
-
click: function(evt) {
|
|
344
|
-
evt.stopPropagation();
|
|
345
|
-
gobj_send_event(gobj, "EV_UNDO_RECORD", {}, gobj);
|
|
346
|
-
}
|
|
347
|
-
}],
|
|
348
|
-
|
|
349
|
-
/*----------------------------*
|
|
350
|
-
* Clear
|
|
351
|
-
*----------------------------*/
|
|
352
|
-
['button', {class: 'button p-1', title: 'clear', 'aria-label': 'clear'}, [
|
|
353
|
-
['span', {class: 'icon m-0'}, '<i class="yi-broom-wide"></i>'],
|
|
354
|
-
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'clear'}, 'clear']
|
|
355
|
-
], {
|
|
356
|
-
click: function(evt) {
|
|
357
|
-
evt.stopPropagation();
|
|
358
|
-
gobj_send_event(gobj, "EV_CLEAR_RECORD", {}, gobj);
|
|
359
|
-
}
|
|
360
|
-
}],
|
|
361
|
-
]],
|
|
362
|
-
|
|
363
|
-
/*----------------------------*
|
|
364
|
-
* Right buttons
|
|
365
|
-
*----------------------------*/
|
|
366
|
-
['div', {class: 'p-1 is-flex is-flex-direction-row is-align-items-center', style:'column-gap:6px;'}, [
|
|
367
|
-
/*----------------------------*
|
|
368
|
-
* Copy
|
|
369
|
-
*----------------------------*/
|
|
370
|
-
['button', {class: 'button p-1', title: 'copy', 'aria-label': 'copy'}, [
|
|
371
|
-
['span', {class: 'icon m-0'}, '<i class="yi-copy"></i>'],
|
|
372
|
-
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'copy'}, 'copy']
|
|
373
|
-
], {click: function(evt) {
|
|
374
|
-
evt.stopPropagation();
|
|
375
|
-
gobj_send_event(gobj, "EV_COPY_RECORD", {}, gobj);
|
|
376
|
-
}
|
|
377
|
-
}],
|
|
378
|
-
|
|
379
|
-
/*----------------------------*
|
|
380
|
-
* Paste
|
|
381
|
-
*----------------------------*/
|
|
382
|
-
['button', {class: 'button p-1', title: 'paste', 'aria-label': 'paste'}, [
|
|
383
|
-
['span', {class: 'icon m-0'}, '<i class="yi-paste"></i>'],
|
|
384
|
-
['span', {class: 'is-hidden-mobile pl-1 pr-1', i18n: 'paste'}, 'paste']
|
|
385
|
-
], {click: async function(evt) {
|
|
386
|
-
evt.stopPropagation();
|
|
387
|
-
await readClipboard(gobj);
|
|
388
|
-
}
|
|
389
|
-
}]
|
|
390
|
-
]]
|
|
391
|
-
]]
|
|
416
|
+
build_toolbar(gobj)
|
|
392
417
|
]]
|
|
393
418
|
);
|
|
394
419
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* form_toolbar_plan.js
|
|
3
|
+
*
|
|
4
|
+
* Which buttons the C_YUI_FORM toolbar shows, and where.
|
|
5
|
+
*
|
|
6
|
+
* Pure, so it can be tested without a DOM: the gclass turns the
|
|
7
|
+
* plan into elements, this decides the plan.
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026, ArtGins.
|
|
10
|
+
* All Rights Reserved.
|
|
11
|
+
***********************************************************************/
|
|
12
|
+
|
|
13
|
+
/* Save/undo/clear sit on the left of the bar, copy/paste on the right.
|
|
14
|
+
* That split is what the layout has always drawn, and keeping it means
|
|
15
|
+
* a caller who drops one group does not leave a hole in the middle. */
|
|
16
|
+
const LEFT_BUTTONS = ["save", "undo", "clear"];
|
|
17
|
+
const RIGHT_BUTTONS = ["copy", "paste"];
|
|
18
|
+
|
|
19
|
+
const DEFAULT_TOOLBAR = ["save", "undo", "clear", "copy", "paste"];
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/***************************************************************
|
|
23
|
+
* plan_toolbar(wanted) -> {left, right, unknown}
|
|
24
|
+
*
|
|
25
|
+
* wanted an array of button names, in the order asked for.
|
|
26
|
+
* Anything that is not an array means "the default
|
|
27
|
+
* five", so a caller that sets nothing keeps exactly
|
|
28
|
+
* the toolbar this gclass always had.
|
|
29
|
+
* [] means no toolbar at all.
|
|
30
|
+
*
|
|
31
|
+
* unknown names that match no button. They are reported and
|
|
32
|
+
* NOT silently dropped: a typo in the list would
|
|
33
|
+
* otherwise remove the save button with no trace.
|
|
34
|
+
***************************************************************/
|
|
35
|
+
function plan_toolbar(wanted)
|
|
36
|
+
{
|
|
37
|
+
if(!Array.isArray(wanted)) {
|
|
38
|
+
wanted = DEFAULT_TOOLBAR;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let left = [];
|
|
42
|
+
let right = [];
|
|
43
|
+
let unknown = [];
|
|
44
|
+
|
|
45
|
+
for(let name of wanted) {
|
|
46
|
+
if(LEFT_BUTTONS.indexOf(name) >= 0) {
|
|
47
|
+
left.push(name);
|
|
48
|
+
} else if(RIGHT_BUTTONS.indexOf(name) >= 0) {
|
|
49
|
+
right.push(name);
|
|
50
|
+
} else {
|
|
51
|
+
unknown.push(name);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {left, right, unknown};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
export {plan_toolbar, DEFAULT_TOOLBAR};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {describe, it, expect} from "vitest";
|
|
2
|
+
import {plan_toolbar, DEFAULT_TOOLBAR} from "./form_toolbar_plan.js";
|
|
3
|
+
|
|
4
|
+
describe("plan_toolbar", () => {
|
|
5
|
+
|
|
6
|
+
it("says nothing -> the five buttons this gclass always had", () => {
|
|
7
|
+
const p = plan_toolbar(undefined);
|
|
8
|
+
expect(p.left).toEqual(["save", "undo", "clear"]);
|
|
9
|
+
expect(p.right).toEqual(["copy", "paste"]);
|
|
10
|
+
expect(p.unknown).toEqual([]);
|
|
11
|
+
expect(DEFAULT_TOOLBAR.length).toBe(5);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("a non-array is not an empty toolbar: it is the default", () => {
|
|
15
|
+
/* Reading a JSON attr that was never set must not silently
|
|
16
|
+
* leave a form with no save button. */
|
|
17
|
+
for(const v of [null, "save", 0, {}]) {
|
|
18
|
+
expect(plan_toolbar(v).left).toContain("save");
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("[] leaves no toolbar at all", () => {
|
|
23
|
+
const p = plan_toolbar([]);
|
|
24
|
+
expect(p.left).toEqual([]);
|
|
25
|
+
expect(p.right).toEqual([]);
|
|
26
|
+
expect(p.unknown).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("one action: a dialog asks for save only", () => {
|
|
30
|
+
const p = plan_toolbar(["save"]);
|
|
31
|
+
expect(p.left).toEqual(["save"]);
|
|
32
|
+
expect(p.right).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("keeps the order asked for inside each side", () => {
|
|
36
|
+
const p = plan_toolbar(["clear", "save", "paste", "copy"]);
|
|
37
|
+
expect(p.left).toEqual(["clear", "save"]);
|
|
38
|
+
expect(p.right).toEqual(["paste", "copy"]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("an unknown name is REPORTED, never silently dropped", () => {
|
|
42
|
+
/* A typo here would otherwise remove a button and leave no
|
|
43
|
+
* trace of why it is missing. */
|
|
44
|
+
const p = plan_toolbar(["save", "sav", "undo"]);
|
|
45
|
+
expect(p.left).toEqual(["save", "undo"]);
|
|
46
|
+
expect(p.unknown).toEqual(["sav"]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("drops one whole side without disturbing the other", () => {
|
|
50
|
+
const p = plan_toolbar(["save", "undo", "clear"]);
|
|
51
|
+
expect(p.left).toEqual(["save", "undo", "clear"]);
|
|
52
|
+
expect(p.right).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
});
|