@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.
- package/index.js +15 -0
- package/package.json +29 -6
- package/plugin.js +13 -24
- package/src/FAvatar.vue +27 -70
- package/src/FConfirmModal.vue +5 -21
- package/src/FContextMenu.vue +33 -57
- package/src/FDialog.vue +264 -0
- package/src/FNoData.vue +0 -1
- package/src/FNotify.vue +18 -62
- package/src/FPreLoader.vue +5 -10
- package/src/FSearchPanel.vue +188 -0
- package/src/FShare.vue +3 -16
- package/src/FTruncate.vue +121 -0
- package/src/FUserRoles.vue +195 -0
- package/src/f-date-picker/FDatePicker.vue +322 -0
- package/src/f-date-picker/FMenuDatePicker.vue +337 -0
- package/src/f-date-picker/FTextFieldDate.vue +498 -0
- package/src/f-toolbar/FFilterPanel.vue +62 -0
- package/src/f-toolbar/FSortPanel.vue +47 -0
- package/src/f-toolbar/FTabPanel.vue +123 -0
- package/src/f-toolbar/FToolbar.vue +131 -0
- package/src/f-toolbar/FViewerPanel.vue +71 -0
- package/src/forcebpmIcon.js +69 -0
- package/src/locales/en.json +24 -24
- package/src/locales/ru.json +24 -24
- package/src/locales/uk.json +24 -24
- package/src/scss/buttons.scss +1 -43
- package/src/scss/card.scss +6 -0
- package/src/scss/expansion.scss +0 -5
- package/src/scss/index.scss +0 -2
- package/src/scss/input.scss +49 -6
- package/src/scss/overlay.scss +3 -3
- package/src/scss/pagination.scss +1 -5
- package/src/scss/skeleton.scss +1 -68
- package/src/scss/snackbar.scss +1 -1
- package/src/scss/tables.scss +141 -145
- package/src/scss/checkbox.scss +0 -25
package/src/FDialog.vue
ADDED
|
@@ -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
package/src/FNotify.vue
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
// FNotify
|
|
3
|
-
import {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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>
|
package/src/FPreLoader.vue
CHANGED
|
@@ -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(
|
|
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(
|
|
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>
|