@vc-shell/framework 1.2.4-beta.5 → 1.2.4-beta.7
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/core/utilities/date/formatDate.ts +1 -1
- package/dist/{DashboardBarChart-B-g_a-7F.js → DashboardBarChart-BzfKkUke.js} +1 -1
- package/dist/{DashboardDonutChart-AktPFUNo.js → DashboardDonutChart-CWfe85Xq.js} +1 -1
- package/dist/{DashboardLineChart-BQKqRFhM.js → DashboardLineChart-kdA8VnrR.js} +1 -1
- package/dist/{GridstackDashboard-BXqCpiMw.js → GridstackDashboard-CGHYkReX.js} +1 -1
- package/dist/framework.js +1 -1
- package/dist/{index-DVyGELzS.js → index-tgmgQAD9.js} +6839 -6798
- package/dist/index.css +1 -1
- package/dist/locales/de.json +2 -0
- package/dist/locales/en.json +2 -0
- package/dist/shared/components/user-dropdown-button/_internal/user-info.vue.d.ts.map +1 -1
- package/dist/shared/pages/_storybook-helpers.d.ts +11 -0
- package/dist/shared/pages/_storybook-helpers.d.ts.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/menu/VcAppMenu.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/_internal/toolbar/ToolbarMobile.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-popup/vc-popup.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-sidebar/vc-sidebar.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/VcDataTable.vue.d.ts +5 -2
- package/dist/ui/components/organisms/vc-table/VcDataTable.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/VcTableAdapter.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/base/BaseVcDataTable.d.ts +9 -1
- package/dist/ui/components/organisms/vc-table/base/BaseVcDataTable.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/components/DataTableBody.vue.d.ts +7 -0
- package/dist/ui/components/organisms/vc-table/components/DataTableBody.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/components/DataTableRow.vue.d.ts +2 -0
- package/dist/ui/components/organisms/vc-table/components/DataTableRow.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/components/TableEmpty.vue.d.ts +7 -9
- package/dist/ui/components/organisms/vc-table/components/TableEmpty.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/components/TableRow.vue.d.ts +4 -0
- package/dist/ui/components/organisms/vc-table/components/TableRow.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/types.d.ts +21 -0
- package/dist/ui/components/organisms/vc-table/types.d.ts.map +1 -1
- package/dist/{vc-editor-BtJrxrBg.js → vc-editor-BNrG1GAG.js} +1 -1
- package/dist/{vc-slider-sUKMaKnc.js → vc-slider-Ce0X1_1m.js} +1 -1
- package/package.json +5 -5
- package/shared/components/user-dropdown-button/_internal/user-info.vue +26 -13
- package/shared/pages/ChangePasswordPage/components/change-password/change-password.stories.ts +44 -0
- package/shared/pages/ForgotPasswordPage/components/forgot-password/forgot-password.stories.ts +38 -0
- package/shared/pages/InvitePage/components/invite/invite.stories.ts +64 -0
- package/shared/pages/LoginPage/components/login/login.stories.ts +52 -0
- package/shared/pages/ResetPasswordPage/components/reset-password/reset-password.stories.ts +64 -0
- package/shared/pages/_storybook-helpers.ts +71 -0
- package/ui/components/molecules/vc-select/vc-select.vue +1 -1
- package/ui/components/organisms/vc-app/_internal/menu/VcAppMenu.vue +1 -2
- package/ui/components/organisms/vc-blade/_internal/toolbar/ToolbarMobile.vue +18 -22
- package/ui/components/organisms/vc-popup/vc-popup.vue +3 -4
- package/ui/components/organisms/vc-sidebar/vc-sidebar.vue +12 -10
- package/ui/components/organisms/vc-table/VcDataTable.vue +58 -11
- package/ui/components/organisms/vc-table/VcTableAdapter.vue +31 -84
- package/ui/components/organisms/vc-table/base/BaseVcDataTable.ts +10 -0
- package/ui/components/organisms/vc-table/components/DataTableBody.vue +10 -0
- package/ui/components/organisms/vc-table/components/DataTableRow.vue +3 -0
- package/ui/components/organisms/vc-table/components/TableEmpty.vue +18 -12
- package/ui/components/organisms/vc-table/components/TableRow.vue +5 -1
- package/ui/components/organisms/vc-table/types.ts +22 -0
- package/ui/components/organisms/vc-table/vc-data-table.stories.ts +205 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
:selection="internalSelection"
|
|
10
10
|
:is-row-selectable="rowSelectable"
|
|
11
11
|
:row-actions="effectiveRowActions"
|
|
12
|
-
:
|
|
12
|
+
:active-item-id="selectedItemId ? toValue(selectedItemId) : undefined"
|
|
13
13
|
:loading="loadingValue"
|
|
14
14
|
:resizable-columns="props.resizableColumns"
|
|
15
15
|
:reorderable-columns="props.reorderableColumns"
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
:pagination="paginationConfig"
|
|
29
29
|
:add-row="addRowConfig"
|
|
30
30
|
:variant="props.variant"
|
|
31
|
+
:empty-state="mappedEmptyState"
|
|
32
|
+
:not-found-state="mappedNotFoundState"
|
|
31
33
|
@sort="handleSort"
|
|
32
34
|
@update:selection="handleSelectionUpdate"
|
|
33
35
|
@row-click="handleRowClick"
|
|
@@ -107,52 +109,12 @@
|
|
|
107
109
|
/>
|
|
108
110
|
</template>
|
|
109
111
|
|
|
110
|
-
<!-- Empty / NotFound -->
|
|
111
|
-
<template #
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class="vc-table-adapter__empty-state"
|
|
117
|
-
>
|
|
118
|
-
<VcIcon
|
|
119
|
-
v-if="props.notfound.icon"
|
|
120
|
-
:icon="props.notfound.icon"
|
|
121
|
-
size="xxl"
|
|
122
|
-
/>
|
|
123
|
-
<p class="vc-table-adapter__empty-text">{{ toValue(props.notfound.text) }}</p>
|
|
124
|
-
<a
|
|
125
|
-
v-if="props.notfound.action"
|
|
126
|
-
class="vc-table-adapter__empty-action"
|
|
127
|
-
@click="props.notfound.clickHandler?.()"
|
|
128
|
-
>
|
|
129
|
-
{{ toValue(props.notfound.action) }}
|
|
130
|
-
</a>
|
|
131
|
-
</div>
|
|
132
|
-
</slot>
|
|
133
|
-
</template>
|
|
134
|
-
<template v-else>
|
|
135
|
-
<slot name="empty">
|
|
136
|
-
<div
|
|
137
|
-
v-if="props.empty"
|
|
138
|
-
class="vc-table-adapter__empty-state"
|
|
139
|
-
>
|
|
140
|
-
<VcIcon
|
|
141
|
-
v-if="props.empty.icon"
|
|
142
|
-
:icon="props.empty.icon"
|
|
143
|
-
size="xxl"
|
|
144
|
-
/>
|
|
145
|
-
<p class="vc-table-adapter__empty-text">{{ toValue(props.empty.text) }}</p>
|
|
146
|
-
<a
|
|
147
|
-
v-if="props.empty.action"
|
|
148
|
-
class="vc-table-adapter__empty-action"
|
|
149
|
-
@click="props.empty.clickHandler?.()"
|
|
150
|
-
>
|
|
151
|
-
{{ toValue(props.empty.action) }}
|
|
152
|
-
</a>
|
|
153
|
-
</div>
|
|
154
|
-
</slot>
|
|
155
|
-
</template>
|
|
112
|
+
<!-- Empty / NotFound — delegate rendering to VcDataTable via props, passthrough custom slots -->
|
|
113
|
+
<template v-if="$slots.notfound" #not-found>
|
|
114
|
+
<slot name="notfound" />
|
|
115
|
+
</template>
|
|
116
|
+
<template v-if="$slots.empty" #empty>
|
|
117
|
+
<slot name="empty" />
|
|
156
118
|
</template>
|
|
157
119
|
|
|
158
120
|
<!-- Loading -->
|
|
@@ -200,8 +162,7 @@ import VcDataTable from "@ui/components/organisms/vc-table/VcDataTable.vue";
|
|
|
200
162
|
import VcColumn from "@ui/components/organisms/vc-table/components/VcColumn.vue";
|
|
201
163
|
import { GlobalFiltersButton } from "@ui/components/organisms/vc-table/components";
|
|
202
164
|
import { VcDropdownPanel } from "@ui/components/molecules";
|
|
203
|
-
import {
|
|
204
|
-
import type { TableAction, TableEmptyAction, DataTablePagination, AddRowConfig } from "@ui/components/organisms/vc-table/types";
|
|
165
|
+
import type { TableAction, TableEmptyAction, DataTablePagination, AddRowConfig, TableStateConfig } from "@ui/components/organisms/vc-table/types";
|
|
205
166
|
import type { ITableColumns, IActionBuilderResult } from "@core/types";
|
|
206
167
|
|
|
207
168
|
// ============================================================================
|
|
@@ -383,16 +344,6 @@ const effectiveRowActions = computed<((item: T) => TableAction<T>[]) | undefined
|
|
|
383
344
|
return (item: T) => (props.itemActionBuilder!(item) ?? []) as TableAction<T>[];
|
|
384
345
|
});
|
|
385
346
|
|
|
386
|
-
// Row class: apply highlight for selectedItemId
|
|
387
|
-
const rowClassWithHighlight = computed(() => {
|
|
388
|
-
const selectedId = toValue(props.selectedItemId);
|
|
389
|
-
if (!selectedId) return undefined;
|
|
390
|
-
return (data: T) => {
|
|
391
|
-
const rowId = String((data as any).id ?? "");
|
|
392
|
-
return rowId === selectedId ? "vc-data-table__row--highlighted" : "";
|
|
393
|
-
};
|
|
394
|
-
});
|
|
395
|
-
|
|
396
347
|
// Pagination
|
|
397
348
|
const paginationConfig = computed<DataTablePagination | undefined>(() => {
|
|
398
349
|
if (props.footer === false || !props.pages) return undefined;
|
|
@@ -416,6 +367,27 @@ const isNotFoundState = computed(
|
|
|
416
367
|
() => props.items.length === 0 && (!!props.searchValue || (props.activeFilterCount ?? 0) > 0),
|
|
417
368
|
);
|
|
418
369
|
|
|
370
|
+
/** Map legacy TableEmptyAction → TableStateConfig for VcDataTable */
|
|
371
|
+
const mappedEmptyState = computed<TableStateConfig | undefined>(() => {
|
|
372
|
+
if (!props.empty) return undefined;
|
|
373
|
+
return {
|
|
374
|
+
icon: props.empty.icon,
|
|
375
|
+
title: typeof props.empty.text === "string" ? props.empty.text : toValue(props.empty.text),
|
|
376
|
+
actionLabel: props.empty.action ? (typeof props.empty.action === "string" ? props.empty.action : toValue(props.empty.action)) : undefined,
|
|
377
|
+
actionHandler: props.empty.clickHandler,
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const mappedNotFoundState = computed<TableStateConfig | undefined>(() => {
|
|
382
|
+
if (!props.notfound) return undefined;
|
|
383
|
+
return {
|
|
384
|
+
icon: props.notfound.icon,
|
|
385
|
+
title: typeof props.notfound.text === "string" ? props.notfound.text : toValue(props.notfound.text),
|
|
386
|
+
actionLabel: props.notfound.action ? (typeof props.notfound.action === "string" ? props.notfound.action : toValue(props.notfound.action)) : undefined,
|
|
387
|
+
actionHandler: props.notfound.clickHandler,
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
|
|
419
391
|
// Loading: unwrap MaybeRef
|
|
420
392
|
const loadingValue = computed(() => toValue(props.loading) ?? false);
|
|
421
393
|
|
|
@@ -598,30 +570,5 @@ function handleAddRow(event: { defaults: Record<string, unknown>; cancel: () =>
|
|
|
598
570
|
<style lang="scss">
|
|
599
571
|
.vc-table-adapter {
|
|
600
572
|
@apply tw-flex tw-flex-col tw-grow tw-basis-0 tw-overflow-hidden;
|
|
601
|
-
|
|
602
|
-
&__empty-state {
|
|
603
|
-
@apply tw-flex tw-flex-col tw-items-center tw-justify-center tw-py-8 tw-gap-3;
|
|
604
|
-
color: var(--neutrals-500);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
&__empty-text {
|
|
608
|
-
@apply tw-text-sm tw-text-center;
|
|
609
|
-
color: var(--neutrals-600);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
&__empty-action {
|
|
613
|
-
@apply tw-text-sm tw-cursor-pointer tw-underline;
|
|
614
|
-
color: var(--primary-500);
|
|
615
|
-
|
|
616
|
-
&:hover {
|
|
617
|
-
color: var(--primary-700);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
/* Row highlight for selectedItemId support.
|
|
623
|
-
* No !important — allows hover (higher specificity) to override naturally. */
|
|
624
|
-
.vc-data-table__row--highlighted {
|
|
625
|
-
background-color: var(--primary-50);
|
|
626
573
|
}
|
|
627
574
|
</style>
|
|
@@ -267,6 +267,15 @@ export const baseVcDataTableProps = {
|
|
|
267
267
|
default: undefined,
|
|
268
268
|
},
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
* ID of the currently active (highlighted) row.
|
|
272
|
+
* Use v-model:activeItemId for two-way binding.
|
|
273
|
+
*/
|
|
274
|
+
activeItemId: {
|
|
275
|
+
type: String,
|
|
276
|
+
default: undefined,
|
|
277
|
+
},
|
|
278
|
+
|
|
270
279
|
// ============================================================================
|
|
271
280
|
// COLUMN FEATURES
|
|
272
281
|
// ============================================================================
|
|
@@ -499,6 +508,7 @@ export const vcDataTableEmits = [
|
|
|
499
508
|
"filter",
|
|
500
509
|
|
|
501
510
|
// Row interactions
|
|
511
|
+
"update:activeItemId",
|
|
502
512
|
"row-click",
|
|
503
513
|
"row-dblclick",
|
|
504
514
|
"row-contextmenu",
|
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
<template v-if="items.length === 0 && !loading">
|
|
8
8
|
<slot name="empty">
|
|
9
9
|
<TableEmpty
|
|
10
|
+
:icon="emptyIcon"
|
|
10
11
|
:title="emptyTitle"
|
|
11
12
|
:description="emptyDescription"
|
|
13
|
+
:action-label="emptyActionLabel"
|
|
14
|
+
:action-handler="emptyActionHandler"
|
|
12
15
|
/>
|
|
13
16
|
</slot>
|
|
14
17
|
</template>
|
|
@@ -141,6 +144,7 @@ export interface RowProps<T> {
|
|
|
141
144
|
index: number;
|
|
142
145
|
columns: ColumnInstance[];
|
|
143
146
|
isSelected: boolean;
|
|
147
|
+
isActive: boolean;
|
|
144
148
|
isSelectable: boolean;
|
|
145
149
|
selectionMode?: "single" | "multiple";
|
|
146
150
|
showSelectionCell: boolean;
|
|
@@ -172,6 +176,12 @@ const props = defineProps<{
|
|
|
172
176
|
emptyTitle?: string;
|
|
173
177
|
/** Empty state description */
|
|
174
178
|
emptyDescription?: string;
|
|
179
|
+
/** Empty state icon */
|
|
180
|
+
emptyIcon?: string;
|
|
181
|
+
/** Empty state action button label */
|
|
182
|
+
emptyActionLabel?: string;
|
|
183
|
+
/** Empty state action button handler */
|
|
184
|
+
emptyActionHandler?: () => void;
|
|
175
185
|
/** Loading text (kept for custom loading slot consumers) */
|
|
176
186
|
loadingText?: string;
|
|
177
187
|
/** Number of skeleton rows to show during loading */
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
<TableRow
|
|
20
20
|
v-if="!groupingEnabled || !expandableRowGroups || isGroupRowExpanded"
|
|
21
21
|
:selected="isSelected"
|
|
22
|
+
:active="isActive"
|
|
22
23
|
:reorderable="reorderable"
|
|
23
24
|
:show-drag-handle="showDragHandle"
|
|
24
25
|
:index="index"
|
|
@@ -154,6 +155,8 @@ const props = defineProps<{
|
|
|
154
155
|
// === Selection ===
|
|
155
156
|
/** Whether this row is selected */
|
|
156
157
|
isSelected?: boolean;
|
|
158
|
+
/** Whether this row is the active (highlighted) row */
|
|
159
|
+
isActive?: boolean;
|
|
157
160
|
/** Whether this row can be selected */
|
|
158
161
|
isSelectable?: boolean;
|
|
159
162
|
/** Selection mode: 'single' or 'multiple' */
|
|
@@ -11,28 +11,34 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
</slot>
|
|
13
13
|
</div>
|
|
14
|
-
<div v-if="$slots.action" class="vc-table-composition__empty-action">
|
|
15
|
-
<slot name="action"
|
|
14
|
+
<div v-if="actionLabel || $slots.action" class="vc-table-composition__empty-action">
|
|
15
|
+
<slot name="action">
|
|
16
|
+
<VcButton
|
|
17
|
+
v-if="actionLabel"
|
|
18
|
+
variant="primary"
|
|
19
|
+
@click="actionHandler?.()"
|
|
20
|
+
>
|
|
21
|
+
{{ actionLabel }}
|
|
22
|
+
</VcButton>
|
|
23
|
+
</slot>
|
|
16
24
|
</div>
|
|
17
25
|
</div>
|
|
18
26
|
</template>
|
|
19
27
|
|
|
20
28
|
<script setup lang="ts">
|
|
21
|
-
import { VcIcon } from "@ui/components/atoms";
|
|
29
|
+
import { VcIcon, VcButton } from "@ui/components/atoms";
|
|
22
30
|
|
|
23
31
|
defineProps<{
|
|
24
|
-
/**
|
|
25
|
-
* Icon to display
|
|
26
|
-
*/
|
|
32
|
+
/** Icon to display */
|
|
27
33
|
icon?: string;
|
|
28
|
-
/**
|
|
29
|
-
* Title text
|
|
30
|
-
*/
|
|
34
|
+
/** Title text */
|
|
31
35
|
title?: string;
|
|
32
|
-
/**
|
|
33
|
-
* Description text
|
|
34
|
-
*/
|
|
36
|
+
/** Description text */
|
|
35
37
|
description?: string;
|
|
38
|
+
/** Action button label — renders VcButton when provided */
|
|
39
|
+
actionLabel?: string;
|
|
40
|
+
/** Action button click handler */
|
|
41
|
+
actionHandler?: () => void;
|
|
36
42
|
}>();
|
|
37
43
|
</script>
|
|
38
44
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
role="row"
|
|
4
4
|
class="vc-table-composition__row"
|
|
5
5
|
:class="{
|
|
6
|
-
'vc-table-composition__row--selected': selected,
|
|
6
|
+
'vc-table-composition__row--selected': selected || active,
|
|
7
7
|
'vc-table-composition__row--clickable': isClickable,
|
|
8
8
|
'vc-table-composition__row--header': variant === 'header',
|
|
9
9
|
'vc-table-composition__row--dragging': isDragging,
|
|
@@ -48,6 +48,10 @@ const props = withDefaults(
|
|
|
48
48
|
* Whether the row is selected
|
|
49
49
|
*/
|
|
50
50
|
selected?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the row is the active (highlighted) row — visual only, does not affect checkbox
|
|
53
|
+
*/
|
|
54
|
+
active?: boolean;
|
|
51
55
|
/**
|
|
52
56
|
* Row variant
|
|
53
57
|
*/
|
|
@@ -69,6 +69,20 @@ export interface TableEmptyAction {
|
|
|
69
69
|
clickHandler?: () => void;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** Configuration for empty/not-found state with icon, text, and optional action button */
|
|
73
|
+
export interface TableStateConfig {
|
|
74
|
+
/** Icon name (e.g. "lucide-package-open", "material-shopping_cart") */
|
|
75
|
+
icon?: string;
|
|
76
|
+
/** Title text */
|
|
77
|
+
title?: string;
|
|
78
|
+
/** Description text below the title */
|
|
79
|
+
description?: string;
|
|
80
|
+
/** Action button label — renders VcButton when provided */
|
|
81
|
+
actionLabel?: string;
|
|
82
|
+
/** Action button click handler */
|
|
83
|
+
actionHandler?: () => void;
|
|
84
|
+
}
|
|
85
|
+
|
|
72
86
|
export type { MaybeRef } from "vue";
|
|
73
87
|
|
|
74
88
|
/** Shared props for editable cell formatters (CellDefault, CellNumber, CellMoney) */
|
|
@@ -400,6 +414,8 @@ export interface VcDataTableProps<T = any> {
|
|
|
400
414
|
rowClass?: (data: T) => string | object;
|
|
401
415
|
/** Per-row function returning inline styles */
|
|
402
416
|
rowStyle?: (data: T) => object;
|
|
417
|
+
/** ID of the currently active (highlighted) row — use with `v-model:activeItemId` */
|
|
418
|
+
activeItemId?: string;
|
|
403
419
|
/** Visual table variant */
|
|
404
420
|
variant?: "default" | "striped" | "bordered";
|
|
405
421
|
|
|
@@ -534,6 +550,12 @@ export interface VcDataTableExtendedProps<T = any> extends VcDataTableProps<T> {
|
|
|
534
550
|
* useful when the table is narrowed (e.g. a second blade opens).
|
|
535
551
|
*/
|
|
536
552
|
showAllColumns?: boolean;
|
|
553
|
+
/** Empty state configuration (shown when items array is empty and no search/filters are active).
|
|
554
|
+
* Overrides default i18n title/description. Use `#empty` slot for fully custom rendering. */
|
|
555
|
+
emptyState?: TableStateConfig;
|
|
556
|
+
/** Not-found state configuration (shown when items array is empty AND search/filters are active).
|
|
557
|
+
* Overrides default i18n title/description. Use `#not-found` slot for fully custom rendering. */
|
|
558
|
+
notFoundState?: TableStateConfig;
|
|
537
559
|
}
|
|
538
560
|
|
|
539
561
|
/** VcDataTable Emits — all events emitted by VcDataTable */
|
|
@@ -495,6 +495,38 @@ export const SingleSelection: StoryFn = () => ({
|
|
|
495
495
|
`,
|
|
496
496
|
});
|
|
497
497
|
|
|
498
|
+
export const ActiveItemHighlight: StoryFn = () => ({
|
|
499
|
+
components: { VcDataTable, VcColumn },
|
|
500
|
+
setup() {
|
|
501
|
+
const activeId = ref<string | undefined>(undefined);
|
|
502
|
+
|
|
503
|
+
return { products: mockProducts, activeId };
|
|
504
|
+
},
|
|
505
|
+
template: `
|
|
506
|
+
<div style="height: 400px">
|
|
507
|
+
<p class="tw-mb-2">Active row ID: {{ activeId ?? 'None' }}
|
|
508
|
+
<button class="tw-ml-2 tw-text-sm tw-underline" @click="activeId = undefined">Clear</button>
|
|
509
|
+
</p>
|
|
510
|
+
<VcDataTable
|
|
511
|
+
:items="products"
|
|
512
|
+
v-model:active-item-id="activeId"
|
|
513
|
+
>
|
|
514
|
+
<VcColumn id="name" field="name" title="Name" />
|
|
515
|
+
<VcColumn id="price" field="price" title="Price" type="money" />
|
|
516
|
+
<VcColumn id="stock" field="stock" title="Stock" type="number" />
|
|
517
|
+
<VcColumn id="status" field="status" title="Status" />
|
|
518
|
+
</VcDataTable>
|
|
519
|
+
</div>
|
|
520
|
+
`,
|
|
521
|
+
});
|
|
522
|
+
ActiveItemHighlight.parameters = {
|
|
523
|
+
docs: {
|
|
524
|
+
description: {
|
|
525
|
+
story: "Click a row to highlight it as active. Click again to deselect. Use `v-model:active-item-id` for two-way binding — the table automatically highlights the row matching the given ID and updates the value on row click.",
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
|
|
498
530
|
/**
|
|
499
531
|
* VcDataTable with single selection via VcColumn (radio button style)
|
|
500
532
|
*/
|
|
@@ -4971,3 +5003,176 @@ SearchWithGlobalFilters.parameters = {
|
|
|
4971
5003
|
},
|
|
4972
5004
|
},
|
|
4973
5005
|
};
|
|
5006
|
+
|
|
5007
|
+
/**
|
|
5008
|
+
* Empty state using the declarative `emptyState` prop (TableStateConfig).
|
|
5009
|
+
* Shown when `items` is empty and there is no active search or filters.
|
|
5010
|
+
*/
|
|
5011
|
+
export const EmptyStateConfig: StoryFn = () => ({
|
|
5012
|
+
components: { VcDataTable, VcColumn },
|
|
5013
|
+
setup() {
|
|
5014
|
+
const actionClicked = ref(false);
|
|
5015
|
+
const emptyState = {
|
|
5016
|
+
icon: "fas fa-box-open",
|
|
5017
|
+
title: "No products yet",
|
|
5018
|
+
description: "Your product catalog is empty. Add your first product to get started.",
|
|
5019
|
+
actionLabel: "Add Product",
|
|
5020
|
+
actionHandler: () => {
|
|
5021
|
+
actionClicked.value = true;
|
|
5022
|
+
},
|
|
5023
|
+
};
|
|
5024
|
+
return { products: [] as Product[], emptyState, actionClicked };
|
|
5025
|
+
},
|
|
5026
|
+
template: `
|
|
5027
|
+
<div style="height: 400px">
|
|
5028
|
+
<div v-if="actionClicked" class="tw-mb-2 tw-p-2 tw-bg-success-50 tw-rounded tw-text-sm tw-text-success-700">
|
|
5029
|
+
Action button clicked!
|
|
5030
|
+
</div>
|
|
5031
|
+
<VcDataTable :items="products" :empty-state="emptyState">
|
|
5032
|
+
<VcColumn id="name" field="name" title="Name" />
|
|
5033
|
+
<VcColumn id="price" field="price" title="Price" type="money" />
|
|
5034
|
+
<VcColumn id="stock" field="stock" title="Stock" type="number" />
|
|
5035
|
+
<VcColumn id="status" field="status" title="Status" />
|
|
5036
|
+
</VcDataTable>
|
|
5037
|
+
</div>
|
|
5038
|
+
`,
|
|
5039
|
+
});
|
|
5040
|
+
EmptyStateConfig.parameters = {
|
|
5041
|
+
docs: {
|
|
5042
|
+
description: {
|
|
5043
|
+
story:
|
|
5044
|
+
"Demonstrates the declarative `emptyState` prop (`TableStateConfig`) — " +
|
|
5045
|
+
"icon, title, description, and an optional action button. " +
|
|
5046
|
+
"Shown when `items` is an empty array and no search/filter is active.",
|
|
5047
|
+
},
|
|
5048
|
+
},
|
|
5049
|
+
};
|
|
5050
|
+
|
|
5051
|
+
/**
|
|
5052
|
+
* Not Found state using the declarative `notFoundState` prop (TableStateConfig).
|
|
5053
|
+
* Shown when `items` is empty AND there is an active search or filter.
|
|
5054
|
+
* The table uses `searchable` with a pre-filled search value to trigger the not-found state.
|
|
5055
|
+
*/
|
|
5056
|
+
export const NotFoundStateConfig: StoryFn = () => ({
|
|
5057
|
+
components: { VcDataTable, VcColumn },
|
|
5058
|
+
setup() {
|
|
5059
|
+
const searchValue = ref("nonexistent product xyz");
|
|
5060
|
+
const actionClicked = ref(false);
|
|
5061
|
+
|
|
5062
|
+
const notFoundState = {
|
|
5063
|
+
icon: "fas fa-search",
|
|
5064
|
+
title: "No results found",
|
|
5065
|
+
description: "We couldn't find any products matching your search. Try adjusting your query.",
|
|
5066
|
+
actionLabel: "Clear Search",
|
|
5067
|
+
actionHandler: () => {
|
|
5068
|
+
searchValue.value = "";
|
|
5069
|
+
actionClicked.value = true;
|
|
5070
|
+
},
|
|
5071
|
+
};
|
|
5072
|
+
|
|
5073
|
+
const emptyState = {
|
|
5074
|
+
icon: "fas fa-box-open",
|
|
5075
|
+
title: "No products yet",
|
|
5076
|
+
description: "Your product catalog is empty.",
|
|
5077
|
+
};
|
|
5078
|
+
|
|
5079
|
+
return { products: [] as Product[], searchValue, notFoundState, emptyState, actionClicked };
|
|
5080
|
+
},
|
|
5081
|
+
template: `
|
|
5082
|
+
<div style="height: 400px">
|
|
5083
|
+
<div v-if="actionClicked" class="tw-mb-2 tw-p-2 tw-bg-success-50 tw-rounded tw-text-sm tw-text-success-700">
|
|
5084
|
+
Search cleared! (In a real app, items would reload and show the empty state instead.)
|
|
5085
|
+
</div>
|
|
5086
|
+
<VcDataTable
|
|
5087
|
+
:items="products"
|
|
5088
|
+
searchable
|
|
5089
|
+
v-model:search-value="searchValue"
|
|
5090
|
+
search-placeholder="Search products..."
|
|
5091
|
+
:not-found-state="notFoundState"
|
|
5092
|
+
:empty-state="emptyState"
|
|
5093
|
+
>
|
|
5094
|
+
<VcColumn id="name" field="name" title="Name" />
|
|
5095
|
+
<VcColumn id="price" field="price" title="Price" type="money" />
|
|
5096
|
+
<VcColumn id="stock" field="stock" title="Stock" type="number" />
|
|
5097
|
+
<VcColumn id="status" field="status" title="Status" />
|
|
5098
|
+
</VcDataTable>
|
|
5099
|
+
</div>
|
|
5100
|
+
`,
|
|
5101
|
+
});
|
|
5102
|
+
NotFoundStateConfig.parameters = {
|
|
5103
|
+
docs: {
|
|
5104
|
+
description: {
|
|
5105
|
+
story:
|
|
5106
|
+
"Demonstrates the `notFoundState` prop — shown instead of `emptyState` when items " +
|
|
5107
|
+
"is empty AND there is an active search query or filters. " +
|
|
5108
|
+
"The action button clears the search. Both `emptyState` and `notFoundState` " +
|
|
5109
|
+
"can be provided simultaneously — the table picks the right one based on context.",
|
|
5110
|
+
},
|
|
5111
|
+
},
|
|
5112
|
+
};
|
|
5113
|
+
|
|
5114
|
+
/**
|
|
5115
|
+
* Both empty and not-found states — interactive demo.
|
|
5116
|
+
* Search to switch between the two states.
|
|
5117
|
+
*/
|
|
5118
|
+
export const EmptyVsNotFound: StoryFn = () => ({
|
|
5119
|
+
components: { VcDataTable, VcColumn },
|
|
5120
|
+
setup() {
|
|
5121
|
+
const searchValue = ref("");
|
|
5122
|
+
|
|
5123
|
+
const emptyState = {
|
|
5124
|
+
icon: "fas fa-box-open",
|
|
5125
|
+
title: "No products yet",
|
|
5126
|
+
description: "Your catalog is empty. Add your first product to get started.",
|
|
5127
|
+
actionLabel: "Add Product",
|
|
5128
|
+
actionHandler: () => alert("Add product clicked!"),
|
|
5129
|
+
};
|
|
5130
|
+
|
|
5131
|
+
const notFoundState = {
|
|
5132
|
+
icon: "fas fa-search",
|
|
5133
|
+
title: "Nothing matches your search",
|
|
5134
|
+
description: "Try a different search term or clear the search field.",
|
|
5135
|
+
actionLabel: "Clear Search",
|
|
5136
|
+
actionHandler: () => {
|
|
5137
|
+
searchValue.value = "";
|
|
5138
|
+
},
|
|
5139
|
+
};
|
|
5140
|
+
|
|
5141
|
+
return { products: [] as Product[], searchValue, emptyState, notFoundState };
|
|
5142
|
+
},
|
|
5143
|
+
template: `
|
|
5144
|
+
<div style="height: 450px">
|
|
5145
|
+
<div class="tw-mb-4 tw-p-3 tw-bg-gradient-to-r tw-from-accent-50 tw-to-primary-50 tw-rounded-lg tw-text-sm">
|
|
5146
|
+
<p class="tw-font-semibold tw-mb-1">Empty vs Not Found</p>
|
|
5147
|
+
<p class="tw-text-neutrals-600">
|
|
5148
|
+
Type anything in the search bar to switch from <strong>empty state</strong> to <strong>not found state</strong>.
|
|
5149
|
+
Clear the search to go back to empty state.
|
|
5150
|
+
</p>
|
|
5151
|
+
</div>
|
|
5152
|
+
<VcDataTable
|
|
5153
|
+
:items="products"
|
|
5154
|
+
searchable
|
|
5155
|
+
v-model:search-value="searchValue"
|
|
5156
|
+
search-placeholder="Type to trigger not-found state..."
|
|
5157
|
+
:empty-state="emptyState"
|
|
5158
|
+
:not-found-state="notFoundState"
|
|
5159
|
+
>
|
|
5160
|
+
<VcColumn id="name" field="name" title="Name" />
|
|
5161
|
+
<VcColumn id="price" field="price" title="Price" type="money" />
|
|
5162
|
+
<VcColumn id="stock" field="stock" title="Stock" type="number" />
|
|
5163
|
+
<VcColumn id="status" field="status" title="Status" />
|
|
5164
|
+
</VcDataTable>
|
|
5165
|
+
</div>
|
|
5166
|
+
`,
|
|
5167
|
+
});
|
|
5168
|
+
EmptyVsNotFound.parameters = {
|
|
5169
|
+
docs: {
|
|
5170
|
+
description: {
|
|
5171
|
+
story:
|
|
5172
|
+
"Interactive demo showing the difference between empty and not-found states. " +
|
|
5173
|
+
"With an empty search field the table shows the `emptyState` config. " +
|
|
5174
|
+
"Type anything in the search bar to see the `notFoundState` config instead. " +
|
|
5175
|
+
"The table automatically switches between the two based on whether a search/filter is active.",
|
|
5176
|
+
},
|
|
5177
|
+
},
|
|
5178
|
+
};
|