@yuneta/gobj-ui 7.12.0 → 7.13.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 CHANGED
@@ -1031,6 +1031,15 @@ from the view's `EV_LANGUAGE_CHANGED` action.
1031
1031
  table that is not built yet or is already gone. Clear the selection after
1032
1032
  acting on it: the rows it names are no longer there.
1033
1033
 
1034
+ **In the treedb topic table** (`C_YUI_TREEDB_TOPIC_WITH_FORM`, and through it
1035
+ `C_YUI_TREEDB_TOPICS`) the bar is **opt-in**: pass `with_selection_bar: true`.
1036
+ It is off by default because the bar takes its words from the HOST's i18n, and
1037
+ a host that has not defined `"{{n}} selected"` and `"clear selection"` renders
1038
+ the keys. It shows only while the table is in **edition mode** — outside it the
1039
+ checkbox column is hidden, and a count of rows nobody can see or untick is a
1040
+ count you cannot act on. The bar carries no action there: the table's own
1041
+ toolbar already has Delete and Copy, and they act on the selection.
1042
+
1034
1043
  `yui_selection_column({visible: false})` is for a table that reveals the column
1035
1044
  only in an edit mode — `C_YUI_TREEDB_TOPIC_WITH_FORM` shows it with
1036
1045
  `showColumn("_check_box_state_")`, and takes its `selectableRows` and every
