forked from google/docsy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tabpane: support persistence of all active tabs (google#1611)
- Loading branch information
Showing
2 changed files
with
108 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,116 @@ | ||
// Storage key name also used as a data-* attribute suffix: | ||
const storageKeyName = 'td-tp-persist'; | ||
// Storage key names and data attribute name: | ||
const td_persistStorageKeyNameBase = 'td-tp-persist'; | ||
const td_persistCounterStorageKeyName = `${td_persistStorageKeyNameBase}-count`; | ||
const td_persistDataAttrName = `data-${td_persistStorageKeyNameBase}`; | ||
|
||
function tdActivateTabsWithKey(key) { | ||
if (!key) return; | ||
document | ||
.querySelectorAll(`[data-${storageKeyName}="${key}"]`) | ||
.forEach((element) => { | ||
new bootstrap.Tab(element).show(); | ||
}); | ||
} | ||
// Utilities | ||
|
||
function tdPersistActiveTab(activeTabKey) { | ||
if (!tdSupportsLocalStorage()) return; | ||
const _tdPersistCssSelector = (attrValue) => | ||
attrValue | ||
? `[${td_persistDataAttrName}="${attrValue}"]` | ||
: `[${td_persistDataAttrName}]`; | ||
|
||
const _tdStoragePersistKey = (tabKey) => | ||
td_persistStorageKeyNameBase + ':' + (tabKey || ''); | ||
|
||
const _tdSupportsLocalStorage = () => typeof Storage !== 'undefined'; | ||
|
||
// Helpers | ||
|
||
function tdPersistKey(key, value) { | ||
// @requires: tdSupportsLocalStorage(); | ||
|
||
try { | ||
localStorage.setItem(storageKeyName, activeTabKey); | ||
tdActivateTabsWithKey(activeTabKey); | ||
if (value) { | ||
localStorage.setItem(key, value); | ||
} else { | ||
localStorage.removeItem(key); | ||
} | ||
} catch (error) { | ||
console.error(`Unable to save active tab '${activeTabKey}' to localStorage:`, error); | ||
const action = value ? 'add' : 'remove'; | ||
console.error( | ||
`Docsy tabpane: unable to ${action} localStorage key '${key}': `, | ||
error | ||
); | ||
} | ||
} | ||
|
||
const tdSupportsLocalStorage = () => typeof Storage !== 'undefined'; | ||
// Retrieve, increment, and store tab-select event count, then returns it. | ||
function tdGetTabSelectEventCountAndInc() { | ||
// @requires: tdSupportsLocalStorage(); | ||
|
||
const storedCount = localStorage.getItem(td_persistCounterStorageKeyName); | ||
let numTabSelectEvents = parseInt(storedCount) || 0; | ||
numTabSelectEvents++; | ||
tdPersistKey(td_persistCounterStorageKeyName, numTabSelectEvents.toString()); | ||
return numTabSelectEvents; | ||
} | ||
|
||
// Main functions | ||
|
||
// On page load, activate tabs | ||
if (tdSupportsLocalStorage()) { | ||
const activeTabKey = localStorage.getItem(storageKeyName); | ||
function tdActivateTabsWithKey(key) { | ||
if (!key) return; | ||
|
||
document.querySelectorAll(_tdPersistCssSelector(key)).forEach((element) => { | ||
new bootstrap.Tab(element).show(); | ||
}); | ||
} | ||
|
||
function tdPersistActiveTab(activeTabKey) { | ||
if (!_tdSupportsLocalStorage()) return; | ||
|
||
tdPersistKey( | ||
_tdStoragePersistKey(activeTabKey), | ||
tdGetTabSelectEventCountAndInc() | ||
); | ||
tdActivateTabsWithKey(activeTabKey); | ||
} | ||
|
||
// Handlers | ||
|
||
function tdGetAndActivatePersistedTabs(tabs) { | ||
// Get unique persistence keys of tabs in this page | ||
var keyOfTabsInThisPage = [ | ||
...new Set( | ||
Array.from(tabs).map((el) => el.getAttribute(td_persistDataAttrName)) | ||
), | ||
]; | ||
|
||
// Create a list of active tabs with their age: | ||
let key_ageList = keyOfTabsInThisPage | ||
// Map to [tab-key, last-activated-age] | ||
.map((k) => [ | ||
k, | ||
parseInt(localStorage.getItem(_tdStoragePersistKey(k))) || 0, | ||
]) | ||
// Exclude tabs that have never been activated | ||
.filter(([k, v]) => v) | ||
// Sort from oldest selected to most recently selected | ||
.sort((a, b) => a[1] - b[1]); | ||
|
||
// Activate tabs from the oldest to the newest | ||
key_ageList.forEach(([key]) => { | ||
tdActivateTabsWithKey(key); | ||
}); | ||
|
||
return key_ageList; | ||
} | ||
|
||
function tdRegisterTabClickHandler(tabs) { | ||
tabs.forEach((tab) => { | ||
tab.addEventListener('click', () => { | ||
const activeTabKey = tab.getAttribute(td_persistDataAttrName); | ||
tdPersistActiveTab(activeTabKey); | ||
}); | ||
}); | ||
} | ||
|
||
// Register listeners and activate tabs | ||
|
||
window.addEventListener('DOMContentLoaded', () => { | ||
if (!_tdSupportsLocalStorage()) return; | ||
|
||
var allTabsInThisPage = document.querySelectorAll(_tdPersistCssSelector()); | ||
tdRegisterTabClickHandler(allTabsInThisPage); | ||
tdGetAndActivatePersistedTabs(allTabsInThisPage); | ||
}); |