PluginProbe
The Innovative Form Builder – IvyForms / 0.2.1
The Innovative Form Builder – IvyForms v0.2.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / views / _components / sub-dialog / IvyShortDialog.vue

IvyShortDialog.vue in The Innovative Form Builder – IvyForms 0.2.1, at frontend/src/views/_components/sub-dialog/IvyShortDialog.vue

410 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <template>
2 <div class="ivyforms-subdialog-wrapper">
3 <ElDialog
4 ref="dialogRef"
5 v-model="dialogVisible"
6 v-bind="{ ...$attrs, type: actionType }"
7 :show-close="false"
8 :style="{ bottom: dialogBottom + 'px' }"
9 class="ivyforms-subdialog"
10 :class="[`type-${actionType}`, `ivyforms-align-${props.align}`, { 'position-top': top }]"
11 :width="props.width"
12 @open="onOpen()"
13 >
14 <template #header>
15 <div ref="dialogHeader" class="ivyforms-subdialog__header">
16 <div class="ivyforms-subdialog__header__drag-handle" />
17 <div class="ivyforms-subdialog-header-icon">
18 <IvyIcon :name="getIconName()" type="fill-duo" size="l" />
19 </div>
20 <div v-if="$slots.header" class="ivyforms-subdialog__header__title-wrapper">
21 <slot name="header" />
22 </div>
23 <div
24 v-if="dialogData.title && !$slots.header"
25 class="ivyforms-subdialog__header__title-wrapper ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-8"
26 >
27 <div
28 class="ivyforms-subdialog__header__title ivyforms-flex ivyforms-gap-8 ivyforms-justify-content-between ivyforms-align-items-center"
29 >
30 <span class="medium-14">{{ dialogData.title }}</span>
31 <slot name="titleButton" />
32 </div>
33 </div>
34 <div v-if="showClose" class="ivyforms-subdialog__header__close">
35 <IvyIcon
36 priority="tertiary"
37 name="close"
38 type="outline"
39 size="s"
40 category="global"
41 color="var(--map-base-dusk-symbol-2)"
42 @click="dialogVisible = false"
43 />
44 </div>
45 </div>
46 </template>
47 <div v-if="dialogData.subtitle" class="ivyforms-subdialog__body">
48 {{ dialogData.subtitle }}
49 </div>
50 <template v-if="dialogData.buttons" #footer>
51 <IvyButtonAction
52 :priority="dialogData.buttons?.close?.type ? dialogData.buttons.close.type : 'tertiary'"
53 size="d"
54 type="fill"
55 :full-width="true"
56 @click="clickClose"
57 >
58 {{ dialogData.buttons?.close?.text || 'Closel' }}
59 </IvyButtonAction>
60 <IvyButtonAction
61 v-if="dialogData.buttons?.confirm"
62 :priority="dialogData.buttons?.confirm?.type || 'primary'"
63 size="d"
64 type="fill"
65 :full-width="true"
66 @click="clickConfirm"
67 >
68 {{ dialogData.buttons?.confirm?.text || 'Confirm' }}
69 </IvyButtonAction>
70 </template>
71 </ElDialog>
72 </div>
73 </template>
74
75 <script setup lang="ts">
76 import { useSwipe } from '@vueuse/core'
77 import { ref } from 'vue'
78 import IvyButtonAction from '@/views/_components/button/IvyButtonAction.vue'
79 import { storeToRefs } from 'pinia'
80 import { useActionEntityStore } from '@/stores/actionEntityStore.ts'
81
82 interface Props {
83 title?: string
84 subtitle?: string | null
85 showClose?: boolean
86 width?: string
87 type?: 'info' | 'success' | 'warning' | 'error' | 'default' | 'premium' | 'upcoming'
88 align?: 'left' | 'center' | 'right'
89 top?: boolean
90 }
91
92 const props = withDefaults(defineProps<Props>(), {
93 title: '',
94 subtitle: '',
95 showClose: true,
96 width: '',
97 type: 'success',
98 align: 'center',
99 top: false,
100 })
101
102 const { dialogVisible, dialogData, actionType } = storeToRefs(useActionEntityStore())
103 const { confirmAction } = useActionEntityStore()
104
105 const clickClose = () => {
106 if (dialogData.value?.buttons?.close.function) {
107 dialogData.value?.buttons?.close.function()
108 dialogVisible.value = false
109 } else {
110 dialogVisible.value = false
111 }
112 }
113
114 const clickConfirm = () => {
115 if (dialogData.value?.buttons?.confirm.function) {
116 dialogData.value?.buttons?.confirm.function()
117 dialogVisible.value = false
118 } else {
119 confirmAction()
120 }
121 }
122
123 const dialogHeader = ref<HTMLElement | null>(null)
124 const dialogBottom = ref(0)
125 const dialogHeight = ref(0)
126 const dialogRef = ref(null)
127
128 const onOpen = () => {
129 dialogBottom.value = 0
130 calculateDialogHeight()
131
132 const { direction, lengthY } = useSwipe(dialogHeader, {
133 threshold: 0,
134 onSwipe() {
135 if (direction.value === 'down') {
136 dialogBottom.value = Math.min(lengthY.value, window.innerHeight)
137 } else if (direction.value === 'up') {
138 dialogBottom.value = Math.max(dialogBottom.value - lengthY.value, 0)
139 }
140 },
141 onSwipeEnd() {
142 if (direction.value === 'down' && Math.abs(lengthY.value) > dialogHeight.value / 2) {
143 dialogVisible.value = false
144 } else {
145 dialogBottom.value = 0
146 }
147 },
148 })
149
150 dialogRef.value.$el.nextSibling.querySelector('.el-overlay-dialog').scrollTo(0, 0)
151 }
152
153 const calculateDialogHeight = () => {
154 const dialogElement = document.querySelector('.ivyforms-subdialog')
155 if (dialogElement) {
156 dialogHeight.value = dialogElement.clientHeight
157 }
158 }
159
160 const getIconName = () => {
161 if (actionType.value) {
162 switch (actionType.value) {
163 case 'duplicate':
164 return 'copy'
165 case 'unsaved_changes':
166 case 'delete':
167 return 'danger'
168 }
169 }
170 }
171 </script>
172 <style lang="scss">
173 @use 'sass:list' as *;
174 @use 'sass:meta' as *;
175 // Dialog
176 .ivyforms-subdialog {
177 display: flex;
178 max-width: 100%;
179 padding: 0;
180 flex-direction: column;
181 align-items: flex-start;
182 border-radius: 16px;
183 background-color: var(--theme-ground-level-2-foreground);
184 margin: 0 auto;
185 max-height: calc(100% - 78px);
186 box-shadow: var(--shadow-500);
187 position: absolute;
188 bottom: 0;
189 overflow: hidden;
190 // Type
191 $types: (
192 'delete': var(--map-status-error-fill-0),
193 'duplicate': var(--map-status-success-fill-0),
194 'unsaved_changes': var(--map-status-warning-fill-0),
195 );
196 &.position-top {
197 top: 5%;
198 bottom: auto !important;
199 position: fixed;
200 left: 50%;
201 transform: translateX(-50%);
202 margin: 0 auto;
203 }
204 @each $type, $fill in $types {
205 &.type-#{$type} {
206 .el-dialog__header {
207 .ivyforms-subdialog__header {
208 .ivyforms-subdialog-header-icon {
209 .ivyforms-icon {
210 svg {
211 fill: $fill;
212 }
213 }
214 }
215 }
216 }
217 }
218 }
219
220 $alignments: (
221 'center': (
222 center,
223 center,
224 center,
225 ),
226 'left': (
227 flex-start,
228 left,
229 flex-start,
230 ),
231 'right': (
232 flex-start,
233 left,
234 flex-end,
235 ),
236 );
237
238 @each $align, $values in $alignments {
239 &.ivyforms-align-#{$align} {
240 .el-dialog__header {
241 .ivyforms-subdialog__header {
242 &__title {
243 align-self: nth($values, 1);
244 span {
245 text-align: nth($values, 2);
246 }
247 }
248 .ivyforms-subdialog-header-icon {
249 align-self: nth($values, 1);
250 }
251 }
252 }
253
254 .el-dialog__footer {
255 justify-content: nth($values, 3);
256 }
257
258 .el-dialog__body {
259 .ivyforms-subdialog__body {
260 // Tablet and up
261 @include tablet-and-up {
262 text-align: nth($values, 2);
263 padding: 0px;
264 horiz-align: nth($values, 2);
265 }
266 }
267 }
268 }
269 }
270
271 // Tablet And Up
272 @include tablet-and-up {
273 max-height: unset;
274 border-radius: 12px;
275 position: relative;
276 min-width: unset;
277 }
278
279 // Element Dialog
280 &.el-dialog {
281 border-radius: 16px;
282 background-color: var(--theme-ground-level-2-foreground);
283 padding: 0;
284
285 &.position-top {
286 top: 5%;
287 bottom: auto !important;
288 position: fixed;
289 left: 50%;
290 transform: translateX(-50%);
291 margin: 0 auto;
292 }
293 // Element Header
294 .el-dialog__header {
295 padding: 24px 24px 8px 24px;
296 width: 100%;
297 }
298
299 .el-dialog__header {
300 padding: 0;
301 }
302 // Default Header
303 .ivyforms-subdialog__header {
304 width: 100%;
305 display: flex;
306 justify-content: space-between;
307 padding: 16px 16px 8px;
308 box-sizing: border-box;
309 display: flex;
310 flex-direction: column;
311 background-color: var(--theme-ground-level-2-foreground);
312
313 // Drag Handle
314 &__drag-handle {
315 width: 32px;
316 height: 4px;
317 border-radius: 100px;
318 opacity: 0.4;
319 background: var(--theme-base-dusk-0);
320 margin: 16px auto 16px;
321
322 // Tablet And Up
323 @include tablet-and-up {
324 display: none;
325 }
326 }
327
328 // Title
329 &__title {
330 // Tablet And Up
331 @include tablet-and-up {
332 margin-top: 16px;
333 }
334 span {
335 color: var(--map-base-text-0);
336 }
337 }
338
339 // Close
340 &__close {
341 display: none;
342 position: absolute;
343 top: 8px;
344 right: 10px;
345 cursor: pointer;
346
347 // Tablet And Up
348 @include tablet-and-up {
349 display: flex;
350 }
351 }
352 .ivyforms-text-secondary {
353 text-align: center;
354 color: var(--map-base-text--1);
355 }
356 }
357
358 // Element Body
359 .el-dialog__body {
360 width: 100%;
361 height: 100%;
362 box-sizing: border-box;
363 color: var(--map-base-text--1);
364 background-color: var(--theme-ground-level-2-foreground);
365 overflow: auto;
366
367 // Tablet And Up
368 @include tablet-and-up {
369 height: initial;
370 max-height: unset;
371 }
372 }
373
374 // Element Footer
375 .el-dialog__footer {
376 display: flex;
377 gap: 12px;
378 width: 100%;
379 padding: 16px;
380 background-color: var(--theme-ground-level-2-foreground);
381 margin-top: auto;
382 position: sticky;
383 bottom: 0;
384 z-index: 2;
385
386 // Button
387 button {
388 flex: 1;
389
390 // Tablet And Up
391 @include tablet-and-up {
392 flex: 0 1 auto;
393 }
394 }
395 }
396 }
397 }
398
399 // Overlay
400 .el-overlay {
401 overflow: hidden;
402 background-color: var(--theme-overlay);
403
404 // Tablet And Up
405 @include tablet-and-up {
406 overflow: auto;
407 }
408 }
409 </style>
410