@byyuurin/ui 0.4.0 → 0.5.0

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.
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.0",
4
4
  "configKey": "ui",
5
5
  "compatibility": {
6
6
  "nuxt": ">=3.16.2"
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
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.Cakz_vv4.mjs';
4
4
  import { d as defaultOptions, r as resolveColors, g as getDefaultUIConfig } from './shared/ui.DYMXCXO6.mjs';
5
5
  import 'node:process';
6
6
  import 'node:url';
@@ -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"
@@ -93,22 +93,24 @@ defineExpose({
93
93
  </div>
94
94
 
95
95
  <div :class="ui.wrapper({ class: [props.ui?.wrapper, item.ui?.wrapper] })" data-part="wrapper">
96
- <StepperTitle as="div" :class="ui.title({ class: [props.ui?.title, item.ui?.title] })" data-part="title">
97
- <slot name="title" :item="item">
98
- {{ item.title }}
99
- </slot>
100
- </StepperTitle>
96
+ <slot :name="item.slot ? `${item.slot}-wrapper` : 'wrapper'" :item="item">
97
+ <StepperTitle v-if="item.title || !!slots[item.slot ? `${item.slot}-title` : 'title']" as="div" :class="ui.title({ class: [props.ui?.title, item.ui?.title] })" data-part="title">
98
+ <slot :name="item.slot ? `${item.slot}-title` : 'title'" :item="item">
99
+ {{ item.title }}
100
+ </slot>
101
+ </StepperTitle>
101
102
 
102
- <StepperDescription as="div" :class="ui.description({ class: [props.ui?.description, item.ui?.description] })" data-part="description">
103
- <slot name="description" :item="item">
104
- {{ item.description }}
105
- </slot>
106
- </StepperDescription>
103
+ <StepperDescription v-if="item.description || !!slots[item.slot ? `${item.slot}-description` : 'description']" as="div" :class="ui.description({ class: [props.ui?.description, item.ui?.description] })" data-part="description">
104
+ <slot :name="item.slot ? `${item.slot}-description` : 'description'" :item="item">
105
+ {{ item.description }}
106
+ </slot>
107
+ </StepperDescription>
108
+ </slot>
107
109
  </div>
108
110
  </StepperItem>
109
111
  </div>
110
112
 
111
- <div v-if="currentStep?.content || !!slots.content || currentStep?.slot" :class="ui.content({ class: props.ui?.content })" data-part="content">
113
+ <div v-if="currentStep?.content || !!slots.content || currentStep?.slot && !!slots[currentStep.slot]" :class="ui.content({ class: props.ui?.content })" data-part="content">
112
114
  <slot :name="currentStep?.slot || 'content'" :item="currentStep">
113
115
  {{ currentStep?.content }}
114
116
  </slot>
@@ -42,6 +42,9 @@ export type StepperSlots<T extends StepperItem = StepperItem> = {
42
42
  item: T;
43
43
  ui: ComponentStyler<typeof theme>;
44
44
  }>;
45
+ wrapper: StaticSlot<{
46
+ item: T;
47
+ }>;
45
48
  title: StaticSlot<{
46
49
  item: T;
47
50
  }>;
@@ -51,7 +54,9 @@ export type StepperSlots<T extends StepperItem = StepperItem> = {
51
54
  content: StaticSlot<{
52
55
  item: T;
53
56
  }>;
54
- } & DynamicSlots<T>;
57
+ } & DynamicSlots<T, 'wrapper' | 'title' | 'description', {
58
+ item: T;
59
+ }>;
55
60
  declare const _default: typeof __VLS_export;
56
61
  export default _default;
57
62
  declare const __VLS_export: <T extends StepperItem>(__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<{
@@ -82,21 +82,23 @@ const ui = computed(() => {
82
82
  </div>
83
83
 
84
84
  <div :class="ui.wrapper({ class: [props.ui?.wrapper, item.ui?.wrapper] })" data-part="wrapper">
85
- <div v-if="item.date" :class="ui.date({ class: [props.ui?.date, item.ui?.date] })" data-part="date">
86
- <slot :name="item.slot ? `${item.slot}-date` : 'date'" :item="item">
87
- {{ item.date }}
88
- </slot>
89
- </div>
90
- <div v-if="item.title || !!slots.title" :class="ui.title({ class: [props.ui?.title, item.ui?.title] })" data-part="title">
91
- <slot :name="item.slot ? `${item.slot}-title` : 'title'" :item="item">
92
- {{ item.title }}
93
- </slot>
94
- </div>
95
- <div v-if="item.description || !!slots.description" :class="ui.description({ class: [props.ui?.description, item.ui?.description] })" data-part="description">
96
- <slot :name="item.slot ? `${item.slot}-description` : 'description'" :item="item">
97
- {{ item.description }}
98
- </slot>
99
- </div>
85
+ <slot :name="item.slot ? `${item.slot}-wrapper` : 'wrapper'" :item="item">
86
+ <div v-if="item.date || !!slots[item.slot ? `${item.slot}-date` : 'date']" :class="ui.date({ class: [props.ui?.date, item.ui?.date] })" data-part="date">
87
+ <slot :name="item.slot ? `${item.slot}-date` : 'date'" :item="item">
88
+ {{ item.date }}
89
+ </slot>
90
+ </div>
91
+ <div v-if="item.title || !!slots[item.slot ? `${item.slot}-title` : 'title']" :class="ui.title({ class: [props.ui?.title, item.ui?.title] })" data-part="title">
92
+ <slot :name="item.slot ? `${item.slot}-title` : 'title'" :item="item">
93
+ {{ item.title }}
94
+ </slot>
95
+ </div>
96
+ <div v-if="item.description || !!slots[item.slot ? `${item.slot}-description` : 'description']" :class="ui.description({ class: [props.ui?.description, item.ui?.description] })" data-part="description">
97
+ <slot :name="item.slot ? `${item.slot}-description` : 'description'" :item="item">
98
+ {{ item.description }}
99
+ </slot>
100
+ </div>
101
+ </slot>
100
102
  </div>
101
103
  </div>
102
104
  </Primitive>
@@ -44,6 +44,9 @@ export type TimelineSlots<T extends TimelineItem = TimelineItem> = {
44
44
  indicator: StaticSlot<{
45
45
  item: T;
46
46
  }>;
47
+ wrapper: StaticSlot<{
48
+ item: T;
49
+ }>;
47
50
  date: StaticSlot<{
48
51
  item: T;
49
52
  }>;
@@ -53,9 +56,9 @@ export type TimelineSlots<T extends TimelineItem = TimelineItem> = {
53
56
  description: StaticSlot<{
54
57
  item: T;
55
58
  }>;
56
- } & DynamicSlots<T, 'indicator' | 'date' | 'title' | 'description', {
59
+ } & DynamicSlots<T, 'indicator' | 'wrapper' | 'date' | 'title' | 'description', {
57
60
  item: T;
58
- }, false>;
61
+ }>;
59
62
  declare const _default: typeof __VLS_export;
60
63
  export default _default;
61
64
  declare const __VLS_export: <T extends TimelineItem>(__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<{
@@ -15,6 +15,7 @@ export * from '../components/Checkbox.vue';
15
15
  export * from '../components/CheckboxGroup.vue';
16
16
  export * from '../components/Chip.vue';
17
17
  export * from '../components/Collapsible.vue';
18
+ export * from '../components/ContextMenu.vue';
18
19
  export * from '../components/Drawer.vue';
19
20
  export * from '../components/DropdownMenu.vue';
20
21
  export * from '../components/FieldGroup.vue';
@@ -13,6 +13,7 @@ export * from "../components/Checkbox.vue";
13
13
  export * from "../components/CheckboxGroup.vue";
14
14
  export * from "../components/Chip.vue";
15
15
  export * from "../components/Collapsible.vue";
16
+ export * from "../components/ContextMenu.vue";
16
17
  export * from "../components/Drawer.vue";
17
18
  export * from "../components/DropdownMenu.vue";
18
19
  export * from "../components/FieldGroup.vue";
@@ -35,7 +35,7 @@ const props = defineProps({
35
35
  exactActiveClass: { type: String, required: false },
36
36
  ariaCurrentValue: { type: String, required: false, default: "page" },
37
37
  viewTransition: { type: Boolean, required: false },
38
- to: { type: null, required: true },
38
+ to: { type: null, required: false },
39
39
  replace: { type: Boolean, required: false }
40
40
  });
41
41
  defineSlots();
@@ -8,7 +8,7 @@ export interface LinkSlots {
8
8
  active: boolean;
9
9
  }>;
10
10
  }
11
- export interface LinkProps extends ComponentBaseProps, Omit<RouterLinkProps, 'custom'>, /** @vue-ignore */ Omit<ButtonHTMLAttributes, 'type' | 'disabled'>, /** @vue-ignore */ Omit<AnchorHTMLAttributes, 'href' | 'target' | 'rel' | 'type'> {
11
+ export interface LinkProps extends ComponentBaseProps, Partial<Omit<RouterLinkProps, 'custom'>>, /** @vue-ignore */ Omit<ButtonHTMLAttributes, 'type' | 'disabled'>, /** @vue-ignore */ Omit<AnchorHTMLAttributes, 'href' | 'target' | 'rel' | 'type'> {
12
12
  /**
13
13
  * The element or component this component should render as when not a link.
14
14
  * @default "button"
@@ -9,7 +9,7 @@ import { ct } from '@byyuurin/ui-kit';
9
9
  import { defu } from 'defu';
10
10
 
11
11
  const name = "@byyuurin/ui";
12
- const version = "0.4.0";
12
+ const version = "0.5.0";
13
13
 
14
14
  const accordion = ct({
15
15
  parts: {
@@ -820,7 +820,7 @@ const calendar = (options) => ct({
820
820
 
821
821
  const card = ct({
822
822
  parts: {
823
- root: "rounded divide-y",
823
+ root: "rounded divide-y overflow-hidden",
824
824
  header: "flex flex-wrap items-center gap-1 p-4 sm:px-6",
825
825
  body: "flex-1 overflow-y-auto p-4 sm:p-6 empty:hidden",
826
826
  footer: "flex items-center gap-1.5 p-4 sm:px-6",
@@ -1200,6 +1200,123 @@ const collapsible = ct({
1200
1200
  }
1201
1201
  });
1202
1202
 
1203
+ const contextMenu = (options) => ct({
1204
+ parts: {
1205
+ content: "min-w-32 bg-default shadow-lg rounded-md ring ring-default overflow-hidden data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-[--reka-context-menu-content-transform-origin] flex flex-col",
1206
+ viewport: "relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1",
1207
+ group: "p-1 isolate",
1208
+ label: "w-full flex items-center font-semibold text-highlighted",
1209
+ separator: "-mx-1 my-1 h-px bg-border",
1210
+ item: "group relative w-full flex items-start select-none outline-none before:content-empty before:absolute before:z-[-1] before:inset-px before:rounded-md data-[disabled]:cursor-not-allowed data-[disabled]:opacity-75",
1211
+ itemLeadingIcon: "shrink-0",
1212
+ itemLeadingAvatar: "shrink-0",
1213
+ itemLeadingAvatarSize: "",
1214
+ itemTrailing: "ms-auto inline-flex gap-1.5 items-center",
1215
+ itemTrailingIcon: "shrink-0",
1216
+ itemTrailingKbds: "hidden lg:inline-flex items-center shrink-0",
1217
+ itemTrailingKbdsSize: "",
1218
+ itemWrapper: "flex-1 flex flex-col text-start min-w-0",
1219
+ itemLabel: "truncate",
1220
+ itemDescription: "truncate text-muted",
1221
+ itemLabelExternalIcon: "inline-block size-3 align-top text-dimmed"
1222
+ },
1223
+ variants: {
1224
+ color: {
1225
+ ...Object.fromEntries((options.theme.colors || []).map((color) => [color, ""])),
1226
+ neutral: ""
1227
+ },
1228
+ active: {
1229
+ true: {
1230
+ item: "text-highlighted before:bg-elevated",
1231
+ itemLeadingIcon: "text-default"
1232
+ },
1233
+ false: {
1234
+ item: [
1235
+ "text-default data-highlighted:text-highlighted data-[state=open]:text-highlighted data-[highlighted]:before:bg-elevated/50 data-[state=open]:before:bg-elevated/50",
1236
+ options.theme.transitions && "transition-colors before:transition-colors"
1237
+ ],
1238
+ itemLeadingIcon: [
1239
+ "text-dimmed group-data-[highlighted]:text-default group-data-[state=open]:text-default",
1240
+ options.theme.transitions && "transition-colors"
1241
+ ]
1242
+ }
1243
+ },
1244
+ loading: {
1245
+ true: {
1246
+ itemLeadingIcon: "animate-spin"
1247
+ }
1248
+ },
1249
+ size: {
1250
+ xs: {
1251
+ label: "p-1 text-xs gap-1",
1252
+ item: "p-1 text-xs gap-1",
1253
+ itemLeadingIcon: "size-4",
1254
+ itemLeadingAvatarSize: "3xs",
1255
+ itemTrailingIcon: "size-4",
1256
+ itemTrailingKbds: "gap-0.5",
1257
+ itemTrailingKbdsSize: "sm"
1258
+ },
1259
+ sm: {
1260
+ label: "p-1.5 text-xs gap-1.5",
1261
+ item: "p-1.5 text-xs gap-1.5",
1262
+ itemLeadingIcon: "size-4",
1263
+ itemLeadingAvatarSize: "3xs",
1264
+ itemTrailingIcon: "size-4",
1265
+ itemTrailingKbds: "gap-0.5",
1266
+ itemTrailingKbdsSize: "sm"
1267
+ },
1268
+ md: {
1269
+ label: "p-1.5 text-sm gap-1.5",
1270
+ item: "p-1.5 text-sm gap-1.5",
1271
+ itemLeadingIcon: "size-5",
1272
+ itemLeadingAvatarSize: "2xs",
1273
+ itemTrailingIcon: "size-5",
1274
+ itemTrailingKbds: "gap-0.5",
1275
+ itemTrailingKbdsSize: "md"
1276
+ },
1277
+ lg: {
1278
+ label: "p-2 text-sm gap-2",
1279
+ item: "p-2 text-sm gap-2",
1280
+ itemLeadingIcon: "size-5",
1281
+ itemLeadingAvatarSize: "2xs",
1282
+ itemTrailingIcon: "size-5",
1283
+ itemTrailingKbds: "gap-1",
1284
+ itemTrailingKbdsSize: "md"
1285
+ },
1286
+ xl: {
1287
+ label: "p-2 text-base gap-2",
1288
+ item: "p-2 text-base gap-2",
1289
+ itemLeadingIcon: "size-6",
1290
+ itemLeadingAvatarSize: "xs",
1291
+ itemTrailingIcon: "size-6",
1292
+ itemTrailingKbds: "gap-1",
1293
+ itemTrailingKbdsSize: "lg"
1294
+ }
1295
+ }
1296
+ },
1297
+ compoundVariants: [
1298
+ ...(options.theme.colors || []).map((color) => ({
1299
+ color,
1300
+ active: false,
1301
+ class: {
1302
+ item: `text-${color} data-[highlighted]:text-${color} data-[highlighted]:before:bg-${color}/10 data-[state=open]:before:bg-${color}/10`,
1303
+ itemLeadingIcon: `text-${color}/75 group-data-[highlighted]:text-${color} group-data-[state=open]:text-${color}`
1304
+ }
1305
+ })),
1306
+ ...(options.theme.colors || []).map((color) => ({
1307
+ color,
1308
+ active: true,
1309
+ class: {
1310
+ item: `text-${color} before:bg-${color}/10`,
1311
+ itemLeadingIcon: `text-${color}`
1312
+ }
1313
+ }))
1314
+ ],
1315
+ defaultVariants: {
1316
+ size: "md"
1317
+ }
1318
+ });
1319
+
1203
1320
  const drawer = ct({
1204
1321
  parts: {
1205
1322
  overlay: "fixed inset-0 bg-elevated/75",
@@ -4511,6 +4628,7 @@ const theme = {
4511
4628
  checkboxGroup: checkboxGroup,
4512
4629
  chip: chip,
4513
4630
  collapsible: collapsible,
4631
+ contextMenu: contextMenu,
4514
4632
  drawer: drawer,
4515
4633
  dropdownMenu: dropdownMenu,
4516
4634
  fieldGroup: fieldGroup,
package/dist/unplugin.mjs CHANGED
@@ -2,7 +2,7 @@ import { fileURLToPath } from 'node:url';
2
2
  import { defu } from 'defu';
3
3
  import { join, normalize } from 'pathe';
4
4
  import { createUnplugin } from 'unplugin';
5
- import { g as getTemplates, n as name } from './shared/ui.DcEKQd0n.mjs';
5
+ import { g as getTemplates, n as name } from './shared/ui.Cakz_vv4.mjs';
6
6
  import { d as defaultOptions, r as resolveColors, g as getDefaultUIConfig } from './shared/ui.DYMXCXO6.mjs';
7
7
  import AutoImport from 'unplugin-auto-import';
8
8
  import { globSync } from 'tinyglobby';
package/dist/vite.mjs CHANGED
@@ -3,7 +3,7 @@ import 'node:url';
3
3
  import 'defu';
4
4
  import 'pathe';
5
5
  import 'unplugin';
6
- import './shared/ui.DcEKQd0n.mjs';
6
+ import './shared/ui.Cakz_vv4.mjs';
7
7
  import 'node:process';
8
8
  import '@nuxt/kit';
9
9
  import '@unocss/config';
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@byyuurin/ui",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
+ "packageManager": "pnpm@10.28.0",
5
6
  "description": "",
6
7
  "author": "Yuurin <byyuurin@gmail.com>",
7
8
  "license": "MIT",
@@ -97,6 +98,21 @@
97
98
  "dist",
98
99
  "vue-plugin.d.ts"
99
100
  ],
101
+ "scripts": {
102
+ "dev": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi dev playground/nuxt --uiDev",
103
+ "dev:build": "nuxi build playground/nuxt",
104
+ "dev:preview": "nuxi preview playground/nuxt",
105
+ "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground/nuxt && pnpm dev:vue:build",
106
+ "dev:vue": "pnpm --filter \"./playground/vue\" dev -- --uiDev",
107
+ "dev:vue:build": "pnpm --filter \"./playground/vue\" build",
108
+ "dev:vue:preview": "pnpm --filter \"./playground/vue\" preview",
109
+ "release": "bumpp && pnpm publish",
110
+ "lint": "eslint .",
111
+ "test": "vitest",
112
+ "test:types": "vue-tsc --noEmit && nuxi typecheck playground/nuxt",
113
+ "prepare": "simple-git-hooks",
114
+ "prepack": "nuxt-module-build build"
115
+ },
100
116
  "peerDependencies": {
101
117
  "joi": "^18.0.0",
102
118
  "superstruct": "^2.0.2",
@@ -201,18 +217,5 @@
201
217
  },
202
218
  "lint-staged": {
203
219
  "*": "eslint --fix"
204
- },
205
- "scripts": {
206
- "dev": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi dev playground/nuxt --uiDev",
207
- "dev:build": "nuxi build playground/nuxt",
208
- "dev:preview": "nuxi preview playground/nuxt",
209
- "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground/nuxt && pnpm dev:vue:build",
210
- "dev:vue": "pnpm --filter \"./playground/vue\" dev -- --uiDev",
211
- "dev:vue:build": "pnpm --filter \"./playground/vue\" build",
212
- "dev:vue:preview": "pnpm --filter \"./playground/vue\" preview",
213
- "release": "bumpp && pnpm publish",
214
- "lint": "eslint .",
215
- "test": "vitest",
216
- "test:types": "vue-tsc --noEmit && nuxi typecheck playground/nuxt"
217
220
  }
218
- }
221
+ }