Skip to content

Instantly share code, notes, and snippets.

@ginjo
Last active January 30, 2023 22:47
Show Gist options
  • Save ginjo/4708e794ae89bca26b62d9406a1c2b2a to your computer and use it in GitHub Desktop.
Save ginjo/4708e794ae89bca26b62d9406a1c2b2a to your computer and use it in GitHub Desktop.
Example of a reusable html tabs vuejs-3 component with bulma css
<!DOCTYPE html>
<html>
<head>
<!--
This is an example of a reusable html tabs vuejs-3 component.
You can set this up once in your project and create as many
tabbed interfaces as you like, with no extra code.
This example was taken from https://github.com/mattmaribojoc/learn-vue-tab
This is a vuejs-3 conversion based on the above example.
It uses the composition API and does not require a build step.
Just open the html file in any modern browser.
Note the use of vuejs 'provide' and 'inject' to share variables
between components.
-->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 Bulma Tabs</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<style lang="css">
* {
margin: 0;
padding: 0;
font-family: 'Karla', sans-serif;
}
.wrapper {
width: 100%;
min-height: 100vh;
background-color: #f8f8f8;
margin: 0;
padding: 20px;
}
.change__style {
background-color: #eee;
font-size: 1em;
margin-bottom: 10px;
padding: 5px;
}
</style>
<style lang="css">
ul.tabs__header {
display: block;
list-style: none;
margin: 0 0 0 20px;
padding: 0;
}
ul.tabs__header > li {
padding: 15px 30px;
border-radius: 10px;
margin: 0;
display: inline-block;
margin-right: 5px;
cursor: pointer;
}
ul.tabs__header > li.tab__selected {
font-weight: bold;
border-radius: 10px 10px 0 0;
border-bottom: 8px solid transparent;
}
.tab {
display: inline-block;
color: black;
padding: 20px;
min-width: 800px;
border-radius: 10px;
min-height: 400px;
}
.tabs__light .tab{
background-color: #fff;
}
.tabs__light li {
background-color: #ddd;
color: #aaa;
}
.tabs__light li.tab__selected {
background-color: #fff;
color: #83FFB3;
}
.tabs__dark .tab{
background-color: #555;
color: #eee;
}
.tabs__dark li {
background-color: #ddd;
color: #aaa;
}
.tabs__dark li.tab__selected {
background-color: #555;
color: white;
}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
// Loads the parts of Vue that we want to use.
const { createApp, ref, toRef, onMounted, provide, inject } = Vue
</script>
</head>
<body>
<div id="vue-app">
<section class="section">
<div class="container">
<div class="title">
Vue3 Bulma Tabs
</div>
<p class="subtitle">
Modular reusable tabs using vuejs-3 and bulma
</p>
<div class='wrapper'>
<button class='change__style' @click='changeStyle()'>Change Style</button>
<tabs :mode="mode">
<tab title="Tab 1">Hello From Tab 1</tab>
<tab title="Tab 2">Hello From Tab 2</tab>
<tab title="Tab 3">Hello From Tab 3</tab>
<tab title="Tab 4">Hello From Tab 4</tab>
</tabs>
</div>
<div class="block"></div>
<div class='wrapper'>
<p class="subtitle">Call the tabs component as often as you like</p>
<button class='change__style' @click='changeStyle()'>Change Style</button>
<tabs :mode="mode">
<tab title="Tab A">Hey hey hey A</tab>
<tab title="Tab B">Hey hey hey B</tab>
<tab title="Tab C">Hey hey hey C</tab>
<tab title="Tab D">Hey hey hey D</tab>
</tabs>
</div>
</div>
</section>
</div>
<script id="tabs" type="text/x-template">
<div :class='{"tabs__light": mode === "light", "tabs__dark": mode === "dark"}'>
<ul class='tabs__header'>
<li v-for='(tab, index) in tabs'
:key='tab.title'
@click='selectTab(index)'
:class='{"tab__selected": (index == selectedIndex)}'>
{{ tab.title }}
</li>
</ul>
<slot></slot>
</div>
</script>
<script id="tab" type="text/x-template">
<div class='tab' ref='thisTab' v-show='isActive'>
<slot></slot>
</div>
</script>
<script>
// Loads the parts of Vue that we want to use.
//const { createApp, ref, onMounted, onCreated } = Vue
// Tab COMPONENT
const Tab = {
template: '#tab',
//props: ['title'],
props: {
title: {
type: String,
default: 'Tab'
}
},
setup(props) {
//const title = ref(props.title) // incorrect but seems to work
const title = toRef(props, 'title') // correct
const isActive = ref(true)
// In vue3 composition, 'this' doesn't work
// this.$parent.tabs.push(this)
// According to this link, we should use 'provider' and 'inject' to
// to access vars between parent/child components.
// https://stackoverflow.com/questions/64154002/vue-3-how-to-get-information-about-children
const tabElements = inject('tabElements')
// First add ref="thisTab" attribute to the child template (see above).
// Then declare the ref here.
// Element '$refs' aren't available until mounted, so use them in onMounted().
// And don't forget to return the ref.
const thisTab = {title, isActive}
onMounted(() => {
tabElements.value.push(thisTab)
})
return {
title,
tabElements,
isActive
}
}
} // Tab
// Tabs COMPONENT
const Tabs = {
template: '#tabs',
props: ['mode'],
setup(props, { slots }) {
const mode = ref('light')
const selectedIndex = ref(0)
const tabs = ref([])
// Exposes 'tabs' to other components (use inject() to get them).
provide('tabElements', tabs)
function selectTab (i) {
selectedIndex.value = i
// loop over all the tabs
tabs.value.forEach((tab, index) => {
tab.isActive = (index === i)
})
}
onMounted(() => {
selectTab(0)
console.log(tabs.value)
// console.log(slots.default())
})
return {
selectedIndex,
tabs,
selectTab
}
}
} // Tabs
// MAIN APP
const App = createApp({
// Provides Vue composition API.
setup() {
const mode = ref('dark')
function changeStyle () {
if (mode.value === 'dark') {
mode.value = 'light'
} else {
mode.value = 'dark'
}
}
return {
mode,
changeStyle
}
},
// Registers (locally) components.
components: {
Tab,
Tabs
},
}).mount('#vue-app') // MAIN APP
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment