aboutsummaryrefslogtreecommitdiffstats
path: root/code/frontend/src/components
diff options
context:
space:
mode:
authorivar <i@oiee.no>2024-04-28 22:37:30 +0200
committerivar <i@oiee.no>2024-04-28 22:37:30 +0200
commitced66c5807575cd29f6aa5632e8ad02b38c8448a (patch)
tree01760648ee293a2aef2288328014b5747d2192b4 /code/frontend/src/components
parent691ad60d7bff5934053d87267c4e303ef3ed5f97 (diff)
downloadgreatoffice-ced66c5807575cd29f6aa5632e8ad02b38c8448a.tar.xz
greatoffice-ced66c5807575cd29f6aa5632e8ad02b38c8448a.zip
WIP new frontend
Diffstat (limited to 'code/frontend/src/components')
-rw-r--r--code/frontend/src/components/locale-switcher.svelte56
-rw-r--r--code/frontend/src/components/sonner.svelte21
-rw-r--r--code/frontend/src/components/style-changer.svelte22
-rw-r--r--code/frontend/src/components/ui/button/button.svelte25
-rw-r--r--code/frontend/src/components/ui/button/index.ts48
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte35
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-content.svelte26
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-item.svelte31
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-label.svelte19
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte11
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte35
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte11
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte13
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte29
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte32
-rw-r--r--code/frontend/src/components/ui/dropdown-menu/index.ts48
16 files changed, 462 insertions, 0 deletions
diff --git a/code/frontend/src/components/locale-switcher.svelte b/code/frontend/src/components/locale-switcher.svelte
new file mode 100644
index 0000000..fc03f39
--- /dev/null
+++ b/code/frontend/src/components/locale-switcher.svelte
@@ -0,0 +1,56 @@
+<script lang="ts">
+ import pwKey from "$actions/pwKey";
+ import {browser} from "$app/environment";
+ import {page} from "$app/stores";
+ import {CookieNames} from "$configuration";
+ import {setLocale, locale} from "$i18n/i18n-svelte";
+ import type {Locales} from "$i18n/i18n-types";
+ import {locales} from "$i18n/i18n-util";
+ import {loadLocaleAsync} from "$i18n/i18n-util.async";
+ import Cookies from "js-cookie";
+
+ export let _pwKey: string | undefined = undefined;
+ export let tabindex: number | undefined = undefined;
+ let currentLocale = Cookies.get(CookieNames.locale);
+
+ async function switch_locale(newLocale: Locales) {
+ if (!newLocale || $locale === newLocale) return;
+ await loadLocaleAsync(newLocale);
+ setLocale(newLocale);
+ document.querySelector("html")?.setAttribute("lang", newLocale);
+ Cookies.set(CookieNames.locale, newLocale);
+ currentLocale = newLocale;
+ console.log("Switched to: " + newLocale);
+ }
+
+ function on_change(event: Event) {
+ const target = event.target as HTMLSelectElement;
+ switch_locale(target.options[target.selectedIndex].value as Locales);
+ }
+
+ $: if (browser) {
+ switch_locale($page.params.lang as Locales);
+ }
+
+ function get_locale_name(iso: string) {
+ switch (iso) {
+ case "nb": {
+ return "Norsk Bokmål";
+ }
+ case "en": {
+ return "English";
+ }
+ }
+ }
+</script>
+
+<select
+ {tabindex}
+ use:pwKey={_pwKey}
+ on:change={on_change}
+ class="mt-1 mr-1 block border-none py-2 pl-3 pr-10 text-base rounded-md right-0 absolute focus:outline-none focus:ring-teal-500 sm:text-sm"
+>
+ {#each locales as aLocale}
+ <option value={aLocale} selected={aLocale === currentLocale}>{get_locale_name(aLocale)}</option>
+ {/each}
+</select>
diff --git a/code/frontend/src/components/sonner.svelte b/code/frontend/src/components/sonner.svelte
new file mode 100644
index 0000000..422e189
--- /dev/null
+++ b/code/frontend/src/components/sonner.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+ import { Toaster as Sonner, type ToasterProps as SonnerProps } from 'svelte-sonner'
+ import { mode } from 'mode-watcher'
+
+ type $$Props = SonnerProps
+</script>
+
+<Sonner
+ theme={$mode}
+ class="toaster group"
+ toastOptions={{
+ classes: {
+ toast:
+ 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
+ description: 'group-[.toast]:text-muted-foreground',
+ actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
+ cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
+ }
+ }}
+ {...$$restProps}
+/>
diff --git a/code/frontend/src/components/style-changer.svelte b/code/frontend/src/components/style-changer.svelte
new file mode 100644
index 0000000..b219b10
--- /dev/null
+++ b/code/frontend/src/components/style-changer.svelte
@@ -0,0 +1,22 @@
+<script lang="ts">
+ import Sun from 'svelte-radix/Sun.svelte'
+ import Moon from 'svelte-radix/Moon.svelte'
+ import { resetMode, setMode } from 'mode-watcher'
+ import { Button } from '$components/ui/button'
+ import * as DropdownMenu from '$components/ui/dropdown-menu'
+</script>
+
+<DropdownMenu.Root>
+ <DropdownMenu.Trigger asChild let:builder>
+ <Button builders={[builder]} variant="outline" size="icon">
+ <Sun class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
+ <Moon class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
+ <span class="sr-only">Toggle theme</span>
+ </Button>
+ </DropdownMenu.Trigger>
+ <DropdownMenu.Content align="end">
+ <DropdownMenu.Item on:click={() => setMode('light')}>Light</DropdownMenu.Item>
+ <DropdownMenu.Item on:click={() => setMode('dark')}>Dark</DropdownMenu.Item>
+ <DropdownMenu.Item on:click={() => resetMode()}>System</DropdownMenu.Item>
+ </DropdownMenu.Content>
+</DropdownMenu.Root>
diff --git a/code/frontend/src/components/ui/button/button.svelte b/code/frontend/src/components/ui/button/button.svelte
new file mode 100644
index 0000000..196ae77
--- /dev/null
+++ b/code/frontend/src/components/ui/button/button.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+ import { Button as ButtonPrimitive } from 'bits-ui'
+ import { type Events, type Props, buttonVariants } from './index.js'
+ import { cn } from '$utils/ui.js'
+
+ type $$Props = Props
+ type $$Events = Events
+
+ let className: $$Props['class'] = undefined
+ export let variant: $$Props['variant'] = 'default'
+ export let size: $$Props['size'] = 'default'
+ export let builders: $$Props['builders'] = []
+ export { className as class }
+</script>
+
+<ButtonPrimitive.Root
+ {builders}
+ class={cn(buttonVariants({ variant, size, className }))}
+ type="button"
+ {...$$restProps}
+ on:click
+ on:keydown
+>
+ <slot />
+</ButtonPrimitive.Root>
diff --git a/code/frontend/src/components/ui/button/index.ts b/code/frontend/src/components/ui/button/index.ts
new file mode 100644
index 0000000..9cfd91c
--- /dev/null
+++ b/code/frontend/src/components/ui/button/index.ts
@@ -0,0 +1,48 @@
+import type { Button as ButtonPrimitive } from 'bits-ui'
+import { type VariantProps, tv } from 'tailwind-variants'
+import Root from './button.svelte'
+
+const buttonVariants = tv({
+ base: 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
+ destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
+ outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
+ secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
+ link: 'text-primary underline-offset-4 hover:underline'
+ },
+ size: {
+ default: 'h-9 px-4 py-2',
+ sm: 'h-8 rounded-md px-3 text-xs',
+ lg: 'h-10 rounded-md px-8',
+ icon: 'h-9 w-9'
+ }
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default'
+ }
+})
+
+type Variant = VariantProps<typeof buttonVariants>['variant']
+type Size = VariantProps<typeof buttonVariants>['size']
+
+type Props = ButtonPrimitive.Props & {
+ variant?: Variant
+ size?: Size
+}
+
+type Events = ButtonPrimitive.Events
+
+export {
+ Root,
+ type Props,
+ type Events,
+ //
+ Root as Button,
+ type Props as ButtonProps,
+ type Events as ButtonEvents,
+ buttonVariants
+}
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
new file mode 100644
index 0000000..ea02af0
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
@@ -0,0 +1,35 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import Check from 'svelte-radix/Check.svelte'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.CheckboxItemProps
+ type $$Events = DropdownMenuPrimitive.CheckboxItemEvents
+
+ let className: $$Props['class'] = undefined
+ export let checked: $$Props['checked'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.CheckboxItem
+ bind:checked
+ class={cn(
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
+ className
+ )}
+ {...$$restProps}
+ on:click
+ on:keydown
+ on:focusin
+ on:focusout
+ on:pointerdown
+ on:pointerleave
+ on:pointermove
+>
+ <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
+ <DropdownMenuPrimitive.CheckboxIndicator>
+ <Check class="h-4 w-4" />
+ </DropdownMenuPrimitive.CheckboxIndicator>
+ </span>
+ <slot />
+</DropdownMenuPrimitive.CheckboxItem>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-content.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-content.svelte
new file mode 100644
index 0000000..a2b8da7
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-content.svelte
@@ -0,0 +1,26 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import { cn, flyAndScale } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.ContentProps
+
+ let className: $$Props['class'] = undefined
+ export let sideOffset: $$Props['sideOffset'] = 4
+ export let transition: $$Props['transition'] = flyAndScale
+ export let transitionConfig: $$Props['transitionConfig'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.Content
+ {transition}
+ {transitionConfig}
+ {sideOffset}
+ class={cn(
+ 'z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none',
+ className
+ )}
+ {...$$restProps}
+ on:keydown
+>
+ <slot />
+</DropdownMenuPrimitive.Content>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-item.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-item.svelte
new file mode 100644
index 0000000..ed45da7
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-item.svelte
@@ -0,0 +1,31 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.ItemProps & {
+ inset?: boolean
+ }
+ type $$Events = DropdownMenuPrimitive.ItemEvents
+
+ let className: $$Props['class'] = undefined
+ export let inset: $$Props['inset'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.Item
+ class={cn(
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
+ inset && 'pl-8',
+ className
+ )}
+ on:click
+ on:keydown
+ on:focusin
+ on:focusout
+ on:pointerdown
+ on:pointerleave
+ on:pointermove
+ {...$$restProps}
+>
+ <slot />
+</DropdownMenuPrimitive.Item>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-label.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-label.svelte
new file mode 100644
index 0000000..69fddd1
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-label.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.LabelProps & {
+ inset?: boolean
+ }
+
+ let className: $$Props['class'] = undefined
+ export let inset: $$Props['inset'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.Label
+ class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
+ {...$$restProps}
+>
+ <slot />
+</DropdownMenuPrimitive.Label>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
new file mode 100644
index 0000000..c07bd1a
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+
+ type $$Props = DropdownMenuPrimitive.RadioGroupProps
+
+ export let value: $$Props['value'] = undefined
+</script>
+
+<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
+ <slot />
+</DropdownMenuPrimitive.RadioGroup>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
new file mode 100644
index 0000000..c754953
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
@@ -0,0 +1,35 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import DotFilled from 'svelte-radix/DotFilled.svelte'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.RadioItemProps
+ type $$Events = DropdownMenuPrimitive.RadioItemEvents
+
+ let className: $$Props['class'] = undefined
+ export let value: DropdownMenuPrimitive.RadioItemProps['value']
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.RadioItem
+ class={cn(
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
+ className
+ )}
+ {value}
+ {...$$restProps}
+ on:click
+ on:keydown
+ on:focusin
+ on:focusout
+ on:pointerdown
+ on:pointerleave
+ on:pointermove
+>
+ <span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
+ <DropdownMenuPrimitive.RadioIndicator>
+ <DotFilled class="h-4 w-4 fill-current" />
+ </DropdownMenuPrimitive.RadioIndicator>
+ </span>
+ <slot />
+</DropdownMenuPrimitive.RadioItem>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte
new file mode 100644
index 0000000..b6c5798
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.SeparatorProps
+
+ let className: $$Props['class'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.Separator class={cn('-mx-1 my-1 h-px bg-muted', className)} {...$$restProps} />
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
new file mode 100644
index 0000000..f9e5953
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
@@ -0,0 +1,13 @@
+<script lang="ts">
+ import type { HTMLAttributes } from 'svelte/elements'
+ import { cn } from '$utils/ui'
+
+ type $$Props = HTMLAttributes<HTMLSpanElement>
+
+ let className: $$Props['class'] = undefined
+ export { className as class }
+</script>
+
+<span class={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...$$restProps}>
+ <slot />
+</span>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
new file mode 100644
index 0000000..7c00a1b
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
@@ -0,0 +1,29 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import { cn, flyAndScale } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.SubContentProps
+
+ let className: $$Props['class'] = undefined
+ export let transition: $$Props['transition'] = flyAndScale
+ export let transitionConfig: $$Props['transitionConfig'] = {
+ x: -10,
+ y: 0
+ }
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.SubContent
+ {transition}
+ {transitionConfig}
+ class={cn(
+ 'z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none',
+ className
+ )}
+ {...$$restProps}
+ on:keydown
+ on:focusout
+ on:pointermove
+>
+ <slot />
+</DropdownMenuPrimitive.SubContent>
diff --git a/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
new file mode 100644
index 0000000..4967d2b
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
@@ -0,0 +1,32 @@
+<script lang="ts">
+ import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+ import ChevronRight from 'svelte-radix/ChevronRight.svelte'
+ import { cn } from '$utils/ui'
+
+ type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
+ inset?: boolean
+ }
+ type $$Events = DropdownMenuPrimitive.SubTriggerEvents
+
+ let className: $$Props['class'] = undefined
+ export let inset: $$Props['inset'] = undefined
+ export { className as class }
+</script>
+
+<DropdownMenuPrimitive.SubTrigger
+ class={cn(
+ 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground',
+ inset && 'pl-8',
+ className
+ )}
+ {...$$restProps}
+ on:click
+ on:keydown
+ on:focusin
+ on:focusout
+ on:pointerleave
+ on:pointermove
+>
+ <slot />
+ <ChevronRight class="ml-auto h-4 w-4" />
+</DropdownMenuPrimitive.SubTrigger>
diff --git a/code/frontend/src/components/ui/dropdown-menu/index.ts b/code/frontend/src/components/ui/dropdown-menu/index.ts
new file mode 100644
index 0000000..df959fa
--- /dev/null
+++ b/code/frontend/src/components/ui/dropdown-menu/index.ts
@@ -0,0 +1,48 @@
+import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
+import Item from './dropdown-menu-item.svelte'
+import Label from './dropdown-menu-label.svelte'
+import Content from './dropdown-menu-content.svelte'
+import Shortcut from './dropdown-menu-shortcut.svelte'
+import RadioItem from './dropdown-menu-radio-item.svelte'
+import Separator from './dropdown-menu-separator.svelte'
+import RadioGroup from './dropdown-menu-radio-group.svelte'
+import SubContent from './dropdown-menu-sub-content.svelte'
+import SubTrigger from './dropdown-menu-sub-trigger.svelte'
+import CheckboxItem from './dropdown-menu-checkbox-item.svelte'
+
+const Sub = DropdownMenuPrimitive.Sub
+const Root = DropdownMenuPrimitive.Root
+const Trigger = DropdownMenuPrimitive.Trigger
+const Group = DropdownMenuPrimitive.Group
+
+export {
+ Sub,
+ Root,
+ Item,
+ Label,
+ Group,
+ Trigger,
+ Content,
+ Shortcut,
+ Separator,
+ RadioItem,
+ SubContent,
+ SubTrigger,
+ RadioGroup,
+ CheckboxItem,
+ //
+ Root as DropdownMenu,
+ Sub as DropdownMenuSub,
+ Item as DropdownMenuItem,
+ Label as DropdownMenuLabel,
+ Group as DropdownMenuGroup,
+ Content as DropdownMenuContent,
+ Trigger as DropdownMenuTrigger,
+ Shortcut as DropdownMenuShortcut,
+ RadioItem as DropdownMenuRadioItem,
+ Separator as DropdownMenuSeparator,
+ RadioGroup as DropdownMenuRadioGroup,
+ SubContent as DropdownMenuSubContent,
+ SubTrigger as DropdownMenuSubTrigger,
+ CheckboxItem as DropdownMenuCheckboxItem
+}