@it-enterprise/forcebpm-ui-kit 1.0.2-beta.21 → 1.0.2-beta.22

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.
@@ -0,0 +1,264 @@
1
+ <script setup>
2
+ // FDialog
3
+ import { onBeforeMount, onBeforeUnmount, watch, inject } from 'vue'
4
+
5
+ const props = defineProps({
6
+ disableCloseBtn: {
7
+ type: Boolean,
8
+ default: false
9
+ },
10
+ name: {
11
+ type: String,
12
+ required: true
13
+ },
14
+ title: {
15
+ type: String,
16
+ default: 'Modal title'
17
+ },
18
+ backAction: {
19
+ type: Function,
20
+ default: null
21
+ },
22
+ noSpace: {
23
+ type: Boolean,
24
+ default: false
25
+ },
26
+ isSearch: {
27
+ type: Boolean,
28
+ default: false
29
+ },
30
+ headerActions: {
31
+ type: Array,
32
+ default: null,
33
+ validator: value => {
34
+ return value.every(
35
+ action =>
36
+ 'label' in action &&
37
+ 'icon' in action &&
38
+ 'action' in action &&
39
+ typeof action.label === 'string' &&
40
+ typeof action.icon === 'string' &&
41
+ typeof action.action === 'function'
42
+ )
43
+ }
44
+ },
45
+ actions: {
46
+ type: Array,
47
+ default: null,
48
+ validator: value => {
49
+ return value.every(
50
+ action =>
51
+ 'name' in action &&
52
+ 'action' in action &&
53
+ typeof action.name === 'string' &&
54
+ typeof action.action === 'function' &&
55
+ (action.outlined === undefined || typeof action.outlined === 'boolean')
56
+ )
57
+ }
58
+ }
59
+ })
60
+
61
+ const emit = defineEmits(['open', 'close'])
62
+
63
+ const emitter = inject('fbpm-emitter', null)
64
+ const events = inject('fbpm-events', {})
65
+
66
+ const modelValue = defineModel({
67
+ type: Boolean,
68
+ default: false
69
+ })
70
+
71
+ const search = defineModel('search', {
72
+ type: String,
73
+ default: null
74
+ })
75
+
76
+ onBeforeMount(() => {
77
+ emitter.on(events.modal[props.name], open)
78
+ })
79
+
80
+ onBeforeUnmount(() => {
81
+ emitter.off(events.modal[props.name], open)
82
+ })
83
+
84
+ const open = props => {
85
+ modelValue.value = true
86
+ emit('open', props)
87
+ }
88
+
89
+ watch(modelValue, val => {
90
+ if (!val) {
91
+ search.value = null
92
+ emit('close')
93
+ }
94
+ })
95
+ </script>
96
+ <template>
97
+ <v-dialog v-model="modelValue" min-width="350" content-class="f-dialog">
98
+ <!-- Header -->
99
+ <div class="f-dialog-header">
100
+ <!-- Title -->
101
+ <div class="f-dialog-header-title">{{ title }}</div>
102
+
103
+ <div class="d-flex align-center f-dialog-header-actions-wrapper">
104
+ <!-- Search -->
105
+ <FSearchPanel
106
+ v-if="isSearch"
107
+ v-model="search"
108
+ ctrl-f
109
+ outlined
110
+ color="fields-light"
111
+ class="f-dialog-header-action"
112
+ :tooltip-props="{ location: 'top', offset: 16 }"
113
+ />
114
+
115
+ <!-- Header actions -->
116
+ <div v-for="(btn, index) in headerActions" :key="index" class="d-flex f-dialog-header-actions">
117
+ <v-tooltip offset="10" :text="btn.label">
118
+ <template #activator="{ props: tooltipProps }">
119
+ <v-btn
120
+ v-bind="tooltipProps"
121
+ size="32"
122
+ variant="text"
123
+ class="f-dialog-header-action"
124
+ :disabled="Boolean(btn.disabled)"
125
+ @click="btn.action"
126
+ >
127
+ <FIcon :icon="btn.icon" size="18" color="fields-light" />
128
+ </v-btn>
129
+ </template>
130
+ </v-tooltip>
131
+ </div>
132
+
133
+ <!-- Close btn -->
134
+ <v-tooltip v-if="!disableCloseBtn" offset="10" :text="$t('buttons.close')">
135
+ <template #activator="{ props: tooltipProps }">
136
+ <v-btn
137
+ v-bind="tooltipProps"
138
+ variant="text"
139
+ size="32"
140
+ class="f-dialog-header-action f-dialog-header-action-close"
141
+ @click="modelValue = false"
142
+ >
143
+ <FIcon icon="times" size="18" color="fields-light" />
144
+ </v-btn>
145
+ </template>
146
+ </v-tooltip>
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Body -->
151
+ <div class="f-dialog-body">
152
+ <!-- Back btn -->
153
+ <v-btn
154
+ v-if="typeof backAction === 'function'"
155
+ height="20"
156
+ variant="text"
157
+ color="secondary"
158
+ class="f-dialog-back-btn pa-0 font-weight-semibold align-self-start"
159
+ :class="noSpace ? 'ml-n1' : 'mt-5 ml-4'"
160
+ @click="
161
+ () => {
162
+ modelValue = false
163
+ backAction()
164
+ }
165
+ "
166
+ >
167
+ <FIcon icon="chevron-left" size="16" color="secondary" />
168
+ {{ $t('buttons.toBack') }}
169
+ </v-btn>
170
+
171
+ <!-- Default slot | browser scroll -->
172
+ <div class="f-dialog-content f-scrollbar-y" :class="{ 'mt-5 pl-5 mr-2 pr-3': !noSpace }">
173
+ <slot></slot>
174
+ </div>
175
+ </div>
176
+
177
+ <!-- Actions -->
178
+ <div v-if="actions || $slots.actions" class="f-dialog-actions d-flex justify-center flex-wrap my-10 mx-5">
179
+ <slot name="actions">
180
+ <v-btn
181
+ v-for="btn in actions"
182
+ :key="btn.name"
183
+ min-width="320"
184
+ class="f-dialog-action"
185
+ :variant="btn.outlined ? 'outlined' : 'elevated'"
186
+ @click="btn.action"
187
+ >
188
+ {{ btn.name }}
189
+ </v-btn>
190
+ </slot>
191
+ </div>
192
+ </v-dialog>
193
+ </template>
194
+
195
+ <style lang="scss" scoped>
196
+ :deep(.f-dialog) {
197
+ background: rgb(var(--v-theme-background));
198
+ box-shadow: 0px 4px 20px 0px rgb(var(--v-theme-title));
199
+ border-radius: 5.8px;
200
+ display: flex;
201
+ flex-direction: column;
202
+ max-height: 90vh; // Limit for the entire dialog
203
+ .f-dialog-header {
204
+ flex-shrink: 0; // Header does not shrink
205
+ display: flex;
206
+ justify-content: space-between;
207
+ background: rgb(var(--v-theme-title-dark));
208
+ border-top-left-radius: 5px;
209
+ border-top-right-radius: 5px;
210
+
211
+ .f-dialog-header-title {
212
+ font-size: 1.15em;
213
+ line-height: 1.25em;
214
+ font-weight: 600;
215
+ color: rgb(var(--v-theme-fields-light));
216
+ padding: 12px 0px 10px 20px;
217
+ }
218
+
219
+ .f-dialog-header-actions-wrapper {
220
+ display: flex;
221
+ align-items: center;
222
+ margin: 0 10px 0 20px;
223
+
224
+ .f-dialog-header-actions {
225
+ display: flex;
226
+ align-items: center;
227
+
228
+ .f-dialog-header-action {
229
+ margin-left: 8px;
230
+ }
231
+ }
232
+ .f-dialog-header-action {
233
+ .v-btn__overlay {
234
+ background-color: currentColor;
235
+ }
236
+ }
237
+ }
238
+ }
239
+ .f-dialog-body {
240
+ display: flex;
241
+ flex-direction: column;
242
+ flex: 1;
243
+ min-height: 0; // Important for flexbox to work with overflow
244
+ .f-dialog-content {
245
+ flex: 1;
246
+ overflow: auto;
247
+ }
248
+ }
249
+
250
+ .f-dialog-actions {
251
+ flex-shrink: 0; // Action section does not shrink
252
+ gap: 8px 24px;
253
+ }
254
+ }
255
+
256
+ .v-theme--dark {
257
+ :deep(.f-dialog) {
258
+ box-shadow: var(--f-box-shadow);
259
+ .f-dialog-header {
260
+ border-bottom: 1px solid rgb(var(--v-theme-line));
261
+ }
262
+ }
263
+ }
264
+ </style>
package/src/FNoData.vue CHANGED
@@ -24,7 +24,6 @@ defineProps({
24
24
  default: ''
25
25
  }
26
26
  })
