@a-vision-software/vue-input-components 1.2.4 → 1.2.6
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 +2 -1
- package/src/App.vue +7 -0
- package/src/assets/colors.css +67 -0
- package/src/assets/logo.svg +1 -0
- package/src/assets/main.css +160 -0
- package/src/components/Action.vue +208 -0
- package/src/components/FileUpload.vue +310 -0
- package/src/components/Navigation.vue +634 -0
- package/src/components/TextInput.vue +503 -0
- package/src/env.d.ts +9 -0
- package/src/index.ts +8 -0
- package/src/main.ts +21 -0
- package/src/router/index.ts +44 -0
- package/src/types/index.ts +3 -0
- package/src/types/navigation.ts +32 -0
- package/src/types.d.ts +23 -0
- package/src/types.ts +108 -0
- package/src/views/ActionTestView.vue +307 -0
- package/src/views/DashboardView.vue +98 -0
- package/src/views/FileUploadTestView.vue +177 -0
- package/src/views/NavigationTestView.vue +319 -0
- package/src/views/TextInputTestView.vue +372 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { Component } from 'vue'
|
|
2
|
+
|
|
3
|
+
export interface TextInputProps {
|
|
4
|
+
modelValue: string
|
|
5
|
+
type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'date'
|
|
6
|
+
label?: string
|
|
7
|
+
placeholder?: string
|
|
8
|
+
icon?: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
readonly?: boolean
|
|
11
|
+
maxlength?: number
|
|
12
|
+
error?: string
|
|
13
|
+
min?: string
|
|
14
|
+
max?: string
|
|
15
|
+
required?: boolean
|
|
16
|
+
success?: string
|
|
17
|
+
labelPosition?: 'top' | 'left'
|
|
18
|
+
labelAlign?: 'left' | 'right' | 'center'
|
|
19
|
+
totalWidth?: string
|
|
20
|
+
inputWidth?: string
|
|
21
|
+
labelWidth?: string
|
|
22
|
+
autosave?: (value: string) => Promise<void>
|
|
23
|
+
isTextarea?: boolean
|
|
24
|
+
maxHeight?: string
|
|
25
|
+
height?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FileUploadProps {
|
|
29
|
+
modelValue: File[]
|
|
30
|
+
label?: string
|
|
31
|
+
placeholder?: string
|
|
32
|
+
error?: string
|
|
33
|
+
disabled?: boolean
|
|
34
|
+
required?: boolean
|
|
35
|
+
multiple?: boolean
|
|
36
|
+
accept?: string
|
|
37
|
+
maxSize?: number
|
|
38
|
+
uploadUrl?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FileUploadEmits {
|
|
42
|
+
(e: 'update:modelValue', files: File[]): void
|
|
43
|
+
(e: 'files-selected', files: File[]): void
|
|
44
|
+
(e: 'start-upload', files: File[]): void
|
|
45
|
+
(e: 'upload-progress', progress: number): void
|
|
46
|
+
(e: 'upload-success', response: any): void
|
|
47
|
+
(e: 'upload-error', error: Error): void
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type TextInputComponent = {
|
|
51
|
+
focus: () => void
|
|
52
|
+
blur: () => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type FileUploadComponent = Component<FileUploadProps>
|
|
56
|
+
|
|
57
|
+
export interface ActionProps {
|
|
58
|
+
icon?: string
|
|
59
|
+
label?: string
|
|
60
|
+
href?: string
|
|
61
|
+
type?: 'button' | 'submit' | 'reset'
|
|
62
|
+
disabled?: boolean
|
|
63
|
+
color?: string
|
|
64
|
+
size?: 'small' | 'regular' | 'large'
|
|
65
|
+
variant?: 'solid' | 'transparent'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type ActionComponent = {
|
|
69
|
+
focus: () => void
|
|
70
|
+
blur: () => void
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface NavigationItem {
|
|
74
|
+
id: string
|
|
75
|
+
label: string
|
|
76
|
+
icon?: string
|
|
77
|
+
url?: string
|
|
78
|
+
children?: NavigationItem[]
|
|
79
|
+
type?: 'tile' | 'tab' | 'dropdown'
|
|
80
|
+
color?: string
|
|
81
|
+
disabled?: boolean
|
|
82
|
+
alignment?: 'start' | 'end'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface NavigationProps {
|
|
86
|
+
items: NavigationItem[]
|
|
87
|
+
type?: 'tiles' | 'tabs' | 'dropdowns'
|
|
88
|
+
orientation?: 'horizontal' | 'vertical'
|
|
89
|
+
activeItem?: string
|
|
90
|
+
color?: string
|
|
91
|
+
hoverColor?: string
|
|
92
|
+
activeColor?: string
|
|
93
|
+
disabledColor?: string
|
|
94
|
+
iconSize?: string
|
|
95
|
+
gap?: string
|
|
96
|
+
padding?: string
|
|
97
|
+
borderRadius?: string
|
|
98
|
+
showIcons?: boolean
|
|
99
|
+
activeItemAlignment?: 'start' | 'end'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface NavigationComponent {
|
|
103
|
+
props: NavigationProps
|
|
104
|
+
emits: {
|
|
105
|
+
(e: 'item-click', item: NavigationItem): void
|
|
106
|
+
(e: 'update:activeItem', id: string): void
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="action-test">
|
|
3
|
+
<div class="navigation">
|
|
4
|
+
<router-link to="/" class="back-link">
|
|
5
|
+
<font-awesome-icon icon="arrow-left" />
|
|
6
|
+
<span>Back to Dashboard</span>
|
|
7
|
+
</router-link>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="content">
|
|
11
|
+
<h1>Action Component Test</h1>
|
|
12
|
+
|
|
13
|
+
<div class="color-controls">
|
|
14
|
+
<div class="color-picker">
|
|
15
|
+
<label for="action-color">Action Color:</label>
|
|
16
|
+
<input type="color" id="action-color" v-model="actionColor" />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="test-section">
|
|
21
|
+
<h2>Basic Actions</h2>
|
|
22
|
+
<div class="action-group">
|
|
23
|
+
<Action
|
|
24
|
+
icon="save"
|
|
25
|
+
label="Save"
|
|
26
|
+
:color="actionColor"
|
|
27
|
+
variant="transparent"
|
|
28
|
+
@click="handleActionClick('Save')"
|
|
29
|
+
/>
|
|
30
|
+
<Action
|
|
31
|
+
icon="trash"
|
|
32
|
+
label="Delete"
|
|
33
|
+
:color="actionColor"
|
|
34
|
+
@click="handleActionClick('Delete')"
|
|
35
|
+
/>
|
|
36
|
+
<Action
|
|
37
|
+
icon="edit"
|
|
38
|
+
label="Edit"
|
|
39
|
+
:color="actionColor"
|
|
40
|
+
@click="handleActionClick('Edit')"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="test-section">
|
|
46
|
+
<h2>Icon Only Actions</h2>
|
|
47
|
+
<div class="action-group">
|
|
48
|
+
<Action icon="plus" :color="actionColor" @click="handleActionClick('Add')" />
|
|
49
|
+
<Action icon="search" :color="actionColor" @click="handleActionClick('Search')" />
|
|
50
|
+
<Action icon="cog" :color="actionColor" @click="handleActionClick('Settings')" />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="test-section">
|
|
55
|
+
<h2>Label Only Actions</h2>
|
|
56
|
+
<div class="action-group">
|
|
57
|
+
<Action
|
|
58
|
+
label="Submit"
|
|
59
|
+
type="submit"
|
|
60
|
+
:color="actionColor"
|
|
61
|
+
size="large"
|
|
62
|
+
@click="handleActionClick('Submit')"
|
|
63
|
+
/>
|
|
64
|
+
<Action label="Cancel" :color="actionColor" @click="handleActionClick('Cancel')" />
|
|
65
|
+
<Action
|
|
66
|
+
label="Reset"
|
|
67
|
+
type="reset"
|
|
68
|
+
:color="actionColor"
|
|
69
|
+
size="small"
|
|
70
|
+
@click="handleActionClick('Reset')"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="test-section">
|
|
76
|
+
<h2>Link Actions</h2>
|
|
77
|
+
<div class="action-group">
|
|
78
|
+
<Action
|
|
79
|
+
icon="external-link"
|
|
80
|
+
label="View Details"
|
|
81
|
+
href="/details"
|
|
82
|
+
:color="actionColor"
|
|
83
|
+
size="large"
|
|
84
|
+
@click="handleActionClick('View Details')"
|
|
85
|
+
/>
|
|
86
|
+
<Action
|
|
87
|
+
icon="download"
|
|
88
|
+
label="Download"
|
|
89
|
+
href="/download"
|
|
90
|
+
:color="actionColor"
|
|
91
|
+
@click="handleActionClick('Download')"
|
|
92
|
+
/>
|
|
93
|
+
<Action
|
|
94
|
+
label="Share"
|
|
95
|
+
href="/share"
|
|
96
|
+
:color="actionColor"
|
|
97
|
+
size="small"
|
|
98
|
+
@click="handleActionClick('Share')"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="test-section">
|
|
104
|
+
<h2>Disabled Actions</h2>
|
|
105
|
+
<div class="action-group">
|
|
106
|
+
<Action
|
|
107
|
+
icon="lock"
|
|
108
|
+
label="Locked"
|
|
109
|
+
disabled
|
|
110
|
+
:color="actionColor"
|
|
111
|
+
@click="handleActionClick('Locked')"
|
|
112
|
+
/>
|
|
113
|
+
<Action
|
|
114
|
+
icon="check"
|
|
115
|
+
label="Completed"
|
|
116
|
+
disabled
|
|
117
|
+
:color="actionColor"
|
|
118
|
+
@click="handleActionClick('Completed')"
|
|
119
|
+
/>
|
|
120
|
+
<Action
|
|
121
|
+
icon="ban"
|
|
122
|
+
label="Disabled"
|
|
123
|
+
disabled
|
|
124
|
+
:color="actionColor"
|
|
125
|
+
@click="handleActionClick('Disabled')"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="test-section">
|
|
131
|
+
<h2>Mixed Actions</h2>
|
|
132
|
+
<div class="action-group">
|
|
133
|
+
<Action
|
|
134
|
+
icon="plus"
|
|
135
|
+
label="Add New"
|
|
136
|
+
:color="actionColor"
|
|
137
|
+
@click="handleActionClick('Add New')"
|
|
138
|
+
/>
|
|
139
|
+
<Action icon="trash" :color="actionColor" @click="handleActionClick('Delete')" />
|
|
140
|
+
<Action label="Cancel" :color="actionColor" @click="handleActionClick('Cancel')" />
|
|
141
|
+
<Action
|
|
142
|
+
icon="external-link"
|
|
143
|
+
label="View"
|
|
144
|
+
href="/view"
|
|
145
|
+
:color="actionColor"
|
|
146
|
+
@click="handleActionClick('View')"
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div class="test-section">
|
|
152
|
+
<h2>Action Log</h2>
|
|
153
|
+
<div class="action-log">
|
|
154
|
+
<div v-for="(log, index) in actionLog" :key="index" class="log-entry">
|
|
155
|
+
{{ log }}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</template>
|
|
162
|
+
|
|
163
|
+
<script setup lang="ts">
|
|
164
|
+
import { ref } from 'vue'
|
|
165
|
+
import Action from '../components/Action.vue'
|
|
166
|
+
|
|
167
|
+
const actionLog = ref<string[]>([])
|
|
168
|
+
const actionColor = ref('#4CAF50')
|
|
169
|
+
|
|
170
|
+
const handleActionClick = (action: string) => {
|
|
171
|
+
actionLog.value.unshift(`[${new Date().toLocaleTimeString()}] Clicked: ${action}`)
|
|
172
|
+
if (actionLog.value.length > 10) {
|
|
173
|
+
actionLog.value.pop()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
|
|
178
|
+
<style scoped>
|
|
179
|
+
.action-test {
|
|
180
|
+
min-height: 100vh;
|
|
181
|
+
background-image: url('https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?auto=format&fit=crop&w=1920&q=80');
|
|
182
|
+
background-size: cover;
|
|
183
|
+
background-position: center;
|
|
184
|
+
background-attachment: fixed;
|
|
185
|
+
position: relative;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.action-test::before {
|
|
189
|
+
content: '';
|
|
190
|
+
position: absolute;
|
|
191
|
+
top: 0;
|
|
192
|
+
left: 0;
|
|
193
|
+
right: 0;
|
|
194
|
+
bottom: 0;
|
|
195
|
+
background: rgba(0, 0, 0, 0.5);
|
|
196
|
+
backdrop-filter: blur(4px);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.content {
|
|
200
|
+
position: relative;
|
|
201
|
+
z-index: 1;
|
|
202
|
+
max-width: 1200px;
|
|
203
|
+
margin: 0 auto;
|
|
204
|
+
padding: 2rem;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.navigation {
|
|
208
|
+
position: relative;
|
|
209
|
+
z-index: 2;
|
|
210
|
+
padding: 2rem;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.back-link {
|
|
214
|
+
display: inline-flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 0.5rem;
|
|
217
|
+
color: white;
|
|
218
|
+
text-decoration: none;
|
|
219
|
+
font-size: 0.875rem;
|
|
220
|
+
transition: opacity 0.2s ease;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.back-link:hover {
|
|
224
|
+
opacity: 0.8;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.color-controls {
|
|
228
|
+
display: grid;
|
|
229
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
230
|
+
gap: 1rem;
|
|
231
|
+
margin-bottom: 2rem;
|
|
232
|
+
padding: 1rem;
|
|
233
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
234
|
+
backdrop-filter: blur(8px);
|
|
235
|
+
border-radius: 0.5rem;
|
|
236
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.color-picker {
|
|
240
|
+
display: flex;
|
|
241
|
+
flex-direction: column;
|
|
242
|
+
gap: 0.5rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.color-picker label {
|
|
246
|
+
font-size: 0.875rem;
|
|
247
|
+
color: white;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.color-picker input[type='color'] {
|
|
251
|
+
width: 100%;
|
|
252
|
+
height: 2.5rem;
|
|
253
|
+
padding: 0.25rem;
|
|
254
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
255
|
+
border-radius: 0.25rem;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.test-section {
|
|
261
|
+
margin-bottom: 3rem;
|
|
262
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
263
|
+
backdrop-filter: blur(8px);
|
|
264
|
+
border-radius: 0.5rem;
|
|
265
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
266
|
+
padding: 1.5rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
h1 {
|
|
270
|
+
margin-bottom: 2rem;
|
|
271
|
+
color: white;
|
|
272
|
+
text-align: center;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
h2 {
|
|
276
|
+
margin-bottom: 1rem;
|
|
277
|
+
color: white;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.action-group {
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-wrap: wrap;
|
|
283
|
+
gap: 1rem;
|
|
284
|
+
margin-bottom: 1rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.action-log {
|
|
288
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
289
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
290
|
+
border-radius: 0.375rem;
|
|
291
|
+
padding: 1rem;
|
|
292
|
+
max-height: 200px;
|
|
293
|
+
overflow-y: auto;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.log-entry {
|
|
297
|
+
padding: 0.5rem;
|
|
298
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
299
|
+
font-family: monospace;
|
|
300
|
+
font-size: 0.875rem;
|
|
301
|
+
color: white;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.log-entry:last-child {
|
|
305
|
+
border-bottom: none;
|
|
306
|
+
}
|
|
307
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="dashboard">
|
|
3
|
+
<h1>Vue Input Components</h1>
|
|
4
|
+
<div class="tile-grid">
|
|
5
|
+
<router-link to="/text-input" class="tile">
|
|
6
|
+
<div class="tile-content">
|
|
7
|
+
<font-awesome-icon icon="keyboard" class="tile-icon" />
|
|
8
|
+
<h2>Text Input</h2>
|
|
9
|
+
<p>Test the text input component with various configurations</p>
|
|
10
|
+
</div>
|
|
11
|
+
</router-link>
|
|
12
|
+
<router-link to="/file-upload" class="tile">
|
|
13
|
+
<div class="tile-content">
|
|
14
|
+
<font-awesome-icon icon="file-upload" class="tile-icon" />
|
|
15
|
+
<h2>File Upload</h2>
|
|
16
|
+
<p>Test the file upload component with drag and drop support</p>
|
|
17
|
+
</div>
|
|
18
|
+
</router-link>
|
|
19
|
+
<router-link to="/action" class="tile">
|
|
20
|
+
<div class="tile-content">
|
|
21
|
+
<font-awesome-icon icon="hand-pointer" class="tile-icon" />
|
|
22
|
+
<h2>Action</h2>
|
|
23
|
+
<p>Test the action component with various button and link styles</p>
|
|
24
|
+
</div>
|
|
25
|
+
</router-link>
|
|
26
|
+
<router-link to="/navigation" class="tile">
|
|
27
|
+
<div class="tile-content">
|
|
28
|
+
<font-awesome-icon icon="bars" class="tile-icon" />
|
|
29
|
+
<h2>Navigation</h2>
|
|
30
|
+
<p>Test the navigation component</p>
|
|
31
|
+
</div>
|
|
32
|
+
</router-link>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts"></script>
|
|
38
|
+
|
|
39
|
+
<style scoped>
|
|
40
|
+
.dashboard {
|
|
41
|
+
max-width: 1200px;
|
|
42
|
+
margin: 0 auto;
|
|
43
|
+
padding: 2rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
h1 {
|
|
47
|
+
text-align: center;
|
|
48
|
+
margin-bottom: 2rem;
|
|
49
|
+
color: var(--text-primary);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.tile-grid {
|
|
53
|
+
display: grid;
|
|
54
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
55
|
+
gap: 2rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.tile {
|
|
59
|
+
text-decoration: none;
|
|
60
|
+
color: inherit;
|
|
61
|
+
background-color: white;
|
|
62
|
+
border-radius: 0.5rem;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
65
|
+
transition:
|
|
66
|
+
transform 0.2s ease-in-out,
|
|
67
|
+
box-shadow 0.2s ease-in-out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.tile:hover {
|
|
71
|
+
transform: translateY(-4px);
|
|
72
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.tile-content {
|
|
76
|
+
padding: 2rem;
|
|
77
|
+
text-align: center;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.tile-icon {
|
|
81
|
+
font-size: 2.5rem;
|
|
82
|
+
margin-bottom: 1rem;
|
|
83
|
+
color: var(--primary);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.tile h2 {
|
|
87
|
+
margin: 0 0 0.5rem 0;
|
|
88
|
+
font-size: 1.5rem;
|
|
89
|
+
color: var(--text-primary);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.tile p {
|
|
93
|
+
margin: 0;
|
|
94
|
+
color: var(--text-secondary);
|
|
95
|
+
font-size: 1rem;
|
|
96
|
+
line-height: 1.5;
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="file-upload-test">
|
|
3
|
+
<div class="header">
|
|
4
|
+
<router-link to="/" class="back-link">
|
|
5
|
+
<font-awesome-icon icon="home" />
|
|
6
|
+
<span>Back to Dashboard</span>
|
|
7
|
+
</router-link>
|
|
8
|
+
<h1>File Upload Test</h1>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="file-upload-grid">
|
|
11
|
+
<div class="upload-section">
|
|
12
|
+
<h2>Basic Upload</h2>
|
|
13
|
+
<FileUpload
|
|
14
|
+
icon="upload"
|
|
15
|
+
upload-url="https://httpbin.org/post"
|
|
16
|
+
@upload-complete="handleUploadComplete"
|
|
17
|
+
@upload-error="handleUploadError"
|
|
18
|
+
/>
|
|
19
|
+
<div
|
|
20
|
+
v-if="uploadStatus.basic && uploadStatus.basic.message"
|
|
21
|
+
:class="['status-message', uploadStatus.basic.type]"
|
|
22
|
+
>
|
|
23
|
+
{{ uploadStatus.basic.message }}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="upload-section">
|
|
27
|
+
<h2>Image Upload</h2>
|
|
28
|
+
<FileUpload
|
|
29
|
+
icon="image"
|
|
30
|
+
@files-selected="handleFilesSelected"
|
|
31
|
+
@start-upload="handleStartUpload"
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="upload-section">
|
|
35
|
+
<h2>Document Upload</h2>
|
|
36
|
+
<FileUpload icon="file" />
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
import { ref } from 'vue'
|
|
44
|
+
import FileUpload from '@/components/FileUpload.vue'
|
|
45
|
+
|
|
46
|
+
interface UploadStatus {
|
|
47
|
+
type: 'success' | 'error'
|
|
48
|
+
message: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const uploadStatus = ref<Record<string, UploadStatus>>({
|
|
52
|
+
basic: { type: 'success', message: '' },
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const handleUploadComplete = (files: File[]) => {
|
|
56
|
+
uploadStatus.value.basic = {
|
|
57
|
+
type: 'success',
|
|
58
|
+
message: `Successfully uploaded ${files.length} file(s)`,
|
|
59
|
+
}
|
|
60
|
+
// Clear status after 3 seconds
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
uploadStatus.value.basic.message = ''
|
|
63
|
+
}, 3000)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const handleUploadError = (error: string) => {
|
|
67
|
+
uploadStatus.value.basic = {
|
|
68
|
+
type: 'error',
|
|
69
|
+
message: error,
|
|
70
|
+
}
|
|
71
|
+
// Clear status after 3 seconds
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
uploadStatus.value.basic.message = ''
|
|
74
|
+
}, 3000)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const handleFilesSelected = (files: File[]) => {
|
|
78
|
+
console.log('Files selected:', files)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const handleStartUpload = (files: File[]) => {
|
|
82
|
+
console.log('Start upload:', files)
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<style scoped>
|
|
87
|
+
.file-upload-test {
|
|
88
|
+
padding: 2rem;
|
|
89
|
+
min-height: 100vh;
|
|
90
|
+
background-image: url('https://images.unsplash.com/photo-1519681393784-d120267933ba?auto=format&fit=crop&q=80&w=2070');
|
|
91
|
+
background-size: cover;
|
|
92
|
+
background-position: center;
|
|
93
|
+
background-attachment: fixed;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.file-upload-grid {
|
|
97
|
+
display: grid;
|
|
98
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
99
|
+
gap: 2rem;
|
|
100
|
+
margin-top: 2rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.upload-section {
|
|
104
|
+
background: rgba(255, 255, 255, 0.8);
|
|
105
|
+
padding: 2rem;
|
|
106
|
+
border-radius: 0.75rem;
|
|
107
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
108
|
+
backdrop-filter: blur(5px);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.upload-section h2 {
|
|
112
|
+
margin-bottom: 1rem;
|
|
113
|
+
color: #495057;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.status-message {
|
|
117
|
+
margin-top: 1rem;
|
|
118
|
+
padding: 0.5rem;
|
|
119
|
+
border-radius: 0.25rem;
|
|
120
|
+
font-size: 0.875rem;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.status-message.success {
|
|
124
|
+
background-color: #d4edda;
|
|
125
|
+
color: #155724;
|
|
126
|
+
border: 1px solid #c3e6cb;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.status-message.error {
|
|
130
|
+
background-color: #f8d7da;
|
|
131
|
+
color: #721c24;
|
|
132
|
+
border: 1px solid #f5c6cb;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.group {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
gap: 1rem;
|
|
139
|
+
padding: 2rem;
|
|
140
|
+
border-radius: 1rem;
|
|
141
|
+
background-color: rgba(255, 255, 255, 0.8);
|
|
142
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
143
|
+
backdrop-filter: blur(5px);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.error {
|
|
147
|
+
color: var(--error-color);
|
|
148
|
+
background-color: var(--error-color-light);
|
|
149
|
+
border: 1px solid var(--error-color);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.header {
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: 0.5rem;
|
|
156
|
+
margin-bottom: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.back-link {
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
gap: 0.25rem;
|
|
163
|
+
color: var(--text-color);
|
|
164
|
+
text-decoration: none;
|
|
165
|
+
font-size: 1rem;
|
|
166
|
+
transition: color 0.2s;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.back-link:hover {
|
|
170
|
+
color: var(--primary-color);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
h1 {
|
|
174
|
+
margin: 0;
|
|
175
|
+
color: #212529;
|
|
176
|
+
}
|
|
177
|
+
</style>
|