@yuneta/gobj-ui 2.2.5 → 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 +695 -1023
- package/dist/gobj-ui.es.js +696 -1024
- 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 +201 -1170
- 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,
|
|
@@ -1001,844 +1025,211 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field)
|
|
|
1001
1025
|
return value;
|
|
1002
1026
|
}
|
|
1003
1027
|
|
|
1004
|
-
|
|
1005
|
-
*
|
|
1006
|
-
|
|
1007
|
-
|
|
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)
|
|
1008
1035
|
{
|
|
1009
1036
|
let desc = gobj_read_attr(gobj, "desc");
|
|
1010
|
-
let
|
|
1011
|
-
let
|
|
1012
|
-
|
|
1013
|
-
for (let i = 0; i < desc.cols.length; i++) {
|
|
1014
|
-
let col = desc.cols[i];
|
|
1015
|
-
if(col.id.charAt(0)==='_') {
|
|
1037
|
+
let template = [];
|
|
1038
|
+
for(let col of desc.cols) {
|
|
1039
|
+
if(!col.id || col.id.charAt(0) === '_') {
|
|
1016
1040
|
continue;
|
|
1017
1041
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
if(!tag) {
|
|
1042
|
+
const field_desc = treedb_get_field_desc(col);
|
|
1043
|
+
if(field_desc.is_hidden) {
|
|
1022
1044
|
continue;
|
|
1023
1045
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
let is_writable = str_in_list(flag, "writable");
|
|
1028
|
-
let is_notnull = str_in_list(flag, "notnull");
|
|
1029
|
-
let is_rowid = str_in_list(flag, "rowid");
|
|
1030
|
-
let is_required = str_in_list(flag, "required");
|
|
1031
|
-
let is_hidden = str_in_list(flag, "hidden");
|
|
1032
|
-
let is_persistent = str_in_list(flag, "persistent");
|
|
1033
|
-
let is_hook = str_in_list(flag, "hook");
|
|
1034
|
-
let is_fkey = str_in_list(flag, "fkey");
|
|
1035
|
-
|
|
1036
|
-
if(1) {
|
|
1037
|
-
/*
|
|
1038
|
-
* update mode by default
|
|
1039
|
-
*/
|
|
1040
|
-
if(id === desc.pkey) {
|
|
1041
|
-
is_writable = false;
|
|
1042
|
-
}
|
|
1046
|
+
if(field_desc.type === "hook") {
|
|
1047
|
+
continue; // hooks don't appear in forms
|
|
1043
1048
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
// if(is_persistent) {
|
|
1047
|
-
// is_writable = true;
|
|
1048
|
-
// }
|
|
1049
|
-
|
|
1050
|
-
if(is_fkey) {
|
|
1051
|
-
// TODO no debería estar definido con writable?
|
|
1052
|
-
// TODO pueden haber enlaces que solo se puedan hacer por programa
|
|
1053
|
-
is_writable = true;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
if(!is_writable) {
|
|
1057
|
-
if(id !== desc.pkey) {
|
|
1058
|
-
// Show only fields writable (except pkey)
|
|
1059
|
-
continue;
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
if(is_hidden) {
|
|
1049
|
+
if(!field_desc.is_writable &&
|
|
1050
|
+
field_desc.type !== "fkey" && col.id !== desc.pkey) {
|
|
1063
1051
|
continue;
|
|
1064
1052
|
}
|
|
1065
|
-
|
|
1066
|
-
if(is_rowid) {
|
|
1067
|
-
is_writable = false;
|
|
1068
|
-
is_required = false;
|
|
1069
|
-
is_notnull = false;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
let field_conf = {
|
|
1073
|
-
tag: tag,
|
|
1074
|
-
inputType: form_input_type,
|
|
1075
|
-
name: col.id,
|
|
1076
|
-
label: col_label(col, gobj_read_str_attr(gobj, "topic_name")),
|
|
1077
|
-
placeholder: "",
|
|
1078
|
-
options: options, // select,select2,radio options
|
|
1079
|
-
extras: extras, // extra html input/textarea attributes
|
|
1080
|
-
readonly: !is_writable,
|
|
1081
|
-
required: is_required,
|
|
1082
|
-
hidden: is_hidden,
|
|
1083
|
-
not_null: is_notnull,
|
|
1084
|
-
is_pkey: (id === desc.pkey),
|
|
1085
|
-
is_rowid: is_rowid
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
let $field = create_html_field(field_conf);
|
|
1089
|
-
$field.field_conf = field_conf;
|
|
1090
|
-
|
|
1091
|
-
$form.appendChild($field);
|
|
1053
|
+
template.push(col);
|
|
1092
1054
|
}
|
|
1093
|
-
return
|
|
1055
|
+
return template;
|
|
1094
1056
|
}
|
|
1095
1057
|
|
|
1096
1058
|
/************************************************************
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
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.
|
|
1102
1064
|
************************************************************/
|
|
1103
|
-
function
|
|
1104
|
-
{
|
|
1105
|
-
const field_desc = treedb_get_field_desc(col);
|
|
1106
|
-
|
|
1107
|
-
let tag = null;
|
|
1108
|
-
let inputType = '';
|
|
1109
|
-
let options = [];
|
|
1110
|
-
let extras = {}; // extra html input/textarea attributes
|
|
1111
|
-
|
|
1112
|
-
switch(field_desc.type) {
|
|
1113
|
-
case "image":
|
|
1114
|
-
case "string":
|
|
1115
|
-
tag = 'input';
|
|
1116
|
-
inputType = 'text';
|
|
1117
|
-
break;
|
|
1118
|
-
case "integer":
|
|
1119
|
-
tag = 'input';
|
|
1120
|
-
extras.inputmode = "numeric";
|
|
1121
|
-
break;
|
|
1122
|
-
case "real":
|
|
1123
|
-
tag = 'input';
|
|
1124
|
-
extras.inputmode = "decimal";
|
|
1125
|
-
break;
|
|
1126
|
-
|
|
1127
|
-
case "rowid":
|
|
1128
|
-
tag = 'input';
|
|
1129
|
-
inputType = 'text';
|
|
1130
|
-
break;
|
|
1131
|
-
|
|
1132
|
-
case "boolean":
|
|
1133
|
-
tag = 'checkbox';
|
|
1134
|
-
break;
|
|
1135
|
-
|
|
1136
|
-
case "now":
|
|
1137
|
-
case "time":
|
|
1138
|
-
tag = 'input';
|
|
1139
|
-
inputType = 'datetime-local';
|
|
1140
|
-
switch(field_desc.real_type) {
|
|
1141
|
-
case "string":
|
|
1142
|
-
break;
|
|
1143
|
-
case "integer":
|
|
1144
|
-
break;
|
|
1145
|
-
}
|
|
1146
|
-
break;
|
|
1147
|
-
case "color":
|
|
1148
|
-
tag = 'input';
|
|
1149
|
-
inputType = 'color';
|
|
1150
|
-
switch(field_desc.real_type) {
|
|
1151
|
-
case "string":
|
|
1152
|
-
break;
|
|
1153
|
-
case "integer":
|
|
1154
|
-
break;
|
|
1155
|
-
}
|
|
1156
|
-
break;
|
|
1157
|
-
case "email":
|
|
1158
|
-
tag = 'input';
|
|
1159
|
-
inputType = 'email';
|
|
1160
|
-
extras.inputmode = "email";
|
|
1161
|
-
break;
|
|
1162
|
-
case "password":
|
|
1163
|
-
tag = 'input';
|
|
1164
|
-
inputType = 'password';
|
|
1165
|
-
break;
|
|
1166
|
-
case "url":
|
|
1167
|
-
tag = 'input';
|
|
1168
|
-
inputType = 'url';
|
|
1169
|
-
extras.inputmode = "url";
|
|
1170
|
-
break;
|
|
1171
|
-
case "tel":
|
|
1172
|
-
tag = 'input';
|
|
1173
|
-
inputType = 'tel';
|
|
1174
|
-
extras.inputmode = "tel";
|
|
1175
|
-
break;
|
|
1176
|
-
|
|
1177
|
-
case "object":
|
|
1178
|
-
case "dict":
|
|
1179
|
-
case "template":
|
|
1180
|
-
case "array":
|
|
1181
|
-
case "list":
|
|
1182
|
-
case "coordinates":
|
|
1183
|
-
case "blob":
|
|
1184
|
-
tag = 'jsoneditor';
|
|
1185
|
-
break;
|
|
1186
|
-
|
|
1187
|
-
case "enum":
|
|
1188
|
-
tag = 'select2';
|
|
1189
|
-
switch(field_desc.real_type) {
|
|
1190
|
-
case "string": // TODO review types
|
|
1191
|
-
case "object":
|
|
1192
|
-
case "dict":
|
|
1193
|
-
case "array":
|
|
1194
|
-
case "list":
|
|
1195
|
-
options = list2options(field_desc.enum_list, "id", "id");
|
|
1196
|
-
break;
|
|
1197
|
-
}
|
|
1198
|
-
break;
|
|
1199
|
-
|
|
1200
|
-
case "hook":
|
|
1201
|
-
// hooks don't appear in form
|
|
1202
|
-
break;
|
|
1203
|
-
|
|
1204
|
-
case "fkey":
|
|
1205
|
-
tag = 'select2';
|
|
1206
|
-
switch(field_desc.real_type) {
|
|
1207
|
-
case "string": // TODO review types
|
|
1208
|
-
case "object":
|
|
1209
|
-
case "dict":
|
|
1210
|
-
case "array":
|
|
1211
|
-
case "list":
|
|
1212
|
-
options = get_fkey_options(gobj, col);
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
break;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
return [tag, inputType, options, extras];
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
/******************************************************************
|
|
1222
|
-
* Helper function to create different form elements
|
|
1223
|
-
* Available types:
|
|
1224
|
-
* - input
|
|
1225
|
-
* - textarea
|
|
1226
|
-
* - select
|
|
1227
|
-
* - select2
|
|
1228
|
-
* - checkbox
|
|
1229
|
-
* - radio
|
|
1230
|
-
* - jsoneditor
|
|
1231
|
-
*
|
|
1232
|
-
* {
|
|
1233
|
-
* tag: field tag,
|
|
1234
|
-
* inputType: input type,
|
|
1235
|
-
* name: id
|
|
1236
|
-
* label: ""
|
|
1237
|
-
* placeholder: "",
|
|
1238
|
-
* options: [{id:"", value:""}]]
|
|
1239
|
-
* extras: {}
|
|
1240
|
-
* readonly: bool
|
|
1241
|
-
* required: bool
|
|
1242
|
-
* hidden: bool
|
|
1243
|
-
* not_null: bool
|
|
1244
|
-
* _col: col
|
|
1245
|
-
* }
|
|
1246
|
-
******************************************************************/
|
|
1247
|
-
function create_html_field(
|
|
1248
|
-
{
|
|
1249
|
-
tag,
|
|
1250
|
-
inputType,
|
|
1251
|
-
name,
|
|
1252
|
-
label,
|
|
1253
|
-
placeholder,
|
|
1254
|
-
options,
|
|
1255
|
-
extras, // extra html input/textarea attributes
|
|
1256
|
-
readonly,
|
|
1257
|
-
required,
|
|
1258
|
-
hidden,
|
|
1259
|
-
not_null
|
|
1260
|
-
}
|
|
1261
|
-
)
|
|
1065
|
+
function build_fkey_options(gobj)
|
|
1262
1066
|
{
|
|
1263
|
-
let
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
case 'checkbox':
|
|
1270
|
-
case 'radio':
|
|
1271
|
-
case 'jsoneditor':
|
|
1272
|
-
base = [
|
|
1273
|
-
'div', {class: 'xfield field is-horizontal'}, [
|
|
1274
|
-
['div', {class: 'field-label is-normal' }, [
|
|
1275
|
-
['label', {class:'label ', i18n:label, for:name}, label]
|
|
1276
|
-
]],
|
|
1277
|
-
|
|
1278
|
-
['div', {class: 'field-body'}, [
|
|
1279
|
-
['div', {class: 'field'}, [
|
|
1280
|
-
['div', {class: 'control' }, []]
|
|
1281
|
-
]]
|
|
1282
|
-
]]
|
|
1283
|
-
]
|
|
1284
|
-
];
|
|
1285
|
-
break;
|
|
1286
|
-
|
|
1287
|
-
default:
|
|
1288
|
-
base = ['div', {class: 'control'}, 'Unsupported element type'];
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
let $element = createElement2(base);
|
|
1292
|
-
let $control = $element.querySelector('.control');
|
|
1293
|
-
|
|
1294
|
-
switch (tag) {
|
|
1295
|
-
case 'input':
|
|
1296
|
-
{
|
|
1297
|
-
let attrs = {
|
|
1298
|
-
class: `input ${name==='id'?'with-focus':''}`,
|
|
1299
|
-
name: name,
|
|
1300
|
-
type: inputType,
|
|
1301
|
-
placeholder: placeholder
|
|
1302
|
-
};
|
|
1303
|
-
Object.assign(attrs, extras);
|
|
1304
|
-
|
|
1305
|
-
if(readonly) {
|
|
1306
|
-
attrs.readonly = '';
|
|
1307
|
-
}
|
|
1308
|
-
let extend = ['input', attrs, '', {
|
|
1309
|
-
'blur': function (evt) {
|
|
1310
|
-
validate_field_on_blur(this);
|
|
1311
|
-
}
|
|
1312
|
-
}];
|
|
1313
|
-
|
|
1314
|
-
let $extend = createElement2(extend);
|
|
1315
|
-
$control.appendChild($extend);
|
|
1316
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1317
|
-
break;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
case 'jsoneditor':
|
|
1321
|
-
{
|
|
1322
|
-
let attrs = {
|
|
1323
|
-
class: 'jsoneditor jse-theme-dark',
|
|
1324
|
-
style: ''
|
|
1325
|
-
};
|
|
1326
|
-
Object.assign(attrs, extras);
|
|
1327
|
-
|
|
1328
|
-
if(readonly) {
|
|
1329
|
-
attrs.readonly = '';
|
|
1330
|
-
}
|
|
1331
|
-
let extend = ['div', attrs];
|
|
1332
|
-
let $extend = createElement2(extend);
|
|
1333
|
-
$extend.setAttribute('name', name);
|
|
1334
|
-
$control.appendChild($extend);
|
|
1335
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1336
|
-
break;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
case 'textarea':
|
|
1340
|
-
{
|
|
1341
|
-
let attrs = {class:'textarea', name:name, placeholder: placeholder};
|
|
1342
|
-
Object.assign(attrs, extras);
|
|
1343
|
-
|
|
1344
|
-
if(readonly) {
|
|
1345
|
-
attrs.readonly = '';
|
|
1346
|
-
}
|
|
1347
|
-
let extend = ['textarea', attrs, '', {
|
|
1348
|
-
'blur': function (evt) {
|
|
1349
|
-
validate_field_on_blur(this);
|
|
1350
|
-
}
|
|
1351
|
-
}];
|
|
1352
|
-
let $extend = createElement2(extend);
|
|
1353
|
-
$control.appendChild($extend);
|
|
1354
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1355
|
-
break;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
case 'select':
|
|
1359
|
-
{
|
|
1360
|
-
// {id:"id", value:"name"}
|
|
1361
|
-
// <option value="id" data-i18n:"name"> name </option>
|
|
1362
|
-
let extend = ['div', {class: 'select' },
|
|
1363
|
-
['select',
|
|
1364
|
-
{name:name},
|
|
1365
|
-
options.map(option =>
|
|
1366
|
-
['option', {value:option.id, i18n:option.value}, option.value]
|
|
1367
|
-
)
|
|
1368
|
-
]
|
|
1369
|
-
];
|
|
1370
|
-
let $extend = createElement2(extend);
|
|
1371
|
-
$control.appendChild($extend);
|
|
1372
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1373
|
-
break;
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
case 'select2':
|
|
1377
|
-
{
|
|
1378
|
-
// {id:"id", value:"name"}
|
|
1379
|
-
// <option value="id" data-i18n:"name"> name </option>
|
|
1380
|
-
let extend = ['select',
|
|
1381
|
-
{
|
|
1382
|
-
name:name,
|
|
1383
|
-
class: 'select2-multiple',
|
|
1384
|
-
multiple:'multiple',
|
|
1385
|
-
style: 'width: 100% !important;'
|
|
1386
|
-
},
|
|
1387
|
-
options.map(option =>
|
|
1388
|
-
['option', {value:option.id, i18n:option.value}, option.value]
|
|
1389
|
-
)
|
|
1390
|
-
];
|
|
1391
|
-
let $extend = createElement2(extend);
|
|
1392
|
-
$control.appendChild($extend);
|
|
1393
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1394
|
-
break;
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
case 'checkbox':
|
|
1398
|
-
{
|
|
1399
|
-
let extend = ['label', {class: 'checkbox'},
|
|
1400
|
-
['input', {type: 'checkbox', name:name}]
|
|
1401
|
-
];
|
|
1402
|
-
let $extend = createElement2(extend);
|
|
1403
|
-
$control.appendChild($extend);
|
|
1404
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1405
|
-
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;
|
|
1406
1073
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
{
|
|
1410
|
-
|
|
1411
|
-
// WARNING here can't use data-i18n, the radio element is destroyed.
|
|
1412
|
-
let elm = `
|
|
1413
|
-
<label class="radio">
|
|
1414
|
-
<input type="radio" name="${name}">
|
|
1415
|
-
${t(option)}
|
|
1416
|
-
</label>
|
|
1417
|
-
`;
|
|
1418
|
-
|
|
1419
|
-
let $extend = createOneHtml(elm);
|
|
1420
|
-
$control.appendChild($extend);
|
|
1421
|
-
}
|
|
1422
|
-
$control.appendChild(createElement2(['p', {class: 'help is-danger', style: 'display:none'}, '']));
|
|
1423
|
-
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;
|
|
1424
1078
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
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 : [];
|
|
1428
1086
|
}
|
|
1429
|
-
|
|
1430
|
-
return $element;
|
|
1087
|
+
return fkey_options;
|
|
1431
1088
|
}
|
|
1432
1089
|
|
|
1433
|
-
|
|
1434
|
-
*
|
|
1435
|
-
|
|
1436
|
-
|
|
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)
|
|
1437
1098
|
{
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
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");
|
|
1441
1103
|
let popup_id = gobj_read_str_attr(gobj, "popup_id");
|
|
1104
|
+
let topic_name = gobj_read_str_attr(gobj, "topic_name");
|
|
1442
1105
|
|
|
1443
1106
|
let $element = createElement2([
|
|
1444
|
-
'div', {id: popup_id, class: 'modal'
|
|
1445
|
-
['div', {
|
|
1446
|
-
['div', {
|
|
1447
|
-
['header', {
|
|
1448
|
-
['p', {
|
|
1449
|
-
['button', {class:
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
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
|
+
}]
|
|
1453
1118
|
]],
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
], {
|
|
1462
|
-
click: function(evt) {
|
|
1463
|
-
evt.stopPropagation();
|
|
1464
|
-
evt.preventDefault();
|
|
1465
|
-
let $form = $element.querySelector('form');
|
|
1466
|
-
if (validate_form(gobj, $form)) {
|
|
1467
|
-
let values = get_form_values($form);
|
|
1468
|
-
let ret;
|
|
1469
|
-
if($form.__mode__==="create") {
|
|
1470
|
-
ret = gobj_send_event(gobj, "EV_CREATE_RECORD", values, gobj);
|
|
1471
|
-
} else {
|
|
1472
|
-
ret = gobj_send_event(gobj, "EV_UPDATE_RECORD", values, gobj);
|
|
1473
|
-
}
|
|
1474
|
-
if(ret === 0) {
|
|
1475
|
-
$element.classList.remove('is-active');
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
}],
|
|
1480
|
-
|
|
1481
|
-
// TODO disable/enable buttons save and undo accord to form state
|
|
1482
|
-
|
|
1483
|
-
['button', { class: 'button px-4 form-close'}, [
|
|
1484
|
-
['span', {class:'icon'}, [
|
|
1485
|
-
['i', {class:'yi-xmark'}]
|
|
1486
|
-
]],
|
|
1487
|
-
['span', {i18n:'cancel'}, 'cancel']
|
|
1488
|
-
]],
|
|
1489
|
-
|
|
1490
|
-
['button', { class: 'button px-4'}, [
|
|
1491
|
-
['span', {class:'icon'}, [
|
|
1492
|
-
['i', {class:'yi-broom'}]
|
|
1493
|
-
]],
|
|
1494
|
-
['span', {i18n:'clear'}, 'clear']
|
|
1495
|
-
], {
|
|
1496
|
-
click: function(evt) {
|
|
1497
|
-
evt.stopPropagation();
|
|
1498
|
-
clear_validation(gobj, $form);
|
|
1499
|
-
clear_data(gobj, $form);
|
|
1500
|
-
}
|
|
1501
|
-
}]
|
|
1502
|
-
]]
|
|
1503
|
-
]]
|
|
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
|
+
}, []]
|
|
1504
1126
|
]]
|
|
1505
1127
|
], {
|
|
1506
|
-
click: function
|
|
1128
|
+
click: function(evt) {
|
|
1507
1129
|
// Capture bubble events
|
|
1508
1130
|
evt.stopPropagation();
|
|
1509
1131
|
}
|
|
1510
1132
|
}
|
|
1511
1133
|
]);
|
|
1512
1134
|
|
|
1513
|
-
|
|
1514
|
-
* Add events
|
|
1515
|
-
*------------------------*/
|
|
1516
|
-
/*
|
|
1517
|
-
* Button close
|
|
1518
|
-
*/
|
|
1519
|
-
$element.querySelectorAll('.modal-close, .form-close, .delete').forEach(function (element) {
|
|
1520
|
-
element.addEventListener('click', function(event) {
|
|
1521
|
-
event.stopPropagation();
|
|
1522
|
-
// TODO ver si hay algo modificado y preguntar si se desecha
|
|
1523
|
-
$element.classList.remove('is-active');
|
|
1524
|
-
});
|
|
1525
|
-
});
|
|
1526
|
-
|
|
1527
|
-
refresh_language($element, t);
|
|
1528
|
-
|
|
1529
|
-
/*-----------------------------------------*
|
|
1530
|
-
* Add to popup layer !!!
|
|
1531
|
-
*-----------------------------------------*/
|
|
1532
|
-
popup_mount_layer().appendChild($element);
|
|
1533
|
-
|
|
1534
|
-
// jQuery($element).off('click').on('click', function(evt) {
|
|
1535
|
-
// evt.stopPropagation();
|
|
1536
|
-
// evt.preventDefault();
|
|
1537
|
-
// });
|
|
1538
|
-
|
|
1539
|
-
/*------------------------*
|
|
1540
|
-
* Jsoneditor
|
|
1541
|
-
*------------------------*/
|
|
1542
|
-
$element.querySelectorAll('.jsoneditor').forEach(function (element) {
|
|
1543
|
-
let font_family = "DejaVu Sans Mono, monospace";
|
|
1544
|
-
let sz = 16;
|
|
1545
|
-
element.style.setProperty('--jse-font-size-mono', sz + 'px');
|
|
1546
|
-
element.style.setProperty('--jse-font-family-mono', font_family);
|
|
1547
|
-
|
|
1548
|
-
element.jsoneditor = new JSONEditor({
|
|
1549
|
-
target: element,
|
|
1550
|
-
props: {
|
|
1551
|
-
timestampTag: function({field, value, path}) {
|
|
1552
|
-
return field === '__t__' || field === '__tm__' || field === 'tm' ||
|
|
1553
|
-
field === 'from_t' || field === 'to_t' || field === 't' ||
|
|
1554
|
-
field === 't_input' || field === 't_output' ||
|
|
1555
|
-
field === 'from_tm' || field === 'to_tm' || field === 'time';
|
|
1556
|
-
},
|
|
1557
|
-
}
|
|
1558
|
-
});
|
|
1559
|
-
});
|
|
1560
|
-
|
|
1561
|
-
/*---------------------------------------------------------*
|
|
1562
|
-
* Extensions of jquery must be done after DOM attaching
|
|
1563
|
-
*---------------------------------------------------------*/
|
|
1564
|
-
// let $$x = jQuery($element).find('.select2-multiple');
|
|
1565
|
-
// if($$x && $$x.select2) {
|
|
1566
|
-
// $$x.select2();
|
|
1567
|
-
// }
|
|
1135
|
+
let $body = $element.querySelector('.modal-card-body');
|
|
1568
1136
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
************************************************************/
|
|
1587
|
-
function show_field_error($field, message)
|
|
1588
|
-
{
|
|
1589
|
-
$field.querySelectorAll('input, textarea, select').forEach(el => {
|
|
1590
|
-
el.classList.add('is-danger');
|
|
1591
|
-
});
|
|
1592
|
-
let $help = $field.querySelector('.help');
|
|
1593
|
-
if($help) {
|
|
1594
|
-
$help.textContent = message;
|
|
1595
|
-
$help.style.display = 'block';
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
/************************************************************
|
|
1600
|
-
* Clear a validation error from a field
|
|
1601
|
-
************************************************************/
|
|
1602
|
-
function clear_field_error($field)
|
|
1603
|
-
{
|
|
1604
|
-
$field.querySelectorAll('input, textarea, select').forEach(el => {
|
|
1605
|
-
el.classList.remove('is-danger');
|
|
1606
|
-
});
|
|
1607
|
-
let $help = $field.querySelector('.help');
|
|
1608
|
-
if($help) {
|
|
1609
|
-
$help.textContent = '';
|
|
1610
|
-
$help.style.display = 'none';
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
/************************************************************
|
|
1615
|
-
* Get the current value of a field for validation
|
|
1616
|
-
************************************************************/
|
|
1617
|
-
function get_field_value_for_validation($field, conf)
|
|
1618
|
-
{
|
|
1619
|
-
switch(conf.tag) {
|
|
1620
|
-
case 'input': {
|
|
1621
|
-
let $input = $field.querySelector('input');
|
|
1622
|
-
if(!$input) {
|
|
1623
|
-
return '';
|
|
1624
|
-
}
|
|
1625
|
-
if($input.type === 'checkbox') {
|
|
1626
|
-
return $input.checked;
|
|
1627
|
-
}
|
|
1628
|
-
return $input.value;
|
|
1629
|
-
}
|
|
1630
|
-
case 'checkbox': {
|
|
1631
|
-
let $input = $field.querySelector('input[type="checkbox"]');
|
|
1632
|
-
if(!$input) {
|
|
1633
|
-
return false;
|
|
1634
|
-
}
|
|
1635
|
-
return $input.checked;
|
|
1636
|
-
}
|
|
1637
|
-
case 'textarea': {
|
|
1638
|
-
let $el = $field.querySelector('textarea');
|
|
1639
|
-
if(!$el) {
|
|
1640
|
-
return '';
|
|
1641
|
-
}
|
|
1642
|
-
return $el.value;
|
|
1643
|
-
}
|
|
1644
|
-
case 'select':
|
|
1645
|
-
case 'select2': {
|
|
1646
|
-
let $el = $field.querySelector('select');
|
|
1647
|
-
if(!$el) {
|
|
1648
|
-
return '';
|
|
1649
|
-
}
|
|
1650
|
-
if($el.tomselect) {
|
|
1651
|
-
return $el.tomselect.getValue();
|
|
1652
|
-
}
|
|
1653
|
-
return $el.value;
|
|
1654
|
-
}
|
|
1655
|
-
case 'jsoneditor': {
|
|
1656
|
-
let $el = $field.querySelector('.jsoneditor');
|
|
1657
|
-
if(!$el || !$el.jsoneditor) {
|
|
1658
|
-
return '';
|
|
1659
|
-
}
|
|
1660
|
-
let val = $el.jsoneditor.get();
|
|
1661
|
-
if(val.text !== undefined) {
|
|
1662
|
-
return val.text;
|
|
1663
|
-
}
|
|
1664
|
-
return JSON.stringify(val.json);
|
|
1665
|
-
}
|
|
1666
|
-
case 'radio': {
|
|
1667
|
-
let $checked = $field.querySelector('input[type="radio"]:checked');
|
|
1668
|
-
if(!$checked) {
|
|
1669
|
-
return '';
|
|
1670
|
-
}
|
|
1671
|
-
return $checked.value;
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
return '';
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
/************************************************************
|
|
1678
|
-
* Check if a field value is empty
|
|
1679
|
-
************************************************************/
|
|
1680
|
-
function is_field_value_empty(value)
|
|
1681
|
-
{
|
|
1682
|
-
if(value === null || value === undefined || value === '') {
|
|
1683
|
-
return true;
|
|
1684
|
-
}
|
|
1685
|
-
if(Array.isArray(value) && value.length === 0) {
|
|
1686
|
-
return true;
|
|
1687
|
-
}
|
|
1688
|
-
return false;
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
/************************************************************
|
|
1692
|
-
* Determine if a field requires validation in the current mode
|
|
1693
|
-
************************************************************/
|
|
1694
|
-
function is_field_required(conf, mode)
|
|
1695
|
-
{
|
|
1696
|
-
if(conf.is_pkey) {
|
|
1697
|
-
// pkey is required only in create mode (unless it's a rowid)
|
|
1698
|
-
return (mode === "create" && !conf.is_rowid);
|
|
1699
|
-
}
|
|
1700
|
-
if(conf.readonly) {
|
|
1701
|
-
return false;
|
|
1702
|
-
}
|
|
1703
|
-
return (conf.required || conf.not_null);
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
/************************************************************
|
|
1707
|
-
* Validate a single field on blur
|
|
1708
|
-
* Called from input/textarea blur event handlers
|
|
1709
|
-
************************************************************/
|
|
1710
|
-
function validate_field_on_blur(element)
|
|
1711
|
-
{
|
|
1712
|
-
let $field = element.closest('.xfield');
|
|
1713
|
-
if(!$field || !$field.field_conf) {
|
|
1714
|
-
return;
|
|
1715
|
-
}
|
|
1716
|
-
let conf = $field.field_conf;
|
|
1717
|
-
let $form = element.closest('form');
|
|
1718
|
-
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;
|
|
1719
1154
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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";
|
|
1723
1159
|
}
|
|
1724
1160
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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();
|
|
1730
1169
|
}
|
|
1731
1170
|
}
|
|
1732
1171
|
|
|
1733
1172
|
/************************************************************
|
|
1734
|
-
*
|
|
1735
|
-
*
|
|
1173
|
+
* Close request from the dialog X: if the form carries
|
|
1174
|
+
* unsaved changes ask first (EV_WINDOW_TO_CLOSE contract).
|
|
1736
1175
|
************************************************************/
|
|
1737
|
-
function
|
|
1176
|
+
function request_close_form_dialog(gobj)
|
|
1738
1177
|
{
|
|
1739
|
-
let
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
if(!is_field_required(conf, mode)) {
|
|
1750
|
-
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
|
+
});
|
|
1751
1188
|
return;
|
|
1752
1189
|
}
|
|
1753
|
-
|
|
1754
|
-
let value = get_field_value_for_validation($field, conf);
|
|
1755
|
-
if(is_field_value_empty(value)) {
|
|
1756
|
-
show_field_error($field, t('this field is required'));
|
|
1757
|
-
is_valid = false;
|
|
1758
|
-
if(!first_invalid) {
|
|
1759
|
-
first_invalid = $field;
|
|
1760
|
-
}
|
|
1761
|
-
} else {
|
|
1762
|
-
clear_field_error($field);
|
|
1763
|
-
}
|
|
1764
|
-
});
|
|
1765
|
-
|
|
1766
|
-
if(first_invalid) {
|
|
1767
|
-
let $focusable = first_invalid.querySelector('input, select, textarea');
|
|
1768
|
-
if($focusable && $focusable.focus) {
|
|
1769
|
-
$focusable.focus();
|
|
1770
|
-
}
|
|
1771
1190
|
}
|
|
1772
|
-
|
|
1773
|
-
return is_valid;
|
|
1191
|
+
close_form_dialog(gobj);
|
|
1774
1192
|
}
|
|
1775
1193
|
|
|
1776
1194
|
/************************************************************
|
|
1777
|
-
*
|
|
1195
|
+
* Destroy the hosted form and remove the dialog DOM.
|
|
1778
1196
|
************************************************************/
|
|
1779
|
-
function
|
|
1197
|
+
function close_form_dialog(gobj)
|
|
1780
1198
|
{
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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
|
+
}
|
|
1784
1213
|
}
|
|
1785
1214
|
|
|
1786
1215
|
/************************************************************
|
|
1787
|
-
*
|
|
1788
|
-
* set form state "update" or "create"
|
|
1789
|
-
* Only the "id" field is modified,
|
|
1790
|
-
* in "update" is readonly
|
|
1791
|
-
* in "create" is writable
|
|
1792
|
-
* Although by default the pkey (primary key) is "id",
|
|
1793
|
-
* use always desc.pkey value, who knows all cases!
|
|
1216
|
+
* True if the topic's pkey col carries the "rowid" flag.
|
|
1794
1217
|
************************************************************/
|
|
1795
|
-
function
|
|
1218
|
+
function pkey_is_rowid(gobj)
|
|
1796
1219
|
{
|
|
1797
1220
|
let desc = gobj_read_attr(gobj, "desc");
|
|
1798
1221
|
let col_pkey = kwid_find_one_record(
|
|
1799
1222
|
gobj,
|
|
1800
1223
|
desc.cols, // kw
|
|
1801
|
-
null,
|
|
1802
|
-
{
|
|
1224
|
+
null, // ids
|
|
1225
|
+
{ // jn_filter
|
|
1803
1226
|
"id": desc.pkey
|
|
1804
1227
|
}
|
|
1805
1228
|
);
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
let $id = $form.querySelector(`[name="${desc.pkey}"]`);
|
|
1809
|
-
|
|
1810
|
-
if($id) {
|
|
1811
|
-
if(mode === "update") {
|
|
1812
|
-
/*
|
|
1813
|
-
* update mode
|
|
1814
|
-
*/
|
|
1815
|
-
$id.setAttribute('readonly', 'readonly');
|
|
1816
|
-
$id.removeAttribute('required');
|
|
1817
|
-
} else {
|
|
1818
|
-
/*
|
|
1819
|
-
* create mode
|
|
1820
|
-
*/
|
|
1821
|
-
if(is_rowid) {
|
|
1822
|
-
$id.setAttribute('readonly', 'readonly');
|
|
1823
|
-
$id.removeAttribute('required');
|
|
1824
|
-
} else {
|
|
1825
|
-
$id.removeAttribute('readonly');
|
|
1826
|
-
$id.setAttribute('required', '');
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
} else {
|
|
1830
|
-
log_error(sprintf("%s: form without id field ('%s')",
|
|
1831
|
-
gobj_short_name(gobj),
|
|
1832
|
-
desc.pkey
|
|
1833
|
-
));
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
if(is_rowid) {
|
|
1837
|
-
mode = "create";
|
|
1229
|
+
if(!col_pkey) {
|
|
1230
|
+
return false;
|
|
1838
1231
|
}
|
|
1839
|
-
|
|
1840
|
-
$form.__mode__ = mode;
|
|
1841
|
-
$form.__index__ = index;
|
|
1232
|
+
return str_in_list(col_pkey.flag, "rowid");
|
|
1842
1233
|
}
|
|
1843
1234
|
|
|
1844
1235
|
/************************************************************
|
|
@@ -1847,35 +1238,10 @@ function set_form_mode(gobj, $form, mode, index)
|
|
|
1847
1238
|
************************************************************/
|
|
1848
1239
|
function show_edit_form(gobj, row, index)
|
|
1849
1240
|
{
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
if(!$element) {
|
|
1855
|
-
$element = build_topic_modal(gobj);
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
let $form = document.getElementById(form_id);
|
|
1859
|
-
clear_validation(gobj,$form);
|
|
1860
|
-
|
|
1861
|
-
if(json_size(row) === 0 || // WARNING _check_box_state_ widely used
|
|
1862
|
-
json_size(row) === 1 && ('_check_box_state_' in row)) {
|
|
1863
|
-
/*
|
|
1864
|
-
* WARNING it won't pass by here,
|
|
1865
|
-
* "create" is call directly from Top New button
|
|
1866
|
-
*/
|
|
1867
|
-
set_form_mode(gobj, $form, "create", index);
|
|
1868
|
-
} else {
|
|
1869
|
-
set_form_mode(gobj, $form, "update", index);
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
set_form_values(gobj, $form, row);
|
|
1873
|
-
|
|
1874
|
-
$element.classList.add('is-active');
|
|
1875
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
1876
|
-
if($with_focus) {
|
|
1877
|
-
$with_focus.focus();
|
|
1878
|
-
}
|
|
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);
|
|
1879
1245
|
}
|
|
1880
1246
|
|
|
1881
1247
|
/************************************************************
|
|
@@ -1884,24 +1250,7 @@ function show_edit_form(gobj, row, index)
|
|
|
1884
1250
|
************************************************************/
|
|
1885
1251
|
function show_create_form(gobj, row)
|
|
1886
1252
|
{
|
|
1887
|
-
|
|
1888
|
-
let form_id = gobj_read_str_attr(gobj, "form_id");
|
|
1889
|
-
|
|
1890
|
-
let $element = document.getElementById(popup_id);
|
|
1891
|
-
if(!$element) {
|
|
1892
|
-
$element = build_topic_modal(gobj);
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
let $form = document.getElementById(form_id);
|
|
1896
|
-
clear_validation(gobj,$form);
|
|
1897
|
-
set_form_mode(gobj, $form, "create", -1);
|
|
1898
|
-
set_form_values(gobj, $form, row);
|
|
1899
|
-
|
|
1900
|
-
$element.classList.add('is-active');
|
|
1901
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
1902
|
-
if($with_focus) {
|
|
1903
|
-
$with_focus.focus();
|
|
1904
|
-
}
|
|
1253
|
+
open_form_dialog(gobj, "create", row);
|
|
1905
1254
|
}
|
|
1906
1255
|
|
|
1907
1256
|
/************************************************************
|
|
@@ -1919,282 +1268,6 @@ function get_schema_col(gobj, id)
|
|
|
1919
1268
|
return null;
|
|
1920
1269
|
}
|
|
1921
1270
|
|
|
1922
|
-
/************************************************************
|
|
1923
|
-
*
|
|
1924
|
-
************************************************************/
|
|
1925
|
-
function clear_data(gobj, $form)
|
|
1926
|
-
{
|
|
1927
|
-
$form.querySelectorAll('input, select, textarea, .jsoneditor').forEach(input => {
|
|
1928
|
-
if(input.name !== 'id') {
|
|
1929
|
-
if (input.classList.contains('jsoneditor')) {
|
|
1930
|
-
let jsoneditor = input.jsoneditor;
|
|
1931
|
-
if(jsoneditor) {
|
|
1932
|
-
jsoneditor.set({text: ''});
|
|
1933
|
-
}
|
|
1934
|
-
} else if (input.type === 'checkbox') {
|
|
1935
|
-
input.checked = false;
|
|
1936
|
-
} else if (input.type === 'radio') {
|
|
1937
|
-
input.checked = false;
|
|
1938
|
-
|
|
1939
|
-
} else if (input.type === 'datetime-local') { /* time */
|
|
1940
|
-
input.value = null;
|
|
1941
|
-
|
|
1942
|
-
} else if (input.multiple) {
|
|
1943
|
-
if(input.tomselect) {
|
|
1944
|
-
input.tomselect.clear();
|
|
1945
|
-
}
|
|
1946
|
-
} else {
|
|
1947
|
-
input.value = null;
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
});
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
/************************************************************
|
|
1954
|
-
*
|
|
1955
|
-
************************************************************/
|
|
1956
|
-
function get_form_values($form)
|
|
1957
|
-
{
|
|
1958
|
-
const values = {};
|
|
1959
|
-
// Process all form elements
|
|
1960
|
-
$form.querySelectorAll('input, select, textarea, .jsoneditor').forEach(input => {
|
|
1961
|
-
if (input.classList.contains('jsoneditor')) {
|
|
1962
|
-
let jsoneditor = input.jsoneditor;
|
|
1963
|
-
if(jsoneditor) {
|
|
1964
|
-
let name = input.getAttribute('name');
|
|
1965
|
-
let value = jsoneditor.get();
|
|
1966
|
-
if(value.text===undefined) {
|
|
1967
|
-
values[name] = JSON.stringify(value.json, null, null);
|
|
1968
|
-
} else {
|
|
1969
|
-
values[name] = value.text;
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
} else if (input.type === 'checkbox') {
|
|
1973
|
-
// Handle single checkbox with true/false values
|
|
1974
|
-
values[input.name] = input.checked;
|
|
1975
|
-
} else if (input.type === 'radio') {
|
|
1976
|
-
// Handle radio buttons
|
|
1977
|
-
if (input.checked) {
|
|
1978
|
-
values[input.name] = input.value;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
} else if (input.type === 'datetime-local') { /* time */
|
|
1982
|
-
// Create a new Date object from the datetime-local input value
|
|
1983
|
-
const date = new Date(input.value);
|
|
1984
|
-
|
|
1985
|
-
// Convert the Date object to epoch time (in seconds)
|
|
1986
|
-
const epochTime = Math.floor(date.getTime() / 1000);
|
|
1987
|
-
values[input.name] = epochTime;
|
|
1988
|
-
|
|
1989
|
-
} else if (input.multiple) {
|
|
1990
|
-
// Handle multi-select with Select2
|
|
1991
|
-
// values[input.name] = $(input).val();
|
|
1992
|
-
|
|
1993
|
-
// Handle multi-select with Tom-Select
|
|
1994
|
-
if (input.tomselect) {
|
|
1995
|
-
values[input.name] = input.tomselect.getValue(); // Returns an array of selected values
|
|
1996
|
-
}
|
|
1997
|
-
} else {
|
|
1998
|
-
// Handle other input types (text, email, number, select, textarea)
|
|
1999
|
-
values[input.name] = input.value;
|
|
2000
|
-
}
|
|
2001
|
-
});
|
|
2002
|
-
|
|
2003
|
-
return values;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
/************************************************************
|
|
2007
|
-
* Convert a record column value from backend to frontend
|
|
2008
|
-
************************************************************/
|
|
2009
|
-
function epochToDateTimeLocal(epochTime)
|
|
2010
|
-
{
|
|
2011
|
-
const date = new Date(epochTime * 1000); // Convert to milliseconds
|
|
2012
|
-
const year = date.getFullYear();
|
|
2013
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
2014
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
2015
|
-
const hours = String(date.getHours()).padStart(2, '0');
|
|
2016
|
-
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
2017
|
-
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
2018
|
-
|
|
2019
|
-
// TODO for 'datetime-local' input, there is no seconds! we must use another library
|
|
2020
|
-
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
/************************************************************
|
|
2024
|
-
*
|
|
2025
|
-
************************************************************/
|
|
2026
|
-
function set_form_values(gobj, form, values)
|
|
2027
|
-
{
|
|
2028
|
-
Object.keys(values).forEach(key => {
|
|
2029
|
-
let element = form.querySelector(`[name="${key}"]`);
|
|
2030
|
-
if (element) {
|
|
2031
|
-
let value = values[key];
|
|
2032
|
-
let col = get_schema_col(gobj, key);
|
|
2033
|
-
if(col) {
|
|
2034
|
-
value = transform__treedb_value_2_form_value(gobj, col, value);
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
switch (element.type) {
|
|
2038
|
-
case 'select':
|
|
2039
|
-
element.value = value;
|
|
2040
|
-
break;
|
|
2041
|
-
|
|
2042
|
-
case 'select-one':
|
|
2043
|
-
element.value = value;
|
|
2044
|
-
break;
|
|
2045
|
-
|
|
2046
|
-
case 'select-multiple':
|
|
2047
|
-
// Handle multi-select with Select2
|
|
2048
|
-
//$(element).val(value).trigger('change');
|
|
2049
|
-
|
|
2050
|
-
// Handle multi-select with Tom-Select
|
|
2051
|
-
if (element.tomselect) {
|
|
2052
|
-
element.tomselect.setValue(value);
|
|
2053
|
-
}
|
|
2054
|
-
break;
|
|
2055
|
-
|
|
2056
|
-
case 'radio':
|
|
2057
|
-
// TODO review
|
|
2058
|
-
let radio = form.querySelector(`input[name="${key}"][value="${values[key]}"]`);
|
|
2059
|
-
if (radio) {
|
|
2060
|
-
radio.checked = true;
|
|
2061
|
-
}
|
|
2062
|
-
break;
|
|
2063
|
-
|
|
2064
|
-
case 'checkbox':
|
|
2065
|
-
element.checked = value; // Assume the value is true or false
|
|
2066
|
-
break;
|
|
2067
|
-
|
|
2068
|
-
case 'text':
|
|
2069
|
-
case 'textarea':
|
|
2070
|
-
case 'email':
|
|
2071
|
-
element.value = value;
|
|
2072
|
-
break;
|
|
2073
|
-
|
|
2074
|
-
case 'datetime-local': /* time */
|
|
2075
|
-
element.value = epochToDateTimeLocal(value);
|
|
2076
|
-
break;
|
|
2077
|
-
|
|
2078
|
-
default:
|
|
2079
|
-
if(element.classList.contains('jsoneditor')) {
|
|
2080
|
-
let jsoneditor = element.jsoneditor;
|
|
2081
|
-
if(jsoneditor) {
|
|
2082
|
-
jsoneditor.set(value);
|
|
2083
|
-
}
|
|
2084
|
-
break;
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
element.value = value;
|
|
2088
|
-
log_error(
|
|
2089
|
-
sprintf("%s: form type unknown: '%s'",
|
|
2090
|
-
gobj_short_name(gobj),
|
|
2091
|
-
element.type
|
|
2092
|
-
)
|
|
2093
|
-
);
|
|
2094
|
-
break;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
});
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
/************************************************************
|
|
2101
|
-
* Convert a record column value from backend
|
|
2102
|
-
* to frontend form field
|
|
2103
|
-
************************************************************/
|
|
2104
|
-
function transform__treedb_value_2_form_value(gobj, col, value)
|
|
2105
|
-
{
|
|
2106
|
-
const field_desc = treedb_get_field_desc(col);
|
|
2107
|
-
|
|
2108
|
-
switch(field_desc.type) {
|
|
2109
|
-
case "string":
|
|
2110
|
-
case "email": // string subtype
|
|
2111
|
-
case "tel": // string subtype
|
|
2112
|
-
case "url": // string subtype
|
|
2113
|
-
break;
|
|
2114
|
-
case "integer":
|
|
2115
|
-
break;
|
|
2116
|
-
case "real":
|
|
2117
|
-
break;
|
|
2118
|
-
case "boolean":
|
|
2119
|
-
break;
|
|
2120
|
-
|
|
2121
|
-
case "object":
|
|
2122
|
-
case "dict":
|
|
2123
|
-
case "template":
|
|
2124
|
-
case "array":
|
|
2125
|
-
case "list":
|
|
2126
|
-
case "coordinates":
|
|
2127
|
-
case "blob":
|
|
2128
|
-
// Return as it is, manage by jsoneditor
|
|
2129
|
-
value = {json: value};
|
|
2130
|
-
// value = {text: JSON.stringify(value)}; // other way
|
|
2131
|
-
break;
|
|
2132
|
-
|
|
2133
|
-
case "enum":
|
|
2134
|
-
switch(field_desc.real_type) {
|
|
2135
|
-
case "string":
|
|
2136
|
-
break;
|
|
2137
|
-
case "object":
|
|
2138
|
-
case "dict":
|
|
2139
|
-
case "array":
|
|
2140
|
-
case "list":
|
|
2141
|
-
break;
|
|
2142
|
-
}
|
|
2143
|
-
break;
|
|
2144
|
-
|
|
2145
|
-
case "now":
|
|
2146
|
-
case "time":
|
|
2147
|
-
switch(field_desc.real_type) {
|
|
2148
|
-
case "string":
|
|
2149
|
-
case "integer":
|
|
2150
|
-
value = parseInt(value) || 0;
|
|
2151
|
-
value = new Date(value);
|
|
2152
|
-
break;
|
|
2153
|
-
}
|
|
2154
|
-
break;
|
|
2155
|
-
|
|
2156
|
-
case "color":
|
|
2157
|
-
switch(field_desc.real_type) {
|
|
2158
|
-
case "string":
|
|
2159
|
-
// TODO
|
|
2160
|
-
break;
|
|
2161
|
-
case "integer":
|
|
2162
|
-
// TODO
|
|
2163
|
-
break;
|
|
2164
|
-
}
|
|
2165
|
-
break;
|
|
2166
|
-
|
|
2167
|
-
case "hook": // Convert data from backend to frontend FORM FIELD
|
|
2168
|
-
// WARNING hook fields are not writable and by now they are not showed in forms.
|
|
2169
|
-
break;
|
|
2170
|
-
|
|
2171
|
-
case "fkey": // Convert data from backend to frontend FORM FIELD
|
|
2172
|
-
let new_value = [];
|
|
2173
|
-
if(value) {
|
|
2174
|
-
if(is_string(value)) {
|
|
2175
|
-
let fkey = treedb_decoder_fkey(col, value);
|
|
2176
|
-
if(fkey) {
|
|
2177
|
-
new_value.push(fkey.id);
|
|
2178
|
-
}
|
|
2179
|
-
} else if(is_array(value)) {
|
|
2180
|
-
for(let i=0; i<value.length; i++) {
|
|
2181
|
-
let fkey = treedb_decoder_fkey(col, value[i]);
|
|
2182
|
-
if(fkey) {
|
|
2183
|
-
new_value.push(fkey.id);
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
} else {
|
|
2187
|
-
log_error("fkey type unsupported: " + JSON.stringify(value));
|
|
2188
|
-
}
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
value = new_value;
|
|
2192
|
-
break;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
return value;
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
1271
|
/************************************************************
|
|
2199
1272
|
* Convert from frontend to backend
|
|
2200
1273
|
* operation: "create" "update"
|
|
@@ -2374,24 +1447,6 @@ function transform__form_value_2_treedb_value(gobj, col, value, operation)
|
|
|
2374
1447
|
return value;
|
|
2375
1448
|
}
|
|
2376
1449
|
|
|
2377
|
-
/************************************************************
|
|
2378
|
-
*
|
|
2379
|
-
************************************************************/
|
|
2380
|
-
function get_fkey_options(gobj, col)
|
|
2381
|
-
{
|
|
2382
|
-
for(let topic_name of Object.keys(col.fkey)) {
|
|
2383
|
-
// HACK we work only with one fkey
|
|
2384
|
-
let kw = {
|
|
2385
|
-
topic_name: topic_name
|
|
2386
|
-
};
|
|
2387
|
-
let webix = gobj_command(gobj_parent(gobj), "get_topic_data", kw, gobj);
|
|
2388
|
-
let current_list = webix.data;
|
|
2389
|
-
return list2options(current_list, "id", "id");
|
|
2390
|
-
}
|
|
2391
|
-
log_error("No fkey options found" + JSON.stringify(col));
|
|
2392
|
-
return null;
|
|
2393
|
-
}
|
|
2394
|
-
|
|
2395
1450
|
/************************************************************
|
|
2396
1451
|
*
|
|
2397
1452
|
************************************************************/
|
|
@@ -2931,58 +1986,29 @@ function ac_paste_rows(gobj, event, kw, src)
|
|
|
2931
1986
|
}
|
|
2932
1987
|
|
|
2933
1988
|
/************************************************************
|
|
2934
|
-
*
|
|
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.
|
|
2935
1992
|
************************************************************/
|
|
2936
|
-
function
|
|
1993
|
+
function ac_form_save_record(gobj, event, kw, src)
|
|
2937
1994
|
{
|
|
2938
|
-
let
|
|
2939
|
-
try {
|
|
2940
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "create");
|
|
2941
|
-
} catch (e) {
|
|
2942
|
-
log_warning(e);
|
|
2943
|
-
throw e;
|
|
2944
|
-
}
|
|
2945
|
-
|
|
1995
|
+
let mode = gobj_read_str_attr(src, "form_mode");
|
|
2946
1996
|
gobj_publish_event(
|
|
2947
1997
|
gobj,
|
|
2948
|
-
"EV_CREATE_RECORD",
|
|
1998
|
+
(mode === "create")? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD",
|
|
2949
1999
|
{
|
|
2950
2000
|
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
2951
|
-
record:
|
|
2001
|
+
record: kw
|
|
2952
2002
|
}
|
|
2953
2003
|
);
|
|
2954
2004
|
|
|
2955
|
-
/*
|
|
2956
|
-
*
|
|
2957
|
-
*/
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
/************************************************************
|
|
2962
|
-
* From internal Save button of "update" modal form
|
|
2963
|
-
************************************************************/
|
|
2964
|
-
function ac_update_record(gobj, event, kw, src)
|
|
2965
|
-
{
|
|
2966
|
-
let new_kw = null;
|
|
2967
|
-
try {
|
|
2968
|
-
new_kw = transform__form_record_2_treedb_record(gobj, kw, "update");
|
|
2969
|
-
} catch (e) {
|
|
2970
|
-
log_warning(e);
|
|
2971
|
-
throw e;
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
gobj_publish_event(
|
|
2975
|
-
gobj,
|
|
2976
|
-
"EV_UPDATE_RECORD",
|
|
2977
|
-
{
|
|
2978
|
-
topic_name: gobj_read_str_attr(gobj, "topic_name"),
|
|
2979
|
-
record: new_kw
|
|
2980
|
-
}
|
|
2981
|
-
);
|
|
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);
|
|
2982
2011
|
|
|
2983
|
-
/*
|
|
2984
|
-
* Return -1 will not close the form
|
|
2985
|
-
*/
|
|
2986
2012
|
return 0;
|
|
2987
2013
|
}
|
|
2988
2014
|
|
|
@@ -3161,8 +2187,7 @@ function create_gclass(gclass_name)
|
|
|
3161
2187
|
["EV_NODE_DELETED", ac_node_deleted, null],
|
|
3162
2188
|
|
|
3163
2189
|
["EV_EDITION_MODE", ac_edition_mode, null],
|
|
3164
|
-
["
|
|
3165
|
-
["EV_UPDATE_RECORD", ac_update_record, null],
|
|
2190
|
+
["EV_SAVE_RECORD", ac_form_save_record, null],
|
|
3166
2191
|
|
|
3167
2192
|
["EV_NEW_ROW", ac_new_row, null],
|
|
3168
2193
|
["EV_DELETE_ROWS", ac_delete_rows, null],
|
|
@@ -3188,6 +2213,7 @@ function create_gclass(gclass_name)
|
|
|
3188
2213
|
["EV_NODE_DELETED", 0],
|
|
3189
2214
|
|
|
3190
2215
|
["EV_EDITION_MODE", 0],
|
|
2216
|
+
["EV_SAVE_RECORD", 0],
|
|
3191
2217
|
["EV_NEW_ROW", 0],
|
|
3192
2218
|
["EV_DELETE_ROWS", 0],
|
|
3193
2219
|
["EV_COPY_ROWS", 0],
|
|
@@ -3233,6 +2259,11 @@ function create_gclass(gclass_name)
|
|
|
3233
2259
|
***************************************************************/
|
|
3234
2260
|
function register_c_yui_treedb_topic_with_form()
|
|
3235
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
|
+
}
|
|
3236
2267
|
return create_gclass(GCLASS_NAME);
|
|
3237
2268
|
}
|
|
3238
2269
|
|