@qnc/qnc_data_tables 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/intermediate/qnc_data_tables_inline_css.js +2 -2
  2. package/package.json +5 -3
  3. package/dist/bound_stored_value.d.ts +0 -39
  4. package/dist/bound_stored_value.js +0 -134
  5. package/dist/bound_stored_value.ts +0 -158
  6. package/dist/column_manager.d.ts +0 -45
  7. package/dist/column_manager.js +0 -124
  8. package/dist/column_manager.ts +0 -160
  9. package/dist/column_resizing.d.ts +0 -3
  10. package/dist/column_resizing.js +0 -30
  11. package/dist/column_resizing.ts +0 -52
  12. package/dist/column_sorting.d.ts +0 -11
  13. package/dist/column_sorting.js +0 -53
  14. package/dist/column_sorting.ts +0 -63
  15. package/dist/conditionally_wrapped_element.d.ts +0 -5
  16. package/dist/conditionally_wrapped_element.js +0 -14
  17. package/dist/conditionally_wrapped_element.ts +0 -17
  18. package/dist/create_mithril_app.d.ts +0 -3
  19. package/dist/create_mithril_app.js +0 -25
  20. package/dist/create_mithril_app.ts +0 -35
  21. package/dist/create_style.d.ts +0 -4
  22. package/dist/create_style.js +0 -9
  23. package/dist/create_style.ts +0 -10
  24. package/dist/custom_element.d.ts +0 -23
  25. package/dist/custom_element.js +0 -63
  26. package/dist/custom_element.ts +0 -71
  27. package/dist/event_names.d.ts +0 -1
  28. package/dist/event_names.js +0 -1
  29. package/dist/event_names.ts +0 -1
  30. package/dist/index.d.ts +0 -5
  31. package/dist/index.js +0 -4
  32. package/dist/index.ts +0 -8
  33. package/dist/mithril_view.d.ts +0 -16
  34. package/dist/mithril_view.js +0 -493
  35. package/dist/mithril_view.ts +0 -1029
  36. package/dist/optional_storage.d.ts +0 -6
  37. package/dist/optional_storage.js +0 -18
  38. package/dist/optional_storage.ts +0 -23
  39. package/dist/overflow_class_manager.d.ts +0 -20
  40. package/dist/overflow_class_manager.js +0 -30
  41. package/dist/overflow_class_manager.ts +0 -40
  42. package/dist/prepare_cell_html.d.ts +0 -5
  43. package/dist/prepare_cell_html.js +0 -38
  44. package/dist/prepare_cell_html.ts +0 -48
  45. package/dist/renderer.d.ts +0 -16
  46. package/dist/renderer.js +0 -51
  47. package/dist/renderer.ts +0 -86
  48. package/dist/selection_fieldset_controller.d.ts +0 -35
  49. package/dist/selection_fieldset_controller.js +0 -85
  50. package/dist/selection_fieldset_controller.ts +0 -104
  51. package/dist/state_machine.d.ts +0 -68
  52. package/dist/state_machine.js +0 -334
  53. package/dist/state_machine.ts +0 -458
  54. package/dist/state_types.d.ts +0 -62
  55. package/dist/state_types.js +0 -1
  56. package/dist/state_types.ts +0 -84
  57. package/dist/table_manager.d.ts +0 -10
  58. package/dist/table_manager.js +0 -19
  59. package/dist/table_manager.ts +0 -32
  60. package/dist/table_options.d.ts +0 -177
  61. package/dist/table_options.js +0 -109
  62. package/dist/table_options.ts +0 -150
  63. package/dist/tsconfig.tsbuildinfo +0 -1
  64. package/dist/watched_mutable_value.d.ts +0 -15
  65. package/dist/watched_mutable_value.js +0 -23
  66. package/dist/watched_mutable_value.ts +0 -23
