Browse Source

feat(docs): Create tabs shortcode

Michael Bromley 7 years ago
parent
commit
9db2f812b8

+ 3 - 0
docs/assets/scripts/main.ts

@@ -1,6 +1,7 @@
 // import '@webcomponents/custom-elements';
 
 import { SearchWidget } from './search-widget';
+import { initTabs } from './tabs';
 import { TocHighlighter } from './toc-highlighter';
 
 // tslint:disable-next-line
@@ -27,4 +28,6 @@ document.addEventListener('DOMContentLoaded', () => {
     const searchButton = document.querySelector('button.search-icon') as HTMLButtonElement;
     searchButton.addEventListener('click', () => searchWidget.toggleActive());
 
+    initTabs();
+
 }, false);

+ 85 - 0
docs/assets/scripts/tabs.ts

@@ -0,0 +1,85 @@
+type TabGroup = HTMLDivElement[];
+
+const TAB_CLASS = 'tab';
+const TAB_CONTROL_CLASS = 'tab-control';
+const TAB_CONTROL_WRAPPER_CLASS = 'tab-controls';
+const CONTAINER_CLASS = 'tab-container';
+const ACTIVE_CLASS = 'active';
+
+export function initTabs() {
+    const tabs = document.querySelectorAll<HTMLDivElement>(`.${TAB_CLASS}`);
+    const tabGroups = groupTabs(Array.from(tabs));
+    const containers = tabGroups.map(g => wrapTabGroup(g));
+    containers.forEach(container => {
+        container.addEventListener('click', e => {
+            const target = e.target as HTMLElement;
+            if (target.classList.contains(TAB_CONTROL_CLASS)) {
+                const tabId = target.dataset.id;
+                // deactivate all sibling tabs & controls
+                const tabsInGroup = Array.from(container.querySelectorAll(`.${TAB_CLASS}`));
+                const controlsInGroup = Array.from(container.querySelectorAll(`.${TAB_CONTROL_CLASS}`));
+                [...tabsInGroup, ...controlsInGroup].forEach(tab => tab.classList.remove(ACTIVE_CLASS));
+                // activate the newly selected tab & control
+                target.classList.add(ACTIVE_CLASS);
+                tabsInGroup.filter(t => t.id === tabId).forEach(tab => tab.classList.add(ACTIVE_CLASS));
+            }
+        });
+    });
+}
+
+/**
+ * Groups sibling tabs together.
+ */
+function groupTabs(tabs: HTMLDivElement[]): TabGroup[] {
+    const tabGroups: TabGroup[] = [];
+    const remainingTabs = tabs.slice();
+    let next: HTMLDivElement | undefined;
+    do {
+        next = remainingTabs.shift();
+        if (next) {
+            const group: TabGroup = [next];
+            let nextSibling = next.nextElementSibling;
+            while (nextSibling === remainingTabs[0]) {
+                if (nextSibling) {
+                    // tslint:disable-next-line:no-non-null-assertion
+                    group.push(remainingTabs.shift()!);
+                }
+                nextSibling = next.nextElementSibling;
+            }
+            tabGroups.push(group);
+        }
+    } while (next);
+    return tabGroups;
+}
+
+/**
+ * Wrap the tabs of the group in a container div and add the tab controls
+ */
+function wrapTabGroup(tabGroup: TabGroup): HTMLDivElement {
+    const tabControls = tabGroup.map(({ title }, i) => {
+        return `<button class="${TAB_CONTROL_CLASS} ${i === 0 ? ACTIVE_CLASS : ''}" data-id="${toId(title)}">${title}</button>`;
+    }).join('');
+    const wrapper = document.createElement('div');
+    wrapper.classList.add(CONTAINER_CLASS);
+    wrapper.innerHTML = `<div class="${TAB_CONTROL_WRAPPER_CLASS}">${tabControls}</div>`;
+
+    const parent = tabGroup[0].parentElement;
+    if (parent) {
+        parent.insertBefore(wrapper, tabGroup[0]);
+    }
+    tabGroup.forEach((tab, i) => {
+        tab.id = toId(tab.title);
+        if (i === 0) {
+            tab.classList.add(ACTIVE_CLASS);
+        }
+        wrapper.appendChild(tab);
+    });
+    return wrapper;
+}
+
+/**
+ * Generate a normalized ID based on the title
+ */
+function toId(title: string): string {
+    return 'tab-' + title.toLowerCase().replace(/[^a-zA-Z]/g, '-');
+}

+ 28 - 0
docs/assets/styles/_shortcodes.scss

@@ -98,3 +98,31 @@
         color: #776e71;
     }
 }
+
+/**
+ Tabs
+ */
+.tab-container {
+    border-left: 2px solid $gray-200;
+    padding-left: 6px;
+    margin-left: -8px;
+    .tab-controls {
+        button.tab-control {
+            color: $gray-600;
+            border: none;
+            padding: 3px 6px;
+            border-bottom: 2px solid transparent;
+            background: none;
+            &.active {
+                border-bottom-color: $brand-color;
+                color: $gray-700;
+            }
+        }
+    }
+    .tab {
+        display: none;
+        &.active {
+            display: block;
+        }
+    }
+}

+ 3 - 0
docs/layouts/shortcodes/tab.html

@@ -0,0 +1,3 @@
+<div class="tab" title="{{ .Get 0 }}">
+  {{ .Inner }}
+</div>