@kubex/zinc 1.1.73 → 1.1.75

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.
@@ -0,0 +1,197 @@
1
+ // A tab selection belongs to a visit: one starts the first time a tab is
2
+ // recorded at a location and ends when that location is left. A reload continues
3
+ // the visit it interrupted, so the tab stays open and Back still steps through
4
+ // the tabs the visit opened; navigating away ends it, so returning starts from
5
+ // the default tab with no tab history behind it.
6
+ //
7
+ // Selections are remembered in two places, because neither is sufficient alone:
8
+ //
9
+ // - Every history entry carries the tab each container was showing when the
10
+ // entry was created, so a tab change is its own Back step. Entries are always
11
+ // merged into, never replaced, so nested pages keep their own records.
12
+ // - Session storage keyed by location survives a reload, which history state
13
+ // does not: the console pushes a fresh `{uri}` state on every document load,
14
+ // discarding whatever the reloaded entry held before a page can read it.
15
+ //
16
+ // Both name the visit they were written for. Ending a visit deletes its stored
17
+ // tabs outright; the tabs left on its history entries cannot be rewritten, so
18
+ // they are retired instead - the visit they name no longer exists.
19
+
20
+ export const TAB_STORE_PREFIX = 'zntab:';
21
+
22
+ const RESTORING_NAVIGATION_TYPES = ['reload', 'back_forward'];
23
+
24
+ const VISIT_STORE_KEY = '__znTabsVisit';
25
+
26
+ const TABS_HISTORY_KEY = '__znTabs';
27
+
28
+ interface TabsHistoryRecord {
29
+ visit: string;
30
+ tabs: Record<string, string>;
31
+ }
32
+
33
+ interface TabsHistoryState {
34
+ [TABS_HISTORY_KEY]?: TabsHistoryRecord;
35
+ }
36
+
37
+ const restorableLocations = new Set<string>();
38
+
39
+ let visitCount = 0;
40
+
41
+ function sessionStore(): Storage | null {
42
+ try {
43
+ return window.sessionStorage;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ function locationKey(): string {
50
+ return window.location.pathname + window.location.search;
51
+ }
52
+
53
+ function documentNavigationType(): string {
54
+ const entries = window.performance?.getEntriesByType('navigation') as PerformanceNavigationTiming[] | undefined;
55
+ return entries?.[0]?.type ?? '';
56
+ }
57
+
58
+ /** Scopes a store key to the current location, so each page keeps its own tab. */
59
+ export function locationScopedKey(key: string): string {
60
+ return `${key}@${locationKey()}`;
61
+ }
62
+
63
+ function visitStoreKey(): string {
64
+ return TAB_STORE_PREFIX + locationScopedKey(VISIT_STORE_KEY);
65
+ }
66
+
67
+ /** The visit the current location is on, or an empty string before one starts. */
68
+ function currentVisit(): string {
69
+ return sessionStore()?.getItem(visitStoreKey()) ?? '';
70
+ }
71
+
72
+ // Starts a visit for the current location, unless one is already under way - a
73
+ // reload lands mid visit and must continue it rather than begin a new one.
74
+ function startLocationVisit(): void {
75
+ const store = sessionStore();
76
+ if (store === null || currentVisit() !== '') {
77
+ return;
78
+ }
79
+
80
+ visitCount += 1;
81
+ store.setItem(visitStoreKey(), `${Date.now()}-${visitCount}`);
82
+ }
83
+
84
+ /**
85
+ * Ends the visits to every location other than the one on screen, discarding
86
+ * the tabs they were left showing. Called whenever the location may have
87
+ * changed, so the only tabs ever remembered are the current page's.
88
+ */
89
+ export function endVisitsToOtherLocations(): void {
90
+ const store = sessionStore();
91
+ if (store === null) {
92
+ return;
93
+ }
94
+
95
+ const suffix = `@${locationKey()}`;
96
+ for (let index = store.length - 1; index >= 0; index--) {
97
+ const key = store.key(index);
98
+ if (key !== null && key.startsWith(TAB_STORE_PREFIX) && !key.endsWith(suffix)) {
99
+ store.removeItem(key);
100
+ }
101
+ }
102
+ }
103
+
104
+ if (RESTORING_NAVIGATION_TYPES.includes(documentNavigationType())) {
105
+ restorableLocations.add(locationKey());
106
+ }
107
+
108
+ // A document load lands on the only location still worth remembering: every
109
+ // other one was navigated away from, whether or not a page was around to see it.
110
+ endVisitsToOtherLocations();
111
+
112
+ window.addEventListener('popstate', () => {
113
+ restorableLocations.add(locationKey());
114
+ endVisitsToOtherLocations();
115
+ }, {passive: true});
116
+
117
+ /**
118
+ * Whether the current location was reached in a way that should replay the tab
119
+ * it was last left on: a reload, or a history traversal. A fresh navigation -
120
+ * including a client side one to a location visited earlier - returns false.
121
+ */
122
+ export function isRestorableLocation(): boolean {
123
+ return restorableLocations.has(locationKey());
124
+ }
125
+
126
+ function asRecord(value: unknown): Record<string, unknown> | null {
127
+ return value !== null && typeof value === 'object' ? value as Record<string, unknown> : null;
128
+ }
129
+
130
+ function historyRecord(): TabsHistoryRecord | null {
131
+ const state = asRecord(window.history.state);
132
+ const record = state === null ? null : asRecord((state as TabsHistoryState)[TABS_HISTORY_KEY]);
133
+ const tabs = record === null ? null : asRecord(record.tabs);
134
+
135
+ if (record === null || tabs === null || typeof record.visit !== 'string') {
136
+ return null;
137
+ }
138
+
139
+ return {visit: record.visit, tabs: tabs as Record<string, string>};
140
+ }
141
+
142
+ /** The tab the current history entry was left showing, if it recorded one for the visit under way. */
143
+ export function getHistoryTab(key: string): string | null {
144
+ if (!key) {
145
+ return null;
146
+ }
147
+
148
+ const visit = currentVisit();
149
+ const record = historyRecord();
150
+ if (visit === '' || record === null || record.visit !== visit) {
151
+ return null;
152
+ }
153
+
154
+ const tab = record.tabs[key];
155
+ return typeof tab === 'string' ? tab : null;
156
+ }
157
+
158
+ // The host's own state is carried over so its entry stays intact - the console
159
+ // reads `state.uri` on popstate - and the url is left exactly as it is.
160
+ function writeHistoryTab(key: string, tab: string, push: boolean): void {
161
+ startLocationVisit();
162
+
163
+ const visit = currentVisit();
164
+ if (!key || visit === '') {
165
+ return;
166
+ }
167
+
168
+ const record = historyRecord();
169
+ const tabs = record !== null && record.visit === visit ? record.tabs : {};
170
+
171
+ // Re-recording what the entry already says would be a wasted history write,
172
+ // and browsers cap how many of those a page may make.
173
+ if (!push && tabs[key] === tab) {
174
+ return;
175
+ }
176
+
177
+ const state = {
178
+ ...asRecord(window.history.state),
179
+ [TABS_HISTORY_KEY]: {visit, tabs: {...tabs, [key]: tab}}
180
+ };
181
+
182
+ if (push) {
183
+ window.history.pushState(state, '', window.location.href);
184
+ } else {
185
+ window.history.replaceState(state, '');
186
+ }
187
+ }
188
+
189
+ /** Adds a history entry for a tab change, making it its own Back step. */
190
+ export function pushHistoryTab(key: string, tab: string): void {
191
+ writeHistoryTab(key, tab, true);
192
+ }
193
+
194
+ /** Records the tab on the current entry without adding a Back step. */
195
+ export function replaceHistoryTab(key: string, tab: string): void {
196
+ writeHistoryTab(key, tab, false);
197
+ }
@@ -1,11 +1,20 @@
1
- import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
2
- import { deepQuerySelectorAll } from "../../utilities/query";
3
- import { HasSlotController } from "../../internal/slot";
4
- import { ifDefined } from "lit/directives/if-defined.js";
5
- import { md5 } from "../../utilities/md5";
6
- import { MutationController } from '@lit-labs/observers/mutation-controller.js';
7
- import { property } from 'lit/decorators.js';
8
- import { Store } from "../../internal/storage";
1
+ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
2
+ import {deepQuerySelectorAll} from "../../utilities/query";
3
+ import {
4
+ endVisitsToOtherLocations,
5
+ getHistoryTab,
6
+ isRestorableLocation,
7
+ locationScopedKey,
8
+ pushHistoryTab,
9
+ replaceHistoryTab,
10
+ TAB_STORE_PREFIX
11
+ } from './tabs-navigation';
12
+ import {HasSlotController} from "../../internal/slot";
13
+ import {ifDefined} from "lit/directives/if-defined.js";
14
+ import {md5} from "../../utilities/md5";
15
+ import {MutationController} from '@lit-labs/observers/mutation-controller.js';
16
+ import {property} from 'lit/decorators.js';
17
+ import {Store} from "../../internal/storage";
9
18
  import ZincElement from '../../internal/zinc-element';
10
19
 
11
20
  import styles from './tabs.scss';
@@ -34,25 +43,25 @@ const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav';
34
43
  */
35
44
  export default class ZnTabs extends ZincElement {
36
45
  static styles: CSSResultGroup = unsafeCSS(styles);
37
- @property({ attribute: 'master-id', reflect: true }) masterId: string;
38
- @property({ attribute: 'default-uri', reflect: true }) defaultUri = '';
39
- @property({ attribute: 'active', reflect: true }) _current = '';
40
- @property({ attribute: 'split', type: Number, reflect: true }) _split: number;
41
- @property({ attribute: 'split-min', type: Number, reflect: true }) _splitMin = 60;
42
- @property({ attribute: 'split-min-secondary', type: Number, reflect: true }) _splitMinSecondary: number;
43
- @property({ attribute: 'split-max', type: Number, reflect: true }) _splitMax: number;
44
- @property({ attribute: 'primary-caption', reflect: true }) primaryCaption = 'Navigation';
45
- @property({ attribute: 'secondary-caption', reflect: true }) secondaryCaption = 'Content';
46
- @property({ attribute: 'no-prefetch', type: Boolean, reflect: true }) noPrefetch = false;
47
- @property({ attribute: 'no-cache', type: Boolean, reflect: true }) noCache = false;
46
+ @property({attribute: 'master-id', reflect: true}) masterId: string;
47
+ @property({attribute: 'default-uri', reflect: true}) defaultUri = '';
48
+ @property({attribute: 'active', reflect: true}) _current = '';
49
+ @property({attribute: 'split', type: Number, reflect: true}) _split: number;
50
+ @property({attribute: 'split-min', type: Number, reflect: true}) _splitMin = 60;
51
+ @property({attribute: 'split-min-secondary', type: Number, reflect: true}) _splitMinSecondary: number;
52
+ @property({attribute: 'split-max', type: Number, reflect: true}) _splitMax: number;
53
+ @property({attribute: 'primary-caption', reflect: true}) primaryCaption = 'Navigation';
54
+ @property({attribute: 'secondary-caption', reflect: true}) secondaryCaption = 'Content';
55
+ @property({attribute: 'no-prefetch', type: Boolean, reflect: true}) noPrefetch = false;
56
+ @property({attribute: 'no-cache', type: Boolean, reflect: true}) noCache = false;
48
57
  // session storage if not local
49
- @property({ attribute: 'local-storage', type: Boolean, reflect: true }) localStorage: boolean;
50
- @property({ attribute: 'store-key' }) storeKey: string;
51
- @property({ attribute: 'store-ttl', type: Number, reflect: true }) storeTtl = 0;
52
- @property({ attribute: 'padded', type: Boolean, reflect: true }) padded = false;
53
- @property({ attribute: 'fetch-style', type: String, reflect: true }) fetchStyle = "";
54
- @property({ attribute: 'full-width', type: Boolean, reflect: true }) fullWidth = false;
55
- @property({ attribute: 'padded-right', type: Boolean, reflect: true }) paddedRight = false;
58
+ @property({attribute: 'local-storage', type: Boolean, reflect: true}) localStorage: boolean;
59
+ @property({attribute: 'store-key'}) storeKey: string;
60
+ @property({attribute: 'store-ttl', type: Number, reflect: true}) storeTtl = 0;
61
+ @property({attribute: 'padded', type: Boolean, reflect: true}) padded = false;
62
+ @property({attribute: 'fetch-style', type: String, reflect: true}) fetchStyle = "";
63
+ @property({attribute: 'full-width', type: Boolean, reflect: true}) fullWidth = false;
64
+ @property({attribute: 'padded-right', type: Boolean, reflect: true}) paddedRight = false;
56
65
  @property() monitor: string;
57
66
  // Creating a header
58
67
  @property() caption: string;
@@ -66,11 +75,12 @@ export default class ZnTabs extends ZincElement {
66
75
  private _tabs: HTMLElement[] = [];
67
76
  private _actions: HTMLElement[] = [];
68
77
  private _knownUri: Map<string, string> = new Map<string, string>();
78
+ private _defaultTab = '';
69
79
  private readonly hasSlotController = new HasSlotController(this, '[default]', 'bottom', 'right', 'left', 'top', 'actions');
70
80
 
71
81
  private readonly _domObserver = new MutationController(this, {
72
82
  target: null,
73
- config: { childList: true, subtree: true },
83
+ config: {childList: true, subtree: true},
74
84
  callback: mutations => {
75
85
  mutations.forEach(mutation => {
76
86
  if (mutation.type !== 'childList') return;
@@ -89,17 +99,16 @@ export default class ZnTabs extends ZincElement {
89
99
 
90
100
  private readonly _monitorObserver = new MutationController(this, {
91
101
  target: null,
92
- config: { childList: true, subtree: true },
102
+ config: {childList: true, subtree: true},
93
103
  callback: mutations => {
94
104
  mutations.forEach(mutation => {
95
105
  if (mutation.type !== 'childList') return;
96
106
  mutation.addedNodes.forEach(node => {
97
107
  if (node instanceof HTMLElement && node.id === this.monitor) {
98
108
  this.reRegisterTabs();
99
- const storedValue = this._store.get(this.storeKey);
109
+ const storedValue = this.getStoredTab();
100
110
  if (storedValue !== null) {
101
- this._prepareTab(storedValue);
102
- this.setActiveTab(storedValue, false, false);
111
+ this.restoreStoredTab(storedValue);
103
112
  }
104
113
  }
105
114
  });
@@ -133,7 +142,7 @@ export default class ZnTabs extends ZincElement {
133
142
 
134
143
  const defaultID = this.defaultUri ? this._uriToId(this.defaultUri) : '';
135
144
 
136
- this._store = new Store(this.localStorage ? window.localStorage : window.sessionStorage, "zntab:", this.storeTtl);
145
+ this._store = new Store(this.localStorage ? window.localStorage : window.sessionStorage, TAB_STORE_PREFIX, this.storeTtl);
137
146
  Array.from(this.children).forEach((element) => {
138
147
  if (element.slot === '') {
139
148
  this._panels.set(element.getAttribute('id') || defaultID, [element]);
@@ -142,6 +151,21 @@ export default class ZnTabs extends ZincElement {
142
151
 
143
152
  this.observerDom();
144
153
  this.monitorDom();
154
+ window.addEventListener('popstate', this.handlePopState, {passive: true});
155
+ }
156
+
157
+ disconnectedCallback() {
158
+ super.disconnectedCallback();
159
+ window.removeEventListener('popstate', this.handlePopState);
160
+
161
+ // The container is torn down before the url it is being replaced by is
162
+ // pushed, so the check waits a task: by then a navigation has changed the
163
+ // location and this visit is over, while a re-render is back on screen.
164
+ setTimeout(() => {
165
+ if (!this.isConnected) {
166
+ endVisitsToOtherLocations();
167
+ }
168
+ });
145
169
  }
146
170
 
147
171
  monitorDom() {
@@ -150,6 +174,116 @@ export default class ZnTabs extends ZincElement {
150
174
  }
151
175
  }
152
176
 
177
+ /** The key the active tab is persisted under. Null disables persistence. */
178
+ protected getTabStoreKey(): string | null {
179
+ return this.storeKey || null;
180
+ }
181
+
182
+ protected getStoredTab(): string | null {
183
+ const key = this.getTabStoreKey();
184
+ if (key === null || !this._store) {
185
+ return null;
186
+ }
187
+
188
+ // Local storage persists across fresh navigation and browser sessions.
189
+ if (this.localStorage) {
190
+ return this._store.get(key);
191
+ }
192
+
193
+ // Nothing is remembered for a location that has been navigated away from:
194
+ // its stored tab went with the visit, and so did the tabs on its entries.
195
+ const stored = this._store.get(locationScopedKey(key));
196
+ if (stored === null) {
197
+ return null;
198
+ }
199
+
200
+ // The history entry is authoritative where it survived; a reload wipes it,
201
+ // leaving the selection the location was last left on.
202
+ const fromHistory = getHistoryTab(key);
203
+ if (fromHistory !== null) {
204
+ return fromHistory;
205
+ }
206
+
207
+ return isRestorableLocation() ? stored : null;
208
+ }
209
+
210
+ protected storeActiveTab(tabName: string) {
211
+ const key = this.getTabStoreKey();
212
+ if (key === null || !this._store) {
213
+ return;
214
+ }
215
+
216
+ // Uri tab ids are derived from the master id, so store the uri itself.
217
+ const value = this.getUriForTabId(tabName) ?? tabName;
218
+ this._store.set(this.localStorage ? key : locationScopedKey(key), value);
219
+ if (!this.localStorage) {
220
+ replaceHistoryTab(key, value);
221
+ }
222
+ }
223
+
224
+ protected restoreStoredTab(stored: string) {
225
+ const uriTab = this._tabs.find(tab => tab.getAttribute('tab-uri') === stored);
226
+ if (uriTab) {
227
+ this.clickTab(uriTab, false);
228
+ return;
229
+ }
230
+
231
+ this._prepareTab(stored);
232
+ this.setActiveTab(stored, true, false);
233
+ }
234
+
235
+ /** Activates the tab the container starts on, ignoring any stored selection. */
236
+ protected activateDefaultTab() {
237
+ if (!this._panels.has(this._defaultTab) && this._tabs.length > 0) {
238
+ const tabUri = this._tabs[0].getAttribute('tab-uri');
239
+ if (tabUri) {
240
+ this.clickTab(this._tabs[0], false);
241
+ return;
242
+ }
243
+ }
244
+
245
+ this.setActiveTab(this._defaultTab, true, false);
246
+ }
247
+
248
+ // Back and forward move between recorded tabs.
249
+ private readonly handlePopState = () => {
250
+ const key = this.getTabStoreKey();
251
+ if (key === null) {
252
+ return;
253
+ }
254
+
255
+ // Only entries that name a tab move the container. Entries that say nothing
256
+ // about it - the extra one the console pushes for every document load, one
257
+ // pushed before this container was on screen, one belonging to another page,
258
+ // one left by a visit that has ended - leave the open tab where it is.
259
+ // Overwriting it with the default tab would show the first tab on the way
260
+ // through, in place of the tab the entry being stepped onto stands for.
261
+ const tab = getHistoryTab(key);
262
+ if (tab === null) {
263
+ return;
264
+ }
265
+
266
+ this.restoreStoredTab(tab);
267
+ };
268
+
269
+ private pushTabHistory(tabName: string) {
270
+ const key = this.getTabStoreKey();
271
+ if (key === null || this.localStorage) {
272
+ return;
273
+ }
274
+
275
+ pushHistoryTab(key, this.getUriForTabId(tabName) ?? tabName);
276
+ }
277
+
278
+ private getUriForTabId(tabId: string): string | null {
279
+ for (const [uri, id] of this._knownUri) {
280
+ if (id === tabId) {
281
+ return uri;
282
+ }
283
+ }
284
+ return null;
285
+ }
286
+
153
287
  _addPanel(panel: HTMLElement) {
154
288
  if (this._panels.has(panel.getAttribute('id')!)) {
155
289
  return;
@@ -176,29 +310,20 @@ export default class ZnTabs extends ZincElement {
176
310
  super.firstUpdated(_changedProperties);
177
311
  setTimeout(() => {
178
312
  this._registerTabs();
313
+ this._defaultTab = this._current || '';
179
314
 
180
- const storedValue = this._store.get(this.storeKey);
315
+ const storedValue = this.getStoredTab();
181
316
  if (storedValue !== null) {
182
- this._prepareTab(storedValue);
183
- this.setActiveTab(storedValue, false, false);
317
+ this.restoreStoredTab(storedValue);
184
318
  return;
185
319
  }
186
320
 
187
- const defaultTab = this._current || '';
188
- if (!this._panels.has(defaultTab) && this._tabs.length > 0) {
189
- const tabUri = this._tabs[0].getAttribute('tab-uri');
190
- if (tabUri) {
191
- this.clickTab(this._tabs[0], false);
192
- return;
193
- }
194
- }
195
-
196
- this.setActiveTab(defaultTab, false, false);
321
+ this.activateDefaultTab();
197
322
  }, 10);
198
323
 
199
324
  this.addEventListener('zn-menu-select', () => {
200
325
  setTimeout(this.reRegisterTabs, 200);
201
- }, { passive: true });
326
+ }, {passive: true});
202
327
  }
203
328
 
204
329
  switchTab(inc: number) {
@@ -215,7 +340,7 @@ export default class ZnTabs extends ZincElement {
215
340
  nextIndex = 0; // wrap around to the first tab
216
341
  }
217
342
  const nextTabId = Array.from(this._panels.keys())[nextIndex];
218
- this.setActiveTab(nextTabId, true, false);
343
+ this.setActiveTab(nextTabId, true, false, null, true);
219
344
  }
220
345
 
221
346
  nextTab() {
@@ -277,7 +402,7 @@ export default class ZnTabs extends ZincElement {
277
402
  }
278
403
 
279
404
  document.dispatchEvent(new CustomEvent('zn-new-element', {
280
- detail: { element: tabNode, source: tabEle }
405
+ detail: {element: tabNode, source: tabEle}
281
406
  }));
282
407
  return tabNode;
283
408
  }
@@ -288,9 +413,9 @@ export default class ZnTabs extends ZincElement {
288
413
  if (target) {
289
414
  if ('startViewTransition' in document) {
290
415
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
291
- (document as any).startViewTransition(() => this.clickTab(target, event.altKey));
416
+ (document as any).startViewTransition(() => this.clickTab(target, event.altKey, true));
292
417
  } else {
293
- this.clickTab(target, event.altKey)
418
+ this.clickTab(target, event.altKey, true)
294
419
  }
295
420
  }
296
421
  }
@@ -303,7 +428,7 @@ export default class ZnTabs extends ZincElement {
303
428
  }
304
429
  }
305
430
 
306
- clickTab(target: HTMLElement, refresh: boolean) {
431
+ clickTab(target: HTMLElement, refresh: boolean, pushHistory = false) {
307
432
  const tabUri = target.getAttribute('tab-uri');
308
433
  const wasCached = !!tabUri && this._panels.has(this._uriToId(tabUri));
309
434
  this.fetchUriTab(target);
@@ -311,7 +436,7 @@ export default class ZnTabs extends ZincElement {
311
436
  if (target.hasAttribute('tab')) {
312
437
  const forceRefresh = refresh || (this.noCache && wasCached);
313
438
  setTimeout(() => {
314
- this.setActiveTab(target.getAttribute('tab') || '', true, forceRefresh, this.getRefTab(target));
439
+ this.setActiveTab(target.getAttribute('tab') || '', true, forceRefresh, this.getRefTab(target), pushHistory);
315
440
  }, 10);
316
441
  }
317
442
  }
@@ -331,7 +456,8 @@ export default class ZnTabs extends ZincElement {
331
456
  return null;
332
457
  }
333
458
 
334
- setActiveTab(tabName: string, store: boolean, refresh: boolean, refTab: string | null = null) {
459
+ setActiveTab(tabName: string, store: boolean, refresh: boolean, refTab: string | null = null, pushHistory = false) {
460
+ const previous = this._current;
335
461
  let hasActive = false;
336
462
  this._tabs.forEach(tab => {
337
463
 
@@ -355,8 +481,14 @@ export default class ZnTabs extends ZincElement {
355
481
  //Set on the element as a failsafe before TabPanel is loaded
356
482
  //This must be done AFTER selectTab to avoid panel bugs
357
483
 
358
- if (store && this._store !== null) {
359
- this._store.set(this.storeKey, tabName);
484
+ // The entry must be pushed before the tab is recorded, so the entry being
485
+ // left keeps the tab it was showing.
486
+ if (pushHistory && this._current === tabName && previous !== tabName) {
487
+ this.pushTabHistory(tabName);
488
+ }
489
+
490
+ if (store) {
491
+ this.storeActiveTab(tabName);
360
492
  }
361
493
  }
362
494
 
@@ -402,7 +534,7 @@ export default class ZnTabs extends ZincElement {
402
534
  gaid = this._activeTab.getAttribute('gaid')!;
403
535
  }
404
536
  document.dispatchEvent(new CustomEvent('zn-refresh-element', {
405
- detail: { element: element, uri: uri, gaid: gaid }
537
+ detail: {element: element, uri: uri, gaid: gaid}
406
538
  }));
407
539
  }
408
540
  });