@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
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Basic usage
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import * as qdt from "@qnc/qnc_data_tables.js";
|
|
5
|
+
// You can also subclass Renderer and override any of the methods, then use that
|
|
6
|
+
qdt.define_custom_element("qnc-data-table", new qdt.Renderer());
|
|
7
|
+
// now any <qnc-data-table> elements will automatically be handled
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Setting up our styles
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import * as qdt from "@qnc/qnc_data_tables.js";
|
|
14
|
+
document.head.appendChild(qdt.create_style());
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Note: in the future, we might provide a css file which you can host/inject as you see fit. For now, this is the only supported technique.
|
|
18
|
+
|
|
19
|
+
## Filter Forms
|
|
20
|
+
|
|
21
|
+
By connecting a data table to a form (via the `filter_form_id` configuration parameter), we'll submit that form's data every time we refresh the table. We'll also refresh the table every time the filter form dispatches a `submit` event. We do NOT, however:
|
|
22
|
+
|
|
23
|
+
- call `preventDefault()` on the form's submit event (you definitely need to this)
|
|
24
|
+
- automatically trigger the form to submit at any time (you might want to trigger the form to submit on every change, or even every input)
|
|
25
|
+
|
|
26
|
+
## Selection actions
|
|
27
|
+
|
|
28
|
+
qnc-data-table elements do not render any kind of "selection action buttons" (ie. things that can be _done_ with the selected items). These elements do expose a TableManager, however, which you can use to implement your selction actions.
|
|
29
|
+
|
|
30
|
+
If you want to define your selection action buttons in your backend (ie. initial page html), we recommend using register_selection_fieldset_controller_custom_element.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## TODO
|
|
34
|
+
|
|
35
|
+
- Move column controls into top control bar, use qnc-dialog instead of details
|
|
36
|
+
- clean up styling, document how to customize (use custom css properties on the root element?)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Creates simple objects that wrap a single value, and persist that value to some type of Storage every time the value is updated.
|
|
3
|
+
|
|
4
|
+
Different "Converters" can be written to store different value types. Stored types should ideally be immutable. Any attempt to _replace_ the wrapper's value will cause stored value to be updated, but _mutating_ the wrapper's value will not.
|
|
5
|
+
*/
|
|
6
|
+
export type Converter<T> = {
|
|
7
|
+
/** may also throw an error */
|
|
8
|
+
decode: (value: string) => T;
|
|
9
|
+
encode: (value: T) => string;
|
|
10
|
+
};
|
|
11
|
+
export declare const NUMBER_CONVERTER: Converter<number>;
|
|
12
|
+
export declare const STRING_CONVERTER: Converter<string>;
|
|
13
|
+
export declare const STRING_ARRAY_CONVERTER: Converter<string[]>;
|
|
14
|
+
export declare const STRING_NUMBER_MAP_CONVERTER: Converter<ReadonlyMap<string, number>>;
|
|
15
|
+
export declare const STRING_SET_CONVERTER: Converter<ReadonlySet<string>>;
|
|
16
|
+
export declare class BoundStoredValue<T> {
|
|
17
|
+
#private;
|
|
18
|
+
private storage;
|
|
19
|
+
private key;
|
|
20
|
+
private converter;
|
|
21
|
+
private default_value;
|
|
22
|
+
constructor(storage: Storage | null, key: string, converter: Converter<T>, default_value: T);
|
|
23
|
+
get value(): T;
|
|
24
|
+
set value(value: T);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
Similar to a BoundStoredValue<ReadonlySet<string>>, but with some extra convenience methods for mutating the set.
|
|
28
|
+
*/
|
|
29
|
+
export declare class BoundStoredStringSet {
|
|
30
|
+
#private;
|
|
31
|
+
private storage;
|
|
32
|
+
private key;
|
|
33
|
+
constructor(storage: Storage | null, key: string);
|
|
34
|
+
get value(): ReadonlySet<string>;
|
|
35
|
+
set value(value: ReadonlySet<string>);
|
|
36
|
+
add(value: string): void;
|
|
37
|
+
delete(value: string): void;
|
|
38
|
+
clear(value: string): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Creates simple objects that wrap a single value, and persist that value to some type of Storage every time the value is updated.
|
|
3
|
+
|
|
4
|
+
Different "Converters" can be written to store different value types. Stored types should ideally be immutable. Any attempt to _replace_ the wrapper's value will cause stored value to be updated, but _mutating_ the wrapper's value will not.
|
|
5
|
+
*/
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
13
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
14
|
+
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");
|
|
15
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
16
|
+
};
|
|
17
|
+
var _BoundStoredValue_value, _BoundStoredStringSet_instances, _BoundStoredStringSet_set, _BoundStoredStringSet_save;
|
|
18
|
+
import * as tu from "@qnc/type_utils";
|
|
19
|
+
export const NUMBER_CONVERTER = {
|
|
20
|
+
// encode(value) {return value.toString()}
|
|
21
|
+
encode: JSON.stringify,
|
|
22
|
+
decode(value) {
|
|
23
|
+
return tu.require_number(tu.parse_json(value));
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export const STRING_CONVERTER = {
|
|
27
|
+
encode: JSON.stringify,
|
|
28
|
+
decode(value) {
|
|
29
|
+
return tu.require_string(tu.parse_json(value));
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export const STRING_ARRAY_CONVERTER = {
|
|
33
|
+
encode: JSON.stringify,
|
|
34
|
+
decode(value) {
|
|
35
|
+
return tu.require_array(tu.parse_json(value)).map(tu.require_string);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
export const STRING_NUMBER_MAP_CONVERTER = {
|
|
39
|
+
encode(value) {
|
|
40
|
+
return JSON.stringify(Array.from(value.entries()));
|
|
41
|
+
},
|
|
42
|
+
decode(value) {
|
|
43
|
+
const map = new Map();
|
|
44
|
+
for (const row of tu.require_array(tu.parse_json(value))) {
|
|
45
|
+
const [key, value] = tu.require_2_tuple(row);
|
|
46
|
+
map.set(tu.require_string(key), tu.require_number(value));
|
|
47
|
+
}
|
|
48
|
+
return map;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export const STRING_SET_CONVERTER = {
|
|
52
|
+
encode(value) {
|
|
53
|
+
return JSON.stringify(Array.from(value));
|
|
54
|
+
},
|
|
55
|
+
decode(value) {
|
|
56
|
+
return new Set(tu.require_array(tu.parse_json(value)).map(tu.require_string));
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
export class BoundStoredValue {
|
|
60
|
+
constructor(storage, key, converter, default_value) {
|
|
61
|
+
this.storage = storage;
|
|
62
|
+
this.key = key;
|
|
63
|
+
this.converter = converter;
|
|
64
|
+
this.default_value = default_value;
|
|
65
|
+
_BoundStoredValue_value.set(this, void 0);
|
|
66
|
+
__classPrivateFieldSet(this, _BoundStoredValue_value, read(storage, key, this.converter.decode, default_value), "f");
|
|
67
|
+
}
|
|
68
|
+
get value() {
|
|
69
|
+
return __classPrivateFieldGet(this, _BoundStoredValue_value, "f");
|
|
70
|
+
}
|
|
71
|
+
set value(value) {
|
|
72
|
+
__classPrivateFieldSet(this, _BoundStoredValue_value, value, "f");
|
|
73
|
+
if (this.storage) {
|
|
74
|
+
this.storage.setItem(this.key, this.converter.encode(value));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
_BoundStoredValue_value = new WeakMap();
|
|
79
|
+
/**
|
|
80
|
+
Similar to a BoundStoredValue<ReadonlySet<string>>, but with some extra convenience methods for mutating the set.
|
|
81
|
+
*/
|
|
82
|
+
export class BoundStoredStringSet {
|
|
83
|
+
constructor(storage, key) {
|
|
84
|
+
_BoundStoredStringSet_instances.add(this);
|
|
85
|
+
this.storage = storage;
|
|
86
|
+
this.key = key;
|
|
87
|
+
_BoundStoredStringSet_set.set(this, void 0);
|
|
88
|
+
__classPrivateFieldSet(this, _BoundStoredStringSet_set, read(this.storage, this.key, function (value) {
|
|
89
|
+
return new Set(tu
|
|
90
|
+
.require_array(tu.parse_json(value))
|
|
91
|
+
.map(tu.require_string));
|
|
92
|
+
}, new Set([])), "f");
|
|
93
|
+
}
|
|
94
|
+
// Important: we return a ReadonlySet!
|
|
95
|
+
get value() {
|
|
96
|
+
return __classPrivateFieldGet(this, _BoundStoredStringSet_set, "f");
|
|
97
|
+
}
|
|
98
|
+
set value(value) {
|
|
99
|
+
__classPrivateFieldSet(this, _BoundStoredStringSet_set, new Set(value), "f");
|
|
100
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_instances, "m", _BoundStoredStringSet_save).call(this);
|
|
101
|
+
}
|
|
102
|
+
// Convenience methods, so you don't have to pass in an entirely new set
|
|
103
|
+
add(value) {
|
|
104
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_set, "f").add(value);
|
|
105
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_instances, "m", _BoundStoredStringSet_save).call(this);
|
|
106
|
+
}
|
|
107
|
+
delete(value) {
|
|
108
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_set, "f").delete(value);
|
|
109
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_instances, "m", _BoundStoredStringSet_save).call(this);
|
|
110
|
+
}
|
|
111
|
+
clear(value) {
|
|
112
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_set, "f").clear();
|
|
113
|
+
__classPrivateFieldGet(this, _BoundStoredStringSet_instances, "m", _BoundStoredStringSet_save);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
_BoundStoredStringSet_set = new WeakMap(), _BoundStoredStringSet_instances = new WeakSet(), _BoundStoredStringSet_save = function _BoundStoredStringSet_save() {
|
|
117
|
+
if (this.storage) {
|
|
118
|
+
this.storage.setItem(this.key, STRING_SET_CONVERTER.encode(__classPrivateFieldGet(this, _BoundStoredStringSet_set, "f")));
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function read(storage, key, from_string, default_value) {
|
|
122
|
+
if (!storage)
|
|
123
|
+
return default_value;
|
|
124
|
+
const value = storage.getItem(key);
|
|
125
|
+
if (value === null)
|
|
126
|
+
return default_value;
|
|
127
|
+
try {
|
|
128
|
+
return from_string(value);
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
console.error(`Received value "${value}" for key "${key}" but not convert to correct type. Using default value instead.`);
|
|
132
|
+
return default_value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Creates simple objects that wrap a single value, and persist that value to some type of Storage every time the value is updated.
|
|
3
|
+
|
|
4
|
+
Different "Converters" can be written to store different value types. Stored types should ideally be immutable. Any attempt to _replace_ the wrapper's value will cause stored value to be updated, but _mutating_ the wrapper's value will not.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as tu from "@qnc/type_utils";
|
|
8
|
+
|
|
9
|
+
export type Converter<T> = {
|
|
10
|
+
/** may also throw an error */
|
|
11
|
+
decode: (value: string) => T;
|
|
12
|
+
encode: (value: T) => string;
|
|
13
|
+
};
|
|
14
|
+
export const NUMBER_CONVERTER: Converter<number> = {
|
|
15
|
+
// encode(value) {return value.toString()}
|
|
16
|
+
encode: JSON.stringify,
|
|
17
|
+
decode(value) {
|
|
18
|
+
return tu.require_number(tu.parse_json(value));
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export const STRING_CONVERTER: Converter<string> = {
|
|
22
|
+
encode: JSON.stringify,
|
|
23
|
+
decode(value) {
|
|
24
|
+
return tu.require_string(tu.parse_json(value));
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export const STRING_ARRAY_CONVERTER: Converter<string[]> = {
|
|
28
|
+
encode: JSON.stringify,
|
|
29
|
+
decode(value) {
|
|
30
|
+
return tu.require_array(tu.parse_json(value)).map(tu.require_string);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export const STRING_NUMBER_MAP_CONVERTER: Converter<
|
|
34
|
+
ReadonlyMap<string, number>
|
|
35
|
+
> = {
|
|
36
|
+
encode(value) {
|
|
37
|
+
return JSON.stringify(Array.from(value.entries()));
|
|
38
|
+
},
|
|
39
|
+
decode(value) {
|
|
40
|
+
const map = new Map<string, number>();
|
|
41
|
+
for (const row of tu.require_array(tu.parse_json(value))) {
|
|
42
|
+
const [key, value] = tu.require_2_tuple(row);
|
|
43
|
+
map.set(tu.require_string(key), tu.require_number(value));
|
|
44
|
+
}
|
|
45
|
+
return map;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export const STRING_SET_CONVERTER: Converter<ReadonlySet<string>> = {
|
|
49
|
+
encode(value: ReadonlySet<string>) {
|
|
50
|
+
return JSON.stringify(Array.from(value));
|
|
51
|
+
},
|
|
52
|
+
decode(value: string) {
|
|
53
|
+
return new Set(
|
|
54
|
+
tu.require_array(tu.parse_json(value)).map(tu.require_string),
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export class BoundStoredValue<T> {
|
|
60
|
+
#value: T;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
private storage: Storage | null,
|
|
64
|
+
private key: string,
|
|
65
|
+
private converter: Converter<T>,
|
|
66
|
+
private default_value: T,
|
|
67
|
+
) {
|
|
68
|
+
this.#value = read(storage, key, this.converter.decode, default_value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get value() {
|
|
72
|
+
return this.#value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
set value(value: T) {
|
|
76
|
+
this.#value = value;
|
|
77
|
+
if (this.storage) {
|
|
78
|
+
this.storage.setItem(this.key, this.converter.encode(value));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
Similar to a BoundStoredValue<ReadonlySet<string>>, but with some extra convenience methods for mutating the set.
|
|
85
|
+
*/
|
|
86
|
+
export class BoundStoredStringSet {
|
|
87
|
+
#set: Set<string>;
|
|
88
|
+
|
|
89
|
+
constructor(
|
|
90
|
+
private storage: Storage | null,
|
|
91
|
+
private key: string,
|
|
92
|
+
) {
|
|
93
|
+
this.#set = read(
|
|
94
|
+
this.storage,
|
|
95
|
+
this.key,
|
|
96
|
+
function (value: string) {
|
|
97
|
+
return new Set(
|
|
98
|
+
tu
|
|
99
|
+
.require_array(tu.parse_json(value))
|
|
100
|
+
.map(tu.require_string),
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
new Set([]),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Important: we return a ReadonlySet!
|
|
108
|
+
get value(): ReadonlySet<string> {
|
|
109
|
+
return this.#set;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
set value(value: ReadonlySet<string>) {
|
|
113
|
+
this.#set = new Set(value);
|
|
114
|
+
this.#save();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Convenience methods, so you don't have to pass in an entirely new set
|
|
118
|
+
add(value: string) {
|
|
119
|
+
this.#set.add(value);
|
|
120
|
+
this.#save();
|
|
121
|
+
}
|
|
122
|
+
delete(value: string) {
|
|
123
|
+
this.#set.delete(value);
|
|
124
|
+
this.#save();
|
|
125
|
+
}
|
|
126
|
+
clear(value: string) {
|
|
127
|
+
this.#set.clear();
|
|
128
|
+
this.#save;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#save() {
|
|
132
|
+
if (this.storage) {
|
|
133
|
+
this.storage.setItem(
|
|
134
|
+
this.key,
|
|
135
|
+
STRING_SET_CONVERTER.encode(this.#set),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function read<T>(
|
|
142
|
+
storage: Storage | null,
|
|
143
|
+
key: string,
|
|
144
|
+
from_string: (value: string) => T,
|
|
145
|
+
default_value: T,
|
|
146
|
+
): T {
|
|
147
|
+
if (!storage) return default_value;
|
|
148
|
+
const value = storage.getItem(key);
|
|
149
|
+
if (value === null) return default_value;
|
|
150
|
+
try {
|
|
151
|
+
return from_string(value);
|
|
152
|
+
} catch (e) {
|
|
153
|
+
console.error(
|
|
154
|
+
`Received value "${value}" for key "${key}" but not convert to correct type. Using default value instead.`,
|
|
155
|
+
);
|
|
156
|
+
return default_value;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Column } from "./table_options.js";
|
|
2
|
+
/**
|
|
3
|
+
Responsible for
|
|
4
|
+
- reading TableOptions.columns and then interpreting and updating user's stored preferences
|
|
5
|
+
*/
|
|
6
|
+
export declare class ColumnManager {
|
|
7
|
+
#private;
|
|
8
|
+
private columns;
|
|
9
|
+
private storage;
|
|
10
|
+
private storage_key;
|
|
11
|
+
on_any_change: () => void;
|
|
12
|
+
constructor(columns: Column[], storage: Storage | null, storage_key: string, on_any_change: () => void);
|
|
13
|
+
/** returns them in display order */
|
|
14
|
+
get all_enabled_columns(): ColumnList;
|
|
15
|
+
/** returns them in display order */
|
|
16
|
+
get fixed_enabled_columns(): ColumnList;
|
|
17
|
+
/** returns them in display order */
|
|
18
|
+
get sortable_enabled_columns(): ColumnList;
|
|
19
|
+
/** returns them in sorted order */
|
|
20
|
+
get all_sortable_columns(): ColumnList;
|
|
21
|
+
/** the only keys that matter are those that correspond to sortable columns; if you pass us keys of fixed columns, we simply ignore those */
|
|
22
|
+
set_order(keys: Array<string>): void;
|
|
23
|
+
set_enabled(key: string, enabled: boolean): void;
|
|
24
|
+
set_width(key: string, width: number): void;
|
|
25
|
+
}
|
|
26
|
+
/** Readonly view of ColumnManager; can be passed to view layer */
|
|
27
|
+
export type ConfiguredColumns = {
|
|
28
|
+
all_enabled_columns: ColumnList;
|
|
29
|
+
fixed_enabled_columns: ColumnList;
|
|
30
|
+
sortable_enabled_columns: ColumnList;
|
|
31
|
+
all_sortable_columns: ColumnList;
|
|
32
|
+
};
|
|
33
|
+
type ManagedColumn = {
|
|
34
|
+
key: string;
|
|
35
|
+
display: string;
|
|
36
|
+
help_text: string;
|
|
37
|
+
sortable: boolean;
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
width: number;
|
|
40
|
+
};
|
|
41
|
+
type ColumnList = ReadonlyArray<Readonly<ManagedColumn>>;
|
|
42
|
+
export type ConfiguredColumn = Readonly<ManagedColumn>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
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");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _ColumnManager_instances, _ColumnManager_fixed_columns, _ColumnManager_sortable_columns, _ColumnManager_all_columns, _ColumnManager_read_order, _ColumnManager_save;
|
|
13
|
+
import * as ss from "superstruct";
|
|
14
|
+
import * as tu from "@qnc/type_utils";
|
|
15
|
+
/**
|
|
16
|
+
Responsible for
|
|
17
|
+
- reading TableOptions.columns and then interpreting and updating user's stored preferences
|
|
18
|
+
*/
|
|
19
|
+
export class ColumnManager {
|
|
20
|
+
constructor(columns, storage, storage_key, on_any_change) {
|
|
21
|
+
var _a;
|
|
22
|
+
_ColumnManager_instances.add(this);
|
|
23
|
+
this.columns = columns;
|
|
24
|
+
this.storage = storage;
|
|
25
|
+
this.storage_key = storage_key;
|
|
26
|
+
this.on_any_change = on_any_change;
|
|
27
|
+
_ColumnManager_fixed_columns.set(this, void 0);
|
|
28
|
+
_ColumnManager_sortable_columns.set(this, void 0);
|
|
29
|
+
_ColumnManager_all_columns.set(this, void 0);
|
|
30
|
+
if (columns.length != new Set(columns.map((c) => c.key)).size)
|
|
31
|
+
throw new Error("columns do not all have unique keys");
|
|
32
|
+
__classPrivateFieldSet(this, _ColumnManager_all_columns, new Map(), "f");
|
|
33
|
+
__classPrivateFieldSet(this, _ColumnManager_fixed_columns, [], "f");
|
|
34
|
+
__classPrivateFieldSet(this, _ColumnManager_sortable_columns, [], "f");
|
|
35
|
+
for (const c of columns) {
|
|
36
|
+
const mutable_column = {
|
|
37
|
+
key: c.key,
|
|
38
|
+
display: c.display,
|
|
39
|
+
help_text: c.help_text,
|
|
40
|
+
width: c.width,
|
|
41
|
+
enabled: c.enabled,
|
|
42
|
+
sortable: c.sortable,
|
|
43
|
+
};
|
|
44
|
+
__classPrivateFieldGet(this, _ColumnManager_all_columns, "f").set(c.key, c);
|
|
45
|
+
if (c.fixed) {
|
|
46
|
+
__classPrivateFieldGet(this, _ColumnManager_fixed_columns, "f").push(c);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
__classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f").push(c);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// update state based on user preferences
|
|
53
|
+
if (storage) {
|
|
54
|
+
const config = tu.parse_json((_a = storage.getItem(this.storage_key)) !== null && _a !== void 0 ? _a : "");
|
|
55
|
+
if (ss.is(config, UserPreferencesStruct)) {
|
|
56
|
+
__classPrivateFieldGet(this, _ColumnManager_instances, "m", _ColumnManager_read_order).call(this, config.map((column) => column.key));
|
|
57
|
+
for (const { key, enabled, width } of config) {
|
|
58
|
+
const column = __classPrivateFieldGet(this, _ColumnManager_all_columns, "f").get(key);
|
|
59
|
+
if (!column)
|
|
60
|
+
continue; // previously saved column is no longer available; no problem, just ignore it
|
|
61
|
+
column.enabled = enabled;
|
|
62
|
+
column.width = width;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** returns them in display order */
|
|
68
|
+
get all_enabled_columns() {
|
|
69
|
+
return __classPrivateFieldGet(this, _ColumnManager_fixed_columns, "f").concat(__classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f").filter((c) => c.enabled));
|
|
70
|
+
}
|
|
71
|
+
/** returns them in display order */
|
|
72
|
+
get fixed_enabled_columns() {
|
|
73
|
+
return __classPrivateFieldGet(this, _ColumnManager_fixed_columns, "f");
|
|
74
|
+
}
|
|
75
|
+
/** returns them in display order */
|
|
76
|
+
get sortable_enabled_columns() {
|
|
77
|
+
return __classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f").filter((c) => c.enabled);
|
|
78
|
+
}
|
|
79
|
+
/** returns them in sorted order */
|
|
80
|
+
get all_sortable_columns() {
|
|
81
|
+
return __classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f");
|
|
82
|
+
}
|
|
83
|
+
/** the only keys that matter are those that correspond to sortable columns; if you pass us keys of fixed columns, we simply ignore those */
|
|
84
|
+
set_order(keys) {
|
|
85
|
+
__classPrivateFieldGet(this, _ColumnManager_instances, "m", _ColumnManager_read_order).call(this, keys);
|
|
86
|
+
__classPrivateFieldGet(this, _ColumnManager_instances, "m", _ColumnManager_save).call(this);
|
|
87
|
+
this.on_any_change();
|
|
88
|
+
}
|
|
89
|
+
set_enabled(key, enabled) {
|
|
90
|
+
const column = __classPrivateFieldGet(this, _ColumnManager_all_columns, "f").get(key);
|
|
91
|
+
if (!column)
|
|
92
|
+
throw new Error(`No column with key ${key}`);
|
|
93
|
+
column.enabled = enabled;
|
|
94
|
+
__classPrivateFieldGet(this, _ColumnManager_instances, "m", _ColumnManager_save).call(this);
|
|
95
|
+
this.on_any_change();
|
|
96
|
+
}
|
|
97
|
+
set_width(key, width) {
|
|
98
|
+
const column = __classPrivateFieldGet(this, _ColumnManager_all_columns, "f").get(key);
|
|
99
|
+
if (!column)
|
|
100
|
+
throw new Error(`No column with key ${key}`);
|
|
101
|
+
column.width = width;
|
|
102
|
+
__classPrivateFieldGet(this, _ColumnManager_instances, "m", _ColumnManager_save).call(this);
|
|
103
|
+
this.on_any_change();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
_ColumnManager_fixed_columns = new WeakMap(), _ColumnManager_sortable_columns = new WeakMap(), _ColumnManager_all_columns = new WeakMap(), _ColumnManager_instances = new WeakSet(), _ColumnManager_read_order = function _ColumnManager_read_order(keys) {
|
|
107
|
+
__classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f").sort(function (a, b) {
|
|
108
|
+
return keys.indexOf(a.key) - keys.indexOf(b.key);
|
|
109
|
+
});
|
|
110
|
+
}, _ColumnManager_save = function _ColumnManager_save() {
|
|
111
|
+
if (!this.storage)
|
|
112
|
+
return;
|
|
113
|
+
const columns = __classPrivateFieldGet(this, _ColumnManager_fixed_columns, "f").concat(__classPrivateFieldGet(this, _ColumnManager_sortable_columns, "f"));
|
|
114
|
+
this.storage.setItem(this.storage_key, JSON.stringify(columns.map((column) => ({
|
|
115
|
+
key: column.key,
|
|
116
|
+
enabled: column.enabled,
|
|
117
|
+
width: column.width,
|
|
118
|
+
}))));
|
|
119
|
+
};
|
|
120
|
+
const UserPreferencesStruct = ss.array(ss.object({
|
|
121
|
+
key: ss.string(),
|
|
122
|
+
width: ss.number(),
|
|
123
|
+
enabled: ss.boolean(),
|
|
124
|
+
}));
|