@veristone/nuxt-v-app 0.2.7 → 0.2.8

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.
@@ -3,106 +3,130 @@
3
3
  * VACard - Veristone Card
4
4
  * Matches XACard API exactly.
5
5
  */
6
- const props = withDefaults(defineProps<{
7
- title?: string
8
- description?: string
9
- icon?: string
10
- collapsible?: boolean
11
- collapsed?: boolean
12
- padding?: boolean
13
- divider?: boolean
14
- variant?: 'default' | 'error' | 'warning' | 'success'
15
- }>(), {
16
- title: '',
17
- description: '',
18
- icon: '',
19
- collapsible: false,
20
- collapsed: false,
21
- padding: true,
22
- divider: true,
23
- variant: 'default'
24
- })
6
+ const props = withDefaults(
7
+ defineProps<{
8
+ title?: string;
9
+ description?: string;
10
+ icon?: string;
11
+ collapsible?: boolean;
12
+ collapsed?: boolean;
13
+ padding?: boolean;
14
+ divider?: boolean;
15
+ variant?: "default" | "error" | "warning" | "success";
16
+ }>(),
17
+ {
18
+ title: "",
19
+ description: "",
20
+ icon: "",
21
+ collapsible: false,
22
+ collapsed: false,
23
+ padding: true,
24
+ divider: true,
25
+ variant: "default",
26
+ }
27
+ );
25
28
 
26
- const isCollapsed = ref(props.collapsed)
27
- watch(() => props.collapsed, (val) => isCollapsed.value = val)
29
+ const isCollapsed = ref(props.collapsed);
30
+ watch(
31
+ () => props.collapsed,
32
+ (val) => (isCollapsed.value = val)
33
+ );
28
34
 
29
35
  const toggleCollapse = () => {
30
- if (props.collapsible) isCollapsed.value = !isCollapsed.value
31
- }
36
+ if (props.collapsible) isCollapsed.value = !isCollapsed.value;
37
+ };
32
38
 
33
39
  const variantClass = computed(() => {
34
- switch (props.variant) {
35
- case 'error': return 'border-red-200 dark:border-red-900 bg-red-50/50 dark:bg-red-900/10'
36
- case 'warning': return 'border-amber-200 dark:border-amber-900 bg-amber-50/50 dark:bg-amber-900/10'
37
- case 'success': return 'border-green-200 dark:border-green-900 bg-green-50/50 dark:bg-green-900/10'
38
- default: return 'border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900'
39
- }
40
- })
40
+ switch (props.variant) {
41
+ case "error":
42
+ return "border-red-200 dark:border-red-900 bg-red-50/50 dark:bg-red-900/10";
43
+ case "warning":
44
+ return "border-amber-200 dark:border-amber-900 bg-amber-50/50 dark:bg-amber-900/10";
45
+ case "success":
46
+ return "border-green-200 dark:border-green-900 bg-green-50/50 dark:bg-green-900/10";
47
+ default:
48
+ return "border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900";
49
+ }
50
+ });
41
51
  </script>
42
52
 
43
53
  <template>
