Skip to content

Commit

Permalink
feat: implement app sidebar section
Browse files Browse the repository at this point in the history
  • Loading branch information
pirhoo committed Jul 4, 2024
1 parent 35b1fac commit 1719e3a
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 7 deletions.
5 changes: 5 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ declare module 'vue' {
AppSidebar: typeof import('./src/components/AppSidebar.vue')['default']
AppSidebarFooter: typeof import('./src/components/AppSidebarFooter.vue')['default']
AppSidebarSection: typeof import('./src/components/AppSidebarSection.vue')['default']
AppSidebarSectionEntry: typeof import('./src/components/AppSidebarSectionEntry.vue')['default']
AppSidebarSectionItem: typeof import('./src/components/AppSidebarSectionItem.vue')['default']
AppSidebarSectionTitle: typeof import('./src/components/AppSidebarSectionTitle.vue')['default']
AppSidebarSectionToggler: typeof import('./src/components/AppSidebarSectionToggler.vue')['default']
AudioViewer: typeof import('./src/components/document/viewers/AudioViewer.vue')['default']
BAlert: typeof import('bootstrap-vue-next')['BAlert']
BatchDownloadActions: typeof import('./src/components/BatchDownloadActions.vue')['default']
Expand Down Expand Up @@ -66,6 +70,7 @@ declare module 'vue' {
ColumnFilterBadge: typeof import('./src/components/ColumnFilterBadge.vue')['default']
ColumnFilterDropdown: typeof import('./src/components/ColumnFilterDropdown.vue')['default']
ContentTypeBadge: typeof import('./src/components/ContentTypeBadge.vue')['default']
copy: typeof import('./src/components/AppSidebarSection copy.vue')['default']
DismissableAlert: typeof import('./src/components/DismissableAlert.vue')['default']
DocumentActions: typeof import('./src/components/DocumentActions.vue')['default']
DocumentAttachments: typeof import('./src/components/DocumentAttachments.vue')['default']
Expand Down
2 changes: 1 addition & 1 deletion src/components/AppSidebarFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const classList = computed(() => {
margin: $spacer-xxs;
&__icon:hover {
color: var(--bs-primary);
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
}
}
}
Expand Down
29 changes: 28 additions & 1 deletion src/components/AppSidebarSection.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<script setup>
import { computed } from 'vue'
import AppSidebarSectionToggler from '@/components/AppSidebarSectionToggler'
import AppSidebarSectionTitle from '@/components/AppSidebarSectionTitle'
const props = defineProps({
compact: {
type: Boolean
Expand All @@ -10,6 +13,9 @@ const props = defineProps({
},
icon: {
type: String
},
active: {
type: Boolean
}
})
Expand All @@ -21,5 +27,26 @@ const classList = computed(() => {
</script>

<template>
<section class="app-sidebar-section" :class="classList"></section>
<section class="app-sidebar-section" :class="classList">
<app-sidebar-section-toggler v-if="compact" :title="title" :icon="icon" :active="active" />
<template v-else>
<slot name="title">
<app-sidebar-section-title class="p-3" :title="title" :icon="icon" :compact="compact" />
</slot>
<div class="app-sidebar-section__entries ms-4 px-3 pb-3">
<slot />
</div>
</template>
</section>
</template>

<style scoped lang="scss">
.app-sidebar-section {
background: var(--bs-body-bg);
border-radius: var(--bs-border-radius);
&:deep(.app-sidebar-section-entry):not(:last-of-type) {
margin-bottom: $spacer-xs;
}
}
</style>
92 changes: 92 additions & 0 deletions src/components/AppSidebarSectionEntry.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<script setup>
import { computed } from 'vue'
import PhosphorIcon from '@/components/PhosphorIcon'
const props = defineProps({
compact: {
type: Boolean
},
active: {
type: Boolean
},
title: {
type: String
},
to: {
type: Object
},
actionTo: {
type: Object
},
actionIcon: {
type: String,
default: 'plus'
},
actionTitle: {
type: String
},
icon: {
type: String
}
})
const classList = computed(() => {
return {
'app-sidebar-section-entry--compact': props.compact,
'app-sidebar-section-entry--active': props.active
}
})
</script>

<template>
<div class="app-sidebar-section-entry d-flex align-entrys-center flex-truncate" :class="classList">
<router-link :to="to" class="app-sidebar-section-entry__link text-truncate flex-grow-1">
<phosphor-icon class="app-sidebar-section-entry__link__icon me-2" :name="icon" />
<slot>{{ title }}</slot>
</router-link>
<router-link
v-if="actionTo"
v-b-tooltip.right
:to="actionTo"
class="app-sidebar-section-entry__action ms-2"
:title="actionTitle"
>
<phosphor-icon class="app-sidebar-section-entry__action__icon" hover-weight="bold" :name="actionIcon" />
<span class="visually-hidden">{{ actionTitle }}</span>
</router-link>
</div>
</template>

<style scoped lang="scss">
.app-sidebar-section-entry {
position: relative;
&--active:before {
content: '';
position: absolute;
left: -$spacer-xs;
top: $spacer-xxs;
bottom: $spacer-xxs;
width: 2px;
border-radius: 1px;
background: var(--bs-secondary);
}
&__action-icon {
cursor: pointer;
width: $font-size-base * $line-height-base;
height: $font-size-base * $line-height-base;
}
&__link,
&__action {
cursor: pointer;
color: inherit;
&:hover {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
}
}
}
</style>
38 changes: 38 additions & 0 deletions src/components/AppSidebarSectionTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup>
import { computed } from 'vue'
import PhosphorIcon from '@/components/PhosphorIcon'
const props = defineProps({
compact: {
type: Boolean
},
title: {
type: String
},
icon: {
type: String
}
})
const classList = computed(() => {
return {
'app-sidebar-section-title--compact': props.compact
}
})
</script>

<template>
<h3 class="app-sidebar-section-title" :class="classList">
<phosphor-icon class="me-2" :name="icon" />
<slot>{{ title }}</slot>
</h3>
</template>

<style lang="scss" scoped>
.app-sidebar-section-title {
font-size: 1em;
font-weight: bold;
margin: 0;
}
</style>
58 changes: 58 additions & 0 deletions src/components/AppSidebarSectionToggler.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup>
import { computed } from 'vue'
import IconButton from '@/components/IconButton'
const props = defineProps({
active: {
type: Boolean
},
title: {
type: String
},
icon: {
type: String
},
to: {
type: Object
}
})
const classList = computed(() => {
return {
'app-sidebar-section-toggler--active': props.active
}
})
</script>

<template>
<icon-button
v-b-tooltip.right
:icon-left="icon"
square
hide-label
variant="outline-primary"
class="app-sidebar-section-toggler"
:title="title"
:class="classList"
>
{{ title }}
</icon-button>
</template>

<style lang="scss" scoped>
.app-sidebar-section-toggler {
border-color: transparent;
width: calc(#{$btn-line-height * $btn-font-size} + #{$spacer * 2} + #{$btn-border-width} * 2);
height: calc(#{$btn-line-height * $btn-font-size} + #{$spacer * 2} + #{$btn-border-width} * 2);
&:not(:hover) {
color: var(--bs-body-color, inherit);
background: var(--bs-body-bg);
}
&--active {
border-color: var(--bs-secondary);
}
}
</style>
44 changes: 39 additions & 5 deletions src/stories/components/AppSidebarSection.stories.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { vueRouter } from 'storybook-vue3-router'

import AppSidebarSection from '@/components/AppSidebarSection'
import AppSidebarSectionEntry from '@/components/AppSidebarSectionEntry'

const routes = [{ path: '/', name: 'home' }]

export default {
decorators: [vueRouter(routes)],
title: 'Components/AppSidebar/Section',
tags: ['autodocs'],
argTypes: {
Expand All @@ -9,19 +15,47 @@ export default {
},
title: {
control: { type: 'text' }
},
active: {
control: { type: 'boolean' }
},
compact: {
control: { type: 'boolean' }
}
},
args: {
icon: 'rocket',
title: 'Group name'
icon: 'circles-three-plus',
title: 'Projects',
active: true,
compact: false
},
render: (args) => ({
components: {
AppSidebarSection
AppSidebarSection,
AppSidebarSectionEntry
},
setup() {
return { args }
},
computed: {
style() {
return { maxWidth: args.compact ? '90px' : '300px' }
}
},
template: `
<app-sidebar-section v-bind="args">
</app-sidebar-section>
<div class="bg-lighter p-3" :style="style">
<app-sidebar-section v-bind="args" :to="{ name: 'home' }">
<app-sidebar-section-entry icon="dots-nine" :action-to="{ name: 'home' }" action-title="Add project">
All projects
</app-sidebar-section-entry>
<app-sidebar-section-entry :active="args.active" icon="push-pin">
Banana Papers
</app-sidebar-section-entry>
<app-sidebar-section-entry icon="push-pin">
Citrus Confidential
</app-sidebar-section-entry>
</app-sidebar-section>
</div>
`
})
}
Expand Down
54 changes: 54 additions & 0 deletions src/stories/components/AppSidebarSectionEntry.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { vueRouter } from 'storybook-vue3-router'

import AppSidebarSectionEntry from '@/components/AppSidebarSectionEntry'

const routes = [{ path: '/', name: 'home' }]

export default {
decorators: [vueRouter(routes)],
title: 'Components/AppSidebar/Section/Entry',
tags: ['autodocs'],
argTypes: {
icon: {
control: { type: 'text' }
},
content: {
control: { type: 'text' }
},
active: {
control: { type: 'boolean' }
}
},
args: {
icon: 'clock-counter-clockwise',
content: 'Visited documents',
active: false
},
render: (args) => ({
components: {
AppSidebarSectionEntry
},
setup() {
return { args }
},
template: `
<div class="card card-body border-0" style="max-width: 270px">
<app-sidebar-section-entry v-bind="args" :to="{ name: 'home' }">
{{ args.content }}
</app-sidebar-section-entry>
</div>
`
})
}

export const Default = {}

export const WithAction = {
args: {
content: 'Batch search',
icon: 'list-magnifying-glass',
actionIcon: 'plus',
actionTitle: 'Add batch search',
actionTo: { name: 'home' }
}
}
Loading

0 comments on commit 1719e3a

Please sign in to comment.