@yuneta/gobj-ui 2.2.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/gobj-ui.cjs.js +702 -1024
- package/dist/gobj-ui.es.js +703 -1025
- package/package.json +1 -1
- package/src/c_yui_form.js +322 -51
- package/src/c_yui_map.js +7 -4
- package/src/c_yui_nav.js +90 -1
- package/src/c_yui_shell.css +68 -2
- package/src/c_yui_shell.js +103 -18
- package/src/c_yui_treedb_topic_with_form.js +216 -1171
- package/src/nav_cards_helpers.js +71 -0
- package/src/nav_cards_helpers.test.js +122 -0
- package/src/shell_section_index.js +106 -0
- package/src/shell_section_index.test.js +151 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_flag_t, getPositionRelativeToBody, gobj_command, gobj_create, gobj_create_service, gobj_current_state, gobj_default_service, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_has_attr, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_pure_number, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict,
|
|
1
|
+
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_default_service, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_has_attr, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_pure_number, is_string, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_get_ids, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_json, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
|
|
2
2
|
import i18next, { t } from "i18next";
|
|
3
3
|
import TomSelect from "tom-select";
|
|
4
|
+
import { JSONEditor } from "vanilla-jsoneditor";
|
|
4
5
|
import { TabulatorFull } from "tabulator-tables";
|
|
5
6
|
import maplibregl from "maplibre-gl";
|
|
6
7
|
import uPlot from "uplot";
|
|
7
8
|
import { BaseLayout, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HistoryEvent, NodeEvent, Toolbar, register } from "@antv/g6";
|
|
8
|
-
import { JSONEditor } from "vanilla-jsoneditor";
|
|
9
9
|
//#region \0rolldown/runtime.js
|
|
10
10
|
var __create = Object.create;
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -5394,6 +5394,10 @@ var attrs_table$11 = [
|
|
|
5394
5394
|
SDATA(data_type_t.DTP_POINTER, "template", 0, "<div>You must supply a template for form</div>", "Template for form, object -> TEMPLATE, array -> DESC"),
|
|
5395
5395
|
SDATA(data_type_t.DTP_JSON, "record", 0, "{}", "Data to form"),
|
|
5396
5396
|
SDATA(data_type_t.DTP_JSON, "placeholders", 0, "{}", "Placeholder values for form fields"),
|
|
5397
|
+
SDATA(data_type_t.DTP_JSON, "fkey_options", 0, "{}", "Options for fkey fields: {topic_name: [ids or {id} records]}, read at build time"),
|
|
5398
|
+
SDATA(data_type_t.DTP_STRING, "form_mode", 0, "", "'create' (pkey editable+required) or 'update' (pkey readonly); empty = no pkey handling"),
|
|
5399
|
+
SDATA(data_type_t.DTP_STRING, "pkey", 0, "id", "Primary key field name, used by form_mode"),
|
|
5400
|
+
SDATA(data_type_t.DTP_STRING, "topic_name", 0, "", "Treedb topic name: labels use the i18n cascade '<topic>.<col>' -> '<col>' -> header (like table headers)"),
|
|
5397
5401
|
SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Default is editable, set false to readonly"),
|
|
5398
5402
|
SDATA(data_type_t.DTP_BOOLEAN, "selectable", 0, true, "Rows selectable with 'click' (BAD with editable=true)"),
|
|
5399
5403
|
SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, false, "Show a first column with checkbox to select rows"),
|
|
@@ -5515,34 +5519,35 @@ function build_ui$9(gobj) {
|
|
|
5515
5519
|
let $container = createElement2([
|
|
5516
5520
|
"div",
|
|
5517
5521
|
{
|
|
5518
|
-
class: "C_YUI_FORM is-flex is-flex-direction-
|
|
5522
|
+
class: "C_YUI_FORM is-flex is-flex-direction-column overscroll-contain",
|
|
5519
5523
|
style: "height:100%; width:100%; box-sizing:border-box;"
|
|
5520
5524
|
},
|
|
5521
5525
|
[[
|
|
5522
5526
|
"div",
|
|
5523
5527
|
{
|
|
5524
5528
|
class: "yui-form is-flex-grow-1 p-2",
|
|
5525
|
-
style: "overflow-y:auto;
|
|
5529
|
+
style: "overflow-y:auto; min-height:0;"
|
|
5526
5530
|
},
|
|
5527
5531
|
build_form(gobj)
|
|
5528
5532
|
], [
|
|
5529
5533
|
"div",
|
|
5530
5534
|
{
|
|
5531
|
-
class: "yui-toolbar-form
|
|
5532
|
-
style: "
|
|
5535
|
+
class: "yui-toolbar-form is-flex-shrink-0 is-flex is-flex-direction-row is-justify-content-space-between is-align-items-center",
|
|
5536
|
+
style: "width:100%; flex-wrap:wrap; row-gap:6px;"
|
|
5533
5537
|
},
|
|
5534
5538
|
[[
|
|
5535
5539
|
"div",
|
|
5536
5540
|
{
|
|
5537
|
-
class: "p-1 is-flex is-flex-direction-
|
|
5538
|
-
style: "
|
|
5541
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
5542
|
+
style: "column-gap:6px;"
|
|
5539
5543
|
},
|
|
5540
5544
|
[
|
|
5541
5545
|
[
|
|
5542
5546
|
"button",
|
|
5543
5547
|
{
|
|
5544
|
-
class: "button p-1
|
|
5545
|
-
|
|
5548
|
+
class: "button p-1 button-save",
|
|
5549
|
+
title: "save",
|
|
5550
|
+
"aria-label": "save",
|
|
5546
5551
|
disabled: true
|
|
5547
5552
|
},
|
|
5548
5553
|
[[
|
|
@@ -5552,7 +5557,7 @@ function build_ui$9(gobj) {
|
|
|
5552
5557
|
], [
|
|
5553
5558
|
"span",
|
|
5554
5559
|
{
|
|
5555
|
-
class: "is-hidden-mobile",
|
|
5560
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
5556
5561
|
i18n: "save"
|
|
5557
5562
|
},
|
|
5558
5563
|
"save"
|
|
@@ -5565,8 +5570,9 @@ function build_ui$9(gobj) {
|
|
|
5565
5570
|
[
|
|
5566
5571
|
"button",
|
|
5567
5572
|
{
|
|
5568
|
-
class: "button p-1
|
|
5569
|
-
|
|
5573
|
+
class: "button p-1 button-undo",
|
|
5574
|
+
title: "undo",
|
|
5575
|
+
"aria-label": "undo",
|
|
5570
5576
|
disabled: true
|
|
5571
5577
|
},
|
|
5572
5578
|
[[
|
|
@@ -5576,7 +5582,7 @@ function build_ui$9(gobj) {
|
|
|
5576
5582
|
], [
|
|
5577
5583
|
"span",
|
|
5578
5584
|
{
|
|
5579
|
-
class: "is-hidden-mobile",
|
|
5585
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
5580
5586
|
i18n: "undo"
|
|
5581
5587
|
},
|
|
5582
5588
|
"undo"
|
|
@@ -5589,8 +5595,9 @@ function build_ui$9(gobj) {
|
|
|
5589
5595
|
[
|
|
5590
5596
|
"button",
|
|
5591
5597
|
{
|
|
5592
|
-
class: "button p-1
|
|
5593
|
-
|
|
5598
|
+
class: "button p-1",
|
|
5599
|
+
title: "clear",
|
|
5600
|
+
"aria-label": "clear"
|
|
5594
5601
|
},
|
|
5595
5602
|
[[
|
|
5596
5603
|
"span",
|
|
@@ -5599,7 +5606,7 @@ function build_ui$9(gobj) {
|
|
|
5599
5606
|
], [
|
|
5600
5607
|
"span",
|
|
5601
5608
|
{
|
|
5602
|
-
class: "is-hidden-mobile",
|
|
5609
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
5603
5610
|
i18n: "clear"
|
|
5604
5611
|
},
|
|
5605
5612
|
"clear"
|
|
@@ -5613,14 +5620,15 @@ function build_ui$9(gobj) {
|
|
|
5613
5620
|
], [
|
|
5614
5621
|
"div",
|
|
5615
5622
|
{
|
|
5616
|
-
class: "p-1 is-flex is-flex-direction-
|
|
5617
|
-
style: "
|
|
5623
|
+
class: "p-1 is-flex is-flex-direction-row is-align-items-center",
|
|
5624
|
+
style: "column-gap:6px;"
|
|
5618
5625
|
},
|
|
5619
5626
|
[[
|
|
5620
5627
|
"button",
|
|
5621
5628
|
{
|
|
5622
|
-
class: "button p-1
|
|
5623
|
-
|
|
5629
|
+
class: "button p-1",
|
|
5630
|
+
title: "copy",
|
|
5631
|
+
"aria-label": "copy"
|
|
5624
5632
|
},
|
|
5625
5633
|
[[
|
|
5626
5634
|
"span",
|
|
@@ -5629,7 +5637,7 @@ function build_ui$9(gobj) {
|
|
|
5629
5637
|
], [
|
|
5630
5638
|
"span",
|
|
5631
5639
|
{
|
|
5632
|
-
class: "is-hidden-mobile",
|
|
5640
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
5633
5641
|
i18n: "copy"
|
|
5634
5642
|
},
|
|
5635
5643
|
"copy"
|
|
@@ -5641,8 +5649,9 @@ function build_ui$9(gobj) {
|
|
|
5641
5649
|
], [
|
|
5642
5650
|
"button",
|
|
5643
5651
|
{
|
|
5644
|
-
class: "button p-1
|
|
5645
|
-
|
|
5652
|
+
class: "button p-1",
|
|
5653
|
+
title: "paste",
|
|
5654
|
+
"aria-label": "paste"
|
|
5646
5655
|
},
|
|
5647
5656
|
[[
|
|
5648
5657
|
"span",
|
|
@@ -5651,7 +5660,7 @@ function build_ui$9(gobj) {
|
|
|
5651
5660
|
], [
|
|
5652
5661
|
"span",
|
|
5653
5662
|
{
|
|
5654
|
-
class: "is-hidden-mobile",
|
|
5663
|
+
class: "is-hidden-mobile pl-1 pr-1",
|
|
5655
5664
|
i18n: "paste"
|
|
5656
5665
|
},
|
|
5657
5666
|
"paste"
|
|
@@ -5665,6 +5674,7 @@ function build_ui$9(gobj) {
|
|
|
5665
5674
|
]]
|
|
5666
5675
|
]);
|
|
5667
5676
|
gobj_write_attr(gobj, "$container", $container);
|
|
5677
|
+
apply_form_mode(gobj, $container.querySelector("form"));
|
|
5668
5678
|
refresh_language($container, t);
|
|
5669
5679
|
let $parent = gobj_read_attr(gobj, "$parent");
|
|
5670
5680
|
if ($parent) $parent.appendChild($container);
|
|
@@ -5707,7 +5717,11 @@ function build_html_form(gobj, $form, prefix, template) {
|
|
|
5707
5717
|
const field_desc = template_get_field_desc(key, value);
|
|
5708
5718
|
let html_field_conf = build_form_field_conf(gobj, field_desc);
|
|
5709
5719
|
html_field_conf.name = field_desc.name;
|
|
5710
|
-
|
|
5720
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
5721
|
+
let label_keys = topic_name ? [topic_name + "." + field_desc.name, field_desc.name] : [field_desc.name];
|
|
5722
|
+
const NO_I18N = "\0";
|
|
5723
|
+
html_field_conf.label = t(label_keys, { defaultValue: field_desc.header });
|
|
5724
|
+
html_field_conf.label_i18n = t(field_desc.name, { defaultValue: NO_I18N }) !== NO_I18N ? field_desc.name : null;
|
|
5711
5725
|
html_field_conf.placeholder = field_desc.placeholder ? t(field_desc.placeholder) : "";
|
|
5712
5726
|
let placeholders = gobj_read_attr(gobj, "placeholders");
|
|
5713
5727
|
if (placeholders[key]) html_field_conf.placeholder = placeholders[key];
|
|
@@ -5751,7 +5765,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
5751
5765
|
switch (field_desc.type) {
|
|
5752
5766
|
case "object":
|
|
5753
5767
|
case "dict":
|
|
5754
|
-
field_conf.tag = "
|
|
5768
|
+
field_conf.tag = "jsoneditor";
|
|
5755
5769
|
break;
|
|
5756
5770
|
case "template":
|
|
5757
5771
|
field_conf.tag = "fieldset";
|
|
@@ -5766,7 +5780,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
5766
5780
|
break;
|
|
5767
5781
|
case "array":
|
|
5768
5782
|
case "list":
|
|
5769
|
-
field_conf.tag = "
|
|
5783
|
+
field_conf.tag = "jsoneditor";
|
|
5770
5784
|
break;
|
|
5771
5785
|
case "enum":
|
|
5772
5786
|
switch (field_desc.real_type) {
|
|
@@ -5784,7 +5798,10 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
5784
5798
|
}
|
|
5785
5799
|
break;
|
|
5786
5800
|
case "hook": break;
|
|
5787
|
-
case "fkey":
|
|
5801
|
+
case "fkey":
|
|
5802
|
+
field_conf.tag = "select2";
|
|
5803
|
+
field_conf.options = get_fkey_select_options(gobj, field_desc);
|
|
5804
|
+
break;
|
|
5788
5805
|
case "image":
|
|
5789
5806
|
case "string":
|
|
5790
5807
|
field_conf.tag = "input";
|
|
@@ -5869,6 +5886,112 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
5869
5886
|
}
|
|
5870
5887
|
return field_conf;
|
|
5871
5888
|
}
|
|
5889
|
+
/************************************************************
|
|
5890
|
+
* Options for a fkey field: the ids of the parent topic's
|
|
5891
|
+
* rows, taken from the `fkey_options` attr
|
|
5892
|
+
* ({topic_name: [ids or {id} records]}, supplied by the host —
|
|
5893
|
+
* the form never queries the backend or its parent itself).
|
|
5894
|
+
* Read at build time.
|
|
5895
|
+
************************************************************/
|
|
5896
|
+
function get_fkey_select_options(gobj, field_desc) {
|
|
5897
|
+
let fkey_desc = field_desc.fkey;
|
|
5898
|
+
if (!is_object(fkey_desc) || Object.keys(fkey_desc).length === 0) {
|
|
5899
|
+
log_error(sprintf("%s: fkey field '%s' without fkey mapping", gobj_short_name(gobj), field_desc.name));
|
|
5900
|
+
return [];
|
|
5901
|
+
}
|
|
5902
|
+
let topic_name = Object.keys(fkey_desc)[0];
|
|
5903
|
+
let fkey_options = gobj_read_attr(gobj, "fkey_options");
|
|
5904
|
+
let list = is_object(fkey_options) ? fkey_options[topic_name] : null;
|
|
5905
|
+
if (!is_array(list)) {
|
|
5906
|
+
log_warning(sprintf("%s: no fkey_options for topic '%s' (field '%s')", gobj_short_name(gobj), topic_name, field_desc.name));
|
|
5907
|
+
return [];
|
|
5908
|
+
}
|
|
5909
|
+
let options = [];
|
|
5910
|
+
for (let item of list) {
|
|
5911
|
+
let id = is_object(item) ? item.id : item;
|
|
5912
|
+
if (is_string(id) && !empty_string(id)) options.push(id);
|
|
5913
|
+
}
|
|
5914
|
+
return options;
|
|
5915
|
+
}
|
|
5916
|
+
/************************************************************
|
|
5917
|
+
* Encode the select2 value (array of ids) into fkey ref(s)
|
|
5918
|
+
* "topic_name^id^hook_name", shaped by the column real type:
|
|
5919
|
+
* string -> single ref (or null if no selection)
|
|
5920
|
+
* object/dict -> {ref: true, ...}
|
|
5921
|
+
* array/list -> [ref, ...]
|
|
5922
|
+
************************************************************/
|
|
5923
|
+
function build_fkey_ref$1(gobj, field_desc, value) {
|
|
5924
|
+
let fkey_desc = field_desc.fkey;
|
|
5925
|
+
if (!is_object(fkey_desc) || Object.keys(fkey_desc).length === 0) {
|
|
5926
|
+
log_error(sprintf("%s: fkey field '%s' without fkey mapping", gobj_short_name(gobj), field_desc.name));
|
|
5927
|
+
return null;
|
|
5928
|
+
}
|
|
5929
|
+
let topic_name = Object.keys(fkey_desc)[0];
|
|
5930
|
+
let hook_name = fkey_desc[topic_name];
|
|
5931
|
+
let refs;
|
|
5932
|
+
switch (field_desc.real_type) {
|
|
5933
|
+
case "string":
|
|
5934
|
+
refs = null;
|
|
5935
|
+
break;
|
|
5936
|
+
case "object":
|
|
5937
|
+
case "dict":
|
|
5938
|
+
refs = {};
|
|
5939
|
+
break;
|
|
5940
|
+
case "array":
|
|
5941
|
+
case "list":
|
|
5942
|
+
refs = [];
|
|
5943
|
+
break;
|
|
5944
|
+
default:
|
|
5945
|
+
log_error(sprintf("%s: fkey field '%s' with bad real_type '%s'", gobj_short_name(gobj), field_desc.name, field_desc.real_type));
|
|
5946
|
+
return null;
|
|
5947
|
+
}
|
|
5948
|
+
if (!is_array(value)) value = value ? [value] : [];
|
|
5949
|
+
for (let v of value) {
|
|
5950
|
+
let id = is_object(v) ? v.id : v;
|
|
5951
|
+
if (!is_string(id) || empty_string(id)) {
|
|
5952
|
+
log_error(sprintf("%s: fkey field '%s' with bad value: %s", gobj_short_name(gobj), field_desc.name, JSON.stringify(v)));
|
|
5953
|
+
continue;
|
|
5954
|
+
}
|
|
5955
|
+
let ref = topic_name + "^" + id + "^" + hook_name;
|
|
5956
|
+
switch (field_desc.real_type) {
|
|
5957
|
+
case "string": return ref;
|
|
5958
|
+
case "object":
|
|
5959
|
+
case "dict":
|
|
5960
|
+
refs[ref] = true;
|
|
5961
|
+
break;
|
|
5962
|
+
case "array":
|
|
5963
|
+
case "list":
|
|
5964
|
+
refs.push(ref);
|
|
5965
|
+
break;
|
|
5966
|
+
}
|
|
5967
|
+
}
|
|
5968
|
+
return refs;
|
|
5969
|
+
}
|
|
5970
|
+
/************************************************************
|
|
5971
|
+
* Apply the form mode to the pkey field:
|
|
5972
|
+
* "update" -> pkey readonly, not required
|
|
5973
|
+
* "create" -> pkey writable and required (readonly if rowid)
|
|
5974
|
+
* Empty form_mode keeps the template-declared behaviour
|
|
5975
|
+
* (backward compatible).
|
|
5976
|
+
************************************************************/
|
|
5977
|
+
function apply_form_mode(gobj, $form) {
|
|
5978
|
+
let mode = gobj_read_str_attr(gobj, "form_mode");
|
|
5979
|
+
if (empty_string(mode)) return;
|
|
5980
|
+
let pkey = gobj_read_str_attr(gobj, "pkey");
|
|
5981
|
+
let $input = $form.querySelector(`[name="${pkey}"]`);
|
|
5982
|
+
if (!$input) {
|
|
5983
|
+
log_error(sprintf("%s: form_mode '%s' but form has no pkey field '%s'", gobj_short_name(gobj), mode, pkey));
|
|
5984
|
+
return;
|
|
5985
|
+
}
|
|
5986
|
+
let is_rowid = $input.field_desc && $input.field_desc.type === "rowid";
|
|
5987
|
+
if (mode === "create" && !is_rowid) {
|
|
5988
|
+
$input.removeAttribute("readonly");
|
|
5989
|
+
$input.setAttribute("required", "");
|
|
5990
|
+
} else {
|
|
5991
|
+
$input.setAttribute("readonly", "readonly");
|
|
5992
|
+
$input.removeAttribute("required");
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5872
5995
|
/******************************************************************
|
|
5873
5996
|
* With html field conf builds an html element,
|
|
5874
5997
|
* ready to add to the form
|
|
@@ -5899,7 +6022,7 @@ function build_form_field_conf(gobj, field_desc) {
|
|
|
5899
6022
|
* default_value:
|
|
5900
6023
|
* }
|
|
5901
6024
|
******************************************************************/
|
|
5902
|
-
function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, name, label, placeholder, options, extras, readonly, required, hidden, default_value }) {
|
|
6025
|
+
function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, name, label, label_i18n, placeholder, options, extras, readonly, required, hidden, default_value }) {
|
|
5903
6026
|
let $element;
|
|
5904
6027
|
let kw_add_table_row = {
|
|
5905
6028
|
$row: null,
|
|
@@ -5921,7 +6044,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
5921
6044
|
"label",
|
|
5922
6045
|
{
|
|
5923
6046
|
class: "label ",
|
|
5924
|
-
i18n: label,
|
|
6047
|
+
i18n: label_i18n || label,
|
|
5925
6048
|
for: name
|
|
5926
6049
|
},
|
|
5927
6050
|
label
|
|
@@ -5955,7 +6078,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
5955
6078
|
"label",
|
|
5956
6079
|
{
|
|
5957
6080
|
class: "label ",
|
|
5958
|
-
i18n: label,
|
|
6081
|
+
i18n: label_i18n || label,
|
|
5959
6082
|
for: name
|
|
5960
6083
|
},
|
|
5961
6084
|
label
|
|
@@ -5997,7 +6120,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
5997
6120
|
"label",
|
|
5998
6121
|
{
|
|
5999
6122
|
class: "label ",
|
|
6000
|
-
i18n: label,
|
|
6123
|
+
i18n: label_i18n || label,
|
|
6001
6124
|
for: name
|
|
6002
6125
|
},
|
|
6003
6126
|
label
|
|
@@ -6028,7 +6151,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6028
6151
|
"label",
|
|
6029
6152
|
{
|
|
6030
6153
|
class: "label ",
|
|
6031
|
-
i18n: label,
|
|
6154
|
+
i18n: label_i18n || label,
|
|
6032
6155
|
for: name
|
|
6033
6156
|
},
|
|
6034
6157
|
label
|
|
@@ -6059,7 +6182,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6059
6182
|
"label",
|
|
6060
6183
|
{
|
|
6061
6184
|
class: "label",
|
|
6062
|
-
i18n: label
|
|
6185
|
+
i18n: label_i18n || label
|
|
6063
6186
|
},
|
|
6064
6187
|
label
|
|
6065
6188
|
]]
|
|
@@ -6143,7 +6266,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6143
6266
|
"label",
|
|
6144
6267
|
{
|
|
6145
6268
|
class: "label ",
|
|
6146
|
-
i18n: label,
|
|
6269
|
+
i18n: label_i18n || label,
|
|
6147
6270
|
for: name
|
|
6148
6271
|
},
|
|
6149
6272
|
label
|
|
@@ -6169,7 +6292,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6169
6292
|
switch (tag) {
|
|
6170
6293
|
case "input": {
|
|
6171
6294
|
let attrs = {
|
|
6172
|
-
class: `input ${name === "
|
|
6295
|
+
class: `input ${name === gobj_read_str_attr(gobj, "pkey") ? "with-focus" : ""}`,
|
|
6173
6296
|
name,
|
|
6174
6297
|
type: inputType,
|
|
6175
6298
|
placeholder
|
|
@@ -6222,8 +6345,8 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6222
6345
|
$control.appendChild($extend);
|
|
6223
6346
|
break;
|
|
6224
6347
|
}
|
|
6225
|
-
case "select":
|
|
6226
|
-
$
|
|
6348
|
+
case "select": {
|
|
6349
|
+
let $wrapper = createElement2([
|
|
6227
6350
|
"div",
|
|
6228
6351
|
{ class: "select" },
|
|
6229
6352
|
[
|
|
@@ -6236,11 +6359,16 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6236
6359
|
i18n: option
|
|
6237
6360
|
},
|
|
6238
6361
|
option
|
|
6239
|
-
])
|
|
6362
|
+
]),
|
|
6363
|
+
{ "change": function(evt) {
|
|
6364
|
+
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6365
|
+
} }
|
|
6240
6366
|
]
|
|
6241
6367
|
]);
|
|
6242
|
-
$control.appendChild($
|
|
6368
|
+
$control.appendChild($wrapper);
|
|
6369
|
+
$extend = $wrapper.querySelector("select");
|
|
6243
6370
|
break;
|
|
6371
|
+
}
|
|
6244
6372
|
case "select2":
|
|
6245
6373
|
$extend = createElement2([
|
|
6246
6374
|
"select",
|
|
@@ -6262,6 +6390,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6262
6390
|
$extend.tom_select = new TomSelect($extend, {
|
|
6263
6391
|
allowEmptyOption: true,
|
|
6264
6392
|
placeholder,
|
|
6393
|
+
maxItems: field_desc.type === "fkey" && field_desc.real_type === "string" ? 1 : null,
|
|
6265
6394
|
plugins: {
|
|
6266
6395
|
"clear_button": { "title": t("Remove all selected options") },
|
|
6267
6396
|
"no_active_items": {},
|
|
@@ -6273,17 +6402,26 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6273
6402
|
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6274
6403
|
});
|
|
6275
6404
|
break;
|
|
6276
|
-
case "checkbox":
|
|
6277
|
-
$
|
|
6405
|
+
case "checkbox": {
|
|
6406
|
+
let $wrapper = createElement2([
|
|
6278
6407
|
"label",
|
|
6279
6408
|
{ class: "checkbox" },
|
|
6280
|
-
[
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6409
|
+
[
|
|
6410
|
+
"input",
|
|
6411
|
+
{
|
|
6412
|
+
type: "checkbox",
|
|
6413
|
+
name
|
|
6414
|
+
},
|
|
6415
|
+
"",
|
|
6416
|
+
{ "change": function(evt) {
|
|
6417
|
+
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6418
|
+
} }
|
|
6419
|
+
]
|
|
6284
6420
|
]);
|
|
6285
|
-
$control.appendChild($
|
|
6421
|
+
$control.appendChild($wrapper);
|
|
6422
|
+
$extend = $wrapper.querySelector("input");
|
|
6286
6423
|
break;
|
|
6424
|
+
}
|
|
6287
6425
|
case "radio":
|
|
6288
6426
|
$extend = createOneHtml(`
|
|
6289
6427
|
<label class="radio">
|
|
@@ -6296,6 +6434,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6296
6434
|
case "jsoneditor": {
|
|
6297
6435
|
let attrs = {
|
|
6298
6436
|
class: "jsoneditor jse-theme-dark",
|
|
6437
|
+
name,
|
|
6299
6438
|
style: ""
|
|
6300
6439
|
};
|
|
6301
6440
|
Object.assign(attrs, extras);
|
|
@@ -6303,6 +6442,18 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
|
|
|
6303
6442
|
else if (required) attrs.required = "";
|
|
6304
6443
|
$extend = createElement2(["div", attrs]);
|
|
6305
6444
|
$control.appendChild($extend);
|
|
6445
|
+
$extend.jsoneditor = new JSONEditor({
|
|
6446
|
+
target: $extend,
|
|
6447
|
+
props: {
|
|
6448
|
+
readOnly: !!readonly,
|
|
6449
|
+
onChange: function() {
|
|
6450
|
+
gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
|
|
6451
|
+
},
|
|
6452
|
+
timestampTag: function({ field, value, path }) {
|
|
6453
|
+
return field === "__t__" || field === "__tm__" || field === "tm" || field === "t" || field === "time" || field === "from_t" || field === "to_t" || field === "t_input" || field === "t_output" || field === "from_tm" || field === "to_tm";
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
});
|
|
6306
6457
|
break;
|
|
6307
6458
|
}
|
|
6308
6459
|
case "fieldset": {
|
|
@@ -6698,7 +6849,7 @@ function template2columns(gobj, columns, template, sub_elements) {
|
|
|
6698
6849
|
/************************************************************
|
|
6699
6850
|
*
|
|
6700
6851
|
************************************************************/
|
|
6701
|
-
function validate_form
|
|
6852
|
+
function validate_form(gobj, $form) {
|
|
6702
6853
|
let validated = true;
|
|
6703
6854
|
if (!$form.checkValidity()) validated = false;
|
|
6704
6855
|
$form.querySelectorAll(".yui-tabulator-table").forEach(($table) => {
|
|
@@ -6719,7 +6870,7 @@ function getInputValue($input) {
|
|
|
6719
6870
|
/************************************************************
|
|
6720
6871
|
* Function to get input value based on its type
|
|
6721
6872
|
************************************************************/
|
|
6722
|
-
function get_form_values
|
|
6873
|
+
function get_form_values(gobj, $form) {
|
|
6723
6874
|
const record = {};
|
|
6724
6875
|
$form.querySelectorAll(".yui-form-data-input").forEach(($input) => {
|
|
6725
6876
|
$input.closest(".xfield");
|
|
@@ -6800,9 +6951,10 @@ function get_form_values$1(gobj, $form) {
|
|
|
6800
6951
|
/************************************************************
|
|
6801
6952
|
*
|
|
6802
6953
|
************************************************************/
|
|
6803
|
-
function clear_data
|
|
6954
|
+
function clear_data(gobj, $form) {
|
|
6955
|
+
let pkey = gobj_read_str_attr(gobj, "pkey");
|
|
6804
6956
|
$form.querySelectorAll("input, select, textarea, .jsoneditor").forEach(($input) => {
|
|
6805
|
-
if ($input.name !==
|
|
6957
|
+
if ($input.name !== pkey) if ($input.classList.contains("jsoneditor")) {
|
|
6806
6958
|
let jsoneditor = $input.jsoneditor;
|
|
6807
6959
|
if (jsoneditor) jsoneditor.set({ text: "" });
|
|
6808
6960
|
} else if ($input.type === "checkbox") $input.checked = false;
|
|
@@ -6821,7 +6973,7 @@ function clear_data$1(gobj, $form) {
|
|
|
6821
6973
|
/************************************************************
|
|
6822
6974
|
*
|
|
6823
6975
|
************************************************************/
|
|
6824
|
-
function clear_validation
|
|
6976
|
+
function clear_validation(gobj, $form) {
|
|
6825
6977
|
$form.querySelectorAll("input").forEach(($input) => {
|
|
6826
6978
|
$input.classList.remove("is-danger");
|
|
6827
6979
|
});
|
|
@@ -6859,10 +7011,10 @@ function convertColor(color) {
|
|
|
6859
7011
|
/************************************************************
|
|
6860
7012
|
*
|
|
6861
7013
|
************************************************************/
|
|
6862
|
-
function set_form_values
|
|
7014
|
+
function set_form_values(gobj, template, $form, record) {
|
|
6863
7015
|
let modified = false;
|
|
6864
|
-
clear_data
|
|
6865
|
-
if (!record || record.length === 0) record = create_template_record(template);
|
|
7016
|
+
clear_data(gobj, $form);
|
|
7017
|
+
if (!record || Object.keys(record).length === 0) record = create_template_record(template);
|
|
6866
7018
|
$form.querySelectorAll(".yui-form-data-input").forEach(($input) => {
|
|
6867
7019
|
let field_desc = $input.field_desc;
|
|
6868
7020
|
let name = $input.dataset.treedb_name;
|
|
@@ -6878,10 +7030,16 @@ function set_form_values$1(gobj, template, $form, record) {
|
|
|
6878
7030
|
modified = true;
|
|
6879
7031
|
}
|
|
6880
7032
|
value = treedb_value_2_form_value(gobj, field_desc, value);
|
|
7033
|
+
if (value === void 0) value = null;
|
|
6881
7034
|
switch (tag) {
|
|
6882
7035
|
case "input":
|
|
6883
7036
|
switch (input_type) {
|
|
6884
|
-
case "datetime-local":
|
|
7037
|
+
case "datetime-local":
|
|
7038
|
+
if (value instanceof Date && !isNaN(value.getTime())) {
|
|
7039
|
+
let pad = (n) => String(n).padStart(2, "0");
|
|
7040
|
+
$input.value = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}T${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
|
7041
|
+
} else $input.value = "";
|
|
7042
|
+
break;
|
|
6885
7043
|
case "color":
|
|
6886
7044
|
$input.value = convertColor(value);
|
|
6887
7045
|
break;
|
|
@@ -6908,11 +7066,11 @@ function set_form_values$1(gobj, template, $form, record) {
|
|
|
6908
7066
|
break;
|
|
6909
7067
|
case "jsoneditor":
|
|
6910
7068
|
let jsoneditor = $input.jsoneditor;
|
|
6911
|
-
if (jsoneditor) jsoneditor.set(value);
|
|
7069
|
+
if (jsoneditor) jsoneditor.set(value ? value : { text: "" });
|
|
6912
7070
|
break;
|
|
6913
7071
|
case "fieldset": break;
|
|
6914
7072
|
case "table":
|
|
6915
|
-
load_tabulator_data(gobj, $input, value);
|
|
7073
|
+
load_tabulator_data(gobj, $input, value ? value : []);
|
|
6916
7074
|
break;
|
|
6917
7075
|
case "coordinates":
|
|
6918
7076
|
if ($input.tagName !== "INPUT") $input = $input.querySelector("input");
|
|
@@ -6958,7 +7116,22 @@ function treedb_value_2_form_value(gobj, field_desc, value) {
|
|
|
6958
7116
|
let type = field_desc.type;
|
|
6959
7117
|
field_desc.real_type;
|
|
6960
7118
|
switch (type) {
|
|
6961
|
-
case "fkey":
|
|
7119
|
+
case "fkey": {
|
|
7120
|
+
let new_value = [];
|
|
7121
|
+
if (is_string(value) && !empty_string(value)) {
|
|
7122
|
+
let fkey = treedb_decoder_fkey(field_desc, value);
|
|
7123
|
+
if (fkey) new_value.push(fkey.id);
|
|
7124
|
+
} else if (is_array(value)) for (let v of value) {
|
|
7125
|
+
let fkey = treedb_decoder_fkey(field_desc, v);
|
|
7126
|
+
if (fkey) new_value.push(fkey.id);
|
|
7127
|
+
}
|
|
7128
|
+
else if (is_object(value)) for (let v of Object.keys(value)) {
|
|
7129
|
+
let fkey = treedb_decoder_fkey(field_desc, v);
|
|
7130
|
+
if (fkey) new_value.push(fkey.id);
|
|
7131
|
+
}
|
|
7132
|
+
value = new_value;
|
|
7133
|
+
break;
|
|
7134
|
+
}
|
|
6962
7135
|
case "hook":
|
|
6963
7136
|
case "enum":
|
|
6964
7137
|
case "uuid":
|
|
@@ -6968,8 +7141,7 @@ function treedb_value_2_form_value(gobj, field_desc, value) {
|
|
|
6968
7141
|
case "url": break;
|
|
6969
7142
|
case "now":
|
|
6970
7143
|
case "time":
|
|
6971
|
-
value = parseInt(value) || 0;
|
|
6972
|
-
value = new Date(value);
|
|
7144
|
+
value = /* @__PURE__ */ new Date((parseInt(value) || 0) * 1e3);
|
|
6973
7145
|
break;
|
|
6974
7146
|
case "date":
|
|
6975
7147
|
case "color":
|
|
@@ -6985,13 +7157,19 @@ function treedb_value_2_form_value(gobj, field_desc, value) {
|
|
|
6985
7157
|
case "base64": break;
|
|
6986
7158
|
case "string":
|
|
6987
7159
|
case "integer":
|
|
7160
|
+
case "real":
|
|
7161
|
+
case "boolean": break;
|
|
6988
7162
|
case "object":
|
|
6989
7163
|
case "dict":
|
|
7164
|
+
value = { json: is_object(value) ? value : {} };
|
|
7165
|
+
break;
|
|
7166
|
+
case "blob":
|
|
7167
|
+
value = { json: is_object(value) || is_array(value) ? value : {} };
|
|
7168
|
+
break;
|
|
6990
7169
|
case "array":
|
|
6991
7170
|
case "list":
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
case "blob": break;
|
|
7171
|
+
value = { json: is_array(value) ? value : [] };
|
|
7172
|
+
break;
|
|
6995
7173
|
case "number": break;
|
|
6996
7174
|
case "coordinates":
|
|
6997
7175
|
let longitude = 0;
|
|
@@ -7023,7 +7201,9 @@ function form_value_2_treedb_value(gobj, field_desc, value) {
|
|
|
7023
7201
|
let type = field_desc.type;
|
|
7024
7202
|
let real_type = field_desc.real_type;
|
|
7025
7203
|
switch (type) {
|
|
7026
|
-
case "fkey":
|
|
7204
|
+
case "fkey":
|
|
7205
|
+
value = build_fkey_ref$1(gobj, field_desc, value);
|
|
7206
|
+
break;
|
|
7027
7207
|
case "hook":
|
|
7028
7208
|
value = null;
|
|
7029
7209
|
break;
|
|
@@ -7131,7 +7311,9 @@ function ac_load_record(gobj, event, kw, src) {
|
|
|
7131
7311
|
gobj_write_attr(gobj, "record", kw);
|
|
7132
7312
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
7133
7313
|
gobj_write_bool_attr(gobj, "initialized", false);
|
|
7134
|
-
|
|
7314
|
+
let modified = set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, kw);
|
|
7315
|
+
apply_form_mode(gobj, $form);
|
|
7316
|
+
set_changed_stated(gobj, modified);
|
|
7135
7317
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
7136
7318
|
return 0;
|
|
7137
7319
|
}
|
|
@@ -7141,8 +7323,8 @@ function ac_load_record(gobj, event, kw, src) {
|
|
|
7141
7323
|
************************************************************/
|
|
7142
7324
|
function ac_save_record(gobj, event, kw, src) {
|
|
7143
7325
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
7144
|
-
if (validate_form
|
|
7145
|
-
gobj_publish_event(gobj, "EV_SAVE_RECORD", get_form_values
|
|
7326
|
+
if (validate_form(gobj, $form)) {
|
|
7327
|
+
gobj_publish_event(gobj, "EV_SAVE_RECORD", get_form_values(gobj, $form));
|
|
7146
7328
|
set_changed_stated(gobj, false);
|
|
7147
7329
|
} else {
|
|
7148
7330
|
kw.abort_close = true;
|
|
@@ -7156,7 +7338,7 @@ function ac_save_record(gobj, event, kw, src) {
|
|
|
7156
7338
|
function ac_undo_record(gobj, event, kw, src) {
|
|
7157
7339
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
7158
7340
|
gobj_write_bool_attr(gobj, "initialized", false);
|
|
7159
|
-
set_form_values
|
|
7341
|
+
set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, gobj_read_attr(gobj, "record"));
|
|
7160
7342
|
set_changed_stated(gobj, false);
|
|
7161
7343
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
7162
7344
|
return 0;
|
|
@@ -7166,8 +7348,8 @@ function ac_undo_record(gobj, event, kw, src) {
|
|
|
7166
7348
|
************************************************************/
|
|
7167
7349
|
function ac_clear_record(gobj, event, kw, src) {
|
|
7168
7350
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
7169
|
-
clear_validation
|
|
7170
|
-
clear_data
|
|
7351
|
+
clear_validation(gobj, $form);
|
|
7352
|
+
clear_data(gobj, $form);
|
|
7171
7353
|
return 0;
|
|
7172
7354
|
}
|
|
7173
7355
|
/************************************************************
|
|
@@ -7212,8 +7394,8 @@ function ac_record_changed(gobj, event, kw, src) {
|
|
|
7212
7394
|
************************************************************/
|
|
7213
7395
|
function ac_copy_record(gobj, event, kw, src) {
|
|
7214
7396
|
let $form = gobj_read_attr(gobj, "$container").querySelector("form");
|
|
7215
|
-
if (validate_form
|
|
7216
|
-
let data = { record: get_form_values
|
|
7397
|
+
if (validate_form(gobj, $form)) {
|
|
7398
|
+
let data = { record: get_form_values(gobj, $form) };
|
|
7217
7399
|
navigator.clipboard.writeText(JSON.stringify(data));
|
|
7218
7400
|
}
|
|
7219
7401
|
return 0;
|
|
@@ -7226,7 +7408,7 @@ function ac_paste_record(gobj, event, kw, src) {
|
|
|
7226
7408
|
let record = kw.record;
|
|
7227
7409
|
if (is_object(record)) {
|
|
7228
7410
|
gobj_write_bool_attr(gobj, "initialized", true);
|
|
7229
|
-
set_form_values
|
|
7411
|
+
set_form_values(gobj, gobj_read_attr(gobj, "template"), $form, record);
|
|
7230
7412
|
}
|
|
7231
7413
|
return 0;
|
|
7232
7414
|
}
|
|
@@ -7372,6 +7554,7 @@ function create_gclass$11(gclass_name) {
|
|
|
7372
7554
|
* Register GClass
|
|
7373
7555
|
***************************************************************/
|
|
7374
7556
|
function register_c_yui_form() {
|
|
7557
|
+
if (gclass_find_by_name(GCLASS_NAME$11, false)) return 0;
|
|
7375
7558
|
return create_gclass$11(GCLASS_NAME$11);
|
|
7376
7559
|
}
|
|
7377
7560
|
//#endregion
|
|
@@ -8449,7 +8632,7 @@ function onClick(gobj, e) {
|
|
|
8449
8632
|
const map = gobj.priv.xmap;
|
|
8450
8633
|
const coordinates = e.features[0].geometry.coordinates.slice();
|
|
8451
8634
|
const properties = e.features[0].properties;
|
|
8452
|
-
const gobj_service = gobj_find_service(properties.gobj_service_name,
|
|
8635
|
+
const gobj_service = properties.gobj_service_name ? gobj_find_service(properties.gobj_service_name, false) : null;
|
|
8453
8636
|
if (gobj_service) {
|
|
8454
8637
|
let window_service_name = `window-map-${clean_name(gobj_name(gobj_service))}`;
|
|
8455
8638
|
let popupContent = createElement2([
|
|
@@ -11743,6 +11926,93 @@ function resolve_route(item_index, route) {
|
|
|
11743
11926
|
};
|
|
11744
11927
|
}
|
|
11745
11928
|
//#endregion
|
|
11929
|
+
//#region src/shell_section_index.js
|
|
11930
|
+
/***********************************************************************
|
|
11931
|
+
* shell_section_index.js
|
|
11932
|
+
*
|
|
11933
|
+
* Pure helper for C_YUI_SHELL: synthesize the stage target of a
|
|
11934
|
+
* section-index landing route.
|
|
11935
|
+
*
|
|
11936
|
+
* A level-1 menu item that declares `submenu.index` opts out of
|
|
11937
|
+
* the redirect-to-default-child behaviour: its own route becomes
|
|
11938
|
+
* a real resting, deep-linkable route whose view is the submenu
|
|
11939
|
+
* itself rendered as a "cards" C_YUI_NAV (the shell's view
|
|
11940
|
+
* contract only requires a $container by the end of mt_create,
|
|
11941
|
+
* which C_YUI_NAV already satisfies). Config:
|
|
11942
|
+
*
|
|
11943
|
+
* "submenu": {
|
|
11944
|
+
* "render": { "top-sub": "tabs" },
|
|
11945
|
+
* "index": true — landing in stage "main"
|
|
11946
|
+
* "index": {"stage": "x"} — landing in stage "x"
|
|
11947
|
+
* }
|
|
11948
|
+
*
|
|
11949
|
+
* Precedence: an explicit inline `target` on the item wins (this
|
|
11950
|
+
* helper returns null then). When the target is synthesized,
|
|
11951
|
+
* `submenu.default` becomes inert for the section: navigate_to()
|
|
11952
|
+
* only redirects to the default child while the entry has NO
|
|
11953
|
+
* target.
|
|
11954
|
+
*
|
|
11955
|
+
* Tabs and cards never coexist (DRY of navigation): while the
|
|
11956
|
+
* index is on stage the shell collapses the secondary zone, and
|
|
11957
|
+
* for index sections the tab strip defaults to desktop/tablet
|
|
11958
|
+
* only — on mobile a "backbar" nav (← <section>) takes its place
|
|
11959
|
+
* inside a child view. See secondary_nav_renders().
|
|
11960
|
+
*
|
|
11961
|
+
* Split out of c_yui_shell.js so it can be unit-tested without
|
|
11962
|
+
* a DOM.
|
|
11963
|
+
*
|
|
11964
|
+
* Copyright (c) 2026, ArtGins.
|
|
11965
|
+
* All Rights Reserved.
|
|
11966
|
+
***********************************************************************/
|
|
11967
|
+
/************************************************************
|
|
11968
|
+
* Return the synthesized target for `item`'s section route,
|
|
11969
|
+
* or null when the item doesn't opt in.
|
|
11970
|
+
************************************************************/
|
|
11971
|
+
function section_index_target(menu_id, item) {
|
|
11972
|
+
if (!item || !item.route || item.target) return null;
|
|
11973
|
+
let sub = item.submenu;
|
|
11974
|
+
if (!sub || !Array.isArray(sub.items) || !sub.index) return null;
|
|
11975
|
+
return {
|
|
11976
|
+
stage: (typeof sub.index === "object" ? sub.index : {}).stage || "main",
|
|
11977
|
+
gclass: "C_YUI_NAV",
|
|
11978
|
+
kw: {
|
|
11979
|
+
menu_id: `index.${menu_id}.${item.id}`,
|
|
11980
|
+
nav_label: item.name || item.id || "",
|
|
11981
|
+
menu_items: sub.items,
|
|
11982
|
+
layout: "cards",
|
|
11983
|
+
level: "secondary"
|
|
11984
|
+
}
|
|
11985
|
+
};
|
|
11986
|
+
}
|
|
11987
|
+
/************************************************************
|
|
11988
|
+
* Return the list of render configs for one (submenu, zone)
|
|
11989
|
+
* pair. `layout` is the raw submenu.render[zone] value
|
|
11990
|
+
* (string or object).
|
|
11991
|
+
*
|
|
11992
|
+
* Without submenu.index: the declared render, unchanged.
|
|
11993
|
+
* With submenu.index (list → detail):
|
|
11994
|
+
* - the declared nav defaults to show_on ">=tablet" (an
|
|
11995
|
+
* explicit show_on in the render object wins);
|
|
11996
|
+
* - a "backbar" nav (show_on "<tablet") is appended so a
|
|
11997
|
+
* child view offers "← <section>" on mobile instead of
|
|
11998
|
+
* the duplicated tab strip. Disable with
|
|
11999
|
+
* index: {backbar: false}; tune its breakpoints with
|
|
12000
|
+
* index: {backbar: {show_on: "..."}}.
|
|
12001
|
+
************************************************************/
|
|
12002
|
+
function secondary_nav_renders(item, layout) {
|
|
12003
|
+
let cfg = layout && typeof layout === "object" ? Object.assign({}, layout) : { layout: String(layout || "vertical") };
|
|
12004
|
+
let sub = item && item.submenu || {};
|
|
12005
|
+
if (!sub.index) return [cfg];
|
|
12006
|
+
let index_cfg = typeof sub.index === "object" ? sub.index : {};
|
|
12007
|
+
if (!cfg.show_on) cfg.show_on = ">=tablet";
|
|
12008
|
+
if (index_cfg.backbar === false) return [cfg];
|
|
12009
|
+
return [cfg, {
|
|
12010
|
+
layout: "backbar",
|
|
12011
|
+
show_on: (typeof index_cfg.backbar === "object" && index_cfg.backbar ? index_cfg.backbar : {}).show_on || "<tablet",
|
|
12012
|
+
back_route: item && item.route || ""
|
|
12013
|
+
}];
|
|
12014
|
+
}
|
|
12015
|
+
//#endregion
|
|
11746
12016
|
//#region src/c_yui_shell.js
|
|
11747
12017
|
/***********************************************************************
|
|
11748
12018
|
* c_yui_shell.js
|
|
@@ -11762,6 +12032,11 @@ function resolve_route(item_index, route) {
|
|
|
11762
12032
|
* $container in its stage, hide the previous one. Lifecycle per item
|
|
11763
12033
|
* decides when the gobj is created/destroyed.
|
|
11764
12034
|
*
|
|
12035
|
+
* A primary item with `submenu.index` gets a section-index landing:
|
|
12036
|
+
* its own route mounts the submenu as a "cards" C_YUI_NAV in the
|
|
12037
|
+
* stage instead of redirecting to the default child (see
|
|
12038
|
+
* shell_section_index.js for the contract).
|
|
12039
|
+
*
|
|
11765
12040
|
* Hash-based 2-level routing (#/primary/secondary). No dependency on
|
|
11766
12041
|
* C_YUI_ROUTING.
|
|
11767
12042
|
*
|
|
@@ -12137,12 +12412,13 @@ function build_item_index(gobj, config) {
|
|
|
12137
12412
|
let items = menu && menu.items || [];
|
|
12138
12413
|
for (let item of items) {
|
|
12139
12414
|
if (item.route) {
|
|
12415
|
+
let target = item.target || section_index_target(menu_id, item);
|
|
12140
12416
|
let prev = priv.item_index[item.route];
|
|
12141
|
-
if (!prev || !prev.target &&
|
|
12417
|
+
if (!prev || !prev.target && target) priv.item_index[item.route] = {
|
|
12142
12418
|
item,
|
|
12143
12419
|
parent_item: null,
|
|
12144
|
-
stage:
|
|
12145
|
-
target
|
|
12420
|
+
stage: target && target.stage || null,
|
|
12421
|
+
target,
|
|
12146
12422
|
menu_id
|
|
12147
12423
|
};
|
|
12148
12424
|
}
|
|
@@ -12220,11 +12496,15 @@ function instantiate_menus(gobj, config) {
|
|
|
12220
12496
|
log_warning(`C_YUI_SHELL: submenu of '${menu_id}.${item.id}' renders in unknown zone '${zone_id}'`);
|
|
12221
12497
|
continue;
|
|
12222
12498
|
}
|
|
12223
|
-
let
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
|
|
12499
|
+
let renders = secondary_nav_renders(item, render_to_obj(layout));
|
|
12500
|
+
let sub_menu_id = `secondary.${menu_id}.${item.id}`;
|
|
12501
|
+
for (let render_cfg of renders) {
|
|
12502
|
+
let $c = gobj_read_attr(instantiate_nav_in_zone(gobj, {
|
|
12503
|
+
items: sub.items,
|
|
12504
|
+
render: { [zone_id]: render_cfg }
|
|
12505
|
+
}, sub_menu_id, zone_id, "secondary", item.name || ""), "$container");
|
|
12506
|
+
if ($c) $c.classList.add("is-hidden");
|
|
12507
|
+
}
|
|
12228
12508
|
}
|
|
12229
12509
|
}
|
|
12230
12510
|
}
|
|
@@ -12238,9 +12518,8 @@ function instantiate_nav_in_zone(gobj, menu, menu_id, zone_id, level, nav_label)
|
|
|
12238
12518
|
let priv = gobj_read_attr(gobj, "priv");
|
|
12239
12519
|
let render_cfg = menu.render && (menu.render[zone_id] || menu.render["*"]) || { layout: "vertical" };
|
|
12240
12520
|
if (is_string(render_cfg)) render_cfg = { layout: render_cfg };
|
|
12241
|
-
let nav_name = `nav_${menu_id.replace(/\./g, "_")}_${zone_id}`;
|
|
12242
12521
|
let layout = render_cfg.layout || "vertical";
|
|
12243
|
-
let nav = gobj_create(
|
|
12522
|
+
let nav = gobj_create(`nav_${menu_id.replace(/\./g, "_")}_${zone_id}` + (layout === "backbar" ? "_backbar" : ""), "C_YUI_NAV", {
|
|
12244
12523
|
menu_id,
|
|
12245
12524
|
nav_label: nav_label || "",
|
|
12246
12525
|
menu_items: menu.items || [],
|
|
@@ -12249,6 +12528,8 @@ function instantiate_nav_in_zone(gobj, menu, menu_id, zone_id, level, nav_label)
|
|
|
12249
12528
|
icon_pos: render_cfg.icon_pos || default_icon_pos(zone_id),
|
|
12250
12529
|
show_label: render_cfg.show_label !== false,
|
|
12251
12530
|
level,
|
|
12531
|
+
show_on: render_cfg.show_on || "",
|
|
12532
|
+
back_route: render_cfg.back_route || "",
|
|
12252
12533
|
shell: gobj
|
|
12253
12534
|
}, gobj);
|
|
12254
12535
|
let $c = gobj_read_attr(nav, "$container");
|
|
@@ -12433,6 +12714,8 @@ function build_view_gobj(gobj, entry, route, stage) {
|
|
|
12433
12714
|
}
|
|
12434
12715
|
stage.el.appendChild($view);
|
|
12435
12716
|
gobj_start(view);
|
|
12717
|
+
let priv = gobj_read_attr(gobj, "priv");
|
|
12718
|
+
if (priv && typeof priv.translator === "function" && target.gclass === "C_YUI_NAV") refresh_language($view, priv.translator);
|
|
12436
12719
|
return view;
|
|
12437
12720
|
}
|
|
12438
12721
|
function safe_id(s) {
|
|
@@ -13017,7 +13300,7 @@ function update_secondary_nav_visibility(gobj, entry) {
|
|
|
13017
13300
|
let active_primary_id = entry.parent_item && entry.parent_item.id || entry.item && entry.item.id || "";
|
|
13018
13301
|
let target_secondary_id = `secondary.${entry.menu_id || ""}.${active_primary_id}`;
|
|
13019
13302
|
let active_primary = entry.parent_item || entry.item || null;
|
|
13020
|
-
let has_secondary = !!(active_primary && active_primary.submenu && Array.isArray(active_primary.submenu.items) && active_primary.submenu.items.some((it) => it && it.route));
|
|
13303
|
+
let has_secondary = !!!(!entry.parent_item && active_primary && active_primary.submenu && active_primary.submenu.index) && !!(active_primary && active_primary.submenu && Array.isArray(active_primary.submenu.items) && active_primary.submenu.items.some((it) => it && it.route));
|
|
13021
13304
|
let config = gobj_read_attr(gobj, "config") || {};
|
|
13022
13305
|
let zones_cfg = config.shell && config.shell.zones || {};
|
|
13023
13306
|
for (let z in zones_cfg) if (zones_cfg[z] && zones_cfg[z].host === "menu.secondary") {
|
|
@@ -13030,7 +13313,7 @@ function update_secondary_nav_visibility(gobj, entry) {
|
|
|
13030
13313
|
if (!nav_menu_id.startsWith("secondary.")) continue;
|
|
13031
13314
|
let $c = gobj_read_attr(nav, "$container");
|
|
13032
13315
|
if (!$c) continue;
|
|
13033
|
-
if (nav_menu_id === target_secondary_id) $c.classList.remove("is-hidden");
|
|
13316
|
+
if (nav_menu_id === target_secondary_id && has_secondary) $c.classList.remove("is-hidden");
|
|
13034
13317
|
else $c.classList.add("is-hidden");
|
|
13035
13318
|
}
|
|
13036
13319
|
}
|
|
@@ -13215,6 +13498,86 @@ function yui_shell_close_dropdown(shell_gobj) {
|
|
|
13215
13498
|
close_toolbar_dropdown(shell_gobj);
|
|
13216
13499
|
}
|
|
13217
13500
|
//#endregion
|
|
13501
|
+
//#region src/nav_cards_helpers.js
|
|
13502
|
+
/***********************************************************************
|
|
13503
|
+
* nav_cards_helpers.js
|
|
13504
|
+
*
|
|
13505
|
+
* Pure descriptor builders for C_YUI_NAV's "cards" layout — a
|
|
13506
|
+
* grid of tappable cards, one per item, used as the section-index
|
|
13507
|
+
* landing of a submenu (list → detail pattern). Split out of
|
|
13508
|
+
* c_yui_nav.js so it can be unit-tested without a DOM: the
|
|
13509
|
+
* functions return createElement2 node descriptors, never
|
|
13510
|
+
* HTMLElements.
|
|
13511
|
+
*
|
|
13512
|
+
* Copyright (c) 2026, ArtGins.
|
|
13513
|
+
* All Rights Reserved.
|
|
13514
|
+
***********************************************************************/
|
|
13515
|
+
/************************************************************
|
|
13516
|
+
* Descriptor of one card: an <a> carrying the same data-*
|
|
13517
|
+
* contract as every other nav item (data-route/data-item-id/
|
|
13518
|
+
* data-disabled), so C_YUI_NAV's delegated click handler and
|
|
13519
|
+
* active-route highlight work unchanged.
|
|
13520
|
+
************************************************************/
|
|
13521
|
+
function card_descriptor(it, show_label) {
|
|
13522
|
+
let children = [];
|
|
13523
|
+
let label = it.name || "";
|
|
13524
|
+
if (it.icon) children.push([
|
|
13525
|
+
"span",
|
|
13526
|
+
{ class: "icon is-medium" },
|
|
13527
|
+
["i", {
|
|
13528
|
+
class: it.icon,
|
|
13529
|
+
"aria-hidden": "true"
|
|
13530
|
+
}]
|
|
13531
|
+
]);
|
|
13532
|
+
if (show_label && label) children.push([
|
|
13533
|
+
"span",
|
|
13534
|
+
{
|
|
13535
|
+
class: "yui-nav-label",
|
|
13536
|
+
i18n: label
|
|
13537
|
+
},
|
|
13538
|
+
label
|
|
13539
|
+
]);
|
|
13540
|
+
let a_attrs = {
|
|
13541
|
+
class: "yui-nav-item yui-nav-card",
|
|
13542
|
+
href: it.route ? "#" + it.route : "#",
|
|
13543
|
+
"data-item-id": it.id,
|
|
13544
|
+
"data-route": it.route || "",
|
|
13545
|
+
"data-disabled": it.disabled ? "1" : "0",
|
|
13546
|
+
"aria-label": label || it.id
|
|
13547
|
+
};
|
|
13548
|
+
if (label) a_attrs["data-i18n-aria-label"] = label;
|
|
13549
|
+
let tip = it.tooltip || it.aria_label;
|
|
13550
|
+
if (tip) {
|
|
13551
|
+
a_attrs.title = tip;
|
|
13552
|
+
a_attrs["data-i18n-title"] = tip;
|
|
13553
|
+
}
|
|
13554
|
+
if (it.disabled) {
|
|
13555
|
+
a_attrs["aria-disabled"] = "true";
|
|
13556
|
+
a_attrs["tabindex"] = "-1";
|
|
13557
|
+
}
|
|
13558
|
+
return [
|
|
13559
|
+
"a",
|
|
13560
|
+
a_attrs,
|
|
13561
|
+
children
|
|
13562
|
+
];
|
|
13563
|
+
}
|
|
13564
|
+
/************************************************************
|
|
13565
|
+
* Descriptor of the whole grid. Decorative items (`header`/
|
|
13566
|
+
* `divider`) are dropped, same policy as the tabs layout.
|
|
13567
|
+
************************************************************/
|
|
13568
|
+
function cards_grid_descriptor(items, show_label) {
|
|
13569
|
+
let cards = [];
|
|
13570
|
+
for (let it of items || []) {
|
|
13571
|
+
if (!it || it.type === "header" || it.type === "divider") continue;
|
|
13572
|
+
cards.push(card_descriptor(it, show_label));
|
|
13573
|
+
}
|
|
13574
|
+
return [
|
|
13575
|
+
"div",
|
|
13576
|
+
{},
|
|
13577
|
+
cards
|
|
13578
|
+
];
|
|
13579
|
+
}
|
|
13580
|
+
//#endregion
|
|
13218
13581
|
//#region src/c_yui_nav.js
|
|
13219
13582
|
/***********************************************************************
|
|
13220
13583
|
* c_yui_nav.js
|
|
@@ -13230,6 +13593,17 @@ function yui_shell_close_dropdown(shell_gobj) {
|
|
|
13230
13593
|
* "drawer" — off-canvas vertical (toggled by hamburger)
|
|
13231
13594
|
* "submenu" — Bulma .menu nested under a heading
|
|
13232
13595
|
* "accordion" — Bulma .menu list with collapsible group
|
|
13596
|
+
* "cards" — grid of tappable cards (section-index landing;
|
|
13597
|
+
* mounted as a stage view by C_YUI_SHELL when a
|
|
13598
|
+
* primary item declares submenu.index)
|
|
13599
|
+
* "backbar" — single "← <section>" link back to the section
|
|
13600
|
+
* index; the mobile replacement of the tab strip
|
|
13601
|
+
* for submenu.index sections (the shell collapses
|
|
13602
|
+
* the secondary zone while the index is on stage)
|
|
13603
|
+
*
|
|
13604
|
+
* The `show_on` attr (same expressions as the shell's zone
|
|
13605
|
+
* `show_on`) applies breakpoint visibility classes to the nav
|
|
13606
|
+
* container; build_ui re-applies them on every rebuild.
|
|
13233
13607
|
*
|
|
13234
13608
|
* Each item supports:
|
|
13235
13609
|
* id, name, icon (CSS class or svg id), route, badge, disabled
|
|
@@ -13267,7 +13641,9 @@ var SUPPORTED_LAYOUTS = [
|
|
|
13267
13641
|
"tabs",
|
|
13268
13642
|
"drawer",
|
|
13269
13643
|
"submenu",
|
|
13270
|
-
"accordion"
|
|
13644
|
+
"accordion",
|
|
13645
|
+
"cards",
|
|
13646
|
+
"backbar"
|
|
13271
13647
|
];
|
|
13272
13648
|
function is_decorative(it) {
|
|
13273
13649
|
return !!(it && (it.type === "header" || it.type === "divider"));
|
|
@@ -13285,6 +13661,8 @@ var attrs_table$4 = [
|
|
|
13285
13661
|
SDATA(data_type_t.DTP_STRING, "icon_pos", 0, "left", "left|right|top|bottom"),
|
|
13286
13662
|
SDATA(data_type_t.DTP_BOOLEAN, "show_label", 0, true, "Show item label next to/under the icon"),
|
|
13287
13663
|
SDATA(data_type_t.DTP_STRING, "level", 0, "primary", "primary|secondary"),
|
|
13664
|
+
SDATA(data_type_t.DTP_STRING, "show_on", 0, "", "Breakpoint visibility expr (shell show_on syntax); empty = always"),
|
|
13665
|
+
SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "backbar layout: route of the section index the ← returns to"),
|
|
13288
13666
|
SDATA(data_type_t.DTP_POINTER, "shell", 0, null, "C_YUI_SHELL gobj (for navigation calls)"),
|
|
13289
13667
|
SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Root HTMLElement of this nav"),
|
|
13290
13668
|
SDATA(data_type_t.DTP_STRING, "active_route", 0, "", "Currently active route"),
|
|
@@ -13368,11 +13746,23 @@ function build_ui$4(gobj) {
|
|
|
13368
13746
|
case "accordion":
|
|
13369
13747
|
$container = render_accordion(gobj, items);
|
|
13370
13748
|
break;
|
|
13749
|
+
case "cards":
|
|
13750
|
+
$container = render_cards(gobj, items);
|
|
13751
|
+
break;
|
|
13752
|
+
case "backbar":
|
|
13753
|
+
$container = render_backbar(gobj);
|
|
13754
|
+
break;
|
|
13371
13755
|
default: $container = render_vertical(gobj, items);
|
|
13372
13756
|
}
|
|
13373
13757
|
$container.setAttribute("data-nav-zone", zone);
|
|
13374
13758
|
$container.setAttribute("data-nav-layout", layout);
|
|
13375
13759
|
$container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
|
|
13760
|
+
let show_on = gobj_read_attr(gobj, "show_on") || "";
|
|
13761
|
+
if (!empty_string(show_on) && show_on !== "*") {
|
|
13762
|
+
let visible = breakpoints_from_expr(show_on);
|
|
13763
|
+
for (let bp of BULMA_BP_ORDER) if (!visible[bp]) $container.classList.add(bulma_hidden_class(bp));
|
|
13764
|
+
$container.setAttribute("data-show-on", show_on);
|
|
13765
|
+
}
|
|
13376
13766
|
if (layout !== "drawer") {
|
|
13377
13767
|
$container.setAttribute("role", "navigation");
|
|
13378
13768
|
if (!$container.hasAttribute("aria-label")) {
|
|
@@ -13543,6 +13933,51 @@ function render_tabs(gobj, items) {
|
|
|
13543
13933
|
]
|
|
13544
13934
|
]);
|
|
13545
13935
|
}
|
|
13936
|
+
function render_cards(gobj, items) {
|
|
13937
|
+
return createElement2(cards_grid_descriptor(items, gobj_read_attr(gobj, "show_label")));
|
|
13938
|
+
}
|
|
13939
|
+
/************************************************************
|
|
13940
|
+
* "backbar": a single "← <section>" link back to the section
|
|
13941
|
+
* index route. Static DOM — the active child view names itself
|
|
13942
|
+
* in its own content, so the bar only carries the way back plus
|
|
13943
|
+
* section context. While the index itself is on stage the shell
|
|
13944
|
+
* collapses the whole secondary zone (tabs and cards never
|
|
13945
|
+
* coexist), so the bar needs no self-hiding logic.
|
|
13946
|
+
************************************************************/
|
|
13947
|
+
function render_backbar(gobj) {
|
|
13948
|
+
let back_route = gobj_read_attr(gobj, "back_route") || "";
|
|
13949
|
+
let label = gobj_read_attr(gobj, "nav_label") || "";
|
|
13950
|
+
let label_attrs = { class: "yui-nav-label" };
|
|
13951
|
+
if (label) label_attrs.i18n = label;
|
|
13952
|
+
let a_attrs = {
|
|
13953
|
+
class: "yui-nav-item yui-nav-back",
|
|
13954
|
+
href: back_route ? "#" + back_route : "#",
|
|
13955
|
+
"data-item-id": "__index__",
|
|
13956
|
+
"data-route": back_route,
|
|
13957
|
+
"aria-label": label || "back"
|
|
13958
|
+
};
|
|
13959
|
+
if (label) a_attrs["data-i18n-aria-label"] = label;
|
|
13960
|
+
return createElement2([
|
|
13961
|
+
"div",
|
|
13962
|
+
{},
|
|
13963
|
+
[
|
|
13964
|
+
"a",
|
|
13965
|
+
a_attrs,
|
|
13966
|
+
[[
|
|
13967
|
+
"span",
|
|
13968
|
+
{ class: "icon is-small" },
|
|
13969
|
+
["i", {
|
|
13970
|
+
class: "yi-arrow-left",
|
|
13971
|
+
"aria-hidden": "true"
|
|
13972
|
+
}]
|
|
13973
|
+
], [
|
|
13974
|
+
"span",
|
|
13975
|
+
label_attrs,
|
|
13976
|
+
label
|
|
13977
|
+
]]
|
|
13978
|
+
]
|
|
13979
|
+
]);
|
|
13980
|
+
}
|
|
13546
13981
|
function render_drawer(gobj, items) {
|
|
13547
13982
|
let menu_id = gobj_read_attr(gobj, "menu_id") || "drawer";
|
|
13548
13983
|
let wrap = document.createElement("div");
|
|
@@ -13844,6 +14279,7 @@ function ac_route_changed(gobj, event, kw, src) {
|
|
|
13844
14279
|
gobj_write_attr(gobj, "active_route", route);
|
|
13845
14280
|
let $c = gobj_read_attr(gobj, "$container");
|
|
13846
14281
|
if (!$c) return 0;
|
|
14282
|
+
if (gobj_read_attr(gobj, "layout") === "backbar") return 0;
|
|
13847
14283
|
$c.querySelectorAll(".is-active").forEach((n) => n.classList.remove("is-active"));
|
|
13848
14284
|
let id_to_mark = null;
|
|
13849
14285
|
if (level === "primary") {
|
|
@@ -14813,7 +15249,7 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
|
|
|
14813
15249
|
* record
|
|
14814
15250
|
* }
|
|
14815
15251
|
********************************************/
|
|
14816
|
-
function ac_create_record
|
|
15252
|
+
function ac_create_record(gobj, event, kw, src) {
|
|
14817
15253
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
14818
15254
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
14819
15255
|
let record = kw.record;
|
|
@@ -14830,7 +15266,7 @@ function ac_create_record$1(gobj, event, kw, src) {
|
|
|
14830
15266
|
* record
|
|
14831
15267
|
* }
|
|
14832
15268
|
********************************************/
|
|
14833
|
-
function ac_update_record
|
|
15269
|
+
function ac_update_record(gobj, event, kw, src) {
|
|
14834
15270
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
14835
15271
|
let topic_name = gobj_read_attr(src, "topic_name");
|
|
14836
15272
|
let record = kw.record;
|
|
@@ -14903,12 +15339,12 @@ function create_gclass$3(gclass_name) {
|
|
|
14903
15339
|
],
|
|
14904
15340
|
[
|
|
14905
15341
|
"EV_CREATE_RECORD",
|
|
14906
|
-
ac_create_record
|
|
15342
|
+
ac_create_record,
|
|
14907
15343
|
null
|
|
14908
15344
|
],
|
|
14909
15345
|
[
|
|
14910
15346
|
"EV_UPDATE_RECORD",
|
|
14911
|
-
ac_update_record
|
|
15347
|
+
ac_update_record,
|
|
14912
15348
|
null
|
|
14913
15349
|
],
|
|
14914
15350
|
[
|
|
@@ -15007,7 +15443,6 @@ var attrs_table$2 = [
|
|
|
15007
15443
|
SDATA(data_type_t.DTP_POINTER, "tabulator", 0, null, "Tabulator instance"),
|
|
15008
15444
|
SDATA(data_type_t.DTP_STRING, "table_id", 0, null, "Table div ID"),
|
|
15009
15445
|
SDATA(data_type_t.DTP_STRING, "toolbar_id", 0, null, "Toolbar ID"),
|
|
15010
|
-
SDATA(data_type_t.DTP_STRING, "form_id", 0, null, "Edit form ID"),
|
|
15011
15446
|
SDATA(data_type_t.DTP_STRING, "popup_id", 0, null, "Edit form popup ID"),
|
|
15012
15447
|
SDATA_END()
|
|
15013
15448
|
];
|
|
@@ -15015,7 +15450,9 @@ var PRIVATE_DATA$2 = {
|
|
|
15015
15450
|
$container: null,
|
|
15016
15451
|
treedb_name: "",
|
|
15017
15452
|
topic_name: "",
|
|
15018
|
-
tabulator: null
|
|
15453
|
+
tabulator: null,
|
|
15454
|
+
form: null,
|
|
15455
|
+
$popup: null
|
|
15019
15456
|
};
|
|
15020
15457
|
var __gclass__$2 = null;
|
|
15021
15458
|
function popup_mount_layer() {
|
|
@@ -15031,7 +15468,6 @@ function mt_create$2(gobj) {
|
|
|
15031
15468
|
let name = clean_name(gobj_name(gobj));
|
|
15032
15469
|
gobj_write_attr(gobj, "table_id", "table" + name);
|
|
15033
15470
|
gobj_write_attr(gobj, "toolbar_id", "toolbar" + name);
|
|
15034
|
-
gobj_write_attr(gobj, "form_id", "form" + name);
|
|
15035
15471
|
gobj_write_attr(gobj, "popup_id", "popup" + name);
|
|
15036
15472
|
build_ui$2(gobj);
|
|
15037
15473
|
let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
@@ -15048,6 +15484,7 @@ function mt_start$2(gobj) {
|
|
|
15048
15484
|
* Framework Method: Stop
|
|
15049
15485
|
***************************************************************/
|
|
15050
15486
|
function mt_stop$2(gobj) {
|
|
15487
|
+
close_form_dialog(gobj);
|
|
15051
15488
|
table__destroy(gobj);
|
|
15052
15489
|
}
|
|
15053
15490
|
/***************************************************************
|
|
@@ -15387,6 +15824,29 @@ function col_label(col, topic_name) {
|
|
|
15387
15824
|
return t(topic_name ? [topic_name + "." + col.id, col.id] : [col.id], { defaultValue: col.header || col.id });
|
|
15388
15825
|
}
|
|
15389
15826
|
/******************************************************************
|
|
15827
|
+
* Column-title formatter: render the header as a span carrying
|
|
15828
|
+
* `data-i18n` (the shared col id) so a live language switch —
|
|
15829
|
+
* which calls refresh_language(document.body) — retranslates the
|
|
15830
|
+
* Tabulator headers in place, without rebuilding the table. Only
|
|
15831
|
+
* tag translatable columns; otherwise keep the schema header
|
|
15832
|
+
* (refresh_language would otherwise overwrite it with the raw id).
|
|
15833
|
+
* Tabulator re-runs this on redraw, so the full cascade re-applies.
|
|
15834
|
+
******************************************************************/
|
|
15835
|
+
function col_title_formatter(gobj, col) {
|
|
15836
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
15837
|
+
return function(cell, formatterParams, onRendered) {
|
|
15838
|
+
let text = col_label(col, topic_name);
|
|
15839
|
+
let attrs = {};
|
|
15840
|
+
const NO_I18N = "\0";
|
|
15841
|
+
if (t(col.id, { defaultValue: NO_I18N }) !== NO_I18N) attrs.i18n = col.id;
|
|
15842
|
+
return createElement2([
|
|
15843
|
+
"span",
|
|
15844
|
+
attrs,
|
|
15845
|
+
text
|
|
15846
|
+
]);
|
|
15847
|
+
};
|
|
15848
|
+
}
|
|
15849
|
+
/******************************************************************
|
|
15390
15850
|
* Build table with Tabulator
|
|
15391
15851
|
* This fn is called on start and when desc attribute is set.
|
|
15392
15852
|
* desc contains the description (columns) of table to create
|
|
@@ -15491,6 +15951,7 @@ function create_tabulator(gobj) {
|
|
|
15491
15951
|
}
|
|
15492
15952
|
let colDef = {
|
|
15493
15953
|
title: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
|
|
15954
|
+
titleFormatter: col_title_formatter(gobj, col),
|
|
15494
15955
|
field: col.id,
|
|
15495
15956
|
sorter,
|
|
15496
15957
|
hozAlign,
|
|
@@ -15543,7 +16004,13 @@ function create_tabulator(gobj) {
|
|
|
15543
16004
|
selectableRows: selectable,
|
|
15544
16005
|
footerElement: "<span class='yui-tabulator-rowcount' style='display:block;text-align:right;font-size:0.8rem;color:#6b7280;padding:0 0.6rem;'></span>"
|
|
15545
16006
|
});
|
|
15546
|
-
let
|
|
16007
|
+
let $view_container = gobj_read_attr(gobj, "$container");
|
|
16008
|
+
let $table_el = $view_container ? $view_container.querySelector(`#${table_id}`) : null;
|
|
16009
|
+
if (!$table_el) {
|
|
16010
|
+
log_error(`${gobj_short_name(gobj)}: table element '#${table_id}' not found in $container`);
|
|
16011
|
+
return;
|
|
16012
|
+
}
|
|
16013
|
+
let tabulator = new TabulatorFull($table_el, tabulator_settings);
|
|
15547
16014
|
function update_rowcount() {
|
|
15548
16015
|
let $rc = tabulator.element && tabulator.element.querySelector(".yui-tabulator-rowcount");
|
|
15549
16016
|
if (!$rc) return;
|
|
@@ -15678,436 +16145,62 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field) {
|
|
|
15678
16145
|
}
|
|
15679
16146
|
return value;
|
|
15680
16147
|
}
|
|
15681
|
-
|
|
15682
|
-
*
|
|
15683
|
-
|
|
15684
|
-
|
|
16148
|
+
/************************************************************
|
|
16149
|
+
* Build the form template from desc.cols: only user-editable
|
|
16150
|
+
* fields reach the form — writable cols, fkeys (linkable) and
|
|
16151
|
+
* the pkey (the hosted C_YUI_FORM's form_mode drives the
|
|
16152
|
+
* pkey's readonly/required state).
|
|
16153
|
+
************************************************************/
|
|
16154
|
+
function build_form_template(gobj) {
|
|
15685
16155
|
let desc = gobj_read_attr(gobj, "desc");
|
|
15686
|
-
let
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
let [tag, form_input_type, options, extras] = transform__treedb_type_2_form_type(gobj, col);
|
|
15695
|
-
if (!tag) continue;
|
|
15696
|
-
let id = col.id;
|
|
15697
|
-
let flag = col.flag;
|
|
15698
|
-
let is_writable = str_in_list(flag, "writable");
|
|
15699
|
-
let is_notnull = str_in_list(flag, "notnull");
|
|
15700
|
-
let is_rowid = str_in_list(flag, "rowid");
|
|
15701
|
-
let is_required = str_in_list(flag, "required");
|
|
15702
|
-
let is_hidden = str_in_list(flag, "hidden");
|
|
15703
|
-
str_in_list(flag, "persistent");
|
|
15704
|
-
str_in_list(flag, "hook");
|
|
15705
|
-
let is_fkey = str_in_list(flag, "fkey");
|
|
15706
|
-
if (id === desc.pkey) is_writable = false;
|
|
15707
|
-
if (is_fkey) is_writable = true;
|
|
15708
|
-
if (!is_writable) {
|
|
15709
|
-
if (id !== desc.pkey) continue;
|
|
15710
|
-
}
|
|
15711
|
-
if (is_hidden) continue;
|
|
15712
|
-
if (is_rowid) {
|
|
15713
|
-
is_writable = false;
|
|
15714
|
-
is_required = false;
|
|
15715
|
-
is_notnull = false;
|
|
15716
|
-
}
|
|
15717
|
-
let field_conf = {
|
|
15718
|
-
tag,
|
|
15719
|
-
inputType: form_input_type,
|
|
15720
|
-
name: col.id,
|
|
15721
|
-
label: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
|
|
15722
|
-
placeholder: "",
|
|
15723
|
-
options,
|
|
15724
|
-
extras,
|
|
15725
|
-
readonly: !is_writable,
|
|
15726
|
-
required: is_required,
|
|
15727
|
-
hidden: is_hidden,
|
|
15728
|
-
not_null: is_notnull,
|
|
15729
|
-
is_pkey: id === desc.pkey,
|
|
15730
|
-
is_rowid
|
|
15731
|
-
};
|
|
15732
|
-
let $field = create_html_field(field_conf);
|
|
15733
|
-
$field.field_conf = field_conf;
|
|
15734
|
-
$form.appendChild($field);
|
|
16156
|
+
let template = [];
|
|
16157
|
+
for (let col of desc.cols) {
|
|
16158
|
+
if (!col.id || col.id.charAt(0) === "_") continue;
|
|
16159
|
+
const field_desc = treedb_get_field_desc(col);
|
|
16160
|
+
if (field_desc.is_hidden) continue;
|
|
16161
|
+
if (field_desc.type === "hook") continue;
|
|
16162
|
+
if (!field_desc.is_writable && field_desc.type !== "fkey" && col.id !== desc.pkey) continue;
|
|
16163
|
+
template.push(col);
|
|
15735
16164
|
}
|
|
15736
|
-
return
|
|
16165
|
+
return template;
|
|
15737
16166
|
}
|
|
15738
16167
|
/************************************************************
|
|
15739
|
-
*
|
|
15740
|
-
*
|
|
15741
|
-
*
|
|
15742
|
-
*
|
|
15743
|
-
*
|
|
16168
|
+
* Collect the linkable parent rows for every fkey col:
|
|
16169
|
+
* {topic_name: rows}, asked to the topics manager (parent)
|
|
16170
|
+
* with the same command the old embedded form used. Collected
|
|
16171
|
+
* fresh on every dialog open, so new parent rows are always
|
|
16172
|
+
* offered.
|
|
15744
16173
|
************************************************************/
|
|
15745
|
-
function
|
|
15746
|
-
|
|
15747
|
-
let
|
|
15748
|
-
let
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
tag = "input";
|
|
15755
|
-
inputType = "text";
|
|
15756
|
-
break;
|
|
15757
|
-
case "integer":
|
|
15758
|
-
tag = "input";
|
|
15759
|
-
extras.inputmode = "numeric";
|
|
15760
|
-
break;
|
|
15761
|
-
case "real":
|
|
15762
|
-
tag = "input";
|
|
15763
|
-
extras.inputmode = "decimal";
|
|
15764
|
-
break;
|
|
15765
|
-
case "rowid":
|
|
15766
|
-
tag = "input";
|
|
15767
|
-
inputType = "text";
|
|
15768
|
-
break;
|
|
15769
|
-
case "boolean":
|
|
15770
|
-
tag = "checkbox";
|
|
15771
|
-
break;
|
|
15772
|
-
case "now":
|
|
15773
|
-
case "time":
|
|
15774
|
-
tag = "input";
|
|
15775
|
-
inputType = "datetime-local";
|
|
15776
|
-
switch (field_desc.real_type) {
|
|
15777
|
-
case "string": break;
|
|
15778
|
-
case "integer": break;
|
|
15779
|
-
}
|
|
15780
|
-
break;
|
|
15781
|
-
case "color":
|
|
15782
|
-
tag = "input";
|
|
15783
|
-
inputType = "color";
|
|
15784
|
-
switch (field_desc.real_type) {
|
|
15785
|
-
case "string": break;
|
|
15786
|
-
case "integer": break;
|
|
15787
|
-
}
|
|
15788
|
-
break;
|
|
15789
|
-
case "email":
|
|
15790
|
-
tag = "input";
|
|
15791
|
-
inputType = "email";
|
|
15792
|
-
extras.inputmode = "email";
|
|
15793
|
-
break;
|
|
15794
|
-
case "password":
|
|
15795
|
-
tag = "input";
|
|
15796
|
-
inputType = "password";
|
|
15797
|
-
break;
|
|
15798
|
-
case "url":
|
|
15799
|
-
tag = "input";
|
|
15800
|
-
inputType = "url";
|
|
15801
|
-
extras.inputmode = "url";
|
|
15802
|
-
break;
|
|
15803
|
-
case "tel":
|
|
15804
|
-
tag = "input";
|
|
15805
|
-
inputType = "tel";
|
|
15806
|
-
extras.inputmode = "tel";
|
|
15807
|
-
break;
|
|
15808
|
-
case "object":
|
|
15809
|
-
case "dict":
|
|
15810
|
-
case "template":
|
|
15811
|
-
case "array":
|
|
15812
|
-
case "list":
|
|
15813
|
-
case "coordinates":
|
|
15814
|
-
case "blob":
|
|
15815
|
-
tag = "jsoneditor";
|
|
15816
|
-
break;
|
|
15817
|
-
case "enum":
|
|
15818
|
-
tag = "select2";
|
|
15819
|
-
switch (field_desc.real_type) {
|
|
15820
|
-
case "string":
|
|
15821
|
-
case "object":
|
|
15822
|
-
case "dict":
|
|
15823
|
-
case "array":
|
|
15824
|
-
case "list":
|
|
15825
|
-
options = list2options(field_desc.enum_list, "id", "id");
|
|
15826
|
-
break;
|
|
15827
|
-
}
|
|
15828
|
-
break;
|
|
15829
|
-
case "hook": break;
|
|
15830
|
-
case "fkey":
|
|
15831
|
-
tag = "select2";
|
|
15832
|
-
switch (field_desc.real_type) {
|
|
15833
|
-
case "string":
|
|
15834
|
-
case "object":
|
|
15835
|
-
case "dict":
|
|
15836
|
-
case "array":
|
|
15837
|
-
case "list":
|
|
15838
|
-
options = get_fkey_options(gobj, col);
|
|
15839
|
-
break;
|
|
15840
|
-
}
|
|
15841
|
-
break;
|
|
15842
|
-
}
|
|
15843
|
-
return [
|
|
15844
|
-
tag,
|
|
15845
|
-
inputType,
|
|
15846
|
-
options,
|
|
15847
|
-
extras
|
|
15848
|
-
];
|
|
15849
|
-
}
|
|
15850
|
-
/******************************************************************
|
|
15851
|
-
* Helper function to create different form elements
|
|
15852
|
-
* Available types:
|
|
15853
|
-
* - input
|
|
15854
|
-
* - textarea
|
|
15855
|
-
* - select
|
|
15856
|
-
* - select2
|
|
15857
|
-
* - checkbox
|
|
15858
|
-
* - radio
|
|
15859
|
-
* - jsoneditor
|
|
15860
|
-
*
|
|
15861
|
-
* {
|
|
15862
|
-
* tag: field tag,
|
|
15863
|
-
* inputType: input type,
|
|
15864
|
-
* name: id
|
|
15865
|
-
* label: ""
|
|
15866
|
-
* placeholder: "",
|
|
15867
|
-
* options: [{id:"", value:""}]]
|
|
15868
|
-
* extras: {}
|
|
15869
|
-
* readonly: bool
|
|
15870
|
-
* required: bool
|
|
15871
|
-
* hidden: bool
|
|
15872
|
-
* not_null: bool
|
|
15873
|
-
* _col: col
|
|
15874
|
-
* }
|
|
15875
|
-
******************************************************************/
|
|
15876
|
-
function create_html_field({ tag, inputType, name, label, placeholder, options, extras, readonly, required, hidden, not_null }) {
|
|
15877
|
-
let base;
|
|
15878
|
-
switch (tag) {
|
|
15879
|
-
case "input":
|
|
15880
|
-
case "textarea":
|
|
15881
|
-
case "select":
|
|
15882
|
-
case "select2":
|
|
15883
|
-
case "checkbox":
|
|
15884
|
-
case "radio":
|
|
15885
|
-
case "jsoneditor":
|
|
15886
|
-
base = [
|
|
15887
|
-
"div",
|
|
15888
|
-
{ class: "xfield field is-horizontal" },
|
|
15889
|
-
[[
|
|
15890
|
-
"div",
|
|
15891
|
-
{ class: "field-label is-normal" },
|
|
15892
|
-
[[
|
|
15893
|
-
"label",
|
|
15894
|
-
{
|
|
15895
|
-
class: "label ",
|
|
15896
|
-
i18n: label,
|
|
15897
|
-
for: name
|
|
15898
|
-
},
|
|
15899
|
-
label
|
|
15900
|
-
]]
|
|
15901
|
-
], [
|
|
15902
|
-
"div",
|
|
15903
|
-
{ class: "field-body" },
|
|
15904
|
-
[[
|
|
15905
|
-
"div",
|
|
15906
|
-
{ class: "field" },
|
|
15907
|
-
[[
|
|
15908
|
-
"div",
|
|
15909
|
-
{ class: "control" },
|
|
15910
|
-
[]
|
|
15911
|
-
]]
|
|
15912
|
-
]]
|
|
15913
|
-
]]
|
|
15914
|
-
];
|
|
15915
|
-
break;
|
|
15916
|
-
default: base = [
|
|
15917
|
-
"div",
|
|
15918
|
-
{ class: "control" },
|
|
15919
|
-
"Unsupported element type"
|
|
15920
|
-
];
|
|
15921
|
-
}
|
|
15922
|
-
let $element = createElement2(base);
|
|
15923
|
-
let $control = $element.querySelector(".control");
|
|
15924
|
-
switch (tag) {
|
|
15925
|
-
case "input": {
|
|
15926
|
-
let attrs = {
|
|
15927
|
-
class: `input ${name === "id" ? "with-focus" : ""}`,
|
|
15928
|
-
name,
|
|
15929
|
-
type: inputType,
|
|
15930
|
-
placeholder
|
|
15931
|
-
};
|
|
15932
|
-
Object.assign(attrs, extras);
|
|
15933
|
-
if (readonly) attrs.readonly = "";
|
|
15934
|
-
let $extend = createElement2([
|
|
15935
|
-
"input",
|
|
15936
|
-
attrs,
|
|
15937
|
-
"",
|
|
15938
|
-
{ "blur": function(evt) {
|
|
15939
|
-
validate_field_on_blur(this);
|
|
15940
|
-
} }
|
|
15941
|
-
]);
|
|
15942
|
-
$control.appendChild($extend);
|
|
15943
|
-
$control.appendChild(createElement2([
|
|
15944
|
-
"p",
|
|
15945
|
-
{
|
|
15946
|
-
class: "help is-danger",
|
|
15947
|
-
style: "display:none"
|
|
15948
|
-
},
|
|
15949
|
-
""
|
|
15950
|
-
]));
|
|
15951
|
-
break;
|
|
15952
|
-
}
|
|
15953
|
-
case "jsoneditor": {
|
|
15954
|
-
let attrs = {
|
|
15955
|
-
class: "jsoneditor jse-theme-dark",
|
|
15956
|
-
style: ""
|
|
15957
|
-
};
|
|
15958
|
-
Object.assign(attrs, extras);
|
|
15959
|
-
if (readonly) attrs.readonly = "";
|
|
15960
|
-
let $extend = createElement2(["div", attrs]);
|
|
15961
|
-
$extend.setAttribute("name", name);
|
|
15962
|
-
$control.appendChild($extend);
|
|
15963
|
-
$control.appendChild(createElement2([
|
|
15964
|
-
"p",
|
|
15965
|
-
{
|
|
15966
|
-
class: "help is-danger",
|
|
15967
|
-
style: "display:none"
|
|
15968
|
-
},
|
|
15969
|
-
""
|
|
15970
|
-
]));
|
|
15971
|
-
break;
|
|
15972
|
-
}
|
|
15973
|
-
case "textarea": {
|
|
15974
|
-
let attrs = {
|
|
15975
|
-
class: "textarea",
|
|
15976
|
-
name,
|
|
15977
|
-
placeholder
|
|
15978
|
-
};
|
|
15979
|
-
Object.assign(attrs, extras);
|
|
15980
|
-
if (readonly) attrs.readonly = "";
|
|
15981
|
-
let $extend = createElement2([
|
|
15982
|
-
"textarea",
|
|
15983
|
-
attrs,
|
|
15984
|
-
"",
|
|
15985
|
-
{ "blur": function(evt) {
|
|
15986
|
-
validate_field_on_blur(this);
|
|
15987
|
-
} }
|
|
15988
|
-
]);
|
|
15989
|
-
$control.appendChild($extend);
|
|
15990
|
-
$control.appendChild(createElement2([
|
|
15991
|
-
"p",
|
|
15992
|
-
{
|
|
15993
|
-
class: "help is-danger",
|
|
15994
|
-
style: "display:none"
|
|
15995
|
-
},
|
|
15996
|
-
""
|
|
15997
|
-
]));
|
|
15998
|
-
break;
|
|
15999
|
-
}
|
|
16000
|
-
case "select": {
|
|
16001
|
-
let $extend = createElement2([
|
|
16002
|
-
"div",
|
|
16003
|
-
{ class: "select" },
|
|
16004
|
-
[
|
|
16005
|
-
"select",
|
|
16006
|
-
{ name },
|
|
16007
|
-
options.map((option) => [
|
|
16008
|
-
"option",
|
|
16009
|
-
{
|
|
16010
|
-
value: option.id,
|
|
16011
|
-
i18n: option.value
|
|
16012
|
-
},
|
|
16013
|
-
option.value
|
|
16014
|
-
])
|
|
16015
|
-
]
|
|
16016
|
-
]);
|
|
16017
|
-
$control.appendChild($extend);
|
|
16018
|
-
$control.appendChild(createElement2([
|
|
16019
|
-
"p",
|
|
16020
|
-
{
|
|
16021
|
-
class: "help is-danger",
|
|
16022
|
-
style: "display:none"
|
|
16023
|
-
},
|
|
16024
|
-
""
|
|
16025
|
-
]));
|
|
16026
|
-
break;
|
|
16027
|
-
}
|
|
16028
|
-
case "select2": {
|
|
16029
|
-
let $extend = createElement2([
|
|
16030
|
-
"select",
|
|
16031
|
-
{
|
|
16032
|
-
name,
|
|
16033
|
-
class: "select2-multiple",
|
|
16034
|
-
multiple: "multiple",
|
|
16035
|
-
style: "width: 100% !important;"
|
|
16036
|
-
},
|
|
16037
|
-
options.map((option) => [
|
|
16038
|
-
"option",
|
|
16039
|
-
{
|
|
16040
|
-
value: option.id,
|
|
16041
|
-
i18n: option.value
|
|
16042
|
-
},
|
|
16043
|
-
option.value
|
|
16044
|
-
])
|
|
16045
|
-
]);
|
|
16046
|
-
$control.appendChild($extend);
|
|
16047
|
-
$control.appendChild(createElement2([
|
|
16048
|
-
"p",
|
|
16049
|
-
{
|
|
16050
|
-
class: "help is-danger",
|
|
16051
|
-
style: "display:none"
|
|
16052
|
-
},
|
|
16053
|
-
""
|
|
16054
|
-
]));
|
|
16055
|
-
break;
|
|
16056
|
-
}
|
|
16057
|
-
case "checkbox": {
|
|
16058
|
-
let $extend = createElement2([
|
|
16059
|
-
"label",
|
|
16060
|
-
{ class: "checkbox" },
|
|
16061
|
-
["input", {
|
|
16062
|
-
type: "checkbox",
|
|
16063
|
-
name
|
|
16064
|
-
}]
|
|
16065
|
-
]);
|
|
16066
|
-
$control.appendChild($extend);
|
|
16067
|
-
$control.appendChild(createElement2([
|
|
16068
|
-
"p",
|
|
16069
|
-
{
|
|
16070
|
-
class: "help is-danger",
|
|
16071
|
-
style: "display:none"
|
|
16072
|
-
},
|
|
16073
|
-
""
|
|
16074
|
-
]));
|
|
16075
|
-
break;
|
|
16076
|
-
}
|
|
16077
|
-
case "radio":
|
|
16078
|
-
for (const option of options) {
|
|
16079
|
-
let $extend = createOneHtml(`
|
|
16080
|
-
<label class="radio">
|
|
16081
|
-
<input type="radio" name="${name}">
|
|
16082
|
-
${t(option)}
|
|
16083
|
-
</label>
|
|
16084
|
-
`);
|
|
16085
|
-
$control.appendChild($extend);
|
|
16086
|
-
}
|
|
16087
|
-
$control.appendChild(createElement2([
|
|
16088
|
-
"p",
|
|
16089
|
-
{
|
|
16090
|
-
class: "help is-danger",
|
|
16091
|
-
style: "display:none"
|
|
16092
|
-
},
|
|
16093
|
-
""
|
|
16094
|
-
]));
|
|
16095
|
-
break;
|
|
16096
|
-
default: log_error(`Unsupported element type, tag ${tag}`);
|
|
16174
|
+
function build_fkey_options(gobj) {
|
|
16175
|
+
let desc = gobj_read_attr(gobj, "desc");
|
|
16176
|
+
let fkey_options = {};
|
|
16177
|
+
for (let col of desc.cols) {
|
|
16178
|
+
if (treedb_get_field_desc(col).type !== "fkey" || !is_object(col.fkey)) continue;
|
|
16179
|
+
let topic_name = Object.keys(col.fkey)[0];
|
|
16180
|
+
if (!topic_name || fkey_options[topic_name] !== void 0) continue;
|
|
16181
|
+
let webix = gobj_command(gobj_parent(gobj), "get_topic_data", { topic_name }, gobj);
|
|
16182
|
+
fkey_options[topic_name] = webix && is_array(webix.data) ? webix.data : [];
|
|
16097
16183
|
}
|
|
16098
|
-
return
|
|
16184
|
+
return fkey_options;
|
|
16099
16185
|
}
|
|
16100
|
-
|
|
16101
|
-
*
|
|
16102
|
-
|
|
16103
|
-
|
|
16104
|
-
|
|
16105
|
-
|
|
16186
|
+
/************************************************************
|
|
16187
|
+
* Open the edit/create dialog hosting a C_YUI_FORM child.
|
|
16188
|
+
* The child is created fresh on every open (schema and fkey
|
|
16189
|
+
* options are read at its build time) and destroyed on close.
|
|
16190
|
+
* Its EV_SAVE_RECORD (CHILD model) arrives already in treedb
|
|
16191
|
+
* shape — see ac_form_save_record().
|
|
16192
|
+
************************************************************/
|
|
16193
|
+
function open_form_dialog(gobj, mode, record) {
|
|
16194
|
+
close_form_dialog(gobj);
|
|
16195
|
+
let priv = gobj.priv;
|
|
16196
|
+
let desc = gobj_read_attr(gobj, "desc");
|
|
16197
|
+
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
16198
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
16106
16199
|
let $element = createElement2([
|
|
16107
16200
|
"div",
|
|
16108
16201
|
{
|
|
16109
|
-
id:
|
|
16110
|
-
class: "modal"
|
|
16202
|
+
id: popup_id,
|
|
16203
|
+
class: "modal is-active"
|
|
16111
16204
|
},
|
|
16112
16205
|
[[
|
|
16113
16206
|
"div",
|
|
@@ -16116,333 +16209,120 @@ function build_topic_modal(gobj) {
|
|
|
16116
16209
|
], [
|
|
16117
16210
|
"div",
|
|
16118
16211
|
{ class: "modal-card modal-is-responsive" },
|
|
16119
|
-
[
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
|
|
16212
|
+
[[
|
|
16213
|
+
"header",
|
|
16214
|
+
{ class: "modal-card-head p-3" },
|
|
16215
|
+
[[
|
|
16216
|
+
"p",
|
|
16217
|
+
{
|
|
16218
|
+
class: "modal-card-title",
|
|
16219
|
+
style: "text-align:center;"
|
|
16220
|
+
},
|
|
16221
|
+
t(topic_name)
|
|
16222
|
+
], [
|
|
16223
|
+
"button",
|
|
16224
|
+
{
|
|
16131
16225
|
class: "delete is-large",
|
|
16132
16226
|
"aria-label": "close"
|
|
16133
|
-
}
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
]
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
},
|
|
16149
|
-
[
|
|
16150
|
-
[
|
|
16151
|
-
"button",
|
|
16152
|
-
{
|
|
16153
|
-
class: "button is-success px-4",
|
|
16154
|
-
type: "submit"
|
|
16155
|
-
},
|
|
16156
|
-
[[
|
|
16157
|
-
"span",
|
|
16158
|
-
{ class: "icon" },
|
|
16159
|
-
[["i", { class: "yi-floppy-disk" }]]
|
|
16160
|
-
], [
|
|
16161
|
-
"span",
|
|
16162
|
-
{ i18n: "save" },
|
|
16163
|
-
"save"
|
|
16164
|
-
]],
|
|
16165
|
-
{ click: function(evt) {
|
|
16166
|
-
evt.stopPropagation();
|
|
16167
|
-
evt.preventDefault();
|
|
16168
|
-
let $form = $element.querySelector("form");
|
|
16169
|
-
if (validate_form(gobj, $form)) {
|
|
16170
|
-
let values = get_form_values($form);
|
|
16171
|
-
let ret;
|
|
16172
|
-
if ($form.__mode__ === "create") ret = gobj_send_event(gobj, "EV_CREATE_RECORD", values, gobj);
|
|
16173
|
-
else ret = gobj_send_event(gobj, "EV_UPDATE_RECORD", values, gobj);
|
|
16174
|
-
if (ret === 0) $element.classList.remove("is-active");
|
|
16175
|
-
}
|
|
16176
|
-
} }
|
|
16177
|
-
],
|
|
16178
|
-
[
|
|
16179
|
-
"button",
|
|
16180
|
-
{ class: "button px-4 form-close" },
|
|
16181
|
-
[[
|
|
16182
|
-
"span",
|
|
16183
|
-
{ class: "icon" },
|
|
16184
|
-
[["i", { class: "yi-xmark" }]]
|
|
16185
|
-
], [
|
|
16186
|
-
"span",
|
|
16187
|
-
{ i18n: "cancel" },
|
|
16188
|
-
"cancel"
|
|
16189
|
-
]]
|
|
16190
|
-
],
|
|
16191
|
-
[
|
|
16192
|
-
"button",
|
|
16193
|
-
{ class: "button px-4" },
|
|
16194
|
-
[[
|
|
16195
|
-
"span",
|
|
16196
|
-
{ class: "icon" },
|
|
16197
|
-
[["i", { class: "yi-broom" }]]
|
|
16198
|
-
], [
|
|
16199
|
-
"span",
|
|
16200
|
-
{ i18n: "clear" },
|
|
16201
|
-
"clear"
|
|
16202
|
-
]],
|
|
16203
|
-
{ click: function(evt) {
|
|
16204
|
-
evt.stopPropagation();
|
|
16205
|
-
clear_validation(gobj, $form);
|
|
16206
|
-
clear_data(gobj, $form);
|
|
16207
|
-
} }
|
|
16208
|
-
]
|
|
16209
|
-
]
|
|
16210
|
-
]]
|
|
16211
|
-
]
|
|
16212
|
-
]
|
|
16227
|
+
},
|
|
16228
|
+
"",
|
|
16229
|
+
{ click: function(evt) {
|
|
16230
|
+
evt.stopPropagation();
|
|
16231
|
+
request_close_form_dialog(gobj);
|
|
16232
|
+
} }
|
|
16233
|
+
]]
|
|
16234
|
+
], [
|
|
16235
|
+
"section",
|
|
16236
|
+
{
|
|
16237
|
+
class: "modal-card-body p-2",
|
|
16238
|
+
style: "display:flex; flex-direction:column;"
|
|
16239
|
+
},
|
|
16240
|
+
[]
|
|
16241
|
+
]]
|
|
16213
16242
|
]],
|
|
16214
16243
|
{ click: function(evt) {
|
|
16215
16244
|
evt.stopPropagation();
|
|
16216
16245
|
} }
|
|
16217
16246
|
]);
|
|
16218
|
-
$element.
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
|
|
16231
|
-
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
|
|
16235
|
-
});
|
|
16236
|
-
});
|
|
16237
|
-
$element.querySelectorAll(".select2-multiple").forEach((el) => {
|
|
16238
|
-
if (el.tomselect) return;
|
|
16239
|
-
new TomSelect(el, {
|
|
16240
|
-
plugins: ["remove_button"],
|
|
16241
|
-
create: false,
|
|
16242
|
-
hideSelected: true,
|
|
16243
|
-
closeAfterSelect: false
|
|
16244
|
-
});
|
|
16245
|
-
});
|
|
16246
|
-
return $element;
|
|
16247
|
-
}
|
|
16248
|
-
/************************************************************
|
|
16249
|
-
* Show a validation error on a field
|
|
16250
|
-
************************************************************/
|
|
16251
|
-
function show_field_error($field, message) {
|
|
16252
|
-
$field.querySelectorAll("input, textarea, select").forEach((el) => {
|
|
16253
|
-
el.classList.add("is-danger");
|
|
16254
|
-
});
|
|
16255
|
-
let $help = $field.querySelector(".help");
|
|
16256
|
-
if ($help) {
|
|
16257
|
-
$help.textContent = message;
|
|
16258
|
-
$help.style.display = "block";
|
|
16259
|
-
}
|
|
16260
|
-
}
|
|
16261
|
-
/************************************************************
|
|
16262
|
-
* Clear a validation error from a field
|
|
16263
|
-
************************************************************/
|
|
16264
|
-
function clear_field_error($field) {
|
|
16265
|
-
$field.querySelectorAll("input, textarea, select").forEach((el) => {
|
|
16266
|
-
el.classList.remove("is-danger");
|
|
16267
|
-
});
|
|
16268
|
-
let $help = $field.querySelector(".help");
|
|
16269
|
-
if ($help) {
|
|
16270
|
-
$help.textContent = "";
|
|
16271
|
-
$help.style.display = "none";
|
|
16247
|
+
let $body = $element.querySelector(".modal-card-body");
|
|
16248
|
+
let form = gobj_create_pure_child("form_" + clean_name(gobj_name(gobj)), "C_YUI_FORM", {
|
|
16249
|
+
template: build_form_template(gobj),
|
|
16250
|
+
record,
|
|
16251
|
+
fkey_options: build_fkey_options(gobj),
|
|
16252
|
+
form_mode: mode,
|
|
16253
|
+
pkey: desc.pkey || "id",
|
|
16254
|
+
topic_name,
|
|
16255
|
+
editable: true,
|
|
16256
|
+
$parent: $body
|
|
16257
|
+
}, gobj);
|
|
16258
|
+
priv.form = form;
|
|
16259
|
+
priv.$popup = $element;
|
|
16260
|
+
let $form_container = gobj_read_attr(form, "$container");
|
|
16261
|
+
if ($form_container) {
|
|
16262
|
+
$form_container.style.flex = "1 1 auto";
|
|
16263
|
+
$form_container.style.minHeight = "0";
|
|
16272
16264
|
}
|
|
16265
|
+
popup_mount_layer().appendChild($element);
|
|
16266
|
+
refresh_language($element, t);
|
|
16267
|
+
gobj_start(form);
|
|
16268
|
+
let $with_focus = $element.querySelector(".with-focus");
|
|
16269
|
+
if ($with_focus) $with_focus.focus();
|
|
16273
16270
|
}
|
|
16274
16271
|
/************************************************************
|
|
16275
|
-
*
|
|
16272
|
+
* Close request from the dialog X: if the form carries
|
|
16273
|
+
* unsaved changes ask first (EV_WINDOW_TO_CLOSE contract).
|
|
16276
16274
|
************************************************************/
|
|
16277
|
-
function
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16284
|
-
|
|
16285
|
-
|
|
16286
|
-
|
|
16287
|
-
if (!$input) return false;
|
|
16288
|
-
return $input.checked;
|
|
16289
|
-
}
|
|
16290
|
-
case "textarea": {
|
|
16291
|
-
let $el = $field.querySelector("textarea");
|
|
16292
|
-
if (!$el) return "";
|
|
16293
|
-
return $el.value;
|
|
16294
|
-
}
|
|
16295
|
-
case "select":
|
|
16296
|
-
case "select2": {
|
|
16297
|
-
let $el = $field.querySelector("select");
|
|
16298
|
-
if (!$el) return "";
|
|
16299
|
-
if ($el.tomselect) return $el.tomselect.getValue();
|
|
16300
|
-
return $el.value;
|
|
16301
|
-
}
|
|
16302
|
-
case "jsoneditor": {
|
|
16303
|
-
let $el = $field.querySelector(".jsoneditor");
|
|
16304
|
-
if (!$el || !$el.jsoneditor) return "";
|
|
16305
|
-
let val = $el.jsoneditor.get();
|
|
16306
|
-
if (val.text !== void 0) return val.text;
|
|
16307
|
-
return JSON.stringify(val.json);
|
|
16308
|
-
}
|
|
16309
|
-
case "radio": {
|
|
16310
|
-
let $checked = $field.querySelector("input[type=\"radio\"]:checked");
|
|
16311
|
-
if (!$checked) return "";
|
|
16312
|
-
return $checked.value;
|
|
16275
|
+
function request_close_form_dialog(gobj) {
|
|
16276
|
+
let priv = gobj.priv;
|
|
16277
|
+
if (priv.form) {
|
|
16278
|
+
let kw = {};
|
|
16279
|
+
gobj_send_event(priv.form, "EV_WINDOW_TO_CLOSE", kw, gobj);
|
|
16280
|
+
if (kw.abort_close) {
|
|
16281
|
+
get_yesnocancel(t("All changes will be lost. Are you sure?"), function(evt) {
|
|
16282
|
+
if (evt === "yes") close_form_dialog(gobj);
|
|
16283
|
+
});
|
|
16284
|
+
return;
|
|
16313
16285
|
}
|
|
16314
16286
|
}
|
|
16315
|
-
|
|
16287
|
+
close_form_dialog(gobj);
|
|
16316
16288
|
}
|
|
16317
16289
|
/************************************************************
|
|
16318
|
-
*
|
|
16290
|
+
* Destroy the hosted form and remove the dialog DOM.
|
|
16319
16291
|
************************************************************/
|
|
16320
|
-
function
|
|
16321
|
-
|
|
16322
|
-
if (
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
* Determine if a field requires validation in the current mode
|
|
16327
|
-
************************************************************/
|
|
16328
|
-
function is_field_required(conf, mode) {
|
|
16329
|
-
if (conf.is_pkey) return mode === "create" && !conf.is_rowid;
|
|
16330
|
-
if (conf.readonly) return false;
|
|
16331
|
-
return conf.required || conf.not_null;
|
|
16332
|
-
}
|
|
16333
|
-
/************************************************************
|
|
16334
|
-
* Validate a single field on blur
|
|
16335
|
-
* Called from input/textarea blur event handlers
|
|
16336
|
-
************************************************************/
|
|
16337
|
-
function validate_field_on_blur(element) {
|
|
16338
|
-
let $field = element.closest(".xfield");
|
|
16339
|
-
if (!$field || !$field.field_conf) return;
|
|
16340
|
-
let conf = $field.field_conf;
|
|
16341
|
-
let $form = element.closest("form");
|
|
16342
|
-
if (!is_field_required(conf, $form ? $form.__mode__ : "update")) {
|
|
16343
|
-
clear_field_error($field);
|
|
16344
|
-
return;
|
|
16292
|
+
function close_form_dialog(gobj) {
|
|
16293
|
+
let priv = gobj.priv;
|
|
16294
|
+
if (priv.form) {
|
|
16295
|
+
if (gobj_is_running(priv.form)) gobj_stop(priv.form);
|
|
16296
|
+
gobj_destroy(priv.form);
|
|
16297
|
+
priv.form = null;
|
|
16345
16298
|
}
|
|
16346
|
-
if (
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
/************************************************************
|
|
16350
|
-
* Validate all required fields in the form
|
|
16351
|
-
* Returns true if valid, false otherwise
|
|
16352
|
-
************************************************************/
|
|
16353
|
-
function validate_form(gobj, $form) {
|
|
16354
|
-
let is_valid = true;
|
|
16355
|
-
let first_invalid = null;
|
|
16356
|
-
let mode = $form.__mode__ || "update";
|
|
16357
|
-
$form.querySelectorAll(".xfield").forEach(($field) => {
|
|
16358
|
-
let conf = $field.field_conf;
|
|
16359
|
-
if (!conf) return;
|
|
16360
|
-
if (!is_field_required(conf, mode)) {
|
|
16361
|
-
clear_field_error($field);
|
|
16362
|
-
return;
|
|
16363
|
-
}
|
|
16364
|
-
if (is_field_value_empty(get_field_value_for_validation($field, conf))) {
|
|
16365
|
-
show_field_error($field, t("this field is required"));
|
|
16366
|
-
is_valid = false;
|
|
16367
|
-
if (!first_invalid) first_invalid = $field;
|
|
16368
|
-
} else clear_field_error($field);
|
|
16369
|
-
});
|
|
16370
|
-
if (first_invalid) {
|
|
16371
|
-
let $focusable = first_invalid.querySelector("input, select, textarea");
|
|
16372
|
-
if ($focusable && $focusable.focus) $focusable.focus();
|
|
16299
|
+
if (priv.$popup) {
|
|
16300
|
+
if (priv.$popup.parentNode) priv.$popup.parentNode.removeChild(priv.$popup);
|
|
16301
|
+
priv.$popup = null;
|
|
16373
16302
|
}
|
|
16374
|
-
return is_valid;
|
|
16375
|
-
}
|
|
16376
|
-
/************************************************************
|
|
16377
|
-
* Clear all validation errors from the form
|
|
16378
|
-
************************************************************/
|
|
16379
|
-
function clear_validation(gobj, $form) {
|
|
16380
|
-
$form.querySelectorAll(".xfield").forEach(($field) => {
|
|
16381
|
-
clear_field_error($field);
|
|
16382
|
-
});
|
|
16383
16303
|
}
|
|
16384
16304
|
/************************************************************
|
|
16385
|
-
*
|
|
16386
|
-
* set form state "update" or "create"
|
|
16387
|
-
* Only the "id" field is modified,
|
|
16388
|
-
* in "update" is readonly
|
|
16389
|
-
* in "create" is writable
|
|
16390
|
-
* Although by default the pkey (primary key) is "id",
|
|
16391
|
-
* use always desc.pkey value, who knows all cases!
|
|
16305
|
+
* True if the topic's pkey col carries the "rowid" flag.
|
|
16392
16306
|
************************************************************/
|
|
16393
|
-
function
|
|
16307
|
+
function pkey_is_rowid(gobj) {
|
|
16394
16308
|
let desc = gobj_read_attr(gobj, "desc");
|
|
16395
|
-
let
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
$id.setAttribute("readonly", "readonly");
|
|
16399
|
-
$id.removeAttribute("required");
|
|
16400
|
-
} else if (is_rowid) {
|
|
16401
|
-
$id.setAttribute("readonly", "readonly");
|
|
16402
|
-
$id.removeAttribute("required");
|
|
16403
|
-
} else {
|
|
16404
|
-
$id.removeAttribute("readonly");
|
|
16405
|
-
$id.setAttribute("required", "");
|
|
16406
|
-
}
|
|
16407
|
-
else log_error(sprintf("%s: form without id field ('%s')", gobj_short_name(gobj), desc.pkey));
|
|
16408
|
-
if (is_rowid) mode = "create";
|
|
16409
|
-
$form.__mode__ = mode;
|
|
16410
|
-
$form.__index__ = index;
|
|
16309
|
+
let col_pkey = kwid_find_one_record(gobj, desc.cols, null, { "id": desc.pkey });
|
|
16310
|
+
if (!col_pkey) return false;
|
|
16311
|
+
return str_in_list(col_pkey.flag, "rowid");
|
|
16411
16312
|
}
|
|
16412
16313
|
/************************************************************
|
|
16413
16314
|
* Show the edit "update" form of a row (record)
|
|
16414
16315
|
* internally called from the icon edit at Op column
|
|
16415
16316
|
************************************************************/
|
|
16416
16317
|
function show_edit_form(gobj, row, index) {
|
|
16417
|
-
|
|
16418
|
-
let form_id = gobj_read_str_attr(gobj, "form_id");
|
|
16419
|
-
let $element = document.getElementById(popup_id);
|
|
16420
|
-
if (!$element) $element = build_topic_modal(gobj);
|
|
16421
|
-
let $form = document.getElementById(form_id);
|
|
16422
|
-
clear_validation(gobj, $form);
|
|
16423
|
-
if (json_size(row) === 0 || json_size(row) === 1 && "_check_box_state_" in row) set_form_mode(gobj, $form, "create", index);
|
|
16424
|
-
else set_form_mode(gobj, $form, "update", index);
|
|
16425
|
-
set_form_values(gobj, $form, row);
|
|
16426
|
-
$element.classList.add("is-active");
|
|
16427
|
-
let $with_focus = $element.querySelector(".with-focus");
|
|
16428
|
-
if ($with_focus) $with_focus.focus();
|
|
16318
|
+
open_form_dialog(gobj, pkey_is_rowid(gobj) ? "create" : "update", row);
|
|
16429
16319
|
}
|
|
16430
16320
|
/************************************************************
|
|
16431
16321
|
* Show the edit "create" form to a new record
|
|
16432
16322
|
* internally called from the top toolbar +New button
|
|
16433
16323
|
************************************************************/
|
|
16434
16324
|
function show_create_form(gobj, row) {
|
|
16435
|
-
|
|
16436
|
-
let form_id = gobj_read_str_attr(gobj, "form_id");
|
|
16437
|
-
let $element = document.getElementById(popup_id);
|
|
16438
|
-
if (!$element) $element = build_topic_modal(gobj);
|
|
16439
|
-
let $form = document.getElementById(form_id);
|
|
16440
|
-
clear_validation(gobj, $form);
|
|
16441
|
-
set_form_mode(gobj, $form, "create", -1);
|
|
16442
|
-
set_form_values(gobj, $form, row);
|
|
16443
|
-
$element.classList.add("is-active");
|
|
16444
|
-
let $with_focus = $element.querySelector(".with-focus");
|
|
16445
|
-
if ($with_focus) $with_focus.focus();
|
|
16325
|
+
open_form_dialog(gobj, "create", row);
|
|
16446
16326
|
}
|
|
16447
16327
|
/************************************************************
|
|
16448
16328
|
* Return column schema of field id
|
|
@@ -16456,174 +16336,6 @@ function get_schema_col(gobj, id) {
|
|
|
16456
16336
|
return null;
|
|
16457
16337
|
}
|
|
16458
16338
|
/************************************************************
|
|
16459
|
-
*
|
|
16460
|
-
************************************************************/
|
|
16461
|
-
function clear_data(gobj, $form) {
|
|
16462
|
-
$form.querySelectorAll("input, select, textarea, .jsoneditor").forEach((input) => {
|
|
16463
|
-
if (input.name !== "id") if (input.classList.contains("jsoneditor")) {
|
|
16464
|
-
let jsoneditor = input.jsoneditor;
|
|
16465
|
-
if (jsoneditor) jsoneditor.set({ text: "" });
|
|
16466
|
-
} else if (input.type === "checkbox") input.checked = false;
|
|
16467
|
-
else if (input.type === "radio") input.checked = false;
|
|
16468
|
-
else if (input.type === "datetime-local") input.value = null;
|
|
16469
|
-
else if (input.multiple) {
|
|
16470
|
-
if (input.tomselect) input.tomselect.clear();
|
|
16471
|
-
} else input.value = null;
|
|
16472
|
-
});
|
|
16473
|
-
}
|
|
16474
|
-
/************************************************************
|
|
16475
|
-
*
|
|
16476
|
-
************************************************************/
|
|
16477
|
-
function get_form_values($form) {
|
|
16478
|
-
const values = {};
|
|
16479
|
-
$form.querySelectorAll("input, select, textarea, .jsoneditor").forEach((input) => {
|
|
16480
|
-
if (input.classList.contains("jsoneditor")) {
|
|
16481
|
-
let jsoneditor = input.jsoneditor;
|
|
16482
|
-
if (jsoneditor) {
|
|
16483
|
-
let name = input.getAttribute("name");
|
|
16484
|
-
let value = jsoneditor.get();
|
|
16485
|
-
if (value.text === void 0) values[name] = JSON.stringify(value.json, null, null);
|
|
16486
|
-
else values[name] = value.text;
|
|
16487
|
-
}
|
|
16488
|
-
} else if (input.type === "checkbox") values[input.name] = input.checked;
|
|
16489
|
-
else if (input.type === "radio") {
|
|
16490
|
-
if (input.checked) values[input.name] = input.value;
|
|
16491
|
-
} else if (input.type === "datetime-local") {
|
|
16492
|
-
const date = new Date(input.value);
|
|
16493
|
-
const epochTime = Math.floor(date.getTime() / 1e3);
|
|
16494
|
-
values[input.name] = epochTime;
|
|
16495
|
-
} else if (input.multiple) {
|
|
16496
|
-
if (input.tomselect) values[input.name] = input.tomselect.getValue();
|
|
16497
|
-
} else values[input.name] = input.value;
|
|
16498
|
-
});
|
|
16499
|
-
return values;
|
|
16500
|
-
}
|
|
16501
|
-
/************************************************************
|
|
16502
|
-
* Convert a record column value from backend to frontend
|
|
16503
|
-
************************************************************/
|
|
16504
|
-
function epochToDateTimeLocal(epochTime) {
|
|
16505
|
-
const date = /* @__PURE__ */ new Date(epochTime * 1e3);
|
|
16506
|
-
const year = date.getFullYear();
|
|
16507
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
16508
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
16509
|
-
const hours = String(date.getHours()).padStart(2, "0");
|
|
16510
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
16511
|
-
String(date.getSeconds()).padStart(2, "0");
|
|
16512
|
-
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
16513
|
-
}
|
|
16514
|
-
/************************************************************
|
|
16515
|
-
*
|
|
16516
|
-
************************************************************/
|
|
16517
|
-
function set_form_values(gobj, form, values) {
|
|
16518
|
-
Object.keys(values).forEach((key) => {
|
|
16519
|
-
let element = form.querySelector(`[name="${key}"]`);
|
|
16520
|
-
if (element) {
|
|
16521
|
-
let value = values[key];
|
|
16522
|
-
let col = get_schema_col(gobj, key);
|
|
16523
|
-
if (col) value = transform__treedb_value_2_form_value(gobj, col, value);
|
|
16524
|
-
switch (element.type) {
|
|
16525
|
-
case "select":
|
|
16526
|
-
element.value = value;
|
|
16527
|
-
break;
|
|
16528
|
-
case "select-one":
|
|
16529
|
-
element.value = value;
|
|
16530
|
-
break;
|
|
16531
|
-
case "select-multiple":
|
|
16532
|
-
if (element.tomselect) element.tomselect.setValue(value);
|
|
16533
|
-
break;
|
|
16534
|
-
case "radio":
|
|
16535
|
-
let radio = form.querySelector(`input[name="${key}"][value="${values[key]}"]`);
|
|
16536
|
-
if (radio) radio.checked = true;
|
|
16537
|
-
break;
|
|
16538
|
-
case "checkbox":
|
|
16539
|
-
element.checked = value;
|
|
16540
|
-
break;
|
|
16541
|
-
case "text":
|
|
16542
|
-
case "textarea":
|
|
16543
|
-
case "email":
|
|
16544
|
-
element.value = value;
|
|
16545
|
-
break;
|
|
16546
|
-
case "datetime-local":
|
|
16547
|
-
element.value = epochToDateTimeLocal(value);
|
|
16548
|
-
break;
|
|
16549
|
-
default:
|
|
16550
|
-
if (element.classList.contains("jsoneditor")) {
|
|
16551
|
-
let jsoneditor = element.jsoneditor;
|
|
16552
|
-
if (jsoneditor) jsoneditor.set(value);
|
|
16553
|
-
break;
|
|
16554
|
-
}
|
|
16555
|
-
element.value = value;
|
|
16556
|
-
log_error(sprintf("%s: form type unknown: '%s'", gobj_short_name(gobj), element.type));
|
|
16557
|
-
break;
|
|
16558
|
-
}
|
|
16559
|
-
}
|
|
16560
|
-
});
|
|
16561
|
-
}
|
|
16562
|
-
/************************************************************
|
|
16563
|
-
* Convert a record column value from backend
|
|
16564
|
-
* to frontend form field
|
|
16565
|
-
************************************************************/
|
|
16566
|
-
function transform__treedb_value_2_form_value(gobj, col, value) {
|
|
16567
|
-
const field_desc = treedb_get_field_desc(col);
|
|
16568
|
-
switch (field_desc.type) {
|
|
16569
|
-
case "string":
|
|
16570
|
-
case "email":
|
|
16571
|
-
case "tel":
|
|
16572
|
-
case "url": break;
|
|
16573
|
-
case "integer": break;
|
|
16574
|
-
case "real": break;
|
|
16575
|
-
case "boolean": break;
|
|
16576
|
-
case "object":
|
|
16577
|
-
case "dict":
|
|
16578
|
-
case "template":
|
|
16579
|
-
case "array":
|
|
16580
|
-
case "list":
|
|
16581
|
-
case "coordinates":
|
|
16582
|
-
case "blob":
|
|
16583
|
-
value = { json: value };
|
|
16584
|
-
break;
|
|
16585
|
-
case "enum":
|
|
16586
|
-
switch (field_desc.real_type) {
|
|
16587
|
-
case "string": break;
|
|
16588
|
-
case "object":
|
|
16589
|
-
case "dict":
|
|
16590
|
-
case "array":
|
|
16591
|
-
case "list": break;
|
|
16592
|
-
}
|
|
16593
|
-
break;
|
|
16594
|
-
case "now":
|
|
16595
|
-
case "time":
|
|
16596
|
-
switch (field_desc.real_type) {
|
|
16597
|
-
case "string":
|
|
16598
|
-
case "integer":
|
|
16599
|
-
value = parseInt(value) || 0;
|
|
16600
|
-
value = new Date(value);
|
|
16601
|
-
break;
|
|
16602
|
-
}
|
|
16603
|
-
break;
|
|
16604
|
-
case "color":
|
|
16605
|
-
switch (field_desc.real_type) {
|
|
16606
|
-
case "string": break;
|
|
16607
|
-
case "integer": break;
|
|
16608
|
-
}
|
|
16609
|
-
break;
|
|
16610
|
-
case "hook": break;
|
|
16611
|
-
case "fkey":
|
|
16612
|
-
let new_value = [];
|
|
16613
|
-
if (value) if (is_string(value)) {
|
|
16614
|
-
let fkey = treedb_decoder_fkey(col, value);
|
|
16615
|
-
if (fkey) new_value.push(fkey.id);
|
|
16616
|
-
} else if (is_array(value)) for (let i = 0; i < value.length; i++) {
|
|
16617
|
-
let fkey = treedb_decoder_fkey(col, value[i]);
|
|
16618
|
-
if (fkey) new_value.push(fkey.id);
|
|
16619
|
-
}
|
|
16620
|
-
else log_error("fkey type unsupported: " + JSON.stringify(value));
|
|
16621
|
-
value = new_value;
|
|
16622
|
-
break;
|
|
16623
|
-
}
|
|
16624
|
-
return value;
|
|
16625
|
-
}
|
|
16626
|
-
/************************************************************
|
|
16627
16339
|
* Convert from frontend to backend
|
|
16628
16340
|
* operation: "create" "update"
|
|
16629
16341
|
************************************************************/
|
|
@@ -16747,18 +16459,6 @@ function transform__form_value_2_treedb_value(gobj, col, value, operation) {
|
|
|
16747
16459
|
/************************************************************
|
|
16748
16460
|
*
|
|
16749
16461
|
************************************************************/
|
|
16750
|
-
function get_fkey_options(gobj, col) {
|
|
16751
|
-
for (let topic_name of Object.keys(col.fkey)) {
|
|
16752
|
-
let kw = { topic_name };
|
|
16753
|
-
let current_list = gobj_command(gobj_parent(gobj), "get_topic_data", kw, gobj).data;
|
|
16754
|
-
return list2options(current_list, "id", "id");
|
|
16755
|
-
}
|
|
16756
|
-
log_error("No fkey options found" + JSON.stringify(col));
|
|
16757
|
-
return null;
|
|
16758
|
-
}
|
|
16759
|
-
/************************************************************
|
|
16760
|
-
*
|
|
16761
|
-
************************************************************/
|
|
16762
16462
|
function fkey_in_col(col, topic_name, hook_name) {
|
|
16763
16463
|
if (hook_name === col.fkey[topic_name]) return true;
|
|
16764
16464
|
return false;
|
|
@@ -17088,37 +16788,18 @@ function ac_paste_rows(gobj, event, kw, src) {
|
|
|
17088
16788
|
return 0;
|
|
17089
16789
|
}
|
|
17090
16790
|
/************************************************************
|
|
17091
|
-
*
|
|
17092
|
-
|
|
17093
|
-
|
|
17094
|
-
let new_kw = null;
|
|
17095
|
-
try {
|
|
17096
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "create");
|
|
17097
|
-
} catch (e) {
|
|
17098
|
-
log_warning(e);
|
|
17099
|
-
throw e;
|
|
17100
|
-
}
|
|
17101
|
-
gobj_publish_event(gobj, "EV_CREATE_RECORD", {
|
|
17102
|
-
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
17103
|
-
record: new_kw
|
|
17104
|
-
});
|
|
17105
|
-
return 0;
|
|
17106
|
-
}
|
|
17107
|
-
/************************************************************
|
|
17108
|
-
* From internal Save button of "update" modal form
|
|
16791
|
+
* EV_SAVE_RECORD from the hosted C_YUI_FORM: kw is the
|
|
16792
|
+
* record already in treedb shape (the form itself encodes
|
|
16793
|
+
* fkeys, times and numbers). Route by the form's mode.
|
|
17109
16794
|
************************************************************/
|
|
17110
|
-
function
|
|
17111
|
-
|
|
17112
|
-
try {
|
|
17113
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "update");
|
|
17114
|
-
} catch (e) {
|
|
17115
|
-
log_warning(e);
|
|
17116
|
-
throw e;
|
|
17117
|
-
}
|
|
17118
|
-
gobj_publish_event(gobj, "EV_UPDATE_RECORD", {
|
|
16795
|
+
function ac_form_save_record(gobj, event, kw, src) {
|
|
16796
|
+
gobj_publish_event(gobj, gobj_read_str_attr(src, "form_mode") === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", {
|
|
17119
16797
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
17120
|
-
record:
|
|
16798
|
+
record: kw
|
|
17121
16799
|
});
|
|
16800
|
+
setTimeout(function() {
|
|
16801
|
+
close_form_dialog(gobj);
|
|
16802
|
+
}, 0);
|
|
17122
16803
|
return 0;
|
|
17123
16804
|
}
|
|
17124
16805
|
/************************************************************
|
|
@@ -17248,13 +16929,8 @@ function create_gclass$2(gclass_name) {
|
|
|
17248
16929
|
null
|
|
17249
16930
|
],
|
|
17250
16931
|
[
|
|
17251
|
-
"
|
|
17252
|
-
|
|
17253
|
-
null
|
|
17254
|
-
],
|
|
17255
|
-
[
|
|
17256
|
-
"EV_UPDATE_RECORD",
|
|
17257
|
-
ac_update_record,
|
|
16932
|
+
"EV_SAVE_RECORD",
|
|
16933
|
+
ac_form_save_record,
|
|
17258
16934
|
null
|
|
17259
16935
|
],
|
|
17260
16936
|
[
|
|
@@ -17319,6 +16995,7 @@ function create_gclass$2(gclass_name) {
|
|
|
17319
16995
|
["EV_LOAD_NODE_UPDATED", 0],
|
|
17320
16996
|
["EV_NODE_DELETED", 0],
|
|
17321
16997
|
["EV_EDITION_MODE", 0],
|
|
16998
|
+
["EV_SAVE_RECORD", 0],
|
|
17322
16999
|
["EV_NEW_ROW", 0],
|
|
17323
17000
|
["EV_DELETE_ROWS", 0],
|
|
17324
17001
|
["EV_COPY_ROWS", 0],
|
|
@@ -17342,6 +17019,7 @@ function create_gclass$2(gclass_name) {
|
|
|
17342
17019
|
* Register GClass
|
|
17343
17020
|
***************************************************************/
|
|
17344
17021
|
function register_c_yui_treedb_topic_with_form() {
|
|
17022
|
+
if (!gclass_find_by_name("C_YUI_FORM", false)) register_c_yui_form();
|
|
17345
17023
|
return create_gclass$2(GCLASS_NAME$2);
|
|
17346
17024
|
}
|
|
17347
17025
|
//#endregion
|