windly-ui 1.0.1 → 1.0.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/LICENSE +21 -0
- package/README.md +57 -33
- package/package.json +47 -15
- package/ui-library/dist/module.d.mts +3 -0
- package/ui-library/dist/module.json +8 -0
- package/ui-library/dist/module.mjs +22 -0
- package/ui-library/dist/types.d.mts +7 -0
- package/ui-library/components/Accordion.vue +0 -118
- package/ui-library/components/Avatar.vue +0 -121
- package/ui-library/components/Badge.vue +0 -116
- package/ui-library/components/Breadcrumbs.vue +0 -138
- package/ui-library/components/Button.vue +0 -154
- package/ui-library/components/Card.vue +0 -84
- package/ui-library/components/Checkbox.vue +0 -148
- package/ui-library/components/CodeBlock.vue +0 -99
- package/ui-library/components/ColorPicker.vue +0 -302
- package/ui-library/components/Date.vue +0 -240
- package/ui-library/components/Dialog.vue +0 -187
- package/ui-library/components/Divider.vue +0 -78
- package/ui-library/components/Drawer.vue +0 -67
- package/ui-library/components/Dropdown.vue +0 -248
- package/ui-library/components/FileUploader.vue +0 -330
- package/ui-library/components/Icon.vue +0 -82
- package/ui-library/components/Image.vue +0 -78
- package/ui-library/components/Input.vue +0 -531
- package/ui-library/components/Radio.vue +0 -356
- package/ui-library/components/ScrollArea.vue +0 -43
- package/ui-library/components/Select.vue +0 -309
- package/ui-library/components/Stepper.vue +0 -361
- package/ui-library/components/TabPanel.vue +0 -25
- package/ui-library/components/Table.vue +0 -152
- package/ui-library/components/Tabs.vue +0 -71
- package/ui-library/components/Textarea.vue +0 -233
- package/ui-library/components/Toggle.vue +0 -319
- package/ui-library/components/Tooltip.vue +0 -200
- package/ui-library/components/plugin.ts +0 -8
- package/ui-library/components/uiProps.ts +0 -11
- package/ui-library/components/useUiClasses.ts +0 -159
- package/ui-library/module.ts +0 -20
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, ref } from 'vue'
|
|
3
|
-
|
|
4
|
-
defineOptions({
|
|
5
|
-
name: 'UIFileUploader',
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
interface UploadedFile {
|
|
9
|
-
file: File
|
|
10
|
-
name: string
|
|
11
|
-
size: number
|
|
12
|
-
type: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
type Separator =
|
|
16
|
-
| 'solid'
|
|
17
|
-
| 'dashed'
|
|
18
|
-
| 'dotted'
|
|
19
|
-
|
|
20
|
-
type Rounded =
|
|
21
|
-
| 'none'
|
|
22
|
-
| 'sm'
|
|
23
|
-
| 'md'
|
|
24
|
-
| 'lg'
|
|
25
|
-
| 'xl'
|
|
26
|
-
|
|
27
|
-
const props = withDefaults(
|
|
28
|
-
defineProps<{
|
|
29
|
-
modelValue?: UploadedFile[]
|
|
30
|
-
header?: string
|
|
31
|
-
subHeader?: string
|
|
32
|
-
fileType?: string
|
|
33
|
-
multiple?: boolean
|
|
34
|
-
disabled?: boolean
|
|
35
|
-
bordered?: boolean
|
|
36
|
-
flat?: boolean
|
|
37
|
-
rounded?: Rounded
|
|
38
|
-
borderColor?: string
|
|
39
|
-
bgColor?: string
|
|
40
|
-
separator?: Separator
|
|
41
|
-
dense?: boolean
|
|
42
|
-
fileRemoverIcon?: string
|
|
43
|
-
fileSizeLimitKb?: number
|
|
44
|
-
}>(),
|
|
45
|
-
{
|
|
46
|
-
modelValue: () => [],
|
|
47
|
-
header: 'Upload Files',
|
|
48
|
-
subHeader:
|
|
49
|
-
'Supports document uploads',
|
|
50
|
-
fileType: '*',
|
|
51
|
-
multiple: true,
|
|
52
|
-
disabled: false,
|
|
53
|
-
bordered: true,
|
|
54
|
-
flat: false,
|
|
55
|
-
rounded: 'xl',
|
|
56
|
-
borderColor: 'gray-300',
|
|
57
|
-
bgColor: 'white',
|
|
58
|
-
separator: 'dashed',
|
|
59
|
-
dense: false,
|
|
60
|
-
fileRemoverIcon: 'delete',
|
|
61
|
-
fileSizeLimitKb: 2048,
|
|
62
|
-
},
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
const emit = defineEmits<{
|
|
66
|
-
(
|
|
67
|
-
e: 'update:modelValue',
|
|
68
|
-
value: UploadedFile[],
|
|
69
|
-
): void
|
|
70
|
-
}>()
|
|
71
|
-
|
|
72
|
-
const fileInput =
|
|
73
|
-
ref<HTMLInputElement | null>(
|
|
74
|
-
null,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
const errorMessage = ref('')
|
|
78
|
-
|
|
79
|
-
const files = computed({
|
|
80
|
-
get: () => props.modelValue,
|
|
81
|
-
|
|
82
|
-
set: (
|
|
83
|
-
value: UploadedFile[],
|
|
84
|
-
) =>
|
|
85
|
-
emit(
|
|
86
|
-
'update:modelValue',
|
|
87
|
-
value,
|
|
88
|
-
),
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const isHex = (val: string) =>
|
|
92
|
-
val.startsWith('#')
|
|
93
|
-
|
|
94
|
-
const roundedClasses = computed(
|
|
95
|
-
() => {
|
|
96
|
-
const map = {
|
|
97
|
-
none: '',
|
|
98
|
-
sm: 'rounded-sm',
|
|
99
|
-
md: 'rounded-md',
|
|
100
|
-
lg: 'rounded-lg',
|
|
101
|
-
xl: 'rounded-xl',
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return map[props.rounded]
|
|
105
|
-
},
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
const separatorClasses = computed(
|
|
109
|
-
() => {
|
|
110
|
-
switch (props.separator) {
|
|
111
|
-
case 'solid':
|
|
112
|
-
return 'border-solid'
|
|
113
|
-
|
|
114
|
-
case 'dotted':
|
|
115
|
-
return 'border-dotted'
|
|
116
|
-
|
|
117
|
-
case 'dashed':
|
|
118
|
-
default:
|
|
119
|
-
return 'border-dashed'
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
const uploaderClasses = computed(
|
|
125
|
-
() => [
|
|
126
|
-
'border-2 transition-all duration-300 flex flex-col items-center justify-center cursor-pointer',
|
|
127
|
-
|
|
128
|
-
roundedClasses.value,
|
|
129
|
-
|
|
130
|
-
separatorClasses.value,
|
|
131
|
-
|
|
132
|
-
props.bordered
|
|
133
|
-
? !isHex(props.borderColor)
|
|
134
|
-
? `border-${props.borderColor}`
|
|
135
|
-
: ''
|
|
136
|
-
: 'border-transparent',
|
|
137
|
-
|
|
138
|
-
props.flat
|
|
139
|
-
? 'shadow-none'
|
|
140
|
-
: 'shadow-md',
|
|
141
|
-
|
|
142
|
-
!isHex(props.bgColor)
|
|
143
|
-
? `bg-${props.bgColor}`
|
|
144
|
-
: '',
|
|
145
|
-
|
|
146
|
-
props.dense
|
|
147
|
-
? 'p-4'
|
|
148
|
-
: 'p-8',
|
|
149
|
-
|
|
150
|
-
props.disabled
|
|
151
|
-
? 'opacity-50 pointer-events-none'
|
|
152
|
-
: 'hover:shadow-lg',
|
|
153
|
-
])
|
|
154
|
-
|
|
155
|
-
const uploaderStyle = computed(
|
|
156
|
-
() => {
|
|
157
|
-
const style: Record<
|
|
158
|
-
string,
|
|
159
|
-
string
|
|
160
|
-
> = {}
|
|
161
|
-
|
|
162
|
-
if (isHex(props.borderColor)) {
|
|
163
|
-
style.borderColor =
|
|
164
|
-
props.borderColor
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (isHex(props.bgColor)) {
|
|
168
|
-
style.backgroundColor =
|
|
169
|
-
props.bgColor
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return style
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
const triggerFileInput =
|
|
177
|
-
(): void => {
|
|
178
|
-
fileInput.value?.click()
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const createFileObject = (
|
|
182
|
-
file: File,
|
|
183
|
-
): UploadedFile => ({
|
|
184
|
-
file,
|
|
185
|
-
name: file.name,
|
|
186
|
-
size: file.size,
|
|
187
|
-
type: file.type,
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
const handleFiles = (
|
|
191
|
-
event: Event | DragEvent,
|
|
192
|
-
): void => {
|
|
193
|
-
const selectedFiles =
|
|
194
|
-
'dataTransfer' in event &&
|
|
195
|
-
event.dataTransfer
|
|
196
|
-
? Array.from(
|
|
197
|
-
event.dataTransfer.files,
|
|
198
|
-
)
|
|
199
|
-
: Array.from(
|
|
200
|
-
(
|
|
201
|
-
event.target as HTMLInputElement
|
|
202
|
-
).files || [],
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
const validFiles =
|
|
206
|
-
selectedFiles.filter((file) => {
|
|
207
|
-
const fileSizeInKb =
|
|
208
|
-
file.size / 1024
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
fileSizeInKb >
|
|
212
|
-
props.fileSizeLimitKb
|
|
213
|
-
) {
|
|
214
|
-
errorMessage.value = `File "${file.name}" exceeds ${props.fileSizeLimitKb} KB`
|
|
215
|
-
|
|
216
|
-
return false
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return true
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
const mapped =
|
|
223
|
-
validFiles.map(
|
|
224
|
-
createFileObject,
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
files.value = props.multiple
|
|
228
|
-
? [
|
|
229
|
-
...files.value,
|
|
230
|
-
...mapped,
|
|
231
|
-
]
|
|
232
|
-
: mapped
|
|
233
|
-
|
|
234
|
-
if (mapped.length > 0) {
|
|
235
|
-
errorMessage.value = ''
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const removeFile = (
|
|
240
|
-
index: number,
|
|
241
|
-
): void => {
|
|
242
|
-
files.value = files.value.filter(
|
|
243
|
-
(_, i) => i !== index,
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
</script>
|
|
247
|
-
|
|
248
|
-
<template>
|
|
249
|
-
<div class="max-w-lg mx-auto">
|
|
250
|
-
<!-- Header -->
|
|
251
|
-
<div class="mb-6 text-center">
|
|
252
|
-
<h2 class="text-2xl font-bold text-gray-800 dark:text-white">
|
|
253
|
-
{{ header }}
|
|
254
|
-
</h2>
|
|
255
|
-
|
|
256
|
-
<p class="text-sm text-gray-500">
|
|
257
|
-
{{ subHeader }}
|
|
258
|
-
</p>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
<!-- Hidden Input -->
|
|
262
|
-
<input ref="fileInput" type="file" class="hidden" :accept="fileType" :multiple="multiple" @change="handleFiles">
|
|
263
|
-
|
|
264
|
-
<!-- Dropzone -->
|
|
265
|
-
<div :class="uploaderClasses" :style="uploaderStyle" @click="triggerFileInput" @dragover.prevent @drop.prevent="
|
|
266
|
-
handleFiles($event)
|
|
267
|
-
">
|
|
268
|
-
<slot name="content">
|
|
269
|
-
<p class="text-gray-600 mb-4">
|
|
270
|
-
{{
|
|
271
|
-
multiple
|
|
272
|
-
? 'Drag & drop files here'
|
|
273
|
-
: 'Drag & drop file here'
|
|
274
|
-
}}
|
|
275
|
-
</p>
|
|
276
|
-
|
|
277
|
-
<button type="button" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
|
278
|
-
{{
|
|
279
|
-
multiple
|
|
280
|
-
? 'Browse Files'
|
|
281
|
-
: 'Browse File'
|
|
282
|
-
}}
|
|
283
|
-
</button>
|
|
284
|
-
</slot>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
<!-- Uploaded Files -->
|
|
288
|
-
<div v-if="files.length" class="mt-6">
|
|
289
|
-
<h3 class="text-lg font-semibold mb-4">
|
|
290
|
-
Uploaded Files
|
|
291
|
-
</h3>
|
|
292
|
-
|
|
293
|
-
<ul class="space-y-2">
|
|
294
|
-
<li v-for="(file, index) in files" :key="index"
|
|
295
|
-
class="flex items-center justify-between p-4 rounded-lg border border-gray-200 bg-gray-50">
|
|
296
|
-
<div class="flex items-center gap-3">
|
|
297
|
-
<span class="material-icons text-blue-500">
|
|
298
|
-
description
|
|
299
|
-
</span>
|
|
300
|
-
|
|
301
|
-
<span>
|
|
302
|
-
{{ file.name }}
|
|
303
|
-
</span>
|
|
304
|
-
</div>
|
|
305
|
-
|
|
306
|
-
<button type="button" class="text-red-500 hover:text-red-700" @click.stop="
|
|
307
|
-
removeFile(index)
|
|
308
|
-
">
|
|
309
|
-
<span class="material-icons">
|
|
310
|
-
{{
|
|
311
|
-
fileRemoverIcon
|
|
312
|
-
}}
|
|
313
|
-
</span>
|
|
314
|
-
</button>
|
|
315
|
-
</li>
|
|
316
|
-
</ul>
|
|
317
|
-
</div>
|
|
318
|
-
|
|
319
|
-
<!-- Error -->
|
|
320
|
-
<div v-if="errorMessage" class="mt-4 text-sm text-red-500">
|
|
321
|
-
{{ errorMessage }}
|
|
322
|
-
</div>
|
|
323
|
-
</div>
|
|
324
|
-
</template>
|
|
325
|
-
|
|
326
|
-
<style scoped>
|
|
327
|
-
.material-icons {
|
|
328
|
-
font-size: 20px;
|
|
329
|
-
}
|
|
330
|
-
</style>
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
defineOptions({
|
|
3
|
-
inheritAttrs: false,
|
|
4
|
-
})
|
|
5
|
-
|
|
6
|
-
import { computed } from 'vue'
|
|
7
|
-
|
|
8
|
-
const props = defineProps({
|
|
9
|
-
name: { type: String, required: true },
|
|
10
|
-
|
|
11
|
-
library: {
|
|
12
|
-
type: String,
|
|
13
|
-
default: 'material',
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
color: {
|
|
17
|
-
type: String,
|
|
18
|
-
default: '',
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
size: {
|
|
22
|
-
type: String,
|
|
23
|
-
default: 'md',
|
|
24
|
-
},
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
const isHex = (val: string) => val.startsWith('#')
|
|
28
|
-
|
|
29
|
-
const sizeMap = {
|
|
30
|
-
xs: 'text-xs',
|
|
31
|
-
sm: 'text-sm',
|
|
32
|
-
md: 'text-base',
|
|
33
|
-
lg: 'text-xl',
|
|
34
|
-
xl: 'text-3xl',
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const iconStyle = computed(() => {
|
|
38
|
-
const style: Record<string, string> = {}
|
|
39
|
-
|
|
40
|
-
if (isHex(props.color)) {
|
|
41
|
-
style.color = props.color
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return style
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const iconClass = computed(() => {
|
|
48
|
-
const classes = [
|
|
49
|
-
sizeMap[props.size as keyof typeof sizeMap],
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
if (!isHex(props.color) && props.color) {
|
|
53
|
-
classes.push(`text-${props.color}`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
switch (props.library) {
|
|
57
|
-
case 'material':
|
|
58
|
-
classes.push('material-icons')
|
|
59
|
-
break
|
|
60
|
-
|
|
61
|
-
case 'material-symbols':
|
|
62
|
-
classes.push('material-symbols-outlined')
|
|
63
|
-
break
|
|
64
|
-
|
|
65
|
-
case 'fontawesome':
|
|
66
|
-
classes.push(props.name)
|
|
67
|
-
break
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return classes
|
|
71
|
-
})
|
|
72
|
-
</script>
|
|
73
|
-
|
|
74
|
-
<template>
|
|
75
|
-
<i
|
|
76
|
-
:class="iconClass"
|
|
77
|
-
:style="iconStyle"
|
|
78
|
-
v-bind="$attrs"
|
|
79
|
-
>
|
|
80
|
-
{{ library.includes('material') ? name : '' }}
|
|
81
|
-
</i>
|
|
82
|
-
</template>
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
defineOptions({
|
|
3
|
-
inheritAttrs: false,
|
|
4
|
-
})
|
|
5
|
-
|
|
6
|
-
import { computed, ref } from 'vue'
|
|
7
|
-
|
|
8
|
-
const props = defineProps({
|
|
9
|
-
src: { type: String, required: true },
|
|
10
|
-
|
|
11
|
-
alt: { type: String, default: '' },
|
|
12
|
-
|
|
13
|
-
placeholder: {
|
|
14
|
-
type: String,
|
|
15
|
-
default: '/images/placeholder.png'
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
width: { type: String, default: '100%' },
|
|
19
|
-
height: { type: String, default: 'auto' },
|
|
20
|
-
|
|
21
|
-
fit: {
|
|
22
|
-
type: String,
|
|
23
|
-
default: 'cover',
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
borderColor: {
|
|
27
|
-
type: String,
|
|
28
|
-
default: '',
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
borderType: {
|
|
32
|
-
type: String,
|
|
33
|
-
default: 'none',
|
|
34
|
-
},
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
const imageSrc = ref(props.src)
|
|
38
|
-
|
|
39
|
-
const isHex = (val: string) => val.startsWith('#')
|
|
40
|
-
|
|
41
|
-
const imageStyle = computed(() => {
|
|
42
|
-
const style: Record<string, string> = {
|
|
43
|
-
width: props.width,
|
|
44
|
-
height: props.height,
|
|
45
|
-
objectFit: props.fit,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (isHex(props.borderColor)) {
|
|
49
|
-
style.borderColor = props.borderColor
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return style
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
const imageClass = computed(() => [
|
|
56
|
-
props.borderType !== 'none' ? 'border' : '',
|
|
57
|
-
props.borderType === 'dashed' ? 'border-dashed' : '',
|
|
58
|
-
props.borderType === 'dotted' ? 'border-dotted' : '',
|
|
59
|
-
!isHex(props.borderColor)
|
|
60
|
-
? `border-${props.borderColor}`
|
|
61
|
-
: '',
|
|
62
|
-
])
|
|
63
|
-
|
|
64
|
-
function onError() {
|
|
65
|
-
imageSrc.value = props.placeholder
|
|
66
|
-
}
|
|
67
|
-
</script>
|
|
68
|
-
|
|
69
|
-
<template>
|
|
70
|
-
<img
|
|
71
|
-
:src="imageSrc"
|
|
72
|
-
:alt="alt"
|
|
73
|
-
:class="imageClass"
|
|
74
|
-
:style="imageStyle"
|
|
75
|
-
@error="onError"
|
|
76
|
-
v-bind="$attrs"
|
|
77
|
-
>
|
|
78
|
-
</template>
|