@yuneta/gobj-ui 7.23.160 → 7.23.162
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 +56 -0
- package/dist/gobj-ui.cjs.js +344 -98
- package/dist/gobj-ui.es.js +345 -99
- 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 +306 -105
- package/src/c_yui_treedb_topics.js +43 -0
- 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
|
************************************************************/
|
|
@@ -2928,13 +3141,19 @@ function show_edit_form(gobj, row, index)
|
|
|
2928
3141
|
* Does a click on this column already mean something else?
|
|
2929
3142
|
*
|
|
2930
3143
|
* The `_` columns are the table's own scaffolding (the selection
|
|
2931
|
-
* checkbox, the Op buttons); a hook opens its children
|
|
2932
|
-
* holding a json document opens it in a viewer
|
|
2933
|
-
*
|
|
3144
|
+
* checkbox, the Op buttons); a hook opens its children, a col
|
|
3145
|
+
* holding a json document opens it in a viewer, and a `file` col
|
|
3146
|
+
* opens its asset in a popup. Anything else is plain data, and a
|
|
3147
|
+
* click on it means "show me this record".
|
|
3148
|
+
*
|
|
3149
|
+
* `file` was missing when its popup arrived (7.23.159), so a click
|
|
3150
|
+
* on a photo opened the popup AND the record on top of it: Tabulator
|
|
3151
|
+
* fires `rowClick` from its own dispatcher, and the cell's
|
|
3152
|
+
* stopPropagation() does not reach it.
|
|
2934
3153
|
************************************************************/
|
|
2935
3154
|
const CELL_WITH_ACTION = [
|
|
2936
3155
|
"hook", "object", "dict", "template", "array", "list",
|
|
2937
|
-
"coordinates", "blob", "gbuffer"
|
|
3156
|
+
"coordinates", "blob", "gbuffer", "file"
|
|
2938
3157
|
];
|
|
2939
3158
|
|
|
2940
3159
|
function cell_has_its_own_action(gobj, field)
|
|
@@ -3296,84 +3515,6 @@ function build_fkey_ref(gobj, col, value)
|
|
|
3296
3515
|
return refs;
|
|
3297
3516
|
}
|
|
3298
3517
|
|
|
3299
|
-
/***************************************************************************
|
|
3300
|
-
* TODO con límite máximo o máximo height o con scroll
|
|
3301
|
-
* en un gobj propio para gestionar los datos en "page"s
|
|
3302
|
-
* que los hook no vengan rellenos si son muchos y que se puedan gestionar
|
|
3303
|
-
* con un gobj
|
|
3304
|
-
***************************************************************************/
|
|
3305
|
-
function show_dropdown_popup_menu(gobj, x, y, items, callback)
|
|
3306
|
-
{
|
|
3307
|
-
let $element = createElement2([
|
|
3308
|
-
'div', {class: 'dropdown popup' }, [
|
|
3309
|
-
['div', {
|
|
3310
|
-
class: 'dropdown-menu', role: 'menu', style: 'min-width:4rem; border: 2px solid var(--bulma-border); padding: 0px;'
|
|
3311
|
-
}, [
|
|
3312
|
-
['div', { class: 'dropdown-content', style: 'padding: 0;' }, []]
|
|
3313
|
-
]]
|
|
3314
|
-
], {
|
|
3315
|
-
'click': (evt) => {
|
|
3316
|
-
evt.stopPropagation();
|
|
3317
|
-
destroyModal();
|
|
3318
|
-
if(callback) {
|
|
3319
|
-
callback(evt);
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
]);
|
|
3324
|
-
|
|
3325
|
-
const destroyModal = () => {
|
|
3326
|
-
$element.classList.remove('is-active');
|
|
3327
|
-
$element.parentNode.removeChild($element);
|
|
3328
|
-
};
|
|
3329
|
-
|
|
3330
|
-
let $dropdown_content = $element.querySelector('.dropdown-content');
|
|
3331
|
-
|
|
3332
|
-
let ids = kwid_get_ids(gobj, items);
|
|
3333
|
-
for(let id of ids) {
|
|
3334
|
-
let $item = createElement2(
|
|
3335
|
-
['a', {class: 'dropdown-item flex-horizontal-section', 'data-value':`${id}`, style:'margin:0px;'}, [
|
|
3336
|
-
['span', {i18n: `${id}`}, `${id}`]
|
|
3337
|
-
], {
|
|
3338
|
-
'click': (evt) => {
|
|
3339
|
-
evt.stopPropagation();
|
|
3340
|
-
destroyModal();
|
|
3341
|
-
if(callback) {
|
|
3342
|
-
callback(evt, this.dataset.value);
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
}]
|
|
3346
|
-
);
|
|
3347
|
-
$dropdown_content.appendChild($item);
|
|
3348
|
-
}
|
|
3349
|
-
|
|
3350
|
-
refresh_language($element, t);
|
|
3351
|
-
|
|
3352
|
-
/*
|
|
3353
|
-
* Add to popup layer
|
|
3354
|
-
*/
|
|
3355
|
-
popup_mount_layer(gobj).appendChild($element);
|
|
3356
|
-
|
|
3357
|
-
/*
|
|
3358
|
-
* Set position
|
|
3359
|
-
*/
|
|
3360
|
-
$element.style.position = "absolute";
|
|
3361
|
-
$element.style.top = y + "px";
|
|
3362
|
-
$element.style.left = x + "px";
|
|
3363
|
-
|
|
3364
|
-
/*
|
|
3365
|
-
* Show
|
|
3366
|
-
*/
|
|
3367
|
-
$element.classList.add('is-active');
|
|
3368
|
-
|
|
3369
|
-
/*
|
|
3370
|
-
* Set focus
|
|
3371
|
-
*/
|
|
3372
|
-
let $with_focus = $element.querySelector('.with-focus');
|
|
3373
|
-
if($with_focus) {
|
|
3374
|
-
$with_focus.focus();
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
3518
|
|
|
3378
3519
|
|
|
3379
3520
|
|
|
@@ -4111,17 +4252,78 @@ function ac_change_locale(gobj, event, kw, src)
|
|
|
4111
4252
|
************************************************************/
|
|
4112
4253
|
function ac_show_hook_data(gobj, event, kw, src)
|
|
4113
4254
|
{
|
|
4114
|
-
|
|
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
|
+
}
|
|
4276
|
+
|
|
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
|
+
}
|
|
4115
4287
|
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
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;
|
|
4123
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
|
+
}
|
|
4124
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);
|
|
4125
4327
|
return 0;
|
|
4126
4328
|
}
|
|
4127
4329
|
|
|
@@ -4473,20 +4675,12 @@ function ac_search(gobj, event, kw, src)
|
|
|
4473
4675
|
return -1;
|
|
4474
4676
|
}
|
|
4475
4677
|
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
/* A treedb row is not flat: an fkey arrives as a list of objects, and
|
|
4483
|
-
* `String()` of that is "[object Object]" -- so searching for the
|
|
4484
|
-
* workshop of a meter, which is where the value a person has in mind
|
|
4485
|
-
* lives, never found anything. `row_matches()` walks into them, and
|
|
4486
|
-
* reads only the `id` of an fkey. See yui_row_search.js. */
|
|
4487
|
-
tabulator.setFilter(function(data) {
|
|
4488
|
-
return row_matches(data, term);
|
|
4489
|
-
});
|
|
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);
|
|
4490
4684
|
|
|
4491
4685
|
return 0;
|
|
4492
4686
|
}
|
|
@@ -4720,6 +4914,9 @@ function create_gclass(gclass_name)
|
|
|
4720
4914
|
["EV_COPY_ROWS", ac_copy_rows, null],
|
|
4721
4915
|
["EV_PASTE_ROWS", ac_paste_rows, null],
|
|
4722
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],
|
|
4723
4920
|
["EV_SHOW_CELL_JSON", ac_show_cell_json, null],
|
|
4724
4921
|
["EV_SHOW_CELL_FILE", ac_show_cell_file, null],
|
|
4725
4922
|
["EV_ASSET_LOADED", ac_asset_loaded, null],
|
|
@@ -4764,7 +4961,11 @@ function create_gclass(gclass_name)
|
|
|
4764
4961
|
["EV_CLEAR_SELECTION", 0],
|
|
4765
4962
|
["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
|
|
4766
4963
|
["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
|
|
4767
|
-
["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],
|
|
4768
4969
|
["EV_SHOW_CELL_JSON", 0],
|
|
4769
4970
|
["EV_SHOW_CELL_FILE", 0],
|
|
4770
4971
|
["EV_ASSET_LOADED", 0],
|
|
@@ -2384,6 +2384,47 @@ function ac_request_page(gobj, event, kw, src)
|
|
|
2384
2384
|
return 0;
|
|
2385
2385
|
}
|
|
2386
2386
|
|
|
2387
|
+
/********************************************
|
|
2388
|
+
* Event from formtable: open the records a hook of one row links --
|
|
2389
|
+
* the CHILD topic's table, filtered to the rows whose fkey names that
|
|
2390
|
+
* row. A hook is the question "which records hang from this one", and
|
|
2391
|
+
* the child's table is where that answer can be read: it pages, it
|
|
2392
|
+
* searches, it opens a record and it exports -- which a list of ids in
|
|
2393
|
+
* a popup could not do past a few dozen of them.
|
|
2394
|
+
* {
|
|
2395
|
+
* topic_name: the child topic
|
|
2396
|
+
* fkey: its fkey column that points back
|
|
2397
|
+
* parent_topic: the topic of the row the hook is on
|
|
2398
|
+
* parent_id: that row's id
|
|
2399
|
+
* hook: the hook column
|
|
2400
|
+
* }
|
|
2401
|
+
********************************************/
|
|
2402
|
+
function ac_open_linked(gobj, event, kw, src)
|
|
2403
|
+
{
|
|
2404
|
+
let child = get_gobj_formtable(gobj, kw.topic_name);
|
|
2405
|
+
if(!child) {
|
|
2406
|
+
/* A child topic this view does not show: a system topic in a
|
|
2407
|
+
* user view, or one the schema names and the backend did not
|
|
2408
|
+
* describe. */
|
|
2409
|
+
log_error(`${gobj_short_name(gobj)}: cannot open '${kw.topic_name}', ` +
|
|
2410
|
+
`linked from '${kw.parent_topic}^${kw.parent_id}': that topic is not in this view`);
|
|
2411
|
+
return -1;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
/* Through the same entry point as a tab click: the switch is local
|
|
2415
|
+
* and the host mirrors it into the URL, so Back returns to the
|
|
2416
|
+
* topic the hook was clicked in. */
|
|
2417
|
+
select_topic_by_id(gobj, gobj_name(child));
|
|
2418
|
+
|
|
2419
|
+
gobj_send_event(child, "EV_FILTER_BY_PARENT", {
|
|
2420
|
+
fkey: kw.fkey,
|
|
2421
|
+
parent_topic: kw.parent_topic,
|
|
2422
|
+
parent_id: kw.parent_id,
|
|
2423
|
+
hook: kw.hook
|
|
2424
|
+
}, gobj);
|
|
2425
|
+
return 0;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2387
2428
|
/********************************************
|
|
2388
2429
|
* Event from formtable: the bytes of one asset a `file` column names,
|
|
2389
2430
|
* for the form's preview or the table's file popup.
|
|
@@ -2676,6 +2717,7 @@ function create_gclass(gclass_name)
|
|
|
2676
2717
|
["EV_UPDATE_FIELD", ac_update_field, null],
|
|
2677
2718
|
["EV_REQUEST_PAGE", ac_request_page, null],
|
|
2678
2719
|
["EV_REQUEST_ASSET", ac_request_asset, null],
|
|
2720
|
+
["EV_OPEN_LINKED", ac_open_linked, null],
|
|
2679
2721
|
["EV_DELETE_RECORD", ac_delete_record, null],
|
|
2680
2722
|
["EV_REFRESH_TOPIC", ac_refresh_topic, null],
|
|
2681
2723
|
["EV_OPEN_JSON", ac_open_json, null],
|
|
@@ -2706,6 +2748,7 @@ function create_gclass(gclass_name)
|
|
|
2706
2748
|
["EV_UPDATE_FIELD", 0],
|
|
2707
2749
|
["EV_REQUEST_PAGE", 0],
|
|
2708
2750
|
["EV_REQUEST_ASSET", 0],
|
|
2751
|
+
["EV_OPEN_LINKED", 0],
|
|
2709
2752
|
["EV_DELETE_RECORD", 0],
|
|
2710
2753
|
["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT|
|
|
2711
2754
|
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", () => {
|