@umbra.ui/core 0.1.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/components/controls/Dropdown/types.d.ts +5 -0
- package/dist/components/controls/Dropdown/types.d.ts.map +1 -0
- package/dist/components/controls/Dropdown/types.js +1 -0
- package/dist/components/controls/SegmentedControl/types.d.ts +6 -0
- package/dist/components/controls/SegmentedControl/types.d.ts.map +1 -0
- package/dist/components/controls/SegmentedControl/types.js +1 -0
- package/dist/components/dialogs/Alert/types.d.ts +7 -0
- package/dist/components/dialogs/Alert/types.d.ts.map +1 -0
- package/dist/components/dialogs/Alert/types.js +1 -0
- package/dist/components/dialogs/Toast/types.d.ts +34 -0
- package/dist/components/dialogs/Toast/types.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/types.js +10 -0
- package/dist/components/dialogs/Toast/useToast.d.ts +36 -0
- package/dist/components/dialogs/Toast/useToast.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/useToast.js +90 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts +3 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/tooltip.js +33 -0
- package/dist/components/indicators/Tooltip/types.d.ts +14 -0
- package/dist/components/indicators/Tooltip/types.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/types.js +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts +18 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.js +57 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts +14 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts.map +1 -0
- package/dist/components/inputs/Tags/tag-bar-styles.js +313 -0
- package/dist/components/inputs/Tags/types.d.ts +93 -0
- package/dist/components/inputs/Tags/types.d.ts.map +1 -0
- package/dist/components/inputs/Tags/types.js +216 -0
- package/dist/components/inputs/search/types.d.ts +9 -0
- package/dist/components/inputs/search/types.d.ts.map +1 -0
- package/dist/components/inputs/search/types.js +1 -0
- package/dist/components/navigation/adaptive/types.d.ts +16 -0
- package/dist/components/navigation/adaptive/types.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/types.js +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts +27 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.js +40 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts +6 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useBreakpoints.js +37 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts +93 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.js +145 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts +31 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewAnimation.js +591 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts +52 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewResize.js +146 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts +25 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts.map +1 -0
- package/dist/components/navigation/navstack/useNavigationStack.js +133 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts +20 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts.map +1 -0
- package/dist/components/navigation/slideover/useSlideoverController.js +267 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts +20 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts.map +1 -0
- package/dist/components/navigation/splitview/useSplitViewController.js +325 -0
- package/dist/components/navigation/tabcontroller/types.d.ts +21 -0
- package/dist/components/navigation/tabcontroller/types.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/types.js +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts +5 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.js +10 -0
- package/dist/components/navigation/types.d.ts +8 -0
- package/dist/components/navigation/types.d.ts.map +1 -0
- package/dist/components/navigation/types.js +1 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts +11 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts.map +1 -0
- package/dist/components/pickers/CollectionPicker/types.js +1 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts +13 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts.map +1 -0
- package/dist/components/pickers/ColorPicker/colors.js +266 -0
- package/dist/components/pickers/FilePicker/types.d.ts +10 -0
- package/dist/components/pickers/FilePicker/types.d.ts.map +1 -0
- package/dist/components/pickers/FilePicker/types.js +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/theme.d.ts +73 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +279 -0
- package/dist/themes/blank.d.ts +7 -0
- package/dist/themes/blank.d.ts.map +1 -0
- package/dist/themes/blank.js +543 -0
- package/dist/themes/crimson-dark.d.ts +4 -0
- package/dist/themes/crimson-dark.d.ts.map +1 -0
- package/dist/themes/crimson-dark.js +552 -0
- package/dist/themes/cyan-light.d.ts +4 -0
- package/dist/themes/cyan-light.d.ts.map +1 -0
- package/dist/themes/cyan-light.js +552 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +551 -0
- package/dist/themes/gold-dark.d.ts +4 -0
- package/dist/themes/gold-dark.d.ts.map +1 -0
- package/dist/themes/gold-dark.js +552 -0
- package/dist/themes/grass-dark.d.ts +4 -0
- package/dist/themes/grass-dark.d.ts.map +1 -0
- package/dist/themes/grass-dark.js +552 -0
- package/dist/themes/indigo.d.ts +4 -0
- package/dist/themes/indigo.d.ts.map +1 -0
- package/dist/themes/indigo.js +552 -0
- package/dist/themes/light.d.ts +4 -0
- package/dist/themes/light.d.ts.map +1 -0
- package/dist/themes/light.js +551 -0
- package/dist/themes/orange-dark.d.ts +4 -0
- package/dist/themes/orange-dark.d.ts.map +1 -0
- package/dist/themes/orange-dark.js +551 -0
- package/dist/themes/orange-light.d.ts +4 -0
- package/dist/themes/orange-light.d.ts.map +1 -0
- package/dist/themes/orange-light.js +551 -0
- package/package.json +62 -0
- package/src/components/controls/Button/Button.vue +417 -0
- package/src/components/controls/Button/README.md +348 -0
- package/src/components/controls/Button/theme.css +200 -0
- package/src/components/controls/Checkbox/Checkbox.vue +164 -0
- package/src/components/controls/Checkbox/README.md +441 -0
- package/src/components/controls/Checkbox/theme.css +36 -0
- package/src/components/controls/Dropdown/Dropdown.vue +476 -0
- package/src/components/controls/Dropdown/README.md +370 -0
- package/src/components/controls/Dropdown/theme.css +50 -0
- package/src/components/controls/Dropdown/types.ts +6 -0
- package/src/components/controls/IconButton/IconButton.vue +267 -0
- package/src/components/controls/IconButton/README.md +502 -0
- package/src/components/controls/IconButton/theme.css +89 -0
- package/src/components/controls/Radio/README.md +591 -0
- package/src/components/controls/Radio/Radio.vue +89 -0
- package/src/components/controls/Radio/theme.css +14 -0
- package/src/components/controls/RangeSlider/README.md +608 -0
- package/src/components/controls/RangeSlider/RangeSlider.vue +535 -0
- package/src/components/controls/RangeSlider/theme.css +80 -0
- package/src/components/controls/SegmentedControl/README.md +587 -0
- package/src/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
- package/src/components/controls/SegmentedControl/theme.css +60 -0
- package/src/components/controls/SegmentedControl/types.ts +5 -0
- package/src/components/controls/Slider/README.md +627 -0
- package/src/components/controls/Slider/Slider.vue +260 -0
- package/src/components/controls/Slider/theme.css +74 -0
- package/src/components/controls/Stepper/README.md +601 -0
- package/src/components/controls/Stepper/Stepper.vue +103 -0
- package/src/components/controls/Stepper/theme.css +53 -0
- package/src/components/controls/Switch/README.md +667 -0
- package/src/components/controls/Switch/Switch.vue +127 -0
- package/src/components/controls/Switch/theme.css +42 -0
- package/src/components/dialogs/Alert/Alert.vue +218 -0
- package/src/components/dialogs/Alert/README.md +450 -0
- package/src/components/dialogs/Alert/theme.css +44 -0
- package/src/components/dialogs/Alert/types.ts +11 -0
- package/src/components/dialogs/Toast/README.md +522 -0
- package/src/components/dialogs/Toast/Toast.vue +296 -0
- package/src/components/dialogs/Toast/ToastContainer.vue +330 -0
- package/src/components/dialogs/Toast/theme.css +44 -0
- package/src/components/dialogs/Toast/types.ts +46 -0
- package/src/components/dialogs/Toast/useToast.ts +127 -0
- package/src/components/indicators/ProgressBar/ProgressBar.vue +98 -0
- package/src/components/indicators/ProgressBar/README.md +744 -0
- package/src/components/indicators/ProgressBar/theme.css +36 -0
- package/src/components/indicators/Tooltip/README.md +723 -0
- package/src/components/indicators/Tooltip/TooltipProvider.vue +142 -0
- package/src/components/indicators/Tooltip/theme.css +18 -0
- package/src/components/indicators/Tooltip/tooltip.ts +48 -0
- package/src/components/indicators/Tooltip/types.ts +15 -0
- package/src/components/indicators/Tooltip/useTooltip.ts +71 -0
- package/src/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
- package/src/components/inputs/AutogrowTextView/README.md +643 -0
- package/src/components/inputs/AutogrowTextView/theme.css +28 -0
- package/src/components/inputs/InputCard/InputCard.vue +600 -0
- package/src/components/inputs/InputCard/README.md +636 -0
- package/src/components/inputs/InputEmail/InputEmail.vue +698 -0
- package/src/components/inputs/InputEmail/README.md +764 -0
- package/src/components/inputs/InputNumber/InputNumber.vue +300 -0
- package/src/components/inputs/InputNumber/README.md +749 -0
- package/src/components/inputs/InputPhone/InputPhone.vue +645 -0
- package/src/components/inputs/InputPhone/README.md +636 -0
- package/src/components/inputs/InputSecure/InputSecure.vue +646 -0
- package/src/components/inputs/InputSecure/README.md +771 -0
- package/src/components/inputs/InputText/InputText.vue +225 -0
- package/src/components/inputs/InputText/README.md +844 -0
- package/src/components/inputs/OTP/OTP.vue +349 -0
- package/src/components/inputs/OTP/README.md +736 -0
- package/src/components/inputs/OTP/theme.css +50 -0
- package/src/components/inputs/StringCapture/README.md +718 -0
- package/src/components/inputs/StringCapture/StringCapture.vue +315 -0
- package/src/components/inputs/StringCapture/theme.css +86 -0
- package/src/components/inputs/Tags/README.md +897 -0
- package/src/components/inputs/Tags/TagBar.vue +793 -0
- package/src/components/inputs/Tags/TagCreation.vue +219 -0
- package/src/components/inputs/Tags/TagPicker.vue +380 -0
- package/src/components/inputs/Tags/tag-bar-styles.ts +354 -0
- package/src/components/inputs/Tags/theme.css +121 -0
- package/src/components/inputs/Tags/types.ts +346 -0
- package/src/components/inputs/search/README.md +759 -0
- package/src/components/inputs/search/SearchBar.vue +394 -0
- package/src/components/inputs/search/SearchResults.vue +310 -0
- package/src/components/inputs/search/theme.css +187 -0
- package/src/components/inputs/search/types.ts +8 -0
- package/src/components/inputs/theme.css +102 -0
- package/src/components/menus/ActionMenu/ActionMenu.vue +383 -0
- package/src/components/menus/ActionMenu/README.md +825 -0
- package/src/components/menus/ActionMenu/theme.css +93 -0
- package/src/components/models/Popover/Popover.vue +551 -0
- package/src/components/models/Popover/README.md +885 -0
- package/src/components/models/Popover/theme.css +52 -0
- package/src/components/models/Sheet/README.md +1159 -0
- package/src/components/models/Sheet/Sheet.vue +465 -0
- package/src/components/models/Sheet/theme.css +72 -0
- package/src/components/models/Sidebar/README.md +1228 -0
- package/src/components/models/Sidebar/Sidebar.vue +480 -0
- package/src/components/models/Sidebar/theme.css +90 -0
- package/src/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
- package/src/components/navigation/adaptive/README.md +768 -0
- package/src/components/navigation/adaptive/types.ts +19 -0
- package/src/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
- package/src/components/navigation/adaptive/useBreakpoints.ts +41 -0
- package/src/components/navigation/adaptive/useContainerMonitor.ts +214 -0
- package/src/components/navigation/adaptive/useViewAnimation.ts +721 -0
- package/src/components/navigation/adaptive/useViewResize.ts +211 -0
- package/src/components/navigation/navstack/NavigationStack.vue +180 -0
- package/src/components/navigation/navstack/README.md +994 -0
- package/src/components/navigation/navstack/useNavigationStack.ts +164 -0
- package/src/components/navigation/slideover/README.md +1275 -0
- package/src/components/navigation/slideover/SlideoverController.vue +287 -0
- package/src/components/navigation/slideover/useSlideoverController.ts +320 -0
- package/src/components/navigation/splitview/README.md +1115 -0
- package/src/components/navigation/splitview/SplitViewController.vue +176 -0
- package/src/components/navigation/splitview/useSplitViewController.ts +388 -0
- package/src/components/navigation/tabcontroller/README.md +919 -0
- package/src/components/navigation/tabcontroller/TabController.vue +307 -0
- package/src/components/navigation/tabcontroller/TabItem.vue +57 -0
- package/src/components/navigation/tabcontroller/types.ts +24 -0
- package/src/components/navigation/tabcontroller/useTabController.ts +18 -0
- package/src/components/navigation/theme.css +91 -0
- package/src/components/navigation/types.ts +7 -0
- package/src/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
- package/src/components/pickers/CollectionPicker/README.md +1115 -0
- package/src/components/pickers/CollectionPicker/theme.css +14 -0
- package/src/components/pickers/CollectionPicker/types.ts +11 -0
- package/src/components/pickers/ColorPicker/ColorPicker.vue +376 -0
- package/src/components/pickers/ColorPicker/README.md +1439 -0
- package/src/components/pickers/ColorPicker/colors.ts +299 -0
- package/src/components/pickers/ColorPicker/theme.css +32 -0
- package/src/components/pickers/DatePicker/DatePicker.vue +660 -0
- package/src/components/pickers/DatePicker/README.md +1195 -0
- package/src/components/pickers/DatePicker/theme.css +22 -0
- package/src/components/pickers/FilePicker/FilePicker.vue +534 -0
- package/src/components/pickers/FilePicker/README.md +1542 -0
- package/src/components/pickers/FilePicker/theme.css +48 -0
- package/src/components/pickers/FilePicker/types.ts +10 -0
- package/src/components/pickers/IconPicker/IconPicker.vue +327 -0
- package/src/components/pickers/IconPicker/README.md +1161 -0
- package/src/components/pickers/IconPicker/theme.css +28 -0
- package/src/components/pickers/theme.css +82 -0
- package/src/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
- package/src/components/views/MarkdownViewer/README.md +833 -0
- package/src/components/views/MarkdownViewer/theme.css +130 -0
- package/src/index.ts +263 -0
- package/src/theme.ts +378 -0
- package/src/themes/crimson-dark.ts +556 -0
- package/src/themes/cyan-light.ts +556 -0
- package/src/themes/dark.ts +557 -0
- package/src/themes/gold-dark.ts +556 -0
- package/src/themes/grass-dark.ts +556 -0
- package/src/themes/indigo.ts +556 -0
- package/src/themes/light.ts +557 -0
- package/src/themes/orange-dark.ts +557 -0
- package/src/themes/orange-light.ts +557 -0
- package/src/vue.d.ts +45 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
# ActionMenu
|
|
2
|
+
|
|
3
|
+
A versatile dropdown menu component built with Vue 3 Composition API and TypeScript. The ActionMenu component provides a flexible interface for displaying contextual actions with support for both click-triggered and right-click context menus, custom icons, destructive actions, and smart positioning.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { ActionMenu } from "@umbra-ui/core";
|
|
9
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Dependencies:**
|
|
13
|
+
|
|
14
|
+
- Vue 3.x
|
|
15
|
+
- @umbra-ui/core (for icon components)
|
|
16
|
+
- @floating-ui/vue (for positioning)
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
```vue
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import { ref } from "vue";
|
|
23
|
+
import { ActionMenu, Button } from "@umbra-ui/core";
|
|
24
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
25
|
+
|
|
26
|
+
const isMenuOpen = ref(false);
|
|
27
|
+
|
|
28
|
+
const menuItems: ActionMenuItem[] = [
|
|
29
|
+
{
|
|
30
|
+
icon: "edit",
|
|
31
|
+
title: "Edit",
|
|
32
|
+
action: () => console.log("Edit clicked"),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
icon: "copy",
|
|
36
|
+
title: "Copy",
|
|
37
|
+
action: () => console.log("Copy clicked"),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
icon: "trash",
|
|
41
|
+
title: "Delete",
|
|
42
|
+
action: () => console.log("Delete clicked"),
|
|
43
|
+
isDestructive: true,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const handleItemClick = (item: ActionMenuItem) => {
|
|
48
|
+
console.log("Menu item clicked:", item.title);
|
|
49
|
+
};
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<div>
|
|
54
|
+
<ActionMenu
|
|
55
|
+
:items="menuItems"
|
|
56
|
+
v-model="isMenuOpen"
|
|
57
|
+
@item-click="handleItemClick"
|
|
58
|
+
>
|
|
59
|
+
<Button>Actions</Button>
|
|
60
|
+
</ActionMenu>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Props
|
|
66
|
+
|
|
67
|
+
| Prop Name | Type | Required | Default | Description |
|
|
68
|
+
| ------------- | ------------------ | -------- | ------- | ---------------------------------------------- |
|
|
69
|
+
| `items` | `ActionMenuItem[]` | Yes | - | Array of menu items to display |
|
|
70
|
+
| `modelValue` | `boolean` | No | `false` | Controls the open/closed state of the menu |
|
|
71
|
+
| `contextMenu` | `boolean` | No | `false` | Enables right-click context menu functionality |
|
|
72
|
+
|
|
73
|
+
## Events
|
|
74
|
+
|
|
75
|
+
| Event Name | Payload Type | Description |
|
|
76
|
+
| ------------------- | ---------------- | ---------------------------------------- |
|
|
77
|
+
| `update:modelValue` | `boolean` | Emitted when the menu open state changes |
|
|
78
|
+
| `item-click` | `ActionMenuItem` | Emitted when a menu item is clicked |
|
|
79
|
+
|
|
80
|
+
## Slots
|
|
81
|
+
|
|
82
|
+
| Slot Name | Description |
|
|
83
|
+
| --------- | --------------------------------------- |
|
|
84
|
+
| `default` | The trigger element that opens the menu |
|
|
85
|
+
|
|
86
|
+
## Exposed Methods/Refs
|
|
87
|
+
|
|
88
|
+
The ActionMenu component exposes the following methods via template refs:
|
|
89
|
+
|
|
90
|
+
| Method Name | Parameters | Description |
|
|
91
|
+
| --------------- | ---------- | ---------------------------------- |
|
|
92
|
+
| `togglePopover` | `()` | Toggles the menu open/closed state |
|
|
93
|
+
|
|
94
|
+
### Example Usage
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { ref } from "vue";
|
|
99
|
+
import { ActionMenu } from "@umbra-ui/core";
|
|
100
|
+
|
|
101
|
+
const actionMenuRef = ref<InstanceType<typeof ActionMenu> | null>(null);
|
|
102
|
+
|
|
103
|
+
const openMenu = () => {
|
|
104
|
+
actionMenuRef.value?.togglePopover();
|
|
105
|
+
};
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<template>
|
|
109
|
+
<ActionMenu ref="actionMenuRef" :items="menuItems">
|
|
110
|
+
<button @click="openMenu">Open Menu</button>
|
|
111
|
+
</ActionMenu>
|
|
112
|
+
</template>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## ActionMenuItem Interface
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface ActionMenuItem {
|
|
119
|
+
icon?: IconKey; // Optional icon from @umbra-ui/icons
|
|
120
|
+
title: string; // Display text for the menu item
|
|
121
|
+
action: () => void; // Function to execute when clicked
|
|
122
|
+
isDestructive?: boolean; // Whether this is a destructive action
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## CSS Customization
|
|
127
|
+
|
|
128
|
+
The ActionMenu component uses CSS custom properties for theming and customization:
|
|
129
|
+
|
|
130
|
+
### Light Theme Variables
|
|
131
|
+
|
|
132
|
+
```css
|
|
133
|
+
/* ActionMenu popup colors */
|
|
134
|
+
--actionmenu-popup-bg: #ffffff;
|
|
135
|
+
--actionmenu-popup-border: 1px solid rgba(0, 0, 0, 0.1);
|
|
136
|
+
--actionmenu-popup-shadow: transparent;
|
|
137
|
+
--actionmenu-popup-inset-shadow: rgba(255, 255, 255, 0.8);
|
|
138
|
+
|
|
139
|
+
/* ActionMenu item colors */
|
|
140
|
+
--actionmenu-item-bg: #ffffff;
|
|
141
|
+
--actionmenu-item-hover-bg: rgba(0, 0, 0, 0.05);
|
|
142
|
+
--actionmenu-item-border: rgba(0, 0, 0, 0.1);
|
|
143
|
+
--actionmenu-item-text: #1f2937;
|
|
144
|
+
--actionmenu-item-icon: #6b7280;
|
|
145
|
+
|
|
146
|
+
/* ActionMenu destructive colors */
|
|
147
|
+
--actionmenu-destructive-text: #dc2626;
|
|
148
|
+
--actionmenu-destructive-icon: #dc2626;
|
|
149
|
+
|
|
150
|
+
/* ActionMenu overlay colors */
|
|
151
|
+
--actionmenu-overlay-bg: rgba(0, 0, 0, 0.1);
|
|
152
|
+
--actionmenu-overlay-blur-bg: rgba(0, 0, 0, 0.1);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Dark Theme Variables
|
|
156
|
+
|
|
157
|
+
```css
|
|
158
|
+
/* ActionMenu popup colors */
|
|
159
|
+
--actionmenu-popup-bg: #484848;
|
|
160
|
+
--actionmenu-popup-border: none;
|
|
161
|
+
--actionmenu-popup-shadow: rgba(0, 0, 0, 0.21);
|
|
162
|
+
--actionmenu-popup-inset-shadow: rgba(255, 255, 255, 0.1);
|
|
163
|
+
|
|
164
|
+
/* ActionMenu item colors */
|
|
165
|
+
--actionmenu-item-bg: #484848;
|
|
166
|
+
--actionmenu-item-hover-bg: rgba(255, 255, 255, 0.05);
|
|
167
|
+
--actionmenu-item-border: rgba(255, 255, 255, 0.1);
|
|
168
|
+
--actionmenu-item-text: #eeeeee;
|
|
169
|
+
--actionmenu-item-icon: #eeeeee;
|
|
170
|
+
|
|
171
|
+
/* ActionMenu destructive colors */
|
|
172
|
+
--actionmenu-destructive-text: #e5484d;
|
|
173
|
+
--actionmenu-destructive-icon: #e5484d;
|
|
174
|
+
|
|
175
|
+
/* ActionMenu overlay colors */
|
|
176
|
+
--actionmenu-overlay-bg: rgba(0, 0, 0, 0.7);
|
|
177
|
+
--actionmenu-overlay-blur-bg: rgba(0, 0, 0, 0.1);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Key Features
|
|
181
|
+
|
|
182
|
+
### Menu Types
|
|
183
|
+
|
|
184
|
+
- **Click Menu**: Standard dropdown menu triggered by clicking the trigger element
|
|
185
|
+
- **Context Menu**: Right-click context menu with smart positioning and blur overlay
|
|
186
|
+
|
|
187
|
+
### Smart Positioning
|
|
188
|
+
|
|
189
|
+
- **Floating UI Integration**: Uses Floating UI for intelligent positioning
|
|
190
|
+
- **Auto-flip**: Automatically flips to opposite side if no space available
|
|
191
|
+
- **Auto-shift**: Shifts position to stay within viewport bounds
|
|
192
|
+
- **Size Constraints**: Respects viewport boundaries with padding
|
|
193
|
+
|
|
194
|
+
### Visual Features
|
|
195
|
+
|
|
196
|
+
- **Smooth Animations**: Fade-in animations for menu and overlay
|
|
197
|
+
- **Hover Effects**: Interactive hover states with icon scaling
|
|
198
|
+
- **Destructive Actions**: Special styling for dangerous actions
|
|
199
|
+
- **Icon Support**: Full integration with @umbra-ui/icons
|
|
200
|
+
- **Backdrop Blur**: Context menus include backdrop blur effect
|
|
201
|
+
|
|
202
|
+
### Accessibility
|
|
203
|
+
|
|
204
|
+
- **Keyboard Navigation**: Full keyboard support
|
|
205
|
+
- **Focus Management**: Proper focus handling
|
|
206
|
+
- **Screen Reader Support**: Semantic markup and ARIA attributes
|
|
207
|
+
- **Click Outside**: Closes menu when clicking outside
|
|
208
|
+
|
|
209
|
+
## Examples
|
|
210
|
+
|
|
211
|
+
### Basic Action Menu
|
|
212
|
+
|
|
213
|
+
```vue
|
|
214
|
+
<script setup lang="ts">
|
|
215
|
+
import { ref } from "vue";
|
|
216
|
+
import { ActionMenu, Button } from "@umbra-ui/core";
|
|
217
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
218
|
+
|
|
219
|
+
const isMenuOpen = ref(false);
|
|
220
|
+
|
|
221
|
+
const menuItems: ActionMenuItem[] = [
|
|
222
|
+
{
|
|
223
|
+
icon: "edit",
|
|
224
|
+
title: "Edit Item",
|
|
225
|
+
action: () => {
|
|
226
|
+
console.log("Editing item...");
|
|
227
|
+
isMenuOpen.value = false;
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
icon: "copy",
|
|
232
|
+
title: "Copy Link",
|
|
233
|
+
action: () => {
|
|
234
|
+
console.log("Copying link...");
|
|
235
|
+
isMenuOpen.value = false;
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
icon: "share",
|
|
240
|
+
title: "Share",
|
|
241
|
+
action: () => {
|
|
242
|
+
console.log("Sharing...");
|
|
243
|
+
isMenuOpen.value = false;
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
const handleItemClick = (item: ActionMenuItem) => {
|
|
249
|
+
console.log("Menu item clicked:", item.title);
|
|
250
|
+
};
|
|
251
|
+
</script>
|
|
252
|
+
|
|
253
|
+
<template>
|
|
254
|
+
<div class="basic-menu">
|
|
255
|
+
<h3>Basic Action Menu</h3>
|
|
256
|
+
|
|
257
|
+
<ActionMenu
|
|
258
|
+
:items="menuItems"
|
|
259
|
+
v-model="isMenuOpen"
|
|
260
|
+
@item-click="handleItemClick"
|
|
261
|
+
>
|
|
262
|
+
<Button>Show Actions</Button>
|
|
263
|
+
</ActionMenu>
|
|
264
|
+
|
|
265
|
+
<p class="status">Menu is {{ isMenuOpen ? "open" : "closed" }}</p>
|
|
266
|
+
</div>
|
|
267
|
+
</template>
|
|
268
|
+
|
|
269
|
+
<style module>
|
|
270
|
+
.basic-menu {
|
|
271
|
+
padding: 2rem;
|
|
272
|
+
border: 1px solid #e0e0e0;
|
|
273
|
+
border-radius: 0.5rem;
|
|
274
|
+
max-width: 400px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.status {
|
|
278
|
+
margin-top: 1rem;
|
|
279
|
+
color: #666;
|
|
280
|
+
font-size: 0.875rem;
|
|
281
|
+
}
|
|
282
|
+
</style>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Context Menu
|
|
286
|
+
|
|
287
|
+
```vue
|
|
288
|
+
<script setup lang="ts">
|
|
289
|
+
import { ref } from "vue";
|
|
290
|
+
import { ActionMenu } from "@umbra-ui/core";
|
|
291
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
292
|
+
|
|
293
|
+
const isContextMenuOpen = ref(false);
|
|
294
|
+
|
|
295
|
+
const contextMenuItems: ActionMenuItem[] = [
|
|
296
|
+
{
|
|
297
|
+
icon: "edit",
|
|
298
|
+
title: "Edit",
|
|
299
|
+
action: () => {
|
|
300
|
+
console.log("Edit from context menu");
|
|
301
|
+
isContextMenuOpen.value = false;
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
icon: "copy",
|
|
306
|
+
title: "Copy",
|
|
307
|
+
action: () => {
|
|
308
|
+
console.log("Copy from context menu");
|
|
309
|
+
isContextMenuOpen.value = false;
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
icon: "move",
|
|
314
|
+
title: "Move",
|
|
315
|
+
action: () => {
|
|
316
|
+
console.log("Move from context menu");
|
|
317
|
+
isContextMenuOpen.value = false;
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
icon: "trash",
|
|
322
|
+
title: "Delete",
|
|
323
|
+
action: () => {
|
|
324
|
+
console.log("Delete from context menu");
|
|
325
|
+
isContextMenuOpen.value = false;
|
|
326
|
+
},
|
|
327
|
+
isDestructive: true,
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
const handleContextItemClick = (item: ActionMenuItem) => {
|
|
332
|
+
console.log("Context menu item clicked:", item.title);
|
|
333
|
+
};
|
|
334
|
+
</script>
|
|
335
|
+
|
|
336
|
+
<template>
|
|
337
|
+
<div class="context-menu-demo">
|
|
338
|
+
<h3>Context Menu Demo</h3>
|
|
339
|
+
<p>Right-click on the box below to see the context menu:</p>
|
|
340
|
+
|
|
341
|
+
<ActionMenu
|
|
342
|
+
:items="contextMenuItems"
|
|
343
|
+
v-model="isContextMenuOpen"
|
|
344
|
+
:context-menu="true"
|
|
345
|
+
@item-click="handleContextItemClick"
|
|
346
|
+
>
|
|
347
|
+
<div class="context-target">
|
|
348
|
+
<h4>Right-click me!</h4>
|
|
349
|
+
<p>This box has a context menu attached.</p>
|
|
350
|
+
</div>
|
|
351
|
+
</ActionMenu>
|
|
352
|
+
</div>
|
|
353
|
+
</template>
|
|
354
|
+
|
|
355
|
+
<style module>
|
|
356
|
+
.context-menu-demo {
|
|
357
|
+
padding: 2rem;
|
|
358
|
+
border: 1px solid #e0e0e0;
|
|
359
|
+
border-radius: 0.5rem;
|
|
360
|
+
max-width: 500px;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.context-target {
|
|
364
|
+
padding: 2rem;
|
|
365
|
+
background-color: #f8f9fa;
|
|
366
|
+
border: 2px dashed #dee2e6;
|
|
367
|
+
border-radius: 0.5rem;
|
|
368
|
+
text-align: center;
|
|
369
|
+
cursor: context-menu;
|
|
370
|
+
transition: background-color 0.2s;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.context-target:hover {
|
|
374
|
+
background-color: #e9ecef;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.context-target h4 {
|
|
378
|
+
margin: 0 0 0.5rem 0;
|
|
379
|
+
color: #495057;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.context-target p {
|
|
383
|
+
margin: 0;
|
|
384
|
+
color: #6c757d;
|
|
385
|
+
font-size: 0.875rem;
|
|
386
|
+
}
|
|
387
|
+
</style>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### File Actions Menu
|
|
391
|
+
|
|
392
|
+
```vue
|
|
393
|
+
<script setup lang="ts">
|
|
394
|
+
import { ref } from "vue";
|
|
395
|
+
import { ActionMenu, Button } from "@umbra-ui/core";
|
|
396
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
397
|
+
|
|
398
|
+
interface FileItem {
|
|
399
|
+
id: string;
|
|
400
|
+
name: string;
|
|
401
|
+
type: "file" | "folder";
|
|
402
|
+
size?: string;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const files = ref<FileItem[]>([
|
|
406
|
+
{ id: "1", name: "document.pdf", type: "file", size: "2.3 MB" },
|
|
407
|
+
{ id: "2", name: "images", type: "folder" },
|
|
408
|
+
{ id: "3", name: "report.docx", type: "file", size: "1.1 MB" },
|
|
409
|
+
{ id: "4", name: "data", type: "folder" },
|
|
410
|
+
]);
|
|
411
|
+
|
|
412
|
+
const selectedFile = ref<FileItem | null>(null);
|
|
413
|
+
const isMenuOpen = ref(false);
|
|
414
|
+
|
|
415
|
+
const getFileMenuItems = (file: FileItem): ActionMenuItem[] => {
|
|
416
|
+
const baseItems: ActionMenuItem[] = [
|
|
417
|
+
{
|
|
418
|
+
icon: "download",
|
|
419
|
+
title: "Download",
|
|
420
|
+
action: () => {
|
|
421
|
+
console.log(`Downloading ${file.name}`);
|
|
422
|
+
isMenuOpen.value = false;
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
icon: "copy",
|
|
427
|
+
title: "Copy",
|
|
428
|
+
action: () => {
|
|
429
|
+
console.log(`Copying ${file.name}`);
|
|
430
|
+
isMenuOpen.value = false;
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
icon: "edit",
|
|
435
|
+
title: "Rename",
|
|
436
|
+
action: () => {
|
|
437
|
+
console.log(`Renaming ${file.name}`);
|
|
438
|
+
isMenuOpen.value = false;
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
|
|
443
|
+
if (file.type === "file") {
|
|
444
|
+
baseItems.push({
|
|
445
|
+
icon: "share",
|
|
446
|
+
title: "Share",
|
|
447
|
+
action: () => {
|
|
448
|
+
console.log(`Sharing ${file.name}`);
|
|
449
|
+
isMenuOpen.value = false;
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
baseItems.push({
|
|
455
|
+
icon: "trash",
|
|
456
|
+
title: "Delete",
|
|
457
|
+
action: () => {
|
|
458
|
+
console.log(`Deleting ${file.name}`);
|
|
459
|
+
isMenuOpen.value = false;
|
|
460
|
+
},
|
|
461
|
+
isDestructive: true,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
return baseItems;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const handleFileClick = (file: FileItem) => {
|
|
468
|
+
selectedFile.value = file;
|
|
469
|
+
isMenuOpen.value = true;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const handleItemClick = (item: ActionMenuItem) => {
|
|
473
|
+
console.log("File action:", item.title);
|
|
474
|
+
};
|
|
475
|
+
</script>
|
|
476
|
+
|
|
477
|
+
<template>
|
|
478
|
+
<div class="file-actions">
|
|
479
|
+
<h3>File Actions Menu</h3>
|
|
480
|
+
<p>Click on a file to see its action menu:</p>
|
|
481
|
+
|
|
482
|
+
<div class="file-list">
|
|
483
|
+
<div
|
|
484
|
+
v-for="file in files"
|
|
485
|
+
:key="file.id"
|
|
486
|
+
class="file-item"
|
|
487
|
+
@click="handleFileClick(file)"
|
|
488
|
+
>
|
|
489
|
+
<div class="file-icon">
|
|
490
|
+
{{ file.type === "folder" ? "📁" : "📄" }}
|
|
491
|
+
</div>
|
|
492
|
+
<div class="file-info">
|
|
493
|
+
<div class="file-name">{{ file.name }}</div>
|
|
494
|
+
<div v-if="file.size" class="file-size">{{ file.size }}</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
<ActionMenu
|
|
500
|
+
v-if="selectedFile"
|
|
501
|
+
:items="getFileMenuItems(selectedFile)"
|
|
502
|
+
v-model="isMenuOpen"
|
|
503
|
+
@item-click="handleItemClick"
|
|
504
|
+
>
|
|
505
|
+
<div style="display: none;"></div>
|
|
506
|
+
</ActionMenu>
|
|
507
|
+
</div>
|
|
508
|
+
</template>
|
|
509
|
+
|
|
510
|
+
<style module>
|
|
511
|
+
.file-actions {
|
|
512
|
+
padding: 2rem;
|
|
513
|
+
border: 1px solid #e0e0e0;
|
|
514
|
+
border-radius: 0.5rem;
|
|
515
|
+
max-width: 500px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.file-list {
|
|
519
|
+
margin-top: 1rem;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.file-item {
|
|
523
|
+
display: flex;
|
|
524
|
+
align-items: center;
|
|
525
|
+
gap: 0.75rem;
|
|
526
|
+
padding: 0.75rem;
|
|
527
|
+
border: 1px solid #e0e0e0;
|
|
528
|
+
border-radius: 0.25rem;
|
|
529
|
+
margin-bottom: 0.5rem;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
transition: background-color 0.2s;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.file-item:hover {
|
|
535
|
+
background-color: #f8f9fa;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.file-icon {
|
|
539
|
+
font-size: 1.5rem;
|
|
540
|
+
width: 2rem;
|
|
541
|
+
text-align: center;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.file-info {
|
|
545
|
+
flex: 1;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.file-name {
|
|
549
|
+
font-weight: 500;
|
|
550
|
+
color: #333;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.file-size {
|
|
554
|
+
font-size: 0.875rem;
|
|
555
|
+
color: #666;
|
|
556
|
+
margin-top: 0.25rem;
|
|
557
|
+
}
|
|
558
|
+
</style>
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### User Profile Menu
|
|
562
|
+
|
|
563
|
+
```vue
|
|
564
|
+
<script setup lang="ts">
|
|
565
|
+
import { ref } from "vue";
|
|
566
|
+
import { ActionMenu, Button } from "@umbra-ui/core";
|
|
567
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
568
|
+
|
|
569
|
+
const user = ref({
|
|
570
|
+
name: "John Doe",
|
|
571
|
+
email: "john@example.com",
|
|
572
|
+
avatar: "👤",
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const isProfileMenuOpen = ref(false);
|
|
576
|
+
|
|
577
|
+
const profileMenuItems: ActionMenuItem[] = [
|
|
578
|
+
{
|
|
579
|
+
icon: "user",
|
|
580
|
+
title: "View Profile",
|
|
581
|
+
action: () => {
|
|
582
|
+
console.log("Viewing profile...");
|
|
583
|
+
isProfileMenuOpen.value = false;
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
icon: "settings",
|
|
588
|
+
title: "Settings",
|
|
589
|
+
action: () => {
|
|
590
|
+
console.log("Opening settings...");
|
|
591
|
+
isProfileMenuOpen.value = false;
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
icon: "bell",
|
|
596
|
+
title: "Notifications",
|
|
597
|
+
action: () => {
|
|
598
|
+
console.log("Opening notifications...");
|
|
599
|
+
isProfileMenuOpen.value = false;
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
icon: "help",
|
|
604
|
+
title: "Help & Support",
|
|
605
|
+
action: () => {
|
|
606
|
+
console.log("Opening help...");
|
|
607
|
+
isProfileMenuOpen.value = false;
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
icon: "logout",
|
|
612
|
+
title: "Sign Out",
|
|
613
|
+
action: () => {
|
|
614
|
+
console.log("Signing out...");
|
|
615
|
+
isProfileMenuOpen.value = false;
|
|
616
|
+
},
|
|
617
|
+
isDestructive: true,
|
|
618
|
+
},
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
const handleProfileItemClick = (item: ActionMenuItem) => {
|
|
622
|
+
console.log("Profile menu item clicked:", item.title);
|
|
623
|
+
};
|
|
624
|
+
</script>
|
|
625
|
+
|
|
626
|
+
<template>
|
|
627
|
+
<div class="profile-menu">
|
|
628
|
+
<h3>User Profile Menu</h3>
|
|
629
|
+
|
|
630
|
+
<div class="user-info">
|
|
631
|
+
<div class="user-avatar">{{ user.avatar }}</div>
|
|
632
|
+
<div class="user-details">
|
|
633
|
+
<div class="user-name">{{ user.name }}</div>
|
|
634
|
+
<div class="user-email">{{ user.email }}</div>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
|
|
638
|
+
<ActionMenu
|
|
639
|
+
:items="profileMenuItems"
|
|
640
|
+
v-model="isProfileMenuOpen"
|
|
641
|
+
@item-click="handleProfileItemClick"
|
|
642
|
+
>
|
|
643
|
+
<Button class="profile-button">
|
|
644
|
+
<span class="profile-avatar">{{ user.avatar }}</span>
|
|
645
|
+
<span>{{ user.name }}</span>
|
|
646
|
+
<span class="dropdown-arrow">▼</span>
|
|
647
|
+
</Button>
|
|
648
|
+
</ActionMenu>
|
|
649
|
+
</div>
|
|
650
|
+
</template>
|
|
651
|
+
|
|
652
|
+
<style module>
|
|
653
|
+
.profile-menu {
|
|
654
|
+
padding: 2rem;
|
|
655
|
+
border: 1px solid #e0e0e0;
|
|
656
|
+
border-radius: 0.5rem;
|
|
657
|
+
max-width: 400px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.user-info {
|
|
661
|
+
display: flex;
|
|
662
|
+
align-items: center;
|
|
663
|
+
gap: 1rem;
|
|
664
|
+
margin-bottom: 1.5rem;
|
|
665
|
+
padding: 1rem;
|
|
666
|
+
background-color: #f8f9fa;
|
|
667
|
+
border-radius: 0.5rem;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.user-avatar {
|
|
671
|
+
font-size: 2rem;
|
|
672
|
+
width: 3rem;
|
|
673
|
+
height: 3rem;
|
|
674
|
+
display: flex;
|
|
675
|
+
align-items: center;
|
|
676
|
+
justify-content: center;
|
|
677
|
+
background-color: #e9ecef;
|
|
678
|
+
border-radius: 50%;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.user-details {
|
|
682
|
+
flex: 1;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.user-name {
|
|
686
|
+
font-weight: 600;
|
|
687
|
+
color: #333;
|
|
688
|
+
margin-bottom: 0.25rem;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.user-email {
|
|
692
|
+
color: #666;
|
|
693
|
+
font-size: 0.875rem;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.profile-button {
|
|
697
|
+
display: flex;
|
|
698
|
+
align-items: center;
|
|
699
|
+
gap: 0.5rem;
|
|
700
|
+
width: 100%;
|
|
701
|
+
justify-content: space-between;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.profile-avatar {
|
|
705
|
+
font-size: 1.25rem;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
.dropdown-arrow {
|
|
709
|
+
font-size: 0.75rem;
|
|
710
|
+
opacity: 0.6;
|
|
711
|
+
}
|
|
712
|
+
</style>
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Custom Styled Menu
|
|
716
|
+
|
|
717
|
+
```vue
|
|
718
|
+
<script setup lang="ts">
|
|
719
|
+
import { ref } from "vue";
|
|
720
|
+
import { ActionMenu, Button } from "@umbra-ui/core";
|
|
721
|
+
import type { ActionMenuItem } from "@umbra-ui/core";
|
|
722
|
+
|
|
723
|
+
const isCustomMenuOpen = ref(false);
|
|
724
|
+
|
|
725
|
+
const customMenuItems: ActionMenuItem[] = [
|
|
726
|
+
{
|
|
727
|
+
icon: "star",
|
|
728
|
+
title: "Add to Favorites",
|
|
729
|
+
action: () => {
|
|
730
|
+
console.log("Added to favorites");
|
|
731
|
+
isCustomMenuOpen.value = false;
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
icon: "bookmark",
|
|
736
|
+
title: "Bookmark",
|
|
737
|
+
action: () => {
|
|
738
|
+
console.log("Bookmarked");
|
|
739
|
+
isCustomMenuOpen.value = false;
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
icon: "flag",
|
|
744
|
+
title: "Report",
|
|
745
|
+
action: () => {
|
|
746
|
+
console.log("Reported");
|
|
747
|
+
isCustomMenuOpen.value = false;
|
|
748
|
+
},
|
|
749
|
+
isDestructive: true,
|
|
750
|
+
},
|
|
751
|
+
];
|
|
752
|
+
|
|
753
|
+
const handleCustomItemClick = (item: ActionMenuItem) => {
|
|
754
|
+
console.log("Custom menu item clicked:", item.title);
|
|
755
|
+
};
|
|
756
|
+
</script>
|
|
757
|
+
|
|
758
|
+
<template>
|
|
759
|
+
<div class="custom-styled-menu">
|
|
760
|
+
<h3>Custom Styled Action Menu</h3>
|
|
761
|
+
|
|
762
|
+
<ActionMenu
|
|
763
|
+
:items="customMenuItems"
|
|
764
|
+
v-model="isCustomMenuOpen"
|
|
765
|
+
@item-click="handleCustomItemClick"
|
|
766
|
+
>
|
|
767
|
+
<Button class="custom-trigger">Custom Menu</Button>
|
|
768
|
+
</ActionMenu>
|
|
769
|
+
</div>
|
|
770
|
+
</template>
|
|
771
|
+
|
|
772
|
+
<style module>
|
|
773
|
+
.custom-styled-menu {
|
|
774
|
+
padding: 2rem;
|
|
775
|
+
border: 1px solid #e0e0e0;
|
|
776
|
+
border-radius: 0.5rem;
|
|
777
|
+
max-width: 400px;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.custom-trigger {
|
|
781
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
782
|
+
color: white;
|
|
783
|
+
border: none;
|
|
784
|
+
padding: 0.75rem 1.5rem;
|
|
785
|
+
border-radius: 0.5rem;
|
|
786
|
+
font-weight: 600;
|
|
787
|
+
transition: transform 0.2s;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.custom-trigger:hover {
|
|
791
|
+
transform: translateY(-2px);
|
|
792
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/* Custom CSS variables for the menu */
|
|
796
|
+
:global(.custom-styled-menu) {
|
|
797
|
+
--actionmenu-popup-bg: #ffffff;
|
|
798
|
+
--actionmenu-popup-border: 2px solid #667eea;
|
|
799
|
+
--actionmenu-popup-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
800
|
+
|
|
801
|
+
--actionmenu-item-hover-bg: rgba(102, 126, 234, 0.1);
|
|
802
|
+
--actionmenu-item-text: #333333;
|
|
803
|
+
--actionmenu-item-icon: #667eea;
|
|
804
|
+
|
|
805
|
+
--actionmenu-destructive-text: #e74c3c;
|
|
806
|
+
--actionmenu-destructive-icon: #e74c3c;
|
|
807
|
+
|
|
808
|
+
--actionmenu-overlay-bg: rgba(102, 126, 234, 0.2);
|
|
809
|
+
}
|
|
810
|
+
</style>
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
## Technical Notes
|
|
814
|
+
|
|
815
|
+
- ActionMenu provides a flexible dropdown menu system with both click and context menu support
|
|
816
|
+
- Floating UI integration ensures smart positioning that adapts to viewport constraints
|
|
817
|
+
- Context menus include backdrop blur effects for better visual separation
|
|
818
|
+
- Destructive actions are visually distinguished with red coloring
|
|
819
|
+
- Full icon integration with @umbra-ui/icons for consistent visual language
|
|
820
|
+
- Smooth animations and hover effects enhance user experience
|
|
821
|
+
- TypeScript interfaces ensure type safety for menu items and actions
|
|
822
|
+
- CSS custom properties enable easy theming and customization
|
|
823
|
+
- Teleport usage ensures proper z-index layering and positioning
|
|
824
|
+
- Auto-cleanup of positioning listeners prevents memory leaks
|
|
825
|
+
- Responsive design adapts to different screen sizes and orientations
|