quasar-ui-danx 0.0.10 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. package/package.json +3 -2
  2. package/src/components/ActionTable/ActionTable.vue +135 -0
  3. package/src/components/ActionTable/BatchActionMenu.vue +60 -0
  4. package/src/components/ActionTable/EmptyTableState.vue +33 -0
  5. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +36 -0
  6. package/src/components/ActionTable/Filters/FilterGroupItem.vue +28 -0
  7. package/src/components/ActionTable/Filters/FilterGroupList.vue +76 -0
  8. package/src/components/ActionTable/Filters/FilterListToggle.vue +50 -0
  9. package/src/components/ActionTable/Filters/FilterableField.vue +141 -0
  10. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  11. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  12. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  13. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  14. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  16. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  17. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  18. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  19. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  20. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  21. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  22. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  23. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  24. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  25. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  26. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  27. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  28. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  29. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  30. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  31. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  32. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  33. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  34. package/src/components/ActionTable/Form/RenderedForm.vue +74 -0
  35. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  36. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  37. package/src/components/ActionTable/index.ts +15 -0
  38. package/src/components/ActionTable/listActions.ts +361 -0
  39. package/src/components/ActionTable/tableColumns.ts +72 -0
  40. package/src/components/ActionTable/tableHelpers.ts +83 -0
  41. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  42. package/src/components/Utility/ContentDrawer.vue +70 -0
  43. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  44. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  45. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  46. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  47. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  48. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  49. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  50. package/src/components/Utility/index.ts +9 -0
  51. package/src/components/index.ts +3 -0
  52. package/src/helpers/FileUpload.ts +294 -0
  53. package/src/helpers/FlashMessages.ts +79 -0
  54. package/src/helpers/array.ts +37 -0
  55. package/src/helpers/compatibility.ts +64 -0
  56. package/src/helpers/date.ts +5 -0
  57. package/src/helpers/download.ts +192 -0
  58. package/src/helpers/downloadPdf.ts +92 -0
  59. package/src/helpers/files.ts +52 -0
  60. package/src/helpers/formats.ts +183 -0
  61. package/src/helpers/http.ts +62 -0
  62. package/src/helpers/index.ts +10 -1
  63. package/src/helpers/multiFileUpload.ts +68 -0
  64. package/src/helpers/singleFileUpload.ts +54 -0
  65. package/src/helpers/storage.ts +8 -0
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <div
3
+ class="collapsable-sidebar overflow-x-hidden overflow-y-scroll relative"
4
+ :class="{
5
+ 'is-collapsed': isCollapsed,
6
+ 'is-right-side': rightSide,
7
+ [displayClass]: true,
8
+ }"
9
+ :style="style"
10
+ >
11
+ <div class="flex-grow max-w-full">
12
+ <slot :is-collapsed="isCollapsed" />
13
+ </div>
14
+ <template v-if="!disabled && (!hideToggleOnCollapse || !isCollapsed)">
15
+ <div
16
+ v-if="!toggleAtTop"
17
+ class="flex w-full p-4"
18
+ :class="rightSide ? 'justify-start' : 'justify-end'"
19
+ >
20
+ <slot name="toggle">
21
+ <QBtn
22
+ class="btn-white-gray"
23
+ @click="toggleCollapse"
24
+ >
25
+ <ToggleIcon
26
+ class="w-5 transition-all"
27
+ :class="{ 'rotate-180': rightSide ? !isCollapsed : isCollapsed }"
28
+ />
29
+ </QBtn>
30
+ </slot>
31
+ </div>
32
+ <div
33
+ v-else
34
+ class="absolute top-0 right-0 cursor-pointer p-2"
35
+ :class="toggleClass"
36
+ @click="toggleCollapse"
37
+ >
38
+ <ToggleIcon
39
+ class="w-5 transition-all"
40
+ :class="{ 'rotate-180': rightSide ? !isCollapsed : isCollapsed }"
41
+ />
42
+ </div>
43
+ </template>
44
+ </div>
45
+ </template>
46
+ <script setup>
47
+ import { ChevronLeftIcon as ToggleIcon } from "@heroicons/vue/outline";
48
+ import { computed, onMounted, ref, watch } from "vue";
49
+
50
+ const emit = defineEmits(["collapse", "update:collapse"]);
51
+ const props = defineProps({
52
+ rightSide: Boolean,
53
+ displayClass: {
54
+ type: String,
55
+ default: "flex flex-col"
56
+ },
57
+ maxWidth: {
58
+ type: String,
59
+ default: "13.5rem"
60
+ },
61
+ minWidth: {
62
+ type: String,
63
+ default: "5.5rem"
64
+ },
65
+ disabled: Boolean,
66
+ collapse: Boolean,
67
+ name: {
68
+ type: String,
69
+ default: "sidebar"
70
+ },
71
+ toggleAtTop: Boolean,
72
+ toggleClass: {
73
+ type: String,
74
+ default: ""
75
+ },
76
+ hideToggleOnCollapse: Boolean
77
+ });
78
+
79
+ const isCollapsed = ref(props.collapse);
80
+
81
+ const stored = localStorage.getItem(props.name + "-is-collapsed");
82
+
83
+ if (stored !== null) {
84
+ isCollapsed.value = stored === "1";
85
+ }
86
+ function toggleCollapse() {
87
+ setCollapse(!isCollapsed.value);
88
+ emit("collapse", isCollapsed.value);
89
+ emit("update:collapse", isCollapsed.value);
90
+ }
91
+
92
+ function setCollapse(state) {
93
+ isCollapsed.value = state;
94
+ localStorage.setItem(props.name + "-is-collapsed", isCollapsed.value ? "1" : "");
95
+ }
96
+
97
+ onMounted(() => {
98
+ emit("collapse", isCollapsed.value);
99
+ emit("update:collapse", isCollapsed.value);
100
+ });
101
+ const style = computed(() => {
102
+ return {
103
+ width: isCollapsed.value ? props.minWidth : props.maxWidth
104
+ };
105
+ });
106
+
107
+ watch(() => props.collapse, () => {
108
+ setCollapse(props.collapse);
109
+ });
110
+ </script>
111
+
112
+ <style
113
+ scoped
114
+ lang="scss"
115
+ >
116
+ .collapsable-sidebar {
117
+ @apply overflow-y-auto scroll-smooth flex-shrink-0 border-r border-neutral-plus-5 transition-all;
118
+ }
119
+ </style>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <QDialog
3
+ v-model="isShowing"
4
+ maximized
5
+ :position="position"
6
+ :seamless="seamless"
7
+ :class="{'hide-backdrop': !overlay}"
8
+ >
9
+ <div>
10
+ <div
11
+ v-if="title"
12
+ class="dialog-title"
13
+ @click.stop.prevent
14
+ >
15
+ {{ title }}
16
+ </div>
17
+ <div
18
+ class="dialog-content bg-white"
19
+ :class="{ [contentClass]: true }"
20
+ >
21
+ <slot />
22
+ </div>
23
+ </div>
24
+ </QDialog>
25
+ </template>
26
+
27
+ <script setup>
28
+ import { computed } from "vue";
29
+
30
+ const emit = defineEmits(["update:show"]);
31
+
32
+ const props = defineProps({
33
+ show: Boolean,
34
+ seamless: Boolean,
35
+ overlay: Boolean,
36
+ position: {
37
+ type: String,
38
+ default: "bottom"
39
+ },
40
+ contentClass: {
41
+ type: String,
42
+ default: "py-8 px-12"
43
+ },
44
+ title: {
45
+ type: String,
46
+ default: "Edit"
47
+ }
48
+ });
49
+
50
+ const isShowing = computed({
51
+ get: () => props.show,
52
+ set: (value) => emit("update:show", value)
53
+ });
54
+ </script>
55
+
56
+ <style
57
+ lang="scss"
58
+ scoped
59
+ >
60
+ .dialog-title {
61
+ @apply bg-gray-very-light text-gray-default font-medium uppercase text-xs px-6 py-3 border-b border-neutral-plus-5 rounded-t-md;
62
+ font-family: "Roboto", sans-serif;
63
+ letter-spacing: 0.05em;
64
+ box-shadow: 0px -4px 12px rgba(0, 0, 0, 0.25);
65
+ }
66
+
67
+ .dialog-content {
68
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
69
+ }
70
+ </style>
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <QDialog
3
+ :full-height="fullHeight"
4
+ :full-width="fullWidth"
5
+ :model-value="!!modelValue"
6
+ :no-backdrop-dismiss="!backdropDismiss"
7
+ :maximized="maximized"
8
+ @update:model-value="onClose"
9
+ >
10
+ <QCard class="flex flex-col flex-nowrap">
11
+ <QCardSection
12
+ v-if="title || $slots.title"
13
+ class="pl-6 pr-10 border-b border-gray-medium"
14
+ >
15
+ <h3
16
+ class="font-normal flex items-center"
17
+ :class="titleClass"
18
+ >
19
+ <slot name="title">{{ title }}</slot>
20
+ </h3>
21
+ <div
22
+ v-if="subtitle"
23
+ class="mt-1 text-sm"
24
+ >{{ subtitle }}
25
+ </div>
26
+ </QCardSection>
27
+ <QCardSection v-if="$slots.toolbar">
28
+ <slot name="toolbar" />
29
+ </QCardSection>
30
+ <QCardSection
31
+ v-if="content || $slots.default"
32
+ class="px-6 bg-neutral-plus-7 flex-grow max-h-full overflow-y-auto"
33
+ :class="contentClass"
34
+ >
35
+ <slot>{{ content }}</slot>
36
+ </QCardSection>
37
+ <div class="flex px-6 py-4 border-t border-gray-medium">
38
+ <div class="flex-grow">
39
+ <QBtn
40
+ :label="cancelText"
41
+ class="action-btn btn-white-gray"
42
+ @click="onClose"
43
+ >
44
+ <slot name="cancel-text" />
45
+ </QBtn>
46
+ </div>
47
+ <slot name="actions" />
48
+ <div v-if="!hideConfirm">
49
+ <QBtn
50
+ :label="$slots['confirm-text'] ? '' : confirmText"
51
+ class="action-btn ml-4"
52
+ :class="confirmClass"
53
+ :loading="isSaving"
54
+ :disable="disabled"
55
+ data-testid="confirm-button"
56
+ @click="onConfirm"
57
+ >
58
+ <slot name="confirm-text" />
59
+ </QBtn>
60
+ </div>
61
+ </div>
62
+ <a
63
+ class="absolute top-0 right-0 p-4 text-black"
64
+ @click="onClose"
65
+ >
66
+ <CloseIcon class="w-5" />
67
+ </a>
68
+ </QCard>
69
+ </QDialog>
70
+ </template>
71
+
72
+ <script setup>
73
+ import { XIcon as CloseIcon } from "@heroicons/vue/outline";
74
+
75
+ const emit = defineEmits(["update:model-value", "confirm", "close"]);
76
+ const props = defineProps({
77
+ modelValue: { type: [String, Boolean, Object], default: true },
78
+ title: {
79
+ type: String,
80
+ default: ""
81
+ },
82
+ titleClass: {
83
+ type: String,
84
+ default: ""
85
+ },
86
+ subtitle: {
87
+ type: String,
88
+ default: ""
89
+ },
90
+ content: {
91
+ type: String,
92
+ default: ""
93
+ },
94
+ backdropDismiss: Boolean,
95
+ maximized: Boolean,
96
+ fullWidth: Boolean,
97
+ fullHeight: Boolean,
98
+ disabled: Boolean,
99
+ isSaving: Boolean,
100
+ closeOnConfirm: Boolean,
101
+ hideConfirm: Boolean,
102
+ confirmText: {
103
+ type: String,
104
+ default: "Confirm"
105
+ },
106
+ cancelText: {
107
+ type: String,
108
+ default: "Cancel"
109
+ },
110
+ confirmClass: {
111
+ type: String,
112
+ default: "bg-blue-base text-white"
113
+ },
114
+ contentClass: {
115
+ type: String,
116
+ default: ""
117
+ }
118
+ });
119
+
120
+ function onConfirm() {
121
+ emit("confirm");
122
+
123
+ if (props.closeOnConfirm) {
124
+ emit("close");
125
+ }
126
+ }
127
+
128
+ function onClose() {
129
+ emit("update:model-value", false);
130
+ emit("close");
131
+ }
132
+ </script>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <QDialog
3
+ :model-value="modelValue"
4
+ maximized
5
+ transition-show="slide-up"
6
+ transition-hide="slide-down"
7
+ @update:model-value="onClose"
8
+ >
9
+ <div class="flex justify-center min-w-xs" :class="computedClass">
10
+ <div
11
+ v-if="closeable"
12
+ v-close-popup
13
+ class="p-4 m-4 absolute-top-right top right cursor-pointer"
14
+ >
15
+ <XIcon class="w-5 h-5" />
16
+ </div>
17
+ <slot />
18
+ </div>
19
+ </QDialog>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { XIcon } from "src/svg";
24
+ import { computed } from "vue";
25
+
26
+ const emit = defineEmits(["update:model-value", "close"]);
27
+ const props = defineProps({
28
+ modelValue: Boolean,
29
+ center: Boolean,
30
+ blue: Boolean,
31
+ closeable: Boolean
32
+ });
33
+
34
+ let computedClass = computed(() => {
35
+ return {
36
+ "bg-blue-base text-white": props.blue,
37
+ "bg-white text-gray-base": !props.blue,
38
+ "items-center": props.center
39
+ };
40
+ });
41
+
42
+ function onClose() {
43
+ emit("update:model-value", false);
44
+ emit("close");
45
+ }
46
+ </script>
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <QDialog
3
+ :full-height="fullHeight"
4
+ :full-width="fullWidth"
5
+ :model-value="!!modelValue"
6
+ :no-backdrop-dismiss="!backdropDismiss"
7
+ :maximized="maximized"
8
+ @update:model-value="onClose"
9
+ >
10
+ <QCard class="flex flex-col flex-nowrap">
11
+ <QCardSection
12
+ v-if="title || $slots.title"
13
+ class="pl-6 pr-10 border-b border-gray-medium"
14
+ >
15
+ <h3
16
+ class="font-normal flex items-center"
17
+ :class="titleClass"
18
+ >
19
+ <slot name="title">{{ title }}</slot>
20
+ </h3>
21
+ <div
22
+ v-if="subtitle"
23
+ class="mt-1 text-sm"
24
+ >{{ subtitle }}
25
+ </div>
26
+ </QCardSection>
27
+ <QCardSection
28
+ v-if="content || $slots.default"
29
+ class="px-6 bg-neutral-plus-7 flex-grow max-h-full overflow-y-auto"
30
+ >
31
+ <slot>{{ content }}</slot>
32
+ </QCardSection>
33
+ <div
34
+ class="flex items-center justify-center px-6 py-4 border-t border-gray-medium"
35
+ >
36
+ <div class="flex-grow text-right">
37
+ <QBtn
38
+ :label="doneText"
39
+ class="action-btn btn-white-gray"
40
+ @click="onClose"
41
+ >
42
+ <slot name="done-text" />
43
+ </QBtn>
44
+ </div>
45
+ </div>
46
+ <a
47
+ class="absolute top-0 right-0 p-4 text-black"
48
+ @click="onClose"
49
+ >
50
+ <CloseIcon class="w-5" />
51
+ </a>
52
+ </QCard>
53
+ </QDialog>
54
+ </template>
55
+
56
+ <script setup>
57
+ import { XIcon as CloseIcon } from "@heroicons/vue/outline";
58
+
59
+ const emit = defineEmits(["update:model-value", "close"]);
60
+ defineProps({
61
+ modelValue: { type: [Boolean, Object], default: true },
62
+ title: {
63
+ type: String,
64
+ default: ""
65
+ },
66
+ titleClass: {
67
+ type: String,
68
+ default: ""
69
+ },
70
+ subtitle: {
71
+ type: String,
72
+ default: ""
73
+ },
74
+ content: {
75
+ type: String,
76
+ default: ""
77
+ },
78
+ backdropDismiss: Boolean,
79
+ maximized: Boolean,
80
+ fullWidth: Boolean,
81
+ fullHeight: Boolean,
82
+ doneText: {
83
+ type: String,
84
+ default: "Done"
85
+ }
86
+ });
87
+
88
+ function onClose() {
89
+ emit("update:model-value", false);
90
+ emit("close");
91
+ }
92
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <ConfirmDialog
3
+ v-bind="$props"
4
+ @confirm="$emit('confirm', newInput)"
5
+ @close="$emit('close', $event)"
6
+ >
7
+ <slot>
8
+ <QInput
9
+ v-model="newInput"
10
+ type="number"
11
+ class="w-full"
12
+ @input="$emit('update:input', $event)"
13
+ />
14
+ </slot>
15
+ </ConfirmDialog>
16
+ </template>
17
+ <script setup>
18
+ import ConfirmDialog from "danx/src/components/Utility/Dialogs/ConfirmDialog";
19
+ import { ref } from "vue";
20
+
21
+ defineEmits(["confirm", "close", "update:input"]);
22
+ const props = defineProps({
23
+ ...ConfirmDialog.props,
24
+ title: {
25
+ type: String,
26
+ default: "Enter Value"
27
+ },
28
+ input: {
29
+ type: [Number, String],
30
+ default: ""
31
+ }
32
+ });
33
+
34
+ const newInput = ref(props.input);
35
+ </script>
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <TransitionGroup tag="div" :name="name" class="relative">
3
+ <slot />
4
+ </TransitionGroup>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineProps({
9
+ name: {
10
+ type: String,
11
+ default: "fade-list"
12
+ }
13
+ });
14
+ </script>
15
+
16
+ <style lang="scss">
17
+ [class*="list-move"], /* apply transition to moving elements */
18
+ [class*="list-enter-active"],
19
+ [class*=".list-leave-active"] {
20
+ transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
21
+ }
22
+
23
+ /* ensure leaving items are taken out of layout flow so that moving
24
+ animations can be calculated correctly. */
25
+ [class*="list-leave-active"] {
26
+ position: absolute !important;
27
+ }
28
+
29
+ /** Default List */
30
+ .list-enter-from,
31
+ .list-leave-to {
32
+ opacity: 0;
33
+ transform: translateX(30px);
34
+ }
35
+
36
+ /** Fade */
37
+ .fade-list-enter-from,
38
+ .fade-list-leave-to {
39
+ opacity: 0;
40
+ transform: scaleY(0.01) translate(30px, 0);
41
+ }
42
+
43
+ /** Fade Down */
44
+ .fade-down-list-enter-from,
45
+ .fade-down-list-leave-to {
46
+ opacity: 0;
47
+ transform-origin: top;
48
+ transform: translateY(30px);
49
+ }
50
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <Transition :name="name">
3
+ <slot />
4
+ </Transition>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineProps({
9
+ name: {
10
+ type: String,
11
+ default: "slide"
12
+ }
13
+ });
14
+ </script>
15
+ <style lang="scss">
16
+ .slide-enter-active,
17
+ .slide-leave-active,
18
+ .slide-right-enter-active,
19
+ .slide-right-leave-active {
20
+ transition: all 0.3s linear;
21
+ width: 100%;
22
+ }
23
+
24
+ .slide-right-enter-active,
25
+ .slide-enter-active {
26
+ position: absolute !important;
27
+ top: 0;
28
+ }
29
+
30
+ /** Slide Left (default) */
31
+ .slide-enter-from {
32
+ right: -120%;
33
+ }
34
+
35
+ .slide-enter-to {
36
+ right: 0;
37
+ }
38
+
39
+ .slide-leave-to {
40
+ left: -120%;
41
+ }
42
+
43
+ .slide-leave-from {
44
+ left: 0;
45
+ }
46
+
47
+ /** Slide Right */
48
+ .slide-right-enter-from {
49
+ left: -120%;
50
+ }
51
+
52
+ .slide-right-enter-to {
53
+ left: 0;
54
+ }
55
+
56
+ .slide-right-leave-to {
57
+ right: -120%;
58
+ }
59
+
60
+ .slide-right-leave-from {
61
+ right: 0;
62
+ }
63
+ </style>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <TransitionGroup
3
+ ref="list"
4
+ tag="div"
5
+ appear
6
+ :css="false"
7
+ @before-enter="onBeforeEnter"
8
+ @enter="onEnter"
9
+ @leave="onLeave"
10
+ >
11
+ <slot />
12
+ </TransitionGroup>
13
+ </template>
14
+
15
+ <script setup>
16
+ import gsap from "gsap";
17
+ import { isTestMode } from "src/helpers/testing";
18
+ import { computed, ref } from "vue";
19
+
20
+ const props = defineProps({
21
+ height: {
22
+ type: [String, Number],
23
+ default: "auto"
24
+ },
25
+ duration: {
26
+ type: Number,
27
+ default: 0.5
28
+ },
29
+ delayOffset: {
30
+ type: Number,
31
+ default: 0.5
32
+ }
33
+ });
34
+
35
+ const list = ref(null);
36
+ const indexDelay = computed(() => {
37
+ return props.delayOffset / list.value.$el.children.length;
38
+ });
39
+
40
+ function onBeforeEnter(el) {
41
+ if (isTestMode.value) {
42
+ return;
43
+ }
44
+ el.style.opacity = 0;
45
+ el.style.height = 0;
46
+ }
47
+
48
+ function onEnter(el, onComplete) {
49
+ if (isTestMode.value) {
50
+ onComplete();
51
+ return;
52
+ }
53
+
54
+ gsap.to(el, {
55
+ opacity: 1,
56
+ duration: props.duration,
57
+ height: props.height,
58
+ delay: el.dataset.index * indexDelay.value,
59
+ onComplete
60
+ });
61
+ }
62
+
63
+ function onLeave(el, onComplete) {
64
+ if (isTestMode.value) {
65
+ onComplete();
66
+ return;
67
+ }
68
+
69
+ gsap.to(el, {
70
+ opacity: 0,
71
+ height: 0,
72
+ duration: props.duration,
73
+ delay: el.dataset.index * indexDelay.value,
74
+ onComplete
75
+ });
76
+ }
77
+ </script>
78
+
79
+ <style scoped lang="scss">
80
+ .list-move,
81
+ .list-enter-active,
82
+ .list-leave-active {
83
+ transition: all 0.5s ease;
84
+ }
85
+
86
+ .list-enter-from,
87
+ .list-leave-to {
88
+ opacity: 0;
89
+ transform: translateX(2em);
90
+ }
91
+
92
+ /* ensure leaving items are taken out of layout flow so that moving
93
+ animations can be calculated correctly. */
94
+ .list-leave-active {
95
+ position: absolute;
96
+ }
97
+ </style>