]> _ Git - pmi.git/commitdiff
WIP #2867 @4
authorStephen Cameron <stephen@cubedesigners.com>
Mon, 8 Jul 2019 08:02:06 +0000 (10:02 +0200)
committerStephen Cameron <stephen@cubedesigners.com>
Mon, 8 Jul 2019 08:02:06 +0000 (10:02 +0200)
resources/js/components/Tab.vue [new file with mode: 0644]
resources/js/components/Tabs.vue [new file with mode: 0644]

diff --git a/resources/js/components/Tab.vue b/resources/js/components/Tab.vue
new file mode 100644 (file)
index 0000000..326ea1a
--- /dev/null
@@ -0,0 +1,40 @@
+<template>
+    <section v-show="isActive"
+             class="tabs-content-panel"
+             :data-id="computedId"
+             :aria-hidden="! isActive"
+             role="tabpanel">
+        <slot></slot>
+    </section>
+</template>
+
+<script>
+    export default {
+        name: 'Tab',
+
+        props: {
+            id: { default: null },
+            name: { required: true },
+            selected: { default: false },
+        },
+
+        data() {
+            return {
+                isActive: false,
+            };
+        },
+
+        computed: {
+            computedId() {
+                return this.id ? this.id : this.name.toLowerCase().replace(/ /g, '-');
+            },
+            hash() {
+                return '#' + this.computedId;
+            },
+        },
+
+        mounted() {
+            this.isActive = this.selected;
+        },
+    }
+</script>
\ No newline at end of file
diff --git a/resources/js/components/Tabs.vue b/resources/js/components/Tabs.vue
new file mode 100644 (file)
index 0000000..c50134b
--- /dev/null
@@ -0,0 +1,168 @@
+<template>
+    <div class="tabs">
+        <ul role="tablist" class="tabs-list" :class="{ 'flex': this.currentMode === 'tabs' }">
+            <li v-for="(tab, i) in tabs"
+                :key="i"
+                :class="{ 'bg-grey-100': tab.isActive, 'is-disabled': tab.isDisabled }"
+                role="presentation"
+            >
+                <a class="block font-display text-lg text-navy py-4 px-8 mr-1"
+                   :aria-controls="tab.hash"
+                   :aria-selected="tab.isActive"
+                   @click="selectTab(tab.hash, $event)"
+                   :href="tab.hash"
+                   role="tab"
+                >
+                    {{ tab.name }}
+                </a>
+                <div v-show="currentMode === 'accordion' && tab.isActive">
+                    <!--<slide-toggle :active="tab.isActive" :duration="300">-->
+                        {{ tab.$el.innerHTML }}
+                    <!--</slide-toggle>-->
+                </div>
+            </li>
+        </ul>
+
+        <div v-show="currentMode === 'tabs'" class="tabs-content bg-grey-100 p-6">
+            <slot></slot>
+        </div>
+    </div>
+</template>
+
+<script>
+
+    import SlideToggle from 'vue-slide-up-down'
+
+    export default {
+
+        components: {
+            SlideToggle
+        },
+
+        props: {
+            options: {
+                type: Object,
+                required: false,
+                default: () => ({
+                    defaultTabHash: null,
+                }),
+            },
+            mode: { default: 'tabs' }, // Tabs mode and responsive by default
+            responsive: { default: true },
+            breakpoint: {
+                type: Number,
+                required: false,
+                default: 768,
+            }
+        },
+        data: () => ({
+            tabs: [],
+            activeTabHash: '',
+            activeTabIndex: 0,
+            width: 0,
+        }),
+
+        computed: {
+            currentMode() {
+                if (!this.responsive) {
+                    return this.mode;
+                }
+
+                // Switch to accordion mode when below the breakpoint
+                return (this.width < this.breakpoint) ? 'accordion' : 'tabs';
+
+            }
+        },
+
+        created() {
+            this.tabs = this.$children;
+        },
+
+        mounted() {
+
+            this.handleResize();
+            window.addEventListener('resize', () => this.handleResize());
+
+            // Change tabs when URL hash changes
+            window.addEventListener('hashchange', () => this.selectTab(window.location.hash));
+
+            // Pre-select tab based on hash
+            if (this.findTab(window.location.hash)) {
+                this.selectTab(window.location.hash);
+                return;
+            }
+
+            // Allow default tab to be set in options
+            if (this.options.defaultTabHash !== null && this.findTab("#" + this.options.defaultTabHash)) {
+                this.selectTab("#" + this.options.defaultTabHash);
+                return;
+            }
+
+            // If we get this far and nothing has been selected, select the first tab
+            if (this.tabs.length) {
+                this.selectTab(this.tabs[0].hash);
+            }
+        },
+
+        methods: {
+
+            handleResize() {
+                // Keep track of width internally
+                // Triggers the computed property currentMode
+                // to change based on window size...
+                this.width = window.innerWidth;
+            },
+
+            findTab(hash) {
+                return this.tabs.find(tab => tab.hash === hash);
+            },
+
+            selectTab(selectedTabHash, event) {
+
+                const selectedTab = this.findTab(selectedTabHash);
+
+                if (! selectedTab) {
+                    return;
+                }
+
+                this.tabs.forEach(tab => {
+                    tab.isActive = (tab.hash === selectedTab.hash);
+                });
+
+                this.$emit('changed', { tab: selectedTab });
+                this.activeTabHash = selectedTab.hash;
+                this.activeTabIndex = this.getTabIndex(selectedTabHash);
+
+                // If we're coming from a click event, prevent default behaviour to get rid of jump in page
+                // if (event) {
+                //     event.preventDefault();
+                // }
+            },
+
+            getTabIndex(hash){
+                const tab = this.findTab(hash);
+
+                return this.tabs.indexOf(tab);
+            },
+
+            getTabHash(index){
+                const tab = this.tabs.find(tab => this.tabs.indexOf(tab) === index);
+
+                if (!tab) {
+                    return;
+                }
+
+                return tab.hash;
+            },
+
+            getActiveTab(){
+                return this.findTab(this.activeTabHash);
+            },
+
+            getActiveTabIndex() {
+                return this.getTabIndex(this.activeTabHash);
+            },
+        },
+
+    }
+</script>