@teipublisher/pb-components 1.44.2 → 2.0.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/CHANGELOG.md +60 -0
- package/dist/demo/demos.json +2 -1
- package/dist/demo/pb-browse-docs.html +2 -1
- package/dist/demo/pb-combo-box.html +1 -1
- package/dist/demo/pb-document.html +2 -2
- package/dist/demo/pb-load.html +2 -2
- package/dist/demo/pb-select-feature.html +1 -1
- package/dist/demo/pb-select-feature2.html +10 -3
- package/dist/demo/pb-select-feature3.html +1 -1
- package/dist/demo/pb-select-odd.html +1 -1
- package/dist/demo/pb-table-grid.html +1 -1
- package/dist/demo/pb-tabs.html +8 -2
- package/dist/demo/pb-toggle-feature.html +2 -2
- package/dist/demo/pb-toggle-feature2.html +2 -2
- package/dist/demo/pb-toggle-feature3.html +2 -2
- package/dist/demo/pb-view.html +1 -1
- package/dist/demo/pb-view4.html +86 -0
- package/dist/pb-code-editor.js +1 -1
- package/dist/pb-component-docs.js +33 -33
- package/dist/pb-components-bundle.js +173 -173
- package/dist/pb-edit-app.js +6 -6
- package/dist/pb-elements.json +93 -27
- package/dist/{pb-i18n-3963b098.js → pb-i18n-8a90c591.js} +1 -1
- package/dist/pb-leaflet-map.js +1 -1
- package/dist/pb-mixin-8a593923.js +158 -0
- package/dist/pb-odd-editor.js +47 -47
- package/dist/{vaadin-element-mixin-08cf11b5.js → vaadin-element-mixin-672938e3.js} +18 -18
- package/i18n/common/en.json +9 -1
- package/package.json +4 -3
- package/pb-elements.json +93 -27
- package/src/dts-client.js +14 -14
- package/src/dts-select-endpoint.js +5 -5
- package/src/pb-ajax.js +4 -4
- package/src/pb-authority-lookup.js +2 -2
- package/src/pb-autocomplete.js +9 -11
- package/src/pb-blacklab-highlight.js +3 -3
- package/src/pb-browse-docs.js +44 -27
- package/src/pb-browse.js +9 -3
- package/src/pb-combo-box.js +2 -2
- package/src/pb-document.js +15 -1
- package/src/pb-download.js +2 -2
- package/src/pb-edit-app.js +2 -2
- package/src/pb-edit-xml.js +2 -2
- package/src/pb-events.js +26 -18
- package/src/pb-grid.js +55 -53
- package/src/pb-lang.js +2 -2
- package/src/pb-link.js +10 -16
- package/src/pb-load.js +35 -25
- package/src/pb-login.js +2 -2
- package/src/pb-manage-odds.js +2 -2
- package/src/pb-markdown.js +2 -2
- package/src/pb-mei.js +2 -2
- package/src/pb-mixin.js +103 -196
- package/src/pb-odd-editor.js +2 -2
- package/src/pb-page.js +30 -21
- package/src/pb-paginate.js +24 -19
- package/src/pb-print-preview.js +2 -2
- package/src/pb-repeat.js +2 -1
- package/src/pb-search.js +34 -8
- package/src/pb-select-feature.js +62 -39
- package/src/pb-select-odd.js +8 -7
- package/src/pb-select-template.js +5 -4
- package/src/pb-select.js +31 -28
- package/src/pb-split-list.js +18 -11
- package/src/pb-table-grid.js +9 -8
- package/src/pb-tabs.js +29 -12
- package/src/pb-toggle-feature.js +51 -55
- package/src/pb-upload.js +10 -3
- package/src/pb-view.js +118 -95
- package/src/theming.js +148 -149
- package/src/urls.js +233 -0
- package/dist/pb-mixin-88125cb2.js +0 -158
package/src/theming.js
CHANGED
|
@@ -1,150 +1,149 @@
|
|
|
1
|
-
import { waitOnce } from "./pb-mixin.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
adoptedSheet
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @param {
|
|
92
|
-
* @param {
|
|
93
|
-
* @
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* @
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
1
|
+
import { waitOnce } from "./pb-mixin.js";
|
|
2
|
+
import 'construct-style-sheets-polyfill';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Maps theme selector to CSSStyleSheet or null.
|
|
6
|
+
*
|
|
7
|
+
* @type {Map<string,(CSSStyleSheet|null)>}
|
|
8
|
+
*/
|
|
9
|
+
const themeMap = new Map();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Load one or more CSS stylesheet from the given URL and return
|
|
13
|
+
* a CSSStyleSheet. The returned stylesheet can be assigned
|
|
14
|
+
* to `adoptedStyleSheets`.
|
|
15
|
+
*
|
|
16
|
+
* @param {string[]} urls absolute URL
|
|
17
|
+
* @returns {Promise<CSSStyleSheet|null>} constructed CSSStyleSheet or null
|
|
18
|
+
*/
|
|
19
|
+
export async function loadStylesheets(urls) {
|
|
20
|
+
const output = [];
|
|
21
|
+
for (const url of urls) {
|
|
22
|
+
const css = await loadResource(url);
|
|
23
|
+
if (css) {
|
|
24
|
+
output.push(css);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (output.length > 0) {
|
|
28
|
+
const sheet = new CSSStyleSheet();
|
|
29
|
+
return sheet.replace(output.join(''));
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function loadResource(url) {
|
|
35
|
+
return fetch(url)
|
|
36
|
+
.then(response => {
|
|
37
|
+
if (response.ok) {
|
|
38
|
+
return response.text();
|
|
39
|
+
}
|
|
40
|
+
console.warn('<theming> Component stylesheet not found: %s', url);
|
|
41
|
+
return null;
|
|
42
|
+
})
|
|
43
|
+
.then(text => text)
|
|
44
|
+
.catch(error => {
|
|
45
|
+
console.error('<theming> Error loading stylesheet %s: %o', url, error);
|
|
46
|
+
return null;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* From the global component theme, import all rules which would apply to the
|
|
52
|
+
* given element into a new CSSStyleSheet and return it.
|
|
53
|
+
*
|
|
54
|
+
* @param {HTMLElement} elem a web component or HTML element
|
|
55
|
+
* @returns {CSSStyleSheet|null} a new CSSStylesheet or null
|
|
56
|
+
*/
|
|
57
|
+
export function importStyles(elem) {
|
|
58
|
+
const page = document.querySelector('pb-page');
|
|
59
|
+
if (!page) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const theme = page.stylesheet;
|
|
63
|
+
if (!theme) {
|
|
64
|
+
// no component styles defined
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const selectors = getSelectors(elem).join('|');
|
|
69
|
+
if (themeMap.has(selectors)) {
|
|
70
|
+
return themeMap.get(selectors);
|
|
71
|
+
}
|
|
72
|
+
const prefixRegex = new RegExp(`^(${selectors})\\b`);
|
|
73
|
+
let adoptedSheet = null;
|
|
74
|
+
const rules = theme.cssRules;
|
|
75
|
+
const newCSS = copyStyles(rules, prefixRegex, []);
|
|
76
|
+
if (newCSS.length > 0) {
|
|
77
|
+
adoptedSheet = new CSSStyleSheet();
|
|
78
|
+
adoptedSheet.replaceSync(newCSS.join(''));
|
|
79
|
+
}
|
|
80
|
+
console.log('<theming> caching stylesheet for %s', selectors);
|
|
81
|
+
themeMap.set(selectors, adoptedSheet);
|
|
82
|
+
return adoptedSheet;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Recursively copy matching styles from the theme CSS
|
|
87
|
+
* to create a new CSS stylesheet having all styles required
|
|
88
|
+
* by the component.
|
|
89
|
+
*
|
|
90
|
+
* @param {CSSRule[]} rules
|
|
91
|
+
* @param {RegExp} prefixRegex
|
|
92
|
+
* @param {string[]} output
|
|
93
|
+
* @returns {string[]}
|
|
94
|
+
*/
|
|
95
|
+
function copyStyles(rules, prefixRegex, output) {
|
|
96
|
+
for (let i = 0; i < rules.length; i++) {
|
|
97
|
+
const rule = rules[i];
|
|
98
|
+
if (rule instanceof CSSStyleRule) {
|
|
99
|
+
if (prefixRegex.test(rule.selectorText)) {
|
|
100
|
+
const css = rule.cssText.replace(prefixRegex, `:host($1) `);
|
|
101
|
+
output.push(css);
|
|
102
|
+
}
|
|
103
|
+
} else if (rule instanceof CSSMediaRule) {
|
|
104
|
+
output.push(`\n@media ${rule.conditionText} {\n`);
|
|
105
|
+
copyStyles(rule.cssRules, prefixRegex, output);
|
|
106
|
+
output.push('\n}\n');
|
|
107
|
+
} else if (rule instanceof CSSFontFaceRule) {
|
|
108
|
+
// not allowed in constructed stylesheets
|
|
109
|
+
} else {
|
|
110
|
+
output.push(rule.cssText);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return output;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get a list of selectors, which could match the given component.
|
|
118
|
+
* This will return the local name of the component, a selector for the id
|
|
119
|
+
* and all classes assigned.
|
|
120
|
+
*
|
|
121
|
+
* @param {HTMLElement} component the web component
|
|
122
|
+
* @returns {string[]} list of selectors
|
|
123
|
+
*/
|
|
124
|
+
function getSelectors(component) {
|
|
125
|
+
const prefixes = [component.localName];
|
|
126
|
+
if (component.id) {
|
|
127
|
+
prefixes.push(`#${component.id}`);
|
|
128
|
+
}
|
|
129
|
+
component.classList.forEach((cls) => prefixes.push(`.${cls}`));
|
|
130
|
+
return prefixes;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Implements support for injecting user-defined styles into a web component's shadow DOM.
|
|
135
|
+
* Styles will be copied from the global component theme CSS imported by `pb-page`
|
|
136
|
+
* (see `theme` property on `pb-page`)
|
|
137
|
+
*/
|
|
138
|
+
export const themableMixin = (superclass) => class ThemableMixin extends superclass {
|
|
139
|
+
|
|
140
|
+
connectedCallback() {
|
|
141
|
+
super.connectedCallback();
|
|
142
|
+
waitOnce('pb-page-ready', (options) => {
|
|
143
|
+
const theme = importStyles(this);
|
|
144
|
+
if (theme) {
|
|
145
|
+
this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, theme];
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
150
149
|
};
|
package/src/urls.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { PbEvents } from "./pb-events.js";
|
|
2
|
+
import { getSubscribedChannels } from "./pb-mixin.js";
|
|
3
|
+
|
|
4
|
+
function log(...args) {
|
|
5
|
+
args[0] = `%c<registry>%c ${args[0]}`;
|
|
6
|
+
args.splice(1, 0, 'font-weight: bold; color: #99FF33;', 'color: inherit; font-weight: normal');
|
|
7
|
+
console.log.apply(null, args);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Central class for tracking state. We distinguish between
|
|
12
|
+
*
|
|
13
|
+
* 1. the initial state of the application as determined by the URL opened in the browser
|
|
14
|
+
* 2. state changes in components occurring while the user interacts with the page
|
|
15
|
+
*
|
|
16
|
+
* 1) is relevant if a user accesses a particular URL by following a link, entering an address into the location bar or
|
|
17
|
+
* opening a bookmark. In this case the components on the page will be in a fresh, empty state. However, they may react
|
|
18
|
+
* to URL parameters and initiate specific properties of their state accordingly. Users expect that navigating to a given URL will
|
|
19
|
+
* consistently result in the same display (i.e. restore a certain state). If users bookmark a specific page of a document shown in
|
|
20
|
+
* TEI Publisher's pb-view, they expect that the same page is displayed when they open the bookmark again. Thus the document path and
|
|
21
|
+
* the page need to be tracked in the URL.
|
|
22
|
+
*
|
|
23
|
+
* 2) applies while the user interacts with the application, i.e. triggers actions, which cause components to change state. Some
|
|
24
|
+
* actions may lead to a new page load, but many – like navigating pages in a `pb-view` – will only change the state of one or more components.
|
|
25
|
+
* From a user's point of view this should be irrelevant: moving back or forward in browser history should consistently restore the
|
|
26
|
+
* previous or following state.
|
|
27
|
+
*
|
|
28
|
+
* Components should thus comply with the following guidelines:
|
|
29
|
+
*
|
|
30
|
+
* - if the component initializes itself, it should retrieve needed parameters from `registry.state`
|
|
31
|
+
* - it may call `registry.replace` to make sure that all parameters required to later restore its initial state are present in the current URL
|
|
32
|
+
* - it should register a listener with `registry.subscribe` to be informed when the user moves back in history (without reloading the page)
|
|
33
|
+
* - it must call `registry.commit` after changing its current state
|
|
34
|
+
*/
|
|
35
|
+
class Registry {
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this.rootPath = '';
|
|
39
|
+
/**
|
|
40
|
+
* Records current state as determined from parsing the URL.
|
|
41
|
+
* This should be used to initialize components.
|
|
42
|
+
*/
|
|
43
|
+
this.state = {};
|
|
44
|
+
/**
|
|
45
|
+
* Used to record state for a given channel. Will be updated
|
|
46
|
+
* if a component calls commit or replace.
|
|
47
|
+
*/
|
|
48
|
+
this.channelStates = {};
|
|
49
|
+
this._listeners = [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
configure(usePath = true, rootPath = '') {
|
|
53
|
+
this.rootPath = rootPath;
|
|
54
|
+
this.usePath = usePath;
|
|
55
|
+
|
|
56
|
+
// determine initial state of the registry by parsing current URL
|
|
57
|
+
const initialState = this._stateFromURL();
|
|
58
|
+
if (!initialState) {
|
|
59
|
+
console.error('<registry> failed to parse URL: %s using template %s', window.location, this.urlTemplate);
|
|
60
|
+
} else {
|
|
61
|
+
this.state = initialState;
|
|
62
|
+
}
|
|
63
|
+
window.history.replaceState(null, '');
|
|
64
|
+
|
|
65
|
+
window.addEventListener('popstate', (ev) => {
|
|
66
|
+
if (ev.state) {
|
|
67
|
+
try {
|
|
68
|
+
this.channelStates = JSON.parse(ev.state);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error('<registry> error restoring state: %s', e.toString());
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
this.channelStates = {};
|
|
74
|
+
}
|
|
75
|
+
this.state = this._stateFromURL();
|
|
76
|
+
log('popstate: %o', this.channelStates);
|
|
77
|
+
|
|
78
|
+
this._listeners.forEach((entry) => {
|
|
79
|
+
entry.callback(this.getState(entry.component));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
PbEvents.emit('pb-popstate', null, this.channelStates);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
subscribe(component, callback) {
|
|
87
|
+
this._listeners.push({
|
|
88
|
+
component,
|
|
89
|
+
callback
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_stateFromURL() {
|
|
94
|
+
const params = {};
|
|
95
|
+
if (window.location.hash.length > 0) {
|
|
96
|
+
params.id = window.location.hash.substring(1);
|
|
97
|
+
}
|
|
98
|
+
if (this.usePath) {
|
|
99
|
+
params.path = window.location.pathname.replace(new RegExp(`^${this.rootPath}/?`), '');
|
|
100
|
+
}
|
|
101
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
102
|
+
urlParams.forEach((value, key) => {
|
|
103
|
+
if (this.usePath && key === 'path') {
|
|
104
|
+
console.warn("Found path parameter in query, but usePath is set to true. The path parameter will be ignored.");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
params[key] = value;
|
|
108
|
+
});
|
|
109
|
+
console.log('root: %s; window: %s, rel: %s', this.rootPath, window.location.pathname, params.path);
|
|
110
|
+
return params;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getState(component) {
|
|
114
|
+
const channel = getSubscribedChannels(component)[0];
|
|
115
|
+
const state = this.channelStates[channel];
|
|
116
|
+
if (state) {
|
|
117
|
+
return state;
|
|
118
|
+
}
|
|
119
|
+
this.channelStates[channel] = {};
|
|
120
|
+
return this.channelStates[channel];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setState(component, newState) {
|
|
124
|
+
const channel = getSubscribedChannels(component)[0];
|
|
125
|
+
this.channelStates[channel] = Object.assign(this.channelStates[channel], newState);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
clearParametersMatching (component, regex) {
|
|
129
|
+
const {state} = this
|
|
130
|
+
|
|
131
|
+
for (const key of Object.keys(state)) {
|
|
132
|
+
if (regex.test(key)) {
|
|
133
|
+
state[key] = null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get(path, defaultValue) {
|
|
139
|
+
if (!this.state) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
const value = path.split('.').reduce((state, component) => {
|
|
143
|
+
if (!state[component]) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return state[component];
|
|
147
|
+
}, this.state);
|
|
148
|
+
return value || defaultValue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
set(path, value) {
|
|
152
|
+
if (!path.contains('.')) {
|
|
153
|
+
this.state[path] = value;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const components = path.split('.');
|
|
157
|
+
const lastPart = components.pop()
|
|
158
|
+
// make sure all intermediate steps are available
|
|
159
|
+
const lastIntermediate = components.reduce((result, nextComponent) => {
|
|
160
|
+
if (!result[nextComponent]) {
|
|
161
|
+
// eslint-disable-next-line no-param-reassign
|
|
162
|
+
result[nextComponent] = {};
|
|
163
|
+
}
|
|
164
|
+
return result[nextComponent];
|
|
165
|
+
},
|
|
166
|
+
this.state
|
|
167
|
+
);
|
|
168
|
+
lastIntermediate[lastPart] = value;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
commit(elem, newState, overwrite = false) {
|
|
172
|
+
this._commit(elem, newState, overwrite, false);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
replace(elem, newState, overwrite = false) {
|
|
176
|
+
this._commit(elem, newState, overwrite, true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
_commit(elem, newState, overwrite, replace) {
|
|
180
|
+
this.state = overwrite ? newState : Object.assign(this.state, newState);
|
|
181
|
+
const resolved = this.urlFromState();
|
|
182
|
+
|
|
183
|
+
const chs = getSubscribedChannels(elem);
|
|
184
|
+
chs.forEach((channel) => {
|
|
185
|
+
if (overwrite || !this.channelStates[channel]) {
|
|
186
|
+
this.channelStates[channel] = newState;
|
|
187
|
+
} else {
|
|
188
|
+
Object.assign(this.channelStates[channel], newState);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const json = this.toJSON();
|
|
193
|
+
if (replace) {
|
|
194
|
+
window.history.replaceState(json, '', resolved);
|
|
195
|
+
log('replace %s: %o %d', resolved.toString(), this.channelStates, window.history.length);
|
|
196
|
+
} else {
|
|
197
|
+
window.history.pushState(json, '', resolved);
|
|
198
|
+
log('commit %s: %o %d', resolved.toString(), this.channelStates, window.history.length);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
urlFromState() {
|
|
203
|
+
const newUrl = new URL(window.location.href);
|
|
204
|
+
for (const [param, value] of Object.entries(this.state)) {
|
|
205
|
+
if (( param !== 'path' || !this.usePath )
|
|
206
|
+
&& param !== 'id') {
|
|
207
|
+
if (value === null) {
|
|
208
|
+
newUrl.searchParams.delete(param)
|
|
209
|
+
} else {
|
|
210
|
+
newUrl.searchParams.set(param, value)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (this.usePath) {
|
|
216
|
+
newUrl.pathname = `${this.rootPath}/${this.state.path}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
newUrl.hash = this.state.id ? `#${this.state.id}` : '';
|
|
220
|
+
|
|
221
|
+
console.log('urlFromState', newUrl.searchParams.toString())
|
|
222
|
+
return newUrl;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
toJSON() {
|
|
226
|
+
return JSON.stringify(this.channelStates);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const registry = new Registry();
|
|
231
|
+
if (!window.pbRegistry) {
|
|
232
|
+
window.pbRegistry = registry;
|
|
233
|
+
}
|