vueless 0.0.808 → 0.0.810

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.808",
3
+ "version": "0.0.810",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -29,20 +29,6 @@ const emit = defineEmits([
29
29
  * @property {array} sortData
30
30
  */
31
31
  "dragSort",
32
-
33
- /**
34
- * Triggers when edit button is clicked.
35
- * @property {number} value
36
- * @property {string} label
37
- */
38
- "clickEdit",
39
-
40
- /**
41
- * Triggers when delete button is clicked.
42
- * @property {number} value
43
- * @property {string} label
44
- */
45
- "clickDelete",
46
32
  ]);
47
33
 
48
34
  const { tm } = useLocale();
@@ -50,13 +36,13 @@ const { tm } = useLocale();
50
36
  const i18nGlobal = tm(COMPONENT_NAME);
51
37
  const currentLocale = computed(() => merge({}, defaultConfig.i18n, i18nGlobal, props.config.i18n));
52
38
 
53
- function isActive(element: DataListItem) {
54
- return element.isActive === undefined || element.isActive;
39
+ function isCrossed(element: DataListItem) {
40
+ return Boolean(element.crossed);
55
41
  }
56
42
 
57
43
  function onDragMove(event: DragMoveEvent): boolean | void {
58
- const isDisabledNestingItem = event.draggedContext.element.isDisabledNesting;
59
- const isNestingAction = !event.relatedContext?.element?.isDisabledNesting;
44
+ const isDisabledNestingItem = !Boolean(event.draggedContext.element.nesting);
45
+ const isNestingAction = Boolean(event.relatedContext?.element?.nesting);
60
46
 
61
47
  if (isDisabledNestingItem && isNestingAction) {
62
48
  return false;
@@ -69,14 +55,6 @@ function onDragEnd() {
69
55
  emit("dragSort", sortData);
70
56
  }
71
57
 
72
- function onClickEdit(value: number, label: string) {
73
- emit("clickEdit", value, label);
74
- }
75
-
76
- function onClickDelete(value: number, label: string) {
77
- emit("clickDelete", value, label);
78
- }
79
-
80
58
  function prepareSortData(list: DataListItem[] = [], parentValue: string | number | null = null) {
81
59
  const sortData: DataListItem[] = [];
82
60
 
@@ -116,28 +94,27 @@ const {
116
94
  labelAttrs,
117
95
  labelCrossedAttrs,
118
96
  customActionsAttrs,
119
- deleteIconAttrs,
120
- editIconAttrs,
121
97
  dragIconAttrs,
98
+ dragAttrs,
122
99
  } = useUI<Config>(defaultConfig);
123
100
  </script>
124
101
 
125
102
  <template>
126
103
  <div v-bind="wrapperAttrs">
127
104
  <!--
128
- @slot Use it to add something instead of the drag icon.
105
+ @slot Use it to add custom empty state.
129
106
  @binding {string} empty-title
130
107
  @binding {string} empty-description
131
108
  -->
132
109
  <slot
133
110
  v-if="!hideEmptyStateForNesting && !list?.length"
134
111
  name="empty"
135
- :empty-title="emptyTitle"
136
- :empty-description="emptyDescription"
112
+ :empty-title="currentLocale.emptyTitle"
113
+ :empty-description="currentLocale.emptyDescription"
137
114
  >
138
115
  <UEmpty
139
- :title="emptyTitle || currentLocale.emptyTitle"
140
- :description="emptyDescription || currentLocale.emptyDescription"
116
+ :title="currentLocale.emptyTitle"
117
+ :description="currentLocale.emptyDescription"
141
118
  v-bind="emptyAttrs"
142
119
  />
143
120
  </slot>
@@ -159,105 +136,64 @@ const {
159
136
  <template #item="{ element }">
160
137
  <div :id="element[valueKey]" v-bind="itemWrapperAttrs" :data-test="getDataTest('item')">
161
138
  <div v-bind="itemAttrs" :data-test="getDataTest(`item-${element[valueKey]}`)">
162
- <!--
163
- @slot Use it to add something instead of the drag icon.
164
- @binding {object} item
165
- @binding {string} icon-name
166
- -->
167
- <slot name="drag" :item="element" :icon-name="config.defaults.dragIcon">
168
- <UIcon
169
- internal
170
- color="gray"
171
- variant="light"
172
- :name="config.defaults.dragIcon"
173
- v-bind="dragIconAttrs"
174
- />
175
- </slot>
139
+ <div v-bind="dragAttrs">
140
+ <!--
141
+ @slot Use it to add something instead of the drag icon.
142
+ @binding {object} item
143
+ @binding {string} icon-name
144
+ -->
145
+ <slot name="drag" :item="element" :icon-name="config.defaults.dragIcon">
146
+ <UIcon
147
+ internal
148
+ color="gray"
149
+ variant="light"
150
+ :name="config.defaults.dragIcon"
151
+ v-bind="dragIconAttrs"
152
+ />
153
+ </slot>
154
+ </div>
176
155
 
177
- <div v-bind="isActive(element) ? labelAttrs : labelCrossedAttrs">
156
+ <div v-bind="isCrossed(element) ? labelCrossedAttrs : labelAttrs">
178
157
  <!--
179
158
  @slot Use it to modify label.
180
159
  @binding {object} item
181
- @binding {boolean} active
160
+ @binding {boolean} crossed
182
161
  -->
183
- <slot name="label" :item="element" :active="isActive(element)">
162
+ <slot name="label" :item="element" :crossed="isCrossed(element)">
184
163
  {{ element[labelKey] }}
185
164
  </slot>
186
165
  </div>
187
166
 
188
- <template v-if="!element.isHiddenActions">
189
- <div
190
- v-if="hasSlotContent($slots['actions']) && !element.isHiddenCustomActions"
191
- v-bind="customActionsAttrs"
192
- >
167
+ <template v-if="element.actions !== false">
168
+ <div v-if="hasSlotContent($slots['actions'])" v-bind="customActionsAttrs">
193
169
  <!--
194
170
  @slot Use it to add custom actions.
195
171
  @binding {object} item
196
172
  -->
197
173
  <slot name="actions" :item="element" />
198
174
  </div>
199
-
200
- <!--
201
- @slot Use it to add something instead of the delete icon.
202
- @binding {object} item
203
- @binding {string} icon-name
204
- -->
205
- <slot name="delete" :item="element" :icon-name="config.defaults.deleteIcon">
206
- <UIcon
207
- v-if="!element.isHiddenDelete"
208
- internal
209
- interactive
210
- color="red"
211
- :name="config.defaults.deleteIcon"
212
- :tooltip="currentLocale.delete"
213
- v-bind="deleteIconAttrs"
214
- :data-test="getDataTest('delete')"
215
- @click="onClickDelete(element[valueKey], element[labelKey])"
216
- />
217
- </slot>
218
-
219
- <!--
220
- @slot Use it to add something instead of the edit icon.
221
- @binding {object} item
222
- @binding {string} icon-name
223
- -->
224
- <slot name="edit" :item="element" :icon-name="config.defaults.editIcon">
225
- <UIcon
226
- v-if="!element.isHiddenEdit"
227
- internal
228
- interactive
229
- color="gray"
230
- :name="config.defaults.editIcon"
231
- :tooltip="currentLocale.edit"
232
- v-bind="editIconAttrs"
233
- :data-test="getDataTest('edit')"
234
- @click="onClickEdit(element[valueKey], element[labelKey])"
235
- />
236
- </slot>
237
175
  </template>
238
176
  </div>
239
177
 
240
178
  <UDataList
241
- v-if="nesting && !element.isDisabledNesting"
179
+ v-if="nesting && element.nesting"
242
180
  :nesting="nesting"
243
181
  hide-empty-state-for-nesting
244
182
  :list="element.children"
245
183
  :group="group"
246
184
  v-bind="nestedAttrs"
247
185
  :data-test="getDataTest('table')"
248
- @click-delete="onClickDelete"
249
- @click-edit="onClickEdit"
250
186
  @drag-sort="onDragEnd"
251
187
  >
252
- <template #label="slotProps: { item: DataListItem; active: boolean }">
188
+ <template #label="slotProps: { item: DataListItem; crossed: boolean }">
253
189
  <!--
254
190
  @slot Use it to modify label.
255
191
  @binding {object} item
256
- @binding {boolean} active
192
+ @binding {boolean} crossed
257
193
  -->
258
- <slot name="label" :item="slotProps.item" :active="slotProps.active">
194
+ <slot name="label" :item="slotProps.item" :crossed="slotProps.crossed">
259
195
  <div
260
- v-bind="slotProps.active ? labelAttrs : labelCrossedAttrs"
196
+ v-bind="slotProps.crossed ? labelCrossedAttrs : labelAttrs"
261
197
  v-text="slotProps.item[labelKey]"
262
198
  />
263
199
  </slot>
@@ -29,6 +29,7 @@ export default /*tw*/ {
29
29
  },
30
30
  },
31
31
  },
32
+ drag: "icon-drag cursor-move",
32
33
  dragIcon: "{UIcon} {>dataListIcon} icon-drag cursor-move opacity-100",
33
34
  label: {
34
35
  base: "font-normal flex-auto pt-px",
@@ -41,18 +42,11 @@ export default /*tw*/ {
41
42
  },
42
43
  },
43
44
  labelCrossed: "{>label} line-through",
44
- customActions: `
45
- space-x-5 opacity-50 md:flex md:items-center md:opacity-0
46
- group-hover/item:md:block group-hover/item:opacity-100
47
- `,
48
- deleteIcon: "{UIcon} {>dataListIcon} hidden md:block md:opacity-0 group-hover/item:md:opacity-100",
49
- editIcon: "{UIcon} {>dataListIcon} fill-gray-500 opacity-50",
45
+ customActions: "space-x-5 opacity-50 flex items-center md:opacity-0 group-hover/item:opacity-100",
50
46
  divider: "{UDivider}",
51
47
  empty: "{UEmpty}",
52
48
  nested: "{UDataList} group/nested ml-6",
53
49
  i18n: {
54
- edit: "Edit",
55
- delete: "Delete",
56
50
  emptyTitle: "",
57
51
  emptyDescription: "There is no data in the list.",
58
52
  },
@@ -64,7 +58,5 @@ export default /*tw*/ {
64
58
  nesting: false,
65
59
  /* icons */
66
60
  dragIcon: "drag_indicator",
67
- deleteIcon: "delete",
68
- editIcon: "edit_note",
69
61
  },
70
62
  };
@@ -1,4 +1,4 @@
1
- import { Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/blocks";
1
+ import { Markdown, Meta, Title, Subtitle, Description, Primary, Controls, Stories, Source } from "@storybook/blocks";
2
2
  import { getSource } from "../../utils/storybook.ts";
3
3
 
4
4
  import * as stories from "./stories.ts";
@@ -13,28 +13,20 @@ import defaultConfig from "../config.ts?raw"
13
13
  <Stories of={stories} />
14
14
 
15
15
  ## Configuring items in a list
16
- You can configure any item in a list by passing the following params.
16
+ You can configure any item in a `list` array by passing the following params:
17
17
 
18
- <Source code={`
19
- <UDataList :list="list" />
20
-
21
- const list = [
22
- {
23
- id: 1, // unique item identifier
24
- label: "Rent", // item label
25
- children: [ // nested items
26
- { label: "Office", id: 11 },
27
- { label: "Shops", id: 12, children: [...] }, // children of subitems
28
- ],
29
- isHiddenEdit: true, // hide edit button
30
- isHiddenDelete: true, // hide delete button
31
- isHiddenActions: true, // hide default and custom action buttons
32
- isHiddenCustomActions: true, // hide only custom action buttons
33
- isDisabledNesting: true, // disable nesting for the item
34
- }
35
- {...}
36
- ];
37
- `} language="jsx" dark />
18
+ <Markdown>
19
+ {`
20
+ | Key name | Description | Type | Default |
21
+ | ----------------------| ------------------------------------------------------ | ---------------| --------|
22
+ | id | Unique item identifier | String, Number | |
23
+ | label | Item label | String | |
24
+ | children | Nested items | Array | |
25
+ | crossed | Controls item style (line-through decoration) | Boolean | false |
26
+ | actions | Show custom actions buttons | Boolean | true |
27
+ | nesting | Enable nesting for the item | Boolean | false |
28
+ `}
29
+ </Markdown>
38
30
 
39
31
  ## Default config
40
32
  <Source code={getSource(defaultConfig)} language="jsx" dark />
@@ -1,3 +1,4 @@
1
+ import { ref } from "vue";
1
2
  import {
2
3
  getArgTypes,
3
4
  getSlotNames,
@@ -9,12 +10,18 @@ import UDataList from "../../ui.data-list/UDataList.vue";
9
10
  import UIcon from "../../ui.image-icon/UIcon.vue";
10
11
  import UButton from "../../ui.button/UButton.vue";
11
12
  import URow from "../../ui.container-row/URow.vue";
13
+ import UBadge from "../../ui.text-badge/UBadge.vue";
14
+ import UAvatar from "../../ui.image-avatar/UAvatar.vue";
15
+ import UHeader from "../../ui.text-header/UHeader.vue";
16
+ import ULoader from "../../ui.loader/ULoader.vue";
17
+ import tooltip from "../../directives/tooltip/vTooltip.ts";
12
18
 
13
19
  import type { Meta, StoryFn } from "@storybook/vue3";
14
- import type { Props } from "../types.ts";
20
+ import type { Props, DataListItem } from "../types.ts";
15
21
 
16
22
  interface UDataListArgs extends Props {
17
23
  slotTemplate?: string;
24
+ enum: "size";
18
25
  }
19
26
 
20
27
  export default {
@@ -24,25 +31,34 @@ export default {
24
31
  args: {
25
32
  list: [
26
33
  {
27
- label: "Salary",
34
+ label: "Expenses",
28
35
  id: 1,
36
+ nesting: true,
29
37
  children: [
30
- { label: "IT", id: 1.1 },
31
- { label: "HR", id: 1.2 },
32
- { label: "C Level", id: 1.3 },
38
+ { label: "Office Supplies", id: 1.1 },
39
+ { label: "Travel & Lodging", id: 1.2 },
40
+ { label: "Utilities", id: 1.3 },
33
41
  ],
34
42
  },
35
43
  {
36
- label: "Rent",
44
+ label: "Revenue Streams",
37
45
  id: 2,
46
+ nesting: true,
38
47
  children: [
39
- { label: "Office", id: 2.1 },
40
- { label: "Shops", id: 2.2 },
48
+ { label: "Product Sales", id: 2.1 },
49
+ { label: "Subscription Services", id: 2.2 },
50
+ { label: "Consulting", id: 2.3 },
41
51
  ],
42
52
  },
43
53
  {
44
- label: "Marketing",
54
+ label: "Departments",
45
55
  id: 3,
56
+ nesting: true,
57
+ children: [
58
+ { label: "Engineering", id: 3.1 },
59
+ { label: "Marketing", id: 3.2 },
60
+ { label: "Finance", id: 3.3 },
61
+ ],
46
62
  },
47
63
  ],
48
64
  },
@@ -57,76 +73,143 @@ export default {
57
73
  } as Meta;
58
74
 
59
75
  const DefaultTemplate: StoryFn<UDataListArgs> = (args: UDataListArgs) => ({
60
- components: { UDataList, UIcon, URow, UButton },
76
+ components: { UDataList, UIcon, URow, UButton, UBadge, UAvatar, ULoader, UHeader },
77
+ directives: { tooltip },
61
78
  setup() {
62
79
  const slots = getSlotNames(UDataList.__name);
63
80
 
64
- return { args, slots };
81
+ const avatars = [
82
+ "https://cdn-icons-png.flaticon.com/128/1999/1999625.png",
83
+ "https://cdn-icons-png.flaticon.com/128/4140/4140057.png",
84
+ "https://cdn-icons-png.flaticon.com/128/4140/4140047.png",
85
+ ];
86
+
87
+ const list = ref(
88
+ args.list?.map((item, index) => ({
89
+ ...item,
90
+ id: item.id,
91
+ avatar: avatars[index % avatars.length],
92
+ })),
93
+ );
94
+
95
+ function removeItem(targetItem: DataListItem) {
96
+ list.value = list.value?.filter((listItem) => listItem.id !== targetItem.id);
97
+
98
+ return alert(`Removed item: ${JSON.stringify(targetItem, null, 2)}`);
99
+ }
100
+
101
+ function editItem(targetItem: DataListItem) {
102
+ alert(`Edit item: ${JSON.stringify(targetItem, null, 2)}`);
103
+ }
104
+
105
+ return { args, slots, removeItem, editItem, list };
65
106
  },
66
107
  template: `
67
- <UDataList v-bind="args">
108
+ <UDataList v-bind="args" :list="args.slotTemplate ? list : args.list">
68
109
  ${args.slotTemplate || getSlotsFragment("")}
69
110
  </UDataList>
70
111
  `,
71
112
  });
72
113
 
114
+ const EnumVariantTemplate: StoryFn<UDataListArgs> = (args: UDataListArgs, { argTypes }) => ({
115
+ components: { URow, UDataList, UHeader },
116
+ setup() {
117
+ return {
118
+ args,
119
+ options: argTypes?.[args.enum]?.options,
120
+ };
121
+ },
122
+ template: `
123
+ <div v-for="(option, index) in options" :key="index">
124
+ <UHeader :label="option" size="xs" />
125
+ <UDataList v-bind="args" :[args.enum]="option" class="mb-4" />
126
+ </div>
127
+ `,
128
+ });
129
+
73
130
  export const Default = DefaultTemplate.bind({});
74
131
  Default.args = {};
75
132
 
76
133
  export const EmptyState = DefaultTemplate.bind({});
77
- EmptyState.args = {
78
- list: [],
79
- emptyTitle: "The list is empty.",
80
- emptyDescription: "There is no data in the list.",
81
- };
134
+ EmptyState.args = { list: [] };
82
135
 
83
136
  export const Nesting = DefaultTemplate.bind({});
84
137
  Nesting.args = { nesting: true };
85
138
 
139
+ export const Size = EnumVariantTemplate.bind({});
140
+ Size.args = { enum: "size" };
141
+
86
142
  export const SlotLabel = DefaultTemplate.bind({});
87
143
  SlotLabel.args = {
88
144
  slotTemplate: `
89
145
  <template #label="{ item }">
90
- <URow gap="xs" align="center">
91
- {{ item.label }}
92
- <UIcon name="check" color="green" size="sm" />
93
- </URow>
146
+ <UBadge :label="item.label" />
94
147
  </template>
95
148
  `,
96
149
  };
97
150
 
98
- export const SlotActions = DefaultTemplate.bind({});
99
- SlotActions.args = {
151
+ export const SlotEmpty = DefaultTemplate.bind({});
152
+ SlotEmpty.args = {
153
+ list: [],
154
+ config: {
155
+ wrapper: "flex flex-col items-center justify-center py-10 gap-4",
156
+ i18n: {
157
+ emptyTitle: "Fetching data...",
158
+ emptyDescription: "Please wait until data is received.",
159
+ },
160
+ },
100
161
  slotTemplate: `
101
- <template #actions>
102
- <UIcon interactive name="star" color="red" />
162
+ <template #empty="{ emptyTitle, emptyDescription }">
163
+ <ULoader loading size="lg" />
164
+ <UHeader :label="emptyTitle" size="xs" />
165
+ <p>{{ emptyDescription }}</p>
103
166
  </template>
104
167
  `,
105
168
  };
169
+ SlotEmpty.parameters = {
170
+ docs: {
171
+ description: {
172
+ story:
173
+ "You can customize the `empty` slot's props (`emptyTitle` and `emptyDescription`) using the `i18n` config key.",
174
+ },
175
+ },
176
+ };
106
177
 
107
178
  export const SlotDrag = DefaultTemplate.bind({});
108
179
  SlotDrag.args = {
180
+ list: [
181
+ { label: "John Doe (Engineering)", id: 1 },
182
+ { label: "Michael Johnson (Finance)", id: 2 },
183
+ { label: "Emma Smith (Marketing)", id: 3 },
184
+ ],
109
185
  slotTemplate: `
110
- <template #drag>
111
- <UIcon interactive name="swap_vert" size="sm" />
186
+ <template #drag="{ item }">
187
+ <UAvatar :src="item.avatar" rounded="full" />
112
188
  </template>
113
189
  `,
114
190
  };
115
191
 
116
- export const SlotDelete = DefaultTemplate.bind({});
117
- SlotDelete.args = {
118
- slotTemplate: `
119
- <template #delete>
120
- <UButton label="Delete" size="xs" variant="secondary" color="red" />
121
- </template>
122
- `,
123
- };
124
-
125
- export const SlotEdit = DefaultTemplate.bind({});
126
- SlotEdit.args = {
192
+ export const SlotActions = DefaultTemplate.bind({});
193
+ SlotActions.args = {
127
194
  slotTemplate: `
128
- <template #edit>
129
- <UButton label="Edit" size="xs" variant="secondary" color="grayscale" />
195
+ <template #actions="{ item }">
196
+ <UButton label="Export" size="xs" />
197
+ <UIcon
198
+ name="delete"
199
+ size="sm"
200
+ color="red"
201
+ interactive
202
+ v-tooltip="'Delete'"
203
+ @click="removeItem(item)"
204
+ />
205
+ <UIcon
206
+ name="edit_note"
207
+ size="sm"
208
+ color="grayscale"
209
+ interactive
210
+ v-tooltip="'Edit'"
211
+ @click="editItem(item)"
212
+ />
130
213
  </template>
131
214
  `,
132
215
  };
@@ -12,12 +12,9 @@ export interface DragMoveEvent extends DragEvent {
12
12
  }
13
13
 
14
14
  export interface DataListItem {
15
- isActive?: boolean;
16
- isHiddenActions?: boolean;
17
- isHiddenCustomActions?: boolean;
18
- isHiddenDelete?: boolean;
19
- isHiddenEdit?: boolean;
20
- isDisabledNesting?: boolean;
15
+ crossed?: boolean;
16
+ actions?: boolean;
17
+ nesting?: boolean;
21
18
  children?: DataListItem[];
22
19
  [key: string]: UnknownType | DataListItem[];
23
20
  }
@@ -48,16 +45,6 @@ export interface Props {
48
45
  */
49
46
  valueKey?: string;
50
47
 
51
- /**
52
- * Empty state title.
53
- */
54
- emptyTitle?: string;
55
-
56
- /**
57
- * Empty state description.
58
- */
59
- emptyDescription?: string;
60
-
61
48
  /**
62
49
  * Drag animation duration.
63
50
  */
@@ -475,6 +475,7 @@ const {
475
475
  headerCheckboxAttrs,
476
476
  headerCounterAttrs,
477
477
  bodyEmptyStateAttrs,
478
+ bodyEmptyStateCellAttrs,
478
479
  bodyDateDividerAttrs,
479
480
  bodySelectedDateDividerAttrs,
480
481
  bodyCellDateDividerAttrs,
@@ -544,13 +545,6 @@ const {
544
545
  <template v-else>
545
546
  {{ column.label }}
546
547
  </template>
547
-
548
- <!--
549
- @slot Use it to add something after the needed header cell.
550
- @binding {object} column
551
- @binding {number} index
552
- -->
553
- <slot :name="`header-${column.key}-after`" :column="column" :index="index" />
554
548
  </div>
555
549
 
556
550
  <ULoaderProgress :loading="loading" v-bind="stickyHeaderLoaderAttrs" />
@@ -626,14 +620,13 @@ const {
626
620
  <thead v-bind="headerAttrs" :style="tableRowWidthStyle">
627
621
  <tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs">
628
622
  <!--
629
- @slot Use it to add something before header row.
630
- @binding {number} cols-count
631
- -->
632
- <slot name="before-header" :cols-count="colsCount" />
623
+ @slot Use it to add something before header row.
624
+ @binding {number} cols-count
625
+ @binding {string} classes
626
+ -->
627
+ <slot name="before-header" :cols-count="colsCount" :classes="headerRowAttrs.class" />
633
628
  </tr>
634
629
 
635
- <tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs"></tr>
636
-
637
630
  <tr ref="header-row" v-bind="headerRowAttrs">
638
631
  <th v-if="selectable" v-bind="headerCellCheckboxAttrs">
639
632
  <UCheckbox
@@ -672,13 +665,6 @@ const {
672
665
  <template v-else>
673
666
  {{ column.label }}
674
667
  </template>
675
-
676
- <!--
677
- @slot Use it to add something after the needed header cell.
678
- @binding {object} column
679
- @binding {number} index
680
- -->
681
- <slot :name="`header-${column.key}-after`" :column="column" :index="index" />
682
668
  </th>
683
669
  </tr>
684
670
 
@@ -792,15 +778,23 @@ const {
792
778
  v-if="rowIndex === lastRow && hasSlotContent($slots['after-last-row'])"
793
779
  v-bind="bodyRowAfterAttrs"
794
780
  >
795
- <!-- @slot Use it to add something after last row. -->
796
- <slot name="after-last-row" :cols-count="colsCount" />
781
+ <!--
782
+ @slot Use it to add something after last row.
783
+ @binding {number} cols-count
784
+ @classes {string} classes
785
+ -->
786
+ <slot
787
+ name="after-last-row"
788
+ :cols-count="colsCount"
789
+ :classes="bodyCellBaseAttrs.class"
790
+ />
797
791
  </tr>
798
792
  </template>
799
793
  </tbody>
800
794
 
801
795
  <tbody v-else>
802
796
  <tr>
803
- <td :colspan="colsCount">
797
+ <td :colspan="colsCount" v-bind="bodyEmptyStateCellAttrs">
804
798
  <!-- @slot Use it to add custom empty state. -->
805
799
  <slot name="empty-state">
806
800
  <UEmpty