feat: header improvements
This commit is contained in:
parent
706a13242e
commit
e8db2c2b45
@ -14,9 +14,9 @@ describe('List View List', () => {
|
|||||||
cy.visit('/lists/1')
|
cy.visit('/lists/1')
|
||||||
cy.url()
|
cy.url()
|
||||||
.should('contain', '/lists/1/list')
|
.should('contain', '/lists/1/list')
|
||||||
cy.get('.list-title h1')
|
cy.get('.list-title')
|
||||||
.should('contain', 'First List')
|
.should('contain', 'First List')
|
||||||
cy.get('.list-title .dropdown')
|
cy.get('.list-title-dropdown')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
cy.get('p')
|
cy.get('p')
|
||||||
.contains('This list is currently empty.')
|
.contains('This list is currently empty.')
|
||||||
@ -62,7 +62,7 @@ describe('List View List', () => {
|
|||||||
})
|
})
|
||||||
cy.visit(`/lists/${lists[1].id}/`)
|
cy.visit(`/lists/${lists[1].id}/`)
|
||||||
|
|
||||||
cy.get('.list-title .icon')
|
cy.get('.list-title-wrapper .icon')
|
||||||
.should('not.exist')
|
.should('not.exist')
|
||||||
cy.get('input.input[placeholder="Add a new task..."')
|
cy.get('input.input[placeholder="Add a new task..."')
|
||||||
.should('not.exist')
|
.should('not.exist')
|
||||||
|
@ -30,7 +30,7 @@ describe('Lists', () => {
|
|||||||
.should('contain', 'Success')
|
.should('contain', 'Success')
|
||||||
cy.url()
|
cy.url()
|
||||||
.should('contain', '/lists/')
|
.should('contain', '/lists/')
|
||||||
cy.get('.list-title h1')
|
cy.get('.list-title')
|
||||||
.should('contain', 'New List')
|
.should('contain', 'New List')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ describe('Lists', () => {
|
|||||||
const newListName = 'New list name'
|
const newListName = 'New list name'
|
||||||
|
|
||||||
cy.visit('/lists/1')
|
cy.visit('/lists/1')
|
||||||
cy.get('.list-title h1')
|
cy.get('.list-title')
|
||||||
.should('contain', 'First List')
|
.should('contain', 'First List')
|
||||||
|
|
||||||
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
|
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child .dropdown .menu-list-dropdown-trigger')
|
||||||
@ -67,7 +67,7 @@ describe('Lists', () => {
|
|||||||
|
|
||||||
cy.get('.global-notification')
|
cy.get('.global-notification')
|
||||||
.should('contain', 'Success')
|
.should('contain', 'Success')
|
||||||
cy.get('.list-title h1')
|
cy.get('.list-title')
|
||||||
.should('contain', newListName)
|
.should('contain', newListName)
|
||||||
.should('not.contain', lists[0].title)
|
.should('not.contain', lists[0].title)
|
||||||
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child')
|
cy.get('.namespace-container .menu.namespaces-lists .menu-list li:first-child')
|
||||||
@ -104,9 +104,9 @@ describe('Lists', () => {
|
|||||||
it('Should archive a list', () => {
|
it('Should archive a list', () => {
|
||||||
cy.visit(`/lists/${lists[0].id}`)
|
cy.visit(`/lists/${lists[0].id}`)
|
||||||
|
|
||||||
cy.get('.list-title .dropdown')
|
cy.get('.list-title-dropdown')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.list-title .dropdown .dropdown-menu .dropdown-item')
|
cy.get('.list-title-dropdown .dropdown-menu .dropdown-item')
|
||||||
.contains('Archive')
|
.contains('Archive')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.modal-content')
|
cy.get('.modal-content')
|
||||||
|
@ -11,7 +11,7 @@ export function createLists() {
|
|||||||
return lists
|
return lists
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prepareLists(setLists = () => {}) {
|
export function prepareLists(setLists = (...args: any[]) => {}) {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const lists = createLists()
|
const lists = createLists()
|
||||||
setLists(lists)
|
setLists(lists)
|
||||||
|
@ -2,9 +2,9 @@ import {createFakeUserAndLogin} from '../../support/authenticateUser'
|
|||||||
import {createLists} from '../list/prepareLists'
|
import {createLists} from '../list/prepareLists'
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
cy.get('.navbar .user .username')
|
cy.get('.navbar .username-dropdown-trigger')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.navbar .user .dropdown-menu .dropdown-item')
|
cy.get('.navbar .dropdown-item')
|
||||||
.contains('Logout')
|
.contains('Logout')
|
||||||
.click()
|
.click()
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ describe('User Settings', () => {
|
|||||||
|
|
||||||
cy.get('.global-notification')
|
cy.get('.global-notification')
|
||||||
.should('contain', 'Success')
|
.should('contain', 'Success')
|
||||||
cy.get('.navbar .user .username')
|
cy.get('.navbar .username-dropdown-trigger .username')
|
||||||
.should('contain', 'Lorem Ipsum')
|
.should('contain', 'Lorem Ipsum')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -2,95 +2,90 @@
|
|||||||
<header
|
<header
|
||||||
:class="{'has-background': background, 'menu-active': menuActive}"
|
:class="{'has-background': background, 'menu-active': menuActive}"
|
||||||
aria-label="main navigation"
|
aria-label="main navigation"
|
||||||
class="navbar main-theme is-fixed-top d-print-none"
|
class="navbar d-print-none"
|
||||||
>
|
>
|
||||||
<router-link :to="{name: 'home'}" class="logo-link">
|
<router-link :to="{name: 'home'}" class="logo-link">
|
||||||
<Logo width="164" height="48"/>
|
<Logo width="164" height="48"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<MenuButton class="menu-button"/>
|
<MenuButton class="menu-button"/>
|
||||||
<div class="list-title" ref="listTitle" v-show="currentList.id">
|
|
||||||
<template v-if="currentList.id">
|
|
||||||
<h1
|
|
||||||
:style="{ 'opacity': currentList.title === '' ? '0': '1' }"
|
|
||||||
class="title">
|
|
||||||
{{ currentList.title === '' ? $t('misc.loading') : getListTitle(currentList) }}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<BaseButton :to="{name: 'list.info', params: {listId: currentList.id}}" class="info-button">
|
<div
|
||||||
<icon icon="circle-info"/>
|
v-if="currentList.id"
|
||||||
</BaseButton>
|
class="list-title-wrapper"
|
||||||
|
>
|
||||||
|
<h1 class="list-title">{{ currentList.title === '' ? $t('misc.loading') : getListTitle(currentList) }}</h1>
|
||||||
|
|
||||||
|
<BaseButton :to="{name: 'list.info', params: {listId: currentList.id}}" class="list-title-button">
|
||||||
|
<icon icon="circle-info"/>
|
||||||
|
</BaseButton>
|
||||||
|
|
||||||
<list-settings-dropdown v-if="canWriteCurrentList && currentList.id !== -1" :list="currentList"/>
|
<list-settings-dropdown
|
||||||
</template>
|
v-if="canWriteCurrentList && currentList.id !== -1"
|
||||||
|
class="list-title-dropdown"
|
||||||
|
:list="currentList"
|
||||||
|
>
|
||||||
|
<template #trigger="{toggleOpen}">
|
||||||
|
<BaseButton class="list-title-button" @click="toggleOpen">
|
||||||
|
<icon icon="ellipsis-h" class="icon"/>
|
||||||
|
</BaseButton>
|
||||||
|
</template>
|
||||||
|
</list-settings-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
@click="openQuickActions"
|
@click="openQuickActions"
|
||||||
class="trigger-button pr-0"
|
class="trigger-button"
|
||||||
v-shortcut="'Control+k'"
|
v-shortcut="'Control+k'"
|
||||||
:title="$t('keyboardShortcuts.quickSearch')"
|
:title="$t('keyboardShortcuts.quickSearch')"
|
||||||
>
|
>
|
||||||
<icon icon="search"/>
|
<icon icon="search"/>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<notifications/>
|
<Notifications />
|
||||||
<div class="user">
|
<dropdown>
|
||||||
<dropdown class="is-right" ref="usernameDropdown">
|
<template #trigger="{toggleOpen, open}">
|
||||||
<template #trigger="{toggleOpen}">
|
<BaseButton
|
||||||
<x-button
|
class="username-dropdown-trigger"
|
||||||
class="username-dropdown-trigger"
|
@click="toggleOpen"
|
||||||
@click="toggleOpen()"
|
variant="secondary"
|
||||||
variant="secondary"
|
:shadow="false"
|
||||||
:shadow="false"
|
>
|
||||||
>
|
<img :src="authStore.avatarUrl" alt="" class="avatar" width="40" height="40"/>
|
||||||
<img :src="authStore.avatarUrl" alt="" class="avatar" width="40" height="40"/>
|
<span class="username">{{ authStore.userDisplayName }}</span>
|
||||||
<span class="username">{{ authStore.userDisplayName }}</span>
|
<span class="icon is-small" :style="{
|
||||||
<span class="icon is-small">
|
transform: open ? 'rotate(180deg)' : 'rotate(0)',
|
||||||
<icon icon="chevron-down"/>
|
}">
|
||||||
</span>
|
<icon icon="chevron-down"/>
|
||||||
</x-button>
|
</span>
|
||||||
</template>
|
</BaseButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
<dropdown-item
|
<dropdown-item :to="{name: 'user.settings'}">
|
||||||
:to="{name: 'user.settings'}"
|
{{ $t('user.settings.title') }}
|
||||||
>
|
</dropdown-item>
|
||||||
{{ $t('user.settings.title') }}
|
<dropdown-item v-if="imprintUrl" :href="imprintUrl">
|
||||||
</dropdown-item>
|
{{ $t('navigation.imprint') }}
|
||||||
<dropdown-item
|
</dropdown-item>
|
||||||
v-if="imprintUrl"
|
<dropdown-item v-if="privacyPolicyUrl" :href="privacyPolicyUrl">
|
||||||
:href="imprintUrl"
|
{{ $t('navigation.privacy') }}
|
||||||
>
|
</dropdown-item>
|
||||||
{{ $t('navigation.imprint') }}
|
<dropdown-item @click="baseStore.setKeyboardShortcutsActive(true)">
|
||||||
</dropdown-item>
|
{{ $t('keyboardShortcuts.title') }}
|
||||||
<dropdown-item
|
</dropdown-item>
|
||||||
v-if="privacyPolicyUrl"
|
<dropdown-item :to="{name: 'about'}">
|
||||||
:href="privacyPolicyUrl"
|
{{ $t('about.title') }}
|
||||||
>
|
</dropdown-item>
|
||||||
{{ $t('navigation.privacy') }}
|
<dropdown-item @click="authStore.logout()">
|
||||||
</dropdown-item>
|
{{ $t('user.auth.logout') }}
|
||||||
<dropdown-item
|
</dropdown-item>
|
||||||
@click="baseStore.setKeyboardShortcutsActive(true)"
|
</dropdown>
|
||||||
>
|
|
||||||
{{ $t('keyboardShortcuts.title') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<dropdown-item
|
|
||||||
:to="{name: 'about'}"
|
|
||||||
>
|
|
||||||
{{ $t('about.title') }}
|
|
||||||
</dropdown-item>
|
|
||||||
<dropdown-item
|
|
||||||
@click="authStore.logout()"
|
|
||||||
>
|
|
||||||
{{ $t('user.auth.logout') }}
|
|
||||||
</dropdown-item>
|
|
||||||
</dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref, computed, onMounted, nextTick} from 'vue'
|
import {computed} from 'vue'
|
||||||
|
|
||||||
import {RIGHTS as Rights} from '@/constants/rights'
|
import {RIGHTS as Rights} from '@/constants/rights'
|
||||||
|
|
||||||
@ -120,182 +115,152 @@ const configStore = useConfigStore()
|
|||||||
const imprintUrl = computed(() => configStore.legal.imprintUrl)
|
const imprintUrl = computed(() => configStore.legal.imprintUrl)
|
||||||
const privacyPolicyUrl = computed(() => configStore.legal.privacyPolicyUrl)
|
const privacyPolicyUrl = computed(() => configStore.legal.privacyPolicyUrl)
|
||||||
|
|
||||||
const usernameDropdown = ref()
|
|
||||||
const listTitle = ref()
|
|
||||||
onMounted(async () => {
|
|
||||||
await nextTick()
|
|
||||||
if (typeof usernameDropdown.value === 'undefined' || typeof listTitle.value === 'undefined') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const usernameWidth = usernameDropdown.value.$el.clientWidth
|
|
||||||
listTitle.value.style.setProperty('--nav-username-width', `${usernameWidth}px`)
|
|
||||||
})
|
|
||||||
|
|
||||||
function openQuickActions() {
|
function openQuickActions() {
|
||||||
baseStore.setQuickActionsActive(true)
|
baseStore.setQuickActionsActive(true)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
$vikunja-nav-logo-full-width: 164px;
|
|
||||||
$user-dropdown-width-mobile: 5rem;
|
$user-dropdown-width-mobile: 5rem;
|
||||||
|
|
||||||
$hamburger-menu-icon-spacing: 1rem;
|
.navbar {
|
||||||
$hamburger-menu-icon-width: 28px;
|
--navbar-button-min-width: 40px;
|
||||||
|
--navbar-gap-width: 1rem;
|
||||||
|
--navbar-icon-size: 1.25rem;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--navbar-gap-width);
|
||||||
|
|
||||||
|
background: var(--site-background);
|
||||||
|
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
padding-right: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: $tablet) {
|
||||||
|
padding-left: 2rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.menu-active {
|
||||||
|
@media screen and (max-width: $tablet) {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: notifications should provide a slot for the icon instead, so that we can style it as we want
|
||||||
|
:deep() {
|
||||||
|
.trigger-button {
|
||||||
|
color: var(--grey-400);
|
||||||
|
font-size: var(--navbar-icon-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.logo-link {
|
.logo-link {
|
||||||
display: none;
|
display: none;
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
|
|
||||||
@media screen and (min-width: $tablet) {
|
@media screen and (min-width: $tablet) {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-left: 2rem;
|
margin-right: .5rem;
|
||||||
margin-right: 1.5rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-button {
|
.menu-button {
|
||||||
align-self: stretch;
|
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
align-self: stretch;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
@media screen and (max-width: $tablet) {
|
||||||
margin-left: $hamburger-menu-icon-spacing;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar.main-theme {
|
.list-title-wrapper {
|
||||||
background: var(--site-background);
|
margin-inline: auto;
|
||||||
justify-content: space-between;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@media screen and (max-width: $desktop) {
|
// this makes the truncated text of the list title work
|
||||||
display: flex;
|
// inside the flexbox parent
|
||||||
justify-content: space-between;
|
min-width: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
@media screen and (min-width: $tablet) {
|
||||||
margin: 0;
|
padding-inline: var(--navbar-gap-width);
|
||||||
font-size: 1.75rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-end {
|
|
||||||
margin-left: 0;
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
|
||||||
&.menu-active {
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
width: $user-dropdown-width-mobile;
|
|
||||||
|
|
||||||
.username-dropdown-trigger {
|
|
||||||
line-height: 1;
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
height: 1rem;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: .5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
// FIXME: notifications should provide a slot for the icon instead, so that we can style it as we want
|
|
||||||
:deep() {
|
|
||||||
.trigger-button {
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--grey-400);
|
|
||||||
padding: .5rem;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * > .trigger-button {
|
|
||||||
width: $navbar-icon-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-family: $vikunja-font;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
border-radius: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
height: 40px;
|
|
||||||
margin-right: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username-dropdown-trigger {
|
|
||||||
background: none;
|
|
||||||
|
|
||||||
&:focus:not(:active), &:active {
|
|
||||||
outline: none !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-title {
|
.list-title {
|
||||||
display: flex;
|
font-size: 1rem;
|
||||||
align-items: center;
|
// We need the following for overflowing ellipsis to work
|
||||||
justify-content: center;
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
$edit-icon-width: 1rem;
|
white-space: nowrap;
|
||||||
|
|
||||||
@media screen and (min-width: $tablet) {
|
@media screen and (min-width: $tablet) {
|
||||||
// We need a fixed width for overflowing ellipsis to work
|
font-size: 1.75rem;
|
||||||
--nav-username-width: 0;
|
|
||||||
width: calc(100vw - #{$user-dropdown-width-mobile} - #{2 * $hamburger-menu-icon-spacing} - #{$hamburger-menu-icon-width} - #{$edit-icon-width} - #{2 * $navbar-icon-width} - #{$vikunja-nav-logo-full-width} - var(--nav-username-width));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title-dropdown {
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
.list-title-button {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-title-button {
|
||||||
|
align-self: stretch;
|
||||||
|
min-width: var(--navbar-button-min-width);
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: var(--navbar-icon-size);
|
||||||
|
color: var(--grey-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-end {
|
||||||
|
margin-left: auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
min-width: var(--navbar-button-min-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.username-dropdown-trigger {
|
||||||
|
padding-left: 1rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: .85rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-family: $vikunja-font;
|
||||||
|
|
||||||
@media screen and (max-width: $tablet) {
|
@media screen and (max-width: $tablet) {
|
||||||
// We need a fixed width for overflowing ellipsis to work
|
display: none;
|
||||||
width: calc(100vw - #{$user-dropdown-width-mobile} - #{2 * $hamburger-menu-icon-spacing} - #{$hamburger-menu-icon-width} - #{$edit-icon-width} - #{2 * $navbar-icon-width});
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.dropdown-trigger) {
|
|
||||||
color: var(--grey-400);
|
|
||||||
margin-left: .5rem;
|
|
||||||
height: 1rem;
|
|
||||||
width: 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-button {
|
.avatar {
|
||||||
text-align: center;
|
border-radius: 100%;
|
||||||
height: 1.25rem;
|
vertical-align: middle;
|
||||||
line-height: 1.25rem;
|
height: 40px;
|
||||||
width: 2rem;
|
margin-right: .5rem;
|
||||||
margin-top: .25rem;
|
|
||||||
padding: 0 .5rem;
|
|
||||||
color: var(--grey-400);
|
|
||||||
margin-left: .5rem;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dropdown" ref="dropdown">
|
<div class="dropdown" ref="dropdown">
|
||||||
<slot name="trigger" :close="close" :toggleOpen="toggleOpen">
|
<slot name="trigger" :close="close" :toggleOpen="toggleOpen" :open="open">
|
||||||
<BaseButton class="dropdown-trigger is-flex" @click="toggleOpen">
|
<BaseButton class="dropdown-trigger is-flex" @click="toggleOpen">
|
||||||
<icon :icon="triggerIcon" class="icon"/>
|
<icon :icon="triggerIcon" class="icon"/>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -56,7 +56,6 @@ onClickOutside(dropdown, (e: Event) => {
|
|||||||
.dropdown {
|
.dropdown {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
<div class="is-flex is-justify-content-center">
|
<slot name="trigger" toggleOpen="() => showNotifications = !showNotifications" :has-unread-notifications="unreadNotifications > 0">
|
||||||
<BaseButton @click.stop="showNotifications = !showNotifications" class="trigger-button">
|
<BaseButton class="trigger-button" @click.stop="showNotifications = !showNotifications">
|
||||||
<span class="unread-indicator" v-if="unreadNotifications > 0"></span>
|
<span class="unread-indicator" v-if="unreadNotifications > 0"></span>
|
||||||
<icon icon="bell"/>
|
<icon icon="bell"/>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</div>
|
</slot>
|
||||||
|
|
||||||
<CustomTransition name="fade">
|
<CustomTransition name="fade">
|
||||||
<div class="notifications-list" v-if="showNotifications" ref="popup">
|
<div class="notifications-list" v-if="showNotifications" ref="popup">
|
||||||
@ -141,7 +141,11 @@ function to(n, index) {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.notifications {
|
.notifications {
|
||||||
width: $navbar-icon-width;
|
display: flex;
|
||||||
|
|
||||||
|
.trigger-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.unread-indicator {
|
.unread-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -156,9 +160,9 @@ function to(n, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notifications-list {
|
.notifications-list {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
right: 1rem;
|
right: 1rem;
|
||||||
margin-top: 1rem;
|
top: calc(100% + 1rem);
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
@ -32,5 +32,4 @@ $button-height: 34px;
|
|||||||
$switch-view-height: 2.69rem;
|
$switch-view-height: 2.69rem;
|
||||||
|
|
||||||
$navbar-height: 4rem;
|
$navbar-height: 4rem;
|
||||||
$navbar-width: 300px;
|
$navbar-width: 300px;
|
||||||
$navbar-icon-width: 40px;
|
|
@ -256,8 +256,8 @@
|
|||||||
--card-border-color: var(--grey-200);
|
--card-border-color: var(--grey-200);
|
||||||
--logo-text-color: hsl(180, 1%, 15%);
|
--logo-text-color: hsl(180, 1%, 15%);
|
||||||
|
|
||||||
@media screen {
|
&.dark {
|
||||||
&.dark {
|
@media screen {
|
||||||
// Light mode colours reversed for dark mode
|
// Light mode colours reversed for dark mode
|
||||||
--grey-900-hsl: 210, 20%, 98%;
|
--grey-900-hsl: 210, 20%, 98%;
|
||||||
--grey-900: hsl(var(--grey-900-hsl));
|
--grey-900: hsl(var(--grey-900-hsl));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user