Skip to content
Snippets Groups Projects
Commit 0c3c2028 authored by Kasper Seweryn's avatar Kasper Seweryn 🥞
Browse files

Add basic popover

parent b6b1ec13
No related branches found
No related tags found
1 merge request!1Implement all components
......@@ -28,6 +28,7 @@ export default defineConfig({
{ text: "Podcast Card", link: "/components/card/podcast" },
] },
{ text: "Activity", link: "/components/activity/" },
{ text: "Popover", link: "/components/popover/" },
]
}
]
......
<script setup lang="ts">
import { ref } from 'vue'
const items = [
{
type: 'button',
text: 'Play next',
icon: 'bi-arrow-up-right',
click: () => {}
},
{
type: 'button',
text: 'Append to queue',
icon: 'bi-arrow-down-right',
click: () => {}
},
{
type: 'button',
text: 'Add to playlist',
icon: 'bi-music-note-list',
click: () => {},
items: [
{
type: 'button',
text: 'Sample playlist',
icon: 'bi-music-note-list',
click: () => {}
},
{ type: 'separator', },
{
type: 'button',
text: 'New playlist',
icon: 'bi-plus-lg',
click: () => {}
},
]
},
{ type: 'separator', },
{
type: 'button',
text: 'Add to favorites',
icon: 'bi-heart',
click: () => {}
},
{
type: 'button',
text: 'Organize and share',
icon: 'bi-collection',
click: () => {},
items: [
{
type: 'button',
text: 'Bandcamp',
icon: '',
click: () => {}
},
{
type: 'button',
text: 'Creative Commons',
icon: '',
click: () => {}
},
{ type: 'separator', },
{
type: 'button',
text: 'New library',
icon: 'bi-plus-lg',
click: () => {}
},
{ type: 'separator', },
{
type: 'button',
text: 'Share by link',
icon: '',
click: () => {}
},
]
},
{
type: 'button',
text: 'Download',
icon: 'bi-cloud-download',
click: () => {}
},
{ type: 'separator', },
{
type: 'button',
text: 'Report',
icon: 'bi-exclamation',
click: () => {}
},
]
const open = ref(false)
</script>
# Popover
```html
<fw-popover :items="items" />
```
<fw-options-button @click="open = !open" />
<fw-popover :items="items" v-model:open="open" />
......@@ -11,10 +11,14 @@ export { default as FwAlbumCard } from './card/album/Card.vue'
export { default as FwRadioCard } from './card/radio/Card.vue'
export { default as FwCard } from './card/Card.vue'
// Activity
export { default as FwActivity } from './activity/Activity.vue'
// Pills
export { default as FwPill } from './pill/Pill.vue'
// Popover
export { default as FwPopover } from './popover/Popover.vue'
// Loader
export { default as FwLoader } from './loader/Loader.vue'
// Pills
export { default as FwPill } from './pill/Pill.vue'
<script setup lang="ts">
import { useVModel, onClickOutside } from '@vueuse/core'
import { computed, ref, reactive, watch } from 'vue'
import { FwPopover } from '~/components'
interface Button {
type: 'button'
text: string
icon: string
click: () => Promise<void> | void
items?: Item[]
}
interface Separator {
type: 'separator'
}
type Item = Button | Separator
type EnumeratedItem = Item & { _id: number }
interface Events {
(e: 'update:open', open: boolean): void
}
interface Props {
items: Item[]
open?: boolean
clickOutside?: boolean
}
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), {
open: false,
clickOutside: true
})
const items = computed<EnumeratedItem[]>(() => props.items.map(item => ({ ...item, _id: Math.random() })))
const hovered = ref<EnumeratedItem>()
const childOpen = reactive<Record<string, boolean>>({})
watch(hovered, (to, from) => {
if (from?._id) {
childOpen[from?._id] = false
}
if (to?._id) {
childOpen[to?._id] = true
}
})
const popover = ref()
const open = useVModel(props, 'open', emit)
onClickOutside(popover, () => {
if (props.clickOutside) {
open.value = false
}
})
watch(open, (to) => {
if (!to) {
hovered.value = undefined
}
})
</script>
<template>
<div
:class="{ 'is-open': open }"
ref="popover"
class="funkwhale popover"
>
<div
v-for="item in items"
:key="item._id"
@mouseover="hovered = item"
:class="['item', item.type]"
>
<template v-if="item.type === 'button'">
<i :class="['bi', item.icon]" />
{{ item.text }}
<i
v-if="item.items?.length"
class="bi bi-chevron-right"
/>
<fw-popover
v-if="item.items?.length"
:items="item.items"
:click-outside="false"
v-model:open="childOpen[item._id]"
/>
</template>
</div>
</div>
</template>
<style lang="scss">
@import './style.scss'
</style>
.funkwhale {
&.popover {
--fw-border-color: var(--fw-grey-500);
html.dark & {
--fw-border-color: var(--fw-grey-800);
}
position: absolute;
padding: 16px 16px 24px;
border: 1px solid var(--fw-border-color);
border-radius: var(--fw-border-radius);
min-width: 246px;
z-index: 999;
background: var(--fw-bg-color);
transition: all .2s ease;
will-change: transform;
pointer-events: none;
opacity: 0;
transform: scale(0.97);
&.is-open {
pointer-events: auto;
opacity: 1;
transform: scale(1);
}
> .item {
&.button {
cursor: pointer;
padding: 0 8px;
height: 32px;
display: flex;
align-items: center;
border-radius: var(--fw-border-radius);
&:hover {
background-color: var(--fw-grey-100);
html.dark & {
background-color: var(--fw-grey-800);
}
}
> .bi:first-child {
margin-right: 16px;
}
> .bi-chevron-right {
margin-left: auto;
}
> .funkwhale.popover {
left: 90%;
align-self: flex-start;
}
}
&.separator {
padding-top: 12px;
margin-bottom: 12px;
border-bottom: 1px solid var(--fw-border-color);
}
}
}
}
......@@ -55,7 +55,7 @@
--fw-pastel-yellow-4: #efa300;
// Background
--fw-bg-color: var(--fw-grey-100);
--fw-bg-color: #fff;
--fw-text-color: var(--fw-blue-900);
// Override Bulma
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment