@it-enterprise/forcebpm-ui-kit 1.0.2-beta.25 → 1.0.2-beta.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -25,7 +25,7 @@ export { default as FUserRoles } from './src/FUserRoles.vue'
25
25
  export { default as FDatePicker } from './src/f-date-picker/FDatePicker.vue'
26
26
  export { default as FMenuDatePicker } from './src/f-date-picker/FMenuDatePicker.vue'
27
27
  export { default as FTextFieldDate } from './src/f-date-picker/FTextFieldDate.vue'
28
- // export { default as FUserGroupPicker } from './src/f-user-group-picker/FUserGroupPicker.vue'
28
+ export { default as FUserGroupPicker } from './src/FUserGroupPicker.vue'
29
29
 
30
30
  // Utils
31
31
  export { hexToRGBA, createRadialGradient } from './src/utils/color.js'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@it-enterprise/forcebpm-ui-kit",
4
- "version": "1.0.2-beta.25",
4
+ "version": "1.0.2-beta.26",
5
5
  "description": "FBPM UI Kit",
6
6
  "author": "it-enterprise",
7
7
  "license": "MIT",
package/src/FDialog.vue CHANGED
@@ -90,6 +90,8 @@ watch(modelValue, val => {
90
90
  if (!val) {
91
91
  search.value = null
92
92
  emit('close')
93
+ } else {
94
+ emit('open')
93
95
  }
94
96
  })
95
97
  </script>