@@ -1,6 +0,0 @@
1
- /** Two purposes: automatic key-prefixing, and the ability to create a "dummy storage" */
2
- export declare function optional_storage_wrapper(storage: Storage | null, key_prefix: string): {
3
- get(key: string): string | null;
4
- set(key: string, value: string): void;
5
- };
6
- export type OptionalStorage = ReturnType<typeof optional_storage_wrapper>;
@@ -1,18 +0,0 @@
1
- /** Two purposes: automatic key-prefixing, and the ability to create a "dummy storage" */
2
- export function optional_storage_wrapper(storage, key_prefix) {
3
- if (!storage)
4
- return {
5
- get(key) {
6
- return null;
7
- },
8
- set(key, value) { },
9
- };
10
- return {
11
- get(key) {
12
- return storage.getItem(`${key_prefix}${key}`);
13
- },
14
- set(key, value) {
15
- storage.setItem(`${key_prefix}${key}`, value);
16
- },
17
- };
18
- }
@@ -1,23 +0,0 @@
1
- /** Two purposes: automatic key-prefixing, and the ability to create a "dummy storage" */
2
- export function optional_storage_wrapper(
3
- storage: Storage | null,
4
- key_prefix: string,
5
- ) {
6
- if (!storage)
7
- return {
8
- get(key: string) {
9
- return null;
10
- },
11
- set(key: string, value: string) {},
12
- };
13
- return {
14
- get(key: string) {
15
- return storage.getItem(`${key_prefix}${key}`);
16
- },
17
- set(key: string, value: string) {
18
- storage.setItem(`${key_prefix}${key}`, value);
19
- },
20
- };
21
- }
22
-
23
- export type OptionalStorage = ReturnType<typeof optional_storage_wrapper>;
@@ -1,20 +0,0 @@
1
- /**
2
- Responsible for toggling "[PREFIX][SIDE]" classes on a possibly-overflowing element.
3
-
4
- Nothing project-specific here
5
- */
6
- export declare class OverflowClassManager {
7
- readonly root: Element;
8
- readonly class_prefix: string;
9
- constructor(root: Element, class_prefix: string);
10
- /**
11
- detector _may_ have zero size; that's okay (it _should_ have 0 size in at least one dimension)
12
-
13
- detector _may_ be longer (in one dimension) than root; that's also okay
14
-
15
- detector should be positioned [side]-most inside the root (this is tricky if root has padding).
16
-
17
- If root element can scroll in both directions, you need to be careful about how you setup your detectors. Eg. right detector should either be full-height, or should use sticky positioning so that it never goes completely above or completely below root. The only way right detector should not intersect (at all) with root is if it is to-the-right-of root.
18
- */
19
- setup_overflow_detector(detector: Element, side: "left" | "right" | "top" | "bottom"): void;
20
- }
@@ -1,30 +0,0 @@
1
- /**
2
- Responsible for toggling "[PREFIX][SIDE]" classes on a possibly-overflowing element.
3
-
4
- Nothing project-specific here
5
- */
6
- export class OverflowClassManager {
7
- constructor(root, class_prefix) {
8
- this.root = root;
9
- this.class_prefix = class_prefix;
10
- }
11
- /**
12
- detector _may_ have zero size; that's okay (it _should_ have 0 size in at least one dimension)
13
-
14
- detector _may_ be longer (in one dimension) than root; that's also okay
15
-
16
- detector should be positioned [side]-most inside the root (this is tricky if root has padding).
17
-
18
- If root element can scroll in both directions, you need to be careful about how you setup your detectors. Eg. right detector should either be full-height, or should use sticky positioning so that it never goes completely above or completely below root. The only way right detector should not intersect (at all) with root is if it is to-the-right-of root.
19
- */
20
- setup_overflow_detector(detector, side) {
21
- const class_name = `${this.class_prefix}${side}`;
22
- new IntersectionObserver((entries, observer) => {
23
- if (entries[0]) {
24
- this.root.classList.toggle(class_name, !entries[0].isIntersecting);
25
- }
26
- }, {
27
- root: this.root,
28
- }).observe(detector);
29
- }
30
- }
@@ -1,40 +0,0 @@
1
- /**
2
- Responsible for toggling "[PREFIX][SIDE]" classes on a possibly-overflowing element.
3
-
4
- Nothing project-specific here
5
- */
6
- export class OverflowClassManager {
7
- constructor(
8
- readonly root: Element,
9
- readonly class_prefix: string,
10
- ) {}
11
-
12
- /**
13
- detector _may_ have zero size; that's okay (it _should_ have 0 size in at least one dimension)
14
-
15
- detector _may_ be longer (in one dimension) than root; that's also okay
16
-
17
- detector should be positioned [side]-most inside the root (this is tricky if root has padding).
18
-
19
- If root element can scroll in both directions, you need to be careful about how you setup your detectors. Eg. right detector should either be full-height, or should use sticky positioning so that it never goes completely above or completely below root. The only way right detector should not intersect (at all) with root is if it is to-the-right-of root.
20
- */
21
- setup_overflow_detector(
22
- detector: Element,
23
- side: "left" | "right" | "top" | "bottom",
24
- ) {
25
- const class_name = `${this.class_prefix}${side}`;
26
- new IntersectionObserver(
27
- (entries, observer) => {
28
- if (entries[0]) {
29
- this.root.classList.toggle(
30
- class_name,
31
- !entries[0].isIntersecting,
32
- );
33
- }
34
- },
35
- {
36
- root: this.root,
37
- },
38
- ).observe(detector);
39
- }
40
- }
@@ -1,5 +0,0 @@
1
- /**
2
- If html represents exactly one node, and that node is an HTMLElement, return that element.
3
- Otherwise, wrap in a div.
4
- */
5
- export declare function prepare_cell_html(html: string): HTMLElement;
@@ -1,38 +0,0 @@
1
- /**
2
- If html represents exactly one node, and that node is an HTMLElement, return that element.
3
- Otherwise, wrap in a div.
4
- */
5
- export function prepare_cell_html(html) {
6
- const t = document.createElement("template");
7
- t.innerHTML = html;
8
- if (t.content.childNodes.length == 1 &&
9
- t.content.children[0] instanceof HTMLElement) {
10
- const element = t.content.children[0];
11
- if (element.hasAttribute("data-qdt-full-cell"))
12
- return element;
13
- if (element instanceof HTMLDivElement ||
14
- element instanceof HTMLAnchorElement) {
15
- return as_flex_container(element, "div");
16
- }
17
- if (element instanceof HTMLButtonElement ||
18
- element instanceof HTMLLabelElement) {
19
- return as_flex_container(element, "span");
20
- }
21
- return doubly_wrapped(html);
22
- }
23
- return doubly_wrapped(html);
24
- }
25
- function as_flex_container(element, wrapper_tag) {
26
- const inner = document.createElement(wrapper_tag);
27
- for (const child of Array.from(element.childNodes)) {
28
- inner.appendChild(child);
29
- }
30
- element.appendChild(inner);
31
- element.classList.add("qdt-flex-cell");
32
- return element;
33
- }
34
- function doubly_wrapped(content) {
35
- const outer = document.createElement("div");
36
- outer.innerHTML = content;
37
- return as_flex_container(outer, "div");
38
- }
@@ -1,48 +0,0 @@
1
- /**
2
- If html represents exactly one node, and that node is an HTMLElement, return that element.
3
- Otherwise, wrap in a div.
4
- */
5
- export function prepare_cell_html(html: string): HTMLElement {
6
- const t = document.createElement("template");
7
- t.innerHTML = html;
8
- if (
9
- t.content.childNodes.length == 1 &&
10
- t.content.children[0] instanceof HTMLElement
11
- ) {
12
- const element = t.content.children[0];
13
- if (element.hasAttribute("data-qdt-full-cell")) return element;
14
-
15
- if (
16
- element instanceof HTMLDivElement ||
17
- element instanceof HTMLAnchorElement
18
- ) {
19
- return as_flex_container(element, "div");
20
- }
21
- if (
22
- element instanceof HTMLButtonElement ||
23
- element instanceof HTMLLabelElement
24
- ) {
25
- return as_flex_container(element, "span");
26
- }
27
- return doubly_wrapped(html);
28
- }
29
- return doubly_wrapped(html);
30
- }
31
-
32
- function as_flex_container(
33
- element: HTMLElement,
34
- wrapper_tag: string,
35
- ): HTMLElement {
36
- const inner = document.createElement(wrapper_tag);
37
- for (const child of Array.from(element.childNodes)) {
38
- inner.appendChild(child);
39
- }
40
- element.appendChild(inner);
41
- element.classList.add("qdt-flex-cell");
42
- return element;
43
- }
44
- function doubly_wrapped(content: string): HTMLElement {
45
- const outer = document.createElement("div");
46
- outer.innerHTML = content;
47
- return as_flex_container(outer, "div");
48
- }
@@ -1,16 +0,0 @@
1
- import m from "mithril";
2
- export declare class Renderer {
3
- /**
4
- Given some phrasing content and some help text, return an "augmented" version of that phrasing content with some means of accessing the help text.
5
- The help text might be long; it should not take up space on the page.
6
- The final rendered result should not be much wider than the initial phrasing content (this may be used inside space-constrained locations, like <th> elements).
7
- */
8
- render_with_help_text(phrasing_content: m.Children, help: string): m.Vnode<any, any>;
9
- render_with_optional_help_text(phrasing_content: m.Children, help: string): m.Children;
10
- render_header(args: {
11
- label: m.Children;
12
- sortable: boolean;
13
- sorted: "no" | "forward" | "reverse";
14
- set_sort: (direction: "forward" | "reverse") => void;
15
- }): m.Vnode<any, any>;
16
- }
package/dist/renderer.js DELETED
@@ -1,51 +0,0 @@
1
- /*
2
- Here we implement a class for performing various low-level rendering operations, which users might want to override (ie. so that the generated markup works better with their stylesheet or to use custom components that are used elsewhere on their site). You should NOT implement a custom Renderer in order to change or add functionality (and we'll do our best with the method signatures/documentation to prevent that).
3
- */
4
- import m from "mithril";
5
- export class Renderer {
6
- /**
7
- Given some phrasing content and some help text, return an "augmented" version of that phrasing content with some means of accessing the help text.
8
- The help text might be long; it should not take up space on the page.
9
- The final rendered result should not be much wider than the initial phrasing content (this may be used inside space-constrained locations, like <th> elements).
10
- */
11
- render_with_help_text(phrasing_content, help) {
12
- return m("span", { title: help }, phrasing_content, m("span", {
13
- tabindex: 0,
14
- onclick: function (e) {
15
- alert(help);
16
- // This allows you to render help text inside other interactive elements
17
- e.preventDefault();
18
- e.stopPropagation();
19
- },
20
- onkeydown: function (e) {
21
- if (e.key == "Enter" || e.key == " ") {
22
- this.click();
23
- // in case you pressed space, don't scroll page
24
- e.preventDefault();
25
- }
26
- },
27
- className: "qnc-data-table-help-text",
28
- }));
29
- }
30
- render_with_optional_help_text(phrasing_content, help) {
31
- if (help)
32
- return this.render_with_help_text(phrasing_content, help);
33
- return phrasing_content;
34
- }
35
- render_header(args) {
36
- // entire header text is a (clickable) button (similar to many file browser and other UIs) :
37
- if (args.sortable)
38
- return m("button", {
39
- onclick: () => args.set_sort(args.sorted == "forward" ? "reverse" : "forward"),
40
- className: "qnc-data-table-header-button",
41
- type: "button",
42
- },
43
- // The wrapping span is so that you can see (in your browser's dev tools) exactly how wide the button content is, in case you want to fix column width at that width
44
- // (note - button _might_ also suffice for this, but users might style button as full-width, for increased click target size)
45
- m("span", m("span", { className: "qnc-data-table-header-button-text" }, args.label), args.sorted == "forward" &&
46
- m("span", { className: "qnc-data-table-sort-indicator" }, " ▲"), args.sorted == "reverse" &&
47
- m("span", { className: "qnc-data-table-sort-indicator" }, " ▼")));
48
- // The wrapping span is so that you can see (in your browser's dev tools) exactly how wide the text content is, in case you want to fix column width at that width
49
- return m("span", args.label);
50
- }
51
- }
package/dist/renderer.ts DELETED
@@ -1,86 +0,0 @@
1
- /*
2
- Here we implement a class for performing various low-level rendering operations, which users might want to override (ie. so that the generated markup works better with their stylesheet or to use custom components that are used elsewhere on their site). You should NOT implement a custom Renderer in order to change or add functionality (and we'll do our best with the method signatures/documentation to prevent that).
3
- */
4
- import m from "mithril";
5
-
6
- export class Renderer {
7
- /**
8
- Given some phrasing content and some help text, return an "augmented" version of that phrasing content with some means of accessing the help text.
9
- The help text might be long; it should not take up space on the page.
10
- The final rendered result should not be much wider than the initial phrasing content (this may be used inside space-constrained locations, like <th> elements).
11
- */
12
- render_with_help_text(phrasing_content: m.Children, help: string) {
13
- return m(
14
- "span",
15
- { title: help },
16
- phrasing_content,
17
- m("span", {
18
- tabindex: 0,
19
- onclick: function (e: MouseEvent) {
20
- alert(help);
21
- // This allows you to render help text inside other interactive elements
22
- e.preventDefault();
23
- e.stopPropagation();
24
- },
25
- onkeydown: function (e: KeyboardEvent) {
26
- if (e.key == "Enter" || e.key == " ") {
27
- this.click();
28
- // in case you pressed space, don't scroll page
29
- e.preventDefault();
30
- }
31
- },
32
- className: "qnc-data-table-help-text",
33
- }),
34
- );
35
- }
36
- render_with_optional_help_text(phrasing_content: m.Children, help: string) {
37
- if (help) return this.render_with_help_text(phrasing_content, help);
38
- return phrasing_content;
39
- }
40
- render_header(args: {
41
- label: m.Children;
42
- sortable: boolean;
43
- sorted: "no" | "forward" | "reverse";
44
- set_sort: (direction: "forward" | "reverse") => void;
45
- }) {
46
- // entire header text is a (clickable) button (similar to many file browser and other UIs) :
47
- if (args.sortable)
48
- return m(
49
- "button",
50
- {
51
- onclick: () =>
52
- args.set_sort(
53
- args.sorted == "forward" ? "reverse" : "forward",
54
- ),
55
- className: "qnc-data-table-header-button",
56
- type: "button",
57
- },
58
-
59
- // The wrapping span is so that you can see (in your browser's dev tools) exactly how wide the button content is, in case you want to fix column width at that width
60
- // (note - button _might_ also suffice for this, but users might style button as full-width, for increased click target size)
61
- m(
62
- "span",
63
- m(
64
- "span",
65
- { className: "qnc-data-table-header-button-text" },
66
- args.label,
67
- ),
68
- args.sorted == "forward" &&
69
- m(
70
- "span",
71
- { className: "qnc-data-table-sort-indicator" },
72
- " ▲",
73
- ),
74
- args.sorted == "reverse" &&
75
- m(
76
- "span",
77
- { className: "qnc-data-table-sort-indicator" },
78
- " ▼",
79
- ),
80
- ),
81
- );
82
-
83
- // The wrapping span is so that you can see (in your browser's dev tools) exactly how wide the text content is, in case you want to fix column width at that width
84
- return m("span", args.label);
85
- }
86
- }
@@ -1,35 +0,0 @@
1
- import { TableManager } from "./table_manager.js";
2
- /**
3
- Intended to wrap a <fieldset> element of "selection action buttons".
4
- Responsible for:
5
- - dynamically updating fieldset's "disabled" attribute
6
- - dynamically updating fieldset's legend (to include # of selected items)
7
- - providing access to the TableManager of the connected data table
8
-
9
- Intended for use when you're generating selection action buttons on your backend (in initial page HTML).
10
-
11
- If you're building selection action buttons on the front-end, this is probably not the best approach.
12
- */
13
- export declare class QdtSelectionFieldsetController extends HTMLElement {
14
- private fieldset;
15
- private legend;
16
- private table;
17
- private on_selection_change;
18
- constructor();
19
- connectedCallback(): void;
20
- private static get selected_attributes();
21
- attributeChangedCallback(name: string, old_value: string, new_value: string): void;
22
- private connect_table;
23
- private connect_fieldset;
24
- private render;
25
- required_table_manager(): TableManager;
26
- }
27
- /**
28
- Registers a custom element (<qdt-selection-fieldset-controller>) for wrapping a <fieldset> element.
29
- The custom element:
30
- - requires a "table-id" attribute to be set, which must be the id of a data table element
31
- - dynamically updates the fieldset's "disabled" attribute
32
- - dynamically updates the fieldset's legend (to include # of selected items)
33
- - provides access to the TableManager of the connected data table
34
- */
35
- export declare function register_selection_fieldset_controller_custom_element(): void;
@@ -1,85 +0,0 @@
1
- import { BaseQncDataTable } from "./custom_element.js";
2
- /**
3
- Intended to wrap a <fieldset> element of "selection action buttons".
4
- Responsible for:
5
- - dynamically updating fieldset's "disabled" attribute
6
- - dynamically updating fieldset's legend (to include # of selected items)
7
- - providing access to the TableManager of the connected data table
8
-
9
- Intended for use when you're generating selection action buttons on your backend (in initial page HTML).
10
-
11
- If you're building selection action buttons on the front-end, this is probably not the best approach.
12
- */
13
- export class QdtSelectionFieldsetController extends HTMLElement {
14
- constructor() {
15
- super();
16
- this.fieldset = null;
17
- this.legend = null;
18
- this.table = null;
19
- this.on_selection_change = () => this.render();
20
- }
21
- connectedCallback() {
22
- this.connect_table();
23
- this.connect_fieldset();
24
- this.render();
25
- }
26
- static get selected_attributes() {
27
- return ["table-id"];
28
- }
29
- attributeChangedCallback(name, old_value, new_value) {
30
- this.connect_table();
31
- this.render();
32
- }
33
- connect_table() {
34
- this.table = null;
35
- const id = this.getAttribute("table-id");
36
- if (!id) {
37
- console.error(`${this.tagName} requires "table-id" attribute`);
38
- return;
39
- }
40
- const table = document.getElementById(id);
41
- if (!table) {
42
- console.error(`"${id}" does not reference any element by id`);
43
- return;
44
- }
45
- if (!(table instanceof BaseQncDataTable)) {
46
- console.error(`"${id}" references an HTML element, but it is not a BaseQncDataTable`);
47
- return;
48
- }
49
- this.table = table;
50
- this.table
51
- .required_table_manager()
52
- .add_selected_ids_changed_listener(this.on_selection_change);
53
- }
54
- connect_fieldset() {
55
- this.fieldset = this.querySelector("fieldset");
56
- if (!this.fieldset)
57
- console.warn("Could not find a fieldset descendant. Our presence is meaningless.");
58
- this.legend = this.querySelector("legend");
59
- }
60
- render() {
61
- const selected_count = this.table
62
- ? this.table.required_table_manager().selected_ids.size
63
- : 0;
64
- if (this.fieldset)
65
- this.fieldset.disabled = selected_count == 0;
66
- if (this.legend)
67
- this.legend.innerText = `With ${selected_count} selected item(s):`;
68
- }
69
- required_table_manager() {
70
- if (!this.table)
71
- throw new Error("We are not connected to a table");
72
- return this.table.required_table_manager();
73
- }
74
- }
75
- /**
76
- Registers a custom element (<qdt-selection-fieldset-controller>) for wrapping a <fieldset> element.
77
- The custom element:
78
- - requires a "table-id" attribute to be set, which must be the id of a data table element
79
- - dynamically updates the fieldset's "disabled" attribute
80
- - dynamically updates the fieldset's legend (to include # of selected items)
81
- - provides access to the TableManager of the connected data table
82
- */
83
- export function register_selection_fieldset_controller_custom_element() {
84
- customElements.define("qdt-selection-fieldset-controller", QdtSelectionFieldsetController);
85
- }
@@ -1,104 +0,0 @@
1
- import { TableManager } from "./table_manager.js";
2
- import { BaseQncDataTable } from "./custom_element.js";
3
-
4
- /**
5
- Intended to wrap a <fieldset> element of "selection action buttons".
6
- Responsible for:
7
- - dynamically updating fieldset's "disabled" attribute
8
- - dynamically updating fieldset's legend (to include # of selected items)
9
- - providing access to the TableManager of the connected data table
10
-
11
- Intended for use when you're generating selection action buttons on your backend (in initial page HTML).
12
-
13
- If you're building selection action buttons on the front-end, this is probably not the best approach.
14
- */
15
- export class QdtSelectionFieldsetController extends HTMLElement {
16
- private fieldset: HTMLFieldSetElement | null = null;
17
- private legend: HTMLLegendElement | null = null;
18
- private table: BaseQncDataTable | null = null;
19
-
20
- private on_selection_change: () => void;
21
-
22
- constructor() {
23
- super();
24
- this.on_selection_change = () => this.render();
25
- }
26
-
27
- connectedCallback() {
28
- this.connect_table();
29
- this.connect_fieldset();
30
- this.render();
31
- }
32
-
33
- private static get selected_attributes() {
34
- return ["table-id"];
35
- }
36
-
37
- attributeChangedCallback(
38
- name: string,
39
- old_value: string,
40
- new_value: string,
41
- ) {
42
- this.connect_table();
43
- this.render();
44
- }
45
- private connect_table() {
46
- this.table = null;
47
-
48
- const id = this.getAttribute("table-id");
49
- if (!id) {
50
- console.error(`${this.tagName} requires "table-id" attribute`);
51
- return;
52
- }
53
- const table = document.getElementById(id);
54
- if (!table) {
55
- console.error(`"${id}" does not reference any element by id`);
56
- return;
57
- }
58
- if (!(table instanceof BaseQncDataTable)) {
59
- console.error(
60
- `"${id}" references an HTML element, but it is not a BaseQncDataTable`,
61
- );
62
- return;
63
- }
64
- this.table = table;
65
- this.table
66
- .required_table_manager()
67
- .add_selected_ids_changed_listener(this.on_selection_change);
68
- }
69
- private connect_fieldset() {
70
- this.fieldset = this.querySelector("fieldset");
71
- if (!this.fieldset)
72
- console.warn(
73
- "Could not find a fieldset descendant. Our presence is meaningless.",
74
- );
75
- this.legend = this.querySelector("legend");
76
- }
77
- private render() {
78
- const selected_count = this.table
79
- ? this.table.required_table_manager().selected_ids.size
80
- : 0;
81
-
82
- if (this.fieldset) this.fieldset.disabled = selected_count == 0;
83
- if (this.legend)
84
- this.legend.innerText = `With ${selected_count} selected item(s):`;
85
- }
86
- required_table_manager(): TableManager {
87
- if (!this.table) throw new Error("We are not connected to a table");
88
- return this.table.required_table_manager();
89
- }
90
- }
91
- /**
92
- Registers a custom element (<qdt-selection-fieldset-controller>) for wrapping a <fieldset> element.
93
- The custom element:
94
- - requires a "table-id" attribute to be set, which must be the id of a data table element
95
- - dynamically updates the fieldset's "disabled" attribute
96
- - dynamically updates the fieldset's legend (to include # of selected items)
97
- - provides access to the TableManager of the connected data table
98
- */
99
- export function register_selection_fieldset_controller_custom_element() {
100
- customElements.define(
101
- "qdt-selection-fieldset-controller",
102
- QdtSelectionFieldsetController,
103
- );
104
- }