Skip to content
Snippets Groups Projects
index.md 8.41 KiB
Newer Older
Kasper Seweryn's avatar
Kasper Seweryn committed
<script setup lang="ts">
import { ref } from 'vue'

Kasper Seweryn's avatar
Kasper Seweryn committed
const bandcampPrivacy = ref('pod')
const ccPrivacy = ref('public')

const bandcamp = ref(false)
const cc = ref(false)
const share = ref(false)

Kasper Seweryn's avatar
Kasper Seweryn committed
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: [
      {
Kasper Seweryn's avatar
Kasper Seweryn committed
        type: 'select',
Kasper Seweryn's avatar
Kasper Seweryn committed
        text: 'Bandcamp',
Kasper Seweryn's avatar
Kasper Seweryn committed
        model: bandcamp,
        extraItems: [
          { 
            type: 'library-privacy-level', 
            model: bandcampPrivacy
          }
        ]
Kasper Seweryn's avatar
Kasper Seweryn committed
      },
      {
Kasper Seweryn's avatar
Kasper Seweryn committed
        type: 'select',
Kasper Seweryn's avatar
Kasper Seweryn committed
        text: 'Creative Commons',
Kasper Seweryn's avatar
Kasper Seweryn committed
        model: cc,
        extraItems: [
          { 
            type: 'library-privacy-level', 
            model: ccPrivacy
          }
        ]
Kasper Seweryn's avatar
Kasper Seweryn committed
      },
      { type: 'separator', },
      {
        type: 'button',
        text: 'New library',
        icon: 'bi-plus-lg',
        click: () => {}
      },
      { type: 'separator', },
      {
Kasper Seweryn's avatar
Kasper Seweryn committed
        type: 'select',
Kasper Seweryn's avatar
Kasper Seweryn committed
        text: 'Share by link',
Kasper Seweryn's avatar
Kasper Seweryn committed
        model: share,
        extraItems: [
          { 
            type: 'circle-button', 
            icon: 'bi-link', 
            click: () => {} 
          },
          { 
            type: 'circle-button', 
            icon: 'bi-code', 
            click: () => {} 
          },
        ]
Kasper Seweryn's avatar
Kasper Seweryn committed
      },
    ]
  },
  {
    type: 'button',
    text: 'Download',
    icon: 'bi-cloud-download',
    click: () => {}
  },
  { type: 'separator', },
  {
    type: 'button',
    text: 'Report',
    icon: 'bi-exclamation',
    click: () => {}
  },
]

const oneButtonMenu = [
  {
    type: 'button',
    text: 'Report',
    icon: 'bi-exclamation',
    click: () => {}
  }
]

const oneSelectMenu = [
  {
    type: 'select',
    text: 'Bandcamp',
    model: bandcamp
  }
]

const oneSeparatorMenu = [
  { type: 'separator', }
]

const nestedMenu = [
  {
    type: 'button',
    text: 'Organize and share',
    icon: 'bi-collection',
    click: () => {},
    items: [
      {
        type: 'select',
        text: 'Bandcamp',
        model: bandcamp,
      }
    ]
  }
]

const extraItemsMenu = [
  {
    type: 'select',
    text: 'Bandcamp',
    model: bandcamp,
    extraItems: [
      {
        type: 'library-privacy-level',
        model: bandcampPrivacy
      }
    ]
  },
  {
    type: 'select',
    text: 'Creative Commons',
    model: cc,
    extraItems: [
      {
        type: 'circle-button',
        icon: 'bi-exclamation',
        click: () => {}
      }
    ]
  }
]

Kasper Seweryn's avatar
Kasper Seweryn committed
const open = ref(false)
const separator = ref(false)
const singleButton = ref(false)
const singleSelect = ref(false)
const extraItemsOpen = ref(false)
Kasper Seweryn's avatar
Kasper Seweryn committed
</script>

# Popover

Popovers are visually hiden menus of items activated by a simple button. Use popovers to create complex menus in a visually appealing way.

| Prop | Data type | Required? | Description |
| ----- | --------- | ----------- | ----------- |
| `items` | Array\<Item\> | Yes | A list of menu items. |
| `v-model:open` | Boolean | Yes | Controls whether the popover is open or not.  |
Ciarán Ainsworth's avatar
Ciarán Ainsworth committed
| `click-outside` | Boolean | No | Controls whether clicking outside the popover closes it. |

## Items

Popovers contain a list of menu items. Items can contain different information based on their type.

### Button

A button is a clickable item users can interact with. It can take the form of a link or an action.

