@byyuurin/ui 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +3 -6
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +3 -2
  4. package/dist/runtime/components/ContextMenu.vue +65 -0
  5. package/dist/runtime/components/ContextMenu.vue.d.ts +138 -0
  6. package/dist/runtime/components/ContextMenuContent.vue +215 -0
  7. package/dist/runtime/components/ContextMenuContent.vue.d.ts +40 -0
  8. package/dist/runtime/components/DropdownMenu.vue.d.ts +2 -2
  9. package/dist/runtime/components/DropdownMenuContent.vue +3 -5
  10. package/dist/runtime/components/DropdownMenuContent.vue.d.ts +2 -7
  11. package/dist/runtime/components/NavigationMenu.vue +1 -1
  12. package/dist/runtime/components/NavigationMenu.vue.d.ts +4 -4
  13. package/dist/runtime/components/Stepper.vue +13 -11
  14. package/dist/runtime/components/Stepper.vue.d.ts +6 -1
  15. package/dist/runtime/components/Timeline.vue +17 -15
  16. package/dist/runtime/components/Timeline.vue.d.ts +5 -2
  17. package/dist/runtime/types/index.d.ts +1 -0
  18. package/dist/runtime/types/index.js +1 -0
  19. package/dist/runtime/utils/style.d.ts +10 -10
  20. package/dist/runtime/vue/overrides/none/Link.vue +119 -0
  21. package/dist/runtime/vue/overrides/none/Link.vue.d.ts +79 -0
  22. package/dist/runtime/vue/{components → overrides/vue-router}/Link.vue +43 -43
  23. package/dist/runtime/vue/{components → overrides/vue-router}/Link.vue.d.ts +9 -9
  24. package/dist/runtime/vue/{stubs.d.ts → stubs/base.d.ts} +1 -2
  25. package/dist/runtime/vue/{stubs.js → stubs/base.js} +1 -2
  26. package/dist/runtime/vue/stubs/none.d.ts +56 -0
  27. package/dist/runtime/vue/stubs/none.js +48 -0
  28. package/dist/runtime/vue/stubs/vue-router.d.ts +2 -0
  29. package/dist/runtime/vue/stubs/vue-router.js +2 -0
  30. package/dist/setup.d.mts +1 -1
  31. package/dist/shared/{ui.CGCKYv7g.d.mts → ui.Cec0yP1b.d.mts} +11 -2
  32. package/dist/shared/{ui.DcEKQd0n.mjs → ui.RIHx5Yhe.mjs} +145 -29
  33. package/dist/unplugin.d.mts +1 -1
  34. package/dist/unplugin.mjs +66 -23
  35. package/dist/vite.d.mts +1 -1
  36. package/dist/vite.mjs +3 -2
  37. package/package.json +19 -15
package/README.md CHANGED
@@ -13,20 +13,17 @@ https://byyuurin-ui.netlify.app/
13
13
  ## Installation
14
14
 
15
15
  ```bash [pnpm]
16
- pnpm add @byyuurin/ui
16
+ pnpm add @byyuurin/ui unocss
17
17
  ```
18
18
 
19
19
  ```bash [yarn]
20
- yarn add @byyuurin/ui
20
+ yarn add @byyuurin/ui unocss
21
21
  ```
22
22
 
23
23
  ```bash [npm]
24
- npm install @byyuurin/ui
24
+ npm install @byyuurin/ui unocss
25
25
  ```
26
26
 
27
- > [!WARNING]
28
- > If you're using pnpm, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/settings#shamefully-hoist) in your `.npmrc` file or install `@byyuurin/ui-kit` in your project's root directory.
29
-
30
27
  ### Nuxt
31
28
 
32
29
  1. Add the UI module in your `nuxt.config.ts`:
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byyuurin/ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "configKey": "ui",
5
5
  "compatibility": {
6
6
  "nuxt": ">=3.16.2"
package/dist/module.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import { defineNuxtModule, createResolver, addPlugin, addComponentsDir, addImportsDir, hasNuxtModule, installModule } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
- import { v as version, n as name, a as addTemplates } from './shared/ui.DcEKQd0n.mjs';
3
+ import { v as version, n as name, a as addTemplates } from './shared/ui.RIHx5Yhe.mjs';
4
4
  import { d as defaultOptions, r as resolveColors, g as getDefaultUIConfig } from './shared/ui.DYMXCXO6.mjs';
5
- import 'node:process';
5
+ import 'node:module';
6
6
  import 'node:url';
7
7
  import '@unocss/config';
8
8
  import 'knitwork';
9
+ import 'pathe';
9
10
  import 'scule';
10
11
  import '@byyuurin/ui-kit';
11
12
  import '../dist/runtime/utils/index.js';
@@ -0,0 +1,65 @@
1
+ <script>
2
+ import theme from "#build/ui/context-menu";
3
+ </script>
4
+
5
+ <script setup>
6
+ import { reactivePick } from "@vueuse/core";
7
+ import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from "reka-ui";
8
+ import { computed, toRef } from "vue";
9
+ import { useAppConfig } from "#imports";
10
+ import { omit, pick } from "../utils";
11
+ import { cv, merge } from "../utils/style";
12
+ import ContextMenuContent from "./ContextMenuContent.vue";
13
+ const props = defineProps({
14
+ size: { type: null, required: false },
15
+ items: { type: null, required: false },
16
+ checkedIcon: { type: [String, Object], required: false },
17
+ loadingIcon: { type: [String, Object], required: false },
18
+ externalIcon: { type: [Boolean, String, Object], required: false, default: true },
19
+ content: { type: Object, required: false },
20
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: true },
21
+ labelKey: { type: null, required: false, default: "label" },
22
+ descriptionKey: { type: null, required: false, default: "description" },
23
+ disabled: { type: Boolean, required: false },
24
+ ui: { type: null, required: false },
25
+ class: { type: [Object, String, Number, Boolean, null, Array], required: false, skipCheck: true },
26
+ pressOpenDelay: { type: Number, required: false },
27
+ modal: { type: Boolean, required: false, default: true }
28
+ });
29
+ const emit = defineEmits(["update:open"]);
30
+ const slots = defineSlots();
31
+ const appConfig = useAppConfig();
32
+ const rootProps = useForwardPropsEmits(reactivePick(props, "modal"), emit);
33
+ const contentProps = toRef(() => props.content);
34
+ const getProxySlots = () => omit(slots, ["default"]);
35
+ const ui = computed(() => {
36
+ const styler = cv(merge(theme, appConfig.ui.contextMenu));
37
+ return styler(pick(props, ["size"]));
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <ContextMenuRoot v-bind="rootProps">
43
+ <ContextMenuTrigger v-if="!!slots.default" as-child :disabled="props.disabled" :class="props.class">
44
+ <slot></slot>
45
+ </ContextMenuTrigger>
46
+
47
+ <ContextMenuContent
48
+ v-bind="contentProps"
49
+ :items="props.items"
50
+ :portal="props.portal"
51
+ :label-key="props.labelKey"
52
+ :description-key="props.descriptionKey"
53
+ :checked-icon="props.checkedIcon"
54
+ :loading-icon="props.loadingIcon"
55
+ :external-icon="props.externalIcon"
56
+ :ui="ui"
57
+ :ui-override="props.ui"
58
+ :class="ui.content({ class: [props.ui?.content, !slots.default && props.class] })"
59
+ >
60
+ <template v-for="(_, name) in getProxySlots()" #[name]="slotProps">
61
+ <slot :name="name" v-bind="slotProps"></slot>
62
+ </template>
63
+ </ContextMenuContent>
64
+ </ContextMenuRoot>
65
+ </template>
@@ -0,0 +1,138 @@
1
+ import type { VariantProps } from '@byyuurin/ui-kit';
2
+ import type { ContextMenuContentEmits, ContextMenuContentProps, ContextMenuRootEmits, ContextMenuRootProps } from 'reka-ui';
3
+ import theme from '#build/ui/context-menu';
4
+ import type { AvatarProps, ComponentBaseProps, ComponentStyler, ComponentUIProps, IconProps, KbdProps, LinkProps } from '../types';
5
+ import type { ArrayOrNested, DynamicSlots, EmitsToProps, GetItemKeys, MergeTypes, NestedItem, StaticSlot } from '../types/utils';
6
+ type ThemeVariants = VariantProps<typeof theme>;
7
+ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'>, ComponentBaseProps {
8
+ label?: string;
9
+ description?: string;
10
+ icon?: IconProps['name'];
11
+ color?: ThemeVariants['color'];
12
+ avatar?: AvatarProps;
13
+ content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> & ComponentBaseProps & Partial<EmitsToProps<ContextMenuContentEmits>>;
14
+ kbds?: KbdProps['value'][] | KbdProps[];
15
+ /**
16
+ * The item type.
17
+ * @default "link"
18
+ */
19
+ type?: 'label' | 'separator' | 'link' | 'checkbox';
20
+ slot?: string;
21
+ loading?: boolean;
22
+ disabled?: boolean;
23
+ checked?: boolean;
24
+ open?: boolean;
25
+ defaultOpen?: boolean;
26
+ children?: ArrayOrNested<ContextMenuItem>;
27
+ onSelect?: (e: Event) => void;
28
+ onUpdateChecked?: (checked: boolean) => void;
29
+ ui?: Pick<ComponentUIProps<typeof theme>, 'content' | 'item' | 'label' | 'separator' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemLabelExternalIcon' | 'itemTrailing' | 'itemTrailingIcon' | 'itemTrailingKbds' | 'itemTrailingKbdsSize'>;
30
+ [key: string]: any;
31
+ }
32
+ export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>> extends ComponentBaseProps, Omit<ContextMenuRootProps, 'dir'> {
33
+ /** @default "md" */
34
+ size?: ThemeVariants['size'];
35
+ items?: T;
36
+ /**
37
+ * The icon displayed when an item is checked.
38
+ * @default app.icons.check
39
+ */
40
+ checkedIcon?: IconProps['name'];
41
+ /**
42
+ * The icon displayed when an item is loading.
43
+ * @default app.icons.loading
44
+ */
45
+ loadingIcon?: IconProps['name'];
46
+ /**
47
+ * The icon displayed when the item is an external link.
48
+ *
49
+ * Set to `false` to hide the external icon.
50
+ * @default app.icons.external
51
+ */
52
+ externalIcon?: boolean | IconProps['name'];
53
+ /** The content of the menu. */
54
+ content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ContextMenuContentEmits>>;
55
+ /**
56
+ * Render the menu in a portal.
57
+ * @default true
58
+ */
59
+ portal?: boolean | string | HTMLElement;
60
+ /**
61
+ * The key used to get the label from the item.
62
+ * @default "label"
63
+ */
64
+ labelKey?: GetItemKeys<T>;
65
+ /**
66
+ * The key used to get the description from the item.
67
+ * @default "description"
68
+ */
69
+ descriptionKey?: GetItemKeys<T>;
70
+ disabled?: boolean;
71
+ ui?: ComponentUIProps<typeof theme>;
72
+ }
73
+ export interface ContextMenuEmits extends ContextMenuRootEmits {
74
+ }
75
+ export type ContextMenuSlots<A extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>, T extends NestedItem<A> = NestedItem<A>> = {
76
+ 'default': StaticSlot;
77
+ 'item': StaticSlot<{
78
+ item: T;
79
+ active?: boolean;
80
+ index: number;
81
+ ui: ComponentStyler<typeof theme>;
82
+ }>;
83
+ 'item-leading': StaticSlot<{
84
+ item: T;
85
+ active?: boolean;
86
+ index: number;
87
+ ui: ComponentStyler<typeof theme>;
88
+ }>;
89
+ 'item-label': StaticSlot<{
90
+ item: T;
91
+ active?: boolean;
92
+ index: number;
93
+ }>;
94
+ 'item-description': StaticSlot<{
95
+ item: T;
96
+ active?: boolean;
97
+ index: number;
98
+ }>;
99
+ 'item-trailing': StaticSlot<{
100
+ item: T;
101
+ active?: boolean;
102
+ index: number;
103
+ ui: ComponentStyler<typeof theme>;
104
+ }>;
105
+ 'content-top': StaticSlot<{
106
+ sub: boolean;
107
+ }>;
108
+ 'content-bottom': StaticSlot<{
109
+ sub: boolean;
110
+ }>;
111
+ } & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
112
+ active?: boolean;
113
+ index: number;
114
+ }> & DynamicSlots<MergeTypes<T>, 'leading' | 'trailing', {
115
+ active?: boolean;
116
+ index: number;
117
+ ui: ComponentStyler<typeof theme>;
118
+ }>;
119
+ declare const _default: typeof __VLS_export;
120
+ export default _default;
121
+ declare const __VLS_export: <T extends ArrayOrNested<ContextMenuItem>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
122
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<ContextMenuProps<T> & {
123
+ "onUpdate:open"?: ((payload: boolean) => any) | undefined;
124
+ }> & (typeof globalThis extends {
125
+ __VLS_PROPS_FALLBACK: infer P;
126
+ } ? P : {});
127
+ expose: (exposed: {}) => void;
128
+ attrs: any;
129
+ slots: ContextMenuSlots<T, NestedItem<T>>;
130
+ emit: (evt: "update:open", payload: boolean) => void;
131
+ }>) => import("vue").VNode & {
132
+ __ctx?: Awaited<typeof __VLS_setup>;
133
+ };
134
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
135
+ [K in keyof T]: T[K];
136
+ } : {
137
+ [K in keyof T as K]: T[K];
138
+ }) & {};
@@ -0,0 +1,215 @@
1
+ <script>
2
+
3
+ </script>
4
+
5
+ <script setup>
6
+ import { createReusableTemplate, reactiveOmit } from "@vueuse/core";
7
+ import { useForwardPropsEmits } from "reka-ui";
8
+ import { ContextMenu } from "reka-ui/namespaced";
9
+ import { computed, toRef } from "vue";
10
+ import { useAppConfig } from "#imports";
11
+ import { useLocale } from "../composables/useLocale";
12
+ import { usePortal } from "../composables/usePortal";
13
+ import { get, isArrayOfArray, omit } from "../utils";
14
+ import { pickLinkProps } from "../utils/link";
15
+ import Avatar from "./Avatar.vue";
16
+ import ContextMenuContent from "./ContextMenuContent.vue";
17
+ import Icon from "./Icon.vue";
18
+ import Kbd from "./Kbd.vue";
19
+ import Link from "./Link.vue";
20
+ import LinkBase from "./LinkBase.vue";
21
+ defineOptions({ inheritAttrs: false });
22
+ const props = defineProps({
23
+ items: { type: null, required: false },
24
+ portal: { type: [Boolean, String], required: false, skipCheck: true },
25
+ sub: { type: Boolean, required: false },
26
+ labelKey: { type: null, required: true },
27
+ descriptionKey: { type: null, required: true },
28
+ checkedIcon: { type: [String, Object], required: false },
29
+ loadingIcon: { type: [String, Object], required: false },
30
+ externalIcon: { type: [Boolean, String, Object], required: false },
31
+ ui: { type: null, required: true },
32
+ uiOverride: { type: null, required: false },
33
+ class: { type: [Object, String, Number, Boolean, null, Array], required: false, skipCheck: true },
34
+ loop: { type: Boolean, required: false },
35
+ sideFlip: { type: Boolean, required: false },
36
+ alignOffset: { type: Number, required: false },
37
+ alignFlip: { type: Boolean, required: false },
38
+ avoidCollisions: { type: Boolean, required: false },
39
+ collisionBoundary: { type: null, required: false },
40
+ collisionPadding: { type: [Number, Object], required: false },
41
+ sticky: { type: String, required: false },
42
+ hideWhenDetached: { type: Boolean, required: false },
43
+ positionStrategy: { type: String, required: false },
44
+ disableUpdateOnLayoutShift: { type: Boolean, required: false },
45
+ prioritizePosition: { type: Boolean, required: false },
46
+ reference: { type: null, required: false }
47
+ });
48
+ const emit = defineEmits(["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "closeAutoFocus"]);
49
+ const slots = defineSlots();
50
+ const { dir } = useLocale();
51
+ const appConfig = useAppConfig();
52
+ const portalProps = usePortal(toRef(() => props.portal));
53
+ const contentProps = useForwardPropsEmits(reactiveOmit(props, "sub", "items", "portal", "labelKey", "descriptionKey", "checkedIcon", "loadingIcon", "externalIcon", "class", "ui", "uiOverride"), emit);
54
+ const getProxySlots = () => omit(slots, ["default"]);
55
+ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate();
56
+ const childrenIcon = computed(() => dir.value === "rtl" ? appConfig.ui.icons.chevronLeft : appConfig.ui.icons.chevronRight);
57
+ const groups = computed(
58
+ () => props.items?.length ? isArrayOfArray(props.items) ? props.items : [props.items] : []
59
+ );
60
+ </script>
61
+
62
+ <template>
63
+ <DefineItemTemplate v-slot="{ item, active, index }">
64
+ <slot :name="item.slot || 'item'" :item="item" :index="index" :ui="ui">
65
+ <slot :name="`${item.slot || 'item'}-leading`" :item="item" :active="active" :index="index" :ui="ui">
66
+ <Icon
67
+ v-if="item.loading"
68
+ :name="props.loadingIcon || appConfig.ui.icons.loading"
69
+ :class="props.ui.itemLeadingIcon({ class: [props.uiOverride?.itemLeadingIcon, item.ui?.itemLeadingIcon], color: item.color, loading: true })"
70
+ data-part="itemLeadingIcon"
71
+ />
72
+ <Icon
73
+ v-else-if="item.icon"
74
+ :name="item.icon"
75
+ :class="props.ui.itemLeadingIcon({ class: [props.uiOverride?.itemLeadingIcon, item.ui?.itemLeadingIcon], color: item.color, active })"
76
+ data-part="itemLeadingIcon"
77
+ />
78
+ <Avatar
79
+ v-else-if="item.avatar"
80
+ v-bind="item.avatar"
81
+ :size="item.ui?.itemLeadingAvatarSize || props.uiOverride?.itemLeadingAvatarSize || props.ui.itemLeadingAvatarSize()"
82
+ :class="props.ui.itemLeadingAvatar({ class: [props.uiOverride?.itemLeadingAvatar, item.ui?.itemLeadingAvatar], active })"
83
+ data-part="itemLeadingAvatar"
84
+ />
85
+ </slot>
86
+
87
+ <span
88
+ v-if="get(item, props.labelKey) || !!slots[`${item.slot || 'item'}-label`] || (get(item, props.descriptionKey) || !!slots[`${item.slot || 'item'}-description`])"
89
+ :class="props.ui.itemWrapper({ class: [props.uiOverride?.itemWrapper, item.ui?.itemWrapper] })"
90
+ data-part="itemWrapper"
91
+ >
92
+ <span
93
+ v-if="get(item, props.labelKey) || !!slots[`${item.slot || 'item'}-label`]"
94
+ :class="props.ui.itemLabel({ class: [props.uiOverride?.itemLabel, item.ui?.itemLabel], active })"
95
+ data-part="itemLabel"
96
+ >
97
+ <slot :name="`${item.slot || 'item'}-label`" :item="item" :active="active" :index="index">
98
+ {{ get(item, props.labelKey) }}
99
+ </slot>
100
+
101
+ <Icon
102
+ v-if="item.target === '_blank' && props.externalIcon !== false"
103
+ :name="typeof props.externalIcon === 'string' ? props.externalIcon : appConfig.ui.icons.external"
104
+ :class="props.ui.itemLabelExternalIcon({ class: [props.uiOverride?.itemLabelExternalIcon, item.ui?.itemLabelExternalIcon], color: item.color, active })"
105
+ data-part="itemLabelExternalIcon"
106
+ />
107
+ </span>
108
+
109
+ <span v-if="get(item, props.descriptionKey) || !!slots[`${item.slot || 'item'}-description`]" :class="props.ui.itemDescription({ class: [props.uiOverride?.itemDescription, item.ui?.itemDescription] })" data-part="itemDescription">
110
+ <slot :name="`${item.slot || 'item'}-description`" :item="item" :active="active" :index="index">
111
+ {{ get(item, props.descriptionKey) }}
112
+ </slot>
113
+ </span>
114
+ </span>
115
+
116
+ <span :class="props.ui.itemTrailing({ class: [props.uiOverride?.itemTrailing, item.ui?.itemTrailing] })" data-part="itemTrailing">
117
+ <slot :name="`${item.slot || 'item'}-trailing`" :item="item" :active="active" :index="index" :ui="ui">
118
+ <Icon v-if="item.children?.length" :name="childrenIcon" :class="props.ui.itemTrailingIcon({ class: [props.uiOverride?.itemTrailingIcon, item.ui?.itemTrailingIcon], color: item.color, active })" data-part="itemTrailingIcon" />
119
+ <span v-else-if="item.kbds?.length" :class="props.ui.itemTrailingKbds({ class: [props.uiOverride?.itemTrailingKbds, item.ui?.itemTrailingKbds] })" data-part="itemTrailingKbds">
120
+ <Kbd
121
+ v-for="(kbd, kbdIndex) in item.kbds"
122
+ :key="kbdIndex"
123
+ :size="item.ui?.itemTrailingKbdsSize || props.uiOverride?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()"
124
+ v-bind="typeof kbd === 'string' ? { value: kbd } : kbd"
125
+ />
126
+ </span>
127
+ </slot>
128
+
129
+ <ContextMenu.ItemIndicator as-child>
130
+ <Icon :name="props.checkedIcon || appConfig.ui.icons.check" :class="props.ui.itemTrailingIcon({ class: [props.uiOverride?.itemTrailingIcon, item.ui?.itemTrailingIcon], color: item.color })" data-part="itemTrailingIcon" />
131
+ </ContextMenu.ItemIndicator>
132
+ </span>
133
+ </slot>
134
+ </DefineItemTemplate>
135
+
136
+ <ContextMenu.Portal v-bind="portalProps">
137
+ <component :is="props.sub ? ContextMenu.SubContent : ContextMenu.Content" :class="props.ui.content({ class: [props.uiOverride?.content, props.class] })" v-bind="contentProps">
138
+ <slot name="content-top" :sub="props.sub ?? false"></slot>
139
+
140
+ <div role="presentation" :class="props.ui.viewport({ class: props.uiOverride?.viewport })" data-part="viewport">
141
+ <ContextMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="props.ui.group({ class: props.uiOverride?.group })" data-part="group">
142
+ <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
143
+ <ContextMenu.Label v-if="item.type === 'label'" :class="props.ui.label({ class: [props.uiOverride?.label, item.ui?.label, item.class] })" data-part="label">
144
+ <ReuseItemTemplate :item="item" :index="index" />
145
+ </ContextMenu.Label>
146
+ <ContextMenu.Separator v-else-if="item.type === 'separator'" :class="props.ui.separator({ class: [props.uiOverride?.separator, item.ui?.separator, item.class] })" data-part="separator" />
147
+ <ContextMenu.Sub v-else-if="item?.children?.length" :open="item.open" :default-open="item.defaultOpen">
148
+ <ContextMenu.SubTrigger
149
+ as="button"
150
+ type="button"
151
+ :disabled="item.disabled"
152
+ :text-value="get(item, props.labelKey)"
153
+ :class="props.ui.item({ class: [props.uiOverride?.item, item.ui?.item, item.class], color: item.color })"
154
+ data-part="item"
155
+ >
156
+ <ReuseItemTemplate :item="item" :index="index" />
157
+ </ContextMenu.SubTrigger>
158
+
159
+ <ContextMenuContent
160
+ sub
161
+ :class="item.ui?.content"
162
+ :ui="props.ui"
163
+ :ui-override="props.uiOverride"
164
+ :portal="props.portal"
165
+ :items="item.children"
166
+ side="right"
167
+ align="start"
168
+ :align-offset="-4"
169
+ :side-offset="3"
170
+ :label-key="props.labelKey"
171
+ :description-key="props.descriptionKey"
172
+ :checked-icon="props.checkedIcon"
173
+ :loading-icon="props.loadingIcon"
174
+ :external-icon="props.externalIcon"
175
+ v-bind="item.content"
176
+ >
177
+ <template v-for="(_, name) in getProxySlots()" #[name]="slotProps">
178
+ <slot :name="name" v-bind="slotProps"></slot>
179
+ </template>
180
+ </ContextMenuContent>
181
+ </ContextMenu.Sub>
182
+ <ContextMenu.CheckboxItem
183
+ v-else-if="item.type === 'checkbox'"
184
+ :model-value="item.checked"
185
+ :disabled="item.disabled"
186
+ :text-value="get(item, props.labelKey)"
187
+ :class="props.ui.item({ class: [props.uiOverride?.item, item.ui?.item, item.class], color: item.color })"
188
+ data-part="item"
189
+ @update:model-value="item.onUpdateChecked"
190
+ @select="item.onSelect"
191
+ >
192
+ <ReuseItemTemplate :item="item" :index="index" />
193
+ </ContextMenu.CheckboxItem>
194
+ <Link v-else v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
195
+ <ContextMenu.Item
196
+ as-child
197
+ :disabled="item.disabled"
198
+ :text-value="get(item, props.labelKey)"
199
+ @select="item.onSelect"
200
+ >
201
+ <LinkBase v-bind="slotProps" :class="props.ui.item({ class: [props.uiOverride?.item, item.ui?.item, item.class], color: item.color, active })" data-part="item">
202
+ <ReuseItemTemplate :item="item" :active="active" :index="index" />
203
+ </LinkBase>
204
+ </ContextMenu.Item>
205
+ </Link>
206
+ </template>
207
+ </ContextMenu.Group>
208
+ </div>
209
+
210
+ <slot></slot>
211
+
212
+ <slot name="content-bottom" :sub="props.sub ?? false"></slot>
213
+ </component>
214
+ </ContextMenu.Portal>
215
+ </template>
@@ -0,0 +1,40 @@
1
+ import type { ContextMenuContentProps as RekaContextMenuContentProps } from 'reka-ui';
2
+ import type theme from '#build/ui/context-menu';
3
+ import type { ComponentBaseProps, ComponentStyler, ComponentUIProps, ContextMenuItem, ContextMenuSlots, IconProps } from '../types';
4
+ import type { ArrayOrNested, GetItemKeys } from '../types/utils';
5
+ interface ContextMenuContentProps<T extends ArrayOrNested<ContextMenuItem>> extends ComponentBaseProps, Omit<RekaContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
6
+ items?: T;
7
+ portal?: boolean | string | HTMLElement;
8
+ sub?: boolean;
9
+ labelKey: GetItemKeys<T>;
10
+ descriptionKey: GetItemKeys<T>;
11
+ checkedIcon?: IconProps['name'];
12
+ loadingIcon?: IconProps['name'];
13
+ externalIcon?: boolean | IconProps['name'];
14
+ ui: ComponentStyler<typeof theme>;
15
+ uiOverride?: ComponentUIProps<typeof theme>;
16
+ }
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
19
+ declare const __VLS_export: <T extends ArrayOrNested<ContextMenuItem>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
20
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<ContextMenuContentProps<T> & {
21
+ onEscapeKeyDown?: ((event: KeyboardEvent) => any) | undefined;
22
+ onPointerDownOutside?: ((event: import("reka-ui").PointerDownOutsideEvent) => any) | undefined;
23
+ onFocusOutside?: ((event: import("reka-ui").FocusOutsideEvent) => any) | undefined;
24
+ onInteractOutside?: ((event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => any) | undefined;
25
+ onCloseAutoFocus?: ((event: Event) => any) | undefined;
26
+ }> & (typeof globalThis extends {
27
+ __VLS_PROPS_FALLBACK: infer P;
28
+ } ? P : {});
29
+ expose: (exposed: {}) => void;
30
+ attrs: any;
31
+ slots: ContextMenuSlots<T>;
32
+ emit: ((evt: "escapeKeyDown", event: KeyboardEvent) => void) & ((evt: "pointerDownOutside", event: import("reka-ui").PointerDownOutsideEvent) => void) & ((evt: "focusOutside", event: import("reka-ui").FocusOutsideEvent) => void) & ((evt: "interactOutside", event: import("reka-ui").PointerDownOutsideEvent | import("reka-ui").FocusOutsideEvent) => void) & ((evt: "closeAutoFocus", event: Event) => void);
33
+ }>) => import("vue").VNode & {
34
+ __ctx?: Awaited<typeof __VLS_setup>;
35
+ };
36
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
37
+ [K in keyof T]: T[K];
38
+ } : {
39
+ [K in keyof T as K]: T[K];
40
+ }) & {};
@@ -9,7 +9,7 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
9
9
  icon?: IconProps['name'];
