@v1nt1248/3nclient-lib 0.1.0 → 0.1.2
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/README.md +5 -0
- package/dist/components/index.d.ts +6 -3
- package/dist/components/ui3n-badge-simple.vue.d.ts +13 -13
- package/dist/components/ui3n-badge.vue.d.ts +18 -16
- package/dist/components/ui3n-breadcrumb.vue.d.ts +15 -13
- package/dist/components/ui3n-breadcrumbs.vue.d.ts +17 -15
- package/dist/components/ui3n-button.vue.d.ts +23 -19
- package/dist/components/ui3n-checkbox.vue.d.ts +17 -15
- package/dist/components/ui3n-chip.vue.d.ts +18 -16
- package/dist/components/ui3n-dialog.vue.d.ts +4 -5
- package/dist/components/ui3n-drop-files.vue.d.ts +16 -14
- package/dist/components/ui3n-emoji.vue.d.ts +11 -11
- package/dist/components/ui3n-icon.vue.d.ts +12 -12
- package/dist/components/ui3n-input.vue.d.ts +12 -5
- package/dist/components/ui3n-list.vue.d.ts +12 -39
- package/dist/components/ui3n-menu.vue.d.ts +14 -12
- package/dist/components/ui3n-notification.vue.d.ts +14 -15
- package/dist/components/ui3n-progress-circular.vue.d.ts +43 -0
- package/dist/components/ui3n-progress-linear.vue.d.ts +42 -0
- package/dist/components/ui3n-step-line-bar.vue.d.ts +30 -0
- package/dist/components/ui3n-switch.vue.d.ts +15 -13
- package/dist/components/ui3n-table/composables/useTable.d.ts +26 -0
- package/dist/components/ui3n-table/types.d.ts +65 -0
- package/dist/components/ui3n-table/ui3n-table.vue.d.ts +22 -0
- package/dist/components/ui3n-table-sort-icon.vue.d.ts +2 -2
- package/dist/components/ui3n-tabs.vue.d.ts +18 -16
- package/dist/components/ui3n-text.vue.d.ts +13 -13
- package/dist/components/ui3n-virtual-scroll.vue.d.ts +6 -28
- package/dist/directives/index.d.ts +0 -1
- package/dist/directives/ui3n-click-outside.d.ts +0 -1
- package/dist/directives/ui3n-html.d.ts +0 -1
- package/dist/index.d.ts +10 -1
- package/dist/plugins/dialogs.d.ts +0 -1
- package/dist/plugins/i18n.d.ts +0 -1
- package/dist/plugins/icons.d.ts +0 -1
- package/dist/plugins/index.d.ts +0 -1
- package/dist/plugins/notifications.d.ts +0 -1
- package/dist/plugins/store-dialogs.d.ts +0 -1
- package/dist/plugins/store-notifications.d.ts +0 -1
- package/dist/plugins/store-vue-bus.d.ts +0 -1
- package/dist/plugins/vue-bus.d.ts +0 -1
- package/dist/style.css +1 -1
- package/dist/tools/sqlite-on-3nstorage/index.d.ts +0 -1
- package/dist/tools/ui.helpers.d.ts +0 -1
- package/dist/ui-3n-lib.js +6588 -6551
- package/package.json +31 -29
- package/src/components/index.ts +46 -4
- package/src/components/ui3n-button.vue +6 -0
- package/src/components/ui3n-checkbox.vue +1 -1
- package/src/components/ui3n-dialog.vue +6 -1
- package/src/components/ui3n-input.vue +61 -11
- package/src/components/ui3n-progress-circular.vue +188 -0
- package/src/components/ui3n-progress-linear.vue +119 -0
- package/src/components/ui3n-step-line-bar.vue +66 -0
- package/src/components/ui3n-table/composables/useTable.ts +206 -0
- package/src/components/ui3n-table/types.ts +80 -0
- package/src/components/ui3n-table/ui3n-table.vue +319 -0
- package/src/components/{ui3n-table.vue → ui3n-table-simple.vue} +3 -4
- package/src/components/ui3n-table-sort-icon.vue +9 -6
- package/dist/components/ui3n-table.vue.d.ts +0 -36
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
export interface Ui3nStepLineBarProps {
|
|
3
|
+
label: string;
|
|
4
|
+
current: number;
|
|
5
|
+
steps: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
withDefaults(
|
|
9
|
+
defineProps<Ui3nStepLineBarProps>(),
|
|
10
|
+
{
|
|
11
|
+
current: 1,
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div :class="$style.stepLineBar">
|
|
18
|
+
<div :class="$style.label">
|
|
19
|
+
{{ label }}
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div :class="$style.body">
|
|
23
|
+
<div v-for="step in steps" :class="[$style.step, step <= current && $style.active]" />
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style lang="scss" module>
|
|
29
|
+
.stepLineBar {
|
|
30
|
+
--ui3n-step-line-bar-steps: v-bind(steps);
|
|
31
|
+
|
|
32
|
+
position: relative;
|
|
33
|
+
width: 100%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.label {
|
|
37
|
+
position: relative;
|
|
38
|
+
width: 100%;
|
|
39
|
+
text-align: center;
|
|
40
|
+
font-size: var(--font-14);
|
|
41
|
+
font-weight: 500;
|
|
42
|
+
line-height: var(--font-20);
|
|
43
|
+
color: var(--color-text-block-accent-default);
|
|
44
|
+
margin-bottom: var(--spacing-m);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.body {
|
|
48
|
+
display: flex;
|
|
49
|
+
width: 100%;
|
|
50
|
+
justify-content: space-between;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: var(--spacing-s);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.step {
|
|
56
|
+
position: relative;
|
|
57
|
+
width: calc((100% - (calc(var(--ui3n-step-line-bar-steps) - 1) * var(--spacing-s))) / var(--ui3n-step-line-bar-steps));
|
|
58
|
+
height: var(--spacing-xs);
|
|
59
|
+
border-radius: var(--spacing-xs);
|
|
60
|
+
background-color: var(--color-bg-control-secondary-default);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.active {
|
|
64
|
+
background-color: var(--color-bg-control-accent-default);
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { computed, onMounted, type Ref, ref, watch, UnwrapRef } from 'vue';
|
|
2
|
+
import { get, isEmpty, size } from 'lodash';
|
|
3
|
+
import {
|
|
4
|
+
Ui3nTableBodyBaseItem,
|
|
5
|
+
Ui3nTableConfig,
|
|
6
|
+
Ui3nTableEmits,
|
|
7
|
+
Ui3nTableProps,
|
|
8
|
+
Ui3nTableSort,
|
|
9
|
+
} from '@/components/ui3n-table/types';
|
|
10
|
+
import type { Ui3nCheckboxValue } from '../../ui3n-checkbox.vue';
|
|
11
|
+
|
|
12
|
+
export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<T>, emits: Ui3nTableEmits<T>) {
|
|
13
|
+
const tableEl = ref<HTMLDivElement | null>(null);
|
|
14
|
+
const currentConfig = ref<Pick<Ui3nTableConfig<T>, 'sortOrder' | 'fieldAsRowKey'>>({
|
|
15
|
+
sortOrder: setInitialSortOrder(),
|
|
16
|
+
fieldAsRowKey: setFieldAsRowKey(),
|
|
17
|
+
});
|
|
18
|
+
const selectedRows: Ref<Set<T> | Array<T[keyof T]>> = !!currentConfig.value?.fieldAsRowKey
|
|
19
|
+
? ref([])
|
|
20
|
+
: (ref(new Set()) as Ref<Set<T>>);
|
|
21
|
+
const hasGroupActionsRow = ref(false);
|
|
22
|
+
|
|
23
|
+
const isRowKeyUsed = computed(() => !!currentConfig.value.fieldAsRowKey);
|
|
24
|
+
const visibleColumns = computed(() => props.head.filter(h => !h.hidden));
|
|
25
|
+
const selectedRowsArray = computed(() =>
|
|
26
|
+
isRowKeyUsed.value
|
|
27
|
+
? props.body.content.filter(row =>
|
|
28
|
+
(selectedRows.value as Array<T[keyof T]>).includes(row[currentConfig.value.fieldAsRowKey as keyof T]),
|
|
29
|
+
)
|
|
30
|
+
: Array.from(selectedRows.value as Set<T>),
|
|
31
|
+
);
|
|
32
|
+
const selectedRowsSize = computed(() =>
|
|
33
|
+
isRowKeyUsed.value ? size(selectedRows.value) : (selectedRows.value as Set<T>).size,
|
|
34
|
+
);
|
|
35
|
+
const showGroupActionsRow = computed(() => props.config?.selectable === 'multiple' && hasGroupActionsRow.value);
|
|
36
|
+
|
|
37
|
+
onMounted(() => {
|
|
38
|
+
tableEl.value && setColumnWidth(tableEl.value);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function setColumnWidth(el: HTMLDivElement) {
|
|
42
|
+
const { config = {}, head } = props;
|
|
43
|
+
const { columnStyle } = config;
|
|
44
|
+
const keys = head.map(h => h.key);
|
|
45
|
+
const columnsWidthValue = keys.reduce((res, key, index) => {
|
|
46
|
+
const width = get(columnStyle, [key, 'width'], '1fr');
|
|
47
|
+
res = index === 0 ? `${width}` : `${res} ${width}`;
|
|
48
|
+
return res;
|
|
49
|
+
}, '');
|
|
50
|
+
el && el.style.setProperty('--ui3n-table-columns-width', columnsWidthValue);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function setFieldAsRowKey() {
|
|
54
|
+
const { config = {}, body } = props;
|
|
55
|
+
const { fieldAsRowKey } = config;
|
|
56
|
+
if (fieldAsRowKey && fieldAsRowKey in get(body, ['content', 0], {})) return fieldAsRowKey;
|
|
57
|
+
|
|
58
|
+
if ('id' in get(body, ['content', 0], {})) return 'id';
|
|
59
|
+
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function setInitialSortOrder(): Ui3nTableSort<T> {
|
|
64
|
+
const { config = {}, head } = props;
|
|
65
|
+
const { sortOrder } = config;
|
|
66
|
+
if (!isEmpty(sortOrder)) {
|
|
67
|
+
return sortOrder;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const h of head) {
|
|
71
|
+
const { key, sortable } = h;
|
|
72
|
+
if (sortable) {
|
|
73
|
+
return {
|
|
74
|
+
field: key,
|
|
75
|
+
direction: 'desc',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {} as Ui3nTableSort<T>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getRowKey(row: T, index: number) {
|
|
84
|
+
return isRowKeyUsed.value ? (row[currentConfig.value.fieldAsRowKey as keyof T] as string | number) : index;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isRowSelected(row: T): boolean {
|
|
88
|
+
if (isRowKeyUsed.value) {
|
|
89
|
+
return !!(selectedRows.value as Array<T[keyof T]>).find(
|
|
90
|
+
keyValue => keyValue === row[currentConfig.value.fieldAsRowKey as keyof T],
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (selectedRows.value as Set<T>).has(row);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getRowStyle(row: T): Record<string, string> {
|
|
98
|
+
if (isEmpty(props.body.rowsStyle) || !isRowKeyUsed.value) return {};
|
|
99
|
+
|
|
100
|
+
const rowKey = row[currentConfig.value.fieldAsRowKey as keyof T] as string | number;
|
|
101
|
+
return get(props, ['body', 'rowsStyle', rowKey], {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function closeGroupActionsRow() {
|
|
105
|
+
hasGroupActionsRow.value = false;
|
|
106
|
+
toggleSelectedRows(false);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function selectInSingleMode(row: T) {
|
|
110
|
+
if (isRowSelected(row)) {
|
|
111
|
+
isRowKeyUsed.value && ((selectedRows.value as Array<T[keyof T]>) = []);
|
|
112
|
+
!isRowKeyUsed.value && (selectedRows.value as Set<T>).clear();
|
|
113
|
+
} else {
|
|
114
|
+
if (isRowKeyUsed.value) {
|
|
115
|
+
(selectedRows.value as Array<T[keyof T]>) = [row[currentConfig.value?.fieldAsRowKey as keyof T]];
|
|
116
|
+
} else {
|
|
117
|
+
(selectedRows.value as Set<T>).clear();
|
|
118
|
+
(selectedRows.value as Set<T>).add(row);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function selectInMultipleMode(row: T) {
|
|
124
|
+
if (isRowSelected(row)) {
|
|
125
|
+
if (!isRowKeyUsed.value) {
|
|
126
|
+
(selectedRows.value as Set<T>).delete(row);
|
|
127
|
+
} else {
|
|
128
|
+
const index = (selectedRows.value as Array<T[keyof T]>).findIndex(
|
|
129
|
+
keyValue => keyValue === row[currentConfig.value.fieldAsRowKey as keyof T],
|
|
130
|
+
);
|
|
131
|
+
(selectedRows.value as Array<T[keyof T]>).splice(index, 1);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
isRowKeyUsed.value &&
|
|
135
|
+
(selectedRows.value as Array<T[keyof T]>).push(row[currentConfig.value.fieldAsRowKey as keyof T]);
|
|
136
|
+
!isRowKeyUsed.value && (selectedRows.value as Set<T>).add(row);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function processSelection(row: T, withoutEvents?: boolean) {
|
|
141
|
+
const { selectable } = props.config;
|
|
142
|
+
|
|
143
|
+
if (selectable === 'single') {
|
|
144
|
+
selectInSingleMode(row);
|
|
145
|
+
} else {
|
|
146
|
+
selectInMultipleMode(row);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
!withoutEvents && emits('select:row', selectedRowsArray.value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function toggleSelectedRows(val: Ui3nCheckboxValue) {
|
|
153
|
+
if (props.config.selectable !== 'multiple') return;
|
|
154
|
+
|
|
155
|
+
if (!!val) {
|
|
156
|
+
for (const item of props.body.content) {
|
|
157
|
+
if (!isRowSelected(item)) {
|
|
158
|
+
processSelection(item, true);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
isRowKeyUsed.value && ((selectedRows.value as Array<T[keyof T]>) = []);
|
|
163
|
+
!isRowKeyUsed.value && (selectedRows.value as Set<T>).clear();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
emits('select:row', selectedRowsArray.value);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function changeSortOrder(field: keyof T) {
|
|
170
|
+
if (currentConfig.value.sortOrder?.field === field) {
|
|
171
|
+
currentConfig.value.sortOrder.direction = currentConfig.value.sortOrder?.direction === 'asc' ? 'desc' : 'asc';
|
|
172
|
+
} else {
|
|
173
|
+
currentConfig.value.sortOrder!.field = field as UnwrapRef<keyof T>;
|
|
174
|
+
currentConfig.value.sortOrder!.direction = 'desc';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
currentConfig.value.sortOrder?.field && emits('change:sort', currentConfig.value.sortOrder as Ui3nTableSort<T>);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
watch(
|
|
181
|
+
() => selectedRowsSize.value,
|
|
182
|
+
(val, oldVal) => {
|
|
183
|
+
if (val && val > oldVal && props.config.selectable === 'multiple' && !showGroupActionsRow.value) {
|
|
184
|
+
hasGroupActionsRow.value = true;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
tableEl,
|
|
191
|
+
currentConfig,
|
|
192
|
+
visibleColumns,
|
|
193
|
+
hasGroupActionsRow,
|
|
194
|
+
showGroupActionsRow,
|
|
195
|
+
selectedRows,
|
|
196
|
+
selectedRowsArray,
|
|
197
|
+
selectedRowsSize,
|
|
198
|
+
closeGroupActionsRow,
|
|
199
|
+
getRowKey,
|
|
200
|
+
isRowSelected,
|
|
201
|
+
getRowStyle,
|
|
202
|
+
processSelection,
|
|
203
|
+
toggleSelectedRows,
|
|
204
|
+
changeSortOrder,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { VNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
export interface Ui3nTableBodyBaseItem {
|
|
4
|
+
id?: string | number | symbol;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type Ui3nTableSort<T extends Ui3nTableBodyBaseItem> = {
|
|
8
|
+
field: keyof T;
|
|
9
|
+
direction: 'asc' | 'desc';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Ui3nTableConfig<T extends Ui3nTableBodyBaseItem, K extends keyof T = keyof T> = {
|
|
13
|
+
tableName?: string;
|
|
14
|
+
sortOrder?: Ui3nTableSort<T>;
|
|
15
|
+
selectable?: 'single' | 'multiple';
|
|
16
|
+
// draggebleRows?: boolean; // ToDo this will be done in the next version
|
|
17
|
+
// draggebleColumns?: boolean; // ToDo this will be done in the next version
|
|
18
|
+
columnStyle?: { [P in Omit<K, 'id'> as string | number]: Record<string, string> };
|
|
19
|
+
fieldAsRowKey?: keyof T;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type Ui3nTableHeadProps<T extends Ui3nTableBodyBaseItem> = {
|
|
23
|
+
key: keyof T;
|
|
24
|
+
text: string;
|
|
25
|
+
sortable?: boolean;
|
|
26
|
+
headCellStyle?: Record<string, string>;
|
|
27
|
+
format?: (content: unknown) => string;
|
|
28
|
+
hidden?: boolean;
|
|
29
|
+
index?: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type Ui3nTableBodyProps<T extends Ui3nTableBodyBaseItem, K extends keyof T = keyof T> = {
|
|
33
|
+
content: T[];
|
|
34
|
+
rowsStyle?: { [P in T[K] as string | number]: Record<string, string> };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface Ui3nTableProps<T extends Ui3nTableBodyBaseItem> {
|
|
38
|
+
config: Ui3nTableConfig<T>;
|
|
39
|
+
head: Ui3nTableHeadProps<T>[];
|
|
40
|
+
body: Ui3nTableBodyProps<T>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Ui3nTableEmits<T extends Ui3nTableBodyBaseItem> {
|
|
44
|
+
(ev: 'change:sort', val: Ui3nTableSort<T>): void;
|
|
45
|
+
(ev: 'select:row', val: T[]): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type Ui3nTableGroupActionsSlot = {
|
|
49
|
+
'group-actions': () => VNode;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type Ui3nTableHeaderCellSlot<T extends Ui3nTableBodyBaseItem, K extends string & keyof T> = {
|
|
53
|
+
[p in `header-cell-${K}`]: () => VNode;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type Ui3nTableBodyRowSlotScope<T extends Ui3nTableBodyBaseItem> = {
|
|
57
|
+
row: T;
|
|
58
|
+
rowStyle?: Record<string, string>;
|
|
59
|
+
rowIndex: number;
|
|
60
|
+
events?: Record<string, (value: unknown) => unknown>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type Ui3nTableBodyRowSlot<T extends Ui3nTableBodyBaseItem> = {
|
|
64
|
+
'body-row': (scope: Ui3nTableBodyRowSlotScope<T>) => VNode;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type Ui3nTableBodyRowCellSlotScope<T extends Ui3nTableBodyBaseItem, K extends string & keyof T> = {
|
|
68
|
+
row: T;
|
|
69
|
+
rowIndex: number;
|
|
70
|
+
cell: T[K];
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type Ui3nTableBodyRowCellSlot<T extends Ui3nTableBodyBaseItem, K extends string & keyof T> = {
|
|
74
|
+
[p in `body-row-cell-${K}`]: (scope: Ui3nTableBodyRowCellSlotScope<T, K>) => VNode;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type Ui3nTableSlots<T extends Ui3nTableBodyBaseItem, K extends string & keyof T> = Ui3nTableGroupActionsSlot &
|
|
78
|
+
Ui3nTableHeaderCellSlot<T, K> &
|
|
79
|
+
Ui3nTableBodyRowSlot<T> &
|
|
80
|
+
Ui3nTableBodyRowCellSlot<T, K>;
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<script setup lang="ts" generic="T extends Ui3nTableBodyBaseItem">
|
|
2
|
+
import { useTable } from './composables/useTable';
|
|
3
|
+
import {
|
|
4
|
+
Ui3nTableBodyBaseItem,
|
|
5
|
+
Ui3nTableEmits,
|
|
6
|
+
Ui3nTableProps,
|
|
7
|
+
Ui3nTableSlots,
|
|
8
|
+
} from './types';
|
|
9
|
+
import Ui3nButton from '../ui3n-button.vue';
|
|
10
|
+
import Ui3nCheckbox from '../ui3n-checkbox.vue';
|
|
11
|
+
import Ui3nTableSortIcon from '../ui3n-table-sort-icon.vue';
|
|
12
|
+
|
|
13
|
+
const props = defineProps<Ui3nTableProps<T>>();
|
|
14
|
+
const emits = defineEmits<Ui3nTableEmits<T>>();
|
|
15
|
+
defineSlots<Ui3nTableSlots<T, string & keyof T>>();
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
tableEl,
|
|
19
|
+
currentConfig,
|
|
20
|
+
visibleColumns,
|
|
21
|
+
showGroupActionsRow,
|
|
22
|
+
selectedRowsArray,
|
|
23
|
+
selectedRowsSize,
|
|
24
|
+
closeGroupActionsRow,
|
|
25
|
+
getRowKey,
|
|
26
|
+
isRowSelected,
|
|
27
|
+
getRowStyle,
|
|
28
|
+
processSelection,
|
|
29
|
+
toggleSelectedRows,
|
|
30
|
+
changeSortOrder,
|
|
31
|
+
} = useTable(props, emits);
|
|
32
|
+
|
|
33
|
+
const eventsHandlers = {
|
|
34
|
+
select: processSelection,
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div ref="tableEl" :class="$style.table">
|
|
40
|
+
<div :class="[$style.header, showGroupActionsRow && $style.withGroupActions]">
|
|
41
|
+
<!-- group actions -->
|
|
42
|
+
<div v-if="showGroupActionsRow" :class="$style.groupActions">
|
|
43
|
+
<ui3n-checkbox
|
|
44
|
+
:indeterminate="selectedRowsSize < body.content.length && selectedRowsSize !== 0"
|
|
45
|
+
:model-value="selectedRowsSize === body.content.length"
|
|
46
|
+
@change="toggleSelectedRows"
|
|
47
|
+
>
|
|
48
|
+
Selected: {{ selectedRowsSize }}
|
|
49
|
+
</ui3n-checkbox>
|
|
50
|
+
|
|
51
|
+
<div :class="$style.groupActionsBody">
|
|
52
|
+
<slot name="group-actions" :selected-rows="selectedRowsArray" />
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<ui3n-button
|
|
56
|
+
type="secondary"
|
|
57
|
+
@click="closeGroupActionsRow"
|
|
58
|
+
>
|
|
59
|
+
Cancel
|
|
60
|
+
</ui3n-button>
|
|
61
|
+
</div>
|
|
62
|
+
<!-- table header -->
|
|
63
|
+
<template
|
|
64
|
+
v-for="h in visibleColumns"
|
|
65
|
+
:key="h.key"
|
|
66
|
+
>
|
|
67
|
+
<div
|
|
68
|
+
:class="[
|
|
69
|
+
$style.headerItemWrapper,
|
|
70
|
+
h.sortable && $style.sortable,
|
|
71
|
+
h.sortable && currentConfig.sortOrder?.field === h.key && $style.sortableActive,
|
|
72
|
+
]"
|
|
73
|
+
:style="h.headCellStyle"
|
|
74
|
+
>
|
|
75
|
+
<div
|
|
76
|
+
:class="$style.headerItem"
|
|
77
|
+
:style="h.headCellStyle"
|
|
78
|
+
v-on="h.sortable ? { click: () => changeSortOrder(h.key) }: {}"
|
|
79
|
+
>
|
|
80
|
+
<slot :name="`header-cell-${h.key as string & keyof T}`">
|
|
81
|
+
<span :class="$style.headerText">
|
|
82
|
+
{{ h.text }}
|
|
83
|
+
</span>
|
|
84
|
+
</slot>
|
|
85
|
+
<ui3n-table-sort-icon
|
|
86
|
+
v-if="h.sortable && currentConfig.sortOrder?.field === h.key"
|
|
87
|
+
:value="currentConfig.sortOrder?.direction"
|
|
88
|
+
size="16"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<!-- table body -->
|
|
96
|
+
<div :class="$style.body">
|
|
97
|
+
<template v-if="$slots['row']">
|
|
98
|
+
<div
|
|
99
|
+
v-for="(row, rowIndex) in body.content"
|
|
100
|
+
:class="[
|
|
101
|
+
$style.row,
|
|
102
|
+
isRowSelected(row) && $style.selected,
|
|
103
|
+
config.selectable && $style.selectable,
|
|
104
|
+
]"
|
|
105
|
+
:key="getRowKey(row, rowIndex)"
|
|
106
|
+
>
|
|
107
|
+
<slot
|
|
108
|
+
name="body-row"
|
|
109
|
+
:row="row"
|
|
110
|
+
:row-style="getRowStyle(row)"
|
|
111
|
+
:row-index="rowIndex"
|
|
112
|
+
:events="eventsHandlers"
|
|
113
|
+
/>
|
|
114
|
+
</div>
|
|
115
|
+
</template>
|
|
116
|
+
|
|
117
|
+
<template v-else>
|
|
118
|
+
<div
|
|
119
|
+
v-for="(row, rowIndex) in body.content"
|
|
120
|
+
:class="[
|
|
121
|
+
$style.row,
|
|
122
|
+
isRowSelected(row) && $style.selected,
|
|
123
|
+
config.selectable && $style.selectable,
|
|
124
|
+
]"
|
|
125
|
+
:key="getRowKey(row, rowIndex)"
|
|
126
|
+
:style="getRowStyle(row)"
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
v-if="config.selectable"
|
|
130
|
+
:class="$style.rowCheckbox"
|
|
131
|
+
>
|
|
132
|
+
<ui3n-checkbox
|
|
133
|
+
:model-value="isRowSelected(row)"
|
|
134
|
+
@change="processSelection(row)"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<template
|
|
139
|
+
v-for="(col, colIndex) in visibleColumns"
|
|
140
|
+
:key="col.key"
|
|
141
|
+
>
|
|
142
|
+
<div :class="[$style.bodyItem, colIndex === 0 && $style.bodyItemFirst]">
|
|
143
|
+
<slot
|
|
144
|
+
:name="`body-row-cell-${(col.key as string & keyof T)}`"
|
|
145
|
+
:row="row"
|
|
146
|
+
:row-index="rowIndex"
|
|
147
|
+
:cell="row[col.key]"
|
|
148
|
+
>
|
|
149
|
+
<span :class="$style.cell">
|
|
150
|
+
{{ row[col.key] }}
|
|
151
|
+
</span>
|
|
152
|
+
</slot>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|
|
155
|
+
</div>
|
|
156
|
+
</template>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</template>
|
|
160
|
+
|
|
161
|
+
<style lang="scss" module>
|
|
162
|
+
@import "../../assets/styles/mixins.scss";
|
|
163
|
+
|
|
164
|
+
.table {
|
|
165
|
+
--ui3n-table-columns-width: auto;
|
|
166
|
+
--ui3n-table-base-head-height: 36px;
|
|
167
|
+
--ui3n-table-group-actions-height: 48px;
|
|
168
|
+
--ui3n-table-body-row-height: 28px;
|
|
169
|
+
--ui3n-table-group-actions-bg-color: var(--color-bg-block-primary-default);
|
|
170
|
+
|
|
171
|
+
position: relative;
|
|
172
|
+
width: 100%;
|
|
173
|
+
height: 100%;
|
|
174
|
+
background-color: transparent;
|
|
175
|
+
overflow-y: auto;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.header {
|
|
179
|
+
position: sticky;
|
|
180
|
+
top: 0;
|
|
181
|
+
width: 100%;
|
|
182
|
+
min-height: var(--ui3n-table-header-height);
|
|
183
|
+
height: var(--ui3n-table-base-head-height);
|
|
184
|
+
display: grid;
|
|
185
|
+
grid-template-columns: var(--ui3n-table-columns-width);
|
|
186
|
+
background-color: var(--color-bg-table-header-default);
|
|
187
|
+
padding-left: var(--spacing-m);
|
|
188
|
+
z-index: 5;
|
|
189
|
+
|
|
190
|
+
&.withGroupActions {
|
|
191
|
+
height: calc(var(--ui3n-table-base-head-height) + var(--ui3n-table-group-actions-height));
|
|
192
|
+
padding-top: var(--ui3n-table-group-actions-height);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.headerItemWrapper:not(:first-child) {
|
|
196
|
+
padding: 0 var(--spacing-xs);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.headerItemWrapper {
|
|
201
|
+
position: relative;
|
|
202
|
+
display: flex;
|
|
203
|
+
justify-content: flex-start;
|
|
204
|
+
align-items: center;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.headerItem {
|
|
208
|
+
position: relative;
|
|
209
|
+
display: flex;
|
|
210
|
+
justify-content: flex-start;
|
|
211
|
+
align-items: center;
|
|
212
|
+
width: fit-content;
|
|
213
|
+
max-width: 100%;
|
|
214
|
+
user-select: none;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.sortable {
|
|
218
|
+
.headerItem {
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.headerText {
|
|
224
|
+
display: block;
|
|
225
|
+
font-size: var(--font-14);
|
|
226
|
+
font-weight: 500;
|
|
227
|
+
color: var(--color-text-table-primary-default);
|
|
228
|
+
@include text-overflow-ellipsis;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.sortableActive {
|
|
232
|
+
.headerItem {
|
|
233
|
+
gap: var(--spacing-s);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.groupActions {
|
|
238
|
+
position: absolute;
|
|
239
|
+
top: 0;
|
|
240
|
+
left: 0;
|
|
241
|
+
display: flex;
|
|
242
|
+
width: 100%;
|
|
243
|
+
height: var(--ui3n-table-group-actions-height);
|
|
244
|
+
padding: 0 var(--spacing-m);
|
|
245
|
+
justify-content: space-between;
|
|
246
|
+
align-items: center;
|
|
247
|
+
background-color: var(--ui3n-table-group-actions-bg-color);
|
|
248
|
+
border-left: 1px solid var(--color-border-block-primary-default);
|
|
249
|
+
border-top: 1px solid var(--color-border-block-primary-default);
|
|
250
|
+
border-right: 1px solid var(--color-border-block-primary-default);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.groupActionsBody {
|
|
254
|
+
display: flex;
|
|
255
|
+
height: 100%;
|
|
256
|
+
padding: 0 var(--spacing-s);
|
|
257
|
+
flex-grow: 1;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.body {
|
|
261
|
+
position: relative;
|
|
262
|
+
width: 100%;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.row {
|
|
266
|
+
position: relative;
|
|
267
|
+
width: 100%;
|
|
268
|
+
min-height: var(--ui3n-table-body-row-height);
|
|
269
|
+
display: grid;
|
|
270
|
+
grid-template-columns: var(--ui3n-table-columns-width);
|
|
271
|
+
padding-left: var(--spacing-m);
|
|
272
|
+
border-left: 1px solid var(--color-border-table-primary-default);
|
|
273
|
+
border-right: 1px solid var(--color-border-table-primary-default);
|
|
274
|
+
border-bottom: 1px solid var(--color-border-block-primary-default);
|
|
275
|
+
background-color: transparent;
|
|
276
|
+
|
|
277
|
+
&.selectable {
|
|
278
|
+
cursor: pointer;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
&:hover {
|
|
282
|
+
background-color: var(--color-bg-control-primary-hover);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.selected {
|
|
287
|
+
background-color: var(--color-bg-control-primary-hover);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.rowCheckbox {
|
|
291
|
+
position: absolute;
|
|
292
|
+
top: 0;
|
|
293
|
+
left: 16px;
|
|
294
|
+
width: var(--spacing-m);
|
|
295
|
+
height: 100%;
|
|
296
|
+
display: flex;
|
|
297
|
+
justify-content: flex-start;
|
|
298
|
+
align-items: center;
|
|
299
|
+
z-index: 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.bodyItem {
|
|
303
|
+
position: relative;
|
|
304
|
+
height: 100%;
|
|
305
|
+
display: flex;
|
|
306
|
+
justify-content: flex-start;
|
|
307
|
+
align-items: center;
|
|
308
|
+
|
|
309
|
+
&.bodyItemFirst {
|
|
310
|
+
padding-left: var(--spacing-ml);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.cell {
|
|
315
|
+
font-size: var(--font-12);
|
|
316
|
+
font-weight: 400;
|
|
317
|
+
color: var(--color-text-table-primary-default);
|
|
318
|
+
}
|
|
319
|
+
</style>
|
|
@@ -11,7 +11,6 @@ export interface Ui3nTableProps {
|
|
|
11
11
|
|
|
12
12
|
export interface Ui3nTableEmits {
|
|
13
13
|
(ev: 'go', name: string): void;
|
|
14
|
-
|
|
15
14
|
(ev: 'action', value: { action: EntityAction, entity: ListingEntryTypeExtended }): void;
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -268,11 +267,11 @@ const handleActions = (ev: MouseEvent, action: EntityAction, entity: ListingEntr
|
|
|
268
267
|
color: var(--color-icon-control-accent-unselected);
|
|
269
268
|
}
|
|
270
269
|
|
|
271
|
-
.rowBookmark{
|
|
270
|
+
.rowBookmark {
|
|
272
271
|
left: 2px;
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
.rowRename{
|
|
274
|
+
.rowRename {
|
|
276
275
|
right: calc(var(--spacing-l) * 4);
|
|
277
276
|
}
|
|
278
277
|
|
|
@@ -324,6 +323,6 @@ const handleActions = (ev: MouseEvent, action: EntityAction, entity: ListingEntr
|
|
|
324
323
|
line-height: var(--font-20);
|
|
325
324
|
color: var(--color-text-control-secondary-default);
|
|
326
325
|
text-align: center;
|
|
327
|
-
padding: 56px 0
|
|
326
|
+
padding: 56px 0;
|
|
328
327
|
}
|
|
329
328
|
</style>
|