| Property | Data type | Required? | Description |
| --------- | --------- | ----------  | ----------- |
|`type` | String | Yes | The item type (`button`). |
| `text` | String | Yes | The text that appears in the menu. |
| `icon` | String | Yes | The icon that appears in the menu. |
| `click` | Event | Yes | The action that triggers when a user clicks the button. |

```vue{2-9}
<script-setup lang="ts">
const items = [
  {    
    type: 'button',
    text: 'Report',
    icon: 'bi-exclamation',
    click: () => {}
  }
]

const open = ref(false)
</script>

<template>
  <fw-options-button @click="open = !open" />
  <fw-popover :items="items" v-model:open="open" />
</template
```

<fw-options-button @click="singleButton = !singleButton" />
<fw-popover :items="oneButtonMenu" v-model:open="singleButton" />

### Select

A select is a checkbox item that users can select or deselect to modify data.

| Property | Data type | Required? | Description |
| --------- | --------- | ----------  | ----------- |
|`type` | String | Yes | The item type (`select`). |
| `text` | String | Yes | The text that appears in the menu. |
| `model` | Property | Yes | The property the select button maps to. |

```vue{2-8}
<script-setup lang="ts">
const items = [
  {
    type: 'select',
    text: 'Bandcamp',
    model: bandcamp
  }
]

const open = ref(false)
</script>

<template>
  <fw-options-button @click="open = !open" />
  <fw-popover :items="items" v-model:open="open" />
</template>
```

<fw-options-button @click="singleSelect = !singleSelect" />
<fw-popover :items="oneSelectMenu" v-model:open="singleSelect" />

### Separator

| Property | Data type | Required? | Description |
| --------- | --------- | ----------  | ----------- |
|`type` | String | Yes | The item type (`separator`). |

Separators are visual separators that break the menu up into different sections.

```vue{2-6}
<script-setup lang="ts">
const items=[
  {
    type: 'separator'
  }
]
</script>

<template>
  <fw-options-button @click="open = !open" />
  <fw-popover :items="items" v-model:open="open" />
</template>
```

<fw-options-button @click="separator = !separator" />
<fw-popover :items="oneSeparatorMenu" v-model:open="separator" />

## Nested items

To create a more complex menu, you can nest items within other menu items. Add a new `items` array inside an item to nest the array as an expanded list.

```vue{8-14}
<script-setup lang="ts">
const items = [
  {
    type: 'button',
    text: 'Organize and share',
    icon: 'bi-collection',
    click: () => {},
    items: [
      {
        type: 'select',
        text: 'Bandcamp',
        model: bandcamp,
      }
    ]
  }
]

const open = ref(false)
</script>

<template>
  <fw-options-button @click="open = !open" />
  <fw-popover :items="items" v-model:open="open" />
</template
```

<fw-options-button @click="singleButton = !singleButton" />
<fw-popover :items="nestedMenu" v-model:open="singleButton" />

## Extra items

::: info
Extra items can only be added to `button` and `select` items.
:::

You can extend items without creating submenus by attaching a small extra menu on the right hand side of the item.

| Property | Data type | Required? | Description |
| --------- | --------- | ----------  | ----------- |
|`type` | String | Yes | The item type. |

::: details Supported types

- `circle-button` – a small clickable circle button
- `library-privacy-level` – a selectable list of Funkwhale privacy levels

:::

```vue{7-12,18-24}
<script-setup lang="ts">
const items = [
  {
    type: 'select',
    text: 'Bandcamp',
    model: bandcamp,
    extraItems: [
      {
        type: 'library-privacy-level',
        model: bandcampPrivacy
      }
    ]
  },
  {
    type: 'select',
    text: 'Creative Commons',
    model: cc,
    extraItems: [
      {
        type: 'circle-button',
        icon: 'bi-exclamation',
        click: () => {}
      }
    ]
  }
]

const open = ref(false)
</script>
<template>
  <fw-options-button @click="open = !open" />
  <fw-popover :items="items" v-model:open="open" />
</template>

```

<fw-options-button @click="extraItemsOpen = !extraItemsOpen" />
<fw-popover :items="extraItemsMenu" v-model:open="extraItemsOpen" />

## Menu

Here is an example of a completed menu containing all supported features.

```vue-html
<fw-options-button @click="open = !open" />
<fw-popover :items="items" v-model:open="open" />
Kasper Seweryn's avatar
Kasper Seweryn committed
```

<fw-options-button @click="open = !open" />
<fw-popover :items="items" v-model:open="open" />