44
- <div
45
- class="va-card rounded-xl border transition-all duration-200 shadow-[0_2px_9px_rgba(0,0,0,0.08)] hover:shadow-[0_4px_12px_rgba(61,113,136,0.12)] hover:-translate-y-0.5"
46
- :class="variantClass"
47
- >
48
- <!-- Header -->
49
- <div
50
- v-if="title || $slots['header-actions'] || icon"
51
- class="flex items-center justify-between"
52
- :class="[
53
- padding ? 'px-5 py-4' : 'px-4 py-3',
54
- divider && !isCollapsed ? 'border-b border-gray-100 dark:border-gray-800' : ''
55
- ]"
56
- >
57
- <div class="flex items-center gap-3 overflow-hidden">
58
- <slot name="header">
59
- <div v-if="icon" class="flex-shrink-0 p-1.5 rounded-md bg-gray-100 dark:bg-gray-800 text-gray-500">
60
- <UIcon :name="icon" class="w-5 h-5" />
61
- </div>
62
- <div class="min-w-0">
63
- <div class="flex items-center gap-2">
64
- <h3 class="font-bold text-gray-900 dark:text-white truncate text-base">{{ title }}</h3>
65
-
66
- <button
67
- v-if="collapsible"
68
- @click="toggleCollapse"
69
- class="ml-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors focus:outline-none"
70
- >
71
- <UIcon
72
- name="i-lucide-chevron-down"
73
- class="w-4 h-4 transition-transform duration-300"
74
- :class="{ '-rotate-180': !isCollapsed }"
75
- />
76
- </button>
77
- </div>
78
- <p v-if="description" class="text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5 font-medium">{{ description }}</p>
79
- </div>
80
- </slot>
81
- </div>
54
+ <div
55
+ class="va-card rounded-xl border transition-all duration-200 shadow-[0_2px_9px_rgba(0,0,0,0.08)] hover:shadow-[0_4px_12px_rgba(61,113,136,0.12)]"
56
+ :class="variantClass"
57
+ >
58
+ <!-- Header -->
59
+ <div
60
+ v-if="title || $slots['header-actions'] || icon"
61
+ class="flex items-center justify-between"
62
+ :class="[
63
+ padding ? 'px-5 py-4' : 'px-4 py-3',
64
+ divider && !isCollapsed
65
+ ? 'border-b border-gray-100 dark:border-gray-800'
66
+ : '',
67
+ ]"
68
+ >
69
+ <div class="flex items-center gap-3 overflow-hidden">
70
+ <slot name="header">
71
+ <div
72
+ v-if="icon"
73
+ class="flex-shrink-0 p-1.5 rounded-md bg-gray-100 dark:bg-gray-800 text-gray-500"
74
+ >
75
+ <UIcon :name="icon" class="w-5 h-5" />
76
+ </div>
77
+ <div class="min-w-0">
78
+ <div class="flex items-center gap-2">
79
+ <h3
80
+ class="font-bold text-gray-900 dark:text-white truncate text-base"
81
+ >
82
+ {{ title }}
83
+ </h3>
82
84
 
83
- <!-- XACard uses 'header-actions' slot -->
84
- <div v-if="$slots['header-actions']" class="flex-shrink-0 flex items-center gap-2 ml-4">
85
- <slot name="header-actions" />
86
- </div>
87
- </div>
85
+ <button
86
+ v-if="collapsible"
87
+ @click="toggleCollapse"
88
+ class="ml-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors focus:outline-none"
89
+ >
90
+ <UIcon
91
+ name="i-lucide-chevron-down"
92
+ class="w-4 h-4 transition-transform duration-300"
93
+ :class="{ '-rotate-180': !isCollapsed }"
94
+ />
95
+ </button>
96
+ </div>
97
+ <p
98
+ v-if="description"
99
+ class="text-xs text-gray-500 dark:text-gray-400 truncate mt-0.5 font-medium"
100
+ >
101
+ {{ description }}
102
+ </p>
103
+ </div>
104
+ </slot>
105
+ </div>
88
106
 
89
- <!-- Body -->
90
- <div
91
- v-show="!isCollapsed"
92
- class="transition-all duration-300 ease-in-out"
93
- >
94
- <div :class="{ 'p-5': padding, 'p-0': !padding }">
95
- <slot />
96
- </div>
107
+ <!-- XACard uses 'header-actions' slot -->
108
+ <div
109
+ v-if="$slots['header-actions']"
110
+ class="flex-shrink-0 flex items-center gap-2 ml-4"
111
+ >
112
+ <slot name="header-actions" />
113
+ </div>
114
+ </div>
97
115
 
