@todovue/tv-ui 0.1.0 → 0.1.2
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 +20 -20
- package/dist/entry.d.ts +2 -2
- package/dist/tv-ui.cjs.js +1 -1
- package/dist/tv-ui.css +1 -1
- package/dist/tv-ui.es.js +50 -49
- package/package.json +4 -2
- package/dist/.htaccess +0 -8
- package/dist/demos/tv-alert/CHANGELOG.md +0 -127
- package/dist/demos/tv-alert/README.md +0 -334
- package/dist/demos/tv-article/CHANGELOG.md +0 -221
- package/dist/demos/tv-article/README.md +0 -258
- package/dist/demos/tv-breadcrumbs/CHANGELOG.md +0 -135
- package/dist/demos/tv-breadcrumbs/README.md +0 -364
- package/dist/demos/tv-button/CHANGELOG.md +0 -158
- package/dist/demos/tv-button/README.md +0 -255
- package/dist/demos/tv-card/CHANGELOG.md +0 -158
- package/dist/demos/tv-card/README.md +0 -332
- package/dist/demos/tv-demo/CHANGELOG.md +0 -352
- package/dist/demos/tv-demo/README.md +0 -229
- package/dist/demos/tv-footer/CHANGELOG.md +0 -67
- package/dist/demos/tv-footer/README.md +0 -760
- package/dist/demos/tv-hero/CHANGELOG.md +0 -137
- package/dist/demos/tv-hero/README.md +0 -410
- package/dist/demos/tv-label/CHANGELOG.md +0 -138
- package/dist/demos/tv-label/README.md +0 -357
- package/dist/demos/tv-menu/CHANGELOG.md +0 -145
- package/dist/demos/tv-menu/README.md +0 -389
- package/dist/demos/tv-modal/CHANGELOG.md +0 -127
- package/dist/demos/tv-modal/README.md +0 -466
- package/dist/demos/tv-pagination/CHANGELOG.md +0 -125
- package/dist/demos/tv-pagination/README.md +0 -275
- package/dist/demos/tv-progress-bar/CHANGELOG.md +0 -84
- package/dist/demos/tv-progress-bar/README.md +0 -894
- package/dist/demos/tv-relative-time/CHANGELOG.md +0 -122
- package/dist/demos/tv-relative-time/README.md +0 -405
- package/dist/demos/tv-scroll-top/CHANGELOG.md +0 -69
- package/dist/demos/tv-scroll-top/README.md +0 -445
- package/dist/demos/tv-search/CHANGELOG.md +0 -155
- package/dist/demos/tv-search/README.md +0 -407
- package/dist/demos/tv-settings/CHANGELOG.md +0 -94
- package/dist/demos/tv-settings/README.md +0 -314
- package/dist/demos/tv-sidebar/CHANGELOG.md +0 -229
- package/dist/demos/tv-sidebar/README.md +0 -592
- package/dist/demos/tv-theme-button/CHANGELOG.md +0 -136
- package/dist/demos/tv-theme-button/README.md +0 -392
- package/dist/demos/tv-toc/CHANGELOG.md +0 -80
- package/dist/demos/tv-toc/README.md +0 -288
- package/dist/favicon.ico +0 -0
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
<p align="center"><img width="150" src="https://res.cloudinary.com/dcdfhi8qz/image/upload/v1763663056/uqqtkgp1lg3xdplutpga.png" alt="TODOvue logo">
|
|
2
|
-
</p>
|
|
3
|
-
|
|
4
|
-
# TODOvue Sidebar (TvSidebar)
|
|
5
|
-
A versatile and flexible Vue 3 sidebar component with multiple display modes: lists, categories (labels), and images. Perfect for blogs, documentation sites, and web applications requiring sidebar navigation or content display. Compatible with both SPA and SSR environments (e.g. Nuxt 3).
|
|
6
|
-
|
|
7
|
-
[](https://www.npmjs.com/package/@todovue/tv-sidebar)
|
|
8
|
-
[](https://www.npmjs.com/package/@todovue/tv-sidebar)
|
|
9
|
-
[](https://www.npmjs.com/package/@todovue/tv-sidebar)
|
|
10
|
-

|
|
11
|
-

|
|
12
|
-

|
|
13
|
-

|
|
14
|
-

|
|
15
|
-

|
|
16
|
-
|
|
17
|
-
> Demo: https://ui.todovue.blog/sidebar
|
|
18
|
-
|
|
19
|
-
## Table of Contents
|
|
20
|
-
- [Features](#features)
|
|
21
|
-
- [Installation](#installation)
|
|
22
|
-
- [Quick Start (SPA)](#quick-start-spa)
|
|
23
|
-
- [Nuxt 4 / SSR Usage](#nuxt-4--ssr-usage)
|
|
24
|
-
- [Component Registration Options](#component-registration-options)
|
|
25
|
-
- [Props](#props)
|
|
26
|
-
- [Events](#events)
|
|
27
|
-
- [Usage Examples](#usage-examples)
|
|
28
|
-
- [Default List Mode](#default-list-mode)
|
|
29
|
-
- [Categories (Labels) Mode](#categories-labels-mode)
|
|
30
|
-
- [Image Mode](#image-mode)
|
|
31
|
-
- [With Limit](#with-limit)
|
|
32
|
-
- [Data Structure](#data-structure)
|
|
33
|
-
- [Styling](#styling)
|
|
34
|
-
- [Navigation Handling](#navigation-handling)
|
|
35
|
-
- [Accessibility](#accessibility)
|
|
36
|
-
- [SSR Notes](#ssr-notes)
|
|
37
|
-
- [Development](#development)
|
|
38
|
-
- [Contributing](#contributing)
|
|
39
|
-
- [License](#license)
|
|
40
|
-
|
|
41
|
-
## Features
|
|
42
|
-
- **Four display modes**: List, Categories (labels), Image, and Grouped/Categorized
|
|
43
|
-
- **Hierarchical grouping**: Organize content with collapsible sections and item counters
|
|
44
|
-
- **Event-driven interactions**: No built-in navigation; emits click events with full objects
|
|
45
|
-
- **Item limit**: Control how many items to display with the `limit` prop
|
|
46
|
-
- **Search/Filter**: Real-time filtering across all display modes including grouped content
|
|
47
|
-
- **Optional clickable images**: Enable with `clickable` to emit click events for images
|
|
48
|
-
- **Label/Category support**: Display colored category labels with click events
|
|
49
|
-
- **Responsive design**: Adapts to different screen sizes
|
|
50
|
-
- **SSR compatible**: Works seamlessly in Nuxt 3 and SSR contexts
|
|
51
|
-
- **Customizable styling**: Built with SCSS for easy theming
|
|
52
|
-
- **Tree-shakeable**: Vue marked as external dependency
|
|
53
|
-
|
|
54
|
-
## Installation
|
|
55
|
-
Using npm:
|
|
56
|
-
```bash
|
|
57
|
-
npm install @todovue/tv-sidebar
|
|
58
|
-
```
|
|
59
|
-
Using yarn:
|
|
60
|
-
```bash
|
|
61
|
-
yarn add @todovue/tv-sidebar
|
|
62
|
-
```
|
|
63
|
-
Using pnpm:
|
|
64
|
-
```bash
|
|
65
|
-
pnpm add @todovue/tv-sidebar
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
> **Note**: This component depends on `@todovue/tv-label` for the categories mode.
|
|
69
|
-
|
|
70
|
-
## Quick Start (SPA)
|
|
71
|
-
Global registration (main.js / main.ts):
|
|
72
|
-
```js
|
|
73
|
-
import { createApp } from 'vue'
|
|
74
|
-
import App from './App.vue'
|
|
75
|
-
import TvSidebar from '@todovue/tv-sidebar'
|
|
76
|
-
import '@todovue/tv-sidebar/style.css' // import styles
|
|
77
|
-
import '@todovue/tv-label/style.css' // import styles
|
|
78
|
-
|
|
79
|
-
createApp(App)
|
|
80
|
-
.use(TvSidebar) // enables <TvSidebar /> globally
|
|
81
|
-
.mount('#app')
|
|
82
|
-
```
|
|
83
|
-
Local import inside a component:
|
|
84
|
-
```vue
|
|
85
|
-
<script setup>
|
|
86
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
87
|
-
import '@todovue/tv-sidebar/style.css' // import styles
|
|
88
|
-
import '@todovue/tv-label/style.css' // import styles
|
|
89
|
-
|
|
90
|
-
const sidebarData = {
|
|
91
|
-
title: "Most Popular Blogs",
|
|
92
|
-
list: [
|
|
93
|
-
{
|
|
94
|
-
id: 1,
|
|
95
|
-
title: "10 Tips for Creating a Successful YouTube Channel",
|
|
96
|
-
link: "/blog/youtube-tips",
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: 2,
|
|
100
|
-
title: "The Benefits of Meditation",
|
|
101
|
-
link: "/blog/meditation",
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
</script>
|
|
106
|
-
|
|
107
|
-
<template>
|
|
108
|
-
<TvSidebar :data="sidebarData" />
|
|
109
|
-
</template>
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Nuxt 4 / SSR Usage
|
|
113
|
-
Add the stylesheet to your `nuxt.config.ts`:
|
|
114
|
-
```ts
|
|
115
|
-
// nuxt.config.ts
|
|
116
|
-
export default defineNuxtConfig({
|
|
117
|
-
modules: [
|
|
118
|
-
'@todovue/tv-card/nuxt'
|
|
119
|
-
]
|
|
120
|
-
})
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Create a plugin file: `plugins/tv-sidebar.client.ts` (or without `.client` suffix for SSR):
|
|
124
|
-
```ts
|
|
125
|
-
import { defineNuxtPlugin } from '#app'
|
|
126
|
-
import TvSidebar from '@todovue/tv-sidebar'
|
|
127
|
-
|
|
128
|
-
export default defineNuxtPlugin(nuxtApp => {
|
|
129
|
-
nuxtApp.vueApp.use(TvSidebar)
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
Use anywhere (no router dependency required):
|
|
133
|
-
```vue
|
|
134
|
-
<script setup>
|
|
135
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
136
|
-
</script>
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Component Registration Options
|
|
140
|
-
| Approach | When to use |
|
|
141
|
-
|---------------------------------------------------------------------|------------------------------------------------|
|
|
142
|
-
| Global via `app.use(TvSidebar)` | Many usages across app / design system install |
|
|
143
|
-
| Local named import `{ TvSidebar }` | Isolated / code-split contexts |
|
|
144
|
-
| Direct default import `import TvSidebar from '@todovue/tv-sidebar'` | Single usage or manual registration |
|
|
145
|
-
|
|
146
|
-
## Props
|
|
147
|
-
| Prop | Type | Default | Description |
|
|
148
|
-
|-------------------|---------|---------------|------------------------------------------------------------------------------------------------------------------------------------------|
|
|
149
|
-
| data | Object | `{}` | Main data object containing title and content (list, labels, or image). |
|
|
150
|
-
| isImage | Boolean | `false` | Enables image display mode. |
|
|
151
|
-
| isLabel | Boolean | `false` | Enables categories/labels display mode. |
|
|
152
|
-
| isOutline | Boolean | `false` | Apply outline style to labels (only works with `isLabel`). |
|
|
153
|
-
| size | String | `'md'` | Sets size of labels (`sm`, `md`, `lg`). Only works with `isLabel`. |
|
|
154
|
-
| limit | Number | `0` | Maximum number of items to display (0 = show all). |
|
|
155
|
-
| clickable | Boolean | `false` | When `true` and `isImage`, the image becomes interactive and emits a `click` event with the image object. When `false`, image is static. |
|
|
156
|
-
| searchable | Boolean | `false` | Enables search/filter input for filtering items in real-time across all display modes. |
|
|
157
|
-
| searchPlaceholder | String | `'Search...'` | Placeholder text for the search input field. |
|
|
158
|
-
| grouped | Boolean | `false` | Enables grouped/categorized mode with collapsible sections. Requires `data.groups` array instead of `data.list` or `data.labels`. |
|
|
159
|
-
| newLabelText | String | `'New'` | Text to display in the "New" badge when `isNew` is true on an item. |
|
|
160
|
-
| newLabelColor | String | `'#FF3B30'` | Hex color code for the "New" badge background. |
|
|
161
|
-
|
|
162
|
-
## Events
|
|
163
|
-
| Event name (kebab) | Emits (camel) | Description |
|
|
164
|
-
|--------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
165
|
-
| `click` | `click` | Emitted when a list item is clicked, when a label is clicked, and when an image is clicked (if `clickable`). The payload is always the full object. |
|
|
166
|
-
| `search` | `search` | Emitted when the search query changes. The payload is the search string. |
|
|
167
|
-
|
|
168
|
-
Usage:
|
|
169
|
-
```vue
|
|
170
|
-
<TvSidebar
|
|
171
|
-
isLabel
|
|
172
|
-
searchable
|
|
173
|
-
:data="categoriesData"
|
|
174
|
-
@click="handleAnyClick"
|
|
175
|
-
@search="handleSearch"
|
|
176
|
-
/>
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Usage Examples
|
|
180
|
-
|
|
181
|
-
### Default List Mode
|
|
182
|
-
Display a numbered list of items that emit the full object on click:
|
|
183
|
-
```vue
|
|
184
|
-
<script setup>
|
|
185
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
186
|
-
|
|
187
|
-
const listData = {
|
|
188
|
-
title: "Most Popular Blogs",
|
|
189
|
-
list: [
|
|
190
|
-
{id: 1, title: "10 Tips for Creating a Successful YouTube Channel", link: "/blog/youtube-tips"},
|
|
191
|
-
{id: 2, title: "The Benefits of Meditation and How to Get Started", link: "/blog/meditation"},
|
|
192
|
-
{id: 3, title: "The Top 5 Destinations for Adventure Travel", link: "/blog/adventure-travel"}
|
|
193
|
-
],
|
|
194
|
-
}
|
|
195
|
-
function handleItemClick(item) {
|
|
196
|
-
console.log('Item clicked:', item)
|
|
197
|
-
}
|
|
198
|
-
</script>
|
|
199
|
-
|
|
200
|
-
<template>
|
|
201
|
-
<TvSidebar :data="listData" @click="handleItemClick" />
|
|
202
|
-
</template>
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Categories (Labels) Mode
|
|
206
|
-
Display colored category labels:
|
|
207
|
-
```vue
|
|
208
|
-
<script setup>
|
|
209
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
210
|
-
|
|
211
|
-
const categoriesData = {
|
|
212
|
-
title: "Categories",
|
|
213
|
-
labels: [
|
|
214
|
-
{
|
|
215
|
-
id: 1,
|
|
216
|
-
name: "Vue.js",
|
|
217
|
-
color: "#4FC08D",
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
id: 2,
|
|
221
|
-
name: "JavaScript",
|
|
222
|
-
color: "#F0DB4F",
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
id: 3,
|
|
226
|
-
name: "HTML",
|
|
227
|
-
color: "#E34F26",
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
id: 4,
|
|
231
|
-
name: "CSS",
|
|
232
|
-
color: "#1572B6",
|
|
233
|
-
}
|
|
234
|
-
]
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function handleCategoryClick(category) {
|
|
238
|
-
console.log('Category clicked:', category)
|
|
239
|
-
// Navigate or filter by category
|
|
240
|
-
}
|
|
241
|
-
</script>
|
|
242
|
-
|
|
243
|
-
<template>
|
|
244
|
-
<TvSidebar
|
|
245
|
-
isLabel
|
|
246
|
-
:data="categoriesData"
|
|
247
|
-
@clickLabel="handleCategoryClick"
|
|
248
|
-
/>
|
|
249
|
-
</template>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### Image Mode
|
|
253
|
-
Display an image with title. Make it interactive with `clickable` if desired:
|
|
254
|
-
```vue
|
|
255
|
-
<script setup>
|
|
256
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
257
|
-
|
|
258
|
-
const imageData = {
|
|
259
|
-
title: "TODOvue Blog",
|
|
260
|
-
image: {
|
|
261
|
-
src: "https://todovue.com/vue.webp",
|
|
262
|
-
alt: "TODOvue Logo",
|
|
263
|
-
link: "https://todovue.com/"
|
|
264
|
-
},
|
|
265
|
-
}
|
|
266
|
-
function handleImageClick(image) {
|
|
267
|
-
console.log('Image clicked:', image)
|
|
268
|
-
}
|
|
269
|
-
</script>
|
|
270
|
-
|
|
271
|
-
<template>
|
|
272
|
-
<!-- Non-clickable image -->
|
|
273
|
-
<TvSidebar isImage :data="imageData" />
|
|
274
|
-
|
|
275
|
-
<!-- Clickable image that emits the image object -->
|
|
276
|
-
<TvSidebar isImage clickable :data="imageData" @click="handleImageClick" />
|
|
277
|
-
</template>
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### With Limit
|
|
281
|
-
Limit the number of displayed items:
|
|
282
|
-
```vue
|
|
283
|
-
<template>
|
|
284
|
-
<!-- Show only first 5 items -->
|
|
285
|
-
<TvSidebar :data="listData" :limit="5" />
|
|
286
|
-
|
|
287
|
-
<!-- Show only first 8 categories -->
|
|
288
|
-
<TvSidebar isLabel :data="categoriesData" :limit="8" />
|
|
289
|
-
</template>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### With Search/Filter
|
|
293
|
-
Enable real-time search and filtering across all display modes:
|
|
294
|
-
```vue
|
|
295
|
-
<script setup>
|
|
296
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
297
|
-
|
|
298
|
-
const listData = {
|
|
299
|
-
title: 'Blog Posts',
|
|
300
|
-
list: [
|
|
301
|
-
{ id: 1, title: 'Getting Started with Vue 3', link: '/blog/vue-3' },
|
|
302
|
-
{ id: 2, title: 'Understanding Composition API', link: '/blog/composition-api' },
|
|
303
|
-
{ id: 3, title: 'Building Reactive Forms', link: '/blog/reactive-forms' }
|
|
304
|
-
]
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const categoriesData = {
|
|
308
|
-
title: 'Categories',
|
|
309
|
-
labels: [
|
|
310
|
-
{ id: 1, name: 'Vue.js', color: '#4FC08D' },
|
|
311
|
-
{ id: 2, name: 'JavaScript', color: '#F0DB4F' },
|
|
312
|
-
{ id: 3, name: 'TypeScript', color: '#007ACC' }
|
|
313
|
-
]
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function handleSearch(query) {
|
|
317
|
-
console.log('Search query:', query)
|
|
318
|
-
// Optionally perform additional actions
|
|
319
|
-
}
|
|
320
|
-
</script>
|
|
321
|
-
|
|
322
|
-
<template>
|
|
323
|
-
<!-- Searchable list with default placeholder -->
|
|
324
|
-
<TvSidebar
|
|
325
|
-
:data="listData"
|
|
326
|
-
searchable
|
|
327
|
-
@search="handleSearch"
|
|
328
|
-
/>
|
|
329
|
-
|
|
330
|
-
<!-- Searchable labels with custom placeholder -->
|
|
331
|
-
<TvSidebar
|
|
332
|
-
isLabel
|
|
333
|
-
:data="categoriesData"
|
|
334
|
-
searchable
|
|
335
|
-
search-placeholder="Filter categories..."
|
|
336
|
-
@search="handleSearch"
|
|
337
|
-
/>
|
|
338
|
-
|
|
339
|
-
<!-- Searchable with limit (filtering applied before limiting) -->
|
|
340
|
-
<TvSidebar
|
|
341
|
-
:data="listData"
|
|
342
|
-
searchable
|
|
343
|
-
:limit="5"
|
|
344
|
-
/>
|
|
345
|
-
</template>
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### Grouped/Categorized Mode
|
|
349
|
-
Organize content hierarchically with collapsible groups and item counters:
|
|
350
|
-
```vue
|
|
351
|
-
<script setup>
|
|
352
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
353
|
-
|
|
354
|
-
const groupedData = {
|
|
355
|
-
title: 'Blog Posts',
|
|
356
|
-
groups: [
|
|
357
|
-
{
|
|
358
|
-
id: 1,
|
|
359
|
-
name: 'Technical',
|
|
360
|
-
collapsed: false,
|
|
361
|
-
items: [
|
|
362
|
-
{ id: 1, title: '10 Tips for Creating a Successful YouTube Channel', link: '/blog/youtube' },
|
|
363
|
-
{ id: 2, title: 'How to Create High-Quality Visual Content', link: '/blog/visual-content' },
|
|
364
|
-
{ id: 3, title: 'The Power of Email Marketing', link: '/blog/email-marketing' }
|
|
365
|
-
]
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
id: 2,
|
|
369
|
-
name: 'Lifestyle',
|
|
370
|
-
collapsed: true,
|
|
371
|
-
items: [
|
|
372
|
-
{ id: 4, title: 'Why You Should Consider a Plant-Based Diet', link: '/blog/plant-based' },
|
|
373
|
-
{ id: 5, title: 'The Pros and Cons of Remote Work', link: '/blog/remote-work' }
|
|
374
|
-
]
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
id: 3,
|
|
378
|
-
name: 'Travel',
|
|
379
|
-
collapsed: false,
|
|
380
|
-
items: [
|
|
381
|
-
{ id: 6, title: 'The Top 5 Destinations for Adventure Travel', link: '/blog/adventure' }
|
|
382
|
-
]
|
|
383
|
-
}
|
|
384
|
-
]
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function handleItemClick(item) {
|
|
388
|
-
console.log('Item clicked:', item)
|
|
389
|
-
}
|
|
390
|
-
</script>
|
|
391
|
-
|
|
392
|
-
<template>
|
|
393
|
-
<!-- Basic grouped mode -->
|
|
394
|
-
<TvSidebar
|
|
395
|
-
grouped
|
|
396
|
-
:data="groupedData"
|
|
397
|
-
@click="handleItemClick"
|
|
398
|
-
/>
|
|
399
|
-
|
|
400
|
-
<!-- Grouped mode with search -->
|
|
401
|
-
<TvSidebar
|
|
402
|
-
grouped
|
|
403
|
-
searchable
|
|
404
|
-
search-placeholder="Search posts..."
|
|
405
|
-
:data="groupedData"
|
|
406
|
-
@click="handleItemClick"
|
|
407
|
-
/>
|
|
408
|
-
|
|
409
|
-
<!-- Grouped mode with limit (applies to items per group) -->
|
|
410
|
-
<TvSidebar
|
|
411
|
-
grouped
|
|
412
|
-
:limit="2"
|
|
413
|
-
:data="groupedData"
|
|
414
|
-
@click="handleItemClick"
|
|
415
|
-
/>
|
|
416
|
-
</template>
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### New Items Indicator
|
|
420
|
-
Mark items as new by adding `isNew: true` to the item object. Customize the label text with `newLabelText`.
|
|
421
|
-
|
|
422
|
-
```vue
|
|
423
|
-
<script setup>
|
|
424
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
425
|
-
|
|
426
|
-
const listData = {
|
|
427
|
-
title: "Updates",
|
|
428
|
-
list: [
|
|
429
|
-
{ id: 1, title: "Stable Release", link: "/v1", isNew: true },
|
|
430
|
-
{ id: 2, title: "Beta Release", link: "/beta" }
|
|
431
|
-
]
|
|
432
|
-
}
|
|
433
|
-
</script>
|
|
434
|
-
|
|
435
|
-
<template>
|
|
436
|
-
<TvSidebar :data="listData" new-label-text="UPDATED" />
|
|
437
|
-
</template>
|
|
438
|
-
```
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### Nuxt Integration
|
|
442
|
-
```diff
|
|
443
|
-
- Using with Nuxt routing:
|
|
444
|
-
- <TvSidebar linkTag="nuxt-link" :data="blogPosts" />
|
|
445
|
-
```
|
|
446
|
-
Nuxt works without router integration in the component. Handle navigation in your click handlers (see Navigation Handling below).
|
|
447
|
-
|
|
448
|
-
## Data Structure
|
|
449
|
-
|
|
450
|
-
### List Mode Data
|
|
451
|
-
```typescript
|
|
452
|
-
{
|
|
453
|
-
title: string,
|
|
454
|
-
list: Array<{
|
|
455
|
-
id: number | string,
|
|
456
|
-
title: string,
|
|
457
|
-
link?: string, // optional; use in your click handler if you want to navigate
|
|
458
|
-
isNew?: boolean // optional; displays a 'New' badge
|
|
459
|
-
}>
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Labels/Categories Mode Data
|
|
464
|
-
```typescript
|
|
465
|
-
{
|
|
466
|
-
title: string,
|
|
467
|
-
labels: Array<{
|
|
468
|
-
id: number | string,
|
|
469
|
-
name: string,
|
|
470
|
-
color: string // Hex color code
|
|
471
|
-
}>
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
### Image Mode Data
|
|
476
|
-
```typescript
|
|
477
|
-
{
|
|
478
|
-
title: string,
|
|
479
|
-
image: {
|
|
480
|
-
src: string, // Image URL
|
|
481
|
-
alt: string, // Alt text for accessibility
|
|
482
|
-
link?: string // optional; use in your click handler for manual navigation
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
### Grouped Mode Data
|
|
488
|
-
```typescript
|
|
489
|
-
{
|
|
490
|
-
title: string,
|
|
491
|
-
groups: Array<{
|
|
492
|
-
id: number | string,
|
|
493
|
-
name: string, // Group/category name
|
|
494
|
-
collapsed: boolean, // Initial collapsed state
|
|
495
|
-
items: Array<{
|
|
496
|
-
id: number | string,
|
|
497
|
-
title: string,
|
|
498
|
-
link?: string, // optional; use in your click handler if you want to navigate
|
|
499
|
-
isNew?: boolean // optional; displays a 'New' badge
|
|
500
|
-
}>
|
|
501
|
-
}>
|
|
502
|
-
}
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
## Styling
|
|
506
|
-
The component uses SCSS for styling. Styles are automatically included when you import the component. The sidebar includes:
|
|
507
|
-
- Clean, minimal design
|
|
508
|
-
- Responsive layout
|
|
509
|
-
- Title with separator line
|
|
510
|
-
- Hover effects on interactive items
|
|
511
|
-
- Proper spacing and typography
|
|
512
|
-
|
|
513
|
-
To customize styles, you can override the CSS classes:
|
|
514
|
-
```css
|
|
515
|
-
.tv-sidebar-body {
|
|
516
|
-
/* Container styles */
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
.tv-sidebar {
|
|
520
|
-
/* Main sidebar styles */
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
.tv-sidebar-title h1 {
|
|
524
|
-
/* Title styles */
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.tv-sidebar-content-li {
|
|
528
|
-
/* List item styles */
|
|
529
|
-
}
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
## Navigation Handling
|
|
533
|
-
Since the component does not perform navigation, handle it in your click handlers. Example with Vue Router:
|
|
534
|
-
```vue
|
|
535
|
-
<script setup>
|
|
536
|
-
import { TvSidebar } from '@todovue/tv-sidebar'
|
|
537
|
-
import { useRouter } from 'vue-router'
|
|
538
|
-
|
|
539
|
-
const router = useRouter()
|
|
540
|
-
|
|
541
|
-
const listData = {
|
|
542
|
-
title: 'Recent Posts',
|
|
543
|
-
list: [
|
|
544
|
-
{ id: 1, title: 'Getting Started with Nuxt 3', link: '/blog/nuxt-3' },
|
|
545
|
-
{ id: 2, title: 'Vue Composition API', link: '/blog/composition-api' }
|
|
546
|
-
]
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function handleClick(item) {
|
|
550
|
-
if (item?.link) router.push(item.link)
|
|
551
|
-
}
|
|
552
|
-
</script>
|
|
553
|
-
|
|
554
|
-
<template>
|
|
555
|
-
<TvSidebar :data="listData" @click="handleClick" />
|
|
556
|
-
</template>
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
## Accessibility
|
|
560
|
-
- Semantic structure with clear headings
|
|
561
|
-
- Alt text support for images
|
|
562
|
-
- Interactive items emit click events; if you need keyboard accessibility, consider handling `keydown` (Enter/Space) on your side or wrapping with accessible elements/roles
|
|
563
|
-
- Color contrast considerations for labels
|
|
564
|
-
|
|
565
|
-
## SSR Notes
|
|
566
|
-
- No direct DOM access (`window` / `document`) → safe for SSR
|
|
567
|
-
- Compatible with Nuxt 3 out of the box
|
|
568
|
-
- Styles are bundled and auto-imported
|
|
569
|
-
- No router/nuxt-link dependency inside the component
|
|
570
|
-
|
|
571
|
-
## Development
|
|
572
|
-
```bash
|
|
573
|
-
git clone https://github.com/TODOvue/tv-sidebar.git
|
|
574
|
-
cd tv-sidebar
|
|
575
|
-
npm install
|
|
576
|
-
npm run dev # run demo playground
|
|
577
|
-
npm run build # build library
|
|
578
|
-
```
|
|
579
|
-
Local demo served from Vite using `index.html` + `src/demo` examples.
|
|
580
|
-
|
|
581
|
-
## Contributing
|
|
582
|
-
PRs and issues welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
|
583
|
-
|
|
584
|
-
## License
|
|
585
|
-
MIT © TODOvue
|
|
586
|
-
|
|
587
|
-
## Dependencies
|
|
588
|
-
- `vue` (^3.0.0) - Peer dependency
|
|
589
|
-
- `@todovue/tv-label` - Used for category/label display mode
|
|
590
|
-
|
|
591
|
-
### Attributions
|
|
592
|
-
Crafted with ❤️ for the TODOvue component ecosystem by Cristhian Daza
|