@@ -121,6 +121,9 @@ const keydownEsc = () => {
121
121
  height: 30px;
122
122
  width: 30px;
123
123
  transition: all 0.2s ease-in-out;
124
+ .v-input__control {
125
+ background-color: transparent;
126
+ }
124
127
  &.v-input--focused {
125
128
  .v-field__prepend-inner {
126
129
  margin-left: 0px;
@@ -0,0 +1,630 @@
1
+ <script setup>
2
+ // FUserGroupPicker
3
+ import { ref, computed, watch } from 'vue'
4
+ import { useI18n } from 'vue-i18n'
5
+ import { useTheme } from 'vuetify'
6
+ import { colorCollection, hexToRGBA } from './utils/color'
7
+
8
+ const { t } = useI18n()
9
+ const theme = useTheme()
10
+
11
+ const props = defineProps({
12
+ items: {
13
+ type: Array,
14
+ required: true,
15
+ validator: (value, props) => {
16
+ const isValid = value.every(item => {
17
+ const hasIdentityId =
18
+ item[props.itemValue] !== undefined && (typeof item[props.itemValue] === 'string' || typeof item[props.itemValue] === 'number')
19
+ const hasName = typeof item.name === 'string'
20
+ const isUserOrGroup = item.type === 'USER' || item.type === 'GROUP' || item.groupId !== undefined
21
+ return hasIdentityId && hasName && isUserOrGroup
22
+ })
23
+ if (!isValid) {
24
+ console.warn('FUserGroupPicker: Each item should have identityId (string or number), name (string) and type "USER" or "GROUP" (or groupId)')
25
+ }
26
+ return isValid
27
+ }
28
+ },
29
+ itemValue: {
30
+ type: String,
31
+ default: 'identityId',
32
+ validator: (value, props) => {
33
+ const hasGroups = props.items?.some(item => item.type === 'GROUP' || item.groupId)
34
+ if (hasGroups && value === 'userId') {
35
+ console.warn('FUserGroupPicker: itemValue cannot be "userId" when items contain groups. Use "identityId" instead.')
36
+ return false
37
+ }
38
+ if (!['identityId', 'userId'].includes(value)) {
39
+ console.warn('FUserGroupPicker: itemValue should be one of identityId, userId')
40
+ return false
41
+ }
42
+ return true
43
+ }
44
+ },
45
+ // todo when there will be a connection in ForceBPMTasks
46
+ executorLists: {
47
+ type: Array,
48
+ default: () => []
49
+ },
50
+ multiple: {
51
+ type: Boolean,
52
+ default: false
53
+ },
54
+ returnObject: {
55
+ type: Boolean,
56
+ default: false
57
+ },
58
+ groupSelection: {
59
+ type: Boolean,
60
+ default: false
61
+ }
62
+ })
63
+
64
+ const state = defineModel('state', {
65
+ type: Boolean,
66
+ default: false
67
+ })
68
+
69
+ const modelValue = defineModel('modelValue', {
70
+ type: [Array, Object, String, Number],
71
+ default: () => []
72
+ })
73
+
74
+ const search = ref('')
75
+ const selectedMenuId = ref(null)
76
+ const tab = ref('USERS_BY_GROUP')
77
+ const calculatedHeight = Math.floor(window.innerHeight * 0.1) // 10vh rounded down
78
+
79
+ // Tabs for header
80
+ const tabs = computed(() => {
81
+ const baseTabs = [
82
+ { value: 'USERS_BY_GROUP', text: t('user.usersByGroup') },
83
+ { value: 'USERS_BY_POSITION', text: t('user.usersByPosition') }
84
+ ]
85
+ if (props.executorLists.length > 0) {
86
+ baseTabs.push({ value: 'EXECUTOR_LISTS', text: t('user.executorLists') })
87
+ }
88
+ return baseTabs
89
+ })
90
+
91
+ // Map for quick access by identityId - O(1)
92
+ const itemsMap = computed(() => {
93
+ const map = new Map()
94
+ for (const item of props.items) {
95
+ map.set(item.identityId, item)
96
+ }
97
+ return map
98
+ })
99
+
100
+ // Map userId -> identityId for executor lists lookup
101
+ const userIdMap = computed(() => {
102
+ const map = new Map()
103
+ for (const item of props.items) {
104
+ if (item.userId !== undefined) {
105
+ map.set(item.userId, item.identityId)
106
+ }
107
+ }
108
+ return map
109
+ })
110
+
111
+ // Unique groups from items (extracted from item.groups)
112
+ const uniqueGroups = computed(() => {
113
+ const groupsMap = new Map()
114
+ for (const item of props.items) {
115
+ if (item.groups?.length) {
116
+ for (const group of item.groups) {
117
+ if (!groupsMap.has(group.identityId)) {
118
+ groupsMap.set(group.identityId, { ...group, id: group.identityId })
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return Array.from(groupsMap.values())
124
+ })
125
+
126
+ // Unique positions from items
127
+ const uniquePositions = computed(() => {
128
+ const positionsMap = new Map()
129
+ for (const item of props.items) {
130
+ if (item.positionCode && !positionsMap.has(item.positionCode)) {
131
+ positionsMap.set(item.positionCode, {
132
+ id: item.positionCode,
133
+ title: item.positionTitle
134
+ })
135
+ }
136
+ }
137
+ return Array.from(positionsMap.values())
138
+ })
139
+
140
+ // Index: groupId -> Set<itemValue> for quick filtering
141
+ const groupIndex = computed(() => {
142
+ const index = new Map()
143
+ for (const item of props.items) {
144
+ if (item.groups?.length) {
145
+ for (const group of item.groups) {
146
+ if (!index.has(group.identityId)) {
147
+ index.set(group.identityId, new Set())
148
+ }
149
+ index.get(group.identityId).add(item.identityId)
150
+ }
151
+ }
152
+ }
153
+ return index
154
+ })
155
+
156
+ // Index: positionCode -> Set<itemValue>
157
+ const positionIndex = computed(() => {
158
+ const index = new Map()
159
+ for (const item of props.items) {
160
+ if (item.positionCode) {
161
+ if (!index.has(item.positionCode)) {
162
+ index.set(item.positionCode, new Set())
163
+ }
164
+ index.get(item.positionCode).add(item.identityId)
165
+ }
166
+ }
167
+ return index
168
+ })
169
+
170
+ // Index: executorListId -> Set<identityId>
171
+ const executorIndex = computed(() => {
172
+ const index = new Map()
173
+ for (const list of props.executorLists) {
174
+ const userIds = new Set()
175
+ for (const userId of list.usersIds || []) {
176
+ const identityId = userIdMap.value.get(userId)
177
+ if (identityId) {
178
+ userIds.add(identityId)
179
+ }
180
+ }
181
+ index.set(list.id, userIds)
182
+ }
183
+ return index
184
+ })
185
+
186
+ // Set selected items - O(1) for checking
187
+ const selectedIds = ref(new Set())
188
+
189
+ // Set selected groups (when groupSelection = true)
190
+ const selectedGroupIds = ref(new Set())
191
+
192
+ const menuItems = computed(() => {
193
+ const color = idx => {
194
+ return hexToRGBA(colorCollection[theme.global.name.value === 'light' ? 'light' : 'dark'][idx % colorCollection.light.length]).slice(5, -3) // rgba(255, 0, 0, 0.2) -> 255, 0, 0
195
+ }
196
+
197
+ switch (tab.value) {
198
+ case 'USERS_BY_GROUP':
199
+ return uniqueGroups.value.map((g, idx) => ({
200
+ id: g.identityId,
201
+ name: g.name,
202
+ color: color(idx)
203
+ }))
204
+ case 'USERS_BY_POSITION':
205
+ return uniquePositions.value.map((p, idx) => ({
206
+ id: p.id,
207
+ name: p.title,
208
+ color: color(idx)
209
+ }))
210
+ case 'EXECUTOR_LISTS':
211
+ return props.executorLists.map((list, idx) => ({
212
+ id: list.id,
213
+ name: list.name,
214
+ color: color(idx)
215
+ }))
216
+ default:
217
+ return []
218
+ }
219
+ })
220
+
221
+ const titleByMenu = computed(() => {
222
+ const currentMenu = menuItems.value.find(i => i.id === selectedMenuId.value)
223
+ return currentMenu?.name || currentMenu?.id || (props.groupSelection ? t('user.allUsersAndGroups') : t('user.allUsers'))
224
+ })
225
+
226
+ const filteredItems = computed(() => {
227
+ let resultIds = null
228
+
229
+ // Step 1: Get relevant item IDs based on selected menu
230
+ if (selectedMenuId.value) {
231
+ switch (tab.value) {
232
+ case 'USERS_BY_GROUP':
233
+ resultIds = groupIndex.value.get(selectedMenuId.value)
234
+ break
235
+ case 'USERS_BY_POSITION':
236
+ resultIds = positionIndex.value.get(selectedMenuId.value)
237
+ break
238
+ case 'EXECUTOR_LISTS':
239
+ resultIds = executorIndex.value.get(selectedMenuId.value)
240
+ break
241
+ }
242
+ }
243
+
244
+ // Step 2: Map IDs to items (if resultIds is null, we take all items)
245
+ let result
246
+ if (resultIds) {
247
+ result = Array.from(resultIds, id => itemsMap.value.get(id)).filter(Boolean)
248
+ } else {
249
+ result = props.items
250
+ }
251
+
252
+ // Step 3: Apply search filter
253
+ if (search.value) {
254
+ result = result.filter(item => item.name?.toLowerCase().includes(search.value.toLowerCase()))
255
+ }
256
+
257
+ // Step 4: If groupSelection is enabled, mark items as disabled if they belong to a selected group
258
+ if (props.groupSelection && selectedGroupIds.value.size > 0) {
259
+ result = result.map(item => {
260
+ const isDisabledByGroup = item.groups?.some(g => selectedGroupIds.value.has(g.identityId))
261
+ return isDisabledByGroup ? { ...item, disabled: true } : item
262
+ })
263
+ }
264
+
265
+ return result
266
+ })
267
+
268
+ // Selected items (groups + users)
269
+ const selectedItems = computed(() => {
270
+ // Users — filter out those covered by a selected group
271
+ const users = Array.from(selectedIds.value, id => itemsMap.value.get(id))
272
+ .filter(Boolean)
273
+ .filter(user => {
274
+ if (props.groupSelection && selectedGroupIds.value.size > 0 && user.groups?.length) {
275
+ return !user.groups.some(g => selectedGroupIds.value.has(g.identityId))
276
+ }
277
+ return true
278
+ })
279
+
280
+ // Groups — with selectAll flag for removeSelected logic
281
+ const groups = Array.from(selectedGroupIds.value, groupId => {
282
+ const group = uniqueGroups.value.find(g => g.identityId === groupId)
283
+ if (group) {
284
+ return {
285
+ ...group,
286
+ type: 'GROUP',
287
+ selectAll: true
288
+ }
289
+ }
290
+ return null
291
+ }).filter(Boolean)
292
+
293
+ const result = [...groups, ...users]
294
+
295
+ if (search.value) {
296
+ return result.filter(item => item.name?.toLowerCase().includes(search.value.toLowerCase()))
297
+ }
298
+
299
+ return result
300
+ })
301
+
302
+ const isSelected = item => {
303
+ // Check if user is individually selected
304
+ if (selectedIds.value.has(item.identityId)) return true
305
+
306
+ // Check if user is selected via group
307
+ if (props.groupSelection && item.groups?.some(g => selectedGroupIds.value.has(g.identityId))) {
308
+ return true
309
+ }
310
+
311
+ return false
312
+ }
313
+
314
+ const toggleSelect = item => {
315
+ if (item.disabled) return
316
+
317
+ const id = item.identityId
318
+
319
+ if (props.multiple) {
320
+ if (selectedIds.value.has(id)) {
321
+ selectedIds.value.delete(id)
322
+ } else {
323
+ selectedIds.value.add(id)
324
+ }
325
+ // Trigger reactivity
326
+ selectedIds.value = new Set(selectedIds.value)
327
+ } else {
328
+ // Single selection - immediately close picker
329
+ selectedIds.value = new Set([id])
330
+ selectHandler()
331
+ }
332
+ }
333
+
334
+ const removeSelected = item => {
335
+ if (item.disabled) return
336
+
337
+ if (item.selectAll) {
338
+ selectedGroupIds.value.delete(item.identityId)
339
+ selectedGroupIds.value = new Set(selectedGroupIds.value)
340
+ } else {
341
+ selectedIds.value.delete(item.identityId)
342
+ selectedIds.value = new Set(selectedIds.value)
343
+ }
344
+ }
345
+
346
+ const toggleAllInMenu = () => {
347
+ if (!selectedMenuId.value) return
348
+
349
+ if (props.groupSelection && tab.value === 'USERS_BY_GROUP') {
350
+ if (selectedGroupIds.value.has(selectedMenuId.value)) {
351
+ // Deselect group
352
+ selectedGroupIds.value.delete(selectedMenuId.value)
353
+ selectedGroupIds.value = new Set(selectedGroupIds.value)
354
+ } else {
355
+ // Select group
356
+ selectedGroupIds.value.add(selectedMenuId.value)
357
+ selectedGroupIds.value = new Set(selectedGroupIds.value)
358
+
359
+ // Remove individually selected users from this group
360
+ const groupUserIds = groupIndex.value.get(selectedMenuId.value)
361
+ if (groupUserIds) {
362
+ for (const userId of groupUserIds) {
363
+ selectedIds.value.delete(userId)
364
+ }
365
+ selectedIds.value = new Set(selectedIds.value)
366
+ }
367
+ }
368
+ } else {
369
+ let ids
370
+ switch (tab.value) {
371
+ case 'USERS_BY_GROUP':
372
+ ids = groupIndex.value.get(selectedMenuId.value)
373
+ break
374
+ case 'USERS_BY_POSITION':
375
+ ids = positionIndex.value.get(selectedMenuId.value)
376
+ break
377
+ case 'EXECUTOR_LISTS':
378
+ ids = executorIndex.value.get(selectedMenuId.value)
379
+ break
380
+ }
381
+
382
+ if (!ids) return
383
+
384
+ if (isAllSelectedInMenu.value) {
385
+ // Deselect all
386
+ for (const id of ids) {
387
+ selectedIds.value.delete(id)
388
+ }
389
+ } else {
390
+ // Select all (skip disabled)
391
+ for (const id of ids) {
392
+ const item = itemsMap.value.get(id)
393
+ if (item && !item.disabled) {
394
+ selectedIds.value.add(id)
395
+ }
396
+ }
397
+ }
398
+ selectedIds.value = new Set(selectedIds.value)
399
+ }
400
+ }
401
+
402
+ // Check if all items in current menu are selected (taking into account groupSelection logic)
403
+ const isAllSelectedInMenu = computed(() => {
404
+ if (!selectedMenuId.value) return false
405
+
406
+ if (props.groupSelection && tab.value === 'USERS_BY_GROUP') {
407
+ return selectedGroupIds.value.has(selectedMenuId.value)
408
+ }
409
+
410
+ let ids
411
+ switch (tab.value) {
412
+ case 'USERS_BY_GROUP':
413
+ ids = groupIndex.value.get(selectedMenuId.value)
414
+ break
415
+ case 'USERS_BY_POSITION':
416
+ ids = positionIndex.value.get(selectedMenuId.value)
417
+ break
418
+ case 'EXECUTOR_LISTS':
419
+ ids = executorIndex.value.get(selectedMenuId.value)
420
+ break
421
+ }
422
+
423
+ if (!ids || ids.size === 0) return false
424
+
425
+ for (const id of ids) {
426
+ if (!selectedIds.value.has(id)) return false
427
+ }
428
+ return true
429
+ })
430
+
431
+ const selectMenuItem = id => {
432
+ selectedMenuId.value = selectedMenuId.value === id ? null : id
433
+ }
434
+
435
+ const open = () => {
436
+ state.value = true
437
+ }
438
+
439
+ const close = () => {
440
+ state.value = false
441
+ search.value = ''
442
+ selectedMenuId.value = null
443
+ }
444
+
445
+ const selectHandler = () => {
446
+ if (props.returnObject) {
447
+ modelValue.value = selectedItems.value
448
+ } else {
449
+ modelValue.value = selectedItems.value.map(item => item[props.itemValue])
450
+ }
451
+
452
+ state.value = false
453
+ }
454
+
455
+ // Reset menu selection when switching tabs
456
+ watch(tab, () => {
457
+ selectedMenuId.value = null
458
+ })
459
+
460
+ // Sync with external modelValue changes
461
+ watch(state, newState => {
462
+ if (newState && modelValue.value) {
463
+ // Reset
464
+ selectedIds.value = new Set()
465
+ selectedGroupIds.value = new Set()
466
+
467
+ // Populate from modelValue
468
+ const values = Array.isArray(modelValue.value) ? modelValue.value : [modelValue.value]
469
+ for (const val of values) {
470
+ if (props.returnObject) {
471
+ if (val.type === 'GROUP' || val.groupId) {
472
+ selectedGroupIds.value.add(val.identityId)
473
+ } else {
474
+ selectedIds.value.add(val.identityId)
475
+ }
476
+ } else {
477
+ // Find item by itemValue field
478
+ let found = false
479
+ for (const item of props.items) {
480
+ if (item[props.itemValue] === val) {
481
+ if (item.type === 'GROUP' || item.groupId) {
482
+ selectedGroupIds.value.add(item.identityId)
483
+ } else {
484
+ selectedIds.value.add(item.identityId)
485
+ }
486
+ found = true
487
+ break
488
+ }
489
+ }
490
+ // If not found in items, try uniqueGroups
491
+ if (!found && uniqueGroups.value.some(g => g[props.itemValue] === val)) {
492
+ const group = uniqueGroups.value.find(g => g[props.itemValue] === val)
493
+ selectedGroupIds.value.add(group.identityId)
494
+ }
495
+ }
496
+ }
497
+ }
498
+ })
499
+ </script>
500
+ <template>
501
+ <FDialog
502
+ v-model:search="search"
503
+ :model-value="state"
504
+ name="userGroupPicker"
505
+ width="80vw"
506
+ max-width="900px"
507
+ persistent
508
+ no-space
509
+ is-search
510
+ :title="$t('user.executors')"
511
+ @open="open"
512
+ @close="close"
513
+ >
514
+ <div class="user-group-picker">
515
+ <!-- Header tabs -->
516
+ <v-tabs v-model="tab" grow class="mb-2">
517
+ <v-tab v-for="tabItem in tabs" :key="tabItem.value" :value="tabItem.value" height="35px" :text="tabItem.text" />
518
+ </v-tabs>
519
+
520
+ <!-- Filter menu -->
521
+ <v-tabs-window v-model="tab" class="mx-5">
522
+ <v-tabs-window-item
523
+ v-for="tabItem in tabs"
524
+ :key="tabItem.value"
525
+ :value="tabItem.value"
526
+ :style="{ height: `${calculatedHeight}px` }"
527
+ class="f-scrollbar-y"
528
+ >
529
+ <v-chip-group selected-class="active" class="pa-0" column>
530
+ <v-chip
531
+ v-for="item in menuItems"
532
+ :key="item.id"
533
+ :style="`--f-user-group-chip-color: ${item.color}`"
534
+ variant="flat"
535
+ class="user-group-picker-chip"
536
+ @click="selectMenuItem(item.id)"
537
+ >
538
+ {{ item.name || item.id }}
539
+ </v-chip>
540
+ </v-chip-group>
541
+ </v-tabs-window-item>
542
+ </v-tabs-window>
543
+
544
+ <div class="d-flex flex-column mx-5 mt-5">
545
+ <!-- Title -->
546
+ <span class="mb-2 ml-1 font-weight-semibold fs-11">{{ titleByMenu }}</span>
547
+
548
+ <!-- Select all users -->
549
+ <v-checkbox
550
+ v-if="selectedMenuId && multiple && (filteredItems.length || selectedItems.length)"
551
+ class="mb-2 ml-1"
552
+ :model-value="isAllSelectedInMenu"
553
+ :label="props.groupSelection && tab === 'USERS_BY_GROUP' ? $t('user.selectGroup') : $t('user.selectAllFromList')"
554
+ @update:model-value="toggleAllInMenu"
555
+ />
556
+ </div>
557
+
558
+ <div v-if="filteredItems.length || selectedItems.length" class="d-flex justify-space-between mx-5">
559
+ <!-- Available items -->
560
+ <v-virtual-scroll :items="filteredItems" height="40vh" item-key="identityId" item-height="40" max-width="45%" class="pr-2">
561
+ <template #default="{ item }">
562
+ <div class="d-flex align-center justify-start picker-list-item" :class="{ 'is-disabled': item.disabled }" @click="toggleSelect(item)">
563
+ <v-checkbox :model-value="isSelected(item)" :disabled="item.disabled" />
564
+ <FAvatar :full-user="item.type === 'USER' ? item : null" :group="item.type === 'GROUP' ? item : null" size="20" class="ml-1 mr-2" />
565
+ <span class="picker-item-name">{{ item.name }}</span>
566
+ </div>
567
+ </template>
568
+ </v-virtual-scroll>
569
+
570
+ <!-- Selected items -->
571
+ <v-virtual-scroll :items="selectedItems" height="40vh" item-key="identityId" item-height="40" max-width="45%" class="pr-2">
572
+ <template #default="{ item }">
573
+ <div class="d-flex align-center justify-start picker-list-item" :class="{ 'is-disabled': item.disabled }" @click="removeSelected(item)">
574
+ <v-checkbox :model-value="true" :disabled="item.disabled" />
575
+ <FAvatar :full-user="item.type === 'USER' ? item : null" :group="item.type === 'GROUP' ? item : null" size="20" class="ml-1 mr-2" />
576
+ <span class="picker-item-name">{{ item.name }}</span>
577
+ </div>
578
+ </template>
579
+ </v-virtual-scroll>
580
+ </div>
581
+
582
+ <FNoData v-else height="40vh" class="mt-5" :is-search="search" />
583
+ </div>
584
+
585
+ <template #actions>
586
+ <v-btn class="mx-auto" width="300" @click="selectHandler">
587
+ {{ $t('buttons.select') }}
588
+ </v-btn>
589
+ </template>
590
+ </FDialog>
591
+ </template>
592
+
593
+ <style lang="scss" scoped>
594
+ .user-group-picker {
595
+ :deep(.v-virtual-scroll__item) {
596
+ padding: 0;
597
+ &:nth-child(odd) {
598
+ background-color: rgb(var(--v-theme-fields));
599
+ }
600
+ .picker-list-item {
601
+ padding: 5px;
602
+ transition: all 0.3s ease-in-out;
603
+ &:not(.is-disabled) {
604
+ cursor: pointer;
605
+ &:hover {
606
+ background-color: rgba(var(--v-theme-primary), 0.2);
607
+ }
608
+ }
609
+ }
610
+ }
611
+ .user-group-picker-chip {
612
+ --v-chip-height: 20px;
613
+ margin: 3px;
614
+ border-radius: 4.8px;
615
+ color: rgb(var(--f-user-group-chip-color));
616
+ background: rgba(var(--f-user-group-chip-color), 0.2);
617
+ border: 1px solid transparent;
618
+ &.active {
619
+ border-color: rgb(var(--f-user-group-chip-color));
620
+ }
621
+ &.v-theme--dark {
622
+ color: rgb(var(--v-theme-title));
623
+ background: rgba(var(--f-user-group-chip-color), 0.7);
624
+ &.active {
625
+ border-color: rgb(var(--v-theme-title));
626
+ }
627
+ }
628
+ }
629
+ }
630
+ </style>
@@ -1,9 +1,10 @@
1
1
  // Override input styles
2
2
  .f-input {
3
3
  border-radius: 5.8px;
4
- background-color: rgb(var(--v-theme-background));
5
4
  color: rgb(var(--v-theme-text));
6
-
5
+ .v-input__control {
6
+ background-color: rgb(var(--v-theme-fields));
7
+ }
7
8
  &:hover {
8
9
  .v-field {
9
10
  .v-label {
@@ -140,6 +141,26 @@
140
141
  }
141
142
  }
142
143
 
144
+ // Autocomplete User
145
+ .f-input.f-autocomplete-user {
146
+ // .v-field {
147
+ // .v-field__outline__end {
148
+ // min-width: calc(100% - 53px);
149
+ // }
150
+ // }
151
+ &.v-input--horizontal .v-input__append {
152
+ margin-inline-start: 0px;
153
+ .f-icon {
154
+ opacity: 1;
155
+ transition: all 0.3s ease-in-out;
156
+ &:hover {
157
+ background: rgb(var(--v-theme-primary)) !important;
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ // Textarea
143
164
  .f-input.v-textarea {
144
165
  .v-field {
145
166
  .v-field__input {
@@ -170,6 +191,7 @@
170
191
  border-radius: 16.8px;
171
192
  font-size: 0.75em;
172
193
  background: rgb(var(--v-theme-line));
194
+ color: rgb(var(--v-theme-subTitle));
173
195
  .v-chip__close {
174
196
  margin-inline-start: 5px;
175
197
  margin-inline-end: 5px;
@@ -242,11 +264,11 @@
242
264
 
243
265
  //Radio
244
266
  .f-radio {
245
- --v-selection-control-size: 16px;
246
267
  &.v-selection-control--inline:not(:last-child) {
247
268
  margin-right: 8px;
248
269
  }
249
270
  .v-selection-control__wrapper {
271
+ --v-selection-control-size: 16px;
250
272
  .v-selection-control__input {
251
273
  &::before {
252
274
  content: none;
@@ -15,7 +15,7 @@
15
15
  height: 1px;
16
16
  width: 100%;
17
17
  background: rgb(var(--v-theme-line));
18
- transform: scaleY(0.7);
18
+ transform: scaleY(1);
19
19
  transform-origin: bottom;
20
20
  }
21
21
  .v-slide-group__content {
@@ -29,8 +29,8 @@
29
29
  }
30
30
  .v-btn__content {
31
31
  .v-tab__slider {
32
- height: 1.5px;
33
- z-index: 1;
32
+ // height: 0;
33
+ // opacity: 0;
34
34
  }
35
35
  }
36
36
  }
@@ -63,19 +63,41 @@
63
63
  &::-webkit-scrollbar {
64
64
  width: 6px;
65
65
  }
66
-
66
+ &::-webkit-scrollbar-track {
67
+ border-radius: 10.8px;
68
+ box-shadow: inset 0 0 6px rgba(var(--v-theme-disabled));
69
+ }
67
70
  &::-webkit-scrollbar-thumb {
68
- background: rgba(var(--v-theme-primary), 0.6);
71
+ border-top: 0px solid rgba(0, 0, 0, 0);
72
+ border-bottom: 0px solid rgba(0, 0, 0, 0);
73
+ background-clip: padding-box;
69
74
  border-radius: 10.8px;
70
-
75
+ height: 50px;
76
+ background-color: rgba(var(--v-theme-disabled));
71
77
  &:hover {
72
- background: rgba(var(--v-theme-primary), 1);
78
+ background-color: rgba(var(--v-theme-primary));
73
79
  }
74
80
  }
81
+ }
75
82
 
83
+ .v-virtual-scroll {
84
+ &::-webkit-scrollbar {
85
+ width: 6px;
86
+ }
76
87
  &::-webkit-scrollbar-track {
77
- background: rgba(var(--v-theme-disabled), 0.4);
78
88
  border-radius: 10.8px;
89
+ box-shadow: inset 0 0 6px rgba(var(--v-theme-disabled));
90
+ }
91
+ &::-webkit-scrollbar-thumb {
92
+ border-top: 0px solid rgba(0, 0, 0, 0);
93
+ border-bottom: 0px solid rgba(0, 0, 0, 0);
94
+ background-clip: padding-box;
95
+ border-radius: 10.8px;
96
+ height: 50px;
97
+ background-color: rgba(var(--v-theme-disabled));
98
+ &:hover {
99
+ background-color: rgba(var(--v-theme-primary));
100
+ }
79
101
  }
80
102
  }
81
103
 
@@ -53,7 +53,19 @@
53
53
  "user": {
54
54
  "telephone": "Phone number",
55
55
  "position": "Position",
56
- "department": "Department"
56
+ "department": "Department",
57
+ "executor": "Executor",
58
+ "executors": "Executors",
59
+ "initiator": "Initiator",
60
+ "candidates": "Candidates",
61
+ "inform": "To attention",
62
+ "executorLists": "Executor lists",
63
+ "usersByGroup": "Users by groups",
64
+ "usersByPosition": "Users by job title",
65
+ "allUsers": "All users",
66
+ "allUsersAndGroups": "All users and groups",
67
+ "selectGroup": "Select group",
68
+ "selectAllFromList": "Select all from list"
57
69
  },
58
70
  "tooltip": {
59
71
  "actions": "Additional actions",
@@ -53,7 +53,20 @@
53
53
  "user": {
54
54
  "telephone": "Номер телефона",
55
55
  "position": "Должность",
56
- "department": "Департамент"
56
+ "department": "Департамент",
57
+ "executor": "Исполнитель",
58
+ "executors": "Исполнители",
59
+ "candidate": "Кандидат",
60
+ "candidates": "Кандидаты",
61
+ "initiator": "Инициатор",
62
+ "inform": "К сведению",
63
+ "executorLists": "Списки исполнителей",
64
+ "usersByGroup": "Пользователи по группам",
65
+ "usersByPosition": "Пользователи по должностям",
66
+ "allUsers": "Все пользователи",
67
+ "allUsersAndGroups": "Все пользователи и группы",
68
+ "selectGroup": "Выбрать группу",
69
+ "selectAllFromList": "Выбрать всех из списка"
57
70
  },
58
71
  "tooltip": {
59
72
  "actions": "Дополнительные действия",
@@ -53,7 +53,20 @@
53
53
  "user": {
54
54
  "telephone": "Номер телефону",
55
55
  "position": "Посада",
56
- "department": "Департамент"
56
+ "department": "Департамент",
57
+ "executor": "Виконавець",
58
+ "executors": "Виконавці",
59
+ "candidate": "Кандидат",
60
+ "candidates": "Кандидати",
61
+ "initiator": "Ініціатор",
62
+ "inform": "До відома",
63
+ "executorLists": "Списки виконавців",
64
+ "usersByGroup": "Користувачі за групами",
65
+ "usersByPosition": "Користувачі за посадою",
66
+ "allUsers": "Всі користувачі",
67
+ "allUsersAndGroups": "Всі користувачі та групи",
68
+ "selectGroup": "Вибрати групу",
69
+ "selectAllFromList": "Вибрати всіх зі списку"
57
70
  },
58
71
  "tooltip": {
59
72
  "actions": "Додаткові дії",
@@ -31,3 +31,8 @@ export const hexToRGBA = (hex, alpha) => {
31
31
  export const createRadialGradient = (mainColor, spreadColor) => {
32
32
  return `radial-gradient(83.34% 88.78% at 50.00% 50.00%, ${hexToRGBA(mainColor, 0.25)} 0%, ${hexToRGBA(mainColor, 0.08)} 31.56%, ${hexToRGBA(spreadColor, 0)} 57.76%)`
33
33
  }
34
+
35
+ export const colorCollection = {
36
+ light: ['#5400A1', '#038D00', '#0051DC', '#EE6D10', '#DC005C', '#7400CF', '#006794', '#0B14EF', '#CA00DC', '#007B5E'],
37
+ dark: ['#8848C5', '#6A9269', '#6E96DB', '#D45496', '#0081B9', '#D1AD55', '#DC7B7B', '#614FCF', '#007B5E', '#AF3EB9']
38
+ }