27
-
28
27
  </script>
29
28
 
30
29
  <template>
package/src/FNotify.vue CHANGED
@@ -1,7 +1,6 @@
1
1
  <script setup>
2
2
  // FNotify
3
- import { computed, ref, onBeforeUnmount, onMounted, watch, defineAsyncComponent, defineEmits } from 'vue'
4
- const FHtmlContent = defineAsyncComponent(() => import('./FHtmlContent'))
3
+ import { ref, onBeforeUnmount, onMounted, watch } from 'vue'
5
4
 
6
5
  const props = defineProps({
7
6
  notify: {
@@ -115,9 +114,9 @@ onBeforeUnmount(() => {
115
114
  })
116
115
 
117
116
  watch(
118
- props.notify,
117
+ () => props.notify,
119
118
  (newval, oldVal) => {
120
- if (newval.length > oldVal.length) {
119
+ if (newval.length > oldVal?.length) {
121
120
  // New notifications added
122
121
  const newNotifications = newval.filter(n => !timers.value[n.id])
123
122
  newNotifications.forEach(v => removeNotifyAfterTimeOut(v))
@@ -132,49 +131,28 @@ watch(
132
131
  v-for="nf in props.notify"
133
132
  :key="nf.id"
134
133
  :data-notify-id="nf.id"
135
- :style="`border-color: rgb(var(--v-theme-${
136
- !nf.color ? 'secondary' : nf.color
137
- }))`"
134
+ :style="`border-color: rgb(var(--v-theme-${!nf.color ? 'secondary' : nf.color}))`"
138
135
  class="snack"
139
136
  @mouseover="hoveredNotifyId = nf.id"
140
137
  @mouseleave="
141
138
  () => {
142
- hoveredNotifyId = null;
143
- continueTimer(nf.id);
139
+ hoveredNotifyId = null
140
+ continueTimer(nf.id)
144
141
  }
145
142
  "
146
143
  @mouseenter="stopTimer(nf.id)"
147
144
  >
148
145
  <!-- Icon -->
149
146
  <div class="icon" :class="`bg-${nf.color || 'secondary'}`">
150
- <span
151
- v-if="nf.color === 'secondary' || nf.color === undefined"
152
- class="simple-icon"
153
- >i</span
154
- >
155
- <FIcon
156
- v-if="nf.color === 'success' || nf.color === 'primary'"
157
- icon="check"
158
- size="16"
159
- color="white"
160
- />
147
+ <span v-if="nf.color === 'secondary' || nf.color === undefined" class="simple-icon">i</span>
148
+ <FIcon v-if="nf.color === 'success' || nf.color === 'primary'" icon="check" size="16" color="white" />
161
149
  <span v-if="nf.color === 'info'" class="info-icon">!</span>
162
- <FIcon
163
- v-if="nf.color === 'error' || nf.color === 'danger'"
164
- icon="times"
165
- size="16"
166
- color="white"
167
- />
150
+ <FIcon v-if="nf.color === 'error' || nf.color === 'danger'" icon="times" size="16" color="white" />
168
151
  </div>
169
152
 
170
153
  <!-- Text -->
171
154
  <div
172
- v-if="
173
- nf.taskId ||
174
- (nf.businessObjectKey &&
175
- nf.businessObjectKeyType &&
176
- nf.businessObjectDefinitionCode)
177
- "
155
+ v-if="nf.taskId || (nf.businessObjectKey && nf.businessObjectKeyType && nf.businessObjectDefinitionCode)"
178
156
  class="text-subTitle"
179
157
  style="overflow-wrap: anywhere"
180
158
  >
@@ -182,51 +160,29 @@ watch(
182
160
  <span class="snack-text selectable-text">
183
161
  {{ nf.text }}
184
162
  </span>
185
- <span
186
- v-if="nf.commentId && nf.taskId"
187
- class="link-open-text cursor-pointer"
188
- @click.stop="goToL(nf)"
189
- >
190
- {{ $t("notification.openDescussion") }}
163
+ <span v-if="nf.commentId && nf.taskId" class="link-open-text cursor-pointer" @click.stop="goToL(nf)">
164
+ {{ $t('notification.openDescussion') }}
191
165
  </span>
192
- <span
193
- v-else-if="nf.taskId"
194
- class="link-open-text cursor-pointer"
195
- @click.stop="goToL(nf)"
196
- >
197
- {{ $t("notification.openTask") }}
166
+ <span v-else-if="nf.taskId" class="link-open-text cursor-pointer" @click.stop="goToL(nf)">
167
+ {{ $t('notification.openTask') }}
198
168
  </span>
199
169
  <span
200
- v-else-if="
201
- nf.businessObjectKey &&
202
- nf.businessObjectKeyType &&
203
- nf.businessObjectDefinitionCode
204
- "
170
+ v-else-if="nf.businessObjectKey && nf.businessObjectKeyType && nf.businessObjectDefinitionCode"
205
171
  class="link-open-text cursor-pointer"
206
172
  @click.stop="goToL(nf)"
207
173
  >
208
- {{ $t("notification.openDocument") }}
174
+ {{ $t('notification.openDocument') }}
209
175
  </span>
210
176
  </div>
211
177
  </div>
212
178
  <div v-else class="text-subTitle" style="overflow-wrap: anywhere">
213
- <FHtmlContent
214
- alowed-links
215
- class="snack-text selectable-text"
216
- :data="nf.text"
217
- />
179
+ <FHtmlContent alowed-links class="snack-text selectable-text" :data="nf.text" />
218
180
  </div>
219
181
 
220
182
  <!-- Close btn -->
221
183
  <v-tooltip :text="$t('buttons.close')" attach offset="12">
222
184
  <template #activator="{ props: tooltipProps }">
223
- <v-btn
224
- class="mb-auto ml-auto"
225
- size="20"
226
- variant="text"
227
- v-bind="tooltipProps"
228
- @click="removeNotifyById(nf.id)"
229
- >
185
+ <v-btn class="mb-auto ml-auto" size="20" variant="text" v-bind="tooltipProps" @click="removeNotifyById(nf.id)">
230
186
  <FIcon icon="times" color="text" size="0.9em" />
231
187
  </v-btn>
232
188
  </template>
@@ -3,20 +3,15 @@
3
3
  const props = defineProps({
4
4
  preLoaders: {
5
5
  type: Array,
6
- default: () => [],
7
- },
8
- });
6
+ default: () => []
7
+ }
8
+ })
9
9
  </script>
10
10
 
11
11
  <template>
12
- <v-overlay
13
- :model-value="!!props.preLoaders.length"
14
- z-index="208"
15
- opacity="0.5"
16
- class="f-preloader"
17
- >
12
+ <v-overlay :model-value="!!props.preLoaders.length" z-index="208" opacity="0.5" class="f-preloader">
18
13
  <div v-if="props.preLoaders.includes('tokenUpdate')" class="text-title">
19
- {{ $t("updating") }}
14
+ {{ $t('updating') }}
20
15
  </div>
21
16
  <div class="f-preloader-lds-ellipsis">
22
17
  <div></div>
@@ -0,0 +1,188 @@
1
+ <script setup>
2
+ // FSearchPanel
3
+ import { ref, watch, useTemplateRef } from 'vue'
4
+ import { useDebounceFn, useEventListener } from '@vueuse/core'
5
+
6
+ const props = defineProps({
7
+ disabled: {
8
+ type: Boolean,
9
+ default: false
10
+ },
11
+ color: {
12
+ type: String,
13
+ default: 'text'
14
+ },
15
+ background: {
16
+ type: String,
17
+ default: 'background'
18
+ },
19
+ outlined: {
20
+ type: Boolean,
21
+ default: false
22
+ },
23
+ searchOnBlur: {
24
+ type: Boolean,
25
+ default: false
26
+ },
27
+ tooltipProps: {
28
+ type: Object,
29
+ default: () => ({ location: 'top' })
30
+ },
31
+ ctrlF: {
32
+ type: Boolean,
33
+ default: false
34
+ }
35
+ })
36
+
37
+ const modelValue = defineModel({
38
+ type: String,
39
+ default: null
40
+ })
41
+
42
+ const search = ref(null)
43
+
44
+ const updateModelValue = useDebounceFn(value => {
45
+ if (modelValue.value === value) return
46
+ modelValue.value = value
47
+ }, 300)
48
+
49
+ const searchInputRef = useTemplateRef('search-input')
50
+ useEventListener('keydown', e => {
51
+ if (!props.ctrlF) return
52
+
53
+ // Focus the search input when Ctrl+F or Cmd+F is pressed
54
+ if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
55
+ e.preventDefault()
56
+ searchInputRef.value?.focus()
57
+ }
58
+ })
59
+
60
+ watch(
61
+ modelValue,
62
+ val => {
63
+ if (val === search.value) return
64
+ search.value = val
65
+ },
66
+ { immediate: true }
67
+ )
68
+
69
+ watch(search, v => {
70
+ if (props.searchOnBlur) return
71
+ updateModelValue(v)
72
+ })
73
+
74
+ const blurHandler = () => {
75
+ updateModelValue(search.value)
76
+ }
77
+
78
+ const keydownEnter = () => {
79
+ searchInputRef.value?.blur()
80
+ updateModelValue(search.value)
81
+ }
82
+
83
+ const keydownEsc = () => {
84
+ searchInputRef.value?.blur()
85
+ }
86
+ </script>
87
+
88
+ <template>
89
+ <div class="f-search-panel">
90
+ <v-text-field
91
+ ref="search-input"
92
+ v-model.trim="search"
93
+ clearable
94
+ :disabled="disabled"
95
+ :bg-color="background"
96
+ :class="{ 'is-outlined': outlined }"
97
+ @blur="blurHandler"
98
+ @keydown.esc.stop.prevent="keydownEsc"
99
+ @keydown.enter="keydownEnter"
100
+ >
101
+ <template #prepend-inner>
102
+ <v-tooltip :disabled="disabled" :text="$t('tooltip.search')" v-bind="tooltipProps">
103
+ <template #activator="{ props: propsTooltip }">
104
+ <FIcon icon="search" :color="color" v-bind="propsTooltip" />
105
+ </template>
106
+ </v-tooltip>
107
+ </template>
108
+ </v-text-field>
109
+
110
+ <!-- Global search -->
111
+ <slot name="globalSearch"></slot>
112
+ </div>
113
+ </template>
114
+
115
+ <style lang="scss" scoped>
116
+ .f-search-panel {
117
+ position: relative;
118
+ min-height: 30px;
119
+ width: auto;
120
+ :deep(.f-input) {
121
+ height: 30px;
122
+ width: 30px;
123
+ transition: all 0.2s ease-in-out;
124
+ &.v-input--focused {
125
+ .v-field__prepend-inner {
126
+ margin-left: 0px;
127
+ cursor: default;
128
+ .f-icon {
129
+ background: rgb(var(--v-theme-primary)) !important;
130
+ }
131
+ }
132
+ }
133
+ &.v-input--focused,
134
+ &.v-input--dirty {
135
+ width: 250px;
136
+ max-width: 250px;
137
+ .f-icon {
138
+ background: rgb(var(--v-theme-text)) !important;
139
+ }
140
+ }
141
+
142
+ &:not(.is-outlined) {
143
+ background: transparent !important;
144
+ .v-field__prepend-inner {
145
+ cursor: pointer;
146
+ margin-left: -6px;
147
+ }
148
+ .v-field {
149
+ background: transparent !important;
150
+ .v-field__outline__start,
151
+ .v-field__outline__notch,
152
+ .v-field__outline__end {
153
+ border-color: transparent;
154
+ }
155
+ }
156
+ &.v-input--dirty,
157
+ &.v-input--focused {
158
+ background: rgb(var(--v-theme-background)) !important;
159
+ }
160
+ }
161
+ &.is-outlined {
162
+ &:not(.v-input--dirty, .v-input--focused) {
163
+ background: transparent !important;
164
+ .v-field__prepend-inner {
165
+ cursor: pointer;
166
+ margin-left: -6px;
167
+ }
168
+ .v-field {
169
+ background: transparent !important;
170
+ .v-field__outline__start,
171
+ .v-field__outline__notch,
172
+ .v-field__outline__end {
173
+ border-color: transparent;
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ .v-field {
180
+ .v-field__input {
181
+ padding-left: 8px;
182
+ padding-top: 8px;
183
+ padding-bottom: 4px;
184
+ }
185
+ }
186
+ }
187
+ }
188
+ </style>
package/src/FShare.vue CHANGED
@@ -47,16 +47,10 @@ onBeforeUnmount(() => {
47
47
  <!-- Header -->
48
48
  <div class="f-dialog-header">
49
49
  <!-- Title -->
50
- <div class="f-dialog-header-title">{{ $t("buttons.share") }}</div>
50
+ <div class="f-dialog-header-title">{{ $t('buttons.share') }}</div>
51
51
  <v-tooltip offset="10" :text="$t('buttons.close')">
52
52
  <template #activator="{ props: tooltipProps }">
53
- <v-btn
54
- v-bind="tooltipProps"
55
- variant="text"
56
- size="32"
57
- class="f-dialog-header-action f-dialog-header-action-close"
58
- @click="close"
59
- >
53
+ <v-btn v-bind="tooltipProps" variant="text" size="32" class="f-dialog-header-action f-dialog-header-action-close" @click="close">
60
54
  <FIcon icon="times" size="18" color="fields-light" />
61
55
  </v-btn>
62
56
  </template>
@@ -67,14 +61,7 @@ onBeforeUnmount(() => {
67
61
  <div class="f-dialog-body">
68
62
  <div class="share-wrap">
69
63
  <template v-for="(btn, idx) in props.buttons">
70
- <v-btn
71
- v-if="!btn.hide"
72
- :key="idx"
73
- variant="text"
74
- class="share-btn"
75
- color="subTitle"
76
- @click="applyMethod(btn.action)"
77
- >
64
+ <v-btn v-if="!btn.hide" :key="idx" variant="text" class="share-btn" color="subTitle" @click="applyMethod(btn.action)">
78
65
  <FIcon :icon="btn.icon" size="24" :color="btn.color" />
79
66
  <!-- <FHtmlContent :data="btn.name" /> -->
80
67
  <span v-if="btn.name" class="share-btn-name">{{ btn.name }}</span>