98
- <!-- Footer -->
99
- <div
100
- v-if="$slots.footer"
101
- class="bg-gray-50 dark:bg-gray-800/50 border-t border-gray-100 dark:border-gray-800"
102
- :class="padding ? 'px-5 py-3' : 'px-4 py-2'"
103
- >
104
- <slot name="footer" />
105
- </div>
106
- </div>
107
- </div>
116
+ <!-- Body -->
117
+ <div v-show="!isCollapsed" class="transition-all duration-300 ease-in-out">
118
+ <div :class="{ 'p-5': padding, 'p-0': !padding }">
119
+ <slot />
120
+ </div>
121
+
122
+ <!-- Footer -->
123
+ <div
124
+ v-if="$slots.footer"
125
+ class="bg-gray-50 dark:bg-gray-800/50 border-t border-gray-100 dark:border-gray-800"
126
+ :class="padding ? 'px-5 py-3' : 'px-4 py-2'"
127
+ >
128
+ <slot name="footer" />
129
+ </div>
130
+ </div>
131
+ </div>
108
132
  </template>
@@ -59,6 +59,8 @@
59
59
  class="w-40"
60
60
  />
61
61
  </template>
62
+
63
+ <slot name="toolbar-left" />
62
64
  </div>
63
65
 
64
66
  <div class="flex items-center gap-2">
@@ -120,7 +122,7 @@
120
122
  }"
121
123
  empty="Nothing to show."
122
124
  v-bind="$attrs"
123
- @row-click="handleRowClick"
125
+ :on-select="props.onRowClick ? (_e: any, row: any) => handleRowClick(row) : undefined"
124
126
  >
125
127
  <template v-for="slotEntry in forwardedSlots" :key="slotEntry.target" #[slotEntry.target]="slotProps">
126
128
  <slot :name="slotEntry.source" v-bind="slotProps" />
@@ -163,40 +165,50 @@
163
165
  </UCard>
164
166
  </template>
165
167
 
166
- <script setup lang="ts">
167
- import { getPaginationRowModel } from "@tanstack/vue-table";
168
- import type { TableColumn, DropdownMenuItem } from "@nuxt/ui";
169
- import type { SortingState } from "@tanstack/vue-table";
170
- import { h, resolveComponent, useSlots } from 'vue';
171
- import CellRenderer from './CellRenderer.vue';
172
- import FilterChips from './FilterChips.vue';
173
-
174
- type FilterDefinition = {
175
- label: string;
176
- key: string;
177
- options: { label: string; value: string }[];
178
- };
179
-
180
- const props = withDefaults(defineProps<{
181
- name?: string;
182
- data: unknown[];
183
- columns: TableColumn<unknown>[];
184
- loading?: boolean;
185
- initialPageLimit?: number;
186
- selectable?: boolean;
187
- showSearch?: boolean;
188
- filters?: FilterDefinition[];
189
- onRefresh?: () => void | Promise<void>;
190
- onRowClick?: (row: unknown) => void;
191
- defaultSort?: string;
192
- defaultSortDesc?: boolean;
193
- manualPagination?: boolean;
194
- total?: number;
195
- }>(), {
196
- selectable: false,
197
- showSearch: false,
198
- manualPagination: false,
199
- });
168
+ /**
169
+ * VATable - Veristone data table component built on Nuxt UI's UTable + TanStack Table.
170
+ *
171
+ * @props
172
+ * name - Optional card header title
173
+ * data - Row data array
174
+ * columns - TanStack TableColumn definitions; supports `meta.preset` for cell rendering
175
+ * loading - Shows skeleton on initial load; shows inline spinner on subsequent refreshes
176
+ * initialPageLimit - Default page size (default: 10)
177
+ * selectable - Enables row checkboxes; exposes `selectedRows` via template ref
178
+ * showSearch - Renders a global search input in the toolbar
179
+ * filters - Array of { key, label, options } for toolbar USelect dropdowns
180
+ * onRefresh - Async callback; shows a refresh spinner button in the toolbar
181
+ * onRowClick - Called with the row's original data when a row is clicked
182
+ * defaultSort - Column key to sort by on mount
183
+ * defaultSortDesc - Sort descending when defaultSort is set (default: false)
184
+ * manualPagination - Enables server-side pagination mode
185
+ * total - Total row count for server-side pagination
186
+ *
187
+ * @models (v-model)
188
+ * sorting - SortingState — sync sort state with parent
189
+ * filterValues - Record<string, unknown> — sync active filter values with parent
190
+ * globalFilter - string — sync search input value with parent
191
+ * page - number (1-based) current page in manual pagination mode
192
+ * itemsPerPage - number page size in manual pagination mode
193
+ *
194
+ * @slots
195
+ * header-right - Content rendered on the right side of the card header
196
+ * toolbar-left - Custom controls (e.g. filter selects) injected into the toolbar
197
+ * next to the search input and built-in filter dropdowns
198
+ * Example:
199
+ * <template #toolbar-left>
200
+ * <USelect v-model="status" :items="opts" placeholder="Status" size="sm" />
201
+ * </template>
202
+ * bulk-actions - Shown in the toolbar when rows are selected; receives
203
+ * { selected, count, clear }
204
+ * actions-cell - Renders an "Actions" column; receives { row }
205
+ * [column]-cell - Override cell rendering for a specific column by accessor key
206
+ *
207
+ * @exposes
208
+ * selectedRows - Array of selected row originals
209
+ * rowSelection - Raw TanStack row selection state
210
+ * clearSelection - Clears the current row selection
211
+ */
200
212
 
201
213
  // Sorting state - v-model support
202
214
  const sorting = defineModel<SortingState>("sorting", {
@@ -472,7 +484,7 @@ const tableColumns = computed(() => {
472
484
 
473
485
  const forwardedSlots = computed(() => {
474
486
  const passthrough = Object.keys(slots).filter((name) => {
475
- return !['default', 'header-right', 'bulk-actions'].includes(name);
487
+ return !['default', 'header-right', 'bulk-actions', 'toolbar-left'].includes(name);
476
488
  });
477
489
 
478
490
  return passthrough.map((source) => {
@@ -1,102 +1,87 @@
1
1
  <script setup lang="ts">
2
2
  interface Feature {
3
- label: string
4
- available: boolean
3
+ label: string;
4
+ available: boolean;
5
5
  }
6
6
 
7
7
  export interface PricePlanProps {
8
- name: string
9
- description: string
10
- price: string
11
- priceSuffix?: string
12
- per?: string
13
- features: Feature[]
14
- current?: boolean
15
- badge?: string
16
- buttonLabel?: string
8
+ name: string;
9
+ description: string;
10
+ price: string;
11
+ priceSuffix?: string;
12
+ per?: string;
13
+ features: Feature[];
14
+ current?: boolean;
15
+ badge?: string;
16
+ buttonLabel?: string;
17
17
  }
18
18
 
19
- defineProps<PricePlanProps>()
19
+ defineProps<PricePlanProps>();
20
20
  </script>
21
21
 
22
22
  <template>
23
- <UCard
24
- :class="[
25
- 'relative transition-all duration-200',
26
- current
27
- ? 'ring-2 ring-success bg-success/5'
28
- : 'hover:shadow-lg hover:-translate-y-1'
29
- ]"
30
- >
31
- <div
32
- v-if="badge"
33
- class="absolute -top-3 left-1/2 -translate-x-1/2"
34
- >
35
- <UBadge
36
- :label="badge"
37
- variant="solid"
38
- color="success"
39
- size="sm"
40
- />
41
- </div>
23
+ <UCard
24
+ :class="[
25
+ 'relative transition-all duration-200',
26
+ current ? 'ring-2 ring-success bg-success/5' : ' ',
27
+ ]"
28
+ >
29
+ <div v-if="badge" class="absolute -top-3 left-1/2 -translate-x-1/2">
30
+ <UBadge :label="badge" variant="solid" color="success" size="sm" />
31
+ </div>
42
32
 
43
- <div class="text-center">
44
- <div>
45
- <h3 class="text-xl font-bold text-highlighted">
46
- {{ name }}
47
- </h3>
48
- <p class="text-dimmed mt-1">
49
- {{ description }}
50
- </p>
51
- </div>
33
+ <div class="text-center">
34
+ <div>
35
+ <h3 class="text-xl font-bold text-highlighted">
36
+ {{ name }}
37
+ </h3>
38
+ <p class="text-dimmed mt-1">
39
+ {{ description }}
40
+ </p>
41
+ </div>
52
42
 
53
- <div class="border-y border-default my-4 py-4">
54
- <div class="mb-4">
55
- <div class="text-3xl font-bold text-success space-x-2">
56
- <span>{{ price }}</span>
57
- <span
58
- v-if="priceSuffix"
59
- class="text-base text-toned"
60
- >{{
61
- priceSuffix
62
- }}</span>
63
- </div>
64
- <div
65
- v-if="per"
66
- class="text-sm text-muted"
67
- >
68
- {{ per }}
69
- </div>
70
- </div>
43
+ <div class="border-y border-default my-4 py-4">
44
+ <div class="mb-4">
45
+ <div class="text-3xl font-bold text-success space-x-2">
46
+ <span>{{ price }}</span>
47
+ <span v-if="priceSuffix" class="text-base text-toned">{{
48
+ priceSuffix
49
+ }}</span>
50
+ </div>
51
+ <div v-if="per" class="text-sm text-muted">
52
+ {{ per }}
53
+ </div>
54
+ </div>
71
55
 
72
- <UButton
73
- :label="buttonLabel"
74
- :variant="current ? 'outline' : 'solid'"
75
- :color="current ? 'success' : 'success'"
76
- class="w-full justify-center"
77
- :disabled="current"
78
- :icon="current ? 'i-lucide-check' : 'i-lucide-arrow-up-right'"
79
- />
80
- </div>
56
+ <UButton
57
+ :label="buttonLabel"
58
+ :variant="current ? 'outline' : 'solid'"
59
+ :color="current ? 'success' : 'success'"
60
+ class="w-full justify-center"
61
+ :disabled="current"
62
+ :icon="current ? 'i-lucide-check' : 'i-lucide-arrow-up-right'"
63
+ />
64
+ </div>
81
65
 
82
- <div class="space-y-4 mt-8">
83
- <div
84
- v-for="(feature, i) in features"
85
- :key="i"
86
- class="flex items-center justify-start gap-2 text-sm"
87
- >
88
- <UIcon
89
- :name="feature.available ? 'i-lucide-check' : 'i-lucide-x'"
90
- :class="feature.available ? 'text-success' : 'text-error'"
91
- size="16"
92
- />
93
- <span
94
- :class="{
95
- 'text-dimmed': !feature.available
96
- }"
97
- >{{ feature.label }}</span>
98
- </div>
99
- </div>
100
- </div>
101
- </UCard>
66
+ <div class="space-y-4 mt-8">
67
+ <div
68
+ v-for="(feature, i) in features"
69
+ :key="i"
70
+ class="flex items-center justify-start gap-2 text-sm"
71
+ >
72
+ <UIcon
73
+ :name="feature.available ? 'i-lucide-check' : 'i-lucide-x'"
74
+ :class="feature.available ? 'text-success' : 'text-error'"
75
+ size="16"
76
+ />
77
+ <span
78
+ :class="{
79
+ 'text-dimmed': !feature.available,
80
+ }"
81
+ >{{ feature.label }}</span
82
+ >
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </UCard>
102
87
  </template>
@@ -1,115 +1,132 @@
1
1
  <script setup lang="ts">
2
2
  definePageMeta({
3
- title: 'Component Playground'
4
- })
3
+ title: "Component Playground",
4
+ });
5
5
 
6
6
  const categories = [
7
- {
8
- title: 'Cards',
9
- description: '15 card components for data display including KPI cards, chart cards, and table cards.',
10
- to: '/playground/cards',
11
- icon: 'i-lucide-credit-card'
12
- },
13
- {
14
- title: 'Charts',
15
- description: '33 chart components including bar charts, line charts, donut charts, and area charts.',
16
- to: '/playground/charts',
17
- icon: 'i-lucide-bar-chart-3'
18
- },
19
- {
20
- title: 'Blocks',
21
- description: '5 block components for layout composition including grid layouts and filter bars.',
22
- to: '/playground/blocks',
23
- icon: 'i-lucide-layout-template'
24
- },
25
- {
26
- title: 'Dashboard Widgets',
27
- description: '6 dashboard widgets including KPI cards, navigation, pricing plans, and usage charts.',
28
- to: '/playground/dashboard',
29
- icon: 'i-lucide-layout-dashboard'
30
- },
31
- {
32
- title: 'Layout',
33
- description: 'Layout components including side navigation, user menus, and slideovers',
34
- to: '/playground/layout',
35
- icon: 'i-lucide-layout'
36
- },
37
- {
38
- title: 'Tables',
39
- description: 'Full-featured data tables with search, sort, filter, export, selection, and pagination.',
40
- to: '/playground/tables',
41
- icon: 'i-lucide-table-2'
42
- },
43
- {
44
- title: 'Base Components',
45
- description: 'Core V/A components including cards, stats, badges, navigation, and data display.',
46
- to: '/playground/base',
47
- icon: 'i-lucide-box'
48
- },
49
- {
50
- title: 'CRUD Components',
51
- description: 'VACrudCreate, VACrudUpdate, and VACrudDelete for modal-based CRUD operations.',
52
- to: '/playground/crud',
53
- icon: 'i-lucide-database'
54
- },
55
- {
56
- title: 'Buttons',
57
- description: 'Action buttons for CRUD operations, navigation, and data export.',
58
- to: '/playground/buttons',
59
- icon: 'i-lucide-mouse-pointer-click'
60
- },
61
- {
62
- title: 'Formatters',
63
- description: 'Data formatting components for currency, dates, percentages, and key-value displays.',
64
- to: '/playground/formatters',
65
- icon: 'i-lucide-text-cursor-input'
66
- },
67
- {
68
- title: 'States',
69
- description: 'Loading, empty, and error state components for consistent UI feedback.',
70
- to: '/playground/states',
71
- icon: 'i-lucide-loader'
72
- },
73
- {
74
- title: 'Modals',
75
- description: 'Modal dialogs and slideover panels for confirmations, forms, and detailed views.',
76
- to: '/playground/modals',
77
- icon: 'i-lucide-panel-right'
78
- }
79
- ]
7
+ {
8
+ title: "Cards",
9
+ description:
10
+ "15 card components for data display including KPI cards, chart cards, and table cards.",
11
+ to: "/playground/cards",
12
+ icon: "i-lucide-credit-card",
13
+ },
14
+ {
15
+ title: "Charts",
16
+ description:
17
+ "33 chart components including bar charts, line charts, donut charts, and area charts.",
18
+ to: "/playground/charts",
19
+ icon: "i-lucide-bar-chart-3",
20
+ },
21
+ {
22
+ title: "Blocks",
23
+ description:
24
+ "5 block components for layout composition including grid layouts and filter bars.",
25
+ to: "/playground/blocks",
26
+ icon: "i-lucide-layout-template",
27
+ },
28
+ {
29
+ title: "Dashboard Widgets",
30
+ description:
31
+ "6 dashboard widgets including KPI cards, navigation, pricing plans, and usage charts.",
32
+ to: "/playground/dashboard",
33
+ icon: "i-lucide-layout-dashboard",
34
+ },
35
+ {
36
+ title: "Layout",
37
+ description:
38
+ "Layout components including side navigation, user menus, and slideovers",
39
+ to: "/playground/layout",
40
+ icon: "i-lucide-layout",
41
+ },
42
+ {
43
+ title: "Tables",
44
+ description:
45
+ "Full-featured data tables with search, sort, filter, export, selection, and pagination.",
46
+ to: "/playground/tables",
47
+ icon: "i-lucide-table-2",
48
+ },
49
+ {
50
+ title: "Base Components",
51
+ description:
52
+ "Core V/A components including cards, stats, badges, navigation, and data display.",
53
+ to: "/playground/base",
54
+ icon: "i-lucide-box",
55
+ },
56
+ {
57
+ title: "CRUD Components",
58
+ description:
59
+ "VACrudCreate, VACrudUpdate, and VACrudDelete for modal-based CRUD operations.",
60
+ to: "/playground/crud",
61
+ icon: "i-lucide-database",
62
+ },
63
+ {
64
+ title: "Buttons",
65
+ description:
66
+ "Action buttons for CRUD operations, navigation, and data export.",
67
+ to: "/playground/buttons",
68
+ icon: "i-lucide-mouse-pointer-click",
69
+ },
70
+ {
71
+ title: "Formatters",
72
+ description:
73
+ "Data formatting components for currency, dates, percentages, and key-value displays.",
74
+ to: "/playground/formatters",
75
+ icon: "i-lucide-text-cursor-input",
76
+ },
77
+ {
78
+ title: "States",
79
+ description:
80
+ "Loading, empty, and error state components for consistent UI feedback.",
81
+ to: "/playground/states",
82
+ icon: "i-lucide-loader",
83
+ },
84
+ {
85
+ title: "Modals",
86
+ description:
87
+ "Modal dialogs and slideover panels for confirmations, forms, and detailed views.",
88
+ to: "/playground/modals",
89
+ icon: "i-lucide-panel-right",
90
+ },
91
+ ];
80
92
  </script>
81
93
 
82
94
  <template>
83
- <UDashboardPanel grow>
84
- <UContainer>
85
- <div class="py-8">
86
- <h1 class="text-3xl font-bold mb-4">Component Playground</h1>
87
- <p class="text-gray-600 dark:text-gray-400 mb-8">
88
- Explore the Veristone component library. This playground showcases 60+ migrated components
89
- plus 36 base V/A components across 9 categories.
90
- </p>
95
+ <UDashboardPanel grow>
96
+ <UContainer>
97
+ <div class="py-8">
98
+ <h1 class="text-3xl font-bold mb-4">Component Playground</h1>
99
+ <p class="text-gray-600 dark:text-gray-400 mb-8">
100
+ Explore the Veristone component library. This playground showcases 60+
101
+ migrated components plus 36 base V/A components across 9 categories.
102
+ </p>
91
103
 
92
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
93
- <ClientOnly v-for="category in categories" :key="category.to">
94
- <UCard class="hover:shadow-lg transition-shadow">
95
- <template #header>
96
- <div class="flex items-center gap-3">
97
- <div class="p-2 rounded-lg bg-primary-50 dark:bg-primary-900/20">
98
- <UIcon :name="category.icon" class="w-5 h-5 text-primary-500" />
99
- </div>
100
- <h2 class="text-xl font-semibold">{{ category.title }}</h2>
101
- </div>
102
- </template>
103
- <p class="mb-4 text-gray-600 dark:text-gray-400">
104
- {{ category.description }}
105
- </p>
106
- <UButton :to="category.to" color="primary" variant="soft">
107
- View {{ category.title }}
108
- </UButton>
109
- </UCard>
110
- </ClientOnly>
111
- </div>
112
- </div>
113
- </UContainer>
114
- </UDashboardPanel>
104
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
105
+ <ClientOnly v-for="category in categories" :key="category.to">
106
+ <UCard class="transition-shadow">
107
+ <template #header>
108
+ <div class="flex items-center gap-3">
109
+ <div
110
+ class="p-2 rounded-lg bg-primary-50 dark:bg-primary-900/20"
111
+ >
112
+ <UIcon
113
+ :name="category.icon"
114
+ class="w-5 h-5 text-primary-500"
115
+ />
116
+ </div>
117
+ <h2 class="text-xl font-semibold">{{ category.title }}</h2>
118
+ </div>
119
+ </template>
120
+ <p class="mb-4 text-gray-600 dark:text-gray-400">
121
+ {{ category.description }}
122
+ </p>
123
+ <UButton :to="category.to" color="primary" variant="soft">
124
+ View {{ category.title }}
125
+ </UButton>
126
+ </UCard>
127
+ </ClientOnly>
128
+ </div>
129
+ </div>
130
+ </UContainer>
131
+ </UDashboardPanel>
115
132
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veristone/nuxt-v-app",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Veristone Nuxt App Layer - Shared components, composables, and layouts",
5
5
  "type": "module",
6
6
  "private": false,
@@ -68,4 +68,4 @@
68
68
  "@nuxt/ui": "^4.0.0",
69
69
  "nuxt": "^4.0.0"
70
70
  }
71
- }
71
+ }