bfg-common 1.2.176 → 1.2.178

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