10
10
  color?: ThemeVariants['color'];
11
11
  avatar?: AvatarProps;
12
- content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>;
12
+ content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & ComponentBaseProps & Partial<EmitsToProps<DropdownMenuContentEmits>>;
13
13
  kbds?: KbdProps['value'][] | KbdProps[];
14
14
  /**
15
15
  * The item type.
@@ -25,7 +25,7 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
25
25
  children?: ArrayOrNested<DropdownMenuItem>;
26
26
  onSelect?: (e: Event) => void;
27
27
  onUpdateChecked?: (checked: boolean) => void;
28
- ui?: Pick<ComponentUIProps<typeof theme>, 'item' | 'label' | 'separator' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemLabelExternalIcon' | 'itemTrailing' | 'itemTrailingIcon' | 'itemTrailingKbds' | 'itemTrailingKbdsSize'>;
28
+ ui?: Pick<ComponentUIProps<typeof theme>, 'content' | 'item' | 'label' | 'separator' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemLabelExternalIcon' | 'itemTrailing' | 'itemTrailingIcon' | 'itemTrailingKbds' | 'itemTrailingKbdsSize'>;
29
29
  [key: string]: any;
30
30
  }
31
31
  type ThemeVariants = VariantProps<typeof theme>;
@@ -20,7 +20,6 @@ import Link from "./Link.vue";
20
20
  import LinkBase from "./LinkBase.vue";
21
21
  defineOptions({ inheritAttrs: false });
22
22
  const props = defineProps({
23
- size: { type: null, required: false },
24
23
  items: { type: null, required: false },
25
24
  portal: { type: [Boolean, String], required: false, skipCheck: true },
26
25
  sub: { type: Boolean, required: false },
@@ -83,8 +82,8 @@ const groups = computed(
83
82
  />
84
83
  <Avatar
85
84
  v-else-if="item.avatar"
85
+ :size="item.ui?.itemLeadingAvatarSize || props.uiOverride?.itemLeadingAvatarSize || props.ui.itemLeadingAvatarSize()"
86
86
  v-bind="item.avatar"
87
- :size="item.avatar.size || props.size"
88
87
  :class="props.ui.itemLeadingAvatar({ class: [props.uiOverride?.itemLeadingAvatar, item.ui?.itemLeadingAvatar], active })"
89
88
  data-part="itemLeadingAvatar"
90
89
  />
@@ -140,7 +139,7 @@ const groups = computed(
140
139
  </DefineItemTemplate>
141
140
 
142
141
  <DropdownMenu.Portal v-bind="portalProps">
143
- <component :is="props.sub ? DropdownMenu.SubContent : DropdownMenu.Content" v-bind="{ ...contentProps, ...$attrs }" :class="props.class">
142
+ <component :is="props.sub ? DropdownMenu.SubContent : DropdownMenu.Content" :class="props.ui.content({ class: [props.uiOverride?.content, props.class] })" v-bind="contentProps">
144
143
  <slot name="content-top" :sub="props.sub ?? false"></slot>
145
144
 
146
145
  <div role="presentation" :class="props.ui.viewport({ class: props.uiOverride?.viewport })" data-part="viewport">
@@ -164,12 +163,11 @@ const groups = computed(
164
163
 
165
164
  <DropdownMenuContent
166
165
  sub
167
- :class="props.class"
166
+ :class="item.ui?.content"
168
167
  :ui="props.ui"
169
168
  :ui-override="props.uiOverride"
170
169
  :portal="props.portal"
171
170
  :items="item.children"
172
- side="right"
173
171
  align="start"
174
172
  :align-offset="-4"
175
173
  :side-offset="3"
@@ -1,11 +1,8 @@
1
- import type { VariantProps } from '@byyuurin/ui-kit';
2
- import type { DropdownMenuContentEmits as RekaDropdownMenuContentEmits, DropdownMenuContentProps as RekaDropdownMenuContentProps } from 'reka-ui';
1
+ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps } from 'reka-ui';
3
2
  import type theme from '#build/ui/dropdown-menu';
4
3
  import type { ComponentBaseProps, ComponentStyler, ComponentUIProps, DropdownMenuItem, DropdownMenuSlots, IconProps } from '../types';
5
4
  import type { ArrayOrNested, DynamicSlots, GetItemKeys, MergeTypes, NestedItem, StaticSlot } from '../types/utils';
6
- type ThemeVariants = VariantProps<typeof theme>;
7
5
  export interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends ComponentBaseProps, Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
8
- size?: ThemeVariants['size'];
9
6
  items?: T;
10
7
  portal?: boolean | string | HTMLElement;
11
8
  sub?: boolean;
@@ -17,9 +14,7 @@ export interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuIt
17
14
  ui: ComponentStyler<typeof theme>;
18
15
  uiOverride?: ComponentUIProps<typeof theme>;
19
16
  }
20
- export interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {
21
- }
22
- export type DropdownMenuContentSlots<A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>, T extends NestedItem<A> = NestedItem<A>> = Pick<DropdownMenuSlots<A>, 'item' | 'item-leading' | 'item-label' | 'item-description' | 'item-trailing' | 'content-top' | 'content-bottom'> & {
17
+ type DropdownMenuContentSlots<A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>, T extends NestedItem<A> = NestedItem<A>> = Pick<DropdownMenuSlots<A>, 'item' | 'item-leading' | 'item-label' | 'item-description' | 'item-trailing' | 'content-top' | 'content-bottom'> & {
23
18
  default: StaticSlot;
24
19
  } & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
25
20
  active: boolean;
@@ -291,7 +291,7 @@ function getAccordionDefaultValue(list, level = 0) {
291
291
  :item="childItem"
292
292
  :index="childIndex"
293
293
  :level="level + 1"
294
- :class="ui.childItem({ class: [props.ui?.childItem, item.ui?.childItem] })"
294
+ :class="ui.childItem({ class: [props.ui?.childItem, childItem.ui?.childItem] })"
295
295
  data-part="childItem"
296
296
  />
297
297
  </AccordionRoot>
@@ -13,8 +13,8 @@ export interface NavigationMenuItem extends ComponentBaseProps, Omit<LinkProps,
13
13
  icon?: IconProps['name'];
14
14
  avatar?: AvatarProps;
15
15
  /**
16
- * Display a chip on the item.
17
- * `{ size: 'xs', variant: 'outline' }`
16
+ * Display a badge on the item.
17
+ * `{ size: 'sm', color: 'neutral', variant: 'outline' }`
18
18
  */
19
19
  badge?: string | number | BadgeProps;
20
20
  /**
@@ -54,9 +54,9 @@ export interface NavigationMenuItem extends ComponentBaseProps, Omit<LinkProps,
54
54
  }
55
55
  type ThemeVariants = VariantProps<typeof theme>;
56
56
  type SingleOrMultipleType = 'single' | 'multiple';
57
- type Orientation = ThemeVariants['orientation'];
57
+ type Orientation = NavigationMenuRootProps['orientation'];
58
58
  type NavigationMenuModelValue<K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> = O extends 'horizontal' ? string : K extends 'single' ? string : K extends 'multiple' ? string[] : MaybeArray<string>;
59
- export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> extends ComponentBaseProps, Pick<NavigationMenuRootProps, 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'type' | 'collapsible'> {
59
+ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> extends ComponentBaseProps, Pick<NavigationMenuRootProps, 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'collapsible'> {
60
60
  /**
61
61
  * The element or component this component should render as.
62
62
  * @default "div"