@nexxtmove/ui 0.1.29 → 0.1.30
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/index.d.ts +30 -8
- package/dist/index.js +1162 -958
- package/dist/nuxt.js +20 -18
- package/package.json +1 -1
- package/src/components/Checkbox/Checkbox.stories.ts +35 -0
- package/src/components/Checkbox/Checkbox.test.ts +55 -0
- package/src/components/Checkbox/Checkbox.vue +46 -0
- package/src/components/Table/Table.stories.ts +283 -0
- package/src/components/Table/Table.test.ts +220 -0
- package/src/components/Table/Table.vue +227 -0
- package/src/components.json +2 -0
- package/src/index.ts +2 -0
package/dist/nuxt.js
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { defineNuxtModule as
|
|
2
|
-
const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button/Button.vue",
|
|
1
|
+
import { defineNuxtModule as c, addComponent as s, addTemplate as i } from "@nuxt/kit";
|
|
2
|
+
const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button/Button.vue", x = "components/Calendar/Calendar.vue", u = "components/Checkbox/Checkbox.vue", p = "components/Chip/Chip.vue", l = "components/CustomerJourneyTile/CustomerJourneyTile.vue", r = "components/DatePicker/DatePicker.vue", d = "components/DropdownButton/DropdownButton.vue", v = "components/Header/Header.vue", N = "components/Icon/Icon.vue", T = "components/InfoBlock/InfoBlock.vue", C = "components/ProgressBar/ProgressBar.vue", S = "components/SocialIcons/SocialIcons.vue", b = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", f = "components/SocialMediaTemplate/SocialMediaTemplate.vue", h = "components/SocialMediaType/SocialMediaType.vue", k = "components/StepperHeader/StepperHeader.vue", B = "components/Table/Table.vue", M = "components/TimelineEvent/TimelineEvent.vue", P = "components/TimelinePhaseblock/TimelinePhaseblock.vue", I = "components/Tooltip/Tooltip.vue", y = {
|
|
3
3
|
NexxtAnimatedNumber: a,
|
|
4
4
|
NexxtButton: m,
|
|
5
|
-
NexxtCalendar:
|
|
5
|
+
NexxtCalendar: x,
|
|
6
|
+
NexxtCheckbox: u,
|
|
6
7
|
NexxtChip: p,
|
|
7
|
-
NexxtCustomerJourneyTile:
|
|
8
|
-
NexxtDatePicker:
|
|
9
|
-
NexxtDropdownButton:
|
|
10
|
-
NexxtHeader:
|
|
11
|
-
NexxtIcon:
|
|
12
|
-
NexxtInfoBlock:
|
|
13
|
-
NexxtProgressBar:
|
|
8
|
+
NexxtCustomerJourneyTile: l,
|
|
9
|
+
NexxtDatePicker: r,
|
|
10
|
+
NexxtDropdownButton: d,
|
|
11
|
+
NexxtHeader: v,
|
|
12
|
+
NexxtIcon: N,
|
|
13
|
+
NexxtInfoBlock: T,
|
|
14
|
+
NexxtProgressBar: C,
|
|
14
15
|
NexxtSocialIcons: S,
|
|
15
|
-
NexxtSocialMediaCustomTemplate:
|
|
16
|
+
NexxtSocialMediaCustomTemplate: b,
|
|
16
17
|
NexxtSocialMediaTemplate: f,
|
|
17
|
-
NexxtSocialMediaType:
|
|
18
|
-
NexxtStepperHeader:
|
|
18
|
+
NexxtSocialMediaType: h,
|
|
19
|
+
NexxtStepperHeader: k,
|
|
20
|
+
NexxtTable: B,
|
|
19
21
|
NexxtTimelineEvent: M,
|
|
20
22
|
NexxtTimelinePhaseblock: P,
|
|
21
|
-
NexxtTooltip:
|
|
22
|
-
},
|
|
23
|
+
NexxtTooltip: I
|
|
24
|
+
}, H = c({
|
|
23
25
|
meta: {
|
|
24
26
|
name: "@nexxtmove/ui",
|
|
25
27
|
configKey: "nexxtmoveUi",
|
|
@@ -31,8 +33,8 @@ const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button
|
|
|
31
33
|
addCSS: !0
|
|
32
34
|
},
|
|
33
35
|
setup(e, o) {
|
|
34
|
-
for (const [t, n] of Object.entries(
|
|
35
|
-
|
|
36
|
+
for (const [t, n] of Object.entries(y))
|
|
37
|
+
s({
|
|
36
38
|
name: t,
|
|
37
39
|
export: "default",
|
|
38
40
|
filePath: `@nexxtmove/ui/${n}`
|
|
@@ -50,5 +52,5 @@ const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button
|
|
|
50
52
|
}
|
|
51
53
|
});
|
|
52
54
|
export {
|
|
53
|
-
|
|
55
|
+
H as default
|
|
54
56
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import Checkbox from './Checkbox.vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Atoms/Checkbox',
|
|
6
|
+
component: Checkbox,
|
|
7
|
+
} satisfies Meta<typeof Checkbox>
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof Checkbox>
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
modelValue: false,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Checked: Story = {
|
|
18
|
+
args: {
|
|
19
|
+
modelValue: true,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Disabled: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
modelValue: false,
|
|
26
|
+
disabled: true,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const DisabledChecked: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
modelValue: true,
|
|
33
|
+
disabled: true,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Checkbox from './Checkbox.vue'
|
|
4
|
+
|
|
5
|
+
describe('Checkbox', () => {
|
|
6
|
+
it('renders a checkbox input', () => {
|
|
7
|
+
const wrapper = mount(Checkbox)
|
|
8
|
+
|
|
9
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('is unchecked by default', () => {
|
|
13
|
+
const wrapper = mount(Checkbox)
|
|
14
|
+
|
|
15
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(false)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('is checked when modelValue is true', () => {
|
|
19
|
+
const wrapper = mount(Checkbox, { props: { modelValue: true } })
|
|
20
|
+
|
|
21
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('emits update:modelValue when toggled', async () => {
|
|
25
|
+
const wrapper = mount(Checkbox, { props: { modelValue: false } })
|
|
26
|
+
|
|
27
|
+
await wrapper.find('input').setValue(true)
|
|
28
|
+
|
|
29
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('disables the input when disabled prop is true', () => {
|
|
33
|
+
const wrapper = mount(Checkbox, { props: { disabled: true } })
|
|
34
|
+
|
|
35
|
+
expect((wrapper.find('input').element as HTMLInputElement).disabled).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('applies opacity class when disabled', () => {
|
|
39
|
+
const wrapper = mount(Checkbox, { props: { disabled: true } })
|
|
40
|
+
|
|
41
|
+
expect(wrapper.find('label').classes()).toContain('opacity-50')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('forwards attributes to the input', () => {
|
|
45
|
+
const wrapper = mount(Checkbox, { attrs: { 'aria-label': 'Accept terms' } })
|
|
46
|
+
|
|
47
|
+
expect(wrapper.find('input').attributes('aria-label')).toBe('Accept terms')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders the check icon', () => {
|
|
51
|
+
const wrapper = mount(Checkbox)
|
|
52
|
+
|
|
53
|
+
expect(wrapper.find('i.fa-check').exists()).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watchEffect } from 'vue'
|
|
3
|
+
import Icon from '../Icon/Icon.vue'
|
|
4
|
+
|
|
5
|
+
interface NexxtCheckboxProps {
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
indeterminate?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
defineOptions({
|
|
11
|
+
name: 'NexxtCheckbox',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const model = defineModel<boolean>()
|
|
15
|
+
const { indeterminate } = defineProps<NexxtCheckboxProps>()
|
|
16
|
+
|
|
17
|
+
const inputRef = ref<HTMLInputElement>()
|
|
18
|
+
watchEffect(() => {
|
|
19
|
+
if (inputRef.value) inputRef.value.indeterminate = indeterminate ?? false
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<label :class="['inline-flex cursor-pointer', disabled ? 'cursor-not-allowed opacity-50' : '']">
|
|
25
|
+
<input
|
|
26
|
+
v-bind="$attrs"
|
|
27
|
+
ref="inputRef"
|
|
28
|
+
type="checkbox"
|
|
29
|
+
class="peer sr-only"
|
|
30
|
+
v-model="model"
|
|
31
|
+
:disabled="disabled"
|
|
32
|
+
/>
|
|
33
|
+
<span
|
|
34
|
+
aria-hidden="true"
|
|
35
|
+
:class="[
|
|
36
|
+
'flex h-5 w-5 shrink-0 items-center justify-center rounded border transition-colors duration-200',
|
|
37
|
+
'peer-focus-visible:ring-primary peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2',
|
|
38
|
+
model || indeterminate
|
|
39
|
+
? 'border-primary text-cornflower-blue-600'
|
|
40
|
+
: 'border-gray-300 text-transparent',
|
|
41
|
+
]"
|
|
42
|
+
>
|
|
43
|
+
<Icon :name="indeterminate ? 'minus' : 'check'" type="solid" class="text-xs" />
|
|
44
|
+
</span>
|
|
45
|
+
</label>
|
|
46
|
+
</template>
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { computed, ref, type ConcreteComponent } from 'vue'
|
|
3
|
+
import Table, { type TableColumn } from './Table.vue'
|
|
4
|
+
|
|
5
|
+
type RowKey = 'name' | 'email' | 'role' | 'status' | 'department' | 'lastLogin'
|
|
6
|
+
|
|
7
|
+
interface TableStoryProps {
|
|
8
|
+
columns: TableColumn<RowKey>[]
|
|
9
|
+
rows: Record<RowKey, string>[]
|
|
10
|
+
selectable?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const meta: Meta<TableStoryProps> = {
|
|
14
|
+
title: 'Components/Atoms/Table',
|
|
15
|
+
component: Table as ConcreteComponent<TableStoryProps>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default meta
|
|
19
|
+
type Story = StoryObj<TableStoryProps>
|
|
20
|
+
|
|
21
|
+
const columns: TableColumn<RowKey>[] = [
|
|
22
|
+
{ key: 'name', label: 'Name', sortable: true },
|
|
23
|
+
{ key: 'email', label: 'Email', sortable: true },
|
|
24
|
+
{ key: 'department', label: 'Department' },
|
|
25
|
+
{ key: 'role', label: 'Role', sortable: true, width: '0.5fr' },
|
|
26
|
+
{ key: 'lastLogin', label: 'lastLogin' },
|
|
27
|
+
{ key: 'status', label: 'Status', width: '0.5fr' },
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const allRows = [
|
|
31
|
+
{
|
|
32
|
+
name: 'Alice Johnson',
|
|
33
|
+
email: 'alice@example.com',
|
|
34
|
+
role: 'Admin',
|
|
35
|
+
status: 'Active',
|
|
36
|
+
department: 'HR',
|
|
37
|
+
lastLogin: '2026-03-25',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Bob Smith',
|
|
41
|
+
email: 'bob@example.com',
|
|
42
|
+
role: 'Editor',
|
|
43
|
+
status: 'Active',
|
|
44
|
+
department: 'Marketing',
|
|
45
|
+
lastLogin: '2026-03-27',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Carol White',
|
|
49
|
+
email: 'carol@example.com',
|
|
50
|
+
role: 'Viewer',
|
|
51
|
+
status: 'Inactive',
|
|
52
|
+
department: 'Finance',
|
|
53
|
+
lastLogin: '2026-02-12',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'David Brown',
|
|
57
|
+
email: 'david@example.com',
|
|
58
|
+
role: 'Editor',
|
|
59
|
+
status: 'Active',
|
|
60
|
+
department: 'IT',
|
|
61
|
+
lastLogin: '2026-03-30',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'Eve Turner',
|
|
65
|
+
email: 'eve@example.com',
|
|
66
|
+
role: 'Admin',
|
|
67
|
+
status: 'Active',
|
|
68
|
+
department: 'HR',
|
|
69
|
+
lastLogin: '2026-03-28',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Frank Miller',
|
|
73
|
+
email: 'frank@example.com',
|
|
74
|
+
role: 'Viewer',
|
|
75
|
+
status: 'Active',
|
|
76
|
+
department: 'Support',
|
|
77
|
+
lastLogin: '2026-03-26',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Grace Lee',
|
|
81
|
+
email: 'grace@example.com',
|
|
82
|
+
role: 'Editor',
|
|
83
|
+
status: 'Active',
|
|
84
|
+
department: 'Product',
|
|
85
|
+
lastLogin: '2026-03-29',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'Henry Wilson',
|
|
89
|
+
email: 'henry@example.com',
|
|
90
|
+
role: 'Viewer',
|
|
91
|
+
status: 'Inactive',
|
|
92
|
+
department: 'Finance',
|
|
93
|
+
lastLogin: '2026-01-10',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'Ivy Clark',
|
|
97
|
+
email: 'ivy@example.com',
|
|
98
|
+
role: 'Admin',
|
|
99
|
+
status: 'Active',
|
|
100
|
+
department: 'IT',
|
|
101
|
+
lastLogin: '2026-03-20',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Jack Davis',
|
|
105
|
+
email: 'jack@example.com',
|
|
106
|
+
role: 'Editor',
|
|
107
|
+
status: 'Active',
|
|
108
|
+
department: 'Marketing',
|
|
109
|
+
lastLogin: '2026-03-23',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'Karen Harris',
|
|
113
|
+
email: 'karen@example.com',
|
|
114
|
+
role: 'Viewer',
|
|
115
|
+
status: 'Active',
|
|
116
|
+
department: 'Support',
|
|
117
|
+
lastLogin: '2026-03-19',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'Leo King',
|
|
121
|
+
email: 'leo@example.com',
|
|
122
|
+
role: 'Editor',
|
|
123
|
+
status: 'Active',
|
|
124
|
+
department: 'Product',
|
|
125
|
+
lastLogin: '2026-03-28',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'Mia Scott',
|
|
129
|
+
email: 'mia@example.com',
|
|
130
|
+
role: 'Admin',
|
|
131
|
+
status: 'Active',
|
|
132
|
+
department: 'HR',
|
|
133
|
+
lastLogin: '2026-03-30',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'Nick Evans',
|
|
137
|
+
email: 'nick@example.com',
|
|
138
|
+
role: 'Viewer',
|
|
139
|
+
status: 'Inactive',
|
|
140
|
+
department: 'Finance',
|
|
141
|
+
lastLogin: '2026-02-05',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'Olivia Foster',
|
|
145
|
+
email: 'olivia@example.com',
|
|
146
|
+
role: 'Editor',
|
|
147
|
+
status: 'Active',
|
|
148
|
+
department: 'Product',
|
|
149
|
+
lastLogin: '2026-03-27',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'Paul Green',
|
|
153
|
+
email: 'paul@example.com',
|
|
154
|
+
role: 'Viewer',
|
|
155
|
+
status: 'Active',
|
|
156
|
+
department: 'Support',
|
|
157
|
+
lastLogin: '2026-03-18',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'Quincy Adams',
|
|
161
|
+
email: 'quincy@example.com',
|
|
162
|
+
role: 'Editor',
|
|
163
|
+
status: 'Active',
|
|
164
|
+
department: 'Marketing',
|
|
165
|
+
lastLogin: '2026-03-25',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'Rachel Lewis',
|
|
169
|
+
email: 'rachel@example.com',
|
|
170
|
+
role: 'Admin',
|
|
171
|
+
status: 'Active',
|
|
172
|
+
department: 'HR',
|
|
173
|
+
lastLogin: '2026-03-29',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'Sam Nelson',
|
|
177
|
+
email: 'sam@example.com',
|
|
178
|
+
role: 'Viewer',
|
|
179
|
+
status: 'Inactive',
|
|
180
|
+
department: 'Finance',
|
|
181
|
+
lastLogin: '2026-01-15',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'Tina Brooks',
|
|
185
|
+
email: 'tina@example.com',
|
|
186
|
+
role: 'Editor',
|
|
187
|
+
status: 'Active',
|
|
188
|
+
department: 'IT',
|
|
189
|
+
lastLogin: '2026-03-30',
|
|
190
|
+
},
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
const sortRows = (rows: typeof allRows, key: string | undefined, direction: 'asc' | 'desc') => {
|
|
194
|
+
if (!key) return rows
|
|
195
|
+
return [...rows].sort((a, b) => {
|
|
196
|
+
const aVal = String(a[key as keyof typeof a] ?? '')
|
|
197
|
+
const bVal = String(b[key as keyof typeof b] ?? '')
|
|
198
|
+
const result = aVal.localeCompare(bVal, undefined, { numeric: true, sensitivity: 'base' })
|
|
199
|
+
return direction === 'asc' ? result : -result
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const Default: Story = {
|
|
204
|
+
args: { columns, rows: allRows },
|
|
205
|
+
render: (args) => ({
|
|
206
|
+
components: { Table },
|
|
207
|
+
setup() {
|
|
208
|
+
const sortKey = ref<string | undefined>(undefined)
|
|
209
|
+
const sortDirection = ref<'asc' | 'desc'>('asc')
|
|
210
|
+
const rows = computed(() =>
|
|
211
|
+
sortRows(args.rows as typeof allRows, sortKey.value, sortDirection.value),
|
|
212
|
+
)
|
|
213
|
+
const onSort = (key: string, direction: 'asc' | 'desc') => {
|
|
214
|
+
sortKey.value = key
|
|
215
|
+
sortDirection.value = direction
|
|
216
|
+
}
|
|
217
|
+
return { args, rows, sortKey, sortDirection, onSort }
|
|
218
|
+
},
|
|
219
|
+
template: `<Table :columns="args.columns" :rows="rows" :sort-key="sortKey" :sort-direction="sortDirection" @sort="onSort" />`,
|
|
220
|
+
}),
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const WithSelection: Story = {
|
|
224
|
+
args: { columns, rows: allRows, selectable: true },
|
|
225
|
+
render: (args) => ({
|
|
226
|
+
components: { Table },
|
|
227
|
+
setup() {
|
|
228
|
+
const sortKey = ref<string | undefined>(undefined)
|
|
229
|
+
const sortDirection = ref<'asc' | 'desc'>('asc')
|
|
230
|
+
const selected = ref<number[]>([])
|
|
231
|
+
const rows = computed(() =>
|
|
232
|
+
sortRows(args.rows as typeof allRows, sortKey.value, sortDirection.value),
|
|
233
|
+
)
|
|
234
|
+
const onSort = (key: string, direction: 'asc' | 'desc') => {
|
|
235
|
+
sortKey.value = key
|
|
236
|
+
sortDirection.value = direction
|
|
237
|
+
}
|
|
238
|
+
return { args, rows, sortKey, sortDirection, onSort, selected }
|
|
239
|
+
},
|
|
240
|
+
template: `<Table :columns="args.columns" :rows="rows" :sort-key="sortKey" :sort-direction="sortDirection" :selectable="args.selectable" v-model="selected" @sort="onSort" />`,
|
|
241
|
+
}),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export const WithSlots: Story = {
|
|
245
|
+
args: { columns, rows: allRows },
|
|
246
|
+
render: (args) => ({
|
|
247
|
+
components: { Table },
|
|
248
|
+
setup() {
|
|
249
|
+
const sortKey = ref<string | undefined>(undefined)
|
|
250
|
+
const sortDirection = ref<'asc' | 'desc'>('asc')
|
|
251
|
+
const rows = computed(() =>
|
|
252
|
+
sortRows(args.rows as typeof allRows, sortKey.value, sortDirection.value),
|
|
253
|
+
)
|
|
254
|
+
const onSort = (key: string, direction: 'asc' | 'desc') => {
|
|
255
|
+
sortKey.value = key
|
|
256
|
+
sortDirection.value = direction
|
|
257
|
+
}
|
|
258
|
+
return { args, rows, sortKey, sortDirection, onSort }
|
|
259
|
+
},
|
|
260
|
+
template: `
|
|
261
|
+
<Table :columns="args.columns" :rows="rows" :sort-key="sortKey" :sort-direction="sortDirection" @sort="onSort">
|
|
262
|
+
<template #cell-name="{ value, row }">
|
|
263
|
+
<div class="flex flex-col">
|
|
264
|
+
<span class="font-medium">{{ value }}</span>
|
|
265
|
+
<span class="text-xs text-slate-400">{{ row.email }}</span>
|
|
266
|
+
</div>
|
|
267
|
+
</template>
|
|
268
|
+
<template #cell-status="{ value }">
|
|
269
|
+
<span
|
|
270
|
+
class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium"
|
|
271
|
+
:class="value === 'Active' ? 'bg-green-100 text-green-700' : 'bg-slate-100 text-slate-500'"
|
|
272
|
+
>
|
|
273
|
+
{{ value }}
|
|
274
|
+
</span>
|
|
275
|
+
</template>
|
|
276
|
+
</Table>
|
|
277
|
+
`,
|
|
278
|
+
}),
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export const Empty: Story = {
|
|
282
|
+
args: { columns, rows: [] },
|
|
283
|
+
}
|