feat: add menubar

This commit is contained in:
Ahmed 2023-09-06 14:42:40 +01:00
parent f0fface575
commit 0768fa5bdd
16 changed files with 353 additions and 0 deletions

View File

@ -0,0 +1,27 @@
<script setup lang="ts">
import {
MenubarRoot,
type MenubarRootEmits,
type MenubarRootProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarRootProps & { class?: string }>()
const emits = defineEmits<MenubarRootEmits>()
</script>
<template>
<MenubarRoot
v-bind="props"
:class="
cn(
'flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm',
props.class,
)
"
@update:model-value="emits('update:modelValue', $event)"
>
<slot />
</MenubarRoot>
</template>

View File

@ -0,0 +1,35 @@
<script setup lang="ts">
import {
MenubarCheckboxItem,
type MenubarCheckboxItemEmits,
type MenubarCheckboxItemProps,
MenubarItemIndicator,
} from 'radix-vue'
import { cn } from '@/lib/utils'
import RadixIconsCheck from '~icons/radix-icons/check'
const props = defineProps<MenubarCheckboxItemProps & { class?: string }>()
const emit = defineEmits<MenubarCheckboxItemEmits>()
</script>
<template>
<MenubarCheckboxItem
v-bind="props"
:class="[
cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class,
),
]"
@update:checked="emit('update:checked', $event)"
@select="emit('select', $event)"
>
<MenubarItemIndicator
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"
>
<RadixIconsCheck class="w-4 h-4" />
</MenubarItemIndicator>
<slot />
</MenubarCheckboxItem>
</template>

View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import {
MenubarContent,
type MenubarContentProps,
MenubarPortal,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = withDefaults(
defineProps<MenubarContentProps & { class?: string }>(),
{
align: 'start',
alignOffset: -4,
sideOffset: 8,
},
)
</script>
<template>
<MenubarPortal>
<MenubarContent
:loop="props.loop"
:as-child="props.asChild"
:side-offset="props.sideOffset"
:side="props.side"
:align="props.align"
:align-offset="props.alignOffset"
:class="
cn(
'z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</MenubarContent>
</MenubarPortal>
</template>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import { MenubarGroup, type MenubarGroupProps } from 'radix-vue'
const props = defineProps<MenubarGroupProps>()
</script>
<template>
<MenubarGroup v-bind="props">
<slot />
</MenubarGroup>
</template>

View File

@ -0,0 +1,28 @@
<script setup lang="ts">
import {
MenubarItem,
type MenubarItemEmits,
type MenubarItemProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarItemProps & { inset?: boolean; class?: string }>()
const emits = defineEmits<MenubarItemEmits>()
</script>
<template>
<MenubarItem
v-bind="props"
:class="[
cn(
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
inset && 'pl-8',
props.class,
),
]"
@select="emits('select', $event)"
>
<slot />
</MenubarItem>
</template>

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
import { MenubarLabel, type MenubarLabelProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarLabelProps & { inset?: boolean; class?: string }>()
</script>
<template>
<MenubarLabel :class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)">
<slot />
</MenubarLabel>
</template>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
import { MenubarMenu, type MenubarMenuProps } from 'radix-vue'
const props = defineProps<MenubarMenuProps>()
</script>
<template>
<MenubarMenu v-bind="props">
<slot />
</MenubarMenu>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import {
MenubarRadioGroup,
type MenubarRadioGroupEmits,
type MenubarRadioGroupProps,
} from 'radix-vue'
const props = defineProps<MenubarRadioGroupProps>()
const emits = defineEmits<MenubarRadioGroupEmits>()
</script>
<template>
<MenubarRadioGroup
v-bind="props"
@update:model-value="emits('update:modelValue', $event)"
>
<slot />
</MenubarRadioGroup>
</template>

View File

@ -0,0 +1,35 @@
<script setup lang="ts">
import {
MenubarItemIndicator,
MenubarRadioItem,
type MenubarRadioItemEmits,
type MenubarRadioItemProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
import RadixIconsDotFilled from '~icons/radix-icons/dot-filled'
const props = defineProps<MenubarRadioItemProps & { class?: string }>()
const emits = defineEmits<MenubarRadioItemEmits>()
</script>
<template>
<MenubarRadioItem
v-bind="props"
:class="[
cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class,
),
]"
@select="emits('select', $event)"
>
<MenubarItemIndicator
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"
>
<RadixIconsDotFilled class="h-4 w-4 fill-current" />
</MenubarItemIndicator>
<slot />
</MenubarRadioItem>
</template>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
import { MenubarSeparator, type MenubarSeparatorProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarSeparatorProps>()
</script>
<template>
<MenubarSeparator :class=" cn('-mx-1 my-1 h-px bg-secondary', $attrs.class ?? '')" v-bind="props" />
</template>

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
</script>
<template>
<span :class="cn('text-xs ml-auto tracking-widest opacity-50', $attrs.class ?? '')">
<slot />
</span>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import {
MenubarSub,
type MenubarSubEmits,
} from 'radix-vue'
interface MenubarSubRootProps {
defaultOpen?: boolean
open?: boolean
}
const props = defineProps<MenubarSubRootProps>()
const emits = defineEmits<MenubarSubEmits>()
</script>
<template>
<MenubarSub v-bind="props" @update:open="emits('update:open', $event)">
<slot />
</MenubarSub>
</template>

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import {
MenubarPortal,
MenubarSubContent,
type MenubarSubContentEmits,
type MenubarSubContentProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = withDefaults(
defineProps<MenubarSubContentProps & { class?: string }>(),
{
sideOffset: 2,
alignOffset: 0,
},
)
const emits = defineEmits<MenubarSubContentEmits>()
</script>
<template>
<MenubarPortal>
<MenubarSubContent
:loop="props.loop"
:as-child="props.asChild"
:side-offset="props.sideOffset"
:side="props.side"
:align="props.align"
:align-offset="props.alignOffset"
:class="
cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</MenubarSubContent>
</MenubarPortal>
</template>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
import { MenubarSubTrigger, type MenubarSubTriggerProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarSubTriggerProps & { inset?: boolean; class?: string }>()
</script>
<template>
<MenubarSubTrigger
v-bind="props"
:class="[
cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
inset && 'pl-8',
props.class,
),
]"
>
<slot />
</MenubarSubTrigger>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import { MenubarTrigger, type MenubarTriggerProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<MenubarTriggerProps & { class?: string }>()
</script>
<template>
<MenubarTrigger
v-bind="props"
:class="
cn(
'flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
props.class,
)
"
>
<slot />
</MenubarTrigger>
</template>

View File

@ -0,0 +1,15 @@
export { default as Menubar } from './Menubar.vue'
export { default as MenubarItem } from './MenubarItem.vue'
export { default as MenubarContent } from './MenubarContent.vue'
export { default as MenubarGroup } from './MenubarGroup.vue'
export { default as MenubarMenu } from './MenubarMenu.vue'
export { default as MenubarRadioGroup } from './MenubarRadioGroup.vue'
export { default as MenubarRadioItem } from './MenubarRadioItem.vue'
export { default as MenubarCheckboxItem } from './MenubarCheckboxItem.vue'
export { default as MenubarSeparator } from './MenubarSeparator.vue'
export { default as MenubarSub } from './MenubarSub.vue'
export { default as MenubarSubContent } from './MenubarSubContent.vue'
export { default as MenubarSubTrigger } from './MenubarSubTrigger.vue'
export { default as MenubarTrigger } from './MenubarTrigger.vue'
export { default as MenubarShortcut } from './MenubarShortcut.vue'
export { default as MenubarLabel } from './MenubarLabel.vue'