@todovue/tv-search 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,63 +1,92 @@
1
1
  <p align="center"><img width="150" src="https://firebasestorage.googleapis.com/v0/b/todovue-blog.appspot.com/o/logo.png?alt=media&token=d8eb592f-e4a9-4b02-8aff-62d337745f41" alt="TODOvue logo">
2
2
  </p>
3
3
 
4
- # TODOvue Search
5
- ###### TvSearch provides a fast, accessible, and fully customizable search interface for Vue 3 apps.
4
+ # TODOvue Search (TvSearch)
5
+ A fast, accessible, and fully customizable search interface component for Vue 3 applications. Provides an elegant modal search experience with keyboard shortcuts, real-time filtering, and complete style customization. Works seamlessly in Single Page Apps or Server-Side Rendered (SSR) environments (e.g. Nuxt 3).
6
6
 
7
- [![npm](https://img.shields.io/npm/v/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) [![Netlify Status](https://api.netlify.com/api/v1/badges/c6992bf1-ed06-4d9b-8b77-752254880951/deploy-status)](https://app.netlify.com/sites/tv-search/deploys) [![npm](https://img.shields.io/npm/dm/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) [![npm](https://img.shields.io/npm/dt/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) ![GitHub](https://img.shields.io/github/license/TODOvue/tv-search) ![GitHub Release Date](https://img.shields.io/github/release-date/TODOvue/tv-search)
7
+ [![npm](https://img.shields.io/npm/v/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) [![Netlify Status](https://api.netlify.com/api/v1/badges/c6992bf1-ed06-4d9b-8b77-752254880951/deploy-status)](https://app.netlify.com/sites/tv-search/deploys) [![npm downloads](https://img.shields.io/npm/dm/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) [![npm total downloads](https://img.shields.io/npm/dt/@todovue/tv-search.svg)](https://www.npmjs.com/package/@todovue/tv-search) ![License](https://img.shields.io/github/license/TODOvue/tv-search) ![Release Date](https://img.shields.io/github/release-date/TODOvue/tv-search)
8
8
 
9
+ > Demo: https://tv-search.netlify.app/
10
+
11
+ ---
9
12
  ## Table of Contents
10
- - [Demo](https://tv-search.netlify.app/)
13
+ - [Features](#features)
11
14
  - [Installation](#installation)
12
- - [Usage](#usage)
15
+ - [Quick Start (SPA)](#quick-start-spa)
16
+ - [Nuxt 3 / SSR Usage](#nuxt-3--ssr-usage)
17
+ - [Component Registration Options](#component-registration-options)
13
18
  - [Props](#props)
14
19
  - [Events](#events)
15
- - [Customize](#customize)
20
+ - [Keyboard Shortcuts](#keyboard-shortcuts)
21
+ - [Customization (Styles / Theming)](#customization-styles--theming)
22
+ - [Results Data Structure](#results-data-structure)
23
+ - [Accessibility](#accessibility)
24
+ - [SSR Notes](#ssr-notes)
25
+ - [Roadmap](#roadmap)
16
26
  - [Development](#development)
17
- - [Changelog](https://github.com/TODOvue/tv-search/blob/main/CHANGELOG.md)
18
- - [Contributing](https://github.com/TODOvue/tv-search/blob/main/CONTRIBUTING.md)
19
- - [License](https://github.com/TODOvue/tv-search/search/main/LICENSE)
27
+ - [Contributing](#contributing)
28
+ - [Changelog](#changelog)
29
+ - [License](#license)
20
30
 
31
+ ---
32
+ ## Features
33
+ - **Keyboard-first UX**: Open with `Ctrl+K` / `Cmd+K`, close with `Esc`
34
+ - **Real-time filtering**: Search as you type with instant results
35
+ - **Modal interface**: Clean overlay design that focuses user attention
36
+ - **Fully customizable**: Override colors for body, input, button, and text
37
+ - **Accessible**: Built with semantic HTML and keyboard navigation
38
+ - **Lightweight**: Minimal dependencies, Vue 3 marked as peer dependency
39
+ - **SSR compatible**: Works in Nuxt 3 and other SSR frameworks
40
+ - **Auto-focus**: Input field receives focus automatically when opened
41
+ - **Click-away close**: Modal closes when clicking outside the content area
42
+ - **Flexible results**: Pass any array of searchable items with custom properties
43
+
44
+ ---
21
45
  ## Installation
22
- Install with npm or yarn
46
+ Using npm:
23
47
  ```bash
24
48
  npm install @todovue/tv-search
25
49
  ```
50
+ Using yarn:
26
51
  ```bash
27
52
  yarn add @todovue/tv-search
28
53
  ```
29
- Import
30
- ```js
31
- import TvSearch from '@todovue/tv-search'
54
+ Using pnpm:
55
+ ```bash
56
+ pnpm add @todovue/tv-search
32
57
  ```
33
58
 
34
- You can also import it directly in the **main.js** file, so you don't have to import it in the pages
59
+ ---
60
+ ## Quick Start (SPA)
61
+ Global registration (main.js / main.ts):
35
62
  ```js
36
- import { createApp } from "vue";
37
- import App from "./App.vue";
38
- import TvSearch from '@todovue/tv-search'
63
+ import { createApp } from 'vue'
64
+ import App from './App.vue'
65
+ import { TvSearch } from '@todovue/tv-search'
39
66
 
40
- const app = createApp(App);
41
- app.component("TvSearch", TvSearch);
42
- app.mount("#app");
67
+ createApp(App)
68
+ .use(TvSearch) // enables <TvSearch /> globally
69
+ .mount('#app')
43
70
  ```
44
- ---
45
- ## Usage
46
- You can open the search component with `Ctrl + k` or `Command + k`
71
+
72
+ Alternatively, register as a component:
47
73
  ```js
48
- <template>
49
- <tv-search
50
- placeholder="Search blog..."
51
- titleButton="Search"
52
- :results="results"
53
- />
54
- </template>
74
+ import { createApp } from 'vue'
75
+ import App from './App.vue'
76
+ import { TvSearch } from '@todovue/tv-search'
77
+
78
+ const app = createApp(App)
79
+ app.component('TvSearch', TvSearch)
80
+ app.mount('#app')
81
+ ```
55
82
 
83
+ Local import inside a component:
84
+ ```vue
56
85
  <script setup>
57
- import { ref } from 'vue';
58
- import TvSearch from '@todovue/tv-search';
59
-
60
- const results = ref([
86
+ import { ref } from 'vue'
87
+ import { TvSearch } from '@todovue/tv-search'
88
+
89
+ const results = ref([
61
90
  {
62
91
  id: 1,
63
92
  title: 'How to use Vue 3',
@@ -76,115 +105,318 @@ You can open the search component with `Ctrl + k` or `Command + k`
76
105
  description: 'Pinia is a modern store for Vue 3',
77
106
  url: 'https://todovue.com/blog/how-to-use-pinia',
78
107
  },
79
- {
80
- id: 4,
81
- title: 'How to use Windi CSS',
82
- description: 'Windi CSS is a utility-first CSS framework',
83
- url: 'https://todovue.com/blog/how-to-use-windi-css',
84
- },
85
- {
86
- id: 5,
87
- title: 'How to use Vitesse',
88
- description: 'Vitesse is a Vue 3 starter template',
89
- url: 'https://todovue.com/blog/how-to-use-vitesse',
90
- }
91
- ]);
108
+ ])
109
+
110
+ function handleSearch(query) {
111
+ console.log('Search query:', query)
112
+ // Handle search logic here
113
+ }
114
+ </script>
115
+
116
+ <template>
117
+ <tv-search
118
+ placeholder="Search documentation..."
119
+ titleButton="Search"
120
+ :results="results"
121
+ @search="handleSearch"
122
+ />
123
+ </template>
124
+ ```
125
+
126
+ ---
127
+ ## Nuxt 3 / SSR Usage
128
+ Create a plugin file: `plugins/tv-search.client.ts` (client-only is recommended since it uses keyboard events):
129
+ ```ts
130
+ import { defineNuxtPlugin } from '#app'
131
+ import { TvSearch } from '@todovue/tv-search'
132
+
133
+ export default defineNuxtPlugin(nuxtApp => {
134
+ nuxtApp.vueApp.component('TvSearch', TvSearch)
135
+ })
136
+ ```
137
+
138
+ Or use the plugin install method:
139
+ ```ts
140
+ import { defineNuxtPlugin } from '#app'
141
+ import { TvSearch } from '@todovue/tv-search'
142
+
143
+ export default defineNuxtPlugin(nuxtApp => {
144
+ nuxtApp.vueApp.use(TvSearch)
145
+ })
146
+ ```
147
+
148
+ Then use anywhere in your Nuxt app:
149
+ ```vue
150
+ <template>
151
+ <tv-search
152
+ placeholder="Search site..."
153
+ titleButton="Search"
154
+ :results="searchResults"
155
+ @search="onSearch"
156
+ />
157
+ </template>
158
+
159
+ <script setup>
160
+ const searchResults = ref([
161
+ // your search results
162
+ ])
163
+
164
+ function onSearch(query) {
165
+ // handle search
166
+ }
167
+ </script>
168
+ ```
169
+
170
+ Optional direct import (no plugin needed):
171
+ ```vue
172
+ <script setup>
173
+ import { TvSearch } from '@todovue/tv-search'
92
174
  </script>
93
175
  ```
176
+
177
+ ---
178
+ ## Component Registration Options
179
+ | Approach | When to use |
180
+ |------------------------------------------------------|----------------------------------------------------|
181
+ | Global via `app.use(TvSearch)` | Design system / used across many pages |
182
+ | Global via `app.component('TvSearch', TvSearch)` | Custom component name / multiple search components |
183
+ | Local named import `import TvSearch from '...'` | Single page usage / code splitting |
184
+ | Nuxt plugin `.client.ts` | SSR apps with client-side interactions |
185
+
94
186
  ---
95
187
  ## Props
96
- | Name | Type | Default | Description | Required |
97
- |--------------|--------|----------------------------|-------------------|----------|
98
- | placeHolder | String | `""` | Placeholder input | `true` |
99
- | titleButton | String | `""` | Title button | `true` |
100
- | results | Array | `[]` | Array of results | `true` |
101
- | customStyles | Object | [See below](#customStyles) | Custom styles | `false` |
102
-
103
- ### customStyles
104
- | Name | Type | Default | Description |
105
- |-------------|--------|-------------|-------------------------|
106
- | bgBody | String | `"#0E131F"` | Background color body |
107
- | bgInput | String | `"#B9C4DF"` | Background color input |
108
- | bgButton | String | `"#Ef233C"` | Background color button |
109
- | colorButton | String | `"#F4FAFF"` | Color button |
188
+ | Prop | Type | Default | Description | Required |
189
+ |--------------|--------|---------|---------------------------------------------------------------------------------------|----------|
190
+ | placeholder | String | `""` | Placeholder text for the search input field | `true` |
191
+ | titleButton | String | `""` | Text displayed on the search button | `true` |
192
+ | results | Array | `[]` | Array of searchable items (see [Results Data Structure](#results-data-structure)) | `true` |
193
+ | customStyles | Object | `{}` | Custom color scheme for theming (see [Customization](#customization-styles--theming)) | `false` |
194
+
195
+ ### customStyles Object
196
+ Customize the appearance by passing a `customStyles` object with any of these properties:
197
+
198
+ | Property | Type | Default | Description |
199
+ |-------------|--------|-------------|----------------------------------------------------------|
200
+ | bgBody | String | `"#0E131F"` | Background color of the modal overlay (with 0.9 opacity) |
201
+ | bgInput | String | `"#B9C4DF"` | Background color of the search input area |
202
+ | bgButton | String | `"#EF233C"` | Background color of the search button |
203
+ | colorButton | String | `"#F4FAFF"` | Text color of the search button |
204
+
110
205
  ---
111
206
  ## Events
112
- | Name | Description |
113
- |--------|-----------------------------------|
114
- | search | Event when the search is executed |
207
+ | Event | Payload Type | Description |
208
+ |--------|--------------|-------------------------------------------------------------------------------------------------|
209
+ | search | String | Emitted when search is triggered (Enter key or button click). Returns the trimmed search query. |
115
210
 
116
- ## Customize
117
- You can customize the component by customStyles, you can see the [props](#props) section to see the available options
118
- ```js
119
- const customStyles = {
120
- bgBody: "#0A4539",
121
- bgInput: "#284780",
122
- bgButton: "#80286E",
123
- colorButton: "#D5B7B7",
124
- };
211
+ Example:
212
+ ```vue
213
+ <tv-search
214
+ placeholder="Search..."
215
+ titleButton="Go"
216
+ :results="items"
217
+ @search="handleSearch"
218
+ />
219
+
220
+ <script setup>
221
+ function handleSearch(query) {
222
+ console.log('User searched for:', query)
223
+ // Perform API call, route navigation, etc.
224
+ }
225
+ </script>
125
226
  ```
126
- ```js
227
+
228
+ ---
229
+ ## Keyboard Shortcuts
230
+ | Shortcut | Action |
231
+ |------------------------|-----------------------------------|
232
+ | `Ctrl + K` / `Cmd + K` | Open the search modal |
233
+ | `Escape` | Close the search modal |
234
+ | `Enter` | Execute search with current input |
235
+ | Click outside modal | Close the search modal |
236
+
237
+ ---
238
+ ## Customization (Styles / Theming)
239
+ You can override the default color scheme by passing a `customStyles` object:
240
+
241
+ ```vue
242
+ <script setup>
243
+ import { ref } from 'vue'
244
+ import { TvSearch } from '@todovue/tv-search'
245
+
246
+ const customStyles = ref({
247
+ bgBody: "#1e1d23",
248
+ bgInput: "#8673a1",
249
+ bgButton: "#80286e",
250
+ colorButton: "#d7c9c9",
251
+ })
252
+
253
+ const results = ref([
254
+ // your results
255
+ ])
256
+ </script>
257
+
127
258
  <template>
128
259
  <tv-search
129
- placeholder="Search blog..."
260
+ placeholder="Type to search..."
130
261
  titleButton="Search"
131
262
  :results="results"
132
263
  :customStyles="customStyles"
133
264
  />
134
265
  </template>
266
+ ```
135
267
 
136
- <script setup>
137
- import { ref } from 'vue';
138
- import TvSearch from '@todovue/tv-search';
139
-
140
- const customStyles = ref({
268
+ ### Example Custom Themes
269
+
270
+ **Dark Theme:**
271
+ ```js
272
+ const darkTheme = {
273
+ bgBody: "#0E131F",
274
+ bgInput: "#1F2937",
275
+ bgButton: "#3B82F6",
276
+ colorButton: "#FFFFFF",
277
+ }
278
+ ```
279
+
280
+ **Light Theme:**
281
+ ```js
282
+ const lightTheme = {
283
+ bgBody: "#F9FAFB",
284
+ bgInput: "#FFFFFF",
285
+ bgButton: "#6366F1",
286
+ colorButton: "#FFFFFF",
287
+ }
288
+ ```
289
+
290
+ **Brand Theme:**
291
+ ```js
292
+ const brandTheme = {
141
293
  bgBody: "#0A4539",
142
294
  bgInput: "#284780",
143
295
  bgButton: "#80286E",
144
296
  colorButton: "#D5B7B7",
145
- });
146
-
147
- const results = ref([
297
+ }
298
+ ```
299
+
300
+ ---
301
+ ## Results Data Structure
302
+ The `results` prop expects an array of objects with the following structure:
303
+
304
+ ```typescript
305
+ interface SearchResult {
306
+ id: number | string; // Unique identifier (required for :key)
307
+ title: string; // Displayed in search results (required)
308
+ description?: string; // Additional info (optional, not currently displayed)
309
+ url?: string; // Navigation target (optional, not currently used in component)
310
+ [key: string]: any; // Any additional custom properties
311
+ }
312
+ ```
313
+
314
+ Example:
315
+ ```js
316
+ const results = [
148
317
  {
149
318
  id: 1,
150
- title: 'Blog - How to use Vue 3',
151
- description: 'Vue 3 is the latest version of Vue.js',
152
- url: 'https://todovue.com/blog/how-to-use-vue-3',
319
+ title: 'Getting Started with Vue 3',
320
+ description: 'Learn the basics of Vue 3 composition API',
321
+ url: '/docs/vue3-intro',
322
+ category: 'Tutorial',
153
323
  },
154
324
  {
155
325
  id: 2,
156
- title: 'Blog - How to use Vite',
157
- description: 'Vite is a build tool for modern web development',
158
- url: 'https://todovue.com/blog/how-to-use-vite',
326
+ title: 'Understanding Reactivity',
327
+ description: 'Deep dive into Vue reactivity system',
328
+ url: '/docs/reactivity',
329
+ category: 'Advanced',
159
330
  },
160
- {
161
- id: 3,
162
- title: 'Blog - How to use Pinia',
163
- description: 'Pinia is a modern store for Vue 3',
164
- url: 'https://todovue.com/blog/how-to-use-pinia',
165
- },
166
- {
167
- id: 4,
168
- title: 'Blog - How to use Windi CSS',
169
- description: 'Windi CSS is a utility-first CSS framework',
170
- url: 'https://todovue.com/blog/how-to-use-windi-css',
171
- },
172
- {
173
- id: 5,
174
- title: 'Blog - How to use Vitesse',
175
- description: 'Vitesse is a Vue 3 starter template',
176
- url: 'https://todovue.com/blog/how-to-use-vitesse',
177
- }
178
- ]);
179
- </script>
331
+ ]
180
332
  ```
333
+
334
+ **Note**: The component currently filters results based on the `title` property matching the user input (case-insensitive). You can handle the `@search` event to implement custom search logic or navigation.
335
+
336
+ ---
337
+ ## Accessibility
338
+ - **Keyboard navigation**: Full support for `Ctrl+K`/`Cmd+K` to open, `Esc` to close, and `Enter` to search
339
+ - **Focus management**: Input automatically receives focus when modal opens and is selected for immediate typing
340
+ - **Semantic HTML**: Uses proper `<button>`, `<input>`, and modal structure
341
+ - **Click-away**: Modal closes when clicking the overlay, providing intuitive UX
342
+
343
+ **Recommendations:**
344
+ - Provide clear, descriptive `placeholder` text
345
+ - Use meaningful `titleButton` text (e.g., "Search", "Find", "Go")
346
+ - Ensure sufficient color contrast when using `customStyles`
347
+ - Consider adding `aria-label` attributes for screen reader support in future versions
348
+
349
+ ---
350
+ ## SSR Notes
351
+ - **Safe for SSR**: No direct DOM access (`window` / `document`) during module initialization
352
+ - **Event listeners**: Keyboard event listeners are registered in `onMounted` and cleaned up in `onBeforeUnmount`
353
+ - **Client-side only**: Keyboard shortcuts require browser environment; use `.client.ts` plugin in Nuxt
354
+ - **Icons**: SVG icons are loaded via Vite's `import.meta.glob`, which works in both SPA and SSR builds
355
+ - **No manual CSS import required**: Styles are automatically injected via `vite-plugin-css-injected-by-js`
356
+
357
+ ---
358
+ ## Roadmap
359
+ | Feature | Status |
360
+ |------------------------------------------|-------------|
361
+ | Support for result `url` navigation | Planned |
362
+ | Display `description` in results | Planned |
363
+ | Customizable search icon | Planned |
364
+ | Multiple keyboard shortcut options | Considering |
365
+ | Result categorization / grouping | Considering |
366
+ | Highlight matching text in results | Considering |
367
+ | Recent searches history | Considering |
368
+ | Loading state indicator | Considering |
369
+ | Pagination for large result sets | Considering |
370
+ | Fuzzy search / advanced filtering | Considering |
371
+ | Theming via CSS variables | Considering |
372
+ | TypeScript type definitions improvement | Planned |
373
+ | ARIA attributes enhancement | Planned |
374
+
375
+ ---
181
376
  ## Development
182
- Clone the repository and install the dependencies
377
+ Clone the repository and install dependencies:
183
378
  ```bash
184
379
  git clone https://github.com/TODOvue/tv-search.git
185
380
  cd tv-search
186
381
  yarn install
187
382
  ```
383
+
384
+ Run development server with demo playground:
385
+ ```bash
386
+ yarn dev
387
+ ```
388
+
389
+ Build the library:
390
+ ```bash
391
+ yarn build
392
+ ```
393
+
394
+ Build demo site:
395
+ ```bash
396
+ yarn build:demo
397
+ ```
398
+
399
+ The demo is served from Vite using `index.html` + `src/demo` examples.
400
+
401
+ ---
402
+ ## Contributing
403
+ Contributions are welcome! Please read our [Contributing Guidelines](https://github.com/TODOvue/tv-search/blob/main/CONTRIBUTING.md) and [Code of Conduct](https://github.com/TODOvue/tv-search/blob/main/CODE_OF_CONDUCT.md) before submitting PRs.
404
+
405
+ **How to contribute:**
406
+ 1. Fork the repository
407
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
408
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
409
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
410
+ 5. Open a Pull Request
411
+
412
+ ---
413
+ ## Changelog
414
+ See [CHANGELOG.md](https://github.com/TODOvue/tv-search/blob/main/CHANGELOG.md) for release history and version changes.
415
+
188
416
  ---
189
417
  ## License
190
- [MIT](https://github.com/TODOvue/tv-search/blob/main/LICENSE)
418
+ [MIT](https://github.com/TODOvue/tv-search/blob/main/LICENSE) © TODOvue
419
+
420
+ ---
421
+ ### Attributions
422
+ Crafted for the TODOvue component ecosystem
@@ -0,0 +1,6 @@
1
+ import { default as TvSearch } from './components/TvSearch.vue';
2
+ export declare const TvSearchPlugin: {
3
+ install(app: any): void;
4
+ };
5
+ export { TvSearch };
6
+ export default TvSearch;