@qnc/qnc_data_tables 1.0.3 → 1.0.6-a
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 +36 -0
- package/dist/bound_stored_value.d.ts +39 -0
- package/dist/bound_stored_value.js +134 -0
- package/dist/bound_stored_value.ts +158 -0
- package/dist/column_manager.d.ts +43 -0
- package/dist/column_manager.js +124 -0
- package/dist/column_manager.ts +158 -0
- package/dist/column_resizing.d.ts +3 -0
- package/dist/column_resizing.js +30 -0
- package/dist/column_resizing.ts +52 -0
- package/dist/column_sorting.d.ts +11 -0
- package/dist/column_sorting.js +53 -0
- package/dist/column_sorting.ts +63 -0
- package/dist/conditionally_wrapped_element.d.ts +5 -0
- package/dist/conditionally_wrapped_element.js +14 -0
- package/dist/conditionally_wrapped_element.ts +17 -0
- package/dist/create_mithril_app.d.ts +3 -0
- package/dist/create_mithril_app.js +25 -0
- package/dist/create_mithril_app.ts +35 -0
- package/dist/create_style.d.ts +4 -0
- package/dist/create_style.js +9 -0
- package/dist/create_style.ts +10 -0
- package/dist/custom_element.d.ts +23 -0
- package/dist/custom_element.js +63 -0
- package/dist/custom_element.ts +71 -0
- package/dist/event_names.d.ts +1 -0
- package/dist/event_names.js +1 -0
- package/dist/event_names.ts +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/index.ts +5 -0
- package/dist/mithril_view.d.ts +16 -0
- package/dist/mithril_view.js +484 -0
- package/dist/mithril_view.ts +1014 -0
- package/dist/optional_storage.d.ts +6 -0
- package/dist/optional_storage.js +18 -0
- package/dist/optional_storage.ts +23 -0
- package/dist/overflow_class_manager.d.ts +20 -0
- package/dist/overflow_class_manager.js +30 -0
- package/dist/overflow_class_manager.ts +40 -0
- package/dist/renderer.d.ts +16 -0
- package/dist/renderer.js +51 -0
- package/dist/renderer.ts +86 -0
- package/dist/selection_fieldset_controller.d.ts +9 -0
- package/dist/selection_fieldset_controller.js +85 -0
- package/dist/selection_fieldset_controller.ts +104 -0
- package/dist/state_machine.d.ts +67 -0
- package/dist/state_machine.js +316 -0
- package/dist/state_machine.ts +434 -0
- package/dist/state_types.d.ts +62 -0
- package/dist/state_types.js +1 -0
- package/dist/state_types.ts +84 -0
- package/dist/table_manager.d.ts +9 -0
- package/dist/table_manager.js +16 -0
- package/dist/table_manager.ts +28 -0
- package/dist/table_options.d.ts +164 -0
- package/dist/table_options.js +97 -0
- package/dist/table_options.ts +132 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/watched_mutable_value.d.ts +15 -0
- package/dist/watched_mutable_value.js +23 -0
- package/dist/watched_mutable_value.ts +23 -0
- package/package.json +12 -5
- package/dist/qnc_data_tables.d.ts +0 -57
- package/dist/qnc_data_tables.js +0 -1136
- package/dist/qnc_data_tables_inline_css.js +0 -8
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Column } from "./table_options.js";
|
|
2
|
+
import * as ss from "superstruct";
|
|
3
|
+
import * as tu from "@qnc/type_utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
Responsible for
|
|
7
|
+
- reading TableOptions.columns and then interpreting and updating user's stored preferences
|
|
8
|
+
*/
|
|
9
|
+
export class ColumnManager {
|
|
10
|
+
#fixed_columns: ManagedColumn[];
|
|
11
|
+
#sortable_columns: ManagedColumn[];
|
|
12
|
+
#all_columns: Map<string, ManagedColumn>;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private columns: Column[],
|
|
16
|
+
private storage: Storage | null,
|
|
17
|
+
private storage_key: string,
|
|
18
|
+
public on_any_change: () => void,
|
|
19
|
+
) {
|
|
20
|
+
if (columns.length != new Set(columns.map((c) => c.key)).size)
|
|
21
|
+
throw new Error("columns do not all have unique keys");
|
|
22
|
+
|
|
23
|
+
this.#all_columns = new Map();
|
|
24
|
+
this.#fixed_columns = [];
|
|
25
|
+
this.#sortable_columns = [];
|
|
26
|
+
|
|
27
|
+
for (const c of columns) {
|
|
28
|
+
const mutable_column: ManagedColumn = {
|
|
29
|
+
key: c.key,
|
|
30
|
+
display: c.display,
|
|
31
|
+
help_text: c.help_text,
|
|
32
|
+
width: c.width,
|
|
33
|
+
enabled: c.enabled,
|
|
34
|
+
sortable: c.sortable,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
this.#all_columns.set(c.key, c);
|
|
38
|
+
if (c.fixed) {
|
|
39
|
+
this.#fixed_columns.push(c);
|
|
40
|
+
} else {
|
|
41
|
+
this.#sortable_columns.push(c);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// update state based on user preferences
|
|
46
|
+
if (storage) {
|
|
47
|
+
const config = tu.parse_json(
|
|
48
|
+
storage.getItem(this.storage_key) ?? "",
|
|
49
|
+
);
|
|
50
|
+
if (ss.is(config, UserPreferencesStruct)) {
|
|
51
|
+
this.#read_order(config.map((column) => column.key));
|
|
52
|
+
|
|
53
|
+
for (const { key, enabled, width } of config) {
|
|
54
|
+
const column = this.#all_columns.get(key);
|
|
55
|
+
if (!column) continue; // previously saved column is no longer available; no problem, just ignore it
|
|
56
|
+
column.enabled = enabled;
|
|
57
|
+
column.width = width;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** returns them in display order */
|
|
64
|
+
get all_enabled_columns(): ColumnList {
|
|
65
|
+
return this.#fixed_columns.concat(
|
|
66
|
+
this.#sortable_columns.filter((c) => c.enabled),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
/** returns them in display order */
|
|
70
|
+
get fixed_enabled_columns(): ColumnList {
|
|
71
|
+
return this.#fixed_columns;
|
|
72
|
+
}
|
|
73
|
+
/** returns them in display order */
|
|
74
|
+
get sortable_enabled_columns(): ColumnList {
|
|
75
|
+
return this.#sortable_columns.filter((c) => c.enabled);
|
|
76
|
+
}
|
|
77
|
+
/** returns them in sorted order */
|
|
78
|
+
get all_sortable_columns(): ColumnList {
|
|
79
|
+
return this.#sortable_columns;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** the only keys that matter are those that correspond to sortable columns; if you pass us keys of fixed columns, we simply ignore those */
|
|
83
|
+
#read_order(keys: Array<string>) {
|
|
84
|
+
this.#sortable_columns.sort(function (
|
|
85
|
+
a: ManagedColumn,
|
|
86
|
+
b: ManagedColumn,
|
|
87
|
+
) {
|
|
88
|
+
return keys.indexOf(a.key) - keys.indexOf(b.key);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** the only keys that matter are those that correspond to sortable columns; if you pass us keys of fixed columns, we simply ignore those */
|
|
93
|
+
set_order(keys: Array<string>) {
|
|
94
|
+
this.#read_order(keys);
|
|
95
|
+
this.#save();
|
|
96
|
+
this.on_any_change();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
set_enabled(key: string, enabled: boolean) {
|
|
100
|
+
const column = this.#all_columns.get(key);
|
|
101
|
+
if (!column) throw new Error(`No column with key ${key}`);
|
|
102
|
+
column.enabled = enabled;
|
|
103
|
+
this.#save();
|
|
104
|
+
this.on_any_change();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set_width(key: string, width: number) {
|
|
108
|
+
const column = this.#all_columns.get(key);
|
|
109
|
+
if (!column) throw new Error(`No column with key ${key}`);
|
|
110
|
+
column.width = width;
|
|
111
|
+
this.#save();
|
|
112
|
+
this.on_any_change();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#save() {
|
|
116
|
+
if (!this.storage) return;
|
|
117
|
+
const columns = this.#fixed_columns.concat(this.#sortable_columns);
|
|
118
|
+
this.storage.setItem(
|
|
119
|
+
this.storage_key,
|
|
120
|
+
JSON.stringify(
|
|
121
|
+
columns.map((column) => ({
|
|
122
|
+
key: column.key,
|
|
123
|
+
enabled: column.enabled,
|
|
124
|
+
width: column.width,
|
|
125
|
+
})),
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Readonly view of ColumnManager; can be passed to view layer */
|
|
132
|
+
export type ConfiguredColumns = {
|
|
133
|
+
all_enabled_columns: ColumnList;
|
|
134
|
+
fixed_enabled_columns: ColumnList;
|
|
135
|
+
sortable_enabled_columns: ColumnList;
|
|
136
|
+
all_sortable_columns: ColumnList;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
type ManagedColumn = {
|
|
140
|
+
key: string;
|
|
141
|
+
display: string;
|
|
142
|
+
help_text: string;
|
|
143
|
+
sortable: boolean;
|
|
144
|
+
enabled: boolean;
|
|
145
|
+
width: number;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
type ColumnList = ReadonlyArray<Readonly<ManagedColumn>>;
|
|
149
|
+
|
|
150
|
+
export type ConfiguredColumn = Readonly<ManagedColumn>;
|
|
151
|
+
|
|
152
|
+
const UserPreferencesStruct = ss.array(
|
|
153
|
+
ss.object({
|
|
154
|
+
key: ss.string(),
|
|
155
|
+
width: ss.number(),
|
|
156
|
+
enabled: ss.boolean(),
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
let resizing = null;
|
|
2
|
+
export function begin_header_drag(event, on_resize, initial_width, on_complete) {
|
|
3
|
+
complete_any_active_resizing();
|
|
4
|
+
const style = document.createElement("style");
|
|
5
|
+
style.innerHTML = "* {cursor: col-resize !important}";
|
|
6
|
+
document.body.appendChild(style);
|
|
7
|
+
resizing = {
|
|
8
|
+
style,
|
|
9
|
+
on_resize,
|
|
10
|
+
initial_width,
|
|
11
|
+
initial_x: event.clientX,
|
|
12
|
+
on_complete,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
window.addEventListener("mousemove", function (event) {
|
|
16
|
+
if (!resizing)
|
|
17
|
+
return;
|
|
18
|
+
event.preventDefault(); // prevent text selection
|
|
19
|
+
const new_width = Math.max(0, resizing.initial_width + event.clientX - resizing.initial_x);
|
|
20
|
+
resizing.on_resize(new_width);
|
|
21
|
+
});
|
|
22
|
+
function complete_any_active_resizing() {
|
|
23
|
+
if (!resizing)
|
|
24
|
+
return;
|
|
25
|
+
resizing.style.remove();
|
|
26
|
+
const on_complete = resizing.on_complete;
|
|
27
|
+
resizing = null;
|
|
28
|
+
on_complete();
|
|
29
|
+
}
|
|
30
|
+
window.addEventListener("mouseup", complete_any_active_resizing);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
type ResizeCallback = (width: number) => void;
|
|
2
|
+
|
|
3
|
+
type ColumnResizing = {
|
|
4
|
+
style: HTMLStyleElement;
|
|
5
|
+
on_resize: ResizeCallback;
|
|
6
|
+
initial_x: number;
|
|
7
|
+
initial_width: number;
|
|
8
|
+
on_complete: () => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let resizing: ColumnResizing | null = null;
|
|
12
|
+
|
|
13
|
+
export function begin_header_drag(
|
|
14
|
+
event: MouseEvent,
|
|
15
|
+
on_resize: ResizeCallback,
|
|
16
|
+
initial_width: number,
|
|
17
|
+
on_complete: () => void,
|
|
18
|
+
) {
|
|
19
|
+
complete_any_active_resizing();
|
|
20
|
+
const style = document.createElement("style");
|
|
21
|
+
style.innerHTML = "* {cursor: col-resize !important}";
|
|
22
|
+
document.body.appendChild(style);
|
|
23
|
+
resizing = {
|
|
24
|
+
style,
|
|
25
|
+
on_resize,
|
|
26
|
+
initial_width,
|
|
27
|
+
initial_x: event.clientX,
|
|
28
|
+
on_complete,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
window.addEventListener("mousemove", function (event) {
|
|
32
|
+
if (!resizing) return;
|
|
33
|
+
event.preventDefault(); // prevent text selection
|
|
34
|
+
|
|
35
|
+
const new_width = Math.max(
|
|
36
|
+
0,
|
|
37
|
+
resizing.initial_width + event.clientX - resizing.initial_x,
|
|
38
|
+
);
|
|
39
|
+
resizing.on_resize(new_width);
|
|
40
|
+
});
|
|
41
|
+
function complete_any_active_resizing() {
|
|
42
|
+
if (!resizing) return;
|
|
43
|
+
resizing.style.remove();
|
|
44
|
+
|
|
45
|
+
const on_complete = resizing.on_complete;
|
|
46
|
+
|
|
47
|
+
resizing = null;
|
|
48
|
+
|
|
49
|
+
on_complete();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
window.addEventListener("mouseup", complete_any_active_resizing);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SortState } from "./state_types.js";
|
|
2
|
+
export declare function make_column_sort_key(column_key: string, direction: "forward" | "reverse"): string;
|
|
3
|
+
export declare function make_function_sort_key(function_key: string): string;
|
|
4
|
+
export declare function parse_sort_key(sort_key: string): SortState | null;
|
|
5
|
+
export declare function encode_sort(sort: SortState | null): string;
|
|
6
|
+
/**
|
|
7
|
+
sort_key identifies the current "sorting function" of the table
|
|
8
|
+
column_key can represent any column
|
|
9
|
+
return value indicates whether the current sorting function is sorting by this column, and if so, which direction
|
|
10
|
+
*/
|
|
11
|
+
export declare function current_sort_direction(column_key: string, sort_key: string): "forward" | "reverse" | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as tu from "@qnc/type_utils";
|
|
2
|
+
// Note: this logic is duplicated in (and coupled to) the back-end
|
|
3
|
+
const column_key_regex = /^_column_((?:forward)|(?:reverse))_(.*)$/;
|
|
4
|
+
export function make_column_sort_key(column_key, direction) {
|
|
5
|
+
return `_column_${direction}_${column_key}`;
|
|
6
|
+
}
|
|
7
|
+
const function_key_regex = /^function_(.*)$/;
|
|
8
|
+
export function make_function_sort_key(function_key) {
|
|
9
|
+
return `function_${function_key}`;
|
|
10
|
+
}
|
|
11
|
+
export function parse_sort_key(sort_key) {
|
|
12
|
+
if (sort_key == "")
|
|
13
|
+
return null;
|
|
14
|
+
let match = sort_key.match(column_key_regex);
|
|
15
|
+
if (match) {
|
|
16
|
+
return {
|
|
17
|
+
type: "column",
|
|
18
|
+
column_key: match[2],
|
|
19
|
+
direction: match[1],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
match = sort_key.match(function_key_regex);
|
|
23
|
+
if (match) {
|
|
24
|
+
return {
|
|
25
|
+
type: "function",
|
|
26
|
+
function_key: match[1],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`Invalid sort key: ${sort_key}`);
|
|
30
|
+
}
|
|
31
|
+
export function encode_sort(sort) {
|
|
32
|
+
if (!sort)
|
|
33
|
+
return "";
|
|
34
|
+
if (sort.type == "column") {
|
|
35
|
+
return make_column_sort_key(sort.column_key, sort.direction);
|
|
36
|
+
}
|
|
37
|
+
if (sort.type == "function") {
|
|
38
|
+
return make_function_sort_key(sort.function_key);
|
|
39
|
+
}
|
|
40
|
+
tu.assert_never(sort);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
sort_key identifies the current "sorting function" of the table
|
|
44
|
+
column_key can represent any column
|
|
45
|
+
return value indicates whether the current sorting function is sorting by this column, and if so, which direction
|
|
46
|
+
*/
|
|
47
|
+
export function current_sort_direction(column_key, sort_key) {
|
|
48
|
+
if (sort_key == make_column_sort_key(column_key, "forward"))
|
|
49
|
+
return "forward";
|
|
50
|
+
if (sort_key == make_column_sort_key(column_key, "reverse"))
|
|
51
|
+
return "reverse";
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { SortState } from "./state_types.js";
|
|
2
|
+
import * as tu from "@qnc/type_utils";
|
|
3
|
+
|
|
4
|
+
// Note: this logic is duplicated in (and coupled to) the back-end
|
|
5
|
+
const column_key_regex = /^_column_((?:forward)|(?:reverse))_(.*)$/;
|
|
6
|
+
export function make_column_sort_key(
|
|
7
|
+
column_key: string,
|
|
8
|
+
direction: "forward" | "reverse",
|
|
9
|
+
) {
|
|
10
|
+
return `_column_${direction}_${column_key}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const function_key_regex = /^function_(.*)$/;
|
|
14
|
+
export function make_function_sort_key(function_key: string) {
|
|
15
|
+
return `function_${function_key}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parse_sort_key(sort_key: string): SortState | null {
|
|
19
|
+
if (sort_key == "") return null;
|
|
20
|
+
let match = sort_key.match(column_key_regex);
|
|
21
|
+
if (match) {
|
|
22
|
+
return {
|
|
23
|
+
type: "column",
|
|
24
|
+
column_key: match[2] as string,
|
|
25
|
+
direction: match[1] as "forward" | "reverse",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
match = sort_key.match(function_key_regex);
|
|
29
|
+
if (match) {
|
|
30
|
+
return {
|
|
31
|
+
type: "function",
|
|
32
|
+
function_key: match[1] as string,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Invalid sort key: ${sort_key}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function encode_sort(sort: SortState | null) {
|
|
39
|
+
if (!sort) return "";
|
|
40
|
+
if (sort.type == "column") {
|
|
41
|
+
return make_column_sort_key(sort.column_key, sort.direction);
|
|
42
|
+
}
|
|
43
|
+
if (sort.type == "function") {
|
|
44
|
+
return make_function_sort_key(sort.function_key);
|
|
45
|
+
}
|
|
46
|
+
tu.assert_never(sort);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
sort_key identifies the current "sorting function" of the table
|
|
51
|
+
column_key can represent any column
|
|
52
|
+
return value indicates whether the current sorting function is sorting by this column, and if so, which direction
|
|
53
|
+
*/
|
|
54
|
+
export function current_sort_direction(
|
|
55
|
+
column_key: string,
|
|
56
|
+
sort_key: string,
|
|
57
|
+
): "forward" | "reverse" | null {
|
|
58
|
+
if (sort_key == make_column_sort_key(column_key, "forward"))
|
|
59
|
+
return "forward";
|
|
60
|
+
if (sort_key == make_column_sort_key(column_key, "reverse"))
|
|
61
|
+
return "reverse";
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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 conditionally_wrapped_element(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
|
+
return t.content.children[0];
|
|
11
|
+
const d = document.createElement("div");
|
|
12
|
+
d.innerHTML = html;
|
|
13
|
+
return d;
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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 conditionally_wrapped_element(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
|
+
return t.content.children[0];
|
|
13
|
+
|
|
14
|
+
const d = document.createElement("div");
|
|
15
|
+
d.innerHTML = html;
|
|
16
|
+
return d;
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import m from "mithril";
|
|
2
|
+
import { app_view } from "./mithril_view.js";
|
|
3
|
+
export function create_mithril_app(container, state_machine, renderer) {
|
|
4
|
+
const updaters = {
|
|
5
|
+
sort_by_function: state_machine.sort_by_function.bind(state_machine),
|
|
6
|
+
sort_by_column: state_machine.sort_by_column.bind(state_machine),
|
|
7
|
+
set_page_size: state_machine.set_page_size.bind(state_machine),
|
|
8
|
+
set_page_number: state_machine.set_page_number.bind(state_machine),
|
|
9
|
+
set_column_enabled: state_machine.set_column_enabled.bind(state_machine),
|
|
10
|
+
set_column_width: state_machine.set_column_width.bind(state_machine),
|
|
11
|
+
set_column_order: state_machine.set_column_order.bind(state_machine),
|
|
12
|
+
set_selected(pk, selected) {
|
|
13
|
+
state_machine.set_selected(pk, selected);
|
|
14
|
+
},
|
|
15
|
+
bulk_set_selected(pks, selected) {
|
|
16
|
+
state_machine.bulk_set_selected(pks, selected);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const component = {
|
|
20
|
+
view() {
|
|
21
|
+
return app_view(state_machine.get_state(), renderer, updaters);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
m.mount(container, component);
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { StateMachine } from "./state_machine.js";
|
|
2
|
+
import m from "mithril";
|
|
3
|
+
import { app_view } from "./mithril_view.js";
|
|
4
|
+
import { Renderer } from "./renderer.js";
|
|
5
|
+
|
|
6
|
+
export function create_mithril_app(
|
|
7
|
+
container: HTMLElement,
|
|
8
|
+
state_machine: StateMachine<string>,
|
|
9
|
+
renderer: Renderer,
|
|
10
|
+
) {
|
|
11
|
+
const updaters = {
|
|
12
|
+
sort_by_function: state_machine.sort_by_function.bind(state_machine),
|
|
13
|
+
sort_by_column: state_machine.sort_by_column.bind(state_machine),
|
|
14
|
+
set_page_size: state_machine.set_page_size.bind(state_machine),
|
|
15
|
+
set_page_number: state_machine.set_page_number.bind(state_machine),
|
|
16
|
+
set_column_enabled:
|
|
17
|
+
state_machine.set_column_enabled.bind(state_machine),
|
|
18
|
+
set_column_width: state_machine.set_column_width.bind(state_machine),
|
|
19
|
+
set_column_order: state_machine.set_column_order.bind(state_machine),
|
|
20
|
+
set_selected(pk: string, selected: boolean) {
|
|
21
|
+
state_machine.set_selected(pk, selected);
|
|
22
|
+
},
|
|
23
|
+
bulk_set_selected(pks: Iterable<string>, selected: boolean) {
|
|
24
|
+
state_machine.bulk_set_selected(pks, selected);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const component = {
|
|
29
|
+
view() {
|
|
30
|
+
return app_view(state_machine.get_state(), renderer, updaters);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
m.mount(container, component);
|
|
35
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import css from "../intermediate/qnc_data_tables_inline_css.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a <style> element that users should inject into their document somewhere.
|
|
4
|
+
*/
|
|
5
|
+
export function create_style() {
|
|
6
|
+
const style = document.createElement("style");
|
|
7
|
+
style.innerText = css;
|
|
8
|
+
return style;
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import css from "../intermediate/qnc_data_tables_inline_css.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a <style> element that users should inject into their document somewhere.
|
|
5
|
+
*/
|
|
6
|
+
export function create_style() {
|
|
7
|
+
const style = document.createElement("style");
|
|
8
|
+
style.innerText = css;
|
|
9
|
+
return style;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Renderer } from "./renderer.js";
|
|
2
|
+
import { TableManager } from "./table_manager.js";
|
|
3
|
+
/**
|
|
4
|
+
Register the given tag name with customElements as data-table container which always uses the given Renderer.
|
|
5
|
+
Most sites will only need to call this once.
|
|
6
|
+
You _may_ call this multiple times, if you want to have multiple "table styles", using different Renderers.
|
|
7
|
+
|
|
8
|
+
Note that any element defined via this function must also have a `table-config` attribute set. This attribute must be a JSON encoded TableOptions object (ie. from table_options.ts).
|
|
9
|
+
*/
|
|
10
|
+
export declare function define_custom_element(tag_name: string, renderer: Renderer): void;
|
|
11
|
+
export declare class BaseQncDataTable extends HTMLElement {
|
|
12
|
+
#private;
|
|
13
|
+
get renderer(): Renderer;
|
|
14
|
+
private _table_manager;
|
|
15
|
+
static get observed_attributes(): string[];
|
|
16
|
+
connectedCallback(): void;
|
|
17
|
+
attributeChangedCallback(): void;
|
|
18
|
+
/**
|
|
19
|
+
To be called whenever you need access to our TableManager.
|
|
20
|
+
You should not store a reference to the result. The result may become invalid if our configuration changes.
|
|
21
|
+
*/
|
|
22
|
+
required_table_manager(): TableManager;
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var _BaseQncDataTable_instances, _BaseQncDataTable_setup;
|
|
7
|
+
import { read_options_from_json_string } from "./table_options.js";
|
|
8
|
+
import { create_mithril_app } from "./create_mithril_app.js";
|
|
9
|
+
import { TableManager } from "./table_manager.js";
|
|
10
|
+
import { StateMachine } from "./state_machine.js";
|
|
11
|
+
import { conditionally_wrapped_element } from "./conditionally_wrapped_element.js";
|
|
12
|
+
import { redraw } from "mithril";
|
|
13
|
+
/**
|
|
14
|
+
Register the given tag name with customElements as data-table container which always uses the given Renderer.
|
|
15
|
+
Most sites will only need to call this once.
|
|
16
|
+
You _may_ call this multiple times, if you want to have multiple "table styles", using different Renderers.
|
|
17
|
+
|
|
18
|
+
Note that any element defined via this function must also have a `table-config` attribute set. This attribute must be a JSON encoded TableOptions object (ie. from table_options.ts).
|
|
19
|
+
*/
|
|
20
|
+
export function define_custom_element(tag_name, renderer) {
|
|
21
|
+
class QncDataTable extends BaseQncDataTable {
|
|
22
|
+
get renderer() {
|
|
23
|
+
return renderer;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
customElements.define(tag_name, QncDataTable);
|
|
27
|
+
}
|
|
28
|
+
export class BaseQncDataTable extends HTMLElement {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
_BaseQncDataTable_instances.add(this);
|
|
32
|
+
this._table_manager = null;
|
|
33
|
+
}
|
|
34
|
+
get renderer() {
|
|
35
|
+
throw new Error("`get renderer()` must be implemented in derived class");
|
|
36
|
+
}
|
|
37
|
+
static get observed_attributes() {
|
|
38
|
+
return ["table-config"];
|
|
39
|
+
}
|
|
40
|
+
connectedCallback() {
|
|
41
|
+
__classPrivateFieldGet(this, _BaseQncDataTable_instances, "m", _BaseQncDataTable_setup).call(this);
|
|
42
|
+
}
|
|
43
|
+
attributeChangedCallback() {
|
|
44
|
+
__classPrivateFieldGet(this, _BaseQncDataTable_instances, "m", _BaseQncDataTable_setup).call(this);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
To be called whenever you need access to our TableManager.
|
|
48
|
+
You should not store a reference to the result. The result may become invalid if our configuration changes.
|
|
49
|
+
*/
|
|
50
|
+
required_table_manager() {
|
|
51
|
+
if (!this._table_manager)
|
|
52
|
+
throw new Error("Element has not been set up yet. It has no StateMachine attached");
|
|
53
|
+
return this._table_manager;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
_BaseQncDataTable_instances = new WeakSet(), _BaseQncDataTable_setup = function _BaseQncDataTable_setup() {
|
|
57
|
+
const config = this.getAttribute("table-config");
|
|
58
|
+
if (!config)
|
|
59
|
+
throw new Error("[table-config] attribute is required");
|
|
60
|
+
const state_machine = new StateMachine(read_options_from_json_string(config), (html) => conditionally_wrapped_element(html).outerHTML, this, (state) => redraw());
|
|
61
|
+
create_mithril_app(this, state_machine, this.renderer);
|
|
62
|
+
this._table_manager = new TableManager(state_machine, this);
|
|
63
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Renderer } from "./renderer.js";
|
|
2
|
+
import { read_options_from_json_string } from "./table_options.js";
|
|
3
|
+
import { create_mithril_app } from "./create_mithril_app.js";
|
|
4
|
+
import { TableManager } from "./table_manager.js";
|
|
5
|
+
import { StateMachine } from "./state_machine.js";
|
|
6
|
+
import { conditionally_wrapped_element } from "./conditionally_wrapped_element.js";
|
|
7
|
+
import { redraw, Vnode } from "mithril";
|
|
8
|
+
import { AppState } from "./state_types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
Register the given tag name with customElements as data-table container which always uses the given Renderer.
|
|
12
|
+
Most sites will only need to call this once.
|
|
13
|
+
You _may_ call this multiple times, if you want to have multiple "table styles", using different Renderers.
|
|
14
|
+
|
|
15
|
+
Note that any element defined via this function must also have a `table-config` attribute set. This attribute must be a JSON encoded TableOptions object (ie. from table_options.ts).
|
|
16
|
+
*/
|
|
17
|
+
export function define_custom_element(tag_name: string, renderer: Renderer) {
|
|
18
|
+
class QncDataTable extends BaseQncDataTable {
|
|
19
|
+
get renderer(): Renderer {
|
|
20
|
+
return renderer;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
customElements.define(tag_name, QncDataTable);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class BaseQncDataTable extends HTMLElement {
|
|
27
|
+
get renderer(): Renderer {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"`get renderer()` must be implemented in derived class",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private _table_manager: TableManager | null = null;
|
|
34
|
+
|
|
35
|
+
static get observed_attributes() {
|
|
36
|
+
return ["table-config"];
|
|
37
|
+
}
|
|
38
|
+
connectedCallback() {
|
|
39
|
+
this.#setup();
|
|
40
|
+
}
|
|
41
|
+
attributeChangedCallback() {
|
|
42
|
+
this.#setup();
|
|
43
|
+
}
|
|
44
|
+
#setup() {
|
|
45
|
+
const config = this.getAttribute("table-config");
|
|
46
|
+
if (!config) throw new Error("[table-config] attribute is required");
|
|
47
|
+
|
|
48
|
+
const state_machine = new StateMachine(
|
|
49
|
+
read_options_from_json_string(config),
|
|
50
|
+
(html) => conditionally_wrapped_element(html).outerHTML,
|
|
51
|
+
this,
|
|
52
|
+
(state: AppState<string>) => redraw(),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
create_mithril_app(this, state_machine, this.renderer);
|
|
56
|
+
|
|
57
|
+
this._table_manager = new TableManager(state_machine, this);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
To be called whenever you need access to our TableManager.
|
|
62
|
+
You should not store a reference to the result. The result may become invalid if our configuration changes.
|
|
63
|
+
*/
|
|
64
|
+
required_table_manager(): TableManager {
|
|
65
|
+
if (!this._table_manager)
|
|
66
|
+
throw new Error(
|
|
67
|
+
"Element has not been set up yet. It has no StateMachine attached",
|
|
68
|
+
);
|
|
69
|
+
return this._table_manager;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SELECTION_CHANGE = "qnc-data-tables-selection-change";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SELECTION_CHANGE = "qnc-data-tables-selection-change";
|