bfg-common 1.4.409 → 1.4.411

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