@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
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
is_string,
|
|
28
28
|
is_object,
|
|
29
29
|
str_in_list,
|
|
30
|
-
createOneHtml,
|
|
31
30
|
getPositionRelativeToBody,
|
|
32
31
|
treedb_get_field_desc,
|
|
33
32
|
treedb_decoder_fkey,
|
|
@@ -37,11 +36,15 @@ import {
|
|
|
37
36
|
treedb_hook_data_size,
|
|
38
37
|
parseBoolean,
|
|
39
38
|
json_size,
|
|
40
|
-
list2options,
|
|
41
39
|
trace_msg,
|
|
42
40
|
log_warning,
|
|
43
|
-
kw_get_local_storage_value,
|
|
44
41
|
kwid_get_ids,
|
|
42
|
+
gobj_create_pure_child,
|
|
43
|
+
gobj_start,
|
|
44
|
+
gobj_stop,
|
|
45
|
+
gobj_destroy,
|
|
46
|
+
gobj_is_running,
|
|
47
|
+
gclass_find_by_name,
|
|
45
48
|
gobj_write_attr,
|
|
46
49
|
gobj_read_bool_attr,
|
|
47
50
|
gobj_read_str_attr,
|
|
@@ -58,13 +61,9 @@ import {
|
|
|
58
61
|
get_ok,
|
|
59
62
|
} from "./c_yui_main.js";
|
|
60
63
|
|
|
61
|
-
import {
|
|
62
|
-
|
|
63
|
-
import { JSONEditor } from 'vanilla-jsoneditor';
|
|
64
|
-
import "vanilla-jsoneditor/themes/jse-theme-dark.css";
|
|
64
|
+
import {register_c_yui_form} from "./c_yui_form.js";
|
|
65
65
|
|
|
66
|
-
import "
|
|
67
|
-
import TomSelect from "tom-select"; // Import Tom-Select JS
|
|
66
|
+
import {t} from "i18next";
|
|
68
67
|
|
|
69
68
|
import "./c_yui_treedb_topic_with_form.css";
|
|
70
69
|
|
|
@@ -121,7 +120,6 @@ SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTML contai
|
|
|
121
120
|
SDATA(data_type_t.DTP_POINTER, "tabulator", 0, null, "Tabulator instance"),
|
|
122
121
|
SDATA(data_type_t.DTP_STRING, "table_id", 0, null, "Table div ID"),
|
|
123
122
|
SDATA(data_type_t.DTP_STRING, "toolbar_id", 0, null, "Toolbar ID"),
|
|
124
|
-
SDATA(data_type_t.DTP_STRING, "form_id", 0, null, "Edit form ID"),
|
|
125
123
|
SDATA(data_type_t.DTP_STRING, "popup_id", 0, null, "Edit form popup ID"),
|
|
126
124
|
|
|
127
125
|
SDATA_END()
|
|
@@ -132,6 +130,8 @@ let PRIVATE_DATA = {
|
|
|
132
130
|
treedb_name: "",
|
|
133
131
|
topic_name: "",
|
|
134
132
|
tabulator: null,
|
|
133
|
+
form: null, // hosted C_YUI_FORM child (while dialog open)
|
|
134
|
+
$popup: null, // dialog DOM element
|
|
135
135
|
};
|
|
136
136
|
|
|
137
137
|
let __gclass__ = null;
|
|
@@ -163,7 +163,6 @@ function mt_create(gobj)
|
|
|
163
163
|
let name = clean_name(gobj_name(gobj));
|
|
164
164
|
gobj_write_attr(gobj, "table_id", "table" + name);
|
|
165
165
|
gobj_write_attr(gobj, "toolbar_id", "toolbar" + name);
|
|
166
|
-
gobj_write_attr(gobj, "form_id", "form" + name);
|
|
167
166
|
gobj_write_attr(gobj, "popup_id", "popup" + name);
|
|
168
167
|
|
|
169
168
|
build_ui(gobj);
|
|
@@ -191,6 +190,7 @@ function mt_start(gobj)
|
|
|
191
190
|
***************************************************************/
|
|
192
191
|
function mt_stop(gobj)
|
|
193
192
|
{
|
|
193
|
+
close_form_dialog(gobj);
|
|
194
194
|
table__destroy(gobj);
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -579,6 +579,29 @@ function col_label(col, topic_name)
|
|
|
579
579
|
return t(keys, { defaultValue: col.header || col.id });
|
|
580
580
|
}
|
|
581
581
|
|
|
582
|
+
/******************************************************************
|
|
583
|
+
* Column-title formatter: render the header as a span carrying
|
|
584
|
+
* `data-i18n` (the shared col id) so a live language switch —
|
|
585
|
+
* which calls refresh_language(document.body) — retranslates the
|
|
586
|
+
* Tabulator headers in place, without rebuilding the table. Only
|
|
587
|
+
* tag translatable columns; otherwise keep the schema header
|
|
588
|
+
* (refresh_language would otherwise overwrite it with the raw id).
|
|
589
|
+
* Tabulator re-runs this on redraw, so the full cascade re-applies.
|
|
590
|
+
******************************************************************/
|
|
591
|
+
function col_title_formatter(gobj, col)
|
|
592
|
+
{
|
|
593
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
594
|
+
return function(cell, formatterParams, onRendered) {
|
|
595
|
+
let text = col_label(col, topic_name);
|
|
596
|
+
let attrs = {};
|
|
597
|
+
const NO_I18N = "\x00";
|
|
598
|
+
if(t(col.id, {defaultValue: NO_I18N}) !== NO_I18N) {
|
|
599
|
+
attrs.i18n = col.id;
|
|
600
|
+
}
|
|
601
|
+
return createElement2(['span', attrs, text]);
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
582
605
|
/******************************************************************
|
|
583
606
|
* Build table with Tabulator
|
|
584
607
|
* This fn is called on start and when desc attribute is set.
|
|
@@ -717,6 +740,7 @@ function create_tabulator(gobj)
|
|
|
717
740
|
|
|
718
741
|
let colDef = {
|
|
719
742
|
title: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
|
|
743
|
+
titleFormatter: col_title_formatter(gobj, col),
|
|
720
744
|
field: col.id,
|
|
721
745
|
sorter: sorter,
|
|
722
746
|
hozAlign: hozAlign,
|
|
@@ -787,7 +811,21 @@ function create_tabulator(gobj)
|
|
|
787
811
|
"color:#6b7280;padding:0 0.6rem;'></span>",
|
|
788
812
|
});
|
|
789
813
|
|
|
790
|
-
|
|
814
|
+
/*
|
|
815
|
+
* Attach by ELEMENT, resolved inside OUR $container — a bare `#id`
|
|
816
|
+
* selector needs the element to be in the document already (a view
|
|
817
|
+
* built before being mounted crashes Tabulator: "no element found",
|
|
818
|
+
* then .on() dies on externalEvents null) and is shadowed by any
|
|
819
|
+
* stale duplicate id elsewhere in the page.
|
|
820
|
+
*/
|
|
821
|
+
let $view_container = gobj_read_attr(gobj, "$container");
|
|
822
|
+
let $table_el = $view_container ?
|
|
823
|
+
$view_container.querySelector(`#${table_id}`) : null;
|
|
824
|
+
if(!$table_el) {
|
|
825
|
+
log_error(`${gobj_short_name(gobj)}: table element '#${table_id}' not found in $container`);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
let tabulator = new Tabulator($table_el, tabulator_settings);
|
|
791
829
|
|
|
792
830
|
/* Keep the footer in sync with the visible (active) row count. */
|
|
793
831
|
function update_rowcount() {
|
|
@@ -987,844 +1025,211 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field)
|
|
|
987
1025
|
return value;
|
|
988
1026
|
}
|
|
989
1027
|
|
|
990
|
-
|
|
991
|
-
*
|
|
992
|
-
|
|
993
|
-
|
|
1028
|
+
/************************************************************
|
|
1029
|
+
* Build the form template from desc.cols: only user-editable
|
|
1030
|
+
* fields reach the form — writable cols, fkeys (linkable) and
|
|
1031
|
+
* the pkey (the hosted C_YUI_FORM's form_mode drives the
|
|
1032
|
+
* pkey's readonly/required state).
|
|
1033
|
+
************************************************************/
|
|
1034
|
+
function build_form_template(gobj)
|
|
994
1035
|
{
|
|
995
1036
|
let desc = gobj_read_attr(gobj, "desc");
|
|
996
|
-
let
|
|
997
|
-
let
|
|
998
|
-
|
|
999
|
-
for (let i = 0; i < desc.cols.length; i++) {
|
|
1000
|
-
let col = desc.cols[i];
|
|
1001
|
-
if(col.id.charAt(0)==='_') {
|
|
1037
|
+
let template = [];
|
|
1038
|
+
for(let col of desc.cols) {
|
|
1039
|
+
if(!col.id || col.id.charAt(0) === '_') {
|
|
1002
1040
|
continue;
|
|
1003
1041
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
if(!tag) {
|
|
1042
|
+
const field_desc = treedb_get_field_desc(col);
|
|
1043
|
+
if(field_desc.is_hidden) {
|
|
1008
1044
|
continue;
|
|
1009
1045
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
let is_writable = str_in_list(flag, "writable");
|
|
1014
|
-
let is_notnull = str_in_list(flag, "notnull");
|
|
1015
|
-
let is_rowid = str_in_list(flag, "rowid");
|
|
1016
|
-
let is_required = str_in_list(flag, "required");
|
|
1017
|
-
let is_hidden = str_in_list(flag, "hidden");
|
|
1018
|
-
let is_persistent = str_in_list(flag, "persistent");
|
|
1019
|
-
let is_hook = str_in_list(flag, "hook");
|
|
1020
|
-
let is_fkey = str_in_list(flag, "fkey");
|
|
1021
|
-
|
|
1022
|
-
if(1) {
|
|
1023
|
-
/*
|
|
1024
|
-
* update mode by default
|
|
1025
|
-
*/
|
|
1026
|
-
if(id === desc.pkey) {
|
|
1027
|
-
is_writable = false;
|
|
1028
|
-
}
|
|
1046
|
+
if(field_desc.type === "hook") {
|
|
1047
|
+
continue; // hooks don't appear in forms
|
|
1029
1048
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
// if(is_persistent) {
|
|
1033
|
-
// is_writable = true;
|
|
1034
|
-
// }
|
|
1035
|
-
|
|
1036
|
-
if(is_fkey) {
|
|
1037
|
-
// TODO no debería estar definido con writable?
|
|
1038
|
-
// TODO pueden haber enlaces que solo se puedan hacer por programa
|
|
1039
|
-
is_writable = true;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
if(!is_writable) {
|
|
1043
|
-
if(id !== desc.pkey) {
|
|
1044
|
-
// Show only fields writable (except pkey)
|
|
1045
|
-
continue;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if(is_hidden) {
|
|
1049
|
+
if(!field_desc.is_writable &&
|
|
1050
|
+
field_desc.type !== "fkey" && col.id !== desc.pkey) {
|
|
1049
1051
|
continue;
|
|
1050
1052
|
}
|
|
1051
|
-
|
|
1052
|
-
if(is_rowid) {
|
|
1053
|
-
is_writable = false;
|
|
1054
|
-
is_required = false;
|
|
1055
|
-
is_notnull = false;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
let field_conf = {
|
|
1059
|
-
tag: tag,
|
|
1060
|
-
inputType: form_input_type,
|
|
1061
|
-
name: col.id,
|
|
1062
|
-
label: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
|
|
1063
|
-
placeholder: "",
|
|
1064
|
-
options: options, // select,select2,radio options
|
|
1065
|
-
extras: extras, // extra html input/textarea attributes
|
|
1066
|
-
readonly: !is_writable,
|
|
1067
|
-
required: is_required,
|
|
1068
|
-
hidden: is_hidden,
|
|
1069
|
-
not_null: is_notnull,
|
|
1070
|
-
is_pkey: (id === desc.pkey),
|
|
1071
|
-
is_rowid: is_rowid
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
let $field = create_html_field(field_conf);
|
|
1075
|
-
$field.field_conf = field_conf;
|
|
1076
|
-
|
|
1077
|
-
$form.appendChild($field);
|
|
1053
|
+
template.push(col);
|
|
1078
1054
|
}
|
|
1079
|
-
return
|
|
1055
|
+
return template;
|
|
1080
1056
|
}
|
|
1081
1057
|
|
|
1082
1058
|
/************************************************************
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1059
|
+
* Collect the linkable parent rows for every fkey col:
|
|
1060
|
+
* {topic_name: rows}, asked to the topics manager (parent)
|
|
1061
|
+
* with the same command the old embedded form used. Collected
|
|
1062
|
+
* fresh on every dialog open, so new parent rows are always
|
|
1063
|
+
* offered.
|
|
1088
1064
|
************************************************************/
|
|
1089
|
-
function
|
|
1090
|
-
{
|
|
1091
|
-
const field_desc = treedb_get_field_desc(col);
|
|
1092
|
-
|
|
1093
|
-
let tag = null;
|
|
1094
|
-
let inputType = '';
|
|
1095
|
-
let options = [];
|
|
1096
|
-
let extras = {}; // extra html input/textarea attributes
|
|
1097
|
-
|
|
1098
|
-
switch(field_desc.type) {
|
|
1099
|
-
case "image":
|
|
1100
|
-
case "string":
|
|
1101
|
-
tag = 'input';
|
|
1102
|
-
inputType = 'text';
|
|
1103
|
-
break;
|
|
1104
|
-
case "integer":
|
|
1105
|
-
tag = 'input';
|
|
1106
|
-
extras.inputmode = "numeric";
|
|
1107
|
-
break;
|
|
1108
|
-
case "real":
|
|
1109
|
-
tag = 'input';
|
|
1110
|
-
extras.inputmode = "decimal";
|
|
1111
|
-
break;
|
|
1112
|
-
|
|
1113
|
-
case "rowid":
|
|
1114
|
-
tag = 'input';
|
|
1115
|
-
inputType = 'text';
|
|
1116
|
-
break;
|
|
1117
|
-
|
|
1118
|
-
case "boolean":
|
|
1119
|
-
tag = 'checkbox';
|
|
1120
|
-
break;
|
|
1121
|
-
|
|
1122
|
-
case "now":
|
|
1123
|
-
case "time":
|
|
1124
|
-
tag = 'input';
|
|
1125
|
-
inputType = 'datetime-local';
|
|
1126
|
-
switch(field_desc.real_type) {
|
|
1127
|
-
case "string":
|
|
1128
|
-
break;
|
|
1129
|
-
case "integer":
|
|
1130
|
-
break;
|
|
1131
|
-
}
|
|
1132
|
-
break;
|
|
1133
|
-
case "color":
|
|
1134
|
-
tag = 'input';
|
|
1135
|
-
inputType = 'color';
|
|
1136
|
-
switch(field_desc.real_type) {
|
|
1137
|
-
case "string":
|
|
1138
|
-
break;
|
|
1139
|
-
case "integer":
|
|
1140
|
-
break;
|
|
1141
|
-
}
|
|
1142
|
-
break;
|
|
1143
|
-
case "email":
|
|
1144
|
-
tag = 'input';
|
|
1145
|
-
inputType = 'email';
|
|
1146
|
-
extras.inputmode = "email";
|
|
1147
|
-
break;
|
|
1148
|
-
case "password":
|
|
1149
|
-
tag = 'input';
|
|
1150
|
-
inputType = 'password';
|
|
1151
|
-
break;
|
|
1152
|
-
case "url":
|
|
1153
|
-
tag = 'input';
|
|
1154
|
-
inputType = 'url';
|
|
1155
|
-
extras.inputmode = "url";
|
|
1156
|
-
break;
|
|
1157
|
-
case "tel":
|
|
1158
|
-
tag = 'input';
|
|
1159
|
-
inputType = 'tel';
|
|
1160
|
-
extras.inputmode = "tel";
|
|
1161
|
-
break;
|
|
1162
|
-
|
|
1163
|
-
case "object":
|
|
1164
|
-
case "dict":
|
|
1165
|
-
case "template":
|
|
1166
|
-
case "array":
|
|
1167
|
-
case "list":
|
|
1168
|
-
case "coordinates":
|
|
1169
|
-
case "blob":
|
|
1170
|
-
tag = 'jsoneditor';
|
|
1171
|
-
break;
|
|
1172
|
-
|
|
1173
|
-
case "enum":
|
|
1174
|
-
tag = 'select2';
|
|
1175
|
-
switch(field_desc.real_type) {
|
|
1176
|
-
case "string": // TODO review types
|
|
1177
|
-
case "object":
|
|
1178
|
-
case "dict":
|
|
1179
|
-
case "array":
|
|
1180
|
-
case "list":
|
|
1181
|
-
options = list2options(field_desc.enum_list, "id", "id");
|
|
1182
|
-
break;
|
|
1183
|
-
}
|
|
1184
|
-
break;
|
|
1185
|
-
|
|
1186
|
-
case "hook":
|
|
1187
|
-
// hooks don't appear in form
|
|
1188
|
-
break;
|
|
1189
|
-
|
|
1190
|
-
case "fkey":
|
|
1191
|
-
tag = 'select2';
|
|
1192
|
-
switch(field_desc.real_type) {
|
|
1193
|
-
case "string": // TODO review types
|
|
1194
|
-
case "object":
|
|
1195
|
-
case "dict":
|
|
1196
|
-
case "array":
|
|
1197
|
-
case "list":
|
|
1198
|
-
options = get_fkey_options(gobj, col);
|
|
1199
|
-
break;
|
|
1200
|
-
}
|
|
1201
|
-
break;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
return [tag, inputType, options, extras];
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
/******************************************************************
|
|
1208
|
-
* Helper function to create different form elements
|
|
1209
|
-
* Available types:
|
|
1210
|
-
* - input
|
|
1211
|
-
* - textarea
|
|
1212
|
-
* - select
|
|
1213
|
-
* - select2
|
|
1214
|
-
* - checkbox
|
|
1215
|
-
* - radio
|
|
1216
|
-
* - jsoneditor
|
|
1217
|
-
*
|
|
1218
|
-
* {
|
|
1219
|
-
* tag: field tag,
|
|
1220
|
-
* inputType: input type,
|
|
1221
|
-
* name: id
|
|
1222
|
-
* label: ""
|
|
1223
|
-
* placeholder: "",
|
|
1224
|
-
* options: [{id:"", value:""}]]
|
|
1225
|
-
* extras: {}
|
|
1226
|
-
* readonly: bool
|
|
1227
|
-
* required: bool
|
|
1228
|
-
* hidden: bool
|
|
1229
|
-
* not_null: bool
|
|
1230
|
-
* _col: col
|
|
1231
|
-
* }
|
|
1232
|
-
******************************************************************/
|
|
1233
|
-
function create_html_field(
|
|
1234
|
-
{
|
|
1235
|
-
tag,
|
|
1236
|
-
inputType,
|
|
1237
|
-
name,
|
|
1238
|
-
label,
|
|
1239
|
-
placeholder,
|
|
1240
|
-
options,
|
|
1241
|
-
extras, // extra html input/textarea attributes
|
|
1242
|
-
readonly,
|
|
1243
|
-
required,
|
|
1244
|
-
hidden,
|
|
1245
|
-
not_null
|
|
1246
|
-
}
|
|
1247
|
-
)
|
|
1065
|
+
function build_fkey_options(gobj)
|
|
1248
1066
|
{
|
|
1249
|
-
let
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
case 'checkbox':
|
|
1256
|
-
case 'radio':
|
|
1257
|
-
case 'jsoneditor':
|
|
1258
|
-
base = [
|
|
1259
|
-
'div', {class: 'xfield field is-horizontal'}, [
|
|
1260
|
-
['div', {class: 'field-label is-normal' }, [
|
|
1261
|
-
['label', {class:'label ', i18n:label, for:name}, label]
|
|
1262
|
-
]],
|
|
1263
|
-
|
|
1264
|
-
['div', {class: 'field-body'}, [
|
|
1265
|
-
['div', {class: 'field'}, [
|
|
1266
|
-
['div', {class: 'control' }, []]
|
|
1267
|
-
]]
|
|
1268
|
-
]]
|
|
1269
|
-
]
|
|
1270
|
-
];
|
|
1271
|
-
break;
|
|
1272
|
-
|
|
1273
|
-
default:
|
|
1274
|
-
base = ['div', {class: 'control'}, 'Unsupported element type'];
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
let $element = createElement2(base);
|
|
1278
|
-
let $control = $element.querySelector('.control');
|
|
1279
|
-
|
|
1280
|
-
switch (tag) {
|
|
1281
|
-
case 'input':
|
|
1282
|
-
{
|
|
1283
|
-
let attrs = {
|
|
1284
|
-
class: `input ${name==='id'?'with-focus':''}`,
|
|
1285
|
-
name: name,
|
|
1286
|
-
type: inputType,
|
|
1287
|
-
placeholder: placeholder
|
|
1288
|
-
};
|
|
1289
|
-
Object.assign(attrs, extras);
|
|
1290
|
-
|
|
1291
|
-
if(readonly) {
|
|
1292
|
-
attrs.readonly = '';
|
|
1293
|
-
}
|
|
1294
|
-
let extend = ['input', attrs, '', {
|
|
1295
|
-
'blur': function (evt) {
|
|
1296
|
-
validate_field_on_blur(this);
|
|
1297
|
-
}
|
|
1298
|
-
}];
|
|
1299
|
-
|
|
1300
|
-
let $extend = createElement2(extend);
|
|
1301
|
-
$control.appendChild($extend);
|
|
1302
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1303
|
-
break;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
case 'jsoneditor':
|
|
1307
|
-
{
|
|
1308
|
-
let attrs = {
|
|
1309
|
-
class: 'jsoneditor jse-theme-dark',
|
|
1310
|
-
style: ''
|
|
1311
|
-
};
|
|
1312
|
-
Object.assign(attrs, extras);
|
|
1313
|
-
|
|
1314
|
-
if(readonly) {
|
|
1315
|
-
attrs.readonly = '';
|
|
1316
|
-
}
|
|
1317
|
-
let extend = ['div', attrs];
|
|
1318
|
-
let $extend = createElement2(extend);
|
|
1319
|
-
$extend.setAttribute('name', name);
|
|
1320
|
-
$control.appendChild($extend);
|
|
1321
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1322
|
-
break;
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
case 'textarea':
|
|
1326
|
-
{
|
|
1327
|
-
let attrs = {class:'textarea', name:name, placeholder: placeholder};
|
|
1328
|
-
Object.assign(attrs, extras);
|
|
1329
|
-
|
|
1330
|
-
if(readonly) {
|
|
1331
|
-
attrs.readonly = '';
|
|
1332
|
-
}
|
|
1333
|
-
let extend = ['textarea', attrs, '', {
|
|
1334
|
-
'blur': function (evt) {
|
|
1335
|
-
validate_field_on_blur(this);
|
|
1336
|
-
}
|
|
1337
|
-
}];
|
|
1338
|
-
let $extend = createElement2(extend);
|
|
1339
|
-
$control.appendChild($extend);
|
|
1340
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1341
|
-
break;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
case 'select':
|
|
1345
|
-
{
|
|
1346
|
-
// {id:"id", value:"name"}
|
|
1347
|
-
// <option value="id" data-i18n:"name"> name </option>
|
|
1348
|
-
let extend = ['div', {class: 'select' },
|
|
1349
|
-
['select',
|
|
1350
|
-
{name:name},
|
|
1351
|
-
options.map(option =>
|
|
1352
|
-
['option', {value:option.id, i18n:option.value}, option.value]
|
|
1353
|
-
)
|
|
1354
|
-
]
|
|
1355
|
-
];
|
|
1356
|
-
let $extend = createElement2(extend);
|
|
1357
|
-
$control.appendChild($extend);
|
|
1358
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1359
|
-
break;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
case 'select2':
|
|
1363
|
-
{
|
|
1364
|
-
// {id:"id", value:"name"}
|
|
1365
|
-
// <option value="id" data-i18n:"name"> name </option>
|
|
1366
|
-
let extend = ['select',
|
|
1367
|
-
{
|
|
1368
|
-
name:name,
|
|
1369
|
-
class: 'select2-multiple',
|
|
1370
|
-
multiple:'multiple',
|
|
1371
|
-
style: 'width: 100% !important;'
|
|
1372
|
-
},
|
|
1373
|
-
options.map(option =>
|
|
1374
|
-
['option', {value:option.id, i18n:option.value}, option.value]
|
|
1375
|
-
)
|
|
1376
|
-
];
|
|
1377
|
-
let $extend = createElement2(extend);
|
|
1378
|
-
$control.appendChild($extend);
|
|
1379
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1380
|
-
break;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
case 'checkbox':
|
|
1384
|
-
{
|
|
1385
|
-
let extend = ['label', {class: 'checkbox'},
|
|
1386
|
-
['input', {type: 'checkbox', name:name}]
|
|
1387
|
-
];
|
|
1388
|
-
let $extend = createElement2(extend);
|
|
1389
|
-
$control.appendChild($extend);
|
|
1390
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1391
|
-
break;
|
|
1067
|
+
let desc = gobj_read_attr(gobj, "desc");
|
|
1068
|
+
let fkey_options = {};
|
|
1069
|
+
for(let col of desc.cols) {
|
|
1070
|
+
const field_desc = treedb_get_field_desc(col);
|
|
1071
|
+
if(field_desc.type !== "fkey" || !is_object(col.fkey)) {
|
|
1072
|
+
continue;
|
|
1392
1073
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
{
|
|
1396
|
-
|
|
1397
|
-
// WARNING here can't use data-i18n, the radio element is destroyed.
|
|
1398
|
-
let elm = `
|
|
1399
|
-
<label class="radio">
|
|
1400
|
-
<input type="radio" name="${name}">
|
|
1401
|
-
${t(option)}
|
|
1402
|
-
</label>
|
|
1403
|
-
`;
|
|
1404
|
-
|
|
1405
|
-
let $extend = createOneHtml(elm);
|
|
1406
|
-
$control.appendChild($extend);
|
|
1407
|
-
}
|
|
1408
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1409
|
-
break;
|
|
1074
|
+
// HACK we work only with one fkey (same as the whole stack)
|
|
1075
|
+
let topic_name = Object.keys(col.fkey)[0];
|
|
1076
|
+
if(!topic_name || fkey_options[topic_name] !== undefined) {
|
|
1077
|
+
continue;
|
|
1410
1078
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1079
|
+
let webix = gobj_command(
|
|
1080
|
+
gobj_parent(gobj),
|
|
1081
|
+
"get_topic_data",
|
|
1082
|
+
{topic_name: topic_name},
|
|
1083
|
+
gobj
|
|
1084
|
+
);
|
|
1085
|
+
fkey_options[topic_name] = (webix && is_array(webix.data))? webix.data : [];
|
|
1414
1086
|
}
|
|
1415
|
-
|
|
1416
|
-
return $element;
|
|
1087
|
+
return fkey_options;
|
|
1417
1088
|
}
|
|
1418
1089
|
|
|
1419
|
-
|
|
1420
|
-
*
|
|
1421
|
-
|
|
1422
|
-
|
|
1090
|
+
/************************************************************
|
|
1091
|
+
* Open the edit/create dialog hosting a C_YUI_FORM child.
|
|
1092
|
+
* The child is created fresh on every open (schema and fkey
|
|
1093
|
+
* options are read at its build time) and destroyed on close.
|
|
1094
|
+
* Its EV_SAVE_RECORD (CHILD model) arrives already in treedb
|
|
1095
|
+
* shape — see ac_form_save_record().
|
|
1096
|
+
************************************************************/
|
|
1097
|
+
function open_form_dialog(gobj, mode, record)
|
|
1423
1098
|
{
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
let
|
|
1099
|
+
close_form_dialog(gobj); // only one dialog at a time
|
|
1100
|
+
|
|
1101
|
+
let priv = gobj.priv;
|
|
1102
|
+
let desc = gobj_read_attr(gobj, "desc");
|
|
1427
1103
|
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
1104
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
1428
1105
|
|
|
1429
1106
|
let $element = createElement2([
|
|
1430
|
-
'div', {id: popup_id, class: 'modal'
|
|
1431
|
-
['div', {
|
|
1432
|
-
['div', {
|
|
1433
|
-
['header', {
|
|
1434
|
-
['p', {
|
|
1435
|
-
['button', {class:
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1107
|
+
'div', {id: popup_id, class: 'modal is-active'}, [
|
|
1108
|
+
['div', {class: 'modal-background'}, ''],
|
|
1109
|
+
['div', {class: 'modal-card modal-is-responsive'}, [
|
|
1110
|
+
['header', {class: 'modal-card-head p-3'}, [
|
|
1111
|
+
['p', {class: 'modal-card-title', style: 'text-align:center;'}, t(topic_name)],
|
|
1112
|
+
['button', {class: 'delete is-large', 'aria-label': 'close'}, '', {
|
|
1113
|
+
click: function(evt) {
|
|
1114
|
+
evt.stopPropagation();
|
|
1115
|
+
request_close_form_dialog(gobj);
|
|
1116
|
+
}
|
|
1117
|
+
}]
|
|
1439
1118
|
]],
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
], {
|
|
1448
|
-
click: function(evt) {
|
|
1449
|
-
evt.stopPropagation();
|
|
1450
|
-
evt.preventDefault();
|
|
1451
|
-
let $form = $element.querySelector('form');
|
|
1452
|
-
if (validate_form(gobj, $form)) {
|
|
1453
|
-
let values = get_form_values($form);
|
|
1454
|
-
let ret;
|
|
1455
|
-
if($form.__mode__==="create") {
|
|
1456
|
-
ret = gobj_send_event(gobj, "EV_CREATE_RECORD", values, gobj);
|
|
1457
|
-
} else {
|
|
1458
|
-
ret = gobj_send_event(gobj, "EV_UPDATE_RECORD", values, gobj);
|
|
1459
|
-
}
|
|
1460
|
-
if(ret === 0) {
|
|
1461
|
-
$element.classList.remove('is-active');
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
}],
|
|
1466
|
-
|
|
1467
|
-
// TODO disable/enable buttons save and undo accord to form state
|
|
1468
|
-
|
|
1469
|
-
['button', { class: 'button px-4 form-close'}, [
|
|
1470
|
-
['span', {class:'icon'}, [
|
|
1471
|
-
['i', {class:'yi-xmark'}]
|
|
1472
|
-
]],
|
|
1473
|
-
['span', {i18n:'cancel'}, 'cancel']
|
|
1474
|
-
]],
|
|
1475
|
-
|
|
1476
|
-
['button', { class: 'button px-4'}, [
|
|
1477
|
-
['span', {class:'icon'}, [
|
|
1478
|
-
['i', {class:'yi-broom'}]
|
|
1479
|
-
]],
|
|
1480
|
-
['span', {i18n:'clear'}, 'clear']
|
|
1481
|
-
], {
|
|
1482
|
-
click: function(evt) {
|
|
1483
|
-
evt.stopPropagation();
|
|
1484
|
-
clear_validation(gobj, $form);
|
|
1485
|
-
clear_data(gobj, $form);
|
|
1486
|
-
}
|
|
1487
|
-
}]
|
|
1488
|
-
]]
|
|
1489
|
-
]]
|
|
1119
|
+
/* flex column so the hosted form takes the dialog
|
|
1120
|
+
* height: fields scroll internally and the form's
|
|
1121
|
+
* bottom toolbar stays visible (dialog footer) */
|
|
1122
|
+
['section', {
|
|
1123
|
+
class: 'modal-card-body p-2',
|
|
1124
|
+
style: 'display:flex; flex-direction:column;'
|
|
1125
|
+
}, []]
|
|
1490
1126
|
]]
|
|
1491
1127
|
], {
|
|
1492
|
-
click: function
|
|
1128
|
+
click: function(evt) {
|
|
1493
1129
|
// Capture bubble events
|
|
1494
1130
|
evt.stopPropagation();
|
|
1495
1131
|
}
|
|
1496
1132
|
}
|
|
1497
1133
|
]);
|
|
1498
1134
|
|
|
1499
|
-
|
|
1500
|
-
* Add events
|
|
1501
|
-
*------------------------*/
|
|
1502
|
-
/*
|
|
1503
|
-
* Button close
|
|
1504
|
-
*/
|
|
1505
|
-
$element.querySelectorAll('.modal-close, .form-close, .delete').forEach(function (element) {
|
|
1506
|
-
element.addEventListener('click', function(event) {
|
|
1507
|
-
event.stopPropagation();
|
|
1508
|
-
// TODO ver si hay algo modificado y preguntar si se desecha
|
|
1509
|
-
$element.classList.remove('is-active');
|
|
1510
|
-
});
|
|
1511
|
-
});
|
|
1512
|
-
|
|
1513
|
-
refresh_language($element, t);
|
|
1514
|
-
|
|
1515
|
-
/*-----------------------------------------*
|
|
1516
|
-
* Add to popup layer !!!
|
|
1517
|
-
*-----------------------------------------*/
|
|
1518
|
-
popup_mount_layer().appendChild($element);
|
|
1519
|
-
|
|
1520
|
-
// jQuery($element).off('click').on('click', function(evt) {
|
|
1521
|
-
// evt.stopPropagation();
|
|
1522
|
-
// evt.preventDefault();
|
|
1523
|
-
// });
|
|
1524
|
-
|
|
1525
|
-
/*------------------------*
|
|
1526
|
-
* Jsoneditor
|
|
1527
|
-
*------------------------*/
|
|
1528
|
-
$element.querySelectorAll('.jsoneditor').forEach(function (element) {
|
|
1529
|
-
let font_family = "DejaVu Sans Mono, monospace";
|
|
1530
|
-
let sz = 16;
|
|
1531
|
-
element.style.setProperty('--jse-font-size-mono', sz + 'px');
|
|
1532
|
-
element.style.setProperty('--jse-font-family-mono', font_family);
|
|
1533
|
-
|
|
1534
|
-
element.jsoneditor = new JSONEditor({
|
|
1535
|
-
target: element,
|
|
1536
|
-
props: {
|
|
1537
|
-
timestampTag: function({field, value, path}) {
|
|
1538
|
-
return field === '__t__' || field === '__tm__' || field === 'tm' ||
|
|
1539
|
-
field === 'from_t' || field === 'to_t' || field === 't' ||
|
|
1540
|
-
field === 't_input' || field === 't_output' ||
|
|
1541
|
-
field === 'from_tm' || field === 'to_tm' || field === 'time';
|
|
1542
|
-
},
|
|
1543
|
-
}
|
|
1544
|
-
});
|
|
1545
|
-
});
|
|
1546
|
-
|
|
1547
|
-
/*---------------------------------------------------------*
|
|
1548
|
-
* Extensions of jquery must be done after DOM attaching
|
|
1549
|
-
*---------------------------------------------------------*/
|
|
1550
|
-
// let $$x = jQuery($element).find('.select2-multiple');
|
|
1551
|
-
// if($$x && $$x.select2) {
|
|
1552
|
-
// $$x.select2();
|
|
1553
|
-
// }
|
|
1135
|
+
let $body = $element.querySelector('.modal-card-body');
|
|
1554
1136
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
************************************************************/
|
|
1573
|
-
function show_field_error($field, message)
|
|
1574
|
-
{
|
|
1575
|
-
$field.querySelectorAll('input, textarea, select').forEach(el => {
|
|
1576
|
-
el.classList.add('is-danger');
|
|
1577
|
-
});
|
|
1578
|
-
let $help = $field.querySelector('.help');
|
|
1579
|
-
if($help) {
|
|
1580
|
-
$help.textContent = message;
|
|
1581
|
-
$help.style.display = 'block';
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
/************************************************************
|
|
1586
|
-
* Clear a validation error from a field
|
|
1587
|
-
************************************************************/
|
|
1588
|
-
function clear_field_error($field)
|
|
1589
|
-
{
|
|
1590
|
-
$field.querySelectorAll('input, textarea, select').forEach(el => {
|
|
1591
|
-
el.classList.remove('is-danger');
|
|
1592
|
-
});
|
|
1593
|
-
let $help = $field.querySelector('.help');
|
|
1594
|
-
if($help) {
|
|
1595
|
-
$help.textContent = '';
|
|
1596
|
-
$help.style.display = 'none';
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
/************************************************************
|
|
1601
|
-
* Get the current value of a field for validation
|
|
1602
|
-
************************************************************/
|
|
1603
|
-
function get_field_value_for_validation($field, conf)
|
|
1604
|
-
{
|
|
1605
|
-
switch(conf.tag) {
|
|
1606
|
-
case 'input': {
|
|
1607
|
-
let $input = $field.querySelector('input');
|
|
1608
|
-
if(!$input) {
|
|
1609
|
-
return '';
|
|
1610
|
-
}
|
|
1611
|
-
if($input.type === 'checkbox') {
|
|
1612
|
-
return $input.checked;
|
|
1613
|
-
}
|
|
1614
|
-
return $input.value;
|
|
1615
|
-
}
|
|
1616
|
-
case 'checkbox': {
|
|
1617
|
-
let $input = $field.querySelector('input[type="checkbox"]');
|
|
1618
|
-
if(!$input) {
|
|
1619
|
-
return false;
|
|
1620
|
-
}
|
|
1621
|
-
return $input.checked;
|
|
1622
|
-
}
|
|
1623
|
-
case 'textarea': {
|
|
1624
|
-
let $el = $field.querySelector('textarea');
|
|
1625
|
-
if(!$el) {
|
|
1626
|
-
return '';
|
|
1627
|
-
}
|
|
1628
|
-
return $el.value;
|
|
1629
|
-
}
|
|
1630
|
-
case 'select':
|
|
1631
|
-
case 'select2': {
|
|
1632
|
-
let $el = $field.querySelector('select');
|
|
1633
|
-
if(!$el) {
|
|
1634
|
-
return '';
|
|
1635
|
-
}
|
|
1636
|
-
if($el.tomselect) {
|
|
1637
|
-
return $el.tomselect.getValue();
|
|
1638
|
-
}
|
|
1639
|
-
return $el.value;
|
|
1640
|
-
}
|
|
1641
|
-
case 'jsoneditor': {
|
|
1642
|
-
let $el = $field.querySelector('.jsoneditor');
|
|
1643
|
-
if(!$el || !$el.jsoneditor) {
|
|
1644
|
-
return '';
|
|
1645
|
-
}
|
|
1646
|
-
let val = $el.jsoneditor.get();
|
|
1647
|
-
if(val.text !== undefined) {
|
|
1648
|
-
return val.text;
|
|
1649
|
-
}
|
|
1650
|
-
return JSON.stringify(val.json);
|
|
1651
|
-
}
|
|
1652
|
-
case 'radio': {
|
|
1653
|
-
let $checked = $field.querySelector('input[type="radio"]:checked');
|
|
1654
|
-
if(!$checked) {
|
|
1655
|
-
return '';
|
|
1656
|
-
}
|
|
1657
|
-
return $checked.value;
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
return '';
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
/************************************************************
|
|
1664
|
-
* Check if a field value is empty
|
|
1665
|
-
************************************************************/
|
|
1666
|
-
function is_field_value_empty(value)
|
|
1667
|
-
{
|
|
1668
|
-
if(value === null || value === undefined || value === '') {
|
|
1669
|
-
return true;
|
|
1670
|
-
}
|
|
1671
|
-
if(Array.isArray(value) && value.length === 0) {
|
|
1672
|
-
return true;
|
|
1673
|
-
}
|
|
1674
|
-
return false;
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
/************************************************************
|
|
1678
|
-
* Determine if a field requires validation in the current mode
|
|
1679
|
-
************************************************************/
|
|
1680
|
-
function is_field_required(conf, mode)
|
|
1681
|
-
{
|
|
1682
|
-
if(conf.is_pkey) {
|
|
1683
|
-
// pkey is required only in create mode (unless it's a rowid)
|
|
1684
|
-
return (mode === "create" && !conf.is_rowid);
|
|
1685
|
-
}
|
|
1686
|
-
if(conf.readonly) {
|
|
1687
|
-
return false;
|
|
1688
|
-
}
|
|
1689
|
-
return (conf.required || conf.not_null);
|
|
1690
|
-
}
|
|
1691
|
-
|
|
1692
|
-
/************************************************************
|
|
1693
|
-
* Validate a single field on blur
|
|
1694
|
-
* Called from input/textarea blur event handlers
|
|
1695
|
-
************************************************************/
|
|
1696
|
-
function validate_field_on_blur(element)
|
|
1697
|
-
{
|
|
1698
|
-
let $field = element.closest('.xfield');
|
|
1699
|
-
if(!$field || !$field.field_conf) {
|
|
1700
|
-
return;
|
|
1701
|
-
}
|
|
1702
|
-
let conf = $field.field_conf;
|
|
1703
|
-
let $form = element.closest('form');
|
|
1704
|
-
let mode = $form ? $form.__mode__ : 'update';
|
|
1137
|
+
let form = gobj_create_pure_child(
|
|
1138
|
+
"form_" + clean_name(gobj_name(gobj)),
|
|
1139
|
+
"C_YUI_FORM",
|
|
1140
|
+
{
|
|
1141
|
+
template: build_form_template(gobj),
|
|
1142
|
+
record: record,
|
|
1143
|
+
fkey_options: build_fkey_options(gobj),
|
|
1144
|
+
form_mode: mode,
|
|
1145
|
+
pkey: desc.pkey || "id",
|
|
1146
|
+
topic_name: topic_name,
|
|
1147
|
+
editable: true,
|
|
1148
|
+
$parent: $body
|
|
1149
|
+
},
|
|
1150
|
+
gobj
|
|
1151
|
+
);
|
|
1152
|
+
priv.form = form;
|
|
1153
|
+
priv.$popup = $element;
|
|
1705
1154
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1155
|
+
let $form_container = gobj_read_attr(form, "$container");
|
|
1156
|
+
if($form_container) {
|
|
1157
|
+
$form_container.style.flex = "1 1 auto";
|
|
1158
|
+
$form_container.style.minHeight = "0";
|
|
1709
1159
|
}
|
|
1710
1160
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1161
|
+
popup_mount_layer().appendChild($element);
|
|
1162
|
+
refresh_language($element, t);
|
|
1163
|
+
|
|
1164
|
+
gobj_start(form); // mt_start loads `record` into the form
|
|
1165
|
+
|
|
1166
|
+
let $with_focus = $element.querySelector('.with-focus');
|
|
1167
|
+
if($with_focus) {
|
|
1168
|
+
$with_focus.focus();
|
|
1716
1169
|
}
|
|
1717
1170
|
}
|
|
1718
1171
|
|
|
1719
1172
|
/************************************************************
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1173
|
+
* Close request from the dialog X: if the form carries
|
|
1174
|
+
* unsaved changes ask first (EV_WINDOW_TO_CLOSE contract).
|
|
1722
1175
|
************************************************************/
|
|
1723
|
-
function
|
|
1176
|
+
function request_close_form_dialog(gobj)
|
|
1724
1177
|
{
|
|
1725
|
-
let
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
if(!is_field_required(conf, mode)) {
|
|
1736
|
-
clear_field_error($field);
|
|
1178
|
+
let priv = gobj.priv;
|
|
1179
|
+
if(priv.form) {
|
|
1180
|
+
let kw = {};
|
|
1181
|
+
gobj_send_event(priv.form, "EV_WINDOW_TO_CLOSE", kw, gobj);
|
|
1182
|
+
if(kw.abort_close) {
|
|
1183
|
+
get_yesnocancel(t('All changes will be lost. Are you sure?'), function(evt) {
|
|
1184
|
+
if(evt === "yes") {
|
|
1185
|
+
close_form_dialog(gobj);
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1737
1188
|
return;
|
|
1738
1189
|
}
|
|
1739
|
-
|
|
1740
|
-
let value = get_field_value_for_validation($field, conf);
|
|
1741
|
-
if(is_field_value_empty(value)) {
|
|
1742
|
-
show_field_error($field, t('this field is required'));
|
|
1743
|
-
is_valid = false;
|
|
1744
|
-
if(!first_invalid) {
|
|
1745
|
-
first_invalid = $field;
|
|
1746
|
-
}
|
|
1747
|
-
} else {
|
|
1748
|
-
clear_field_error($field);
|
|
1749
|
-
}
|
|
1750
|
-
});
|
|
1751
|
-
|
|
1752
|
-
if(first_invalid) {
|
|
1753
|
-
let $focusable = first_invalid.querySelector('input, select, textarea');
|
|
1754
|
-
if($focusable && $focusable.focus) {
|
|
1755
|
-
$focusable.focus();
|
|
1756
|
-
}
|
|
1757
1190
|
}
|
|
1758
|
-
|
|
1759
|
-
return is_valid;
|
|
1191
|
+
close_form_dialog(gobj);
|
|
1760
1192
|
}
|
|
1761
1193
|
|
|
1762
1194
|
/************************************************************
|
|
1763
|
-
*
|
|
1195
|
+
* Destroy the hosted form and remove the dialog DOM.
|
|
1764
1196
|
************************************************************/
|
|
1765
|
-
function
|
|
1197
|
+
function close_form_dialog(gobj)
|
|
1766
1198
|
{
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1199
|
+
let priv = gobj.priv;
|
|
1200
|
+
if(priv.form) {
|
|
1201
|
+
if(gobj_is_running(priv.form)) {
|
|
1202
|
+
gobj_stop(priv.form);
|
|
1203
|
+
}
|
|
1204
|
+
gobj_destroy(priv.form);
|
|
1205
|
+
priv.form = null;
|
|
1206
|
+
}
|
|
1207
|
+
if(priv.$popup) {
|
|
1208
|
+
if(priv.$popup.parentNode) {
|
|
1209
|
+
priv.$popup.parentNode.removeChild(priv.$popup);
|
|
1210
|
+
}
|
|
1211
|
+
priv.$popup = null;
|
|
1212
|
+
}
|
|
1770
1213
|
}
|
|
1771
1214
|
|
|
1772
1215
|
/************************************************************
|
|
1773
|
-
*
|
|
1774
|
-
* set form state "update" or "create"
|
|
1775
|
-
* Only the "id" field is modified,
|
|
1776
|
-
* in "update" is readonly
|
|
1777
|
-
* in "create" is writable
|
|
1778
|
-
* Although by default the pkey (primary key) is "id",
|
|
1779
|
-
* use always desc.pkey value, who knows all cases!
|
|
1216
|
+
* True if the topic's pkey col carries the "rowid" flag.
|
|
1780
1217
|
************************************************************/
|
|
1781
|
-
function
|
|
1218
|
+
function pkey_is_rowid(gobj)
|
|
1782
1219
|
{
|
|
1783
1220
|
let desc = gobj_read_attr(gobj, "desc");
|
|
1784
1221
|
let col_pkey = kwid_find_one_record(
|
|
1785
1222
|
gobj,
|
|
1786
1223
|
desc.cols, // kw
|
|
1787
|
-
null,
|
|
1788
|
-
{
|
|
1224
|
+
null, // ids
|
|
1225
|
+
{ // jn_filter
|
|
1789
1226
|
"id": desc.pkey
|
|
1790
1227
|
}
|
|
1791
1228
|
);
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
let $id = $form.querySelector(`[name="${desc.pkey}"]`);
|
|
1795
|
-
|
|
1796
|
-
if($id) {
|
|
1797
|
-
if(mode === "update") {
|
|
1798
|
-
/*
|
|
1799
|
-
* update mode
|
|
1800
|
-
*/
|
|
1801
|
-
$id.setAttribute('readonly', 'readonly');
|
|
1802
|
-
$id.removeAttribute('required');
|
|
1803
|
-
} else {
|
|
1804
|
-
/*
|
|
1805
|
-
* create mode
|
|
1806
|
-
*/
|
|
1807
|
-
if(is_rowid) {
|
|
1808
|
-
$id.setAttribute('readonly', 'readonly');
|
|
1809
|
-
$id.removeAttribute('required');
|
|
1810
|
-
} else {
|
|
1811
|
-
$id.removeAttribute('readonly');
|
|
1812
|
-
$id.setAttribute('required', '');
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
} else {
|
|
1816
|
-
log_error(sprintf("%s: form without id field ('%s')",
|
|
1817
|
-
gobj_short_name(gobj),
|
|
1818
|
-
desc.pkey
|
|
1819
|
-
));
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
if(is_rowid) {
|
|
1823
|
-
mode = "create";
|
|
1229
|
+
if(!col_pkey) {
|
|
1230
|
+
return false;
|
|
1824
1231
|
}
|
|
1825
|
-
|
|
1826
|
-
$form.__mode__ = mode;
|
|
1827
|
-
$form.__index__ = index;
|
|
1232
|
+
return str_in_list(col_pkey.flag, "rowid");
|
|
1828
1233
|
}
|
|
1829
1234
|
|
|
1830
1235
|
/************************************************************
|
|
@@ -1833,35 +1238,10 @@ function set_form_mode(gobj, $form, mode, index)
|
|
|
1833
1238
|
************************************************************/
|
|
1834
1239
|
function show_edit_form(gobj, row, index)
|
|
1835
1240
|
{
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
if(!$element) {
|
|
1841
|
-
$element = build_topic_modal(gobj);
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
let $form = document.getElementById(form_id);
|
|
1845
|
-
clear_validation(gobj,$form);
|
|
1846
|
-
|
|
1847
|
-
if(json_size(row) === 0 || // WARNING _check_box_state_ widely used
|
|
1848
|
-
json_size(row) === 1 && ('_check_box_state_' in row)) {
|
|
1849
|
-
/*
|
|
1850
|
-
* WARNING it won't pass by here,
|
|
1851
|
-
* "create" is call directly from Top New button
|
|
1852
|
-
*/
|
|
1853
|
-
set_form_mode(gobj, $form, "create", index);
|
|
1854
|
-
} else {
|
|
1855
|
-
set_form_mode(gobj, $form, "update", index);
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
set_form_values(gobj, $form, row);
|
|
1859
|
-
|
|
1860
|
-
$element.classList.add('is-active');
|
|
1861
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
1862
|
-
if($with_focus) {
|
|
1863
|
-
$with_focus.focus();
|
|
1864
|
-
}
|
|
1241
|
+
/* a rowid pkey has no "update": every save appends a new
|
|
1242
|
+
* instance (timeranger semantics) — open in create mode */
|
|
1243
|
+
let mode = pkey_is_rowid(gobj)? "create" : "update";
|
|
1244
|
+
open_form_dialog(gobj, mode, row);
|
|
1865
1245
|
}
|
|
1866
1246
|
|
|
1867
1247
|
/************************************************************
|
|
@@ -1870,24 +1250,7 @@ function show_edit_form(gobj, row, index)
|
|
|
1870
1250
|
************************************************************/
|
|
1871
1251
|
function show_create_form(gobj, row)
|
|
1872
1252
|
{
|
|
1873
|
-
|
|
1874
|
-
let form_id = gobj_read_str_attr(gobj, "form_id");
|
|
1875
|
-
|
|
1876
|
-
let $element = document.getElementById(popup_id);
|
|
1877
|
-
if(!$element) {
|
|
1878
|
-
$element = build_topic_modal(gobj);
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
let $form = document.getElementById(form_id);
|
|
1882
|
-
clear_validation(gobj,$form);
|
|
1883
|
-
set_form_mode(gobj, $form, "create", -1);
|
|
1884
|
-
set_form_values(gobj, $form, row);
|
|
1885
|
-
|
|
1886
|
-
$element.classList.add('is-active');
|
|
1887
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
1888
|
-
if($with_focus) {
|
|
1889
|
-
$with_focus.focus();
|
|
1890
|
-
}
|
|
1253
|
+
open_form_dialog(gobj, "create", row);
|
|
1891
1254
|
}
|
|
1892
1255
|
|
|
1893
1256
|
/************************************************************
|
|
@@ -1905,282 +1268,6 @@ function get_schema_col(gobj, id)
|
|
|
1905
1268
|
return null;
|
|
1906
1269
|
}
|
|
1907
1270
|
|
|
1908
|
-
/************************************************************
|
|
1909
|
-
*
|
|
1910
|
-
************************************************************/
|
|
1911
|
-
function clear_data(gobj, $form)
|
|
1912
|
-
{
|
|
1913
|
-
$form.querySelectorAll('input, select, textarea, .jsoneditor').forEach(input => {
|
|
1914
|
-
if(input.name !== 'id') {
|
|
1915
|
-
if (input.classList.contains('jsoneditor')) {
|
|
1916
|
-
let jsoneditor = input.jsoneditor;
|
|
1917
|
-
if(jsoneditor) {
|
|
1918
|
-
jsoneditor.set({text: ''});
|
|
1919
|
-
}
|
|
1920
|
-
} else if (input.type === 'checkbox') {
|
|
1921
|
-
input.checked = false;
|
|
1922
|
-
} else if (input.type === 'radio') {
|
|
1923
|
-
input.checked = false;
|
|
1924
|
-
|
|
1925
|
-
} else if (input.type === 'datetime-local') { /* time */
|
|
1926
|
-
input.value = null;
|
|
1927
|
-
|
|
1928
|
-
} else if (input.multiple) {
|
|
1929
|
-
if(input.tomselect) {
|
|
1930
|
-
input.tomselect.clear();
|
|
1931
|
-
}
|
|
1932
|
-
} else {
|
|
1933
|
-
input.value = null;
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
/************************************************************
|
|
1940
|
-
*
|
|
1941
|
-
************************************************************/
|
|
1942
|
-
function get_form_values($form)
|
|
1943
|
-
{
|
|
1944
|
-
const values = {};
|
|
1945
|
-
// Process all form elements
|
|
1946
|
-
$form.querySelectorAll('input, select, textarea, .jsoneditor').forEach(input => {
|
|
1947
|
-
if (input.classList.contains('jsoneditor')) {
|
|
1948
|
-
let jsoneditor = input.jsoneditor;
|
|
1949
|
-
if(jsoneditor) {
|
|
1950
|
-
let name = input.getAttribute('name');
|
|
1951
|
-
let value = jsoneditor.get();
|
|
1952
|
-
if(value.text===undefined) {
|
|
1953
|
-
values[name] = JSON.stringify(value.json, null, null);
|
|
1954
|
-
} else {
|
|
1955
|
-
values[name] = value.text;
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
} else if (input.type === 'checkbox') {
|
|
1959
|
-
// Handle single checkbox with true/false values
|
|
1960
|
-
values[input.name] = input.checked;
|
|
1961
|
-
} else if (input.type === 'radio') {
|
|
1962
|
-
// Handle radio buttons
|
|
1963
|
-
if (input.checked) {
|
|
1964
|
-
values[input.name] = input.value;
|
|
1965
|
-
}
|
|
1966
|
-
|
|
1967
|
-
} else if (input.type === 'datetime-local') { /* time */
|
|
1968
|
-
// Create a new Date object from the datetime-local input value
|
|
1969
|
-
const date = new Date(input.value);
|
|
1970
|
-
|
|
1971
|
-
// Convert the Date object to epoch time (in seconds)
|
|
1972
|
-
const epochTime = Math.floor(date.getTime() / 1000);
|
|
1973
|
-
values[input.name] = epochTime;
|
|
1974
|
-
|
|
1975
|
-
} else if (input.multiple) {
|
|
1976
|
-
// Handle multi-select with Select2
|
|
1977
|
-
// values[input.name] = $(input).val();
|
|
1978
|
-
|
|
1979
|
-
// Handle multi-select with Tom-Select
|
|
1980
|
-
if (input.tomselect) {
|
|
1981
|
-
values[input.name] = input.tomselect.getValue(); // Returns an array of selected values
|
|
1982
|
-
}
|
|
1983
|
-
} else {
|
|
1984
|
-
// Handle other input types (text, email, number, select, textarea)
|
|
1985
|
-
values[input.name] = input.value;
|
|
1986
|
-
}
|
|
1987
|
-
});
|
|
1988
|
-
|
|
1989
|
-
return values;
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
/************************************************************
|
|
1993
|
-
* Convert a record column value from backend to frontend
|
|
1994
|
-
************************************************************/
|
|
1995
|
-
function epochToDateTimeLocal(epochTime)
|
|
1996
|
-
{
|
|
1997
|
-
const date = new Date(epochTime * 1000); // Convert to milliseconds
|
|
1998
|
-
const year = date.getFullYear();
|
|
1999
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
2000
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
2001
|
-
const hours = String(date.getHours()).padStart(2, '0');
|
|
2002
|
-
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
2003
|
-
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
2004
|
-
|
|
2005
|
-
// TODO for 'datetime-local' input, there is no seconds! we must use another library
|
|
2006
|
-
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
/************************************************************
|
|
2010
|
-
*
|
|
2011
|
-
************************************************************/
|
|
2012
|
-
function set_form_values(gobj, form, values)
|
|
2013
|
-
{
|
|
2014
|
-
Object.keys(values).forEach(key => {
|
|
2015
|
-
let element = form.querySelector(`[name="${key}"]`);
|
|
2016
|
-
if (element) {
|
|
2017
|
-
let value = values[key];
|
|
2018
|
-
let col = get_schema_col(gobj, key);
|
|
2019
|
-
if(col) {
|
|
2020
|
-
value = transform__treedb_value_2_form_value(gobj, col, value);
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
switch (element.type) {
|
|
2024
|
-
case 'select':
|
|
2025
|
-
element.value = value;
|
|
2026
|
-
break;
|
|
2027
|
-
|
|
2028
|
-
case 'select-one':
|
|
2029
|
-
element.value = value;
|
|
2030
|
-
break;
|
|
2031
|
-
|
|
2032
|
-
case 'select-multiple':
|
|
2033
|
-
// Handle multi-select with Select2
|
|
2034
|
-
//$(element).val(value).trigger('change');
|
|
2035
|
-
|
|
2036
|
-
// Handle multi-select with Tom-Select
|
|
2037
|
-
if (element.tomselect) {
|
|
2038
|
-
element.tomselect.setValue(value);
|
|
2039
|
-
}
|
|
2040
|
-
break;
|
|
2041
|
-
|
|
2042
|
-
case 'radio':
|
|
2043
|
-
// TODO review
|
|
2044
|
-
let radio = form.querySelector(`input[name="${key}"][value="${values[key]}"]`);
|
|
2045
|
-
if (radio) {
|
|
2046
|
-
radio.checked = true;
|
|
2047
|
-
}
|
|
2048
|
-
break;
|
|
2049
|
-
|
|
2050
|
-
case 'checkbox':
|
|
2051
|
-
element.checked = value; // Assume the value is true or false
|
|
2052
|
-
break;
|
|
2053
|
-
|
|
2054
|
-
case 'text':
|
|
2055
|
-
case 'textarea':
|
|
2056
|
-
case 'email':
|
|
2057
|
-
element.value = value;
|
|
2058
|
-
break;
|
|
2059
|
-
|
|
2060
|
-
case 'datetime-local': /* time */
|
|
2061
|
-
element.value = epochToDateTimeLocal(value);
|
|
2062
|
-
break;
|
|
2063
|
-
|
|
2064
|
-
default:
|
|
2065
|
-
if(element.classList.contains('jsoneditor')) {
|
|
2066
|
-
let jsoneditor = element.jsoneditor;
|
|
2067
|
-
if(jsoneditor) {
|
|
2068
|
-
jsoneditor.set(value);
|
|
2069
|
-
}
|
|
2070
|
-
break;
|
|
2071
|
-
}
|
|
2072
|
-
|
|
2073
|
-
element.value = value;
|
|
2074
|
-
log_error(
|
|
2075
|
-
sprintf("%s: form type unknown: '%s'",
|
|
2076
|
-
gobj_short_name(gobj),
|
|
2077
|
-
element.type
|
|
2078
|
-
)
|
|
2079
|
-
);
|
|
2080
|
-
break;
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2083
|
-
});
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
/************************************************************
|
|
2087
|
-
* Convert a record column value from backend
|
|
2088
|
-
* to frontend form field
|
|
2089
|
-
************************************************************/
|
|
2090
|
-
function transform__treedb_value_2_form_value(gobj, col, value)
|
|
2091
|
-
{
|
|
2092
|
-
const field_desc = treedb_get_field_desc(col);
|
|
2093
|
-
|
|
2094
|
-
switch(field_desc.type) {
|
|
2095
|
-
case "string":
|
|
2096
|
-
case "email": // string subtype
|
|
2097
|
-
case "tel": // string subtype
|
|
2098
|
-
case "url": // string subtype
|
|
2099
|
-
break;
|
|
2100
|
-
case "integer":
|
|
2101
|
-
break;
|
|
2102
|
-
case "real":
|
|
2103
|
-
break;
|
|
2104
|
-
case "boolean":
|
|
2105
|
-
break;
|
|
2106
|
-
|
|
2107
|
-
case "object":
|
|
2108
|
-
case "dict":
|
|
2109
|
-
case "template":
|
|
2110
|
-
case "array":
|
|
2111
|
-
case "list":
|
|
2112
|
-
case "coordinates":
|
|
2113
|
-
case "blob":
|
|
2114
|
-
// Return as it is, manage by jsoneditor
|
|
2115
|
-
value = {json: value};
|
|
2116
|
-
// value = {text: JSON.stringify(value)}; // other way
|
|
2117
|
-
break;
|
|
2118
|
-
|
|
2119
|
-
case "enum":
|
|
2120
|
-
switch(field_desc.real_type) {
|
|
2121
|
-
case "string":
|
|
2122
|
-
break;
|
|
2123
|
-
case "object":
|
|
2124
|
-
case "dict":
|
|
2125
|
-
case "array":
|
|
2126
|
-
case "list":
|
|
2127
|
-
break;
|
|
2128
|
-
}
|
|
2129
|
-
break;
|
|
2130
|
-
|
|
2131
|
-
case "now":
|
|
2132
|
-
case "time":
|
|
2133
|
-
switch(field_desc.real_type) {
|
|
2134
|
-
case "string":
|
|
2135
|
-
case "integer":
|
|
2136
|
-
value = parseInt(value) || 0;
|
|
2137
|
-
value = new Date(value);
|
|
2138
|
-
break;
|
|
2139
|
-
}
|
|
2140
|
-
break;
|
|
2141
|
-
|
|
2142
|
-
case "color":
|
|
2143
|
-
switch(field_desc.real_type) {
|
|
2144
|
-
case "string":
|
|
2145
|
-
// TODO
|
|
2146
|
-
break;
|
|
2147
|
-
case "integer":
|
|
2148
|
-
// TODO
|
|
2149
|
-
break;
|
|
2150
|
-
}
|
|
2151
|
-
break;
|
|
2152
|
-
|
|
2153
|
-
case "hook": // Convert data from backend to frontend FORM FIELD
|
|
2154
|
-
// WARNING hook fields are not writable and by now they are not showed in forms.
|
|
2155
|
-
break;
|
|
2156
|
-
|
|
2157
|
-
case "fkey": // Convert data from backend to frontend FORM FIELD
|
|
2158
|
-
let new_value = [];
|
|
2159
|
-
if(value) {
|
|
2160
|
-
if(is_string(value)) {
|
|
2161
|
-
let fkey = treedb_decoder_fkey(col, value);
|
|
2162
|
-
if(fkey) {
|
|
2163
|
-
new_value.push(fkey.id);
|
|
2164
|
-
}
|
|
2165
|
-
} else if(is_array(value)) {
|
|
2166
|
-
for(let i=0; i<value.length; i++) {
|
|
2167
|
-
let fkey = treedb_decoder_fkey(col, value[i]);
|
|
2168
|
-
if(fkey) {
|
|
2169
|
-
new_value.push(fkey.id);
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
} else {
|
|
2173
|
-
log_error("fkey type unsupported: " + JSON.stringify(value));
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
value = new_value;
|
|
2178
|
-
break;
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
return value;
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
1271
|
/************************************************************
|
|
2185
1272
|
* Convert from frontend to backend
|
|
2186
1273
|
* operation: "create" "update"
|
|
@@ -2360,24 +1447,6 @@ function transform__form_value_2_treedb_value(gobj, col, value, operation)
|
|
|
2360
1447
|
return value;
|
|
2361
1448
|
}
|
|
2362
1449
|
|
|
2363
|
-
/************************************************************
|
|
2364
|
-
*
|
|
2365
|
-
************************************************************/
|
|
2366
|
-
function get_fkey_options(gobj, col)
|
|
2367
|
-
{
|
|
2368
|
-
for(let topic_name of Object.keys(col.fkey)) {
|
|
2369
|
-
// HACK we work only with one fkey
|
|
2370
|
-
let kw = {
|
|
2371
|
-
topic_name: topic_name
|
|
2372
|
-
};
|
|
2373
|
-
let webix = gobj_command(gobj_parent(gobj), "get_topic_data", kw, gobj);
|
|
2374
|
-
let current_list = webix.data;
|
|
2375
|
-
return list2options(current_list, "id", "id");
|
|
2376
|
-
}
|
|
2377
|
-
log_error("No fkey options found" + JSON.stringify(col));
|
|
2378
|
-
return null;
|
|
2379
|
-
}
|
|
2380
|
-
|
|
2381
1450
|
/************************************************************
|
|
2382
1451
|
*
|
|
2383
1452
|
************************************************************/
|
|
@@ -2917,58 +1986,29 @@ function ac_paste_rows(gobj, event, kw, src)
|
|
|
2917
1986
|
}
|
|
2918
1987
|
|
|
2919
1988
|
/************************************************************
|
|
2920
|
-
*
|
|
1989
|
+
* EV_SAVE_RECORD from the hosted C_YUI_FORM: kw is the
|
|
1990
|
+
* record already in treedb shape (the form itself encodes
|
|
1991
|
+
* fkeys, times and numbers). Route by the form's mode.
|
|
2921
1992
|
************************************************************/
|
|
2922
|
-
function
|
|
1993
|
+
function ac_form_save_record(gobj, event, kw, src)
|
|
2923
1994
|
{
|
|
2924
|
-
let
|
|
2925
|
-
try {
|
|
2926
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "create");
|
|
2927
|
-
} catch (e) {
|
|
2928
|
-
log_warning(e);
|
|
2929
|
-
throw e;
|
|
2930
|
-
}
|
|
2931
|
-
|
|
1995
|
+
let mode = gobj_read_str_attr(src, "form_mode");
|
|
2932
1996
|
gobj_publish_event(
|
|
2933
1997
|
gobj,
|
|
2934
|
-
"EV_CREATE_RECORD",
|
|
1998
|
+
(mode === "create")? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD",
|
|
2935
1999
|
{
|
|
2936
2000
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
2937
|
-
record:
|
|
2001
|
+
record: kw
|
|
2938
2002
|
}
|
|
2939
2003
|
);
|
|
2940
2004
|
|
|
2941
|
-
/*
|
|
2942
|
-
*
|
|
2943
|
-
*/
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
/************************************************************
|
|
2948
|
-
* From internal Save button of "update" modal form
|
|
2949
|
-
************************************************************/
|
|
2950
|
-
function ac_update_record(gobj, event, kw, src)
|
|
2951
|
-
{
|
|
2952
|
-
let new_kw = null;
|
|
2953
|
-
try {
|
|
2954
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "update");
|
|
2955
|
-
} catch (e) {
|
|
2956
|
-
log_warning(e);
|
|
2957
|
-
throw e;
|
|
2958
|
-
}
|
|
2959
|
-
|
|
2960
|
-
gobj_publish_event(
|
|
2961
|
-
gobj,
|
|
2962
|
-
"EV_UPDATE_RECORD",
|
|
2963
|
-
{
|
|
2964
|
-
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
2965
|
-
record: new_kw
|
|
2966
|
-
}
|
|
2967
|
-
);
|
|
2005
|
+
/* we are INSIDE the form's gobj_publish_event stack — never
|
|
2006
|
+
* destroy the publisher synchronously from a subscriber
|
|
2007
|
+
* callback: defer the close */
|
|
2008
|
+
setTimeout(function() {
|
|
2009
|
+
close_form_dialog(gobj);
|
|
2010
|
+
}, 0);
|
|
2968
2011
|
|
|
2969
|
-
/*
|
|
2970
|
-
* Return -1 will not close the form
|
|
2971
|
-
*/
|
|
2972
2012
|
return 0;
|
|
2973
2013
|
}
|
|
2974
2014
|
|
|
@@ -3147,8 +2187,7 @@ function create_gclass(gclass_name)
|
|
|
3147
2187
|
["EV_NODE_DELETED", ac_node_deleted, null],
|
|
3148
2188
|
|
|
3149
2189
|
["EV_EDITION_MODE", ac_edition_mode, null],
|
|
3150
|
-
["
|
|
3151
|
-
["EV_UPDATE_RECORD", ac_update_record, null],
|
|
2190
|
+
["EV_SAVE_RECORD", ac_form_save_record, null],
|
|
3152
2191
|
|
|
3153
2192
|
["EV_NEW_ROW", ac_new_row, null],
|
|
3154
2193
|
["EV_DELETE_ROWS", ac_delete_rows, null],
|
|
@@ -3174,6 +2213,7 @@ function create_gclass(gclass_name)
|
|
|
3174
2213
|
["EV_NODE_DELETED", 0],
|
|
3175
2214
|
|
|
3176
2215
|
["EV_EDITION_MODE", 0],
|
|
2216
|
+
["EV_SAVE_RECORD", 0],
|
|
3177
2217
|
["EV_NEW_ROW", 0],
|
|
3178
2218
|
["EV_DELETE_ROWS", 0],
|
|
3179
2219
|
["EV_COPY_ROWS", 0],
|
|
@@ -3219,6 +2259,11 @@ function create_gclass(gclass_name)
|
|
|
3219
2259
|
***************************************************************/
|
|
3220
2260
|
function register_c_yui_treedb_topic_with_form()
|
|
3221
2261
|
{
|
|
2262
|
+
/* the edit/create dialog hosts a C_YUI_FORM child: make sure
|
|
2263
|
+
* its gclass exists even if the app didn't register it */
|
|
2264
|
+
if(!gclass_find_by_name("C_YUI_FORM", false)) {
|
|
2265
|
+
register_c_yui_form();
|
|
2266
|
+
}
|
|
3222
2267
|
return create_gclass(GCLASS_NAME);
|
|
3223
2268
|
}
|
|
3224
2269
|
|