@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,759 @@
|
|
|
1
|
+
# Search Components
|
|
2
|
+
|
|
3
|
+
A comprehensive search system built with Vue 3 Composition API and TypeScript. The search components provide a powerful search interface with real-time filtering, animated results display, and advanced features like pagination, highlighting, and keyboard navigation.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { SearchBar, SearchResults } from "@umbra-ui/core";
|
|
9
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Dependencies:**
|
|
13
|
+
|
|
14
|
+
- Vue 3.x
|
|
15
|
+
- @umbra-ui/icons (for magnifier and chevron icons)
|
|
16
|
+
- gsap (for animations)
|
|
17
|
+
- @floating-ui/vue (for positioning)
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
```vue
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { ref } from "vue";
|
|
24
|
+
import { SearchBar, SearchResults } from "@umbra-ui/core";
|
|
25
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
26
|
+
|
|
27
|
+
const searchQuery = ref("");
|
|
28
|
+
const searchItems = ref<SearchResult[]>([
|
|
29
|
+
{
|
|
30
|
+
id: "1",
|
|
31
|
+
title: "Example Item",
|
|
32
|
+
description: "This is an example search result",
|
|
33
|
+
caption: "Category",
|
|
34
|
+
footnote: "Tag",
|
|
35
|
+
icon: "📄",
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
40
|
+
console.log("Selected:", result);
|
|
41
|
+
searchQuery.value = result.title;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleSearchFocus = () => {
|
|
45
|
+
console.log("Search focused");
|
|
46
|
+
};
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<div>
|
|
51
|
+
<SearchBar
|
|
52
|
+
v-model:query="searchQuery"
|
|
53
|
+
:items="searchItems"
|
|
54
|
+
type="documents"
|
|
55
|
+
placeholder="Search documents..."
|
|
56
|
+
@onSelect="handleSearchSelect"
|
|
57
|
+
@onFocus="handleSearchFocus"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Components
|
|
64
|
+
|
|
65
|
+
### SearchBar
|
|
66
|
+
|
|
67
|
+
The main search input component with dropdown results.
|
|
68
|
+
|
|
69
|
+
#### Props
|
|
70
|
+
|
|
71
|
+
| Prop Name | Type | Required | Default | Description |
|
|
72
|
+
| ------------- | ---------------- | -------- | ---------- | ------------------------------------------ |
|
|
73
|
+
| `type` | `string` | No | `"items"` | Type of items being searched (for display) |
|
|
74
|
+
| `query` | `string` | No | `""` | The search query value |
|
|
75
|
+
| `items` | `SearchResult[]` | No | `[]` | Array of searchable items |
|
|
76
|
+
| `placeholder` | `string` | No | `"Search"` | Placeholder text for the search input |
|
|
77
|
+
|
|
78
|
+
#### Events
|
|
79
|
+
|
|
80
|
+
| Event Name | Payload Type | Description |
|
|
81
|
+
| -------------- | -------------- | -------------------------------------------- |
|
|
82
|
+
| `update:query` | `string` | Emitted when the search query changes |
|
|
83
|
+
| `onSelect` | `SearchResult` | Emitted when a search result is selected |
|
|
84
|
+
| `onFocus` | `void` | Emitted when the search input receives focus |
|
|
85
|
+
|
|
86
|
+
### SearchResults
|
|
87
|
+
|
|
88
|
+
The results display component (used internally by SearchBar).
|
|
89
|
+
|
|
90
|
+
#### Props
|
|
91
|
+
|
|
92
|
+
| Prop Name | Type | Required | Description |
|
|
93
|
+
| --------- | ---------------- | -------- | ---------------------------- |
|
|
94
|
+
| `type` | `string` | Yes | Type of items being searched |
|
|
95
|
+
| `query` | `string` | Yes | Current search query |
|
|
96
|
+
| `matches` | `SearchResult[]` | Yes | Filtered search results |
|
|
97
|
+
|
|
98
|
+
#### Events
|
|
99
|
+
|
|
100
|
+
| Event Name | Payload Type | Description |
|
|
101
|
+
| ---------- | -------------- | ---------------------------------------- |
|
|
102
|
+
| `onSelect` | `SearchResult` | Emitted when a search result is selected |
|
|
103
|
+
|
|
104
|
+
## SearchResult Interface
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
interface SearchResult {
|
|
108
|
+
id: string;
|
|
109
|
+
icon?: string;
|
|
110
|
+
title: string;
|
|
111
|
+
description: string;
|
|
112
|
+
caption: string;
|
|
113
|
+
footnote: string;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## CSS Customization
|
|
118
|
+
|
|
119
|
+
The search components use CSS custom properties for theming and customization:
|
|
120
|
+
|
|
121
|
+
### SearchBar Variables
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
/* Search Bar Colors */
|
|
125
|
+
--search-bar-bg: var(--search-bar-background);
|
|
126
|
+
--search-bar-text: var(--search-bar-text-color);
|
|
127
|
+
--search-bar-placeholder: var(--search-bar-placeholder-color);
|
|
128
|
+
--search-bar-border: var(--search-bar-border-color);
|
|
129
|
+
--search-bar-focus-border: var(--search-bar-focus-border-color);
|
|
130
|
+
--search-bar-shadow: var(--search-bar-shadow-color);
|
|
131
|
+
--search-bar-inset-shadow: var(--search-bar-inset-shadow-color);
|
|
132
|
+
|
|
133
|
+
/* Search Overlay */
|
|
134
|
+
--search-overlay-bg: var(--search-overlay-background);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### SearchResults Variables
|
|
138
|
+
|
|
139
|
+
```css
|
|
140
|
+
/* Results Container */
|
|
141
|
+
--search-results-bg: var(--search-results-background);
|
|
142
|
+
--search-results-border: var(--search-results-border-color);
|
|
143
|
+
--search-results-shadow: var(--search-results-shadow-color);
|
|
144
|
+
--search-results-inset-shadow: var(--search-results-inset-shadow-color);
|
|
145
|
+
|
|
146
|
+
/* Subheader */
|
|
147
|
+
--search-subheader-bg: var(--search-subheader-background);
|
|
148
|
+
--search-subheader-border: var(--search-subheader-border-color);
|
|
149
|
+
--search-subheader-text: var(--search-subheader-text-color);
|
|
150
|
+
|
|
151
|
+
/* Result Items */
|
|
152
|
+
--search-result-bg: var(--search-result-background);
|
|
153
|
+
--search-result-hover-bg: var(--search-result-hover-background);
|
|
154
|
+
--search-result-border: var(--search-result-border-color);
|
|
155
|
+
--search-result-text: var(--search-result-text-color);
|
|
156
|
+
|
|
157
|
+
/* Icons */
|
|
158
|
+
--search-icon-bg: var(--search-icon-background);
|
|
159
|
+
--search-icon-border: var(--search-icon-border-color);
|
|
160
|
+
|
|
161
|
+
/* Footnotes */
|
|
162
|
+
--search-footnote-bg: var(--search-footnote-background);
|
|
163
|
+
|
|
164
|
+
/* Empty State */
|
|
165
|
+
--search-nothing-text: var(--search-nothing-text-color);
|
|
166
|
+
|
|
167
|
+
/* Load More */
|
|
168
|
+
--search-load-more-bg: var(--search-load-more-background);
|
|
169
|
+
--search-load-more-hover-bg: var(--search-load-more-hover-background);
|
|
170
|
+
--search-load-more-text: var(--search-load-more-text-color);
|
|
171
|
+
|
|
172
|
+
/* Highlighting */
|
|
173
|
+
--search-highlight-bg: var(--search-highlight-background);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Key Features
|
|
177
|
+
|
|
178
|
+
### Search Functionality
|
|
179
|
+
|
|
180
|
+
- **Real-time Filtering**: Results update as you type
|
|
181
|
+
- **Multi-field Search**: Searches across title, description, caption, and footnote
|
|
182
|
+
- **Case-insensitive**: Search is not case-sensitive
|
|
183
|
+
- **Highlighting**: Query terms are highlighted in results
|
|
184
|
+
|
|
185
|
+
### User Experience
|
|
186
|
+
|
|
187
|
+
- **Animated Dropdown**: Smooth animations for showing/hiding results
|
|
188
|
+
- **Floating Positioning**: Results are positioned using Floating UI
|
|
189
|
+
- **Keyboard Navigation**: Full keyboard support with escape to close
|
|
190
|
+
- **Click Outside**: Clicking outside closes the search results
|
|
191
|
+
- **Loading States**: Shows loading indicator while building search index
|
|
192
|
+
|
|
193
|
+
### Results Display
|
|
194
|
+
|
|
195
|
+
- **Pagination**: Load more results with pagination
|
|
196
|
+
- **Rich Content**: Support for icons, titles, descriptions, captions, and footnotes
|
|
197
|
+
- **Icon Support**: Both emoji and image icons
|
|
198
|
+
- **Hover Effects**: Visual feedback on result hover
|
|
199
|
+
- **Empty States**: Proper handling of no results
|
|
200
|
+
|
|
201
|
+
### Accessibility
|
|
202
|
+
|
|
203
|
+
- **ARIA Labels**: Proper labeling for screen readers
|
|
204
|
+
- **Keyboard Navigation**: Full keyboard support
|
|
205
|
+
- **Focus Management**: Proper focus handling
|
|
206
|
+
- **Screen Reader Support**: Accessible result announcements
|
|
207
|
+
|
|
208
|
+
## Examples
|
|
209
|
+
|
|
210
|
+
### Basic Search
|
|
211
|
+
|
|
212
|
+
```vue
|
|
213
|
+
<script setup lang="ts">
|
|
214
|
+
import { ref } from "vue";
|
|
215
|
+
import { SearchBar } from "@umbra-ui/core";
|
|
216
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
217
|
+
|
|
218
|
+
const searchQuery = ref("");
|
|
219
|
+
const searchItems = ref<SearchResult[]>([
|
|
220
|
+
{
|
|
221
|
+
id: "1",
|
|
222
|
+
title: "Getting Started Guide",
|
|
223
|
+
description: "Learn how to get started with our platform",
|
|
224
|
+
caption: "Documentation",
|
|
225
|
+
footnote: "Guide",
|
|
226
|
+
icon: "📚",
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: "2",
|
|
230
|
+
title: "API Reference",
|
|
231
|
+
description: "Complete API documentation and examples",
|
|
232
|
+
caption: "Documentation",
|
|
233
|
+
footnote: "API",
|
|
234
|
+
icon: "🔧",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: "3",
|
|
238
|
+
title: "User Manual",
|
|
239
|
+
description: "Detailed user manual with step-by-step instructions",
|
|
240
|
+
caption: "Documentation",
|
|
241
|
+
footnote: "Manual",
|
|
242
|
+
icon: "📖",
|
|
243
|
+
},
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
247
|
+
console.log("Selected:", result);
|
|
248
|
+
searchQuery.value = result.title;
|
|
249
|
+
};
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
<template>
|
|
253
|
+
<div class="basic-search">
|
|
254
|
+
<h3>Documentation Search</h3>
|
|
255
|
+
|
|
256
|
+
<SearchBar
|
|
257
|
+
v-model:query="searchQuery"
|
|
258
|
+
:items="searchItems"
|
|
259
|
+
type="documents"
|
|
260
|
+
placeholder="Search documentation..."
|
|
261
|
+
@onSelect="handleSearchSelect"
|
|
262
|
+
/>
|
|
263
|
+
|
|
264
|
+
<div v-if="searchQuery" class="search-info">
|
|
265
|
+
<p>Searching for: "{{ searchQuery }}"</p>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</template>
|
|
269
|
+
|
|
270
|
+
<style module>
|
|
271
|
+
.basic-search {
|
|
272
|
+
max-width: 500px;
|
|
273
|
+
padding: 2rem;
|
|
274
|
+
border: 1px solid #e0e0e0;
|
|
275
|
+
border-radius: 0.5rem;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.search-info {
|
|
279
|
+
margin-top: 1rem;
|
|
280
|
+
padding: 1rem;
|
|
281
|
+
background-color: #f8f9fa;
|
|
282
|
+
border-radius: 0.25rem;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.search-info p {
|
|
286
|
+
margin: 0;
|
|
287
|
+
color: #666;
|
|
288
|
+
font-size: 0.875rem;
|
|
289
|
+
}
|
|
290
|
+
</style>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Advanced Search with Custom Data
|
|
294
|
+
|
|
295
|
+
```vue
|
|
296
|
+
<script setup lang="ts">
|
|
297
|
+
import { ref, computed } from "vue";
|
|
298
|
+
import { SearchBar } from "@umbra-ui/core";
|
|
299
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
300
|
+
|
|
301
|
+
const searchQuery = ref("");
|
|
302
|
+
const allItems = ref<SearchResult[]>([
|
|
303
|
+
{
|
|
304
|
+
id: "1",
|
|
305
|
+
title: "Vue.js Framework",
|
|
306
|
+
description:
|
|
307
|
+
"Progressive JavaScript framework for building user interfaces",
|
|
308
|
+
caption: "Framework",
|
|
309
|
+
footnote: "JavaScript",
|
|
310
|
+
icon: "🟢",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: "2",
|
|
314
|
+
title: "React Library",
|
|
315
|
+
description: "A JavaScript library for building user interfaces",
|
|
316
|
+
caption: "Library",
|
|
317
|
+
footnote: "JavaScript",
|
|
318
|
+
icon: "⚛️",
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: "3",
|
|
322
|
+
title: "Angular Platform",
|
|
323
|
+
description: "Platform for building mobile and desktop web applications",
|
|
324
|
+
caption: "Platform",
|
|
325
|
+
footnote: "TypeScript",
|
|
326
|
+
icon: "🔴",
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: "4",
|
|
330
|
+
title: "Svelte Compiler",
|
|
331
|
+
description:
|
|
332
|
+
"Cybernetically enhanced web apps with compile-time optimizations",
|
|
333
|
+
caption: "Compiler",
|
|
334
|
+
footnote: "JavaScript",
|
|
335
|
+
icon: "🧡",
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
338
|
+
|
|
339
|
+
const filteredItems = computed(() => {
|
|
340
|
+
if (!searchQuery.value.trim()) {
|
|
341
|
+
return allItems.value;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const query = searchQuery.value.toLowerCase();
|
|
345
|
+
return allItems.value.filter((item) => {
|
|
346
|
+
const searchText =
|
|
347
|
+
`${item.title} ${item.description} ${item.caption} ${item.footnote}`.toLowerCase();
|
|
348
|
+
return searchText.includes(query);
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
353
|
+
console.log("Selected framework:", result);
|
|
354
|
+
searchQuery.value = result.title;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const handleSearchFocus = () => {
|
|
358
|
+
console.log("Search focused - showing all items");
|
|
359
|
+
};
|
|
360
|
+
</script>
|
|
361
|
+
|
|
362
|
+
<template>
|
|
363
|
+
<div class="advanced-search">
|
|
364
|
+
<h3>Framework Search</h3>
|
|
365
|
+
|
|
366
|
+
<SearchBar
|
|
367
|
+
v-model:query="searchQuery"
|
|
368
|
+
:items="filteredItems"
|
|
369
|
+
type="frameworks"
|
|
370
|
+
placeholder="Search JavaScript frameworks..."
|
|
371
|
+
@onSelect="handleSearchSelect"
|
|
372
|
+
@onFocus="handleSearchFocus"
|
|
373
|
+
/>
|
|
374
|
+
|
|
375
|
+
<div class="search-stats">
|
|
376
|
+
<p>Total items: {{ allItems.length }}</p>
|
|
377
|
+
<p v-if="searchQuery">Filtered results: {{ filteredItems.length }}</p>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</template>
|
|
381
|
+
|
|
382
|
+
<style module>
|
|
383
|
+
.advanced-search {
|
|
384
|
+
max-width: 600px;
|
|
385
|
+
padding: 2rem;
|
|
386
|
+
border: 1px solid #e0e0e0;
|
|
387
|
+
border-radius: 0.5rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.search-stats {
|
|
391
|
+
margin-top: 1rem;
|
|
392
|
+
padding: 1rem;
|
|
393
|
+
background-color: #f8f9fa;
|
|
394
|
+
border-radius: 0.25rem;
|
|
395
|
+
display: flex;
|
|
396
|
+
gap: 2rem;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.search-stats p {
|
|
400
|
+
margin: 0;
|
|
401
|
+
color: #666;
|
|
402
|
+
font-size: 0.875rem;
|
|
403
|
+
}
|
|
404
|
+
</style>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Search with API Integration
|
|
408
|
+
|
|
409
|
+
```vue
|
|
410
|
+
<script setup lang="ts">
|
|
411
|
+
import { ref, watch } from "vue";
|
|
412
|
+
import { SearchBar } from "@umbra-ui/core";
|
|
413
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
414
|
+
|
|
415
|
+
const searchQuery = ref("");
|
|
416
|
+
const searchItems = ref<SearchResult[]>([]);
|
|
417
|
+
const isLoading = ref(false);
|
|
418
|
+
const error = ref("");
|
|
419
|
+
|
|
420
|
+
const searchAPI = async (query: string) => {
|
|
421
|
+
if (!query.trim()) {
|
|
422
|
+
searchItems.value = [];
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
isLoading.value = true;
|
|
427
|
+
error.value = "";
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
// Simulate API call
|
|
431
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
432
|
+
|
|
433
|
+
// Simulate API response
|
|
434
|
+
const mockResults: SearchResult[] = [
|
|
435
|
+
{
|
|
436
|
+
id: "1",
|
|
437
|
+
title: `Search Result for "${query}"`,
|
|
438
|
+
description: `This is a mock result for the query "${query}"`,
|
|
439
|
+
caption: "API Result",
|
|
440
|
+
footnote: "Mock",
|
|
441
|
+
icon: "🔍",
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: "2",
|
|
445
|
+
title: `Another Result for "${query}"`,
|
|
446
|
+
description: `Another mock result for the query "${query}"`,
|
|
447
|
+
caption: "API Result",
|
|
448
|
+
footnote: "Mock",
|
|
449
|
+
icon: "📄",
|
|
450
|
+
},
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
searchItems.value = mockResults;
|
|
454
|
+
} catch (err) {
|
|
455
|
+
error.value = "Failed to search. Please try again.";
|
|
456
|
+
searchItems.value = [];
|
|
457
|
+
} finally {
|
|
458
|
+
isLoading.value = false;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// Debounced search
|
|
463
|
+
let searchTimeout: NodeJS.Timeout;
|
|
464
|
+
watch(searchQuery, (newQuery) => {
|
|
465
|
+
clearTimeout(searchTimeout);
|
|
466
|
+
searchTimeout = setTimeout(() => {
|
|
467
|
+
searchAPI(newQuery);
|
|
468
|
+
}, 300);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
472
|
+
console.log("Selected from API:", result);
|
|
473
|
+
searchQuery.value = result.title;
|
|
474
|
+
};
|
|
475
|
+
</script>
|
|
476
|
+
|
|
477
|
+
<template>
|
|
478
|
+
<div class="api-search">
|
|
479
|
+
<h3>API-Powered Search</h3>
|
|
480
|
+
|
|
481
|
+
<SearchBar
|
|
482
|
+
v-model:query="searchQuery"
|
|
483
|
+
:items="searchItems"
|
|
484
|
+
type="api-results"
|
|
485
|
+
placeholder="Search with API..."
|
|
486
|
+
@onSelect="handleSearchSelect"
|
|
487
|
+
/>
|
|
488
|
+
|
|
489
|
+
<div v-if="isLoading" class="loading-state">
|
|
490
|
+
<p>Searching...</p>
|
|
491
|
+
</div>
|
|
492
|
+
|
|
493
|
+
<div v-if="error" class="error-state">
|
|
494
|
+
<p>{{ error }}</p>
|
|
495
|
+
</div>
|
|
496
|
+
|
|
497
|
+
<div v-if="searchQuery && !isLoading && !error" class="search-info">
|
|
498
|
+
<p>Found {{ searchItems.length }} results for "{{ searchQuery }}"</p>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
</template>
|
|
502
|
+
|
|
503
|
+
<style module>
|
|
504
|
+
.api-search {
|
|
505
|
+
max-width: 500px;
|
|
506
|
+
padding: 2rem;
|
|
507
|
+
border: 1px solid #e0e0e0;
|
|
508
|
+
border-radius: 0.5rem;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.loading-state {
|
|
512
|
+
margin-top: 1rem;
|
|
513
|
+
padding: 1rem;
|
|
514
|
+
background-color: #e3f2fd;
|
|
515
|
+
border-radius: 0.25rem;
|
|
516
|
+
text-align: center;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.loading-state p {
|
|
520
|
+
margin: 0;
|
|
521
|
+
color: #1976d2;
|
|
522
|
+
font-weight: 500;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.error-state {
|
|
526
|
+
margin-top: 1rem;
|
|
527
|
+
padding: 1rem;
|
|
528
|
+
background-color: #ffebee;
|
|
529
|
+
border-radius: 0.25rem;
|
|
530
|
+
text-align: center;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.error-state p {
|
|
534
|
+
margin: 0;
|
|
535
|
+
color: #d32f2f;
|
|
536
|
+
font-weight: 500;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.search-info {
|
|
540
|
+
margin-top: 1rem;
|
|
541
|
+
padding: 1rem;
|
|
542
|
+
background-color: #f8f9fa;
|
|
543
|
+
border-radius: 0.25rem;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.search-info p {
|
|
547
|
+
margin: 0;
|
|
548
|
+
color: #666;
|
|
549
|
+
font-size: 0.875rem;
|
|
550
|
+
}
|
|
551
|
+
</style>
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Search with Custom Styling
|
|
555
|
+
|
|
556
|
+
```vue
|
|
557
|
+
<script setup lang="ts">
|
|
558
|
+
import { ref } from "vue";
|
|
559
|
+
import { SearchBar } from "@umbra-ui/core";
|
|
560
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
561
|
+
|
|
562
|
+
const searchQuery = ref("");
|
|
563
|
+
const searchItems = ref<SearchResult[]>([
|
|
564
|
+
{
|
|
565
|
+
id: "1",
|
|
566
|
+
title: "Custom Styled Search",
|
|
567
|
+
description: "This search has custom styling applied",
|
|
568
|
+
caption: "Custom",
|
|
569
|
+
footnote: "Styled",
|
|
570
|
+
icon: "🎨",
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: "2",
|
|
574
|
+
title: "Themed Results",
|
|
575
|
+
description: "Results with custom theme colors",
|
|
576
|
+
caption: "Theme",
|
|
577
|
+
footnote: "Custom",
|
|
578
|
+
icon: "🌈",
|
|
579
|
+
},
|
|
580
|
+
]);
|
|
581
|
+
|
|
582
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
583
|
+
console.log("Selected:", result);
|
|
584
|
+
searchQuery.value = result.title;
|
|
585
|
+
};
|
|
586
|
+
</script>
|
|
587
|
+
|
|
588
|
+
<template>
|
|
589
|
+
<div class="custom-search">
|
|
590
|
+
<h3>Custom Styled Search</h3>
|
|
591
|
+
|
|
592
|
+
<SearchBar
|
|
593
|
+
v-model:query="searchQuery"
|
|
594
|
+
:items="searchItems"
|
|
595
|
+
type="custom-items"
|
|
596
|
+
placeholder="Search with custom styling..."
|
|
597
|
+
@onSelect="handleSearchSelect"
|
|
598
|
+
/>
|
|
599
|
+
</div>
|
|
600
|
+
</template>
|
|
601
|
+
|
|
602
|
+
<style module>
|
|
603
|
+
.custom-search {
|
|
604
|
+
max-width: 500px;
|
|
605
|
+
padding: 2rem;
|
|
606
|
+
border: 1px solid #e0e0e0;
|
|
607
|
+
border-radius: 0.5rem;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* Custom CSS variables for theming */
|
|
611
|
+
.custom-search {
|
|
612
|
+
--search-bar-bg: #f8f9fa;
|
|
613
|
+
--search-bar-text: #495057;
|
|
614
|
+
--search-bar-placeholder: #6c757d;
|
|
615
|
+
--search-bar-border: 2px solid #dee2e6;
|
|
616
|
+
--search-bar-focus-border: #007bff;
|
|
617
|
+
|
|
618
|
+
--search-results-bg: #ffffff;
|
|
619
|
+
--search-results-border: 1px solid #dee2e6;
|
|
620
|
+
--search-results-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
621
|
+
|
|
622
|
+
--search-subheader-bg: #e9ecef;
|
|
623
|
+
--search-subheader-text: #495057;
|
|
624
|
+
|
|
625
|
+
--search-result-bg: #ffffff;
|
|
626
|
+
--search-result-hover-bg: #f8f9fa;
|
|
627
|
+
--search-result-text: #212529;
|
|
628
|
+
|
|
629
|
+
--search-icon-bg: #e9ecef;
|
|
630
|
+
--search-icon-border: #dee2e6;
|
|
631
|
+
|
|
632
|
+
--search-highlight-bg: rgba(255, 193, 7, 0.3);
|
|
633
|
+
--search-overlay-bg: rgba(0, 0, 0, 0.5);
|
|
634
|
+
}
|
|
635
|
+
</style>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Search with Keyboard Shortcuts
|
|
639
|
+
|
|
640
|
+
```vue
|
|
641
|
+
<script setup lang="ts">
|
|
642
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
643
|
+
import { SearchBar } from "@umbra-ui/core";
|
|
644
|
+
import type { SearchResult } from "@umbra-ui/core";
|
|
645
|
+
|
|
646
|
+
const searchQuery = ref("");
|
|
647
|
+
const searchItems = ref<SearchResult[]>([
|
|
648
|
+
{
|
|
649
|
+
id: "1",
|
|
650
|
+
title: "Keyboard Shortcuts",
|
|
651
|
+
description: "Learn about keyboard shortcuts in our application",
|
|
652
|
+
caption: "Help",
|
|
653
|
+
footnote: "Shortcuts",
|
|
654
|
+
icon: "⌨️",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
id: "2",
|
|
658
|
+
title: "Search Tips",
|
|
659
|
+
description: "Tips and tricks for better search results",
|
|
660
|
+
caption: "Help",
|
|
661
|
+
footnote: "Tips",
|
|
662
|
+
icon: "💡",
|
|
663
|
+
},
|
|
664
|
+
]);
|
|
665
|
+
|
|
666
|
+
const searchBarRef = ref();
|
|
667
|
+
|
|
668
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
669
|
+
// Ctrl/Cmd + K to focus search
|
|
670
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
|
|
671
|
+
event.preventDefault();
|
|
672
|
+
searchBarRef.value?.focus();
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Escape to clear search
|
|
676
|
+
if (event.key === "Escape" && searchQuery.value) {
|
|
677
|
+
searchQuery.value = "";
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
onMounted(() => {
|
|
682
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
onUnmounted(() => {
|
|
686
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const handleSearchSelect = (result: SearchResult) => {
|
|
690
|
+
console.log("Selected:", result);
|
|
691
|
+
searchQuery.value = result.title;
|
|
692
|
+
};
|
|
693
|
+
</script>
|
|
694
|
+
|
|
695
|
+
<template>
|
|
696
|
+
<div class="keyboard-search">
|
|
697
|
+
<h3>Search with Keyboard Shortcuts</h3>
|
|
698
|
+
|
|
699
|
+
<div class="shortcuts-info">
|
|
700
|
+
<p><kbd>Ctrl</kbd> + <kbd>K</kbd> to focus search</p>
|
|
701
|
+
<p><kbd>Escape</kbd> to clear search</p>
|
|
702
|
+
</div>
|
|
703
|
+
|
|
704
|
+
<SearchBar
|
|
705
|
+
ref="searchBarRef"
|
|
706
|
+
v-model:query="searchQuery"
|
|
707
|
+
:items="searchItems"
|
|
708
|
+
type="help-items"
|
|
709
|
+
placeholder="Search help articles..."
|
|
710
|
+
@onSelect="handleSearchSelect"
|
|
711
|
+
/>
|
|
712
|
+
</div>
|
|
713
|
+
</template>
|
|
714
|
+
|
|
715
|
+
<style module>
|
|
716
|
+
.keyboard-search {
|
|
717
|
+
max-width: 500px;
|
|
718
|
+
padding: 2rem;
|
|
719
|
+
border: 1px solid #e0e0e0;
|
|
720
|
+
border-radius: 0.5rem;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.shortcuts-info {
|
|
724
|
+
margin-bottom: 1rem;
|
|
725
|
+
padding: 1rem;
|
|
726
|
+
background-color: #f8f9fa;
|
|
727
|
+
border-radius: 0.25rem;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.shortcuts-info p {
|
|
731
|
+
margin: 0.25rem 0;
|
|
732
|
+
color: #666;
|
|
733
|
+
font-size: 0.875rem;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
kbd {
|
|
737
|
+
background-color: #f8f9fa;
|
|
738
|
+
border: 1px solid #dee2e6;
|
|
739
|
+
border-radius: 0.25rem;
|
|
740
|
+
padding: 0.125rem 0.25rem;
|
|
741
|
+
font-size: 0.75rem;
|
|
742
|
+
font-family: monospace;
|
|
743
|
+
}
|
|
744
|
+
</style>
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
## Technical Notes
|
|
748
|
+
|
|
749
|
+
- Search components provide a comprehensive search interface with real-time filtering
|
|
750
|
+
- Floating UI is used for precise positioning of search results
|
|
751
|
+
- GSAP animations provide smooth transitions for showing/hiding results
|
|
752
|
+
- Search results support pagination with "Load More" functionality
|
|
753
|
+
- Query highlighting uses regex to highlight matching terms in results
|
|
754
|
+
- Keyboard navigation includes escape to close and full arrow key support
|
|
755
|
+
- Click outside functionality closes the search results
|
|
756
|
+
- Responsive design adapts to different screen sizes
|
|
757
|
+
- CSS custom properties enable easy theming for light and dark modes
|
|
758
|
+
- TypeScript interfaces ensure type safety for search results
|
|
759
|
+
- Accessibility features include ARIA labels and screen reader support
|