bfg-common 1.1.57 → 1.1.59

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.
@@ -1,1476 +1,1471 @@
1
- <template>
2
- <div class="relative" style="display: flex">
3
- <div
4
- :id="outerWrapper"
5
- :class="[
6
- 'datagrid-outer-wrapper',
7
- {
8
- 'datagrid-outer-wrapper-opened-info': isShowHiddenInfo,
9
- },
10
- ]"
11
- >
12
- <div class="datagrid-inner-wrapper">
13
- <div
14
- :class="[
15
- 'datagrid',
16
- {
17
- 'datagrid-opened-info': isShowHiddenInfo,
18
- },
19
- ]"
20
- >
21
- <div :id="tableWrapperId" class="datagrid-table-wrapper">
22
- <div class="datagrid-table">
23
- <div class="datagrid-header">
24
- <div class="datagrid-row">
25
- <div class="datagrid-row-master datagrid-row-flex">
26
- <div v-if="props.type" class="datagrid-row-sticky">
27
- <div
28
- :id="expandableCaret"
29
- class="datagrid-column datagrid-expandable-caret datagrid-fixed-column flex-align-center flex-justify-center"
30
- >
31
- <template v-if="props.type === 'checkbox'">
32
- <div
33
- class="clr-checkbox-wrapper flex-align-center flex-justify-center w-100"
34
- >
35
- <input
36
- :id="`${props.testId}-${inputId}-all`"
37
- v-model="selectedAll"
38
- type="checkbox"
39
- class="checkbox-btn"
40
- :value="-1"
41
- @change="changeAll"
42
- />
43
- <label
44
- :for="`${props.testId}-${inputId}-all`"
45
- class="clr-control-label"
46
- />
47
- </div>
48
- <div class="datagrid-column-separator"></div>
49
- </template>
50
- </div>
51
- <div
52
- v-if="$slots.toggleBlock"
53
- class="datagrid-toggle-block datagrid-column datagrid-fixed-width"
54
- >
55
- <div class="datagrid-column-flex">
56
- <div class="datagrid-column-separator">
57
- <span class="clr-sr-only" />
58
- <div class="datagrid-column-resize-tracker" />
59
- </div>
60
- </div>
61
- </div>
62
- </div>
63
- <div class="datagrid-row-scrollable">
64
- <div
65
- v-for="(item, key) in headItemsPresent"
66
- :key="key"
67
- :style="{
68
- width: columnsWidth[key][0],
69
- minWidth: columnsWidth[key][1],
70
- }"
71
- class="datagrid-column datagrid-fixed-width"
72
- >
73
- <div class="datagrid-column-flex">
74
- <button
75
- :id="`${props.testId}-sort-column-${key}`"
76
- :class="[
77
- 'datagrid-column-title',
78
- {
79
- 'default text-decoration-none':
80
- !item.sortColumn,
81
- },
82
- ]"
83
- @click="sortTable(item.sortColumn)"
84
- >
85
- <slot name="th" :key="key" :item="item">
86
- <span class="title-column">
87
- {{ item.text }}
88
- </span>
89
- </slot>
90
- <span
91
- v-if="item.sortColumn"
92
- class="sort-arrow-wrap"
93
- >
94
- <atoms-the-icon
95
- v-show="sortInfo[0] === item.sortColumn"
96
- :class="['sort-arrow', { down: sortInfo[1] }]"
97
- name="sort-arrow"
98
- />
99
- </span>
100
- </button>
101
- <div>
102
- <button
103
- v-if="item.hasFilter"
104
- :id="`${props.testId}-filter-icon-${key}`"
105
- class="datagrid-filter-toggle clr-anchor clr-smart-open-close"
106
- @click="filterShow[key] = !filterShow[key]"
107
- >
108
- <atoms-the-icon
109
- v-if="filterTerm[key]"
110
- class="filter-icon"
111
- name="filter-solid"
112
- style="fill: #0079b8"
113
- />
114
- <atoms-the-icon
115
- v-else
116
- class="filter-icon"
117
- name="filter"
118
- />
119
- </button>
120
- <atoms-popup-simple-popup
121
- v-model="filterShow[key]"
122
- :test-id="`${props.testId}-filter`"
123
- top="100%"
124
- right="0"
125
- >
126
- <input
127
- :id="`${props.testId}-filter-input-${key}`"
128
- v-model="filterTerm[key]"
129
- type="text"
130
- @input="filtering"
131
- />
132
- </atoms-popup-simple-popup>
133
- </div>
134
- <div
135
- v-show="
136
- !isShowHiddenInfo ||
137
- (isShowHiddenInfo && key === 0)
138
- "
139
- :class="[
140
- 'datagrid-column-separator',
141
- {
142
- 'double-arrow-separator':
143
- key === 0 && props.withInfo,
144
- },
145
- ]"
146
- @mousedown="setGrab(key, $event)"
147
- >
148
- <button
149
- :id="`${props.testId}-resize-tracker-${key}`"
150
- class="datagrid-column-handle drag-handle draggable"
151
- />
152
- <span class="clr-sr-only" />
153
- <div class="datagrid-column-resize-tracker" />
154
- </div>
155
- </div>
156
- </div>
157
- </div>
158
- </div>
159
- </div>
160
- </div>
161
-
162
- <template v-if="bodyItemsPresent.length">
163
- <div
164
- v-for="(item, key) in bodyItemsPresent"
165
- :key="item.id"
166
- :class="[
167
- 'datagrid-body-row datagrid-row animation',
168
- {
169
- 'datagrid-selected active':
170
- !props.withInfo && checkIsSelectedRow(item[0].id),
171
- 'datagrid-selected-info':
172
- props.withInfo &&
173
- isShowHiddenInfo &&
174
- checkIsSelectedRow(item[0].id),
175
- },
176
- ]"
177
- >
178
- <div>
179
- <div
180
- class="datagrid-row-master datagrid-row-flex"
181
- @click.prevent.stop="selectRowById(item[0].id)"
182
- >
183
- <div
184
- v-if="props.type"
185
- class="datagrid-row-sticky"
186
- @click.stop
187
- >
188
- <div
189
- :id="expandableCaret"
190
- :class="[
191
- 'datagrid-expandable-caret flex-justify-center datagrid-cell',
192
- {
193
- 'datagrid-fixed-column': !props.withInfo,
194
- 'datagrid-fixed-column-without-separator':
195
- props.withInfo,
196
- },
197
- ]"
198
- >
199
- <slot name="type" :key="key" :item="item">
200
- <div
201
- :class="`clr-${props.type}-wrapper flex-justify-center flex-align-center`"
202
- >
203
- <input
204
- :id="`${props.testId}-${inputId}-${item[0].id}`"
205
- v-model="selectedRowLocal"
206
- :type="props.type"
207
- :class="`${props.type}-btn`"
208
- :value="item[0].id"
209
- name="selected-store"
210
- @change="changeSelectedRow($event, item[0].id)"
211
- />
212
- <label
213
- :for="`${props.testId}-${inputId}-${item[0].id}`"
214
- class="clr-control-label"
215
- />
216
- </div>
217
- </slot>
218
- </div>
219
- <div
220
- v-if="$slots.toggleBlock"
221
- class="datagrid-cell datagrid-fixed-width"
222
- >
223
- <div class="flex-align-center">
224
- <button
225
- :id="`${props.testId}-toggle-icon-${key}`"
226
- class="datagrid-expandable-caret-button reset-btn"
227
- @click="toggle(key)"
228
- >
229
- <atoms-the-icon
230
- :class="[
231
- 'datagrid-expandable-caret-icon',
232
- { toggle: toggedItems[key] },
233
- ]"
234
- name="angle"
235
- />
236
- </button>
237
- </div>
238
- </div>
239
- </div>
240
- <div class="datagrid-row-scrollable">
241
- <div class="datagrid-scrolling-cells">
242
- <div
243
- v-for="(item2, key2) in item"
244
- v-show="
245
- !isShowHiddenInfo ||
246
- item2.key === 'col1' ||
247
- item2.key === 'icon'
248
- "
249
- :key="key2"
250
- :style="{
251
- width: columnsWidth[key2][0],
252
- minWidth: columnsWidth[key2][1],
253
- }"
254
- :class="[
255
- 'datagrid-cell datagrid-fixed-width',
256
- {
257
- 'hidden-text-triangle':
258
- isShowHiddenInfo &&
259
- item2.data?.name === 'hidden-text-triangle',
260
- },
261
- ]"
262
- >
263
- <div
264
- :class="[
265
- 'flex-align-center h-100',
266
- {
267
- 'double-arrow-width':
268
- item2.data?.name === 'doubleArrows',
269
- },
270
- ]"
271
- >
272
- <div
273
- v-if="item2.data?.name === 'doubleArrows'"
274
- class="double-arrow-wrap"
275
- :class="{
276
- 'arrow-selected':
277
- isShowHiddenInfo &&
278
- item2.id === selectedRow,
279
- }"
280
- @click.prevent.stop="
281
- doubleArrowClick(item2, item)
282
- "
283
- >
284
- <atoms-the-icon
285
- v-if="item2.data?.name === 'doubleArrows'"
286
- name="doubleArrows"
287
- class="double-arrow"
288
- :class="{
289
- 'arrow-selected':
290
- isShowHiddenInfo &&
291
- item2.id === selectedRow,
292
- }"
293
- />
294
- </div>
295
- <span
296
- v-if="item2.text && item2.data?.iconClassName"
297
- class="vertical-divider"
298
- />
299
- <slot :name="item2.key" :item="item2">
300
- <span class="text-ellipsis" :title="item2.text"
301
- >{{ item2.text }}
302
- </span>
303
- </slot>
304
- </div>
305
- </div>
306
- </div>
307
- <div
308
- v-show="toggedItems[key]"
309
- class="datagrid-row-flex datagrid-row-detail datagrid-container"
310
- >
311
- <slot name="toggleBlock" />
312
- </div>
313
- </div>
314
- </div>
315
- </div>
316
- </div>
317
- </template>
318
- <template v-else>
319
- <div class="datagrid-placeholder-container">
320
- <div class="datagrid-placeholder datagrid-empty">
321
- <div class="datagrid-placeholder-image"></div>
322
-
323
- <span>No items found</span>
324
- </div>
325
- </div>
326
- </template>
327
- </div>
328
- </div>
329
- </div>
330
-
331
- <div
332
- v-if="!hideFooter"
333
- :class="[
334
- 'datagrid-footer',
335
- {
336
- 'datagrid-footer-opened-info': isShowHiddenInfo,
337
- },
338
- ]"
339
- >
340
- <div
341
- v-if="props.type === 'checkbox' && props.selectedRow?.length"
342
- class="clr-form-control-disabled"
343
- >
344
- <div class="clr-checkbox-wrapper">
345
- <input
346
- :id="`${props.testId}-check-all`"
347
- type="checkbox"
348
- checked
349
- />
350
- <label class="clr-control-label">
351
- {{ props.selectedRow?.length }}
352
- </label>
353
- </div>
354
- </div>
355
- <div class="datagrid-footer-description flex-align-center">
356
- <atoms-table-data-grid-column-switch
357
- v-if="columnKeysLocal"
358
- v-model:column-keys="columnKeysLocal"
359
- :test-id="`${props.testId}-footer-description`"
360
- class="switch-icon"
361
- />
362
-
363
- <div v-if="$slots.action" class="datagrid-footer__action">
364
- <slot name="action" />
365
- </div>
366
- </div>
367
-
368
- <atoms-table-data-grid-pagination
369
- test-id="props.testId"
370
- :page-size="props.pageSize"
371
- :page="props.page"
372
- :options="mergeOptions"
373
- :total-items="props.totalItems"
374
- :total-pages="props.totalPages"
375
- :hide-pagination="props.hidePagination"
376
- :hide-page-size="props.hidePageSize"
377
- :show-page-info="props.showPageInfo"
378
- :page-items-count="props.bodyItems.length"
379
- :is-show-hidden-info="isShowHiddenInfo"
380
- @change-page-size="changePageSize"
381
- @change-page="changePage"
382
- />
383
- </div>
384
-
385
- <atoms-loader v-show="props.loading" class="datagrid-spinner" />
386
- </div>
387
- </div>
388
- <!-- {{ showedInfoRow }}-->
389
- <!-- {{ selectedRow }}-->
390
- <!-- {{ isShowHiddenInfo }}-->
391
- <!-- {{-->
392
- <!-- isShowHiddenInfo &&-->
393
- <!-- showedInfoRow &&-->
394
- <!-- showedInfoRow.id === selectedRow &&-->
395
- <!-- !showedInfoRow.data?.text-->
396
- <!-- }}-->
397
- <div
398
- v-if="isShowInfo"
399
- id="hidden-features"
400
- :class="[
401
- 'hidden-features',
402
- {
403
- 'show-hidden-info': isShowHiddenInfo,
404
- },
405
- ]"
406
- >
407
- <div class="header">
408
- <slot name="hiddenInfoHeader" :item="showedInfoCol" />
409
- <button
410
- :id="`${props.testId}-hidden-info-toggle-icon-${showedInfoRow?.key}-${showedInfoRow?.data.name}`"
411
- class="signpost-action close"
412
- @click="isShowHiddenInfo = false"
413
- >
414
- <atoms-the-icon class="close-icon" name="close" />
415
- </button>
416
- </div>
417
- <slot name="hiddenInfoBody" />
418
- </div>
419
- </div>
420
- </template>
421
-
422
- <script setup lang="ts">
423
- import { UI_I_HTMLSelectElement } from '~/models/interfaces'
424
- import { itemsPerPage } from '~/components/atoms/table/dataGrid/config/itemsPerPage'
425
- import {
426
- UI_I_HeadItem,
427
- UI_I_BodyItem,
428
- UI_I_ColumnKey,
429
- } from '~/components/atoms/table/dataGrid/models/interfaces'
430
- import { UI_I_OptionItem } from '~/components/atoms/models/interfaces'
431
- import {
432
- UI_T_ColumnKeys,
433
- UI_T_DataGridType,
434
- UI_T_SelectedRow,
435
- } from '~/components/atoms/table/dataGrid/models/types'
436
-
437
- const props = withDefaults(
438
- defineProps<{
439
- headItems: UI_I_HeadItem[]
440
- bodyItems: UI_I_BodyItem[][]
441
- pageSize: number
442
- page: number
443
- totalItems: number
444
- totalPages: number
445
- loading?: boolean
446
- columnKeys?: UI_I_ColumnKey[]
447
- selectedRow?: UI_T_SelectedRow
448
- type?: UI_T_DataGridType
449
- itemsPerPage?: UI_I_OptionItem[]
450
- offSelectByRow?: boolean
451
- hidePageSize?: boolean
452
- showPageInfo?: boolean
453
- serverOff?: boolean
454
- fillWidth?: boolean
455
- hideFooter?: boolean
456
- hidePagination?: boolean
457
- testId?: string
458
- withInfo?: boolean
459
- }>(),
460
- {
461
- columnKeys: undefined,
462
- selectedRow: undefined,
463
- type: undefined,
464
- itemsPerPage: undefined,
465
- fillWidth: true,
466
- testId: 'ui-data-grid',
467
- withInfo: false,
468
- }
469
- )
470
-
471
- const emits = defineEmits<{
472
- (event: 'update:column-keys', value: UI_T_ColumnKeys): void
473
- (event: 'update:selected-row', value: UI_T_SelectedRow): void
474
- (event: 'update:page-size', value: number): void
475
- (event: 'update:page', value: number): void
476
- (event: 'filtering', value: any): void
477
- (event: 'sorting', value: [string, boolean]): void
478
- (event: 'change', value: any): void
479
- (event: 'row-detail', value: number): void
480
- }>()
481
-
482
- const mergeOptions = computed(() => props.itemsPerPage || itemsPerPage)
483
-
484
- const inputId = ref(`radio-input-${useUniqueId()}`)
485
-
486
- const columnKeysLocal = computed<UI_T_ColumnKeys>({
487
- get() {
488
- return props.columnKeys
489
- },
490
- set(newValue) {
491
- emits('update:column-keys', newValue)
492
- },
493
- })
494
-
495
- const selectedAll = ref<boolean>(false)
496
- const changeAll = (e: UI_I_HTMLSelectElement): void => {
497
- const newSelectedRow: (number | string)[] = []
498
- if (e.target.checked) {
499
- bodyItemsPresent.value.forEach((row) => {
500
- newSelectedRow.push(row[0].id)
501
- })
502
- }
503
-
504
- emits('update:selected-row', newSelectedRow)
505
- }
506
-
507
- const selectedRowLocal = ref<UI_T_SelectedRow>([])
508
- const isShowHiddenInfo = ref<boolean>(false)
509
- const tableWrapperId = `table-wrapper-${useUniqueId()}`
510
- let memoryWidth = '0'
511
-
512
- const showedInfoRow = ref<UI_I_BodyItem | null>(null)
513
- const showedInfoCol = ref<UI_I_BodyItem[] | null>(null)
514
-
515
- const isShowInfo = computed(
516
- () =>
517
- showedInfoRow.value &&
518
- showedInfoRow.value.id === props.selectedRow &&
519
- !showedInfoRow.value.data?.text &&
520
- isShowHiddenInfo.value
521
- )
522
-
523
- const setShowedInfoData = (
524
- item: UI_I_BodyItem | null,
525
- items: UI_I_BodyItem[] | null,
526
- reset: boolean
527
- ): void => {
528
- showedInfoRow.value = item
529
- showedInfoCol.value = items
530
-
531
- reset && (isShowHiddenInfo.value = false)
532
- item && selectRowById(item.id)
533
- reset && (isShowHiddenInfo.value = true)
534
- }
535
-
536
- const doubleArrowClick = (
537
- item: UI_I_BodyItem,
538
- items: UI_I_BodyItem[]
539
- ): void => {
540
- if (!isShowHiddenInfo.value) {
541
- setShowedInfoData(item, items)
542
- } else {
543
- if (showedInfoRow.value.id !== item.id) {
544
- setShowedInfoData(item, items, true)
545
-
546
- return
547
- } else {
548
- setShowedInfoData(null, null)
549
- }
550
- }
551
-
552
- isShowHiddenInfo.value = !isShowHiddenInfo.value
553
- }
554
-
555
- watch(
556
- () => props.selectedRow,
557
- (val) => {
558
- if (val === undefined) return
559
-
560
- selectedRowLocal.value = val
561
- },
562
- { immediate: true }
563
- )
564
- const selectRowById = (id: number | string): void => {
565
- if (props.offSelectByRow || isShowHiddenInfo.value) return
566
-
567
- if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
568
- const has = selectedRowLocal.value.includes(id)
569
- if (has) {
570
- selectedRowLocal.value = selectedRowLocal.value.filter(
571
- (rowId: string | number) => rowId !== id
572
- )
573
- } else {
574
- selectedRowLocal.value.push(id)
575
- }
576
-
577
- emits('change', selectedRowLocal.value)
578
- emits('update:selected-row', selectedRowLocal.value)
579
-
580
- changeSelectedAll(!has)
581
- return
582
- }
583
-
584
- emits('change', id)
585
- emits('update:selected-row', id)
586
- }
587
- const changeSelectedRow = (
588
- e: UI_I_HTMLSelectElement,
589
- id: number | string
590
- ): void => {
591
- if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
592
- const newSelectedRow = [...selectedRowLocal.value]
593
- emits('change', newSelectedRow)
594
- emits('update:selected-row', newSelectedRow)
595
-
596
- changeSelectedAll(e.target.checked)
597
- return
598
- }
599
-
600
- emits('change', id)
601
- emits('update:selected-row', id)
602
- }
603
- const changeSelectedAll = (checked: boolean): void => {
604
- if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
605
- const newSelectedRow = [...selectedRowLocal.value]
606
-
607
- if (!checked) {
608
- selectedAll.value = false
609
- } else if (newSelectedRow.length === bodyItemsPresent.value.length) {
610
- selectedAll.value = true
611
- }
612
- }
613
- }
614
-
615
- const checkIsSelectedRow = (id: number | string): boolean => {
616
- if (props.type === 'checkbox' && Array.isArray(props.selectedRow)) {
617
- return props.selectedRow.includes(id)
618
- }
619
- return props.selectedRow === id
620
- }
621
-
622
- // Sorting
623
- const sortInfo = ref<[string, boolean]>(['', true])
624
- const sortedColumnIndex = computed<number>(() => {
625
- let index = -1
626
- headItemsPresent.value.forEach((item, key) => {
627
- if (item.sortColumn === sortInfo.value[0]) index = key
628
- })
629
-
630
- return index
631
- })
632
- const sortTable = (sortName: string): void => {
633
- if (!sortName) return
634
-
635
- emits('update:selected-row', selectedRowLocal.value)
636
-
637
- if (sortInfo.value[0] === sortName) {
638
- sortInfo.value[1] = !sortInfo.value[1]
639
-
640
- emits('sorting', sortInfo.value)
641
- return
642
- }
643
- sortInfo.value[0] = sortName
644
- sortInfo.value[1] = true
645
-
646
- emits('sorting', sortInfo.value)
647
- }
648
-
649
- // Filtering
650
- const filterShow = ref<boolean[]>([])
651
- const filterTerm = ref<string[]>([])
652
- const filtering = (): void => {
653
- const filter = headItemsPresent.value
654
- .filter((_, key) => filterTerm.value[key])
655
- .map((item, key) => {
656
- return [item.key, filterTerm.value[key]]
657
- })
658
-
659
- emits('filtering', filter)
660
- }
661
-
662
- // Pagination
663
- const changePageSize = (value: number): void => {
664
- emits('update:page-size', value)
665
- }
666
- const changePage = (value: number): void => {
667
- emits('update:page', value)
668
- }
669
-
670
- const headItemsPresent = computed<UI_I_HeadItem[]>(() => {
671
- if (!columnKeysLocal.value) {
672
- return props.headItems
673
- }
674
-
675
- return props.headItems.filter((item, key) => {
676
- return columnKeysLocal.value?.find(
677
- (item2, key2) => item2.key === item.key && key === key2
678
- )?.show
679
- })
680
- })
681
-
682
- const bodyItemsPresent = computed<UI_I_BodyItem[][]>(() => {
683
- let items: UI_I_BodyItem[][] = props.bodyItems
684
-
685
- // Filtering by column
686
- if (columnKeysLocal.value) {
687
- items = items.map((arr) => {
688
- arr = arr.filter((item, key) => {
689
- return columnKeysLocal.value?.find(
690
- (item2, key2) => item2.key === item.key && key === key2
691
- )?.show
692
- })
693
- return arr
694
- })
695
- }
696
-
697
- if (props.serverOff) {
698
- // Sorting
699
- if (sortInfo.value[0] !== '') {
700
- items = useDeepCopy(items).sort(
701
- (a: UI_I_BodyItem[], b: UI_I_BodyItem[]) => {
702
- const first = a[sortedColumnIndex.value].text.toString().toLowerCase()
703
- const second = b[sortedColumnIndex.value].text.toString().toLowerCase()
704
-
705
- if (sortInfo.value[1]) {
706
- return first > second ? 1 : first < second ? -1 : 0
707
- } else {
708
- return first < second ? 1 : first > second ? -1 : 0
709
- }
710
- }
711
- )
712
- }
713
- // Filtering
714
- const hasFilter = headItemsPresent.value.some(
715
- (_, key) => filterTerm.value[key]
716
- )
717
- if (hasFilter) {
718
- items = items.filter((row) => {
719
- // 0 - начальное значение
720
- // 1 - найден элемент
721
- // -1 - ненайден элемент
722
- let hasItem = 0
723
- headItemsPresent.value.forEach((item, key) => {
724
- if (!filterTerm.value[key]) return
725
- const term = filterTerm.value[key].toLowerCase()
726
-
727
- row.forEach((col) => {
728
- if (
729
- col.key === item.key &&
730
- typeof col.text === 'string' &&
731
- hasItem != -1
732
- ) {
733
- hasItem = col.text.toLowerCase().includes(term) ? 1 : -1
734
- }
735
- })
736
- })
737
-
738
- return hasItem === 1
739
- })
740
- }
741
- }
742
-
743
- return items
744
- })
745
-
746
- const toggedItems = ref<boolean[]>([])
747
-
748
- const toggle = (key: number): void => {
749
- toggedItems.value[key] = !toggedItems.value[key]
750
- if (!toggedItems.value[key]) return
751
- emits('row-detail', key)
752
- }
753
-
754
- const outerWrapper = `datagrid-outer-wrapper${useUniqueId()}`
755
- const expandableCaret = `datagrid-expandable-caret${useUniqueId()}`
756
-
757
- // Resize
758
- let helper = false
759
- let setColumnWidth = (): void => {
760
- if (!helper) {
761
- columnsWidth.value = []
762
- headItemsPresent.value.forEach((item) => {
763
- columnsWidth.value.push([
764
- item.width, // width
765
- item.width, // min-width
766
- ])
767
- })
768
-
769
- helper = true
770
- setTimeout(() => setColumnWidth(), 0)
771
- return
772
- }
773
- // Не продолжаем, чтобы ширина калонок была так как заданно в headItemsPresent
774
- if (!props.fillWidth) return
775
-
776
- const dataGridWidth = document.getElementById(outerWrapper)?.clientWidth || 0
777
- const leftColumnWidth =
778
- document.getElementById(expandableCaret)?.clientWidth || 0
779
- const bordersWidth = 2
780
- const columnWidth =
781
- (dataGridWidth - leftColumnWidth - bordersWidth) /
782
- headItemsPresent.value.length
783
-
784
- headItemsPresent.value.forEach((item) => {
785
- columnsWidth.value.push([
786
- `${columnWidth}px`, // width
787
- item.width, // min-width
788
- ])
789
- })
790
- }
791
- setColumnWidth = useThrottle(setColumnWidth)
792
-
793
- const columnsWidth = ref<[string, string][]>([])
794
- watch(
795
- bodyItemsPresent,
796
- (items: UI_I_BodyItem[][]) => {
797
- items.forEach(() => {
798
- toggedItems.value.push(false)
799
- })
800
- setColumnWidth()
801
- },
802
- {
803
- immediate: true,
804
- deep: true,
805
- }
806
- )
807
-
808
- watch(
809
- headItemsPresent,
810
- (items: UI_I_HeadItem[]) => {
811
- filterShow.value = items.map(() => false)
812
- filterTerm.value = items.map(() => '')
813
- },
814
- {
815
- immediate: true,
816
- deep: true,
817
- }
818
- )
819
-
820
- const grabColumn = ref<number>(-1)
821
- const grabStartX = ref<number>(0)
822
- const setGrab = (key: number, e: MouseEvent): void => {
823
- if (props.withInfo && key === 0) return
824
-
825
- if (!isShowHiddenInfo.value) {
826
- grabColumn.value = key
827
- const currentWidth = parseInt(columnsWidth.value[grabColumn.value][0])
828
- grabStartX.value = e.clientX - currentWidth
829
- }
830
- }
831
-
832
- const isDrag = ref<boolean>(false)
833
-
834
- const mouseup = (): void => {
835
- if (grabColumn.value === -1) {
836
- return
837
- }
838
-
839
- grabColumn.value = -1
840
- isDrag.value = false
841
- }
842
-
843
- let resizeObserve: ResizeObserver
844
- let mousemove = (e: MouseEvent): void => {
845
- if (grabColumn.value === -1) return
846
-
847
- e.preventDefault()
848
- isDrag.value = true
849
- resizeObserve.disconnect()
850
-
851
- const minWidth = parseInt(columnsWidth.value[grabColumn.value][1])
852
- const changeX = Math.max(e.clientX - grabStartX.value, minWidth)
853
-
854
- columnsWidth.value[grabColumn.value] = [`${changeX}px`, `${minWidth}px`]
855
- }
856
- mousemove = useThrottle(mousemove)
857
-
858
- const setResizeObserve = (): void => {
859
- const el = document.getElementById(outerWrapper)
860
-
861
- if (!el) {
862
- setTimeout(setResizeObserve, 0)
863
- return
864
- }
865
-
866
- resizeObserve = new ResizeObserver(setColumnWidth)
867
- resizeObserve.observe(el)
868
- }
869
-
870
- let resizeObserveWithInfo: ResizeObserver
871
-
872
- const setColumnWidthWithInfo = (ent: ResizeObserverEntry[]): void => {
873
- if (isShowHiddenInfo.value) {
874
- memoryWidth = columnsWidth.value[1][0]
875
- columnsWidth.value[1][0] = `${ent[0].contentBoxSize[0].inlineSize - 86}px`
876
- } else {
877
- columnsWidth.value[1][0] = memoryWidth
878
- }
879
- }
880
-
881
- const setResizeObserveWithInfo = (): void => {
882
- const el = document.querySelector('.datagrid')
883
-
884
- if (!el) {
885
- setTimeout(setResizeObserveWithInfo, 0)
886
- return
887
- }
888
-
889
- resizeObserveWithInfo = new ResizeObserver(setColumnWidthWithInfo)
890
- resizeObserveWithInfo.observe(el)
891
- }
892
-
893
- onMounted(() => {
894
- window.addEventListener('mousemove', mousemove)
895
- window.addEventListener('mouseup', mouseup)
896
-
897
- if (props.withInfo) {
898
- setResizeObserveWithInfo()
899
- }
900
-
901
- setResizeObserve()
902
- })
903
- onUnmounted(() => {
904
- window.removeEventListener('mouseup', mouseup)
905
- window.removeEventListener('mousemove', mousemove)
906
- })
907
- </script>
908
-
909
- <style scoped lang="scss">
910
- @import '~/assets/scss/common/mixins';
911
- .datagrid-outer-wrapper {
912
- @include flex($dir: row);
913
- padding-top: 12px;
914
- flex-grow: 1;
915
- overflow: auto;
916
-
917
- &.datagrid-outer-wrapper-opened-info {
918
- width: 35%;
919
- min-width: 195px;
920
- }
921
-
922
- & * {
923
- box-sizing: border-box;
924
- }
925
-
926
- & svg {
927
- fill: var(--table-color);
928
- }
929
-
930
- .datagrid-inner-wrapper {
931
- @include flex($dir: column);
932
- flex-grow: 1;
933
- overflow: auto;
934
-
935
- .datagrid {
936
- overflow: auto;
937
- flex: 1 1 auto;
938
- border-radius: 0.125rem 0.125rem 0 0;
939
- background-color: var(--block-view-bg-color);
940
- border-color: var(--global-border-color);
941
-
942
- &.datagrid-opened-info {
943
- border-radius: 0.125rem 0 0 0;
944
- overflow-x: hidden;
945
- }
946
-
947
- .datagrid-table-wrapper {
948
- min-width: max-content;
949
- display: flex;
950
- flex: 1 1 auto;
951
- min-height: 100%;
952
-
953
- .datagrid-table {
954
- @include flex($dir: column);
955
- flex: 1 1 auto;
956
- align-content: flex-start;
957
- position: relative;
958
-
959
- .datagrid-header {
960
- position: sticky;
961
- top: 0;
962
- z-index: 501;
963
- width: auto;
964
-
965
- .datagrid-row-scrollable {
966
- flex-direction: row;
967
-
968
- .datagrid-column-separator {
969
- user-select: none;
970
-
971
- &::after {
972
- cursor: col-resize;
973
- }
974
- &.double-arrow-separator .datagrid-column-handle {
975
- cursor: unset;
976
- }
977
- &.double-arrow-separator::after {
978
- cursor: unset;
979
- }
980
- }
981
- }
982
-
983
- .datagrid-row {
984
- color: var(--table-color);
985
- background-color: var(--block-view-bg-color);
986
-
987
- &.datagrid-selected {
988
- background-color: var(--row-selected-bg-color);
989
- border-bottom: 1px solid var(--global-border-color);
990
-
991
- & :deep(*) {
992
- color: var(--table-active-color);
993
- }
994
- }
995
-
996
- .datagrid-column {
997
- background-color: var(--block-view-bg-color);
998
- border-bottom: 1px solid var(--global-border-color);
999
- padding: 6px 12px 5px;
1000
- position: relative;
1001
- //flex: 1 1 auto;
1002
-
1003
- &:last-child {
1004
- flex: 1 1 auto;
1005
- }
1006
-
1007
- &.datagrid-toggle-block {
1008
- width: 38px;
1009
- min-width: 38px;
1010
-
1011
- .datagrid-column-separator {
1012
- cursor: default;
1013
-
1014
- &::after {
1015
- cursor: default;
1016
- }
1017
- }
1018
- }
1019
- }
1020
-
1021
- .datagrid-column-title {
1022
- align-self: auto;
1023
- color: var(--table-color);
1024
- background-color: var(--block-view-bg-color);
1025
- border-bottom-color: var(--global-border-color);
1026
- font-size: 11px;
1027
- @include flex($align: center);
1028
- display: flex;
1029
- flex-direction: row;
1030
- justify-content: flex-start;
1031
- align-items: center;
1032
- //line-height: 24px;
1033
- line-height: normal;
1034
-
1035
- .title-column {
1036
- font-weight: bold;
1037
- font-size: 11px;
1038
- //text-overflow: ellipsis;
1039
- //white-space: nowrap;
1040
- }
1041
-
1042
- .sort-arrow-wrap {
1043
- margin-left: auto;
1044
- height: 14px;
1045
- width: 14px;
1046
- min-height: 14px;
1047
- min-width: 14px;
1048
- vertical-align: middle;
1049
-
1050
- .sort-arrow {
1051
- &.down {
1052
- transform: rotate(180deg);
1053
- }
1054
- }
1055
- }
1056
- .title-icon {
1057
- width: 24px;
1058
- height: 24px;
1059
- }
1060
- }
1061
-
1062
- .datagrid-filter-toggle {
1063
- background: unset;
1064
- margin-top: -2px;
1065
-
1066
- .filter-icon {
1067
- width: 16px;
1068
- height: 16px;
1069
- }
1070
- }
1071
- }
1072
- }
1073
-
1074
- .datagrid-placeholder-container {
1075
- .datagrid-placeholder.datagrid-empty {
1076
- background: var(--block-view-bg-color);
1077
- }
1078
- span {
1079
- font-size: 16px;
1080
- color: var(--global-font-color4);
1081
- }
1082
- }
1083
-
1084
- .datagrid-row {
1085
- width: 100%;
1086
- min-width: max-content;
1087
- border-top: none;
1088
- color: var(--table-color);
1089
- background-color: var(--block-view-bg-color);
1090
- border-bottom: 1px solid var(--global-border-color);
1091
-
1092
- &.datagrid-selected {
1093
- & :deep(*) {
1094
- color: var(--table-active-color);
1095
- }
1096
-
1097
- .datagrid-cell {
1098
- background-color: var(--row-selected-bg-color);
1099
- border-bottom: 1px solid var(--global-border-color);
1100
- position: relative;
1101
- line-height: 1rem;
1102
- }
1103
-
1104
- .datagrid-row-detail {
1105
- background-color: var(--row-selected-bg-color);
1106
- border-bottom: 1px solid var(--global-border-color);
1107
- }
1108
- }
1109
-
1110
- .datagrid-cell {
1111
- min-width: 38px;
1112
- min-height: 28px;
1113
- padding: 2px 12px 3px 12px;
1114
- position: relative;
1115
-
1116
- &:has(.double-arrow-wrap) {
1117
- padding-left: 4px;
1118
- }
1119
-
1120
- &:has(.double-arrow-wrap)::after {
1121
- content: '';
1122
- width: 0.5px;
1123
- height: calc(100% - 6px);
1124
- position: absolute;
1125
- right: 2px;
1126
- top: 3px;
1127
- background-color: var(--global-border-color);
1128
- }
1129
-
1130
- &:last-child {
1131
- flex: 1 1 auto;
1132
- }
1133
-
1134
- button {
1135
- height: 0.9rem;
1136
- width: 0.9rem;
1137
-
1138
- .arrow-icon {
1139
- width: 16px;
1140
- height: 16px;
1141
- transform: rotate(90deg);
1142
- }
1143
- }
1144
-
1145
- .datagrid-cell-icon {
1146
- display: inline-block;
1147
- width: 16px;
1148
- height: 16px;
1149
- }
1150
-
1151
- .datagrid-expandable-caret-button {
1152
- cursor: pointer;
1153
- width: 100%;
1154
-
1155
- .datagrid-expandable-caret-icon {
1156
- width: 16px;
1157
- height: 16px;
1158
- transform: rotate(90deg);
1159
- fill: var(--table-caret-color);
1160
-
1161
- &.toggle {
1162
- transform: rotate(180deg);
1163
- }
1164
- }
1165
- }
1166
- }
1167
-
1168
- &.datagrid-selected-info {
1169
- width: 100%;
1170
- min-width: max-content;
1171
- border-top: none;
1172
- color: var(--table-color);
1173
- background-color: var(--block-view-bg-color);
1174
-
1175
- .datagrid-cell {
1176
- position: relative;
1177
-
1178
- &.hidden-text-triangle::after {
1179
- content: '';
1180
- position: absolute;
1181
- transform: rotate(225deg);
1182
- border-bottom: 15px solid transparent;
1183
- border-right: 15px solid var(--global-bg-color);
1184
- bottom: 26%;
1185
- right: -16px;
1186
- z-index: 1000;
1187
- }
1188
-
1189
- &.hidden-text-triangle::before {
1190
- content: '';
1191
- transform: rotate(-135deg);
1192
- position: absolute;
1193
- border-bottom: 16px solid transparent;
1194
- border-right: 16px solid var(--global-border-color);
1195
- bottom: 23%;
1196
- right: -17px;
1197
- z-index: 999;
1198
- }
1199
- }
1200
- }
1201
- }
1202
-
1203
- .datagrid-row-sticky {
1204
- @include flex($w: nowrap);
1205
- position: sticky;
1206
- left: 0;
1207
- z-index: 500;
1208
- background-color: var(--block-view-bg-color);
1209
-
1210
- .datagrid-column {
1211
- color: var(--table-color);
1212
- background-color: var(--block-view-bg-color);
1213
- border-bottom-color: var(--global-border-color);
1214
- text-align: left;
1215
- display: flex;
1216
- flex: 1 1 auto;
1217
- vertical-align: top;
1218
- border: none;
1219
-
1220
- &.datagrid-expandable-caret {
1221
- .datagrid-column-separator {
1222
- display: block;
1223
-
1224
- &::after {
1225
- top: 0;
1226
- height: 18px;
1227
- }
1228
- }
1229
-
1230
- .radio-btn {
1231
- width: 16px;
1232
- height: 16px;
1233
- }
1234
- }
1235
-
1236
- .datagrid-column-separator {
1237
- position: relative;
1238
- //left: 0.6rem;
1239
- flex: 0 0 auto;
1240
- width: 0.05rem;
1241
- order: 100;
1242
- margin-left: auto;
1243
- height: 100%;
1244
- }
1245
- }
1246
-
1247
- .datagrid-fixed-column-without-separator {
1248
- background-color: var(--block-view-bg-color);
1249
-
1250
- &::after {
1251
- background-color: transparent;
1252
- }
1253
- }
1254
-
1255
- .datagrid-fixed-column {
1256
- background-color: var(--block-view-bg-color);
1257
-
1258
- &::after {
1259
- content: '';
1260
- width: 0.05rem;
1261
- height: calc(100% - 0.5rem);
1262
- position: absolute;
1263
- right: 0;
1264
- top: 0.25rem;
1265
- background-color: var(--global-border-color);
1266
- }
1267
- }
1268
- }
1269
-
1270
- .datagrid-row-scrollable {
1271
- @include flex($ff: row nowrap);
1272
- flex: 1 1 auto;
1273
-
1274
- .datagrid-scrolling-cells {
1275
- @include flex($ff: row nowrap);
1276
- flex: 1 1 auto;
1277
- }
1278
- }
1279
- .datagrid-body-row {
1280
- .datagrid-row-scrollable {
1281
- @include flex($ff: column nowrap);
1282
-
1283
- .datagrid-row-detail {
1284
- flex: 1;
1285
- }
1286
- }
1287
-
1288
- &:not(.datagrid-selected):hover {
1289
- border-bottom: 1px solid var(--table-hover-row-border-color);
1290
- background-color: var(--table-hover-row-background-color);
1291
- color: var(--table-hover-row-color);
1292
-
1293
- .datagrid-fixed-column {
1294
- background-color: var(--table-hover-row-background-color);
1295
- }
1296
- .datagrid-cell {
1297
- background-color: var(--table-hover-row-background-color);
1298
- }
1299
- }
1300
- }
1301
- }
1302
- }
1303
- }
1304
-
1305
- .datagrid-footer {
1306
- flex: 0 0 auto;
1307
- @include flex($ff: row nowrap, $just: space-between, $align: stretch);
1308
- font-size: 0.55rem;
1309
- background-color: var(--block-view-bg-color);
1310
- border: 0.05rem solid var(--global-border-color);
1311
- border-top: none;
1312
- border-radius: 0 0 0.15rem 0.15rem;
1313
- padding: 0 0.6rem;
1314
- height: 28px;
1315
-
1316
- &.datagrid-footer-opened-info {
1317
- border-radius: 0 0 0 0.15rem;
1318
- flex-direction: row-reverse;
1319
- }
1320
-
1321
- .clr-form-control-disabled {
1322
- opacity: 1;
1323
-
1324
- .clr-checkbox-wrapper {
1325
- label {
1326
- color: #8c8c8c;
1327
- margin-right: 9px;
1328
- padding-left: 22px;
1329
-
1330
- &:before {
1331
- position: absolute;
1332
- top: 4px;
1333
- left: 0;
1334
- content: '';
1335
- display: inline-block;
1336
- height: 16px;
1337
- width: 16px;
1338
- border: none;
1339
- border-radius: 3px;
1340
- background-color: var(--table-checkbox-color);
1341
- }
1342
-
1343
- &:after {
1344
- position: absolute;
1345
- content: '';
1346
- display: inline-block;
1347
- height: 5px;
1348
- width: 8px;
1349
- border-left: 2px solid #fff;
1350
- border-bottom: 2px solid #fff;
1351
- top: 4px;
1352
- left: 4px;
1353
- transform: translateY(4px) rotate(-45deg);
1354
- }
1355
- }
1356
- }
1357
- }
1358
- }
1359
-
1360
- .datagrid-spinner {
1361
- top: 0.6rem;
1362
- height: calc(100% - 0.6rem);
1363
- }
1364
-
1365
- .datagrid-footer-description {
1366
- flex: 1 1 auto;
1367
-
1368
- :deep(svg) {
1369
- fill: var(--table-switch-color);
1370
- }
1371
- }
1372
-
1373
- .clr-radio-wrapper,
1374
- .clr-checkbox-wrapper {
1375
- height: 18px;
1376
- }
1377
-
1378
- .clr-radio-wrapper label,
1379
- .clr-checkbox-wrapper label {
1380
- padding-left: 16px;
1381
- }
1382
- }
1383
- }
1384
- //.vertical-divider {
1385
- // width: 1px;
1386
- // height: 22px;
1387
- // background: #bbb;
1388
- // margin: 2px 2px 0 -5px;
1389
- //}
1390
-
1391
- .hidden-features {
1392
- padding: 9px 14px;
1393
- background-color: var(--global-bg-color);
1394
- color: var(--global-font-color2);
1395
- border: 1px solid var(--global-border-color);
1396
- border-left: none;
1397
- overflow-y: auto;
1398
- overflow-x: hidden;
1399
- width: 65%;
1400
-
1401
- .header {
1402
- display: flex;
1403
- align-items: center;
1404
- justify-content: space-between;
1405
- background-color: inherit;
1406
-
1407
- h3 {
1408
- font-size: 1rem;
1409
- color: var(--global-font-color2) !important;
1410
- line-height: 1.7rem;
1411
- }
1412
- button {
1413
- line-height: 24px;
1414
-
1415
- .close-icon {
1416
- margin-left: 10px;
1417
- width: 24px;
1418
- height: 24px;
1419
- }
1420
- }
1421
- }
1422
- }
1423
- .double-arrow-width {
1424
- width: 25px;
1425
- }
1426
- .double-arrow-wrap {
1427
- width: 30px;
1428
- height: 20px;
1429
- padding-top: 1px;
1430
- padding-left: 5px;
1431
- cursor: pointer;
1432
- background-color: transparent;
1433
-
1434
- .double-arrow {
1435
- background-color: transparent;
1436
- transform: rotate(90deg);
1437
- width: 16px;
1438
- height: 16px;
1439
- }
1440
-
1441
- .double-arrow.arrow-selected {
1442
- transform: rotate(-90deg) !important;
1443
- background-color: #0079b8;
1444
- }
1445
- }
1446
- .double-arrow-wrap.arrow-selected {
1447
- background-color: #0079b8;
1448
- border-radius: 3px;
1449
- }
1450
- </style>
1451
-
1452
- <style>
1453
- :root {
1454
- --table-color: #565656;
1455
- --table-active-color: #fff;
1456
- --table-checkbox-color: #454545;
1457
- --table-caret-color: #454545;
1458
- --table-switch-color: #575757;
1459
- --table-hover-row-border-color: #666;
1460
- --table-hover-row-background-color: #e8e8e8;
1461
- --table-hover-row-color: #454545;
1462
- --table-spinner-bg-color: #ffffff99;
1463
- }
1464
-
1465
- :root.dark-theme {
1466
- --table-color: #fff;
1467
- --table-active-color: #565656;
1468
- --table-checkbox-color: #49aeda;
1469
- --table-caret-color: #ffffff;
1470
- --table-switch-color: #b3b3b3;
1471
- --table-hover-row-border-color: #fff;
1472
- --table-hover-row-background-color: #324f61;
1473
- --table-hover-row-color: #e9ecef;
1474
- --table-spinner-bg-color: rgba(0, 0, 0, 0.5);
1475
- }
1476
- </style>
1
+ <template>
2
+ <div class="relative" style="display: flex">
3
+ <div
4
+ :id="outerWrapper"
5
+ :class="[
6
+ 'datagrid-outer-wrapper',
7
+ {
8
+ 'datagrid-outer-wrapper-opened-info': isShowHiddenInfo,
9
+ },
10
+ ]"
11
+ >
12
+ <div class="datagrid-inner-wrapper">
13
+ <div
14
+ :class="[
15
+ 'datagrid',
16
+ {
17
+ 'datagrid-opened-info': isShowHiddenInfo,
18
+ },
19
+ ]"
20
+ >
21
+ <div :id="tableWrapperId" class="datagrid-table-wrapper">
22
+ <div class="datagrid-table">
23
+ <div class="datagrid-header">
24
+ <div class="datagrid-row">
25
+ <div class="datagrid-row-master datagrid-row-flex">
26
+ <div v-if="props.type" class="datagrid-row-sticky">
27
+ <div
28
+ :id="expandableCaret"
29
+ class="datagrid-column datagrid-expandable-caret datagrid-fixed-column flex-align-center flex-justify-center"
30
+ >
31
+ <template v-if="props.type === 'checkbox'">
32
+ <div
33
+ class="clr-checkbox-wrapper flex-align-center flex-justify-center w-100"
34
+ >
35
+ <input
36
+ :id="`${props.testId}-${inputId}-all`"
37
+ v-model="selectedAll"
38
+ type="checkbox"
39
+ class="checkbox-btn"
40
+ :value="-1"
41
+ @change="changeAll"
42
+ />
43
+ <label
44
+ :for="`${props.testId}-${inputId}-all`"
45
+ class="clr-control-label"
46
+ />
47
+ </div>
48
+ <div class="datagrid-column-separator"></div>
49
+ </template>
50
+ </div>
51
+ <div
52
+ v-if="$slots.toggleBlock"
53
+ class="datagrid-toggle-block datagrid-column datagrid-fixed-width"
54
+ >
55
+ <div class="datagrid-column-flex">
56
+ <div class="datagrid-column-separator">
57
+ <span class="clr-sr-only" />
58
+ <div class="datagrid-column-resize-tracker" />
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ <div class="datagrid-row-scrollable">
64
+ <div
65
+ v-for="(item, key) in headItemsPresent"
66
+ :key="key"
67
+ :style="{
68
+ width: columnsWidth[key][0],
69
+ minWidth: columnsWidth[key][1],
70
+ }"
71
+ class="datagrid-column datagrid-fixed-width"
72
+ >
73
+ <div class="datagrid-column-flex">
74
+ <button
75
+ :id="`${props.testId}-sort-column-${key}`"
76
+ :class="[
77
+ 'datagrid-column-title',
78
+ {
79
+ 'default text-decoration-none':
80
+ !item.sortColumn,
81
+ },
82
+ ]"
83
+ @click="sortTable(item.sortColumn)"
84
+ >
85
+ <slot name="th" :key="key" :item="item">
86
+ <span class="title-column" :title="item.text" :style="{
87
+ width: columnsWidth[key][0]?.slice(0, -2) - (item.hasFilter ? 65 : 40) + 'px'
88
+ }">
89
+ {{ item.text }}
90
+ </span>
91
+ </slot>
92
+ <span
93
+ v-if="item.sortColumn"
94
+ class="sort-arrow-wrap"
95
+ >
96
+ <atoms-the-icon
97
+ v-show="sortInfo[0] === item.sortColumn"
98
+ :class="['sort-arrow', { down: sortInfo[1] }]"
99
+ name="sort-arrow"
100
+ />
101
+ </span>
102
+ </button>
103
+ <button
104
+ v-if="item.hasFilter"
105
+ :id="`${props.testId}-filter-icon-${key}`"
106
+ class="datagrid-filter-toggle clr-anchor clr-smart-open-close"
107
+ @click="filterShow[key] = !filterShow[key]"
108
+ >
109
+ <atoms-the-icon
110
+ v-if="filterTerm[key]"
111
+ class="filter-icon"
112
+ name="filter-solid"
113
+ style="fill: #0079b8"
114
+ />
115
+ <atoms-the-icon
116
+ v-else
117
+ class="filter-icon"
118
+ name="filter"
119
+ />
120
+ </button>
121
+ <atoms-popup-simple-popup
122
+ v-model="filterShow[key]"
123
+ :test-id="`${props.testId}-filter`"
124
+ top="100%"
125
+ right="0"
126
+ >
127
+ <input
128
+ :id="`${props.testId}-filter-input-${key}`"
129
+ v-model="filterTerm[key]"
130
+ type="text"
131
+ @input="filtering"
132
+ />
133
+ </atoms-popup-simple-popup>
134
+ <div
135
+ v-show="
136
+ !isShowHiddenInfo ||
137
+ (isShowHiddenInfo && key === 0)
138
+ "
139
+ :class="[
140
+ 'datagrid-column-separator',
141
+ {
142
+ 'double-arrow-separator':
143
+ key === 0 && props.withInfo,
144
+ },
145
+ ]"
146
+ @mousedown="setGrab(key, $event)"
147
+ >
148
+ <button
149
+ :id="`${props.testId}-resize-tracker-${key}`"
150
+ class="datagrid-column-handle drag-handle draggable"
151
+ />
152
+ <span class="clr-sr-only" />
153
+ <div class="datagrid-column-resize-tracker" />
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <template v-if="bodyItemsPresent.length">
163
+ <div
164
+ v-for="(item, key) in bodyItemsPresent"
165
+ :key="item.id"
166
+ :class="[
167
+ 'datagrid-body-row datagrid-row animation',
168
+ {
169
+ 'datagrid-selected active':
170
+ !props.withInfo && checkIsSelectedRow(item[0].id),
171
+ 'datagrid-selected-info':
172
+ props.withInfo &&
173
+ isShowHiddenInfo &&
174
+ checkIsSelectedRow(item[0].id),
175
+ },
176
+ ]"
177
+ >
178
+ <div>
179
+ <div
180
+ class="datagrid-row-master datagrid-row-flex"
181
+ @click.prevent.stop="selectRowById(item[0].id)"
182
+ >
183
+ <div
184
+ v-if="props.type"
185
+ class="datagrid-row-sticky"
186
+ @click.stop
187
+ >
188
+ <div
189
+ :id="expandableCaret"
190
+ :class="[
191
+ 'datagrid-expandable-caret flex-justify-center datagrid-cell',
192
+ {
193
+ 'datagrid-fixed-column': !props.withInfo,
194
+ 'datagrid-fixed-column-without-separator':
195
+ props.withInfo,
196
+ },
197
+ ]"
198
+ >
199
+ <slot name="type" :key="key" :item="item">
200
+ <div
201
+ :class="`clr-${props.type}-wrapper flex-justify-center flex-align-center`"
202
+ >
203
+ <input
204
+ :id="`${props.testId}-${inputId}-${item[0].id}`"
205
+ v-model="selectedRowLocal"
206
+ :type="props.type"
207
+ :class="`${props.type}-btn`"
208
+ :value="item[0].id"
209
+ name="selected-store"
210
+ @change="changeSelectedRow($event, item[0].id)"
211
+ />
212
+ <label
213
+ :for="`${props.testId}-${inputId}-${item[0].id}`"
214
+ class="clr-control-label"
215
+ />
216
+ </div>
217
+ </slot>
218
+ </div>
219
+ <div
220
+ v-if="$slots.toggleBlock"
221
+ class="datagrid-cell datagrid-fixed-width"
222
+ >
223
+ <div class="flex-align-center">
224
+ <button
225
+ :id="`${props.testId}-toggle-icon-${key}`"
226
+ class="datagrid-expandable-caret-button reset-btn"
227
+ @click="toggle(key)"
228
+ >
229
+ <atoms-the-icon
230
+ :class="[
231
+ 'datagrid-expandable-caret-icon',
232
+ { toggle: toggedItems[key] },
233
+ ]"
234
+ name="angle"
235
+ />
236
+ </button>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ <div class="datagrid-row-scrollable">
241
+ <div class="datagrid-scrolling-cells">
242
+ <div
243
+ v-for="(item2, key2) in item"
244
+ v-show="
245
+ !isShowHiddenInfo ||
246
+ item2.key === 'col1' ||
247
+ item2.key === 'icon'
248
+ "
249
+ :key="key2"
250
+ :style="{
251
+ width: columnsWidth[key2][0],
252
+ minWidth: columnsWidth[key2][1],
253
+ }"
254
+ :class="[
255
+ 'datagrid-cell datagrid-fixed-width',
256
+ {
257
+ 'hidden-text-triangle':
258
+ isShowHiddenInfo &&
259
+ item2.data?.name === 'hidden-text-triangle',
260
+ },
261
+ ]"
262
+ >
263
+ <div
264
+ :class="[
265
+ 'flex-align-center h-100',
266
+ {
267
+ 'double-arrow-width':
268
+ item2.data?.name === 'doubleArrows',
269
+ },
270
+ ]"
271
+ >
272
+ <div
273
+ v-if="item2.data?.name === 'doubleArrows'"
274
+ class="double-arrow-wrap"
275
+ :class="{
276
+ 'arrow-selected':
277
+ isShowHiddenInfo &&
278
+ item2.id === selectedRow,
279
+ }"
280
+ @click.prevent.stop="
281
+ doubleArrowClick(item2, item)
282
+ "
283
+ >
284
+ <atoms-the-icon
285
+ v-if="item2.data?.name === 'doubleArrows'"
286
+ name="doubleArrows"
287
+ class="double-arrow"
288
+ :class="{
289
+ 'arrow-selected':
290
+ isShowHiddenInfo &&
291
+ item2.id === selectedRow,
292
+ }"
293
+ />
294
+ </div>
295
+ <span
296
+ v-if="item2.text && item2.data?.iconClassName"
297
+ class="vertical-divider"
298
+ />
299
+ <slot :name="item2.key" :item="item2">
300
+ <span class="text-ellipsis" :title="item2.text"
301
+ >{{ item2.text }}
302
+ </span>
303
+ </slot>
304
+ </div>
305
+ </div>
306
+ </div>
307
+ <div
308
+ v-show="toggedItems[key]"
309
+ class="datagrid-row-flex datagrid-row-detail datagrid-container"
310
+ >
311
+ <slot name="toggleBlock" />
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ </template>
318
+ <template v-else>
319
+ <div class="datagrid-placeholder-container">
320
+ <div class="datagrid-placeholder datagrid-empty">
321
+ <div class="datagrid-placeholder-image"></div>
322
+
323
+ <span>No items found</span>
324
+ </div>
325
+ </div>
326
+ </template>
327
+ </div>
328
+ </div>
329
+ </div>
330
+
331
+ <div
332
+ v-if="!hideFooter"
333
+ :class="[
334
+ 'datagrid-footer',
335
+ {
336
+ 'datagrid-footer-opened-info': isShowHiddenInfo,
337
+ },
338
+ ]"
339
+ >
340
+ <div
341
+ v-if="props.type === 'checkbox' && props.selectedRow?.length"
342
+ class="clr-form-control-disabled"
343
+ >
344
+ <div class="clr-checkbox-wrapper">
345
+ <input
346
+ :id="`${props.testId}-check-all`"
347
+ type="checkbox"
348
+ checked
349
+ />
350
+ <label class="clr-control-label">
351
+ {{ props.selectedRow?.length }}
352
+ </label>
353
+ </div>
354
+ </div>
355
+ <div class="datagrid-footer-description flex-align-center">
356
+ <atoms-table-data-grid-column-switch
357
+ v-if="columnKeysLocal"
358
+ v-model:column-keys="columnKeysLocal"
359
+ :test-id="`${props.testId}-footer-description`"
360
+ class="switch-icon"
361
+ />
362
+
363
+ <div v-if="$slots.action" class="datagrid-footer__action">
364
+ <slot name="action" />
365
+ </div>
366
+ </div>
367
+
368
+ <atoms-table-data-grid-pagination
369
+ test-id="props.testId"
370
+ :page-size="props.pageSize"
371
+ :page="props.page"
372
+ :options="mergeOptions"
373
+ :total-items="props.totalItems"
374
+ :total-pages="props.totalPages"
375
+ :hide-pagination="props.hidePagination"
376
+ :hide-page-size="props.hidePageSize"
377
+ :show-page-info="props.showPageInfo"
378
+ :page-items-count="props.bodyItems.length"
379
+ :is-show-hidden-info="isShowHiddenInfo"
380
+ @change-page-size="changePageSize"
381
+ @change-page="changePage"
382
+ />
383
+ </div>
384
+
385
+ <atoms-loader v-show="props.loading" class="datagrid-spinner" />
386
+ </div>
387
+ </div>
388
+ <div
389
+ v-if="isShowInfo"
390
+ id="hidden-features"
391
+ :class="[
392
+ 'hidden-features',
393
+ {
394
+ 'show-hidden-info': isShowHiddenInfo,
395
+ },
396
+ ]"
397
+ >
398
+ <div class="header">
399
+ <slot name="hiddenInfoHeader" :item="showedInfoCol" />
400
+ <button
401
+ :id="`${props.testId}-hidden-info-toggle-icon-${showedInfoRow?.key}-${showedInfoRow?.data.name}`"
402
+ class="signpost-action close"
403
+ @click="isShowHiddenInfo = false"
404
+ >
405
+ <atoms-the-icon class="close-icon" name="close" />
406
+ </button>
407
+ </div>
408
+ <slot name="hiddenInfoBody" />
409
+ </div>
410
+ </div>
411
+ </template>
412
+
413
+ <script setup lang="ts">
414
+ import { UI_I_HTMLSelectElement } from '~/models/interfaces'
415
+ import { itemsPerPage } from '~/components/atoms/table/dataGrid/config/itemsPerPage'
416
+ import {
417
+ UI_I_HeadItem,
418
+ UI_I_BodyItem,
419
+ UI_I_ColumnKey,
420
+ } from '~/components/atoms/table/dataGrid/models/interfaces'
421
+ import { UI_I_OptionItem } from '~/components/atoms/models/interfaces'
422
+ import {
423
+ UI_T_ColumnKeys,
424
+ UI_T_DataGridType,
425
+ UI_T_SelectedRow,
426
+ } from '~/components/atoms/table/dataGrid/models/types'
427
+
428
+ const props = withDefaults(
429
+ defineProps<{
430
+ headItems: UI_I_HeadItem[]
431
+ bodyItems: UI_I_BodyItem[][]
432
+ pageSize: number
433
+ page: number
434
+ totalItems: number
435
+ totalPages: number
436
+ loading?: boolean
437
+ columnKeys?: UI_I_ColumnKey[]
438
+ selectedRow?: UI_T_SelectedRow
439
+ type?: UI_T_DataGridType
440
+ itemsPerPage?: UI_I_OptionItem[]
441
+ offSelectByRow?: boolean
442
+ hidePageSize?: boolean
443
+ showPageInfo?: boolean
444
+ serverOff?: boolean
445
+ fillWidth?: boolean
446
+ hideFooter?: boolean
447
+ hidePagination?: boolean
448
+ testId?: string
449
+ withInfo?: boolean
450
+ }>(),
451
+ {
452
+ columnKeys: undefined,
453
+ selectedRow: undefined,
454
+ type: undefined,
455
+ itemsPerPage: undefined,
456
+ fillWidth: true,
457
+ testId: 'ui-data-grid',
458
+ withInfo: false,
459
+ }
460
+ )
461
+
462
+ const emits = defineEmits<{
463
+ (event: 'update:column-keys', value: UI_T_ColumnKeys): void
464
+ (event: 'update:selected-row', value: UI_T_SelectedRow): void
465
+ (event: 'update:page-size', value: number): void
466
+ (event: 'update:page', value: number): void
467
+ (event: 'filtering', value: any): void
468
+ (event: 'sorting', value: [string, boolean]): void
469
+ (event: 'change', value: any): void
470
+ (event: 'row-detail', value: number): void
471
+ }>()
472
+
473
+ const mergeOptions = computed(() => props.itemsPerPage || itemsPerPage)
474
+
475
+ const inputId = ref(`radio-input-${useUniqueId()}`)
476
+
477
+ const columnKeysLocal = computed<UI_T_ColumnKeys>({
478
+ get() {
479
+ return props.columnKeys
480
+ },
481
+ set(newValue) {
482
+ emits('update:column-keys', newValue)
483
+ },
484
+ })
485
+
486
+ const selectedAll = ref<boolean>(false)
487
+ const changeAll = (e: UI_I_HTMLSelectElement): void => {
488
+ const newSelectedRow: (number | string)[] = []
489
+ if (e.target.checked) {
490
+ bodyItemsPresent.value.forEach((row) => {
491
+ newSelectedRow.push(row[0].id)
492
+ })
493
+ }
494
+
495
+ emits('update:selected-row', newSelectedRow)
496
+ }
497
+
498
+ const selectedRowLocal = ref<UI_T_SelectedRow>([])
499
+ const isShowHiddenInfo = ref<boolean>(false)
500
+ const tableWrapperId = `table-wrapper-${useUniqueId()}`
501
+ let memoryWidth = '0'
502
+
503
+ const showedInfoRow = ref<UI_I_BodyItem | null>(null)
504
+ const showedInfoCol = ref<UI_I_BodyItem[] | null>(null)
505
+
506
+ const isShowInfo = computed(
507
+ () =>
508
+ showedInfoRow.value &&
509
+ showedInfoRow.value.id === props.selectedRow &&
510
+ !showedInfoRow.value.data?.text &&
511
+ isShowHiddenInfo.value
512
+ )
513
+
514
+ const setShowedInfoData = (
515
+ item: UI_I_BodyItem | null,
516
+ items: UI_I_BodyItem[] | null,
517
+ reset: boolean = false
518
+ ): void => {
519
+ showedInfoRow.value = item
520
+ showedInfoCol.value = items
521
+
522
+ reset && (isShowHiddenInfo.value = false)
523
+ item && selectRowById(item.id)
524
+ reset && (isShowHiddenInfo.value = true)
525
+ }
526
+
527
+ const doubleArrowClick = (
528
+ item: UI_I_BodyItem,
529
+ items: UI_I_BodyItem[]
530
+ ): void => {
531
+ if (!isShowHiddenInfo.value) {
532
+ setShowedInfoData(item, items)
533
+ } else {
534
+ if (showedInfoRow.value?.id !== item.id) {
535
+ setShowedInfoData(item, items, true)
536
+
537
+ return
538
+ } else {
539
+ setShowedInfoData(null, null)
540
+ }
541
+ }
542
+
543
+ isShowHiddenInfo.value = !isShowHiddenInfo.value
544
+ }
545
+
546
+ watch(
547
+ () => props.selectedRow,
548
+ (val) => {
549
+ if (val === undefined) return
550
+
551
+ selectedRowLocal.value = val
552
+ },
553
+ { immediate: true }
554
+ )
555
+ const selectRowById = (id: number | string): void => {
556
+ if (props.offSelectByRow || isShowHiddenInfo.value) return
557
+
558
+ if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
559
+ const has = selectedRowLocal.value.includes(id)
560
+ if (has) {
561
+ selectedRowLocal.value = selectedRowLocal.value.filter(
562
+ (rowId: string | number) => rowId !== id
563
+ )
564
+ } else {
565
+ selectedRowLocal.value.push(id)
566
+ }
567
+
568
+ emits('change', selectedRowLocal.value)
569
+ emits('update:selected-row', selectedRowLocal.value)
570
+
571
+ changeSelectedAll(!has)
572
+ return
573
+ }
574
+
575
+ emits('change', id)
576
+ emits('update:selected-row', id)
577
+ }
578
+ const changeSelectedRow = (
579
+ e: UI_I_HTMLSelectElement,
580
+ id: number | string
581
+ ): void => {
582
+ if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
583
+ const newSelectedRow = [...selectedRowLocal.value]
584
+ emits('change', newSelectedRow)
585
+ emits('update:selected-row', newSelectedRow)
586
+
587
+ changeSelectedAll(e.target.checked)
588
+ return
589
+ }
590
+
591
+ emits('change', id)
592
+ emits('update:selected-row', id)
593
+ }
594
+ const changeSelectedAll = (checked: boolean): void => {
595
+ if (props.type === 'checkbox' && Array.isArray(selectedRowLocal.value)) {
596
+ const newSelectedRow = [...selectedRowLocal.value]
597
+
598
+ if (!checked) {
599
+ selectedAll.value = false
600
+ } else if (newSelectedRow.length === bodyItemsPresent.value.length) {
601
+ selectedAll.value = true
602
+ }
603
+ }
604
+ }
605
+
606
+ const checkIsSelectedRow = (id: number | string): boolean => {
607
+ if (props.type === 'checkbox' && Array.isArray(props.selectedRow)) {
608
+ return props.selectedRow.includes(id)
609
+ }
610
+ return props.selectedRow === id
611
+ }
612
+
613
+ // Sorting
614
+ const sortInfo = ref<[string, boolean]>(['', true])
615
+ const sortedColumnIndex = computed<number>(() => {
616
+ let index = -1
617
+ headItemsPresent.value.forEach((item, key) => {
618
+ if (item.sortColumn === sortInfo.value[0]) index = key
619
+ })
620
+
621
+ return index
622
+ })
623
+ const sortTable = (sortName: string): void => {
624
+ if (!sortName) return
625
+
626
+ emits('update:selected-row', selectedRowLocal.value)
627
+
628
+ if (sortInfo.value[0] === sortName) {
629
+ sortInfo.value[1] = !sortInfo.value[1]
630
+
631
+ emits('sorting', sortInfo.value)
632
+ return
633
+ }
634
+ sortInfo.value[0] = sortName
635
+ sortInfo.value[1] = true
636
+
637
+ emits('sorting', sortInfo.value)
638
+ }
639
+
640
+ // Filtering
641
+ const filterShow = ref<boolean[]>([])
642
+ const filterTerm = ref<string[]>([])
643
+ const filtering = (): void => {
644
+ const filter = headItemsPresent.value
645
+ .filter((_, key) => filterTerm.value[key])
646
+ .map((item, key) => {
647
+ return [item.key, filterTerm.value[key]]
648
+ })
649
+
650
+ emits('filtering', filter)
651
+ }
652
+
653
+ // Pagination
654
+ const changePageSize = (value: number): void => {
655
+ emits('update:page-size', value)
656
+ }
657
+ const changePage = (value: number): void => {
658
+ emits('update:page', value)
659
+ }
660
+
661
+ const headItemsPresent = computed<UI_I_HeadItem[]>(() => {
662
+ if (!columnKeysLocal.value) {
663
+ return props.headItems
664
+ }
665
+
666
+ return props.headItems.filter((item, key) => {
667
+ return columnKeysLocal.value?.find(
668
+ (item2, key2) => item2.key === item.key && key === key2
669
+ )?.show
670
+ })
671
+ })
672
+
673
+ const bodyItemsPresent = computed<UI_I_BodyItem[][]>(() => {
674
+ let items: UI_I_BodyItem[][] = props.bodyItems
675
+
676
+ // Filtering by column
677
+ if (columnKeysLocal.value) {
678
+ items = items.map((arr) => {
679
+ arr = arr.filter((item, key) => {
680
+ return columnKeysLocal.value?.find(
681
+ (item2, key2) => item2.key === item.key && key === key2
682
+ )?.show
683
+ })
684
+ return arr
685
+ })
686
+ }
687
+
688
+ if (props.serverOff) {
689
+ // Sorting
690
+ if (sortInfo.value[0] !== '') {
691
+ items = useDeepCopy(items).sort(
692
+ (a: UI_I_BodyItem[], b: UI_I_BodyItem[]) => {
693
+ const first = a[sortedColumnIndex.value].text.toString().toLowerCase()
694
+ const second = b[sortedColumnIndex.value].text
695
+ .toString()
696
+ .toLowerCase()
697
+
698
+ if (sortInfo.value[1]) {
699
+ return first > second ? 1 : first < second ? -1 : 0
700
+ } else {
701
+ return first < second ? 1 : first > second ? -1 : 0
702
+ }
703
+ }
704
+ )
705
+ }
706
+ // Filtering
707
+ const hasFilter = headItemsPresent.value.some(
708
+ (_, key) => filterTerm.value[key]
709
+ )
710
+ if (hasFilter) {
711
+ items = items.filter((row) => {
712
+ // 0 - начальное значение
713
+ // 1 - найден элемент
714
+ // -1 - ненайден элемент
715
+ let hasItem = 0
716
+ headItemsPresent.value.forEach((item, key) => {
717
+ if (!filterTerm.value[key]) return
718
+ const term = filterTerm.value[key].toLowerCase()
719
+
720
+ row.forEach((col) => {
721
+ if (
722
+ col.key === item.key &&
723
+ typeof col.text === 'string' &&
724
+ hasItem != -1
725
+ ) {
726
+ hasItem = col.text.toLowerCase().includes(term) ? 1 : -1
727
+ }
728
+ })
729
+ })
730
+
731
+ return hasItem === 1
732
+ })
733
+ }
734
+ }
735
+
736
+ return items
737
+ })
738
+
739
+ const toggedItems = ref<boolean[]>([])
740
+
741
+ const toggle = (key: number): void => {
742
+ toggedItems.value[key] = !toggedItems.value[key]
743
+ if (!toggedItems.value[key]) return
744
+ emits('row-detail', key)
745
+ }
746
+
747
+ const outerWrapper = `datagrid-outer-wrapper${useUniqueId()}`
748
+ const expandableCaret = `datagrid-expandable-caret${useUniqueId()}`
749
+
750
+ // Resize
751
+ let helper = false
752
+ let setColumnWidth = (): void => {
753
+ if (!helper) {
754
+ columnsWidth.value = []
755
+ headItemsPresent.value.forEach((item) => {
756
+ columnsWidth.value.push([
757
+ item.width, // width
758
+ item.width, // min-width
759
+ ])
760
+ })
761
+
762
+ helper = true
763
+ setTimeout(() => setColumnWidth(), 0)
764
+ return
765
+ }
766
+ // Не продолжаем, чтобы ширина калонок была так как заданно в headItemsPresent
767
+ if (!props.fillWidth) return
768
+
769
+ const dataGridWidth = document.getElementById(outerWrapper)?.clientWidth || 0
770
+ const leftColumnWidth =
771
+ document.getElementById(expandableCaret)?.clientWidth || 0
772
+ const bordersWidth = 2
773
+ const columnWidth =
774
+ (dataGridWidth - leftColumnWidth - bordersWidth) /
775
+ headItemsPresent.value.length
776
+
777
+ headItemsPresent.value.forEach((item) => {
778
+ columnsWidth.value.push([
779
+ `${columnWidth}px`, // width
780
+ item.width, // min-width
781
+ ])
782
+ })
783
+ }
784
+ setColumnWidth = useThrottle(setColumnWidth)
785
+
786
+ const columnsWidth = ref<[string, string][]>([])
787
+ watch(
788
+ bodyItemsPresent,
789
+ (items: UI_I_BodyItem[][]) => {
790
+ items.forEach(() => {
791
+ toggedItems.value.push(false)
792
+ })
793
+ setColumnWidth()
794
+ },
795
+ {
796
+ immediate: true,
797
+ deep: true,
798
+ }
799
+ )
800
+
801
+ watch(
802
+ headItemsPresent,
803
+ (items: UI_I_HeadItem[]) => {
804
+ filterShow.value = items.map(() => false)
805
+ filterTerm.value = items.map(() => '')
806
+ },
807
+ {
808
+ immediate: true,
809
+ deep: true,
810
+ }
811
+ )
812
+
813
+ const grabColumn = ref<number>(-1)
814
+ const grabStartX = ref<number>(0)
815
+ const setGrab = (key: number, e: MouseEvent): void => {
816
+ if (props.withInfo && key === 0) return
817
+
818
+ if (!isShowHiddenInfo.value) {
819
+ grabColumn.value = key
820
+ const currentWidth = parseInt(columnsWidth.value[grabColumn.value][0])
821
+ grabStartX.value = e.clientX - currentWidth
822
+ }
823
+ }
824
+
825
+ const isDrag = ref<boolean>(false)
826
+
827
+ const mouseup = (): void => {
828
+ if (grabColumn.value === -1) {
829
+ return
830
+ }
831
+
832
+ grabColumn.value = -1
833
+ isDrag.value = false
834
+ }
835
+
836
+ let resizeObserve: ResizeObserver
837
+ let mousemove = (e: MouseEvent): void => {
838
+ if (grabColumn.value === -1) return
839
+
840
+ e.preventDefault()
841
+ isDrag.value = true
842
+ resizeObserve.disconnect()
843
+
844
+ const minWidth = parseInt(columnsWidth.value[grabColumn.value][1])
845
+ const changeX = Math.max(e.clientX - grabStartX.value, minWidth)
846
+
847
+ columnsWidth.value[grabColumn.value] = [`${changeX}px`, `${minWidth}px`]
848
+ }
849
+ mousemove = useThrottle(mousemove)
850
+
851
+ const setResizeObserve = (): void => {
852
+ const el = document.getElementById(outerWrapper)
853
+
854
+ if (!el) {
855
+ setTimeout(setResizeObserve, 0)
856
+ return
857
+ }
858
+
859
+ resizeObserve = new ResizeObserver(setColumnWidth)
860
+ resizeObserve.observe(el)
861
+ }
862
+
863
+ let resizeObserveWithInfo: ResizeObserver
864
+
865
+ const setColumnWidthWithInfo = (ent: ResizeObserverEntry[]): void => {
866
+ if (isShowHiddenInfo.value) {
867
+ memoryWidth = columnsWidth.value[1][0]
868
+ columnsWidth.value[1][0] = `${ent[0].contentBoxSize[0].inlineSize - 86}px`
869
+ } else {
870
+ columnsWidth.value[1][0] = memoryWidth
871
+ }
872
+ }
873
+
874
+ const setResizeObserveWithInfo = (): void => {
875
+ const el = document.querySelector('.datagrid')
876
+
877
+ if (!el) {
878
+ setTimeout(setResizeObserveWithInfo, 0)
879
+ return
880
+ }
881
+
882
+ resizeObserveWithInfo = new ResizeObserver(setColumnWidthWithInfo)
883
+ resizeObserveWithInfo.observe(el)
884
+ }
885
+
886
+ onMounted(() => {
887
+ window.addEventListener('mousemove', mousemove)
888
+ window.addEventListener('mouseup', mouseup)
889
+
890
+ if (props.withInfo) {
891
+ setResizeObserveWithInfo()
892
+ }
893
+
894
+ setResizeObserve()
895
+ })
896
+ onUnmounted(() => {
897
+ window.removeEventListener('mouseup', mouseup)
898
+ window.removeEventListener('mousemove', mousemove)
899
+ })
900
+ </script>
901
+
902
+ <style scoped lang="scss">
903
+ @import '~/assets/scss/common/mixins';
904
+ .datagrid-outer-wrapper {
905
+ @include flex($dir: row);
906
+ padding-top: 12px;
907
+ flex-grow: 1;
908
+ overflow: auto;
909
+
910
+ &.datagrid-outer-wrapper-opened-info {
911
+ width: 35%;
912
+ min-width: 195px;
913
+ }
914
+
915
+ & * {
916
+ box-sizing: border-box;
917
+ }
918
+
919
+ & svg {
920
+ fill: var(--table-color);
921
+ }
922
+
923
+ .datagrid-inner-wrapper {
924
+ @include flex($dir: column);
925
+ flex-grow: 1;
926
+ overflow: auto;
927
+
928
+ .datagrid {
929
+ overflow: auto;
930
+ flex: 1 1 auto;
931
+ border-radius: 0.125rem 0.125rem 0 0;
932
+ background-color: var(--block-view-bg-color);
933
+ border-color: var(--global-border-color);
934
+
935
+ &.datagrid-opened-info {
936
+ border-radius: 0.125rem 0 0 0;
937
+ overflow-x: hidden;
938
+ }
939
+
940
+ .datagrid-table-wrapper {
941
+ min-width: max-content;
942
+ display: flex;
943
+ flex: 1 1 auto;
944
+ min-height: 100%;
945
+
946
+ .datagrid-table {
947
+ @include flex($dir: column);
948
+ flex: 1 1 auto;
949
+ align-content: flex-start;
950
+ position: relative;
951
+
952
+ .datagrid-header {
953
+ position: sticky;
954
+ top: 0;
955
+ z-index: 501;
956
+ width: auto;
957
+
958
+ .datagrid-row-scrollable {
959
+ flex-direction: row;
960
+
961
+ .datagrid-column-separator {
962
+ user-select: none;
963
+
964
+ &::after {
965
+ cursor: col-resize;
966
+ }
967
+ &.double-arrow-separator .datagrid-column-handle {
968
+ cursor: unset;
969
+ }
970
+ &.double-arrow-separator::after {
971
+ cursor: unset;
972
+ }
973
+ }
974
+ }
975
+
976
+ .datagrid-row {
977
+ color: var(--table-color);
978
+ background-color: var(--block-view-bg-color);
979
+
980
+ &.datagrid-selected {
981
+ background-color: var(--row-selected-bg-color);
982
+ border-bottom: 1px solid var(--global-border-color);
983
+
984
+ & :deep(*) {
985
+ color: var(--table-active-color);
986
+ }
987
+ }
988
+
989
+ .datagrid-column {
990
+ background-color: var(--block-view-bg-color);
991
+ border-bottom: 1px solid var(--global-border-color);
992
+ padding: 6px 12px 5px;
993
+ position: relative;
994
+ //flex: 1 1 auto;
995
+
996
+ &:last-child {
997
+ flex: 1 1 auto;
998
+ }
999
+
1000
+ &.datagrid-toggle-block {
1001
+ width: 38px;
1002
+ min-width: 38px;
1003
+
1004
+ .datagrid-column-separator {
1005
+ cursor: default;
1006
+
1007
+ &::after {
1008
+ cursor: default;
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ .datagrid-column-title {
1015
+ align-self: auto;
1016
+ color: var(--table-color);
1017
+ background-color: var(--block-view-bg-color);
1018
+ border-bottom-color: var(--global-border-color);
1019
+ font-size: 11px;
1020
+ @include flex($align: center);
1021
+ display: flex;
1022
+ flex-direction: row;
1023
+ justify-content: flex-start;
1024
+ align-items: center;
1025
+ //line-height: 24px;
1026
+ line-height: normal;
1027
+
1028
+ .title-column {
1029
+ font-weight: bold;
1030
+ font-size: 11px;
1031
+ overflow: hidden;
1032
+ text-wrap: none;
1033
+ text-overflow: ellipsis;
1034
+ }
1035
+
1036
+ .sort-arrow-wrap {
1037
+ margin-left: auto;
1038
+ height: 14px;
1039
+ width: 14px;
1040
+ min-height: 14px;
1041
+ min-width: 14px;
1042
+ vertical-align: middle;
1043
+
1044
+ .sort-arrow {
1045
+ &.down {
1046
+ transform: rotate(180deg);
1047
+ }
1048
+ }
1049
+ }
1050
+ .title-icon {
1051
+ width: 24px;
1052
+ height: 24px;
1053
+ }
1054
+ }
1055
+
1056
+ .datagrid-filter-toggle {
1057
+ background: unset;
1058
+ margin-top: -4px;
1059
+ align-self: center;
1060
+
1061
+ .filter-icon {
1062
+ width: 16px;
1063
+ height: 16px;
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ .datagrid-placeholder-container {
1070
+ .datagrid-placeholder.datagrid-empty {
1071
+ background: var(--block-view-bg-color);
1072
+ }
1073
+ span {
1074
+ font-size: 16px;
1075
+ color: var(--global-font-color4);
1076
+ }
1077
+ }
1078
+
1079
+ .datagrid-row {
1080
+ width: 100%;
1081
+ min-width: max-content;
1082
+ border-top: none;
1083
+ color: var(--table-color);
1084
+ background-color: var(--block-view-bg-color);
1085
+ border-bottom: 1px solid var(--global-border-color);
1086
+
1087
+ &.datagrid-selected {
1088
+ & :deep(*) {
1089
+ color: var(--table-active-color);
1090
+ }
1091
+
1092
+ .datagrid-cell {
1093
+ background-color: var(--row-selected-bg-color);
1094
+ border-bottom: 1px solid var(--global-border-color);
1095
+ position: relative;
1096
+ line-height: 1rem;
1097
+ }
1098
+
1099
+ .datagrid-row-detail {
1100
+ background-color: var(--row-selected-bg-color);
1101
+ border-bottom: 1px solid var(--global-border-color);
1102
+ }
1103
+ }
1104
+
1105
+ .datagrid-cell {
1106
+ min-width: 38px;
1107
+ min-height: 28px;
1108
+ padding: 2px 12px 3px 12px;
1109
+ position: relative;
1110
+
1111
+ &:has(.double-arrow-wrap) {
1112
+ padding-left: 4px;
1113
+ }
1114
+
1115
+ &:has(.double-arrow-wrap)::after {
1116
+ content: '';
1117
+ width: 0.5px;
1118
+ height: calc(100% - 6px);
1119
+ position: absolute;
1120
+ right: 2px;
1121
+ top: 3px;
1122
+ background-color: var(--global-border-color);
1123
+ }
1124
+
1125
+ &:last-child {
1126
+ flex: 1 1 auto;
1127
+ }
1128
+
1129
+ button {
1130
+ height: 0.9rem;
1131
+ width: 0.9rem;
1132
+
1133
+ .arrow-icon {
1134
+ width: 16px;
1135
+ height: 16px;
1136
+ transform: rotate(90deg);
1137
+ }
1138
+ }
1139
+
1140
+ .datagrid-cell-icon {
1141
+ display: inline-block;
1142
+ width: 16px;
1143
+ height: 16px;
1144
+ }
1145
+
1146
+ .datagrid-expandable-caret-button {
1147
+ cursor: pointer;
1148
+ width: 100%;
1149
+
1150
+ .datagrid-expandable-caret-icon {
1151
+ width: 16px;
1152
+ height: 16px;
1153
+ transform: rotate(90deg);
1154
+ fill: var(--table-caret-color);
1155
+
1156
+ &.toggle {
1157
+ transform: rotate(180deg);
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+
1163
+ &.datagrid-selected-info {
1164
+ width: 100%;
1165
+ min-width: max-content;
1166
+ border-top: none;
1167
+ color: var(--table-color);
1168
+ background-color: var(--block-view-bg-color);
1169
+
1170
+ .datagrid-cell {
1171
+ position: relative;
1172
+
1173
+ &.hidden-text-triangle::after {
1174
+ content: '';
1175
+ position: absolute;
1176
+ transform: rotate(225deg);
1177
+ border-bottom: 15px solid transparent;
1178
+ border-right: 15px solid var(--global-bg-color);
1179
+ bottom: 26%;
1180
+ right: -16px;
1181
+ z-index: 1000;
1182
+ }
1183
+
1184
+ &.hidden-text-triangle::before {
1185
+ content: '';
1186
+ transform: rotate(-135deg);
1187
+ position: absolute;
1188
+ border-bottom: 16px solid transparent;
1189
+ border-right: 16px solid var(--global-border-color);
1190
+ bottom: 23%;
1191
+ right: -17px;
1192
+ z-index: 999;
1193
+ }
1194
+ }
1195
+ }
1196
+ }
1197
+
1198
+ .datagrid-row-sticky {
1199
+ @include flex($w: nowrap);
1200
+ position: sticky;
1201
+ left: 0;
1202
+ z-index: 500;
1203
+ background-color: var(--block-view-bg-color);
1204
+
1205
+ .datagrid-column {
1206
+ color: var(--table-color);
1207
+ background-color: var(--block-view-bg-color);
1208
+ border-bottom-color: var(--global-border-color);
1209
+ text-align: left;
1210
+ display: flex;
1211
+ flex: 1 1 auto;
1212
+ vertical-align: top;
1213
+ border: none;
1214
+
1215
+ &.datagrid-expandable-caret {
1216
+ .datagrid-column-separator {
1217
+ display: block;
1218
+
1219
+ &::after {
1220
+ top: 0;
1221
+ height: 18px;
1222
+ }
1223
+ }
1224
+
1225
+ .radio-btn {
1226
+ width: 16px;
1227
+ height: 16px;
1228
+ }
1229
+ }
1230
+
1231
+ .datagrid-column-separator {
1232
+ position: relative;
1233
+ //left: 0.6rem;
1234
+ flex: 0 0 auto;
1235
+ width: 0.05rem;
1236
+ order: 100;
1237
+ margin-left: auto;
1238
+ height: 100%;
1239
+ }
1240
+ }
1241
+
1242
+ .datagrid-fixed-column-without-separator {
1243
+ background-color: var(--block-view-bg-color);
1244
+
1245
+ &::after {
1246
+ background-color: transparent;
1247
+ }
1248
+ }
1249
+
1250
+ .datagrid-fixed-column {
1251
+ background-color: var(--block-view-bg-color);
1252
+
1253
+ &::after {
1254
+ content: '';
1255
+ width: 0.05rem;
1256
+ height: calc(100% - 0.5rem);
1257
+ position: absolute;
1258
+ right: 0;
1259
+ top: 0.25rem;
1260
+ background-color: var(--global-border-color);
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ .datagrid-row-scrollable {
1266
+ @include flex($ff: row nowrap);
1267
+ flex: 1 1 auto;
1268
+
1269
+ .datagrid-scrolling-cells {
1270
+ @include flex($ff: row nowrap);
1271
+ flex: 1 1 auto;
1272
+ }
1273
+ }
1274
+ .datagrid-body-row {
1275
+ .datagrid-row-scrollable {
1276
+ @include flex($ff: column nowrap);
1277
+
1278
+ .datagrid-row-detail {
1279
+ flex: 1;
1280
+ }
1281
+ }
1282
+
1283
+ &:not(.datagrid-selected):hover {
1284
+ border-bottom: 1px solid var(--table-hover-row-border-color);
1285
+ background-color: var(--table-hover-row-background-color);
1286
+ color: var(--table-hover-row-color);
1287
+
1288
+ .datagrid-fixed-column {
1289
+ background-color: var(--table-hover-row-background-color);
1290
+ }
1291
+ .datagrid-cell {
1292
+ background-color: var(--table-hover-row-background-color);
1293
+ }
1294
+ }
1295
+ }
1296
+ }
1297
+ }
1298
+ }
1299
+
1300
+ .datagrid-footer {
1301
+ flex: 0 0 auto;
1302
+ @include flex($ff: row nowrap, $just: space-between, $align: stretch);
1303
+ font-size: 0.55rem;
1304
+ background-color: var(--block-view-bg-color);
1305
+ border: 0.05rem solid var(--global-border-color);
1306
+ border-top: none;
1307
+ border-radius: 0 0 0.15rem 0.15rem;
1308
+ padding: 0 0.6rem;
1309
+ height: 28px;
1310
+
1311
+ &.datagrid-footer-opened-info {
1312
+ border-radius: 0 0 0 0.15rem;
1313
+ flex-direction: row-reverse;
1314
+ }
1315
+
1316
+ .clr-form-control-disabled {
1317
+ opacity: 1;
1318
+
1319
+ .clr-checkbox-wrapper {
1320
+ label {
1321
+ color: #8c8c8c;
1322
+ margin-right: 9px;
1323
+ padding-left: 22px;
1324
+
1325
+ &:before {
1326
+ position: absolute;
1327
+ top: 4px;
1328
+ left: 0;
1329
+ content: '';
1330
+ display: inline-block;
1331
+ height: 16px;
1332
+ width: 16px;
1333
+ border: none;
1334
+ border-radius: 3px;
1335
+ background-color: var(--table-checkbox-color);
1336
+ }
1337
+
1338
+ &:after {
1339
+ position: absolute;
1340
+ content: '';
1341
+ display: inline-block;
1342
+ height: 5px;
1343
+ width: 8px;
1344
+ border-left: 2px solid #fff;
1345
+ border-bottom: 2px solid #fff;
1346
+ top: 4px;
1347
+ left: 4px;
1348
+ transform: translateY(4px) rotate(-45deg);
1349
+ }
1350
+ }
1351
+ }
1352
+ }
1353
+ }
1354
+
1355
+ .datagrid-spinner {
1356
+ top: 0.6rem;
1357
+ height: calc(100% - 0.6rem);
1358
+ }
1359
+
1360
+ .datagrid-footer-description {
1361
+ flex: 1 1 auto;
1362
+
1363
+ :deep(svg) {
1364
+ fill: var(--table-switch-color);
1365
+ }
1366
+ }
1367
+
1368
+ .clr-radio-wrapper,
1369
+ .clr-checkbox-wrapper {
1370
+ height: 18px;
1371
+ }
1372
+
1373
+ .clr-radio-wrapper label,
1374
+ .clr-checkbox-wrapper label {
1375
+ padding-left: 16px;
1376
+ }
1377
+ }
1378
+ }
1379
+ //.vertical-divider {
1380
+ // width: 1px;
1381
+ // height: 22px;
1382
+ // background: #bbb;
1383
+ // margin: 2px 2px 0 -5px;
1384
+ //}
1385
+
1386
+ .hidden-features {
1387
+ padding: 9px 14px;
1388
+ background-color: var(--global-bg-color);
1389
+ color: var(--global-font-color2);
1390
+ border: 1px solid var(--global-border-color);
1391
+ border-left: none;
1392
+ overflow-y: auto;
1393
+ overflow-x: hidden;
1394
+ width: 65%;
1395
+
1396
+ .header {
1397
+ display: flex;
1398
+ align-items: center;
1399
+ justify-content: space-between;
1400
+ background-color: inherit;
1401
+
1402
+ h3 {
1403
+ font-size: 1rem;
1404
+ color: var(--global-font-color2) !important;
1405
+ line-height: 1.7rem;
1406
+ }
1407
+ button {
1408
+ line-height: 24px;
1409
+
1410
+ .close-icon {
1411
+ margin-left: 10px;
1412
+ width: 24px;
1413
+ height: 24px;
1414
+ }
1415
+ }
1416
+ }
1417
+ }
1418
+ .double-arrow-width {
1419
+ width: 25px;
1420
+ }
1421
+ .double-arrow-wrap {
1422
+ width: 30px;
1423
+ height: 20px;
1424
+ padding-top: 1px;
1425
+ padding-left: 5px;
1426
+ cursor: pointer;
1427
+ background-color: transparent;
1428
+
1429
+ .double-arrow {
1430
+ background-color: transparent;
1431
+ transform: rotate(90deg);
1432
+ width: 16px;
1433
+ height: 16px;
1434
+ }
1435
+
1436
+ .double-arrow.arrow-selected {
1437
+ transform: rotate(-90deg) !important;
1438
+ background-color: #0079b8;
1439
+ }
1440
+ }
1441
+ .double-arrow-wrap.arrow-selected {
1442
+ background-color: #0079b8;
1443
+ border-radius: 3px;
1444
+ }
1445
+ </style>
1446
+
1447
+ <style>
1448
+ :root {
1449
+ --table-color: #565656;
1450
+ --table-active-color: #fff;
1451
+ --table-checkbox-color: #454545;
1452
+ --table-caret-color: #454545;
1453
+ --table-switch-color: #575757;
1454
+ --table-hover-row-border-color: #666;
1455
+ --table-hover-row-background-color: #e8e8e8;
1456
+ --table-hover-row-color: #454545;
1457
+ --table-spinner-bg-color: #ffffff99;
1458
+ }
1459
+
1460
+ :root.dark-theme {
1461
+ --table-color: #fff;
1462
+ --table-active-color: #565656;
1463
+ --table-checkbox-color: #49aeda;
1464
+ --table-caret-color: #ffffff;
1465
+ --table-switch-color: #b3b3b3;
1466
+ --table-hover-row-border-color: #fff;
1467
+ --table-hover-row-background-color: #324f61;
1468
+ --table-hover-row-color: #e9ecef;
1469
+ --table-spinner-bg-color: rgba(0, 0, 0, 0.5);
1470
+ }
1471
+ </style>