Skip to content
Snippets Groups Projects
Commit b44e0e8d authored by Kasper Seweryn's avatar Kasper Seweryn :pancakes:
Browse files

feat: add alert and modal components

Part-of: <!93>
parent 2cba5b74
No related branches found
No related tags found
1 merge request!93feat: add alert and modal components
...@@ -14,6 +14,8 @@ export default defineConfig({ ...@@ -14,6 +14,8 @@ export default defineConfig({
text: "Components", text: "Components",
items: [ items: [
{ text: "Pill", link: "/components/pill/" }, { text: "Pill", link: "/components/pill/" },
{ text: "Alert", link: "/components/alert/" },
{ text: "Modal", link: "/components/modal/" },
{ text: "Loader", link: "/components/loader/" }, { text: "Loader", link: "/components/loader/" },
{ text: "Activity", link: "/components/activity/" }, { text: "Activity", link: "/components/activity/" },
{ text: "Card", items: [ { text: "Card", items: [
......
# Alert
| Prop | Data type | Required? | Default | Description |
| ------------- | -------------------------------------------------- | --------- | ----------- | ----------------------- |
| `color` | `blue` \| `red` \| `purple` \| `green` \| `yellow` | No | `secondary` | Renders a colored alert |
## Alert colors
Funkwhale alert support a range of pastel colors to create visually appealing interfaces.
::: details Colors
- Red
- Blue
- Purple
- Green
- Yellow
:::
### Blue
```vue-html
<fw-alert color="blue">
Blue alert
</fw-alert>
```
<fw-alert color="blue">
Blue alert
</fw-alert>
### Red
```vue-html
<fw-alert color="red">
Red alert
</fw-alert>
```
<fw-alert color="red">
Red alert
</fw-alert>
### Purple
```vue-html
<fw-alert color="purple">
Purple alert
</fw-alert>
```
<fw-alert color="purple">
Purple alert
</fw-alert>
### Green
```vue-html
<fw-alert color="green">
Green alert
</fw-alert>
```
<fw-alert color="green">
Green alert
</fw-alert>
### Yellow
```vue-html
<fw-alert color="yellow">
Yellow alert
</fw-alert>
```
<fw-alert color="yellow">
Yellow alert
</fw-alert>
## Alert actions
```vue-html{2-4}
<fw-alert>
Awesome artist
<template #actions>
<fw-button>Got it</fw-button>
</template>
</fw-alert>
```
<fw-alert>
Awesome artist
<template #actions>
<fw-button>Got it</fw-button>
</template>
</fw-alert>
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
const open = ref(false)
const open2 = ref(false)
const open3 = ref(false)
const alertOpen = ref(true)
watchEffect(() => {
if (open3.value === false) {
alertOpen.value = true
}
})
const open4 = ref(false)
const open5 = ref(false)
</script>
# Modal
| Prop | Data type | Required? | Default | Description |
| ------------- | ----------------- | --------- | ------- | ------------------------ |
| `title` | `string` | Yes | | Modal title |
| `v-model` | `true` \| `false` | Yes | | Should the modal be open |
## Modal open
```vue-html
<fw-modal v-model="open" title="My modal">
Modal content
</fw-modal>
<fw-button @click="open = true">
Open modal
</fw-button>
```
<fw-modal v-model="open" title="My modal">
Modal content
</fw-modal>
<fw-button @click="open = true">
Open modal
</fw-button>
## Modal actions
```vue-html
<fw-modal v-model="open" title="My modal">
Modal content
<template #actions>
<fw-button @click="open = false" color="secondary">
Cancel
</fw-button>
<fw-button @click="open = false">
Ok
</fw-button>
</template>
</fw-modal>
<fw-button @click="open = true">
Open modal
</fw-button>
```
<fw-modal v-model="open2" title="My modal">
Modal content
<template #actions>
<fw-button @click="open2 = false" color="secondary">
Cancel
</fw-button>
<fw-button @click="open2 = false">
Ok
</fw-button>
</template>
</fw-modal>
<fw-button @click="open2 = true">
Open modal
</fw-button>
## Nested modals
```vue-html
<fw-modal v-model="open" title="My modal">
Modal content
<template #actions>
<fw-button @click="open = false" color="secondary">
Cancel
</fw-button>
<fw-button @click="open = false">
Ok
</fw-button>
</template>
</fw-modal>
<fw-button @click="open = true">
Open modal
</fw-button>
```
<fw-modal v-model="open4" title="My modal">
<fw-modal v-model="open5" title="My modal">
Nested modal content
</fw-modal>
<fw-button @click="open5 = true">
Open nested modal
</fw-button>
</fw-modal>
<fw-button @click="open4 = true">
Open modal
</fw-button>
## Alert inside modal
```vue-html
<fw-modal v-model="open" title="My modal">
Modal content
<template #alert v-if="alertOpen">
<fw-alert>
Alert content
<template #actions>
<fw-button @click="alertOpen = false">Close alert</fw-button>
</template>
</fw-alert>
</template>
</fw-modal>
<fw-button @click="open = true">
Open modal
</fw-button>
```
<fw-modal v-model="open3" title="My modal">
Modal content
<template #alert v-if="alertOpen">
<fw-alert>
Alert content
<template #actions>
<fw-button @click="alertOpen = false">Close alert</fw-button>
</template>
</fw-alert>
</template>
<template #actions>
<fw-button @click="open3 = false" color="secondary">
Cancel
</fw-button>
<fw-button @click="open3 = false">
Ok
</fw-button>
</template>
</fw-modal>
<fw-button @click="open3 = true">
Open modal
</fw-button>
<script setup lang="ts">
import { useColorOrPastel } from '~/composables/colors'
const props = defineProps<PastelProps>()
const color = useColorOrPastel(() => props.color, 'blue')
</script>
<template>
<div
class="funkwhale is-colored alert"
:class="[color]"
>
<slot />
<div v-if="$slots.actions" class="actions">
<slot name="actions" />
</div>
</div>
</template>
<style lang="scss">
@import './style.scss'
</style>
.funkwhale.alert {
color: var(--fw-gray-900);
@include light-theme {
background-color: var(--fw-pastel-1, var(--fw-bg-color));
> .actions .funkwhale.button {
--fw-bg-color: var(--fw-pastel-2);
&:hover, &.is-hovered {
--fw-bg-color: var(--fw-pastel-3);
}
&:active, &.is-active {
--fw-bg-color: var(--fw-pastel-4);
}
}
}
@include dark-theme {
background-color: var(--fw-pastel-4, var(--fw-bg-color));
> .actions .funkwhale.button {
--fw-bg-color: var(--fw-pastel-3);
box-shadow: 0 2px 4px 2px rgba(#000, 0.1);
&:hover, &.is-hovered {
--fw-bg-color: var(--fw-pastel-2);
}
&:active, &.is-active {
--fw-bg-color: var(--fw-pastel-1);
}
}
}
padding: 0.625rem 2rem;
line-height: 1.2;
display: flex;
align-items: center;
> .actions {
margin-left: auto;
}
}
...@@ -43,3 +43,9 @@ export { default as FwTab } from './tabs/Tab.vue' ...@@ -43,3 +43,9 @@ export { default as FwTab } from './tabs/Tab.vue'
// Utils // Utils
export { default as FwSanitizedHtml } from './utils/SanitizedHtml.vue' export { default as FwSanitizedHtml } from './utils/SanitizedHtml.vue'
export { default as FwMarkdown } from './utils/Markdown.vue' export { default as FwMarkdown } from './utils/Markdown.vue'
// Modal
export { default as FwModal } from './modal/Modal.vue'
// Alert
export { default as FwAlert } from './alert/Alert.vue'
<script setup lang="ts">
const title = defineProp<string>('title', { required: true })
const open = defineModel<boolean>({ required: true })
</script>
<template>
<Teleport to="body">
<Transition mode="out-in">
<div v-if="open" @click.exact.stop="open = false" class="funkwhale overlay">
<div @click.stop class="funkwhale modal">
<h2>{{ title }}</h2>
<Transition>
<div v-if="$slots.alert" class="alert-container">
<div>
<slot name="alert" />
</div>
</div>
</Transition>
<hr />
<div class="modal-content">
<slot />
</div>
<hr v-if="$slots.actions" />
<div v-if="$slots.actions" class="modal-actions">
<slot name="actions" />
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style lang="scss">
@import './style.scss'
</style>
.funkwhale.modal {
background: var(--fw-bg-color);
box-shadow: 0 2px 4px 2px rgba(#000, 0.2);
border-radius: 1rem;
min-width: 32rem;
max-width: 90vw;
> hr {
padding: 0;
margin: 0;
}
> .alert-container {
display: grid;
grid-template-rows: 1fr;
margin-bottom: -1px;
&.v-enter-active,
&.v-leave-active {
transition: grid-template-rows 0.2s ease;
}
&.v-enter-from,
&.v-leave-to {
grid-template-rows: 0fr;
}
> div {
overflow: hidden;
}
}
> h2 {
font-size: 1.25em;
padding: 1.625rem 4.5rem 1rem;
line-height: 1.2;
text-align: center;
}
.modal-content {
padding: 1rem 2rem 1rem;
}
.modal-actions {
padding: 0.75rem 2rem 1rem;
}
}
.funkwhale.overlay {
background: rgba(#000, .2);
position: fixed;
inset: 0;
z-index: 9001;
display: flex;
align-items: center;
justify-content: center;
&.v-enter-active,
&.v-leave-active {
transition: opacity 0.2s ease;
.funkwhale.modal {
transition: transform 0.2s ease;
}
}
&.v-enter-from,
&.v-leave-to {
opacity: 0;
.funkwhale.modal {
transform: translateY(1rem);
}
}
&.v-leave-to {
.funkwhale.modal {
transform: translateY(-1rem);
}
}
}
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
// Horizontal rule // Horizontal rule
%hr { %hr {
border: none;
border-bottom: 1px solid var(--fw-border-color); border-bottom: 1px solid var(--fw-border-color);
@include light-theme { @include light-theme {
...@@ -62,7 +63,6 @@ ...@@ -62,7 +63,6 @@
padding: 8px 0 0 0; padding: 8px 0 0 0;
margin: 0 0 8px 0; margin: 0 0 8px 0;
border: none;
} }
hr:not(.is-raw) { hr:not(.is-raw) {
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
@import "font"; @import "font";
@import "colors"; @import "colors";
body { html, body {
font-family: $font-main; font-family: $font-main;
font-size: 16px; font-size: 16px !important;
// background-color: var(--fw-bg-color); // background-color: var(--fw-bg-color);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment