@aggc/ui 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/DataTable-CIiU5Jx3.js +8688 -0
- package/dist/components/DataTable.styles.d.ts +48 -0
- package/dist/components/DataTable.types.d.ts +38 -0
- package/dist/components/DataTable.vue.d.ts +72 -0
- package/dist/components/UiModal.styles.d.ts +13 -3
- package/dist/components/UiTooltip.styles.d.ts +1 -0
- package/dist/components/UiTooltip.vue.d.ts +25 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components.js +29 -15
- package/dist/index.js +82 -68
- package/dist/ui.css +776 -373
- package/package.json +1 -1
- package/src/components/DataTable.styles.ts +493 -0
- package/src/components/DataTable.test.ts +249 -0
- package/src/components/DataTable.types.ts +42 -0
- package/src/components/DataTable.vue +567 -0
- package/src/components/UiModal.styles.ts +42 -25
- package/src/components/UiModal.vue +4 -1
- package/src/components/UiToast.styles.ts +1 -1
- package/src/components/UiTooltip.styles.ts +25 -0
- package/src/components/UiTooltip.test.ts +37 -0
- package/src/components/UiTooltip.vue +37 -0
- package/src/components/index.ts +13 -0
- package/src/css/base.css +13 -2
- package/src/stories/layout/DataTable.stories.ts +141 -0
- package/src/stories/primitives/UiTooltip.stories.ts +46 -0
- package/src/stories/support/sources.ts +81 -0
- package/dist/chunks/UiToastProvider.vue_vue_type_script_setup_true_lang-D7OGRCU4.js +0 -3958
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
uiModalHeaderContentClass,
|
|
21
21
|
uiModalOverlayClass,
|
|
22
22
|
uiModalTitleClass,
|
|
23
|
+
uiModalWrapperClass,
|
|
23
24
|
} from "./UiModal.styles";
|
|
24
25
|
|
|
25
26
|
withDefaults(
|
|
@@ -52,7 +53,8 @@ function handleOpenChange(open: boolean) {
|
|
|
52
53
|
<DialogRoot :open="open" @update:open="handleOpenChange">
|
|
53
54
|
<DialogPortal>
|
|
54
55
|
<DialogOverlay :class="uiModalOverlayClass" />
|
|
55
|
-
<
|
|
56
|
+
<div :class="uiModalWrapperClass">
|
|
57
|
+
<DialogContent :class="uiModalContentClass({ size })">
|
|
56
58
|
<div :class="uiModalHeaderClass">
|
|
57
59
|
<div :class="uiModalHeaderContentClass">
|
|
58
60
|
<DialogTitle :class="uiModalTitleClass">{{ title }}</DialogTitle>
|
|
@@ -74,6 +76,7 @@ function handleOpenChange(open: boolean) {
|
|
|
74
76
|
<slot name="actions" />
|
|
75
77
|
</div>
|
|
76
78
|
</DialogContent>
|
|
79
|
+
</div>
|
|
77
80
|
</DialogPortal>
|
|
78
81
|
</DialogRoot>
|
|
79
82
|
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { css } from "@styled/css";
|
|
2
|
+
|
|
3
|
+
// Tooltip content floating panel. Positioned by reka-ui (floating-ui),
|
|
4
|
+
// so this only defines the visual surface.
|
|
5
|
+
|
|
6
|
+
export const tooltipContent = css({
|
|
7
|
+
bg: "bg.menu",
|
|
8
|
+
color: "text.primary",
|
|
9
|
+
fontSize: "xs",
|
|
10
|
+
fontWeight: "500",
|
|
11
|
+
lineHeight: "1.4",
|
|
12
|
+
px: "2.5",
|
|
13
|
+
py: "1.5",
|
|
14
|
+
borderRadius: "md",
|
|
15
|
+
boxShadow:
|
|
16
|
+
"0 4px 16px -4px rgba(15,23,42,0.08), 0 2px 6px -2px rgba(15,23,42,0.04)",
|
|
17
|
+
zIndex: "70",
|
|
18
|
+
maxWidth: "90",
|
|
19
|
+
whiteSpace: "normal",
|
|
20
|
+
wordBreak: "break-word",
|
|
21
|
+
_dark: {
|
|
22
|
+
boxShadow:
|
|
23
|
+
"0 4px 16px -4px rgba(0,0,0,0.48), 0 2px 6px -2px rgba(0,0,0,0.28)",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mount } from "@vue/test-utils";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import UiTooltip from "./UiTooltip.vue";
|
|
4
|
+
|
|
5
|
+
describe("UiTooltip", () => {
|
|
6
|
+
it("renders the trigger slot", () => {
|
|
7
|
+
const wrapper = mount(UiTooltip, {
|
|
8
|
+
props: { content: "Help" },
|
|
9
|
+
slots: { default: '<button type="button">Hover me</button>' },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(wrapper.text()).toContain("Hover me");
|
|
13
|
+
expect(wrapper.find("button").exists()).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders the trigger bare when content is empty (no tooltip)", () => {
|
|
17
|
+
const wrapper = mount(UiTooltip, {
|
|
18
|
+
props: { content: "" },
|
|
19
|
+
slots: { default: '<button type="button">Bare</button>' },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(wrapper.find("button").exists()).toBe(true);
|
|
23
|
+
expect(wrapper.text()).toContain("Bare");
|
|
24
|
+
// No tooltip content rendered for empty content.
|
|
25
|
+
expect(wrapper.find('[role="tooltip"]').exists()).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("keeps the trigger focusable", () => {
|
|
29
|
+
const wrapper = mount(UiTooltip, {
|
|
30
|
+
props: { content: "Help" },
|
|
31
|
+
slots: { default: '<button type="button">Hover me</button>' },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const trigger = wrapper.find("button");
|
|
35
|
+
expect(trigger.attributes("tabindex")).not.toBe("-1");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
TooltipProvider,
|
|
4
|
+
TooltipRoot,
|
|
5
|
+
TooltipTrigger,
|
|
6
|
+
TooltipContent,
|
|
7
|
+
} from "reka-ui";
|
|
8
|
+
import { tooltipContent } from "./UiTooltip.styles";
|
|
9
|
+
|
|
10
|
+
withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
/** Tooltip text. When empty, renders the trigger bare (no tooltip). */
|
|
13
|
+
content: string;
|
|
14
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
15
|
+
/** Open delay in ms. */
|
|
16
|
+
delay?: number;
|
|
17
|
+
}>(),
|
|
18
|
+
{
|
|
19
|
+
side: "top",
|
|
20
|
+
delay: 500,
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<TooltipProvider v-if="content" :delay-duration="delay">
|
|
27
|
+
<TooltipRoot>
|
|
28
|
+
<TooltipTrigger as-child>
|
|
29
|
+
<slot />
|
|
30
|
+
</TooltipTrigger>
|
|
31
|
+
<TooltipContent :side="side" :side-offset="6" :class="tooltipContent">
|
|
32
|
+
{{ content }}
|
|
33
|
+
</TooltipContent>
|
|
34
|
+
</TooltipRoot>
|
|
35
|
+
</TooltipProvider>
|
|
36
|
+
<slot v-else />
|
|
37
|
+
</template>
|
package/src/components/index.ts
CHANGED
|
@@ -13,3 +13,16 @@ export { default as UiSelect } from "./UiSelect.vue";
|
|
|
13
13
|
export { default as UiSkeleton } from "./UiSkeleton.vue";
|
|
14
14
|
export { default as UiToast } from "./UiToast.vue";
|
|
15
15
|
export { default as UiToastProvider } from "./UiToastProvider.vue";
|
|
16
|
+
export { default as UiTooltip } from "./UiTooltip.vue";
|
|
17
|
+
export { default as DataTable } from "./DataTable.vue";
|
|
18
|
+
export type { DataTableColumn } from "./DataTable.types";
|
|
19
|
+
export {
|
|
20
|
+
DropdownMenuItem,
|
|
21
|
+
DropdownMenuSeparator,
|
|
22
|
+
DropdownMenuRoot,
|
|
23
|
+
DropdownMenuTrigger,
|
|
24
|
+
DropdownMenuContent,
|
|
25
|
+
} from "reka-ui";
|
|
26
|
+
export { actionMenu, menuBtnIcon } from "./DataTable.styles";
|
|
27
|
+
export { tooltipContent } from "./UiTooltip.styles";
|
|
28
|
+
export { TooltipRoot, TooltipTrigger, TooltipContent, TooltipProvider } from "reka-ui";
|
package/src/css/base.css
CHANGED
|
@@ -91,11 +91,22 @@ body {
|
|
|
91
91
|
@keyframes modalIn {
|
|
92
92
|
from {
|
|
93
93
|
opacity: 0;
|
|
94
|
-
transform: scale(0.
|
|
94
|
+
transform: translateY(8px) scale(0.97);
|
|
95
95
|
}
|
|
96
96
|
to {
|
|
97
97
|
opacity: 1;
|
|
98
|
-
transform:
|
|
98
|
+
transform: translateY(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@keyframes modalSlideUp {
|
|
103
|
+
from {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
transform: translateY(100%);
|
|
106
|
+
}
|
|
107
|
+
to {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
transform: translateY(0);
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/vue3-vite";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import {
|
|
4
|
+
DataTable,
|
|
5
|
+
UiButton,
|
|
6
|
+
DropdownMenuItem,
|
|
7
|
+
DropdownMenuSeparator,
|
|
8
|
+
DropdownMenuRoot,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
} from "../../components";
|
|
12
|
+
import type { DataTableColumn } from "../../components";
|
|
13
|
+
import StoryThemeFrame from "../support/StoryThemeFrame.vue";
|
|
14
|
+
import { storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass } from "../support/storyStyles";
|
|
15
|
+
import { dataTableDefaultSource, dataTableLoadingSource, dataTableSortableSource } from "../support/sources";
|
|
16
|
+
|
|
17
|
+
interface Member {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
email: string;
|
|
21
|
+
role: "ADMIN" | "MEMBER";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const columns: DataTableColumn[] = [
|
|
25
|
+
{ key: "name", label: "Member", width: "1fr" },
|
|
26
|
+
{ key: "email", label: "Email", width: "1fr" },
|
|
27
|
+
{ key: "role", label: "Role", width: "120px" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const meta = {
|
|
31
|
+
title: "Layout/DataTable",
|
|
32
|
+
id: "data-table",
|
|
33
|
+
// Generic <T> component doesn't fit Meta's ConcreteComponent type; cast keeps
|
|
34
|
+
// autodocs working without forcing typed args on every story.
|
|
35
|
+
component: DataTable as never,
|
|
36
|
+
tags: ["autodocs"],
|
|
37
|
+
parameters: {
|
|
38
|
+
docs: {
|
|
39
|
+
description: {
|
|
40
|
+
component:
|
|
41
|
+
"Reusable data table with debounced search, Set-based selection (shift+click, drag-to-select, Ctrl/Cmd+A, Escape), responsive mobile cards, bulk actions, per-row actions, and loading skeletons.",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
} satisfies Meta;
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj;
|
|
49
|
+
|
|
50
|
+
export const Default: Story = {
|
|
51
|
+
parameters: { docs: { source: { code: dataTableDefaultSource } } },
|
|
52
|
+
render: () => ({
|
|
53
|
+
components: { DataTable, UiButton, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuContent, StoryThemeFrame },
|
|
54
|
+
setup() {
|
|
55
|
+
const previewTheme = ref<"light" | "dark">("dark");
|
|
56
|
+
const selected = ref<string[]>([]);
|
|
57
|
+
const items = ref<Member[]>([
|
|
58
|
+
{ id: "1", name: "Alice Johnson", email: "alice@example.com", role: "ADMIN" },
|
|
59
|
+
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "MEMBER" },
|
|
60
|
+
{ id: "3", name: "Carla Mendes", email: "carla@example.com", role: "MEMBER" },
|
|
61
|
+
]);
|
|
62
|
+
return { previewTheme, selected, items, columns, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass };
|
|
63
|
+
},
|
|
64
|
+
template: `
|
|
65
|
+
<StoryThemeFrame v-model:theme="previewTheme" preview-id="default">
|
|
66
|
+
<div :class="[storySurfaceClass, storySurfaceWideClass]">
|
|
67
|
+
<p :class="storySectionLabelClass">Searchable + selectable</p>
|
|
68
|
+
<DataTable v-model:selected="selected" :items="items" :columns="columns" searchable :search-fields="['name', 'email']" search-placeholder="Search by name or email...">
|
|
69
|
+
<template #bulk-actions="{ selectedItems }">
|
|
70
|
+
<UiButton size="sm" variant="outline">{{ selectedItems.length }} selected</UiButton>
|
|
71
|
+
</template>
|
|
72
|
+
<template #cell-name="{ item }"><strong>{{ item.name }}</strong></template>
|
|
73
|
+
<template #cell-email="{ item }">{{ item.email }}</template>
|
|
74
|
+
<template #cell-role="{ item }">{{ item.role === 'ADMIN' ? 'Admin' : 'Member' }}</template>
|
|
75
|
+
<template #actions="{ item }">
|
|
76
|
+
<DropdownMenuItem @select="() => {}">Change role</DropdownMenuItem>
|
|
77
|
+
<DropdownMenuSeparator />
|
|
78
|
+
<DropdownMenuItem @select="() => {}">Deactivate</DropdownMenuItem>
|
|
79
|
+
</template>
|
|
80
|
+
</DataTable>
|
|
81
|
+
<p :class="storyCaptionClass">Shift+click ranges, drag to paint, Ctrl/Cmd+A selects all visible, Escape clears.</p>
|
|
82
|
+
</div>
|
|
83
|
+
</StoryThemeFrame>
|
|
84
|
+
`,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Loading: Story = {
|
|
89
|
+
parameters: { docs: { source: { code: dataTableLoadingSource } } },
|
|
90
|
+
render: () => ({
|
|
91
|
+
components: { DataTable, StoryThemeFrame },
|
|
92
|
+
setup() {
|
|
93
|
+
const previewTheme = ref<"light" | "dark">("dark");
|
|
94
|
+
return { previewTheme, columns, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass };
|
|
95
|
+
},
|
|
96
|
+
template: `
|
|
97
|
+
<StoryThemeFrame v-model:theme="previewTheme" preview-id="loading">
|
|
98
|
+
<div :class="[storySurfaceClass, storySurfaceWideClass]">
|
|
99
|
+
<p :class="storySectionLabelClass">Loading skeletons</p>
|
|
100
|
+
<DataTable :items="[]" :columns="columns" :is-loading="true" searchable />
|
|
101
|
+
<p :class="storyCaptionClass">Skeleton rows render while data loads, with aria-busy on the root.</p>
|
|
102
|
+
</div>
|
|
103
|
+
</StoryThemeFrame>
|
|
104
|
+
`,
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const SortablePaginated: Story = {
|
|
109
|
+
parameters: { docs: { source: { code: dataTableSortableSource } } },
|
|
110
|
+
render: () => ({
|
|
111
|
+
components: { DataTable, StoryThemeFrame },
|
|
112
|
+
setup() {
|
|
113
|
+
const previewTheme = ref<"light" | "dark">("dark");
|
|
114
|
+
const items = ref<Member[]>([
|
|
115
|
+
{ id: "1", name: "Alice Johnson", email: "alice@example.com", role: "ADMIN" },
|
|
116
|
+
{ id: "2", name: "Bob Smith", email: "bob@example.com", role: "MEMBER" },
|
|
117
|
+
{ id: "3", name: "Carla Mendes", email: "carla@example.com", role: "MEMBER" },
|
|
118
|
+
{ id: "4", name: "Dan Lee", email: "dan@example.com", role: "MEMBER" },
|
|
119
|
+
]);
|
|
120
|
+
const sortColumns: DataTableColumn[] = [
|
|
121
|
+
{ key: "name", label: "Member", width: "1fr", sortable: true },
|
|
122
|
+
{ key: "email", label: "Email", width: "1fr", sortable: true },
|
|
123
|
+
{ key: "role", label: "Role", width: "120px", sortable: true },
|
|
124
|
+
];
|
|
125
|
+
return { previewTheme, items, sortColumns, storyCaptionClass, storySectionLabelClass, storySurfaceClass, storySurfaceWideClass };
|
|
126
|
+
},
|
|
127
|
+
template: `
|
|
128
|
+
<StoryThemeFrame v-model:theme="previewTheme" preview-id="sortable">
|
|
129
|
+
<div :class="[storySurfaceClass, storySurfaceWideClass]">
|
|
130
|
+
<p :class="storySectionLabelClass">Sortable + paginated (display-only)</p>
|
|
131
|
+
<DataTable :items="items" :columns="sortColumns" :page-size="3" :selectable="false">
|
|
132
|
+
<template #cell-name="{ item }"><strong>{{ item.name }}</strong></template>
|
|
133
|
+
<template #cell-email="{ item }">{{ item.email }}</template>
|
|
134
|
+
<template #cell-role="{ item }">{{ item.role === 'ADMIN' ? 'Admin' : 'Member' }}</template>
|
|
135
|
+
</DataTable>
|
|
136
|
+
<p :class="storyCaptionClass">sortable columns toggle asc/desc/clear; pageSize adds client-side pagination. selectable=false hides the checkbox column.</p>
|
|
137
|
+
</div>
|
|
138
|
+
</StoryThemeFrame>
|
|
139
|
+
`,
|
|
140
|
+
}),
|
|
141
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/vue3-vite";
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import { UiTooltip, UiButton } from "../../components";
|
|
4
|
+
import StoryThemeFrame from "../support/StoryThemeFrame.vue";
|
|
5
|
+
import { storyCaptionClass, storySectionLabelClass, storySurfaceClass } from "../support/storyStyles";
|
|
6
|
+
import { uiTooltipDefaultSource } from "../support/sources";
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Primitives/UiTooltip",
|
|
10
|
+
id: "ui-tooltip",
|
|
11
|
+
component: UiTooltip as never,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
"Accessible tooltip built on Reka UI. Renders the trigger in place and floats the content on hover/focus with positioning, portal, and keyboard support.",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
} satisfies Meta;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj;
|
|
25
|
+
|
|
26
|
+
export const Default: Story = {
|
|
27
|
+
parameters: { docs: { source: { code: uiTooltipDefaultSource } } },
|
|
28
|
+
render: () => ({
|
|
29
|
+
components: { UiTooltip, UiButton, StoryThemeFrame },
|
|
30
|
+
setup() {
|
|
31
|
+
const previewTheme = ref<"light" | "dark">("dark");
|
|
32
|
+
return { previewTheme, storyCaptionClass, storySectionLabelClass, storySurfaceClass };
|
|
33
|
+
},
|
|
34
|
+
template: `
|
|
35
|
+
<StoryThemeFrame v-model:theme="previewTheme" preview-id="default">
|
|
36
|
+
<div :class="storySurfaceClass" style="min-height:160px;display:flex;align-items:center;justify-content:center;gap:4">
|
|
37
|
+
<p :class="storySectionLabelClass">Hover the button</p>
|
|
38
|
+
<UiTooltip content="Saves changes to the workspace">
|
|
39
|
+
<UiButton size="sm">Save</UiButton>
|
|
40
|
+
</UiTooltip>
|
|
41
|
+
<p :class="storyCaptionClass">Reka UI handles positioning, portal, and focus.</p>
|
|
42
|
+
</div>
|
|
43
|
+
</StoryThemeFrame>
|
|
44
|
+
`,
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
@@ -372,3 +372,84 @@ import { PageSurface, SectionCard, StatusBadge, UiButton } from "@aggc/ui";
|
|
|
372
372
|
</SectionCard>
|
|
373
373
|
</PageSurface>
|
|
374
374
|
</template>`;
|
|
375
|
+
|
|
376
|
+
export const dataTableDefaultSource = `<script setup lang="ts">
|
|
377
|
+
import { ref } from "vue";
|
|
378
|
+
import { DataTable, UiButton, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuContent } from "@aggc/ui";
|
|
379
|
+
import type { DataTableColumn } from "@aggc/ui";
|
|
380
|
+
|
|
381
|
+
interface Member { id: string; name: string; email: string; }
|
|
382
|
+
|
|
383
|
+
const items = ref<Member[]>([
|
|
384
|
+
{ id: "1", name: "Alice", email: "alice@example.com" },
|
|
385
|
+
{ id: "2", name: "Bob", email: "bob@example.com" },
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
const columns: DataTableColumn[] = [
|
|
389
|
+
{ key: "name", label: "Member", width: "1fr" },
|
|
390
|
+
{ key: "email", label: "Email", width: "1fr" },
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
const selected = ref<string[]>([]);
|
|
394
|
+
</script>
|
|
395
|
+
|
|
396
|
+
<template>
|
|
397
|
+
<DataTable v-model:selected="selected" :items="items" :columns="columns" searchable :search-fields="['name', 'email']">
|
|
398
|
+
<template #bulk-actions="{ selectedItems }">
|
|
399
|
+
<UiButton size="sm" variant="outline">{{ selectedItems.length }} selected</UiButton>
|
|
400
|
+
</template>
|
|
401
|
+
<template #cell-name="{ item }">{{ item.name }}</template>
|
|
402
|
+
<template #cell-email="{ item }">{{ item.email }}</template>
|
|
403
|
+
<template #actions="{ item }">
|
|
404
|
+
<DropdownMenuItem @select="() => {}">Edit {{ item.name }}</DropdownMenuItem>
|
|
405
|
+
</template>
|
|
406
|
+
</DataTable>
|
|
407
|
+
</template>`;
|
|
408
|
+
|
|
409
|
+
export const dataTableLoadingSource = `<script setup lang="ts">
|
|
410
|
+
import { DataTable } from "@aggc/ui";
|
|
411
|
+
import type { DataTableColumn } from "@aggc/ui";
|
|
412
|
+
|
|
413
|
+
const columns: DataTableColumn[] = [
|
|
414
|
+
{ key: "name", label: "Member", width: "1fr" },
|
|
415
|
+
{ key: "email", label: "Email", width: "1fr" },
|
|
416
|
+
];
|
|
417
|
+
</script>
|
|
418
|
+
|
|
419
|
+
<template>
|
|
420
|
+
<DataTable :items="[]" :columns="columns" :is-loading="true" searchable />
|
|
421
|
+
</template>`;
|
|
422
|
+
|
|
423
|
+
export const uiTooltipDefaultSource = `<script setup lang="ts">
|
|
424
|
+
import { UiTooltip } from "@aggc/ui";
|
|
425
|
+
</script>
|
|
426
|
+
|
|
427
|
+
<template>
|
|
428
|
+
<UiTooltip content="Helpful tip">
|
|
429
|
+
<button type="button">Hover me</button>
|
|
430
|
+
</UiTooltip>
|
|
431
|
+
</template>`;
|
|
432
|
+
|
|
433
|
+
export const dataTableSortableSource = `<script setup lang="ts">
|
|
434
|
+
import { ref } from "vue";
|
|
435
|
+
import { DataTable } from "@aggc/ui";
|
|
436
|
+
import type { DataTableColumn } from "@aggc/ui";
|
|
437
|
+
|
|
438
|
+
interface Member { id: string; name: string; email: string; }
|
|
439
|
+
|
|
440
|
+
const items = ref<Member[]>([
|
|
441
|
+
{ id: "1", name: "Alice", email: "alice@example.com" },
|
|
442
|
+
{ id: "2", name: "Bob", email: "bob@example.com" },
|
|
443
|
+
{ id: "3", name: "Carla", email: "carla@example.com" },
|
|
444
|
+
{ id: "4", name: "Dan", email: "dan@example.com" },
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
const columns: DataTableColumn[] = [
|
|
448
|
+
{ key: "name", label: "Member", width: "1fr", sortable: true },
|
|
449
|
+
{ key: "email", label: "Email", width: "1fr", sortable: true },
|
|
450
|
+
];
|
|
451
|
+
</script>
|
|
452
|
+
|
|
453
|
+
<template>
|
|
454
|
+
<DataTable :items="items" :columns="columns" :page-size="3" />
|
|
455
|
+
</template>`;
|