Skip to content
Snippets Groups Projects
Commit 3b29686f authored by Ciarán Ainsworth's avatar Ciarán Ainsworth
Browse files

Merge branch 'wvffle/vui' into 'main'

Implement all components

Closes #1

See merge request funkwhale/vui!1
parents af9203eb a9eac5cf
No related branches found
No related tags found
1 merge request!1Implement all components
Pipeline #23756 passed with stages
in 5 minutes and 43 seconds
Showing
with 698 additions and 5 deletions
> 1%
last 2 versions
not dead
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{html,js,vue,css,scss,json,yml,ts,md}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
node_modules node_modules
/dist /dist
/coverage /coverage
junit.xml
# local env files # local env files
.env.local .env.local
...@@ -22,5 +23,6 @@ pnpm-debug.log* ...@@ -22,5 +23,6 @@ pnpm-debug.log*
*.sln *.sln
*.sw? *.sw?
# vitepress # vitepress
public public
include:
- project: 'funkwhale/gitlab-ci-templates'
file: '/static_pages/jobs.yaml'
stages: stages:
- build - build
- test - test
...@@ -5,14 +9,14 @@ stages: ...@@ -5,14 +9,14 @@ stages:
default: default:
image: node:16-slim image: node:16-slim
before_script: before_script:
- yarn install - yarn install
build: build:
stage: build stage: build
script: yarn build script: yarn build
pages: docs:
stage: build stage: build
script: yarn docs:build script: yarn docs:build
artifacts: artifacts:
...@@ -28,6 +32,14 @@ test: ...@@ -28,6 +32,14 @@ test:
coverage_format: cobertura coverage_format: cobertura
path: coverage/cobertura-coverage.xml path: coverage/cobertura-coverage.xml
deploy:
extends: .ssh_access
dependencies:
- docs
script:
- rsync -r -e "ssh -p 2281" $CI_PROJECT_DIR/public/ ui@ui.funkwhale.audio:/htdocs/
only:
- main@funkwhale/vui
publish: publish:
stage: deploy stage: deploy
......
tasks:
- name: Docs
init: yarn install
command: yarn docs:dev
vscode:
extensions:
- ZixuanChen.vitest-explorer
- lukashass.volar
import { defineConfig } from 'vitepress'
export default defineConfig({
title: "Funkwhale Vue Components",
outDir: "../public",
cleanUrls: 'with-subfolders',
themeConfig: {
nav: [
{ text: "Home", link: "https://funkwhale.audio" },
{ text: "Gitlab", link: "https://dev.funkwhale.audio/funkwhale/vui" }
],
sidebar: [
{
text: "Components",
items: [
{ text: "Pill", link: "/components/pill/" },
{ text: "Loader", link: "/components/loader/" },
{ text: "Activity", link: "/components/activity/" },
{ text: "Card", items: [
{ text: "Basic Card", link: "/components/card/basic" },
{ text: "Radio Card", link: "/components/card/radio" },
{ text: "Artist Card", link: "/components/card/artist" },
{ text: "Album Card", link: "/components/card/album" },
{ text: "Playlist Card", link: "/components/card/playlist" },
{ text: "Podcast Card", link: "/components/card/podcast" },
] },
{ text: "Form", items: [
{ text: "Input", link: "/components/input/" },
{ text: "Textarea", link: "/components/textarea/" },
{ text: "Toggle", link: "/components/toggle/" },
{ text: "Popover", link: "/components/popover/" },
{ text: "Button", items: [
{ text: "Basic Button", link: "/components/button/basic" },
{ text: "Play Button", link: "/components/button/play" },
] },
] },
{ text: "Content Navigation", items: [
{ text: "Table of Contents", link: "/components/toc/" },
{ text: "Pagination", link: "/components/pagination/" },
{ text: "Tabs", link: "/components/tabs/" },
] },
]
}
]
}
})
import { createI18n } from 'vue-i18n'
import DefaultTheme from 'vitepress/theme'
import en from '~/locales/en.yaml'
import Funkwhale from '~/main'
export default {
...DefaultTheme,
enhanceApp({ app }) {
const i18n = createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: { en }
})
app.use(Funkwhale)
app.use(i18n)
}
}
<script setup lang="ts">
const track = {
name: 'Some lovely track',
artist: {
name: 'Artist'
},
cover: {
urls: {
original: 'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'
}
}
}
const user = {
username: 'username'
}
</script>
# Activity
```html
<fw-activity :track="track" :user="user" />
```
<fw-activity :track="track" :user="user" />
::: info
Whenever there is more than 1 activity entry in a row, there would be a 1px border to separate the entries
:::
```html
<fw-activity :track="track" :user="user" v-for="i in 3" :key="i" />
```
<fw-activity :track="track" :user="user" v-for="i in 3" :key="i" />
<script setup>
const click = () => new Promise(resolve => setTimeout(resolve, 1000))
</script>
# Button
## Button colors
### Primary button
```html
<fw-button>
Primary button
</fw-button>
```
<fw-button>
Primary button
</fw-button>
### Secondary button
```html
<fw-button secondary>
Secondary button
</fw-button>
```
<fw-button secondary>
Secondary button
</fw-button>
### Destructive button
```html
<fw-button destructive>
Destructive button
</fw-button>
```
<fw-button destructive>
Destructive button
</fw-button>
## Button types
### Filled
```html
<fw-button>
Filled button
</fw-button>
```
<fw-button>
Filled button
</fw-button>
### Outline
```html
<fw-button outline>
Outline button
</fw-button>
```
<fw-button outline>
Outline button
</fw-button>
## Button shapes
### Normal
```html
<fw-button>
Normal button
</fw-button>
```
<fw-button>
Normal button
</fw-button>
### Round
```html
<fw-button round>
Round button
</fw-button>
```
<fw-button round>
Round button
</fw-button>
## Button states
### Disabled
```html
<fw-button disabled>
Disabled button
</fw-button>
```
<fw-button disabled>
Disabled button
</fw-button>
### Active
```html
<fw-button is-active>
Active button
</fw-button>
```
<fw-button is-active>
Active button
</fw-button>
### Loading
```html
<fw-button is-loading>
Loading button
</fw-button>
```
<fw-button is-loading>
Loading button
</fw-button>
&nbsp;
#### Promise handling in @click
When a function passed to `@click` returns a promise, the button will automatically toggle it's loading state on click, and when promise resolves/rejects, it will toggle it off.
::: danger
The `<fw-button>` component does not have any promise rejection mechanism implemented. Make sure, that the `@click` handler never rejects.
:::
```vue
<script setup lang="ts">
const click = () => new Promise(resolve => setTimeout(resolve, 1000))
</script>
<template>
<fw-button @click="click">
Click me
</fw-button>
</template>
```
<fw-button @click="click">
Click me
</fw-button>
## Icons
You can use [Bootstrap Icons](https://icons.getbootstrap.com/) in your button component
::: info
When the button doesn't have any contents or it's contents are empty, it will shrink down to the icon size.
To avoid this, you can add `&nbsp;`.
:::
```html
<fw-button secondary icon="bi-three-dots-vertical" />
<fw-button secondary is-round icon="bi-x" />
<fw-button icon="bi-save">&nbsp;</fw-button>
<fw-button destructive icon="bi-trash">
Delete
</fw-button>
```
<fw-button secondary icon="bi-three-dots-vertical" />
<fw-button secondary round icon="bi-x" />
<fw-button icon="bi-save">&nbsp;</fw-button>
<fw-button destructive icon="bi-trash">
Delete
</fw-button>
# Play Button
```html
<fw-play-button @play="play" />
```
<fw-play-button />
<script setup lang="ts">
const album = {
name: 'Relatively Long Album Name',
artist: {
name: 'Artist Name'
},
tracks: [{}, {}, {}, {}],
cover: {
urls: {
original: 'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'
}
}
}
</script>
# Album card
## Normal card
```html
<fw-album-card :album="album" />
```
<fw-album-card :album="album" />
<script setup lang="ts">
const artist = {
name: 'Artist Name',
tags: ['Electro'],
albums: [{}, {}, {}, {}],
cover: {
urls: {
original: 'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'
}
}
}
</script>
# Artist card
## Normal card
```html
<fw-artist-card :artist="artist" />
```
<fw-artist-card :artist="artist" />
<script setup lang="ts">
const alert = (message: string) => window.alert(message)
</script>
# Card
## Basic card
```html
<fw-card title="For music lovers">
Access your personnal music collection from anywhere. Beeing focused on the promotion of Free licensed content, Funkwhale sports advanced sharing features.
</fw-card>
```
<fw-card title="For music lovers">
Access your personnal music collection from anywhere. Beeing focused on the promotion of Free licensed content, Funkwhale sports advanced sharing features.
</fw-card>
## Interactive card
```html
<fw-card
title="Frequently Asked Questions"
@click="alert('hello')"
>
You have a question about Funkwhale? Get a quick answer!
</fw-card>
```
<fw-card
title="Frequently Asked Questions"
@click="alert('hello')"
>
You have a question about Funkwhale? Get a quick answer!
</fw-card>
## Category card
```html
<fw-card
category
title="Example Translations"
@click="alert('hello')"
/>
```
<fw-card
category
title="Example Translations"
@click="alert('hello')"
/>
## Card with an image
```html
<fw-card
title="For music lovers"
image="https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb"
>
Access your personnal music collection from anywhere. Beeing focused on the promotion of Free licensed content, Funkwhale sports advanced sharing features.
</fw-card>
```
<fw-card
title="For music lovers"
image="https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb"
>
Access your personnal music collection from anywhere. Beeing focused on the promotion of Free licensed content, Funkwhale sports advanced sharing features.
</fw-card>
## Card with a button
```html
<fw-card
title="Join an existing pod"
button-title="Find a pod"
@click="alert('hello')"
>
The easiest way to start using Funkwhale is to register an account on one of the many available pods.
</fw-card>
```
<fw-card
title="Join an existing pod"
button-title="Find a pod"
@click="alert('hello')"
>
The easiest way to start using Funkwhale is to register an account on one of the many available pods.
</fw-card>
<script setup lang="ts">
const playlist = {
name: 'Incredible Playlist',
user: {
full_username: '@username:example.com',
username: 'username'
},
tracks_count: 27,
album_covers: [
'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb',
'https://unsplash.com/photos/SVGan4GCopM/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTV8fG11c2ljaWFufGVufDB8Mnx8fDE2NjI5NzQwNjI&force=true&w=640',
'https://unsplash.com/photos/P4gXe-RsEXI/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8Nnx8cGVyZm9ybWVyfGVufDB8Mnx8fDE2NjMwMTM3OTE&force=true&w=640'
]
}
</script>
# Playlist card
## Normal card
```html
<fw-playlist-card :playlist="playlist" />
```
<div style="display: grid; grid-template-columns: auto auto 1fr; grid-gap: 2rem;">
<fw-playlist-card :playlist="playlist" />
<fw-playlist-card :playlist="{ ...playlist, album_covers: [] }" />
</div>
<script setup lang="ts">
const podcast = {
name: 'Podcast title',
tracks_count: 8,
artist: {
name: 'Podcast owner',
modification_date: '2022-09-11',
cover: {
urls: {
original: 'https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb'
}
}
},
}
</script>
# Podcast card
## Normal card
```html
<fw-podcast-card :podcast="podcast" />
```
<fw-podcast-card :podcast="podcast" />
<script setup lang="ts">
const radio = {
name: 'Less Listened',
description: 'First artist, Second Artist, One Other Artist, Yet Another One'
}
</script>
# Radio card
## Card colors
### Blue
This is the default color
```html
<fw-radio-card blue :radio="radio" />
```
<fw-radio-card blue :radio="radio" />
### Red
```html
<fw-radio-card red :radio="radio" />
```
<fw-radio-card red :radio="radio" />
### Purple
```html
<fw-radio-card purple :radio="radio" />
```
<fw-radio-card purple :radio="radio" />
### Green
```html
<fw-radio-card green :radio="radio" />
```
<fw-radio-card green :radio="radio" />
### Yellow
```html
<fw-radio-card yellow :radio="radio" />
```
<fw-radio-card yellow :radio="radio" />
## Card sizes
### Normal card
```html
<fw-radio-card :radio="radio" />
```
<fw-radio-card :radio="radio" />
### Small card
```html
<fw-radio-card small :radio="radio" />
```
<fw-radio-card small :radio="radio" />
# Input
## Input model
```html
<fw-input v-model="value" placeholder="Search" />
```
<fw-input placeholder="Search" />
## Input icons
You can use bootstrap icons
```html
<fw-input v-model="value" icon="bi-search" placeholder="Search" />
```
<fw-input icon="bi-search" placeholder="Search" />
## Input-right slot
```html
<fw-input v-model="value" placeholder="Search">
<template #input-right>
suffix
</template>
</fw-input>
```
<fw-input placeholder="Search">
<template #input-right>
suffix
</template>
</fw-input>
<style>
.docs-loader-container div[style^=width] {
border: 1px solid #666;
height: 2em;
}
</style>
# Loader
```html
<div style="width: 50%">
<fw-loader />
</div>
```
<div class="docs-loader-container">
<div style="width: 50%">
<fw-loader />
</div>
</div>
## No container
By default the `<fw-loader />` component creates a container that is 100% height of component's parent. Sometimes we do not want such behavior. In those cases we can disable this using `:container="false"` property. The loader will then be centered in the middle of the first parent that has `position: relative` set.
```html
<div style="position: relative">
<div style="width: 50%">
<fw-loader :container="false" />
</div>
</div>
```
<div class="docs-loader-container">
<div style="position: relative">
<div style="width: 50%">
<fw-loader :container="false" />
</div>
</div>
</div>
<script setup lang="ts">
import { ref } from 'vue'
const page = ref(1)
</script>
# Pagination
## Pagination model
```html
<fw-pagination :pages="8" v-model:page="page" />
```
<fw-pagination :pages="8" v-model:page="page" />
# Pill
## Pill colors
### Primary pill
```html
<fw-pill primary>
Primary pill
</fw-pill>
```
<fw-pill primary>
Primary pill
</fw-pill>
### Secondary pill
This is the default pill color
```html
<fw-pill>
Secondary pill
</fw-pill>
```
<fw-pill>
Secondary pill
</fw-pill>
### Destructive pill
```html
<fw-pill destructive>
Destructive pill
</fw-pill>
```
<fw-pill destructive>
Destructive pill
</fw-pill>
### Blue
```html
<fw-pill blue>
Blue pill
</fw-pill>
```
<fw-pill blue>
Blue pill
</fw-pill>
### Red
```html
<fw-pill red>
Red pill
</fw-pill>
```
<fw-pill red>
Red pill
</fw-pill>
### Purple
```html
<fw-pill purple>
Purple pill
</fw-pill>
```
<fw-pill purple>
Purple pill
</fw-pill>
### Green
```html
<fw-pill green>
Green pill
</fw-pill>
```
<fw-pill green>
Green pill
</fw-pill>
### Yellow
```html
<fw-pill yellow>
Yellow pill
</fw-pill>
```
<fw-pill yellow>
Yellow pill
</fw-pill>
## Images
```html
<fw-pill>
<template #image>
<img src="/images/awesome-artist.png" />
</template>
Awesome artist
</fw-pill>
```
<fw-pill>
<template #image>
<div style="background-color: #0004" />
</template>
Awesome artist
</fw-pill>
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