Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(local-nav): add :actions option #567

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions docs/components/local-nav.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ const title = [
</template>
```

### Page actions

You may define `:actions` to display page actions (buttons). The `:actions` prop accepts array of [`Action`](#action) object. The action object is the same as props for [`<SButton>`](button) except `:size` since the size is fixed to `small`, and an extra `onClick` function to handle the click event.

```vue
<script setup lang="ts">
const title = [
{ text: 'Page title' },
]

const actions = [
{ label: 'Edit title', onClick: () => { /* ... */ } },
{ label: 'About', onClick: () => { /* ... */ } }
]
</script>

<template>
<SLocalNav :title="title" :actions="actions" />
</template>
```

### Page navigation

You may define `:menu` to display a page navigation. The `:menu` props accepts "double nested" array, in order to create grouping of menu items.
Expand Down Expand Up @@ -94,6 +115,30 @@ interface Title {
}
```

### `Action`

```ts
import { type Component } from 'vue'
import { type Mode, type Tooltip, type Type } from '@globalbrain/sefirot/lib/components/SButton.vue'

export interface Action {
tag?: string
type?: Type
mode?: Mode
icon?: Component
leadIcon?: Component
trailIcon?: Component
iconMode?: Mode
label?: string
labelMode?: Mode
href?: string
loading?: boolean
disabled?: boolean
tooltip?: string | Tooltip
onClick?(): void
}
```

### `MenuItem`

The type of menu item.
Expand All @@ -114,6 +159,7 @@ interface Props {
avatar?: Avatar
title: Title[]
description?: string
actions?: Action[]
menu?: MenuItem[][]
}
```
Expand Down Expand Up @@ -149,7 +195,7 @@ interface Props {
/>
```

### `description`
### `:description`

Add description text under the title.

Expand All @@ -166,6 +212,26 @@ interface Props {
/>
```

### `:actions`

The actions of the page.

```ts
interface Props {
actions: Action[]
}
```

```vue-html
<SLocalNav
:title="[{ text: 'Page title' }]"
:actions="[
{ label: 'Edit title' },
{ label: 'About' }
]"
/>
```

### `:menu`

The menu of the page.
Expand All @@ -179,9 +245,9 @@ interface Props {
```vue-html
<SLocalNav
:title="[{ text: 'Page title' }]"
:menu="[
:menu="[[
{ text: 'Menu item 1', link: '/menu1' },
{ text: 'Menu item 2', link: '/menu2' }
]"
]]"
/>
```
20 changes: 18 additions & 2 deletions lib/components/SLocalNav.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { computed } from 'vue'
import SLocalNavActions, { type Action } from './SLocalNavActions.vue'
import SLocalNavAvatar from './SLocalNavAvatar.vue'
import SLocalNavDescription from './SLocalNavDescription.vue'
import SLocalNavMenu, { type MenuItem } from './SLocalNavMenu.vue'
import SLocalNavTitle, { type Title } from './SLocalNavTitle.vue'

export type { Title, MenuItem }
export type { Title, Action, MenuItem }

export interface Avatar {
image?: string | null
Expand All @@ -16,6 +17,7 @@ const props = defineProps<{
avatar?: Avatar
title: Title[]
description?: string
actions?: Action[]
menu?: MenuItem[][]
}>()

Expand All @@ -37,7 +39,10 @@ const normalizedMenu = computed(() => {
/>
</div>
<div class="title-bar-body">
<SLocalNavTitle :title="title" />
<div class="title-bar-title">
<SLocalNavTitle :title="title" />
<SLocalNavActions v-if="actions?.length" :actions="actions" />
</div>
<SLocalNavDescription v-if="description" :text="description" />
</div>
</div>
Expand Down Expand Up @@ -71,4 +76,15 @@ const normalizedMenu = computed(() => {
align-items: center;
gap: 16px;
}

.title-bar-body {
flex-grow: 1;
max-width: 100%;
}

.title-bar-title {
display: flex;
align-items: center;
gap: 24px;
}
</style>
56 changes: 56 additions & 0 deletions lib/components/SLocalNavActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { type Component } from 'vue'
import SButton, { type Mode, type Tooltip, type Type } from './SButton.vue'

export interface Action {
tag?: string
type?: Type
mode?: Mode
icon?: Component
leadIcon?: Component
trailIcon?: Component
iconMode?: Mode
label?: string
labelMode?: Mode
href?: string
loading?: boolean
disabled?: boolean
tooltip?: string | Tooltip
onClick?(): void
}

defineProps<{
actions: Action[]
}>()
</script>

<template>
<div class="SLocalNavActions">
<div v-for="action, i in actions" :key="i" class="action">
<SButton
size="small"
:type="action.type"
:mode="action.mode"
:icon="action.icon"
:lead-icon="action.leadIcon"
:trail-icon="action.trailIcon"
:icon-mode="action.iconMode"
:label="action.label"
:label-mode="action.labelMode"
:href="action.href"
:loading="action.loading"
:disabled="action.disabled"
:tooltip="action.tooltip"
@click="() => { action.onClick?.() }"
/>
</div>
</div>
</template>

<style scoped lang="postcss">
.SLocalNavActions {
display: flex;
flex-shrink: 0;
gap: 8px;
}
</style>
2 changes: 2 additions & 0 deletions lib/components/SLocalNavTitle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ defineProps<{
<style scoped lang="postcss">
.SLocalNavTitle {
display: flex;
flex-grow: 1;
width: 100%;
overflow: hidden;
}

Expand Down
12 changes: 9 additions & 3 deletions stories/components/SLocalNav.01_Playground.story.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import IconActivity from '~icons/ph/activity-bold'
import IconCurrencyCircleDollar from '~icons/ph/currency-circle-dollar-bold'
import IconGear from '~icons/ph/gear-bold'
import IconActivity from '~icons/ph/activity'
import IconCurrencyCircleDollar from '~icons/ph/currency-circle-dollar'
import IconGear from '~icons/ph/gear'
import SLocalNav from 'sefirot/components/SLocalNav.vue'

const title = 'Components / SLocalNav / 01. Playground'
Expand All @@ -12,6 +12,11 @@ const navTitle = [
{ text: 'Series A' }
]

const navActions = [
{ label: 'Edit title', onClick: () => {} },
{ label: 'About', onClick: () => {} }
]

const navMenu = [
[
{ icon: IconActivity, text: 'Overview', link: '#', active: true },
Expand All @@ -28,6 +33,7 @@ const navMenu = [
<Board :title="title" :docs="docs">
<SLocalNav
:title="navTitle"
:actions="navActions"
:menu="navMenu"
/>
</Board>
Expand Down
Loading