@proj-airi/ui 0.7.1 → 0.7.2-beta.2
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/package.json +1 -1
- package/src/components/Animations/BidirectionalTransition.vue +20 -0
- package/src/components/Animations/TransitionVertical.vue +24 -1
- package/src/components/Animations/index.ts +1 -0
- package/src/components/Form/Input/BasicInputFile.vue +21 -5
- package/src/components/Form/Input/InputFile.vue +18 -40
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
fromClass?: string
|
|
4
|
+
activeClass?: string
|
|
5
|
+
toClass?: string
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<Transition
|
|
11
|
+
:enter-from-class="fromClass"
|
|
12
|
+
:enter-active-class="activeClass"
|
|
13
|
+
:enter-to-class="toClass"
|
|
14
|
+
:leave-from-class="toClass"
|
|
15
|
+
:leave-active-class="activeClass"
|
|
16
|
+
:leave-to-class="fromClass"
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</Transition>
|
|
20
|
+
</template>
|
|
@@ -48,6 +48,9 @@ function getElementStyle(element: HTMLElement) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
let animation: Animation | null = null
|
|
52
|
+
let lastElement: HTMLElement | null = null
|
|
53
|
+
|
|
51
54
|
function prepareElement(element: HTMLElement, initialStyle: initialStyle) {
|
|
52
55
|
const { width } = getComputedStyle(element)
|
|
53
56
|
element.style.width = width
|
|
@@ -72,7 +75,8 @@ function animateTransition(
|
|
|
72
75
|
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
|
|
73
76
|
options?: number | KeyframeAnimationOptions,
|
|
74
77
|
) {
|
|
75
|
-
|
|
78
|
+
lastElement = element
|
|
79
|
+
animation = element.animate(keyframes, options)
|
|
76
80
|
// Set height to 'auto' to restore it after animation
|
|
77
81
|
element.style.height = initialStyle.height
|
|
78
82
|
animation.onfinish = () => {
|
|
@@ -106,9 +110,26 @@ function getEnterKeyframes(height: string, initialStyle: initialStyle) {
|
|
|
106
110
|
]
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
function cancelAnimation(HTMLElement: HTMLElement, overflow: string, done: () => void) {
|
|
114
|
+
if (HTMLElement !== lastElement)
|
|
115
|
+
return false
|
|
116
|
+
if (!animation)
|
|
117
|
+
return false
|
|
118
|
+
if (animation.playState !== 'running')
|
|
119
|
+
return false
|
|
120
|
+
animation.onfinish = () => {
|
|
121
|
+
HTMLElement.style.overflow = overflow
|
|
122
|
+
done()
|
|
123
|
+
}
|
|
124
|
+
animation.reverse()
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
|
|
109
128
|
function enterTransition(element: Element, done: () => void) {
|
|
110
129
|
const HTMLElement = element as HTMLElement
|
|
111
130
|
const initialStyle = getElementStyle(HTMLElement)
|
|
131
|
+
if (cancelAnimation(HTMLElement, initialStyle.overflow, done))
|
|
132
|
+
return
|
|
112
133
|
const height = prepareElement(HTMLElement, initialStyle)
|
|
113
134
|
const keyframes = getEnterKeyframes(height, initialStyle)
|
|
114
135
|
const options = { duration: props.duration, easing: props.easingEnter }
|
|
@@ -118,6 +139,8 @@ function enterTransition(element: Element, done: () => void) {
|
|
|
118
139
|
function leaveTransition(element: Element, done: () => void) {
|
|
119
140
|
const HTMLElement = element as HTMLElement
|
|
120
141
|
const initialStyle = getElementStyle(HTMLElement)
|
|
142
|
+
if (cancelAnimation(HTMLElement, initialStyle.overflow, done))
|
|
143
|
+
return
|
|
121
144
|
const { height } = getComputedStyle(HTMLElement)
|
|
122
145
|
HTMLElement.style.height = height
|
|
123
146
|
HTMLElement.style.overflow = 'hidden'
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import { useDebounce } from '@vueuse/core'
|
|
3
3
|
import { ref } from 'vue'
|
|
4
4
|
|
|
5
|
-
defineProps<{
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
class?: string | string[] | null
|
|
7
|
+
isDraggingClasses?: string | string[] | null
|
|
8
|
+
isNotDraggingClasses?: string | string[] | null
|
|
6
9
|
accept?: string
|
|
7
10
|
multiple?: boolean
|
|
8
11
|
}>()
|
|
@@ -14,13 +17,20 @@ const isDragging = ref(false)
|
|
|
14
17
|
const isDraggingDebounced = useDebounce(isDragging, 150)
|
|
15
18
|
|
|
16
19
|
function handleFileChange(e: Event) {
|
|
20
|
+
files.value = []
|
|
21
|
+
|
|
17
22
|
const input = e.target as HTMLInputElement
|
|
23
|
+
if (!input.files)
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < input.files?.length; i++) {
|
|
27
|
+
files.value.push(input.files[i])
|
|
28
|
+
}
|
|
18
29
|
|
|
19
|
-
if (
|
|
20
|
-
firstFile.value =
|
|
30
|
+
if (files.value && files.value.length > 0) {
|
|
31
|
+
firstFile.value = files.value[0]
|
|
21
32
|
}
|
|
22
33
|
|
|
23
|
-
files.value = Array.from(input.files || [])
|
|
24
34
|
isDragging.value = false
|
|
25
35
|
}
|
|
26
36
|
</script>
|
|
@@ -28,6 +38,12 @@ function handleFileChange(e: Event) {
|
|
|
28
38
|
<template>
|
|
29
39
|
<label
|
|
30
40
|
relative cursor-pointer
|
|
41
|
+
:class="[
|
|
42
|
+
props.class,
|
|
43
|
+
isDragging
|
|
44
|
+
? [...Array.isArray(isDraggingClasses) ? isDraggingClasses : [isDraggingClasses]]
|
|
45
|
+
: [...Array.isArray(isNotDraggingClasses) ? isNotDraggingClasses : [isNotDraggingClasses]],
|
|
46
|
+
]"
|
|
31
47
|
@dragover="isDragging = true"
|
|
32
48
|
@dragleave="isDragging = false"
|
|
33
49
|
>
|
|
@@ -35,7 +51,7 @@ function handleFileChange(e: Event) {
|
|
|
35
51
|
type="file"
|
|
36
52
|
:accept="accept"
|
|
37
53
|
:multiple="multiple"
|
|
38
|
-
class="absolute inset-0 h-
|
|
54
|
+
class="absolute inset-0 h-0 w-0 cursor-pointer appearance-none opacity-0"
|
|
39
55
|
@change="handleFileChange"
|
|
40
56
|
>
|
|
41
57
|
<slot :is-dragging="isDraggingDebounced" :first-file="firstFile" :files="files" />
|
|
@@ -1,71 +1,49 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import { ref } from 'vue'
|
|
2
|
+
import BasicInputFile from './BasicInputFile.vue'
|
|
4
3
|
|
|
5
4
|
defineProps<{
|
|
6
5
|
accept?: string
|
|
7
6
|
multiple?: boolean
|
|
8
7
|
}>()
|
|
9
|
-
|
|
10
|
-
const files = defineModel<File[]>({ required: false, default: () => [] })
|
|
11
|
-
const firstFile = ref<File>()
|
|
12
|
-
|
|
13
|
-
const isDragging = ref(false)
|
|
14
|
-
const isDraggingDebounced = useDebounce(isDragging, 150)
|
|
15
|
-
|
|
16
|
-
function handleFileChange(e: Event) {
|
|
17
|
-
const input = e.target as HTMLInputElement
|
|
18
|
-
|
|
19
|
-
if (input.files && input.files.length > 0) {
|
|
20
|
-
firstFile.value = input.files[0]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
files.value = Array.from(input.files || [])
|
|
24
|
-
isDragging.value = false
|
|
25
|
-
}
|
|
26
8
|
</script>
|
|
27
9
|
|
|
28
10
|
<template>
|
|
29
|
-
<
|
|
30
|
-
relative
|
|
11
|
+
<BasicInputFile
|
|
31
12
|
class="min-h-[120px] flex flex-col cursor-pointer items-center justify-center rounded-xl p-6"
|
|
32
|
-
:
|
|
33
|
-
|
|
34
|
-
|
|
13
|
+
:is-not-dragging-classes="[
|
|
14
|
+
'border-neutral-200 dark:border-neutral-700 hover:border-primary-300 dark:hover:border-primary-700',
|
|
15
|
+
'bg-white/60 dark:bg-black/30 hover:bg-white/80 dark:hover:bg-black/40',
|
|
16
|
+
]"
|
|
17
|
+
:is-dragging-classes="[
|
|
18
|
+
'border-primary-400 dark:border-primary-600 hover:border-primary-300 dark:hover:border-primary-700',
|
|
19
|
+
'bg-primary-50/5 dark:bg-primary-900/5',
|
|
35
20
|
]"
|
|
36
21
|
border="dashed 2"
|
|
37
22
|
transition="all duration-300"
|
|
38
|
-
|
|
23
|
+
opacity-95
|
|
39
24
|
hover="scale-100 opacity-100 shadow-md dark:shadow-lg"
|
|
40
|
-
|
|
41
|
-
|
|
25
|
+
|
|
26
|
+
:accept="accept"
|
|
27
|
+
:multiple="multiple"
|
|
42
28
|
>
|
|
43
|
-
<
|
|
44
|
-
type="file"
|
|
45
|
-
:accept="accept"
|
|
46
|
-
:multiple="multiple"
|
|
47
|
-
cursor-pointer
|
|
48
|
-
class="absolute inset-0 h-full w-full opacity-0"
|
|
49
|
-
@change="handleFileChange"
|
|
50
|
-
>
|
|
51
|
-
<slot :is-dragging="isDraggingDebounced" :first-file="firstFile" :files="files">
|
|
29
|
+
<template #default="{ isDragging }">
|
|
52
30
|
<div
|
|
53
31
|
class="flex flex-col items-center"
|
|
54
32
|
:class="[
|
|
55
|
-
|
|
33
|
+
isDragging ? 'text-primary-500 dark:text-primary-400' : 'text-neutral-400 dark:text-neutral-500',
|
|
56
34
|
]"
|
|
57
35
|
>
|
|
58
36
|
<div i-solar:upload-square-line-duotone mb-2 text-5xl />
|
|
59
37
|
<p font-medium text="center lg">
|
|
60
38
|
Upload
|
|
61
39
|
</p>
|
|
62
|
-
<p v-if="
|
|
40
|
+
<p v-if="isDragging" text="center" text-sm>
|
|
63
41
|
Release to upload
|
|
64
42
|
</p>
|
|
65
43
|
<p v-else text="center" text-sm>
|
|
66
44
|
Click or drag and drop a file here
|
|
67
45
|
</p>
|
|
68
46
|
</div>
|
|
69
|
-
</
|
|
70
|
-
</
|
|
47
|
+
</template>
|
|
48
|
+
</BasicInputFile>
|
|
71
49
|
</template>
|