@yuneta/gobj-ui 7.23.161 → 7.23.163
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 +69 -0
- package/dist/gobj-ui.cjs.js +341 -97
- package/dist/gobj-ui.es.js +342 -98
- package/package.json +1 -1
- package/src/c_yui_treedb_topic_with_form.css +30 -0
- package/src/c_yui_treedb_topic_with_form.js +296 -101
- package/src/c_yui_treedb_topics.js +61 -7
- package/src/delete_impact.js +7 -0
- package/src/delete_impact.test.js +8 -0
|
@@ -258,6 +258,10 @@ let PRIVATE_DATA = {
|
|
|
258
258
|
cell_file_win: null, // C_YUI_WINDOW presenting it (laptop)
|
|
259
259
|
_pending_assets: null, // req_id -> {target, col, id, slot, timer}
|
|
260
260
|
_asset_seq: 0, // correlation id of an asset request
|
|
261
|
+
parent_filter: null, // {fkey, parent_topic, parent_id, hook}: rows linked to one record
|
|
262
|
+
search_term: "", // the search box, lower-cased ("" = none)
|
|
263
|
+
$parent_filter: null, // the chip that says the parent filter is on
|
|
264
|
+
hook_choice_modal: null, // { close } of "which child topic?" (a hook with several)
|
|
261
265
|
};
|
|
262
266
|
|
|
263
267
|
let __gclass__ = null;
|
|
@@ -459,6 +463,7 @@ function mt_stop(gobj)
|
|
|
459
463
|
close_schema_dialog(gobj);
|
|
460
464
|
close_cell_json_dialog(gobj);
|
|
461
465
|
close_cell_file_dialog(gobj);
|
|
466
|
+
close_hook_choice(gobj);
|
|
462
467
|
drop_pending_assets(gobj);
|
|
463
468
|
table__destroy(gobj);
|
|
464
469
|
}
|
|
@@ -476,6 +481,7 @@ function mt_destroy(gobj)
|
|
|
476
481
|
close_schema_dialog(gobj);
|
|
477
482
|
close_cell_json_dialog(gobj);
|
|
478
483
|
close_cell_file_dialog(gobj);
|
|
484
|
+
close_hook_choice(gobj);
|
|
479
485
|
drop_pending_assets(gobj);
|
|
480
486
|
destroy_ui(gobj);
|
|
481
487
|
}
|
|
@@ -898,6 +904,31 @@ function build_ui(gobj)
|
|
|
898
904
|
$container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
|
|
899
905
|
}
|
|
900
906
|
|
|
907
|
+
/* The parent filter, when a hook of another row opened this table
|
|
908
|
+
* (EV_FILTER_BY_PARENT). Hidden until then. The topic is a KEY (it is
|
|
909
|
+
* translated like the tab that names it) and the id is DATA. */
|
|
910
|
+
let $parent_filter = createElement2(
|
|
911
|
+
['div', {class: 'TREEDB_PARENT_FILTER is-hidden', style: 'flex:none;'}, [
|
|
912
|
+
['span', {class: 'PARENT_FILTER_LABEL', i18n: 'filtered by'}, 'filtered by'],
|
|
913
|
+
['span', {class: 'PARENT_FILTER_TOPIC'}, ''],
|
|
914
|
+
['strong', {class: 'PARENT_FILTER_ID'}, ''],
|
|
915
|
+
['button', {
|
|
916
|
+
class: 'delete PARENT_FILTER_CLEAR',
|
|
917
|
+
type: 'button',
|
|
918
|
+
title: t('clear filter'),
|
|
919
|
+
'data-i18n-title': 'clear filter',
|
|
920
|
+
'aria-label': t('clear filter'),
|
|
921
|
+
'data-i18n-aria-label': 'clear filter'
|
|
922
|
+
}, [], {
|
|
923
|
+
click: () => {
|
|
924
|
+
gobj_send_event(gobj, "EV_CLEAR_PARENT_FILTER", {}, gobj);
|
|
925
|
+
}
|
|
926
|
+
}]
|
|
927
|
+
]]
|
|
928
|
+
);
|
|
929
|
+
gobj.priv.$parent_filter = $parent_filter;
|
|
930
|
+
$container.insertBefore($parent_filter, $container.querySelector(`#${table_id}`));
|
|
931
|
+
|
|
901
932
|
let $toolbar_slot = $container.querySelector('.toolbar_tabulator_table');
|
|
902
933
|
if($table_toolbar instanceof Element) {
|
|
903
934
|
$toolbar_slot.appendChild($table_toolbar);
|
|
@@ -1741,6 +1772,7 @@ function create_tabulator(gobj)
|
|
|
1741
1772
|
* one row. */
|
|
1742
1773
|
yui_tabulator_name_row_selects(tabulator, t);
|
|
1743
1774
|
tabulator._ready = true;
|
|
1775
|
+
apply_row_filters(gobj);
|
|
1744
1776
|
update_rowcount();
|
|
1745
1777
|
/* La ✕ de cada filtro de cabecera. Un filtro se quita borrando lo
|
|
1746
1778
|
* escrito, y con varias columnas filtradas volver a la tabla entera
|
|
@@ -2031,8 +2063,14 @@ function transform__treedb_value_2_table_value(gobj, col, value, row, field)
|
|
|
2031
2063
|
let items = treedb_hook_data_size(value);
|
|
2032
2064
|
|
|
2033
2065
|
if(items > 0) {
|
|
2066
|
+
/* An HTML STRING, so the name is escaped by hand; a language
|
|
2067
|
+
* change re-runs setColumns(), which re-runs this. */
|
|
2068
|
+
let hook_title = String(t("show linked records"))
|
|
2069
|
+
.replace(/&/g, "&").replace(/"/g, """)
|
|
2070
|
+
.replace(/</g, "<").replace(/>/g, ">");
|
|
2034
2071
|
value = [
|
|
2035
2072
|
'<a class="hook_cell" ',
|
|
2073
|
+
`title="${hook_title}" aria-label="${hook_title}" `,
|
|
2036
2074
|
`data-row_id="${row.id}" `,
|
|
2037
2075
|
`data-col_id="${col.id}" > `,
|
|
2038
2076
|
'<span style="" class="icon yi-eye"></span>',
|
|
@@ -2892,6 +2930,181 @@ function close_cell_file_dialog(gobj)
|
|
|
2892
2930
|
teardown_cell_file(gobj);
|
|
2893
2931
|
}
|
|
2894
2932
|
|
|
2933
|
+
/************************************************************
|
|
2934
|
+
* Ask the host to open the rows of `topic_name` whose `fkey`
|
|
2935
|
+
* names this row -- what a hook of this row links.
|
|
2936
|
+
************************************************************/
|
|
2937
|
+
function publish_open_linked(gobj, topic_name, fkey, parent_id, hook)
|
|
2938
|
+
{
|
|
2939
|
+
gobj_publish_event(gobj, "EV_OPEN_LINKED", {
|
|
2940
|
+
topic_name: topic_name,
|
|
2941
|
+
fkey: fkey,
|
|
2942
|
+
parent_topic: gobj_read_str_attr(gobj, "topic_name"),
|
|
2943
|
+
parent_id: parent_id,
|
|
2944
|
+
hook: hook
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
/************************************************************
|
|
2949
|
+
* "Which child topic?" -- for a hook whose children live in
|
|
2950
|
+
* more than one. The standardized dialog, so Escape, Back and
|
|
2951
|
+
* a click outside close it like every other one.
|
|
2952
|
+
************************************************************/
|
|
2953
|
+
function open_hook_choice_dialog(gobj, targets, parent_id, hook)
|
|
2954
|
+
{
|
|
2955
|
+
close_hook_choice(gobj);
|
|
2956
|
+
let priv = gobj.priv;
|
|
2957
|
+
let shell = yui_shell_of(gobj);
|
|
2958
|
+
if(!shell) {
|
|
2959
|
+
log_error(`${gobj_short_name(gobj)}: no shell, cannot ask which topic to open`);
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
/* The topic names are KEYS, translated like the tabs that name them. */
|
|
2963
|
+
let $body = createElement2(
|
|
2964
|
+
['div', {class: 'TREEDB_HOOK_CHOICE'}, targets.map((tg) => {
|
|
2965
|
+
return ['button', {
|
|
2966
|
+
class: 'button HOOK_CHOICE_TOPIC',
|
|
2967
|
+
type: 'button',
|
|
2968
|
+
title: t(tg.topic_name),
|
|
2969
|
+
'data-i18n-title': tg.topic_name,
|
|
2970
|
+
'aria-label': t(tg.topic_name),
|
|
2971
|
+
'data-i18n-aria-label': tg.topic_name
|
|
2972
|
+
}, [
|
|
2973
|
+
['span', {i18n: tg.topic_name}, tg.topic_name]
|
|
2974
|
+
], {
|
|
2975
|
+
click: () => {
|
|
2976
|
+
gobj_send_event(gobj, "EV_CHOOSE_LINKED", {
|
|
2977
|
+
topic_name: tg.topic_name,
|
|
2978
|
+
fkey: tg.fkey,
|
|
2979
|
+
parent_id: parent_id,
|
|
2980
|
+
hook: hook
|
|
2981
|
+
}, gobj);
|
|
2982
|
+
}
|
|
2983
|
+
}];
|
|
2984
|
+
})]
|
|
2985
|
+
);
|
|
2986
|
+
priv.hook_choice_modal = yui_shell_show_modal(shell, $body, {
|
|
2987
|
+
dialog: true,
|
|
2988
|
+
logical_class: "TREEDB_HOOK_CHOICE_SHEET",
|
|
2989
|
+
title: "choose a topic",
|
|
2990
|
+
t: t,
|
|
2991
|
+
on_close: function() {
|
|
2992
|
+
priv.hook_choice_modal = null;
|
|
2993
|
+
}
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
/************************************************************
|
|
2998
|
+
* Close "which child topic?" if it is open.
|
|
2999
|
+
************************************************************/
|
|
3000
|
+
function close_hook_choice(gobj)
|
|
3001
|
+
{
|
|
3002
|
+
let priv = gobj.priv;
|
|
3003
|
+
if(priv.hook_choice_modal) {
|
|
3004
|
+
let modal = priv.hook_choice_modal;
|
|
3005
|
+
priv.hook_choice_modal = null;
|
|
3006
|
+
modal.close();
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
/************************************************************
|
|
3011
|
+
* Does this row's `pf.fkey` name the parent record? The fkey
|
|
3012
|
+
* arrives as one ref, a list of them or a dict keyed by ref,
|
|
3013
|
+
* each in any of the shapes treedb_decoder_fkey() reads. The
|
|
3014
|
+
* topic and the hook are compared when the ref carries them:
|
|
3015
|
+
* one row can hang from the same parent through two hooks.
|
|
3016
|
+
************************************************************/
|
|
3017
|
+
function row_links_to(gobj, data, pf)
|
|
3018
|
+
{
|
|
3019
|
+
let col = get_schema_col(gobj, pf.fkey);
|
|
3020
|
+
let v = data? data[pf.fkey] : null;
|
|
3021
|
+
if(!col || v === null || v === undefined || v === "") {
|
|
3022
|
+
return false;
|
|
3023
|
+
}
|
|
3024
|
+
let refs;
|
|
3025
|
+
if(is_array(v)) {
|
|
3026
|
+
refs = v;
|
|
3027
|
+
} else if(is_object(v) && v.id === undefined) {
|
|
3028
|
+
refs = Object.keys(v);
|
|
3029
|
+
} else {
|
|
3030
|
+
refs = [v];
|
|
3031
|
+
}
|
|
3032
|
+
for(let ref of refs) {
|
|
3033
|
+
if(ref === null || ref === undefined || ref === "") {
|
|
3034
|
+
continue;
|
|
3035
|
+
}
|
|
3036
|
+
let fk = treedb_decoder_fkey(col, ref);
|
|
3037
|
+
if(!fk || String(fk.id) !== pf.parent_id) {
|
|
3038
|
+
continue;
|
|
3039
|
+
}
|
|
3040
|
+
if(fk.topic_name && pf.parent_topic && fk.topic_name !== pf.parent_topic) {
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
if(fk.hook_name && pf.hook && fk.hook_name !== pf.hook) {
|
|
3044
|
+
continue;
|
|
3045
|
+
}
|
|
3046
|
+
return true;
|
|
3047
|
+
}
|
|
3048
|
+
return false;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
/************************************************************
|
|
3052
|
+
* The ONE programmatic filter of the table: the parent filter
|
|
3053
|
+
* AND the search term. Kept as state and applied here, because
|
|
3054
|
+
* Tabulator's setFilter() replaces every programmatic filter.
|
|
3055
|
+
* Before `tableBuilt` the state waits; tableBuilt applies it.
|
|
3056
|
+
************************************************************/
|
|
3057
|
+
function apply_row_filters(gobj)
|
|
3058
|
+
{
|
|
3059
|
+
let priv = gobj.priv;
|
|
3060
|
+
let tabulator = gobj_read_attr(gobj, "tabulator");
|
|
3061
|
+
if(!tabulator) {
|
|
3062
|
+
log_error(`${gobj_short_name(gobj)}: no table to filter`);
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
if(!tabulator._ready) {
|
|
3066
|
+
return; /* tableBuilt applies it */
|
|
3067
|
+
}
|
|
3068
|
+
let term = priv.search_term || "";
|
|
3069
|
+
let pf = priv.parent_filter;
|
|
3070
|
+
if(!term && !pf) {
|
|
3071
|
+
tabulator.clearFilter();
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
tabulator.setFilter(function(data) {
|
|
3075
|
+
if(pf && !row_links_to(gobj, data, pf)) {
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
/* A treedb row is not flat: an fkey arrives as a list of
|
|
3079
|
+
* objects. `row_matches()` walks into them and reads only the
|
|
3080
|
+
* `id` of an fkey. See yui_row_search.js. */
|
|
3081
|
+
return !term || row_matches(data, term);
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
/************************************************************
|
|
3086
|
+
* Show or hide the chip that says the parent filter is on.
|
|
3087
|
+
************************************************************/
|
|
3088
|
+
function render_parent_filter(gobj)
|
|
3089
|
+
{
|
|
3090
|
+
let priv = gobj.priv;
|
|
3091
|
+
let $chip = priv.$parent_filter;
|
|
3092
|
+
if(!$chip) {
|
|
3093
|
+
log_error(`${gobj_short_name(gobj)}: no parent-filter chip to paint`);
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
let pf = priv.parent_filter;
|
|
3097
|
+
if(!pf) {
|
|
3098
|
+
$chip.classList.add("is-hidden");
|
|
3099
|
+
return;
|
|
3100
|
+
}
|
|
3101
|
+
let $topic = $chip.querySelector(".PARENT_FILTER_TOPIC");
|
|
3102
|
+
$topic.setAttribute("data-i18n", pf.parent_topic);
|
|
3103
|
+
$topic.textContent = t(pf.parent_topic);
|
|
3104
|
+
$chip.querySelector(".PARENT_FILTER_ID").textContent = pf.parent_id;
|
|
3105
|
+
$chip.classList.remove("is-hidden");
|
|
3106
|
+
}
|
|
3107
|
+
|
|
2895
3108
|
/************************************************************
|
|
2896
3109
|
* True if the topic's pkey col carries the "rowid" flag.
|
|
2897
3110
|
************************************************************/
|
|
@@ -3302,84 +3515,6 @@ function build_fkey_ref(gobj, col, value)
|
|
|
3302
3515
|
return refs;
|
|
3303
3516
|
}
|
|
3304
3517
|
|
|
3305
|
-
/***************************************************************************
|
|
3306
|
-
* TODO con límite máximo o máximo height o con scroll
|
|
3307
|
-
* en un gobj propio para gestionar los datos en "page"s
|
|
3308
|
-
* que los hook no vengan rellenos si son muchos y que se puedan gestionar
|
|
3309
|
-
* con un gobj
|
|
3310
|
-
***************************************************************************/
|
|
3311
|
-
function show_dropdown_popup_menu(gobj, x, y, items, callback)
|
|
3312
|
-
{
|
|
3313
|
-
let $element = createElement2([
|
|
3314
|
-
'div', {class: 'dropdown popup' }, [
|
|
3315
|
-
['div', {
|
|
3316
|
-
class: 'dropdown-menu', role: 'menu', style: 'min-width:4rem; border: 2px solid var(--bulma-border); padding: 0px;'
|
|
3317
|
-
}, [
|
|
3318
|
-
['div', { class: 'dropdown-content', style: 'padding: 0;' }, []]
|
|
3319
|
-
]]
|
|
3320
|
-
], {
|
|
3321
|
-
'click': (evt) => {
|
|
3322
|
-
evt.stopPropagation();
|
|
3323
|
-
destroyModal();
|
|
3324
|
-
if(callback) {
|
|
3325
|
-
callback(evt);
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
]);
|
|
3330
|
-
|
|
3331
|
-
const destroyModal = () => {
|
|
3332
|
-
$element.classList.remove('is-active');
|
|
3333
|
-
$element.parentNode.removeChild($element);
|
|
3334
|
-
};
|
|
3335
|
-
|
|
3336
|
-
let $dropdown_content = $element.querySelector('.dropdown-content');
|
|
3337
|
-
|
|
3338
|
-
let ids = kwid_get_ids(gobj, items);
|
|
3339
|
-
for(let id of ids) {
|
|
3340
|
-
let $item = createElement2(
|
|
3341
|
-
['a', {class: 'dropdown-item flex-horizontal-section', 'data-value':`${id}`, style:'margin:0px;'}, [
|
|
3342
|
-
['span', {i18n: `${id}`}, `${id}`]
|
|
3343
|
-
], {
|
|
3344
|
-
'click': (evt) => {
|
|
3345
|
-
evt.stopPropagation();
|
|
3346
|
-
destroyModal();
|
|
3347
|
-
if(callback) {
|
|
3348
|
-
callback(evt, this.dataset.value);
|
|
3349
|
-
}
|
|
3350
|
-
}
|
|
3351
|
-
}]
|
|
3352
|
-
);
|
|
3353
|
-
$dropdown_content.appendChild($item);
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
refresh_language($element, t);
|
|
3357
|
-
|
|
3358
|
-
/*
|
|
3359
|
-
* Add to popup layer
|
|
3360
|
-
*/
|
|
3361
|
-
popup_mount_layer(gobj).appendChild($element);
|
|
3362
|
-
|
|
3363
|
-
/*
|
|
3364
|
-
* Set position
|
|
3365
|
-
*/
|
|
3366
|
-
$element.style.position = "absolute";
|
|
3367
|
-
$element.style.top = y + "px";
|
|
3368
|
-
$element.style.left = x + "px";
|
|
3369
|
-
|
|
3370
|
-
/*
|
|
3371
|
-
* Show
|
|
3372
|
-
*/
|
|
3373
|
-
$element.classList.add('is-active');
|
|
3374
|
-
|
|
3375
|
-
/*
|
|
3376
|
-
* Set focus
|
|
3377
|
-
*/
|
|
3378
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
3379
|
-
if($with_focus) {
|
|
3380
|
-
$with_focus.focus();
|
|
3381
|
-
}
|
|
3382
|
-
}
|
|
3383
3518
|
|
|
3384
3519
|
|
|
3385
3520
|
|
|
@@ -4117,17 +4252,78 @@ function ac_change_locale(gobj, event, kw, src)
|
|
|
4117
4252
|
************************************************************/
|
|
4118
4253
|
function ac_show_hook_data(gobj, event, kw, src)
|
|
4119
4254
|
{
|
|
4120
|
-
|
|
4255
|
+
/* The hook's mapping says where its children live: {child_topic:
|
|
4256
|
+
* fkey_col}. Opening that topic, filtered, replaced a popup listing
|
|
4257
|
+
* every child id -- 5675 rows in a box with no height, no scroll and
|
|
4258
|
+
* no way out but a click inside it, whose ids did nothing. */
|
|
4259
|
+
let col = get_schema_col(gobj, kw.col_id);
|
|
4260
|
+
let hook = (col && is_object(col.hook))? col.hook : null;
|
|
4261
|
+
if(!hook || kw.row_id === undefined || kw.row_id === null) {
|
|
4262
|
+
log_error(`${gobj_short_name(gobj)}: hook '${kw.col_id}' names no child topic to open`);
|
|
4263
|
+
return -1;
|
|
4264
|
+
}
|
|
4265
|
+
let targets = Object.keys(hook).map((topic) => {
|
|
4266
|
+
return {topic_name: topic, fkey: hook[topic]};
|
|
4267
|
+
});
|
|
4268
|
+
if(targets.length === 1) {
|
|
4269
|
+
publish_open_linked(gobj, targets[0].topic_name, targets[0].fkey,
|
|
4270
|
+
String(kw.row_id), kw.col_id);
|
|
4271
|
+
return 0;
|
|
4272
|
+
}
|
|
4273
|
+
open_hook_choice_dialog(gobj, targets, String(kw.row_id), kw.col_id);
|
|
4274
|
+
return 0;
|
|
4275
|
+
}
|
|
4121
4276
|
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4277
|
+
/************************************************************
|
|
4278
|
+
* A hook with several child topics: pick one.
|
|
4279
|
+
* {topic_name, fkey, parent_id, hook}
|
|
4280
|
+
************************************************************/
|
|
4281
|
+
function ac_choose_linked(gobj, event, kw, src)
|
|
4282
|
+
{
|
|
4283
|
+
close_hook_choice(gobj);
|
|
4284
|
+
publish_open_linked(gobj, kw.topic_name, kw.fkey, kw.parent_id, kw.hook);
|
|
4285
|
+
return 0;
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
/************************************************************
|
|
4289
|
+
* Show only the rows linked to one record of another topic.
|
|
4290
|
+
* Sent by the host when a hook of that record was clicked.
|
|
4291
|
+
* {fkey, parent_topic, parent_id, hook}
|
|
4292
|
+
*
|
|
4293
|
+
* Over the rows the table HOLDS: with `with_remote_paging` that
|
|
4294
|
+
* is one page, which is one of the reasons that flag stays off.
|
|
4295
|
+
************************************************************/
|
|
4296
|
+
function ac_filter_by_parent(gobj, event, kw, src)
|
|
4297
|
+
{
|
|
4298
|
+
let priv = gobj.priv;
|
|
4299
|
+
if(!kw || !kw.fkey || kw.parent_id === undefined || kw.parent_id === null) {
|
|
4300
|
+
log_error(`${gobj_short_name(gobj)}: a parent filter with no fkey or no parent`);
|
|
4301
|
+
return -1;
|
|
4129
4302
|
}
|
|
4303
|
+
if(!get_schema_col(gobj, kw.fkey)) {
|
|
4304
|
+
log_error(`${gobj_short_name(gobj)}: parent filter on '${kw.fkey}', ` +
|
|
4305
|
+
`which is not a column of '${gobj_read_str_attr(gobj, "topic_name")}'`);
|
|
4306
|
+
return -1;
|
|
4307
|
+
}
|
|
4308
|
+
priv.parent_filter = {
|
|
4309
|
+
fkey: kw.fkey,
|
|
4310
|
+
parent_topic: kw.parent_topic || "",
|
|
4311
|
+
parent_id: String(kw.parent_id),
|
|
4312
|
+
hook: kw.hook || ""
|
|
4313
|
+
};
|
|
4314
|
+
render_parent_filter(gobj);
|
|
4315
|
+
apply_row_filters(gobj);
|
|
4316
|
+
return 0;
|
|
4317
|
+
}
|
|
4130
4318
|
|
|
4319
|
+
/************************************************************
|
|
4320
|
+
* The ✕ of the parent filter: back to the whole topic.
|
|
4321
|
+
************************************************************/
|
|
4322
|
+
function ac_clear_parent_filter(gobj, event, kw, src)
|
|
4323
|
+
{
|
|
4324
|
+
gobj.priv.parent_filter = null;
|
|
4325
|
+
render_parent_filter(gobj);
|
|
4326
|
+
apply_row_filters(gobj);
|
|
4131
4327
|
return 0;
|
|
4132
4328
|
}
|
|
4133
4329
|
|
|
@@ -4479,20 +4675,12 @@ function ac_search(gobj, event, kw, src)
|
|
|
4479
4675
|
return -1;
|
|
4480
4676
|
}
|
|
4481
4677
|
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
/* A treedb row is not flat: an fkey arrives as a list of objects, and
|
|
4489
|
-
* `String()` of that is "[object Object]" -- so searching for the
|
|
4490
|
-
* workshop of a meter, which is where the value a person has in mind
|
|
4491
|
-
* lives, never found anything. `row_matches()` walks into them, and
|
|
4492
|
-
* reads only the `id` of an fkey. See yui_row_search.js. */
|
|
4493
|
-
tabulator.setFilter(function(data) {
|
|
4494
|
-
return row_matches(data, term);
|
|
4495
|
-
});
|
|
4678
|
+
/* Kept, and applied together with the parent filter: Tabulator's
|
|
4679
|
+
* setFilter() REPLACES every programmatic filter and clearFilter()
|
|
4680
|
+
* drops them all, so the search box used to wipe the parent filter
|
|
4681
|
+
* and the parent filter would have wiped the search. */
|
|
4682
|
+
gobj.priv.search_term = ((kw && kw.text) || "").toLowerCase();
|
|
4683
|
+
apply_row_filters(gobj);
|
|
4496
4684
|
|
|
4497
4685
|
return 0;
|
|
4498
4686
|
}
|
|
@@ -4726,6 +4914,9 @@ function create_gclass(gclass_name)
|
|
|
4726
4914
|
["EV_COPY_ROWS", ac_copy_rows, null],
|
|
4727
4915
|
["EV_PASTE_ROWS", ac_paste_rows, null],
|
|
4728
4916
|
["EV_SHOW_HOOK_DATA", ac_show_hook_data, null],
|
|
4917
|
+
["EV_CHOOSE_LINKED", ac_choose_linked, null],
|
|
4918
|
+
["EV_FILTER_BY_PARENT", ac_filter_by_parent, null],
|
|
4919
|
+
["EV_CLEAR_PARENT_FILTER", ac_clear_parent_filter, null],
|
|
4729
4920
|
["EV_SHOW_CELL_JSON", ac_show_cell_json, null],
|
|
4730
4921
|
["EV_SHOW_CELL_FILE", ac_show_cell_file, null],
|
|
4731
4922
|
["EV_ASSET_LOADED", ac_asset_loaded, null],
|
|
@@ -4770,7 +4961,11 @@ function create_gclass(gclass_name)
|
|
|
4770
4961
|
["EV_CLEAR_SELECTION", 0],
|
|
4771
4962
|
["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
|
|
4772
4963
|
["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
|
|
4773
|
-
["EV_SHOW_HOOK_DATA",
|
|
4964
|
+
["EV_SHOW_HOOK_DATA", 0],
|
|
4965
|
+
["EV_CHOOSE_LINKED", 0],
|
|
4966
|
+
["EV_FILTER_BY_PARENT", 0],
|
|
4967
|
+
["EV_CLEAR_PARENT_FILTER", 0],
|
|
4968
|
+
["EV_OPEN_LINKED", event_flag_t.EVF_OUTPUT_EVENT],
|
|
4774
4969
|
["EV_SHOW_CELL_JSON", 0],
|
|
4775
4970
|
["EV_SHOW_CELL_FILE", 0],
|
|
4776
4971
|
["EV_ASSET_LOADED", 0],
|
|
@@ -74,6 +74,21 @@ import {t} from "i18next";
|
|
|
74
74
|
***************************************************************/
|
|
75
75
|
const GCLASS_NAME = "C_YUI_TREEDB_TOPICS";
|
|
76
76
|
|
|
77
|
+
/*
|
|
78
|
+
* How a topic TABLE reads its rows. `hook_size`: a hook comes back as
|
|
79
|
+
* its COUNT, `[{"size": N}]`, not as the id of every child. The cell
|
|
80
|
+
* only ever showed the number, and a click on it opens the child topic
|
|
81
|
+
* filtered (EV_OPEN_LINKED) -- so the ids were downloaded for nothing:
|
|
82
|
+
* every device id, once per device type, on every load of that topic.
|
|
83
|
+
*
|
|
84
|
+
* Only the table LOADS use it. The writes keep their own options, and
|
|
85
|
+
* the node events still carry whole hooks (c_node publishes them with
|
|
86
|
+
* `list_dict`, whatever a reader asked for); the cell counts either
|
|
87
|
+
* shape (`treedb_hook_data_size`), and so does a delete's question
|
|
88
|
+
* (`delete_impact`'s ref_count).
|
|
89
|
+
*/
|
|
90
|
+
const TABLE_READ_OPTIONS = {list_dict: true, hook_size: true};
|
|
91
|
+
|
|
77
92
|
/***************************************************************
|
|
78
93
|
* Data
|
|
79
94
|
***************************************************************/
|
|
@@ -1316,9 +1331,7 @@ function get_nodes(gobj, topic_name)
|
|
|
1316
1331
|
gobj,
|
|
1317
1332
|
treedb_name,
|
|
1318
1333
|
topic_name,
|
|
1319
|
-
{
|
|
1320
|
-
list_dict: true
|
|
1321
|
-
}
|
|
1334
|
+
Object.assign({}, TABLE_READ_OPTIONS)
|
|
1322
1335
|
);
|
|
1323
1336
|
}
|
|
1324
1337
|
|
|
@@ -2371,7 +2384,7 @@ function ac_request_page(gobj, event, kw, src)
|
|
|
2371
2384
|
gobj,
|
|
2372
2385
|
gobj_read_str_attr(gobj, "treedb_name"),
|
|
2373
2386
|
topic_name,
|
|
2374
|
-
{
|
|
2387
|
+
Object.assign({}, TABLE_READ_OPTIONS),
|
|
2375
2388
|
{from: kw.from, limit: kw.limit, req_id: kw.req_id}
|
|
2376
2389
|
);
|
|
2377
2390
|
|
|
@@ -2384,6 +2397,47 @@ function ac_request_page(gobj, event, kw, src)
|
|
|
2384
2397
|
return 0;
|
|
2385
2398
|
}
|
|
2386
2399
|
|
|
2400
|
+
/********************************************
|
|
2401
|
+
* Event from formtable: open the records a hook of one row links --
|
|
2402
|
+
* the CHILD topic's table, filtered to the rows whose fkey names that
|
|
2403
|
+
* row. A hook is the question "which records hang from this one", and
|
|
2404
|
+
* the child's table is where that answer can be read: it pages, it
|
|
2405
|
+
* searches, it opens a record and it exports -- which a list of ids in
|
|
2406
|
+
* a popup could not do past a few dozen of them.
|
|
2407
|
+
* {
|
|
2408
|
+
* topic_name: the child topic
|
|
2409
|
+
* fkey: its fkey column that points back
|
|
2410
|
+
* parent_topic: the topic of the row the hook is on
|
|
2411
|
+
* parent_id: that row's id
|
|
2412
|
+
* hook: the hook column
|
|
2413
|
+
* }
|
|
2414
|
+
********************************************/
|
|
2415
|
+
function ac_open_linked(gobj, event, kw, src)
|
|
2416
|
+
{
|
|
2417
|
+
let child = get_gobj_formtable(gobj, kw.topic_name);
|
|
2418
|
+
if(!child) {
|
|
2419
|
+
/* A child topic this view does not show: a system topic in a
|
|
2420
|
+
* user view, or one the schema names and the backend did not
|
|
2421
|
+
* describe. */
|
|
2422
|
+
log_error(`${gobj_short_name(gobj)}: cannot open '${kw.topic_name}', ` +
|
|
2423
|
+
`linked from '${kw.parent_topic}^${kw.parent_id}': that topic is not in this view`);
|
|
2424
|
+
return -1;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
/* Through the same entry point as a tab click: the switch is local
|
|
2428
|
+
* and the host mirrors it into the URL, so Back returns to the
|
|
2429
|
+
* topic the hook was clicked in. */
|
|
2430
|
+
select_topic_by_id(gobj, gobj_name(child));
|
|
2431
|
+
|
|
2432
|
+
gobj_send_event(child, "EV_FILTER_BY_PARENT", {
|
|
2433
|
+
fkey: kw.fkey,
|
|
2434
|
+
parent_topic: kw.parent_topic,
|
|
2435
|
+
parent_id: kw.parent_id,
|
|
2436
|
+
hook: kw.hook
|
|
2437
|
+
}, gobj);
|
|
2438
|
+
return 0;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2387
2441
|
/********************************************
|
|
2388
2442
|
* Event from formtable: the bytes of one asset a `file` column names,
|
|
2389
2443
|
* for the form's preview or the table's file popup.
|
|
@@ -2551,9 +2605,7 @@ function ac_refresh_topic(gobj, event, kw, src)
|
|
|
2551
2605
|
return 0;
|
|
2552
2606
|
}
|
|
2553
2607
|
|
|
2554
|
-
let options = {
|
|
2555
|
-
list_dict: true
|
|
2556
|
-
};
|
|
2608
|
+
let options = Object.assign({}, TABLE_READ_OPTIONS);
|
|
2557
2609
|
|
|
2558
2610
|
let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
|
|
2559
2611
|
|
|
@@ -2676,6 +2728,7 @@ function create_gclass(gclass_name)
|
|
|
2676
2728
|
["EV_UPDATE_FIELD", ac_update_field, null],
|
|
2677
2729
|
["EV_REQUEST_PAGE", ac_request_page, null],
|
|
2678
2730
|
["EV_REQUEST_ASSET", ac_request_asset, null],
|
|
2731
|
+
["EV_OPEN_LINKED", ac_open_linked, null],
|
|
2679
2732
|
["EV_DELETE_RECORD", ac_delete_record, null],
|
|
2680
2733
|
["EV_REFRESH_TOPIC", ac_refresh_topic, null],
|
|
2681
2734
|
["EV_OPEN_JSON", ac_open_json, null],
|
|
@@ -2706,6 +2759,7 @@ function create_gclass(gclass_name)
|
|
|
2706
2759
|
["EV_UPDATE_FIELD", 0],
|
|
2707
2760
|
["EV_REQUEST_PAGE", 0],
|
|
2708
2761
|
["EV_REQUEST_ASSET", 0],
|
|
2762
|
+
["EV_OPEN_LINKED", 0],
|
|
2709
2763
|
["EV_DELETE_RECORD", 0],
|
|
2710
2764
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT|
|
|
2711
2765
|
event_flag_t.EVF_NO_WARN_SUBS],
|
package/src/delete_impact.js
CHANGED
|
@@ -31,6 +31,13 @@
|
|
|
31
31
|
***************************************************************/
|
|
32
32
|
function ref_count(value)
|
|
33
33
|
{
|
|
34
|
+
/* A hook read with `hook_size` is not its children but their COUNT:
|
|
35
|
+
* `[{"size": N}]`. Counted as a one-element list it said a parent of
|
|
36
|
+
* 5675 children took ONE with it. */
|
|
37
|
+
if(Array.isArray(value) && value.length === 1 && value[0] &&
|
|
38
|
+
typeof value[0] === "object" && typeof value[0].size === "number") {
|
|
39
|
+
return value[0].size;
|
|
40
|
+
}
|
|
34
41
|
if(Array.isArray(value)) {
|
|
35
42
|
return value.length;
|
|
36
43
|
}
|
|
@@ -21,6 +21,14 @@ describe("ref_count", () => {
|
|
|
21
21
|
expect(ref_count(undefined)).toBe(0);
|
|
22
22
|
expect(ref_count(7)).toBe(0);
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
it("reads a hook read with hook_size as its count", () => {
|
|
26
|
+
expect(ref_count([{size: 5675}])).toBe(5675);
|
|
27
|
+
expect(ref_count([{size: 0}])).toBe(0);
|
|
28
|
+
expect(delete_impact(DESC, {id: "a", users: [{size: 12}]}).children).toBe(12);
|
|
29
|
+
/* one real child that happens to be an object is still ONE */
|
|
30
|
+
expect(ref_count([{id: "u1"}])).toBe(1);
|
|
31
|
+
});
|
|
24
32
|
});
|
|
25
33
|
|
|
26
34
|
describe("delete_impact", () => {
|