@@ -14496,6 +14496,7 @@ var attrs_table$6 = [
14496
14496
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
14497
14497
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
14498
14498
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
14499
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Each topic table shows the shared selection bar (\"N selected\" + a way out) above it while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, so a host that turns it on must define \"{{n}} selected\" and \"clear selection\""),
14499
14500
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
14500
14501
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
14501
14502
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_cards_landing", 0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
@@ -15310,7 +15311,8 @@ function process_treedb_descs$1(gobj) {
15310
15311
  treedb_name,
15311
15312
  topic_name: key,
15312
15313
  desc,
15313
- with_remote_paging: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging")
15314
+ with_remote_paging: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_remote_paging"),
15315
+ with_selection_bar: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_selection_bar")
15314
15316
  };
15315
15317
  let id = `${(0, _yuneta_gobj_js.gobj_name)(gobj)}?${desc.topic_name}`;
15316
15318
  let gobj_topic_form = (0, _yuneta_gobj_js.gobj_create_service)(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
@@ -16403,6 +16405,119 @@ function yui_selected_rows(tabulator) {
16403
16405
  return [];
16404
16406
  }
16405
16407
  }
16408
+ /***************************************************************
16409
+ * Drop the selection (after acting on it, or after a
16410
+ * reload that leaves the ticked rows behind).
16411
+ ***************************************************************/
16412
+ function yui_clear_selection(tabulator) {
16413
+ if (!tabulator || typeof tabulator.deselectRow !== "function") return;
16414
+ try {
16415
+ tabulator.deselectRow();
16416
+ } catch (e) {}
16417
+ }
16418
+ /***************************************************************
16419
+ * The bar that appears while rows are selected.
16420
+ *
16421
+ * t the HOST's translator
16422
+ * opts:
16423
+ * actions [{label, icon, class, on_click}] -- `label` is an
16424
+ * i18n KEY, `class` extra Bulma classes for the
16425
+ * button (e.g. "is-danger"), `on_click` the DOM
16426
+ * handler, whose job is to send an event
16427
+ * on_clear called by the "clear selection" button
16428
+ * name logical-class prefix, default "TABLE"
16429
+ *
16430
+ * -> {$el, set_count(n), refresh()}
16431
+ *
16432
+ * `refresh()` redraws it in the current language: the count is
16433
+ * composed at render time, so it cannot re-translate itself
16434
+ * through `refresh_language()`. Call it on EV_LANGUAGE_CHANGED.
16435
+ ***************************************************************/
16436
+ function yui_selection_bar(t, opts) {
16437
+ let o = opts || {};
16438
+ let actions = Array.isArray(o.actions) ? o.actions : [];
16439
+ let name = o.name || "TABLE";
16440
+ let count = 0;
16441
+ let $count = (0, _yuneta_gobj_js.createElement2)([
16442
+ "span",
16443
+ { class: `${name}_SELECTION_COUNT has-text-weight-semibold` },
16444
+ ""
16445
+ ]);
16446
+ let $buttons = [];
16447
+ for (let action of actions) {
16448
+ let $b = (0, _yuneta_gobj_js.createElement2)([
16449
+ "button",
16450
+ {
16451
+ class: `${name}_SELECTION_ACTION button ${action.class || ""}`,
16452
+ type: "button"
16453
+ },
16454
+ [[
16455
+ "span",
16456
+ { class: "icon" },
16457
+ [["i", { class: action.icon || "yi-check" }]]
16458
+ ], [
16459
+ "span",
16460
+ {},
16461
+ ""
16462
+ ]],
16463
+ { click: action.on_click }
16464
+ ]);
16465
+ $b._yui_label = action.label || "";
16466
+ $buttons.push($b);
16467
+ }
16468
+ let $clear = (0, _yuneta_gobj_js.createElement2)([
16469
+ "button",
16470
+ {
16471
+ class: `${name}_SELECTION_CLEAR button is-ghost`,
16472
+ type: "button"
16473
+ },
16474
+ [[
16475
+ "span",
16476
+ { class: "icon" },
16477
+ [["i", { class: "yi-xmark" }]]
16478
+ ], [
16479
+ "span",
16480
+ {},
16481
+ ""
16482
+ ]],
16483
+ { click: function(e) {
16484
+ if (typeof o.on_clear === "function") o.on_clear(e);
16485
+ } }
16486
+ ]);
16487
+ $clear._yui_label = "clear selection";
16488
+ let $el = (0, _yuneta_gobj_js.createElement2)([
16489
+ "div",
16490
+ {
16491
+ class: `${name}_SELECTION_BAR is-align-items-center is-flex-wrap-wrap mb-2 is-hidden`,
16492
+ style: "display:flex; gap:0.5rem;"
16493
+ },
16494
+ [$count].concat($buttons, [$clear])
16495
+ ]);
16496
+ function label_of($b) {
16497
+ return $b.querySelector("span.icon ~ span");
16498
+ }
16499
+ function refresh() {
16500
+ $count.textContent = t("{{n}} selected", { n: count });
16501
+ for (let $b of $buttons.concat([$clear])) {
16502
+ let $label = label_of($b);
16503
+ let key = $b._yui_label;
16504
+ if ($label && key) $label.textContent = t(key);
16505
+ $b.title = key ? t(key) : "";
16506
+ $b.setAttribute("aria-label", $b.title);
16507
+ }
16508
+ $el.classList.toggle("is-hidden", count <= 0);
16509
+ }
16510
+ function set_count(n) {
16511
+ count = n > 0 ? n : 0;
16512
+ refresh();
16513
+ }
16514
+ refresh();
16515
+ return {
16516
+ $el,
16517
+ set_count,
16518
+ refresh
16519
+ };
16520
+ }
16406
16521
  //#endregion
16407
16522
  //#region src/treedb_write_plan.js
16408
16523
  /***********************************************************************
@@ -16571,6 +16686,7 @@ var attrs_table$5 = [
16571
16686
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
16572
16687
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
16573
16688
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_radio", 0, false, "Auxiliary first column to select one row"),
16689
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Show the shared selection bar (\"N selected\" + a way out) above the table while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, and a host that has not defined \"{{n}} selected\" and \"clear selection\" would render the keys"),
16574
16690
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "broadcast_select_rows_event", 0, false, "Broadcast select rows event"),
16575
16691
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "broadcast_unselect_rows_event", 0, false, "Broadcast unselect rows event"),
16576
16692
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "tabulator_settings", 0, {
@@ -16589,6 +16705,7 @@ var attrs_table$5 = [
16589
16705
  }, "Default settings for Tabulator"),
16590
16706
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for UI"),
16591
16707
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "tabulator", 0, null, "Tabulator instance"),
16708
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "selection_bar", 0, null, "The selection bar (yui_table_select.js), when with_selection_bar"),
16592
16709
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "table_id", 0, null, "Table div ID"),
16593
16710
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "toolbar_id", 0, null, "Toolbar ID"),
16594
16711
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "popup_id", 0, null, "Edit form popup ID"),
@@ -17005,6 +17122,15 @@ function build_ui$5(gobj) {
17005
17122
  style: "margin-top:0px !important;"
17006
17123
  }]]
17007
17124
  ]);
17125
+ if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_checkbox") && (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_selection_bar")) {
17126
+ let bar = yui_selection_bar(i18next.t, {
17127
+ name: "TREEDB_TABLE",
17128
+ actions: [],
17129
+ on_clear: () => (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_CLEAR_SELECTION", {}, gobj)
17130
+ });
17131
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "selection_bar", bar);
17132
+ $container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
17133
+ }
17008
17134
  let $toolbar_slot = $container.querySelector(".toolbar_tabulator_table");
17009
17135
  if ($table_toolbar instanceof Element) $toolbar_slot.appendChild($table_toolbar);
17010
17136
  else $toolbar_slot.appendChild((0, _yuneta_gobj_js.createElement2)(["div", {}]));
@@ -17013,6 +17139,24 @@ function build_ui$5(gobj) {
17013
17139
  (0, _yuneta_gobj_js.refresh_language)($container, i18next.t);
17014
17140
  }
17015
17141
  /************************************************************
17142
+ * How many rows are ticked, on the bar.
17143
+ *
17144
+ * Only while the table is EDITABLE: outside edition its checkbox
17145
+ * column is hidden (`hideColumn`), and a count of rows nobody can
17146
+ * see or untick is a count you cannot act on. The selection itself
17147
+ * is left alone -- coming back to edition finds it, and the bar
17148
+ * recounts from the table.
17149
+ ************************************************************/
17150
+ function render_selection_bar(gobj) {
17151
+ let bar = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "selection_bar");
17152
+ if (!bar) return;
17153
+ if (!(0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "editable")) {
17154
+ bar.set_count(0);
17155
+ return;
17156
+ }
17157
+ bar.set_count(yui_selected_rows((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator")).length);
17158
+ }
17159
+ /************************************************************
17016
17160
  * Destroy UI
17017
17161
  ************************************************************/
17018
17162
  function destroy_ui$2(gobj) {
@@ -18202,6 +18346,8 @@ function refuse_if_readonly$2(gobj, event) {
18202
18346
  * dict. Re-apply it in the new language.
18203
18347
  ***************************************************************/
18204
18348
  function ac_language_changed$2(gobj, event, kw, src) {
18349
+ let bar = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "selection_bar");
18350
+ if (bar) bar.refresh();
18205
18351
  let tabulator = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator");
18206
18352
  if (!tabulator) return 0;
18207
18353
  yui_tabulator_relocalize(tabulator, i18next.t);
@@ -18315,6 +18461,7 @@ function ac_edition_mode(gobj, event, kw, src) {
18315
18461
  $button_copy_record.setAttribute("disabled", true);
18316
18462
  $button_paste_record.setAttribute("disabled", true);
18317
18463
  }
18464
+ render_selection_bar(gobj);
18318
18465
  return 0;
18319
18466
  }
18320
18467
  /************************************************************
@@ -18460,6 +18607,7 @@ function ac_select_rows(gobj, event, kw, src) {
18460
18607
  $button_copy_record.setAttribute("disabled", true);
18461
18608
  }
18462
18609
  }
18610
+ render_selection_bar(gobj);
18463
18611
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "broadcast_select_rows_event")) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, event, kw);
18464
18612
  return 0;
18465
18613
  }
@@ -18480,10 +18628,19 @@ function ac_unselect_rows(gobj, event, kw, src) {
18480
18628
  $button_copy_record.setAttribute("disabled", true);
18481
18629
  }
18482
18630
  }
18631
+ render_selection_bar(gobj);
18483
18632
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "broadcast_select_rows_event")) (0, _yuneta_gobj_js.gobj_publish_event)(gobj, event, kw);
18484
18633
  return 0;
18485
18634
  }
18486
18635
  /************************************************************
18636
+ * The way out of a selection (the bar's clear button).
18637
+ ************************************************************/
18638
+ function ac_clear_selection(gobj, event, kw, src) {
18639
+ yui_clear_selection((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "tabulator"));
18640
+ render_selection_bar(gobj);
18641
+ return 0;
18642
+ }
18643
+ /************************************************************
18487
18644
  * { locale: "es" }
18488
18645
  ************************************************************/
18489
18646
  function ac_change_locale(gobj, event, kw, src) {
@@ -18960,6 +19117,11 @@ function create_gclass$5(gclass_name) {
18960
19117
  ac_select_rows,
18961
19118
  null
18962
19119
  ],
19120
+ [
19121
+ "EV_CLEAR_SELECTION",
19122
+ ac_clear_selection,
19123
+ null
19124
+ ],
18963
19125
  [
18964
19126
  "EV_UNSELECT_ROWS",
18965
19127
  ac_unselect_rows,
@@ -19068,6 +19230,7 @@ function create_gclass$5(gclass_name) {
19068
19230
  ["EV_DELETE_ROWS", 0],
19069
19231
  ["EV_COPY_ROWS", 0],
19070
19232
  ["EV_PASTE_ROWS", 0],
19233
+ ["EV_CLEAR_SELECTION", 0],
19071
19234
  ["EV_SELECT_ROWS", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
19072
19235
  ["EV_UNSELECT_ROWS", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
19073
19236
  ["EV_SHOW_HOOK_DATA", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT],
@@ -14491,6 +14491,7 @@ var attrs_table$6 = [
14491
14491
  SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
14492
14492
  SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
14493
14493
  SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
14494
+ SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Each topic table shows the shared selection bar (\"N selected\" + a way out) above it while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, so a host that turns it on must define \"{{n}} selected\" and \"clear selection\""),
14494
14495
  SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
14495
14496
  SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
14496
14497
  SDATA(data_type_t.DTP_BOOLEAN, "with_cards_landing", 0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
@@ -15305,7 +15306,8 @@ function process_treedb_descs$1(gobj) {
15305
15306
  treedb_name,
15306
15307
  topic_name: key,
15307
15308
  desc,
15308
- with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging")
15309
+ with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging"),
15310
+ with_selection_bar: gobj_read_bool_attr(gobj, "with_selection_bar")
15309
15311
  };
15310
15312
  let id = `${gobj_name(gobj)}?${desc.topic_name}`;
15311
15313
  let gobj_topic_form = gobj_create_service(id, "C_YUI_TREEDB_TOPIC_WITH_FORM", kw_topic_form, gobj);
@@ -16313,6 +16315,40 @@ function yui_tabulator_relocalize(table, t) {
16313
16315
  }
16314
16316
  //#endregion
16315
16317
  //#region src/yui_table_select.js
16318
+ /***********************************************************************
16319
+ * yui_table_select.js
16320
+ *
16321
+ * SELECT ROWS IN A TABLE, and act on the lot.
16322
+ *
16323
+ * Deleting twenty rows one confirmation at a time is not a
16324
+ * workflow, it is a punishment, and every table that lets you
16325
+ * remove a row will eventually be asked to remove twenty. This
16326
+ * is that facility, once: the checkbox column, the settings
16327
+ * that make selection behave, and the bar that appears while
16328
+ * something is selected and carries what can be done to it.
16329
+ *
16330
+ * Two decisions are baked in, both learned in the treedb
16331
+ * topic table:
16332
+ *
16333
+ * - Selection is driven ONLY by the checkbox, never by
16334
+ * clicking the row (`selectableRows: "highlight"`). A table
16335
+ * row is full of things to click -- an editor, an icon, a
16336
+ * nested table -- and click-to-select ticks a row every time
16337
+ * you reach for one of them.
16338
+ *
16339
+ * - The header checkbox covers the ACTIVE rows, the ones the
16340
+ * filters leave on screen. "Select all" over rows nobody can
16341
+ * see is how a filtered delete takes the whole topic with
16342
+ * it.
16343
+ *
16344
+ * The bar takes its words from the HOST's `t`: this library
16345
+ * translates through the app's i18next, and the app owns the
16346
+ * keys. It needs "{{n}} selected" and "clear selection"; each
16347
+ * action brings its own key.
16348
+ *
16349
+ * Copyright (c) 2026, ArtGins.
16350
+ * All Rights Reserved.
16351
+ ***********************************************************************/
16316
16352
  /***************************************************************
16317
16353
  * The checkbox column. Put it FIRST in the column list.
16318
16354
  *
@@ -16364,6 +16400,119 @@ function yui_selected_rows(tabulator) {
16364
16400
  return [];
16365
16401
  }
16366
16402
  }
16403
+ /***************************************************************
16404
+ * Drop the selection (after acting on it, or after a
16405
+ * reload that leaves the ticked rows behind).
16406
+ ***************************************************************/
16407
+ function yui_clear_selection(tabulator) {
16408
+ if (!tabulator || typeof tabulator.deselectRow !== "function") return;
16409
+ try {
16410
+ tabulator.deselectRow();
16411
+ } catch (e) {}
16412
+ }
16413
+ /***************************************************************
16414
+ * The bar that appears while rows are selected.
16415
+ *
16416
+ * t the HOST's translator
16417
+ * opts:
16418
+ * actions [{label, icon, class, on_click}] -- `label` is an
16419
+ * i18n KEY, `class` extra Bulma classes for the
16420
+ * button (e.g. "is-danger"), `on_click` the DOM
16421
+ * handler, whose job is to send an event
16422
+ * on_clear called by the "clear selection" button
16423
+ * name logical-class prefix, default "TABLE"
16424
+ *
16425
+ * -> {$el, set_count(n), refresh()}
16426
+ *
16427
+ * `refresh()` redraws it in the current language: the count is
16428
+ * composed at render time, so it cannot re-translate itself
16429
+ * through `refresh_language()`. Call it on EV_LANGUAGE_CHANGED.
16430
+ ***************************************************************/
16431
+ function yui_selection_bar(t, opts) {
16432
+ let o = opts || {};
16433
+ let actions = Array.isArray(o.actions) ? o.actions : [];
16434
+ let name = o.name || "TABLE";
16435
+ let count = 0;
16436
+ let $count = createElement2([
16437
+ "span",
16438
+ { class: `${name}_SELECTION_COUNT has-text-weight-semibold` },
16439
+ ""
16440
+ ]);
16441
+ let $buttons = [];
16442
+ for (let action of actions) {
16443
+ let $b = createElement2([
16444
+ "button",
16445
+ {
16446
+ class: `${name}_SELECTION_ACTION button ${action.class || ""}`,
16447
+ type: "button"
16448
+ },
16449
+ [[
16450
+ "span",
16451
+ { class: "icon" },
16452
+ [["i", { class: action.icon || "yi-check" }]]
16453
+ ], [
16454
+ "span",
16455
+ {},
16456
+ ""
16457
+ ]],
16458
+ { click: action.on_click }
16459
+ ]);
16460
+ $b._yui_label = action.label || "";
16461
+ $buttons.push($b);
16462
+ }
16463
+ let $clear = createElement2([
16464
+ "button",
16465
+ {
16466
+ class: `${name}_SELECTION_CLEAR button is-ghost`,
16467
+ type: "button"
16468
+ },
16469
+ [[
16470
+ "span",
16471
+ { class: "icon" },
16472
+ [["i", { class: "yi-xmark" }]]
16473
+ ], [
16474
+ "span",
16475
+ {},
16476
+ ""
16477
+ ]],
16478
+ { click: function(e) {
16479
+ if (typeof o.on_clear === "function") o.on_clear(e);
16480
+ } }
16481
+ ]);
16482
+ $clear._yui_label = "clear selection";
16483
+ let $el = createElement2([
16484
+ "div",
16485
+ {
16486
+ class: `${name}_SELECTION_BAR is-align-items-center is-flex-wrap-wrap mb-2 is-hidden`,
16487
+ style: "display:flex; gap:0.5rem;"
16488
+ },
16489
+ [$count].concat($buttons, [$clear])
16490
+ ]);
16491
+ function label_of($b) {
16492
+ return $b.querySelector("span.icon ~ span");
16493
+ }
16494
+ function refresh() {
16495
+ $count.textContent = t("{{n}} selected", { n: count });
16496
+ for (let $b of $buttons.concat([$clear])) {
16497
+ let $label = label_of($b);
16498
+ let key = $b._yui_label;
16499
+ if ($label && key) $label.textContent = t(key);
16500
+ $b.title = key ? t(key) : "";
16501
+ $b.setAttribute("aria-label", $b.title);
16502
+ }
16503
+ $el.classList.toggle("is-hidden", count <= 0);
16504
+ }
16505
+ function set_count(n) {
16506
+ count = n > 0 ? n : 0;
16507
+ refresh();
16508
+ }
16509
+ refresh();
16510
+ return {
16511
+ $el,
16512
+ set_count,
16513
+ refresh
16514
+ };
16515
+ }
16367
16516
  //#endregion
16368
16517
  //#region src/treedb_write_plan.js
16369
16518
  /***********************************************************************
@@ -16532,6 +16681,7 @@ var attrs_table$5 = [
16532
16681
  SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"),
16533
16682
  SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
16534
16683
  SDATA(data_type_t.DTP_BOOLEAN, "with_radio", 0, false, "Auxiliary first column to select one row"),
16684
+ SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Show the shared selection bar (\"N selected\" + a way out) above the table while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, and a host that has not defined \"{{n}} selected\" and \"clear selection\" would render the keys"),
16535
16685
  SDATA(data_type_t.DTP_BOOLEAN, "broadcast_select_rows_event", 0, false, "Broadcast select rows event"),
16536
16686
  SDATA(data_type_t.DTP_BOOLEAN, "broadcast_unselect_rows_event", 0, false, "Broadcast unselect rows event"),
16537
16687
  SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
@@ -16550,6 +16700,7 @@ var attrs_table$5 = [
16550
16700
  }, "Default settings for Tabulator"),
16551
16701
  SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for UI"),
16552
16702
  SDATA(data_type_t.DTP_POINTER, "tabulator", 0, null, "Tabulator instance"),
16703
+ SDATA(data_type_t.DTP_POINTER, "selection_bar", 0, null, "The selection bar (yui_table_select.js), when with_selection_bar"),
16553
16704
  SDATA(data_type_t.DTP_STRING, "table_id", 0, null, "Table div ID"),
16554
16705
  SDATA(data_type_t.DTP_STRING, "toolbar_id", 0, null, "Toolbar ID"),
16555
16706
  SDATA(data_type_t.DTP_STRING, "popup_id", 0, null, "Edit form popup ID"),
@@ -16966,6 +17117,15 @@ function build_ui$5(gobj) {
16966
17117
  style: "margin-top:0px !important;"
16967
17118
  }]]
16968
17119
  ]);
17120
+ if (gobj_read_bool_attr(gobj, "with_checkbox") && gobj_read_bool_attr(gobj, "with_selection_bar")) {
17121
+ let bar = yui_selection_bar(t, {
17122
+ name: "TREEDB_TABLE",
17123
+ actions: [],
17124
+ on_clear: () => gobj_send_event(gobj, "EV_CLEAR_SELECTION", {}, gobj)
17125
+ });
17126
+ gobj_write_attr(gobj, "selection_bar", bar);
17127
+ $container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
17128
+ }
16969
17129
  let $toolbar_slot = $container.querySelector(".toolbar_tabulator_table");
16970
17130
  if ($table_toolbar instanceof Element) $toolbar_slot.appendChild($table_toolbar);
16971
17131
  else $toolbar_slot.appendChild(createElement2(["div", {}]));
@@ -16974,6 +17134,24 @@ function build_ui$5(gobj) {
16974
17134
  refresh_language($container, t);
16975
17135
  }
16976
17136
  /************************************************************
17137
+ * How many rows are ticked, on the bar.
17138
+ *
17139
+ * Only while the table is EDITABLE: outside edition its checkbox
17140
+ * column is hidden (`hideColumn`), and a count of rows nobody can
17141
+ * see or untick is a count you cannot act on. The selection itself
17142
+ * is left alone -- coming back to edition finds it, and the bar
17143
+ * recounts from the table.
17144
+ ************************************************************/
17145
+ function render_selection_bar(gobj) {
17146
+ let bar = gobj_read_attr(gobj, "selection_bar");
17147
+ if (!bar) return;
17148
+ if (!gobj_read_bool_attr(gobj, "editable")) {
17149
+ bar.set_count(0);
17150
+ return;
17151
+ }
17152
+ bar.set_count(yui_selected_rows(gobj_read_attr(gobj, "tabulator")).length);
17153
+ }
17154
+ /************************************************************
16977
17155
  * Destroy UI
16978
17156
  ************************************************************/
16979
17157
  function destroy_ui$2(gobj) {
@@ -18163,6 +18341,8 @@ function refuse_if_readonly$2(gobj, event) {
18163
18341
  * dict. Re-apply it in the new language.
18164
18342
  ***************************************************************/
18165
18343
  function ac_language_changed$2(gobj, event, kw, src) {
18344
+ let bar = gobj_read_attr(gobj, "selection_bar");
18345
+ if (bar) bar.refresh();
18166
18346
  let tabulator = gobj_read_attr(gobj, "tabulator");
18167
18347
  if (!tabulator) return 0;
18168
18348
  yui_tabulator_relocalize(tabulator, t);
@@ -18276,6 +18456,7 @@ function ac_edition_mode(gobj, event, kw, src) {
18276
18456
  $button_copy_record.setAttribute("disabled", true);
18277
18457
  $button_paste_record.setAttribute("disabled", true);
18278
18458
  }
18459
+ render_selection_bar(gobj);
18279
18460
  return 0;
18280
18461
  }
18281
18462
  /************************************************************
@@ -18421,6 +18602,7 @@ function ac_select_rows(gobj, event, kw, src) {
18421
18602
  $button_copy_record.setAttribute("disabled", true);
18422
18603
  }
18423
18604
  }
18605
+ render_selection_bar(gobj);
18424
18606
  if (gobj_read_bool_attr(gobj, "broadcast_select_rows_event")) gobj_publish_event(gobj, event, kw);
18425
18607
  return 0;
18426
18608
  }
@@ -18441,10 +18623,19 @@ function ac_unselect_rows(gobj, event, kw, src) {
18441
18623
  $button_copy_record.setAttribute("disabled", true);
18442
18624
  }
18443
18625
  }
18626
+ render_selection_bar(gobj);
18444
18627
  if (gobj_read_bool_attr(gobj, "broadcast_select_rows_event")) gobj_publish_event(gobj, event, kw);
18445
18628
  return 0;
18446
18629
  }
18447
18630
  /************************************************************
18631
+ * The way out of a selection (the bar's clear button).
18632
+ ************************************************************/
18633
+ function ac_clear_selection(gobj, event, kw, src) {
18634
+ yui_clear_selection(gobj_read_attr(gobj, "tabulator"));
18635
+ render_selection_bar(gobj);
18636
+ return 0;
18637
+ }
18638
+ /************************************************************
18448
18639
  * { locale: "es" }
18449
18640
  ************************************************************/
18450
18641
  function ac_change_locale(gobj, event, kw, src) {
@@ -18921,6 +19112,11 @@ function create_gclass$5(gclass_name) {
18921
19112
  ac_select_rows,
18922
19113
  null
18923
19114
  ],
19115
+ [
19116
+ "EV_CLEAR_SELECTION",
19117
+ ac_clear_selection,
19118
+ null
19119
+ ],
18924
19120
  [
18925
19121
  "EV_UNSELECT_ROWS",
18926
19122
  ac_unselect_rows,
@@ -19029,6 +19225,7 @@ function create_gclass$5(gclass_name) {
19029
19225
  ["EV_DELETE_ROWS", 0],
19030
19226
  ["EV_COPY_ROWS", 0],
19031
19227
  ["EV_PASTE_ROWS", 0],
19228
+ ["EV_CLEAR_SELECTION", 0],
19032
19229
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
19033
19230
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
19034
19231
  ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.12.0",
3
+ "version": "7.13.0",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -78,7 +78,9 @@ import {yui_tabulator_lang, yui_tabulator_relocalize} from "./yui_tabulator_i18n
78
78
  import {
79
79
  yui_selection_column,
80
80
  yui_selection_settings,
81
+ yui_selection_bar,
81
82
  yui_selected_rows,
83
+ yui_clear_selection,
82
84
  } from "./yui_table_select.js";
83
85
 
84
86
  import {t} from "i18next";
@@ -129,6 +131,7 @@ SDATA(data_type_t.DTP_BOOLEAN, "editable", 0, false, "Edit state"
129
131
  /*---------------- Selection Mode ----------------*/
130
132
  SDATA(data_type_t.DTP_BOOLEAN, "with_checkbox", 0, true, "Auxiliary first column to select rows"),
131
133
  SDATA(data_type_t.DTP_BOOLEAN, "with_radio", 0, false, "Auxiliary first column to select one row"),
134
+ SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Show the shared selection bar (\"N selected\" + a way out) above the table while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, and a host that has not defined \"{{n}} selected\" and \"clear selection\" would render the keys"),
132
135
  SDATA(data_type_t.DTP_BOOLEAN, "broadcast_select_rows_event", 0, false, "Broadcast select rows event"),
133
136
  SDATA(data_type_t.DTP_BOOLEAN, "broadcast_unselect_rows_event", 0, false, "Broadcast unselect rows event"),
134
137
 
@@ -148,6 +151,7 @@ SDATA(data_type_t.DTP_JSON, "tabulator_settings", 0, {
148
151
  /*---------------- Internal Attributes ----------------*/
149
152
  SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTML container for UI"),
150
153
  SDATA(data_type_t.DTP_POINTER, "tabulator", 0, null, "Tabulator instance"),
154
+ SDATA(data_type_t.DTP_POINTER, "selection_bar", 0, null, "The selection bar (yui_table_select.js), when with_selection_bar"),
151
155
  SDATA(data_type_t.DTP_STRING, "table_id", 0, null, "Table div ID"),
152
156
  SDATA(data_type_t.DTP_STRING, "toolbar_id", 0, null, "Toolbar ID"),
153
157
  SDATA(data_type_t.DTP_STRING, "popup_id", 0, null, "Edit form popup ID"),
@@ -648,6 +652,21 @@ function build_ui(gobj)
648
652
  ]
649
653
  ]]
650
654
  );
655
+ /* The shared selection bar, between the toolbar and the table. It is
656
+ * the only thing that says HOW MANY rows are ticked: with 200 rows to a
657
+ * page, the checkboxes that answer that question are not all on screen.
658
+ * Opt-in, because it takes its words from the host's i18n. */
659
+ if(gobj_read_bool_attr(gobj, "with_checkbox") &&
660
+ gobj_read_bool_attr(gobj, "with_selection_bar")) {
661
+ let bar = yui_selection_bar(t, {
662
+ name: "TREEDB_TABLE",
663
+ actions: [], /* the toolbar above already carries them */
664
+ on_clear: () => gobj_send_event(gobj, "EV_CLEAR_SELECTION", {}, gobj)
665
+ });
666
+ gobj_write_attr(gobj, "selection_bar", bar);
667
+ $container.insertBefore(bar.$el, $container.querySelector(`#${table_id}`));
668
+ }
669
+
651
670
  let $toolbar_slot = $container.querySelector('.toolbar_tabulator_table');
652
671
  if($table_toolbar instanceof Element) {
653
672
  $toolbar_slot.appendChild($table_toolbar);
@@ -660,6 +679,28 @@ function build_ui(gobj)
660
679
  refresh_language($container, t);
661
680
  }
662
681
 
682
+ /************************************************************
683
+ * How many rows are ticked, on the bar.
684
+ *
685
+ * Only while the table is EDITABLE: outside edition its checkbox
686
+ * column is hidden (`hideColumn`), and a count of rows nobody can
687
+ * see or untick is a count you cannot act on. The selection itself
688
+ * is left alone -- coming back to edition finds it, and the bar
689
+ * recounts from the table.
690
+ ************************************************************/
691
+ function render_selection_bar(gobj)
692
+ {
693
+ let bar = gobj_read_attr(gobj, "selection_bar");
694
+ if(!bar) {
695
+ return;
696
+ }
697
+ if(!gobj_read_bool_attr(gobj, "editable")) {
698
+ bar.set_count(0);
699
+ return;
700
+ }
701
+ bar.set_count(yui_selected_rows(gobj_read_attr(gobj, "tabulator")).length);
702
+ }
703
+
663
704
  /************************************************************
664
705
  * Destroy UI
665
706
  ************************************************************/
@@ -2435,6 +2476,10 @@ function refuse_if_readonly(gobj, event)
2435
2476
  ***************************************************************/
2436
2477
  function ac_language_changed(gobj, event, kw, src)
2437
2478
  {
2479
+ let bar = gobj_read_attr(gobj, "selection_bar");
2480
+ if(bar) {
2481
+ bar.refresh(); /* a count composed at render time */
2482
+ }
2438
2483
  let tabulator = gobj_read_attr(gobj, "tabulator");
2439
2484
  if(!tabulator) {
2440
2485
  return 0;
@@ -2661,6 +2706,8 @@ function ac_edition_mode(gobj, event, kw, src)
2661
2706
  $button_paste_record.setAttribute("disabled", true);
2662
2707
  }
2663
2708
 
2709
+ render_selection_bar(gobj);
2710
+
2664
2711
  return 0;
2665
2712
  }
2666
2713
 
@@ -2900,6 +2947,8 @@ function ac_select_rows(gobj, event, kw, src)
2900
2947
  }
2901
2948
  }
2902
2949
 
2950
+ render_selection_bar(gobj);
2951
+
2903
2952
  if(gobj_read_bool_attr(gobj, "broadcast_select_rows_event")) {
2904
2953
  gobj_publish_event(gobj, event, kw);
2905
2954
  }
@@ -2931,7 +2980,10 @@ function ac_unselect_rows(gobj, event, kw, src)
2931
2980
  }
2932
2981
  }
2933
2982
 
2983
+ render_selection_bar(gobj);
2984
+
2934
2985
  // WARNING with radio, there is no unselect event.
2986
+
2935
2987
  if(gobj_read_bool_attr(gobj, "broadcast_select_rows_event")) {
2936
2988
  gobj_publish_event(gobj, event, kw);
2937
2989
  }
@@ -2939,6 +2991,16 @@ function ac_unselect_rows(gobj, event, kw, src)
2939
2991
  return 0;
2940
2992
  }
2941
2993
 
2994
+ /************************************************************
2995
+ * The way out of a selection (the bar's clear button).
2996
+ ************************************************************/
2997
+ function ac_clear_selection(gobj, event, kw, src)
2998
+ {
2999
+ yui_clear_selection(gobj_read_attr(gobj, "tabulator"));
3000
+ render_selection_bar(gobj);
3001
+ return 0;
3002
+ }
3003
+
2942
3004
  /************************************************************
2943
3005
  * { locale: "es" }
2944
3006
  ************************************************************/
@@ -3447,6 +3509,7 @@ function create_gclass(gclass_name)
3447
3509
  ["EV_NEW_ROW", ac_new_row, null],
3448
3510
  ["EV_DELETE_ROWS", ac_delete_rows, null],
3449
3511
  ["EV_SELECT_ROWS", ac_select_rows, null],
3512
+ ["EV_CLEAR_SELECTION", ac_clear_selection, null],
3450
3513
  ["EV_UNSELECT_ROWS", ac_unselect_rows, null],
3451
3514
  ["EV_COPY_ROWS", ac_copy_rows, null],
3452
3515
  ["EV_PASTE_ROWS", ac_paste_rows, null],
@@ -3485,6 +3548,7 @@ function create_gclass(gclass_name)
3485
3548
  ["EV_DELETE_ROWS", 0],
3486
3549
  ["EV_COPY_ROWS", 0],
3487
3550
  ["EV_PASTE_ROWS", 0],
3551
+ ["EV_CLEAR_SELECTION", 0],
3488
3552
  ["EV_SELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
3489
3553
  ["EV_UNSELECT_ROWS", event_flag_t.EVF_OUTPUT_EVENT],
3490
3554
  ["EV_SHOW_HOOK_DATA", event_flag_t.EVF_OUTPUT_EVENT],
@@ -76,6 +76,7 @@ SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote yuno for
76
76
  SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote TreeDB service name"),
77
77
  SDATA(data_type_t.DTP_JSON, "descs", 0, null, "Description of topics"),
78
78
  SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "Each topic table pulls its rows a PAGE at a time (`nodes` from/limit) instead of loading the topic whole. Safe against a backend that cannot page: it answers the whole list, which the table reads as one page"),
79
+ SDATA(data_type_t.DTP_BOOLEAN, "with_selection_bar", 0, false, "Each topic table shows the shared selection bar (\"N selected\" + a way out) above it while in edition mode. OFF by default: the bar takes its words from the HOST's i18n, so a host that turns it on must define \"{{n}} selected\" and \"clear selection\""),
79
80
  SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
80
81
  SDATA(data_type_t.DTP_STRING, "tabs_style", 0, "is-toggle is-fullwidth", "Bulma tab styling"),
81
82
  SDATA(data_type_t.DTP_BOOLEAN, "with_cards_landing",0, false, "Land on a grid of topic cards (list->detail): a card opens its table, with the tabs bar + a back-to-grid button. Off = tabs only (legacy)."),
@@ -1039,7 +1040,8 @@ function process_treedb_descs(gobj)
1039
1040
  treedb_name: treedb_name,
1040
1041
  topic_name: key,
1041
1042
  desc: desc,
1042
- with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging")
1043
+ with_remote_paging: gobj_read_bool_attr(gobj, "with_remote_paging"),
1044
+ with_selection_bar: gobj_read_bool_attr(gobj, "with_selection_bar")
1043
1045
  };
1044
1046
 
1045
1047
  let id = `${gobj_name(gobj)}?${desc.topic_name}`;
package/src/tabulator.css CHANGED
@@ -220,9 +220,7 @@
220
220
  */
221
221
  [data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing input,
222
222
  [data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing select,
223
- [data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing textarea,
224
- [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter input,
225
- [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter select {
223
+ [data-theme=dark] .tabulator-row .tabulator-cell.tabulator-editing textarea {
226
224
  background-color: var(--bulma-grey-darker, #2b2f38);
227
225
  color: var(--bulma-text-strong, #f5f6f7) !important;
228
226
  caret-color: var(--bulma-text-strong, #f5f6f7);
@@ -234,6 +232,44 @@
234
232
  color: var(--bulma-text-weak, #8a8f98);
235
233
  }
236
234
 
235
+ /* ====================================================================
236
+ * The HEADER FILTER box is FURNITURE, not an open editor.
237
+ *
238
+ * Both are inputs inside the table, so they shared these rules -- but an
239
+ * editor is a MODE: one cell, while you type into it, and deliberately the
240
+ * brightest thing on screen. A filter box is permanent and there is one per
241
+ * column, so wearing the editor's border a header of eight columns drew
242
+ * eight hard boxes across the top of every table, link-BLUE in dark theme.
243
+ *
244
+ * Discreet at rest -- a hairline in the theme's weakest border colour, no
245
+ * wash of its own -- and only the FOCUSED one stands out, which is the one
246
+ * you are typing in.
247
+ * ==================================================================== */
248
+ .tabulator .tabulator-header .tabulator-header-filter input,
249
+ .tabulator .tabulator-header .tabulator-header-filter select {
250
+ border: 1px solid var(--bulma-border-weak, #e8eaee);
251
+ border-radius: 3px;
252
+ background-color: transparent;
253
+ }
254
+
255
+ .tabulator .tabulator-header .tabulator-header-filter input:focus,
256
+ .tabulator .tabulator-header .tabulator-header-filter select:focus {
257
+ border-color: var(--bulma-link, #4a9eff);
258
+ outline: none;
259
+ }
260
+
261
+ [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter input,
262
+ [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter select {
263
+ border-color: var(--bulma-border, #3a3f4a);
264
+ color: var(--bulma-text-strong, #f5f6f7);
265
+ caret-color: var(--bulma-text-strong, #f5f6f7);
266
+ }
267
+
268
+ [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter input:focus,
269
+ [data-theme=dark] .tabulator .tabulator-header .tabulator-header-filter select:focus {
270
+ border-color: var(--bulma-link, #4a9eff);
271
+ }
272
+
237
273
  [data-theme=dark] .tabulator-row.tabulator-selected {
238
274
  background-color: var(--bulma-link) !important;
239
275
  color: var(--bulma-link-invert) !important;
@@ -313,9 +349,7 @@
313
349
  /* same editor fix as [data-theme=dark] above */
314
350
  [data-theme=system] .tabulator-row .tabulator-cell.tabulator-editing input,
315
351
  [data-theme=system] .tabulator-row .tabulator-cell.tabulator-editing select,
316
- [data-theme=system] .tabulator-row .tabulator-cell.tabulator-editing textarea,
317
- [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter input,
318
- [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter select {
352
+ [data-theme=system] .tabulator-row .tabulator-cell.tabulator-editing textarea {
319
353
  background-color: var(--bulma-grey-darker, #2b2f38);
320
354
  color: var(--bulma-text-strong, #f5f6f7) !important;
321
355
  caret-color: var(--bulma-text-strong, #f5f6f7);
@@ -325,6 +359,17 @@
325
359
  [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter input::placeholder {
326
360
  color: var(--bulma-text-weak, #8a8f98);
327
361
  }
362
+ /* same furniture-not-editor rule as [data-theme=dark] above */
363
+ [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter input,
364
+ [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter select {
365
+ border-color: var(--bulma-border, #3a3f4a);
366
+ color: var(--bulma-text-strong, #f5f6f7);
367
+ caret-color: var(--bulma-text-strong, #f5f6f7);
368
+ }
369
+ [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter input:focus,
370
+ [data-theme=system] .tabulator .tabulator-header .tabulator-header-filter select:focus {
371
+ border-color: var(--bulma-link, #4a9eff);
372
+ }
328
373
  [data-theme=system] .tabulator-row.tabulator-selected {
329
374
  background-color: var(--bulma-link) !important;
330
375
  color: var(--bulma-link-invert) !important;