bfg-common 1.1.39 → 1.1.40

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,1476 @@
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="columnKeys"
358
- v-model:column-keys="columnKeys"
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 columnKeys = 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 (!columnKeys.value) {
672
- return props.headItems
673
- }
674
-
675
- return props.headItems.filter((item, key) => {
676
- return columnKeys.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 (columnKeys.value) {
687
- items = items.map((arr) => {
688
- arr = arr.filter((item, key) => {
689
- return columnKeys.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">
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>