@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,897 @@
|
|
|
1
|
+
# Tags Components
|
|
2
|
+
|
|
3
|
+
A comprehensive tag management system built with Vue 3 Composition API and TypeScript. The Tags components provide a powerful interface for creating, selecting, and managing tags with advanced features like real-time search, keyboard navigation, custom styling, and multiple overflow modes.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { TagBar, TagPicker, TagCreation } from "@umbra-ui/core";
|
|
9
|
+
import type { TagItem, TagStyleConfig } from "@umbra-ui/core";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Dependencies:**
|
|
13
|
+
|
|
14
|
+
- Vue 3.x
|
|
15
|
+
- @umbra-ui/core (for IconButton and ColorPicker components)
|
|
16
|
+
- @umbra-ui/colors (for color schemes)
|
|
17
|
+
- @floating-ui/vue (for positioning)
|
|
18
|
+
- gsap (for animations)
|
|
19
|
+
- tinycolor2 (for color manipulation)
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
```vue
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { ref } from "vue";
|
|
26
|
+
import { TagBar } from "@umbra-ui/core";
|
|
27
|
+
import type { TagItem } from "@umbra-ui/core";
|
|
28
|
+
|
|
29
|
+
const allTags = ref<TagItem[]>([
|
|
30
|
+
{ id: "1", title: "Vue.js" },
|
|
31
|
+
{ id: "2", title: "TypeScript" },
|
|
32
|
+
{ id: "3", title: "Frontend" },
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const selectedTags = ref<TagItem[]>([]);
|
|
36
|
+
const query = ref("");
|
|
37
|
+
const matchIndex = ref(-1);
|
|
38
|
+
|
|
39
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
40
|
+
selectedTags.value.push(tag);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
44
|
+
const index = selectedTags.value.findIndex((t) => t.id === tag.id);
|
|
45
|
+
if (index > -1) {
|
|
46
|
+
selectedTags.value.splice(index, 1);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const handleTagCreate = (title: string) => {
|
|
51
|
+
const newTag: TagItem = {
|
|
52
|
+
id: Date.now().toString(),
|
|
53
|
+
title: title.trim(),
|
|
54
|
+
};
|
|
55
|
+
allTags.value.push(newTag);
|
|
56
|
+
selectedTags.value.push(newTag);
|
|
57
|
+
};
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<div>
|
|
62
|
+
<TagBar
|
|
63
|
+
:all-tags="allTags"
|
|
64
|
+
:selected-tags="selectedTags"
|
|
65
|
+
v-model:query="query"
|
|
66
|
+
v-model:match-index="matchIndex"
|
|
67
|
+
placeholder="Add tags..."
|
|
68
|
+
overflow="fixed"
|
|
69
|
+
:allow-editing="true"
|
|
70
|
+
@tag-select="handleTagSelect"
|
|
71
|
+
@tag-remove="handleTagRemove"
|
|
72
|
+
@tag-create="handleTagCreate"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
</template>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Components
|
|
79
|
+
|
|
80
|
+
### TagBar
|
|
81
|
+
|
|
82
|
+
The main tag management component with input field and tag display.
|
|
83
|
+
|
|
84
|
+
#### Props
|
|
85
|
+
|
|
86
|
+
| Prop Name | Type | Required | Default | Description |
|
|
87
|
+
| -------------- | ----------------------------- | -------- | ------------ | -------------------------------------------- |
|
|
88
|
+
| `allTags` | `TagItem[]` | No | `[]` | Array of all available tags |
|
|
89
|
+
| `selectedTags` | `TagItem[]` | No | `[]` | Array of currently selected tags |
|
|
90
|
+
| `query` | `string` | No | `""` | Current search query |
|
|
91
|
+
| `matchIndex` | `number` | No | `-1` | Index of currently highlighted match |
|
|
92
|
+
| `placeholder` | `string` | No | `"ADD TAGS"` | Placeholder text for the input field |
|
|
93
|
+
| `overflow` | `"full" \| "fixed" \| "list"` | No | `"fixed"` | How tags overflow when they exceed container |
|
|
94
|
+
| `allowEditing` | `boolean` | No | `true` | Whether tags can be edited/removed |
|
|
95
|
+
|
|
96
|
+
#### Events
|
|
97
|
+
|
|
98
|
+
| Event Name | Payload Type | Description |
|
|
99
|
+
| ------------------- | ------------ | ---------------------------------------- |
|
|
100
|
+
| `update:query` | `string` | Emitted when the search query changes |
|
|
101
|
+
| `update:matchIndex` | `number` | Emitted when the match index changes |
|
|
102
|
+
| `tag-create` | `string` | Emitted when a new tag should be created |
|
|
103
|
+
| `tag-select` | `TagItem` | Emitted when a tag is selected |
|
|
104
|
+
| `tag-remove` | `TagItem` | Emitted when a tag is removed |
|
|
105
|
+
| `tag-reorder` | `TagItem[]` | Emitted when tags are reordered |
|
|
106
|
+
|
|
107
|
+
### TagPicker
|
|
108
|
+
|
|
109
|
+
The dropdown picker component for selecting tags (used internally by TagBar).
|
|
110
|
+
|
|
111
|
+
#### Props
|
|
112
|
+
|
|
113
|
+
| Prop Name | Type | Required | Description |
|
|
114
|
+
| -------------- | ----------- | -------- | ------------------------------------ |
|
|
115
|
+
| `allTags` | `TagItem[]` | Yes | Array of all available tags |
|
|
116
|
+
| `selectedTags` | `TagItem[]` | Yes | Array of currently selected tags |
|
|
117
|
+
| `query` | `string` | Yes | Current search query |
|
|
118
|
+
| `matchIndex` | `number` | Yes | Index of currently highlighted match |
|
|
119
|
+
| `canCreateTag` | `boolean` | No | Whether a new tag can be created |
|
|
120
|
+
|
|
121
|
+
#### Events
|
|
122
|
+
|
|
123
|
+
| Event Name | Payload Type | Description |
|
|
124
|
+
| ------------------- | ------------ | ---------------------------------------- |
|
|
125
|
+
| `tag-select` | `TagItem` | Emitted when a tag is selected |
|
|
126
|
+
| `tag-create` | `string` | Emitted when a new tag should be created |
|
|
127
|
+
| `update:query` | `string` | Emitted when the search query changes |
|
|
128
|
+
| `update:matchIndex` | `number` | Emitted when the match index changes |
|
|
129
|
+
| `quit` | `void` | Emitted when the picker should be closed |
|
|
130
|
+
|
|
131
|
+
### TagCreation
|
|
132
|
+
|
|
133
|
+
A component for creating new tags with color selection.
|
|
134
|
+
|
|
135
|
+
#### Props
|
|
136
|
+
|
|
137
|
+
| Prop Name | Type | Required | Default | Description |
|
|
138
|
+
| ---------------- | ---------- | -------- | ------- | ------------------------------------------- |
|
|
139
|
+
| `containerID` | `string` | Yes | - | Unique ID for the container |
|
|
140
|
+
| `initialColor` | `Color` | No | Gray | Initial color for the new tag |
|
|
141
|
+
| `existingTitles` | `string[]` | No | `[]` | Array of existing tag titles for validation |
|
|
142
|
+
|
|
143
|
+
#### Events
|
|
144
|
+
|
|
145
|
+
| Event Name | Payload Type | Description |
|
|
146
|
+
| ---------- | --------------- | -------------------------------------------- |
|
|
147
|
+
| `onCreate` | `string, Color` | Emitted when a tag is created (title, color) |
|
|
148
|
+
|
|
149
|
+
## TagItem Interface
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface TagItem {
|
|
153
|
+
id: string;
|
|
154
|
+
title: string;
|
|
155
|
+
style?: TagStyleConfig;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## TagStyleConfig Interface
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
interface TagStyleConfig {
|
|
163
|
+
// Direct CSS properties for full control
|
|
164
|
+
backgroundColor?: string;
|
|
165
|
+
color?: string;
|
|
166
|
+
border?: string;
|
|
167
|
+
borderRadius?: string;
|
|
168
|
+
padding?: string;
|
|
169
|
+
fontSize?: string;
|
|
170
|
+
fontWeight?: string;
|
|
171
|
+
boxShadow?: string;
|
|
172
|
+
|
|
173
|
+
// Optional hover state
|
|
174
|
+
hover?: {
|
|
175
|
+
backgroundColor?: string;
|
|
176
|
+
color?: string;
|
|
177
|
+
border?: string;
|
|
178
|
+
transform?: string;
|
|
179
|
+
boxShadow?: string;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Helper properties for convenience
|
|
183
|
+
size?: "sm" | "md" | "lg";
|
|
184
|
+
colorScheme?: string; // Radix color name or hex
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## CSS Customization
|
|
189
|
+
|
|
190
|
+
The Tags components use CSS custom properties for theming and customization:
|
|
191
|
+
|
|
192
|
+
### TagBar Variables
|
|
193
|
+
|
|
194
|
+
```css
|
|
195
|
+
/* TagBar Container */
|
|
196
|
+
--tagbar-bg: var(--tagbar-background);
|
|
197
|
+
--tagbar-border: var(--tagbar-border-color);
|
|
198
|
+
--tagbar-text: var(--tagbar-text-color);
|
|
199
|
+
|
|
200
|
+
/* TagBar Field */
|
|
201
|
+
--tagbar-field-bg: var(--tagbar-field-background);
|
|
202
|
+
--tagbar-field-text: var(--tagbar-field-text-color);
|
|
203
|
+
|
|
204
|
+
/* TagBar List */
|
|
205
|
+
--tagbar-list-border: var(--tagbar-list-border-color);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### TagPicker Variables
|
|
209
|
+
|
|
210
|
+
```css
|
|
211
|
+
/* TagPicker Container */
|
|
212
|
+
--tagpicker-container-bg: var(--tagpicker-container-background);
|
|
213
|
+
--tagpicker-container-shadow: var(--tagpicker-container-shadow-color);
|
|
214
|
+
--tagpicker-container-inset-shadow: var(
|
|
215
|
+
--tagpicker-container-inset-shadow-color
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
/* TagPicker Subheader */
|
|
219
|
+
--tagpicker-subheader-bg: var(--tagpicker-subheader-background);
|
|
220
|
+
--tagpicker-subheader-border: var(--tagpicker-subheader-border-color);
|
|
221
|
+
--tagpicker-subheader-text: var(--tagpicker-subheader-text-color);
|
|
222
|
+
--tagpicker-subheader-label: var(--tagpicker-subheader-label-color);
|
|
223
|
+
|
|
224
|
+
/* TagPicker Tags */
|
|
225
|
+
--tagpicker-tag-border: var(--tagpicker-tag-border-color);
|
|
226
|
+
--tagpicker-tag-text: var(--tagpicker-tag-text-color);
|
|
227
|
+
--tagpicker-tag-hover-bg: var(--tagpicker-tag-hover-background);
|
|
228
|
+
--tagpicker-tag-selected-bg: var(--tagpicker-tag-selected-background);
|
|
229
|
+
|
|
230
|
+
/* TagPicker Create */
|
|
231
|
+
--tagpicker-create-bg: var(--tagpicker-create-background);
|
|
232
|
+
--tagpicker-create-hover-bg: var(--tagpicker-create-hover-background);
|
|
233
|
+
--tagpicker-create-selected-bg: var(--tagpicker-create-selected-background);
|
|
234
|
+
--tagpicker-create-border: var(--tagpicker-create-border-color);
|
|
235
|
+
--tagpicker-create-text: var(--tagpicker-create-text-color);
|
|
236
|
+
--tagpicker-create-error-text: var(--tagpicker-create-error-text-color);
|
|
237
|
+
|
|
238
|
+
/* TagPicker Empty State */
|
|
239
|
+
--tagpicker-empty-text: var(--tagpicker-empty-text-color);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Key Features
|
|
243
|
+
|
|
244
|
+
### Tag Management
|
|
245
|
+
|
|
246
|
+
- **Real-time Search**: Filter tags as you type with instant results
|
|
247
|
+
- **Keyboard Navigation**: Full keyboard support with arrow keys and Enter
|
|
248
|
+
- **Tag Creation**: Create new tags on-the-fly with custom colors
|
|
249
|
+
- **Tag Removal**: Click tags to remove them from selection
|
|
250
|
+
- **Duplicate Prevention**: Prevents adding duplicate tags
|
|
251
|
+
|
|
252
|
+
### Overflow Modes
|
|
253
|
+
|
|
254
|
+
- **Full**: Horizontal scroll with hidden scrollbar
|
|
255
|
+
- **Fixed**: Tags wrap to multiple lines
|
|
256
|
+
- **List**: Each tag on its own row with borders
|
|
257
|
+
|
|
258
|
+
### Styling System
|
|
259
|
+
|
|
260
|
+
- **Custom Styles**: Full CSS property control for each tag
|
|
261
|
+
- **Color Schemes**: Support for Radix colors and custom hex colors
|
|
262
|
+
- **Size Presets**: Small, medium, and large size options
|
|
263
|
+
- **Hover Effects**: Customizable hover states
|
|
264
|
+
- **Theme Support**: Light and dark mode compatibility
|
|
265
|
+
|
|
266
|
+
### User Experience
|
|
267
|
+
|
|
268
|
+
- **Smooth Animations**: GSAP-powered transitions and animations
|
|
269
|
+
- **Floating Positioning**: Smart positioning using Floating UI
|
|
270
|
+
- **Responsive Design**: Adapts to different screen sizes
|
|
271
|
+
- **Accessibility**: Full keyboard navigation and screen reader support
|
|
272
|
+
|
|
273
|
+
## Examples
|
|
274
|
+
|
|
275
|
+
### Basic Tag Management
|
|
276
|
+
|
|
277
|
+
```vue
|
|
278
|
+
<script setup lang="ts">
|
|
279
|
+
import { ref } from "vue";
|
|
280
|
+
import { TagBar } from "@umbra-ui/core";
|
|
281
|
+
import type { TagItem } from "@umbra-ui/core";
|
|
282
|
+
|
|
283
|
+
const allTags = ref<TagItem[]>([
|
|
284
|
+
{ id: "1", title: "JavaScript" },
|
|
285
|
+
{ id: "2", title: "Vue.js" },
|
|
286
|
+
{ id: "3", title: "TypeScript" },
|
|
287
|
+
{ id: "4", title: "Frontend" },
|
|
288
|
+
{ id: "5", title: "Backend" },
|
|
289
|
+
{ id: "6", title: "Full Stack" },
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
const selectedTags = ref<TagItem[]>([]);
|
|
293
|
+
const query = ref("");
|
|
294
|
+
const matchIndex = ref(-1);
|
|
295
|
+
|
|
296
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
297
|
+
selectedTags.value.push(tag);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
301
|
+
const index = selectedTags.value.findIndex((t) => t.id === tag.id);
|
|
302
|
+
if (index > -1) {
|
|
303
|
+
selectedTags.value.splice(index, 1);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const handleTagCreate = (title: string) => {
|
|
308
|
+
const newTag: TagItem = {
|
|
309
|
+
id: Date.now().toString(),
|
|
310
|
+
title: title.trim(),
|
|
311
|
+
};
|
|
312
|
+
allTags.value.push(newTag);
|
|
313
|
+
selectedTags.value.push(newTag);
|
|
314
|
+
};
|
|
315
|
+
</script>
|
|
316
|
+
|
|
317
|
+
<template>
|
|
318
|
+
<div class="basic-tags">
|
|
319
|
+
<h3>Basic Tag Management</h3>
|
|
320
|
+
|
|
321
|
+
<TagBar
|
|
322
|
+
:all-tags="allTags"
|
|
323
|
+
:selected-tags="selectedTags"
|
|
324
|
+
v-model:query="query"
|
|
325
|
+
v-model:match-index="matchIndex"
|
|
326
|
+
placeholder="Add skills..."
|
|
327
|
+
overflow="fixed"
|
|
328
|
+
:allow-editing="true"
|
|
329
|
+
@tag-select="handleTagSelect"
|
|
330
|
+
@tag-remove="handleTagRemove"
|
|
331
|
+
@tag-create="handleTagCreate"
|
|
332
|
+
/>
|
|
333
|
+
|
|
334
|
+
<div v-if="selectedTags.length > 0" class="selected-tags">
|
|
335
|
+
<h4>Selected Tags ({{ selectedTags.length }})</h4>
|
|
336
|
+
<ul>
|
|
337
|
+
<li v-for="tag in selectedTags" :key="tag.id">
|
|
338
|
+
{{ tag.title }}
|
|
339
|
+
</li>
|
|
340
|
+
</ul>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</template>
|
|
344
|
+
|
|
345
|
+
<style module>
|
|
346
|
+
.basic-tags {
|
|
347
|
+
max-width: 600px;
|
|
348
|
+
padding: 2rem;
|
|
349
|
+
border: 1px solid #e0e0e0;
|
|
350
|
+
border-radius: 0.5rem;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.selected-tags {
|
|
354
|
+
margin-top: 1.5rem;
|
|
355
|
+
padding: 1rem;
|
|
356
|
+
background-color: #f8f9fa;
|
|
357
|
+
border-radius: 0.25rem;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.selected-tags h4 {
|
|
361
|
+
margin: 0 0 0.5rem 0;
|
|
362
|
+
color: #333;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.selected-tags ul {
|
|
366
|
+
margin: 0;
|
|
367
|
+
padding-left: 1.5rem;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.selected-tags li {
|
|
371
|
+
margin-bottom: 0.25rem;
|
|
372
|
+
color: #666;
|
|
373
|
+
}
|
|
374
|
+
</style>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Styled Tags with Custom Colors
|
|
378
|
+
|
|
379
|
+
```vue
|
|
380
|
+
<script setup lang="ts">
|
|
381
|
+
import { ref } from "vue";
|
|
382
|
+
import { TagBar, createStyledTag } from "@umbra-ui/core";
|
|
383
|
+
import type { TagItem, TagStyleConfig } from "@umbra-ui/core";
|
|
384
|
+
|
|
385
|
+
const allTags = ref<TagItem[]>([
|
|
386
|
+
createStyledTag("1", "Frontend", { colorScheme: "blue", size: "md" }),
|
|
387
|
+
createStyledTag("2", "Backend", { colorScheme: "green", size: "md" }),
|
|
388
|
+
createStyledTag("3", "Database", { colorScheme: "orange", size: "md" }),
|
|
389
|
+
createStyledTag("4", "DevOps", { colorScheme: "purple", size: "md" }),
|
|
390
|
+
createStyledTag("5", "UI/UX", { colorScheme: "pink", size: "md" }),
|
|
391
|
+
createStyledTag("6", "Mobile", { colorScheme: "cyan", size: "md" }),
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
const selectedTags = ref<TagItem[]>([]);
|
|
395
|
+
const query = ref("");
|
|
396
|
+
const matchIndex = ref(-1);
|
|
397
|
+
|
|
398
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
399
|
+
selectedTags.value.push(tag);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
403
|
+
const index = selectedTags.value.findIndex((t) => t.id === tag.id);
|
|
404
|
+
if (index > -1) {
|
|
405
|
+
selectedTags.value.splice(index, 1);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const handleTagCreate = (title: string) => {
|
|
410
|
+
const newTag = createStyledTag(Date.now().toString(), title.trim(), {
|
|
411
|
+
colorScheme: "gray",
|
|
412
|
+
size: "md",
|
|
413
|
+
});
|
|
414
|
+
allTags.value.push(newTag);
|
|
415
|
+
selectedTags.value.push(newTag);
|
|
416
|
+
};
|
|
417
|
+
</script>
|
|
418
|
+
|
|
419
|
+
<template>
|
|
420
|
+
<div class="styled-tags">
|
|
421
|
+
<h3>Styled Tags with Custom Colors</h3>
|
|
422
|
+
|
|
423
|
+
<TagBar
|
|
424
|
+
:all-tags="allTags"
|
|
425
|
+
:selected-tags="selectedTags"
|
|
426
|
+
v-model:query="query"
|
|
427
|
+
v-model:match-index="matchIndex"
|
|
428
|
+
placeholder="Add technology tags..."
|
|
429
|
+
overflow="full"
|
|
430
|
+
:allow-editing="true"
|
|
431
|
+
@tag-select="handleTagSelect"
|
|
432
|
+
@tag-remove="handleTagRemove"
|
|
433
|
+
@tag-create="handleTagCreate"
|
|
434
|
+
/>
|
|
435
|
+
|
|
436
|
+
<div class="tag-info">
|
|
437
|
+
<p>Available tags: {{ allTags.length }}</p>
|
|
438
|
+
<p>Selected tags: {{ selectedTags.length }}</p>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
</template>
|
|
442
|
+
|
|
443
|
+
<style module>
|
|
444
|
+
.styled-tags {
|
|
445
|
+
max-width: 600px;
|
|
446
|
+
padding: 2rem;
|
|
447
|
+
border: 1px solid #e0e0e0;
|
|
448
|
+
border-radius: 0.5rem;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.tag-info {
|
|
452
|
+
margin-top: 1rem;
|
|
453
|
+
display: flex;
|
|
454
|
+
gap: 2rem;
|
|
455
|
+
color: #666;
|
|
456
|
+
font-size: 0.875rem;
|
|
457
|
+
}
|
|
458
|
+
</style>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Form Integration
|
|
462
|
+
|
|
463
|
+
```vue
|
|
464
|
+
<script setup lang="ts">
|
|
465
|
+
import { ref, computed } from "vue";
|
|
466
|
+
import { TagBar } from "@umbra-ui/core";
|
|
467
|
+
import type { TagItem } from "@umbra-ui/core";
|
|
468
|
+
|
|
469
|
+
interface ProjectForm {
|
|
470
|
+
title: string;
|
|
471
|
+
description: string;
|
|
472
|
+
tags: TagItem[];
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const form = ref<ProjectForm>({
|
|
476
|
+
title: "",
|
|
477
|
+
description: "",
|
|
478
|
+
tags: [],
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const allTags = ref<TagItem[]>([
|
|
482
|
+
{ id: "1", title: "Web Development" },
|
|
483
|
+
{ id: "2", title: "Mobile App" },
|
|
484
|
+
{ id: "3", title: "API" },
|
|
485
|
+
{ id: "4", title: "Database" },
|
|
486
|
+
{ id: "5", title: "UI/UX" },
|
|
487
|
+
{ id: "6", title: "Testing" },
|
|
488
|
+
]);
|
|
489
|
+
|
|
490
|
+
const query = ref("");
|
|
491
|
+
const matchIndex = ref(-1);
|
|
492
|
+
|
|
493
|
+
const isFormValid = computed(() => {
|
|
494
|
+
return (
|
|
495
|
+
form.value.title && form.value.description && form.value.tags.length > 0
|
|
496
|
+
);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
500
|
+
form.value.tags.push(tag);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
504
|
+
const index = form.value.tags.findIndex((t) => t.id === tag.id);
|
|
505
|
+
if (index > -1) {
|
|
506
|
+
form.value.tags.splice(index, 1);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const handleTagCreate = (title: string) => {
|
|
511
|
+
const newTag: TagItem = {
|
|
512
|
+
id: Date.now().toString(),
|
|
513
|
+
title: title.trim(),
|
|
514
|
+
};
|
|
515
|
+
allTags.value.push(newTag);
|
|
516
|
+
form.value.tags.push(newTag);
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const handleSubmit = () => {
|
|
520
|
+
if (isFormValid.value) {
|
|
521
|
+
console.log("Project created:", form.value);
|
|
522
|
+
// Handle form submission
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
</script>
|
|
526
|
+
|
|
527
|
+
<template>
|
|
528
|
+
<div class="form-integration">
|
|
529
|
+
<h3>Create New Project</h3>
|
|
530
|
+
|
|
531
|
+
<form @submit.prevent="handleSubmit">
|
|
532
|
+
<div class="form-group">
|
|
533
|
+
<label>Project Title *</label>
|
|
534
|
+
<input
|
|
535
|
+
v-model="form.title"
|
|
536
|
+
type="text"
|
|
537
|
+
placeholder="Enter project title"
|
|
538
|
+
required
|
|
539
|
+
/>
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
<div class="form-group">
|
|
543
|
+
<label>Description *</label>
|
|
544
|
+
<textarea
|
|
545
|
+
v-model="form.description"
|
|
546
|
+
placeholder="Enter project description"
|
|
547
|
+
rows="4"
|
|
548
|
+
required
|
|
549
|
+
></textarea>
|
|
550
|
+
</div>
|
|
551
|
+
|
|
552
|
+
<div class="form-group">
|
|
553
|
+
<label>Tags *</label>
|
|
554
|
+
<TagBar
|
|
555
|
+
:all-tags="allTags"
|
|
556
|
+
:selected-tags="form.tags"
|
|
557
|
+
v-model:query="query"
|
|
558
|
+
v-model:match-index="matchIndex"
|
|
559
|
+
placeholder="Add project tags..."
|
|
560
|
+
overflow="fixed"
|
|
561
|
+
:allow-editing="true"
|
|
562
|
+
@tag-select="handleTagSelect"
|
|
563
|
+
@tag-remove="handleTagRemove"
|
|
564
|
+
@tag-create="handleTagCreate"
|
|
565
|
+
/>
|
|
566
|
+
<p class="help-text">Add at least one tag to categorize your project</p>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<button type="submit" :disabled="!isFormValid" class="submit-button">
|
|
570
|
+
Create Project
|
|
571
|
+
</button>
|
|
572
|
+
</form>
|
|
573
|
+
</div>
|
|
574
|
+
</template>
|
|
575
|
+
|
|
576
|
+
<style module>
|
|
577
|
+
.form-integration {
|
|
578
|
+
max-width: 600px;
|
|
579
|
+
padding: 2rem;
|
|
580
|
+
border: 1px solid #e0e0e0;
|
|
581
|
+
border-radius: 0.5rem;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.form-group {
|
|
585
|
+
margin-bottom: 1.5rem;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.form-group label {
|
|
589
|
+
display: block;
|
|
590
|
+
margin-bottom: 0.5rem;
|
|
591
|
+
font-weight: 500;
|
|
592
|
+
color: #333;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.form-group input,
|
|
596
|
+
.form-group textarea {
|
|
597
|
+
width: 100%;
|
|
598
|
+
padding: 0.75rem;
|
|
599
|
+
border: 1px solid #ddd;
|
|
600
|
+
border-radius: 0.25rem;
|
|
601
|
+
font-size: 1rem;
|
|
602
|
+
font-family: inherit;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.form-group input:focus,
|
|
606
|
+
.form-group textarea:focus {
|
|
607
|
+
outline: none;
|
|
608
|
+
border-color: #007bff;
|
|
609
|
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.help-text {
|
|
613
|
+
margin-top: 0.5rem;
|
|
614
|
+
color: #666;
|
|
615
|
+
font-size: 0.875rem;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.submit-button {
|
|
619
|
+
width: 100%;
|
|
620
|
+
padding: 0.75rem;
|
|
621
|
+
background-color: #007bff;
|
|
622
|
+
color: white;
|
|
623
|
+
border: none;
|
|
624
|
+
border-radius: 0.25rem;
|
|
625
|
+
font-size: 1rem;
|
|
626
|
+
cursor: pointer;
|
|
627
|
+
transition: background-color 0.2s;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.submit-button:hover:not(:disabled) {
|
|
631
|
+
background-color: #0056b3;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.submit-button:disabled {
|
|
635
|
+
background-color: #6c757d;
|
|
636
|
+
cursor: not-allowed;
|
|
637
|
+
}
|
|
638
|
+
</style>
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### Different Overflow Modes
|
|
642
|
+
|
|
643
|
+
```vue
|
|
644
|
+
<script setup lang="ts">
|
|
645
|
+
import { ref } from "vue";
|
|
646
|
+
import { TagBar } from "@umbra-ui/core";
|
|
647
|
+
import type { TagItem } from "@umbra-ui/core";
|
|
648
|
+
|
|
649
|
+
const allTags = ref<TagItem[]>([
|
|
650
|
+
{ id: "1", title: "JavaScript" },
|
|
651
|
+
{ id: "2", title: "TypeScript" },
|
|
652
|
+
{ id: "3", title: "Vue.js" },
|
|
653
|
+
{ id: "4", title: "React" },
|
|
654
|
+
{ id: "5", title: "Angular" },
|
|
655
|
+
{ id: "6", title: "Node.js" },
|
|
656
|
+
{ id: "7", title: "Python" },
|
|
657
|
+
{ id: "8", title: "Java" },
|
|
658
|
+
{ id: "9", title: "C#" },
|
|
659
|
+
{ id: "10", title: "Go" },
|
|
660
|
+
]);
|
|
661
|
+
|
|
662
|
+
const selectedTags = ref<TagItem[]>([
|
|
663
|
+
{ id: "1", title: "JavaScript" },
|
|
664
|
+
{ id: "2", title: "TypeScript" },
|
|
665
|
+
{ id: "3", title: "Vue.js" },
|
|
666
|
+
{ id: "4", title: "React" },
|
|
667
|
+
{ id: "5", title: "Node.js" },
|
|
668
|
+
]);
|
|
669
|
+
|
|
670
|
+
const query = ref("");
|
|
671
|
+
const matchIndex = ref(-1);
|
|
672
|
+
|
|
673
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
674
|
+
selectedTags.value.push(tag);
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
678
|
+
const index = selectedTags.value.findIndex((t) => t.id === tag.id);
|
|
679
|
+
if (index > -1) {
|
|
680
|
+
selectedTags.value.splice(index, 1);
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
const handleTagCreate = (title: string) => {
|
|
685
|
+
const newTag: TagItem = {
|
|
686
|
+
id: Date.now().toString(),
|
|
687
|
+
title: title.trim(),
|
|
688
|
+
};
|
|
689
|
+
allTags.value.push(newTag);
|
|
690
|
+
selectedTags.value.push(newTag);
|
|
691
|
+
};
|
|
692
|
+
</script>
|
|
693
|
+
|
|
694
|
+
<template>
|
|
695
|
+
<div class="overflow-modes">
|
|
696
|
+
<h3>Different Overflow Modes</h3>
|
|
697
|
+
|
|
698
|
+
<div class="mode-section">
|
|
699
|
+
<h4>Full Mode (Horizontal Scroll)</h4>
|
|
700
|
+
<TagBar
|
|
701
|
+
:all-tags="allTags"
|
|
702
|
+
:selected-tags="selectedTags"
|
|
703
|
+
v-model:query="query"
|
|
704
|
+
v-model:match-index="matchIndex"
|
|
705
|
+
placeholder="Add tags..."
|
|
706
|
+
overflow="full"
|
|
707
|
+
:allow-editing="true"
|
|
708
|
+
@tag-select="handleTagSelect"
|
|
709
|
+
@tag-remove="handleTagRemove"
|
|
710
|
+
@tag-create="handleTagCreate"
|
|
711
|
+
/>
|
|
712
|
+
</div>
|
|
713
|
+
|
|
714
|
+
<div class="mode-section">
|
|
715
|
+
<h4>Fixed Mode (Wrap Tags)</h4>
|
|
716
|
+
<TagBar
|
|
717
|
+
:all-tags="allTags"
|
|
718
|
+
:selected-tags="selectedTags"
|
|
719
|
+
v-model:query="query"
|
|
720
|
+
v-model:match-index="matchIndex"
|
|
721
|
+
placeholder="Add tags..."
|
|
722
|
+
overflow="fixed"
|
|
723
|
+
:allow-editing="true"
|
|
724
|
+
@tag-select="handleTagSelect"
|
|
725
|
+
@tag-remove="handleTagRemove"
|
|
726
|
+
@tag-create="handleTagCreate"
|
|
727
|
+
/>
|
|
728
|
+
</div>
|
|
729
|
+
|
|
730
|
+
<div class="mode-section">
|
|
731
|
+
<h4>List Mode (Vertical List)</h4>
|
|
732
|
+
<TagBar
|
|
733
|
+
:all-tags="allTags"
|
|
734
|
+
:selected-tags="selectedTags"
|
|
735
|
+
v-model:query="query"
|
|
736
|
+
v-model:match-index="matchIndex"
|
|
737
|
+
placeholder="Add tags..."
|
|
738
|
+
overflow="list"
|
|
739
|
+
:allow-editing="true"
|
|
740
|
+
@tag-select="handleTagSelect"
|
|
741
|
+
@tag-remove="handleTagRemove"
|
|
742
|
+
@tag-create="handleTagCreate"
|
|
743
|
+
/>
|
|
744
|
+
</div>
|
|
745
|
+
</div>
|
|
746
|
+
</template>
|
|
747
|
+
|
|
748
|
+
<style module>
|
|
749
|
+
.overflow-modes {
|
|
750
|
+
max-width: 800px;
|
|
751
|
+
padding: 2rem;
|
|
752
|
+
border: 1px solid #e0e0e0;
|
|
753
|
+
border-radius: 0.5rem;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.mode-section {
|
|
757
|
+
margin-bottom: 2rem;
|
|
758
|
+
padding: 1rem;
|
|
759
|
+
border: 1px solid #f0f0f0;
|
|
760
|
+
border-radius: 0.25rem;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.mode-section h4 {
|
|
764
|
+
margin: 0 0 1rem 0;
|
|
765
|
+
color: #333;
|
|
766
|
+
}
|
|
767
|
+
</style>
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Custom Styling
|
|
771
|
+
|
|
772
|
+
```vue
|
|
773
|
+
<script setup lang="ts">
|
|
774
|
+
import { ref } from "vue";
|
|
775
|
+
import { TagBar, createStyledTag } from "@umbra-ui/core";
|
|
776
|
+
import type { TagItem, TagStyleConfig } from "@umbra-ui/core";
|
|
777
|
+
|
|
778
|
+
const customStyle: TagStyleConfig = {
|
|
779
|
+
backgroundColor: "#e3f2fd",
|
|
780
|
+
color: "#1976d2",
|
|
781
|
+
border: "2px solid #2196f3",
|
|
782
|
+
borderRadius: "20px",
|
|
783
|
+
padding: "0.5rem 1rem",
|
|
784
|
+
fontSize: "0.875rem",
|
|
785
|
+
fontWeight: "600",
|
|
786
|
+
hover: {
|
|
787
|
+
backgroundColor: "#bbdefb",
|
|
788
|
+
transform: "scale(1.05)",
|
|
789
|
+
},
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
const allTags = ref<TagItem[]>([
|
|
793
|
+
createStyledTag("1", "Custom Style", customStyle),
|
|
794
|
+
createStyledTag("2", "Another Tag", customStyle),
|
|
795
|
+
createStyledTag("3", "Styled Tag", customStyle),
|
|
796
|
+
]);
|
|
797
|
+
|
|
798
|
+
const selectedTags = ref<TagItem[]>([]);
|
|
799
|
+
const query = ref("");
|
|
800
|
+
const matchIndex = ref(-1);
|
|
801
|
+
|
|
802
|
+
const handleTagSelect = (tag: TagItem) => {
|
|
803
|
+
selectedTags.value.push(tag);
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
const handleTagRemove = (tag: TagItem) => {
|
|
807
|
+
const index = selectedTags.value.findIndex((t) => t.id === tag.id);
|
|
808
|
+
if (index > -1) {
|
|
809
|
+
selectedTags.value.splice(index, 1);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
const handleTagCreate = (title: string) => {
|
|
814
|
+
const newTag = createStyledTag(
|
|
815
|
+
Date.now().toString(),
|
|
816
|
+
title.trim(),
|
|
817
|
+
customStyle
|
|
818
|
+
);
|
|
819
|
+
allTags.value.push(newTag);
|
|
820
|
+
selectedTags.value.push(newTag);
|
|
821
|
+
};
|
|
822
|
+
</script>
|
|
823
|
+
|
|
824
|
+
<template>
|
|
825
|
+
<div class="custom-styling">
|
|
826
|
+
<h3>Custom Styled Tags</h3>
|
|
827
|
+
|
|
828
|
+
<TagBar
|
|
829
|
+
:all-tags="allTags"
|
|
830
|
+
:selected-tags="selectedTags"
|
|
831
|
+
v-model:query="query"
|
|
832
|
+
v-model:match-index="matchIndex"
|
|
833
|
+
placeholder="Add custom tags..."
|
|
834
|
+
overflow="fixed"
|
|
835
|
+
:allow-editing="true"
|
|
836
|
+
@tag-select="handleTagSelect"
|
|
837
|
+
@tag-remove="handleTagRemove"
|
|
838
|
+
@tag-create="handleTagCreate"
|
|
839
|
+
/>
|
|
840
|
+
|
|
841
|
+
<div class="style-info">
|
|
842
|
+
<h4>Custom Style Properties:</h4>
|
|
843
|
+
<ul>
|
|
844
|
+
<li>Background: Light blue with transparency</li>
|
|
845
|
+
<li>Border: 2px solid blue</li>
|
|
846
|
+
<li>Border radius: 20px (pill shape)</li>
|
|
847
|
+
<li>Hover: Scale effect and darker background</li>
|
|
848
|
+
</ul>
|
|
849
|
+
</div>
|
|
850
|
+
</div>
|
|
851
|
+
</template>
|
|
852
|
+
|
|
853
|
+
<style module>
|
|
854
|
+
.custom-styling {
|
|
855
|
+
max-width: 600px;
|
|
856
|
+
padding: 2rem;
|
|
857
|
+
border: 1px solid #e0e0e0;
|
|
858
|
+
border-radius: 0.5rem;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.style-info {
|
|
862
|
+
margin-top: 1.5rem;
|
|
863
|
+
padding: 1rem;
|
|
864
|
+
background-color: #f8f9fa;
|
|
865
|
+
border-radius: 0.25rem;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.style-info h4 {
|
|
869
|
+
margin: 0 0 0.5rem 0;
|
|
870
|
+
color: #333;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.style-info ul {
|
|
874
|
+
margin: 0;
|
|
875
|
+
padding-left: 1.5rem;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.style-info li {
|
|
879
|
+
margin-bottom: 0.25rem;
|
|
880
|
+
color: #666;
|
|
881
|
+
}
|
|
882
|
+
</style>
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
## Technical Notes
|
|
886
|
+
|
|
887
|
+
- Tags components provide a comprehensive tag management system with advanced features
|
|
888
|
+
- Real-time search filters tags as you type with instant results
|
|
889
|
+
- Keyboard navigation includes arrow keys, Enter, and Escape for full control
|
|
890
|
+
- Multiple overflow modes handle different layout requirements
|
|
891
|
+
- Custom styling system supports both Radix colors and custom CSS properties
|
|
892
|
+
- Floating UI provides smart positioning for the tag picker
|
|
893
|
+
- GSAP animations create smooth transitions and visual feedback
|
|
894
|
+
- ResizeObserver tracks container changes for responsive behavior
|
|
895
|
+
- TypeScript interfaces ensure type safety for all tag operations
|
|
896
|
+
- Accessibility features include keyboard navigation and screen reader support
|
|
897
|
+
- Color picker integration allows custom tag colors during creation
|