@dnax/ui 0.0.1

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.
Files changed (150) hide show
  1. package/README.md +15 -0
  2. package/components/QAccordion.vue +68 -0
  3. package/components/QAccordionContent.vue +62 -0
  4. package/components/QAccordionItem.vue +24 -0
  5. package/components/QAccordionTrigger.vue +51 -0
  6. package/components/QActionSheet.vue +159 -0
  7. package/components/QApp.vue +8 -0
  8. package/components/QAutocomplete.vue +316 -0
  9. package/components/QAvatar.vue +92 -0
  10. package/components/QBackHeader.vue +69 -0
  11. package/components/QBackTop.vue +125 -0
  12. package/components/QBadge.vue +55 -0
  13. package/components/QBottomSheet.vue +177 -0
  14. package/components/QBottomSheetFooter.vue +9 -0
  15. package/components/QBottomSheetHeader.vue +35 -0
  16. package/components/QBottomSheetProvider.vue +62 -0
  17. package/components/QBottomSheetTrigger.vue +18 -0
  18. package/components/QBtn.vue +144 -0
  19. package/components/QBtnGroup.vue +31 -0
  20. package/components/QBubble.vue +36 -0
  21. package/components/QBubbleContent.vue +28 -0
  22. package/components/QBubbleGroup.vue +25 -0
  23. package/components/QBubbleReactions.vue +40 -0
  24. package/components/QCard.vue +63 -0
  25. package/components/QCardActions.vue +27 -0
  26. package/components/QCardContent.vue +29 -0
  27. package/components/QCarousel.vue +115 -0
  28. package/components/QCarouselContent.vue +23 -0
  29. package/components/QCarouselItem.vue +20 -0
  30. package/components/QCarouselNav.vue +51 -0
  31. package/components/QCheckbox.vue +146 -0
  32. package/components/QChip.vue +91 -0
  33. package/components/QCircularProgress.vue +122 -0
  34. package/components/QCol.vue +52 -0
  35. package/components/QCollapse.vue +141 -0
  36. package/components/QConfigProvider.vue +164 -0
  37. package/components/QContainer.vue +201 -0
  38. package/components/QCountDown.vue +164 -0
  39. package/components/QCountryPicker.vue +593 -0
  40. package/components/QDataGrid.vue +491 -0
  41. package/components/QDatePicker.vue +250 -0
  42. package/components/QDialog.vue +163 -0
  43. package/components/QDialogFooter.vue +9 -0
  44. package/components/QDialogHeader.vue +41 -0
  45. package/components/QDialogProvider.vue +65 -0
  46. package/components/QDialogTrigger.vue +18 -0
  47. package/components/QFab.vue +80 -0
  48. package/components/QFabAction.vue +48 -0
  49. package/components/QFilePicker.vue +294 -0
  50. package/components/QFooter.vue +72 -0
  51. package/components/QGallery.vue +280 -0
  52. package/components/QGrid.vue +54 -0
  53. package/components/QHeader.vue +38 -0
  54. package/components/QIcon.vue +53 -0
  55. package/components/QImagePicker.vue +248 -0
  56. package/components/QImagePreview.vue +225 -0
  57. package/components/QImagePreviewProvider.vue +35 -0
  58. package/components/QImg.vue +142 -0
  59. package/components/QInfiniteScroll.vue +109 -0
  60. package/components/QInnerLoading.vue +67 -0
  61. package/components/QInput.vue +289 -0
  62. package/components/QInputOtp.vue +238 -0
  63. package/components/QInputPassword.vue +67 -0
  64. package/components/QInputTag.vue +281 -0
  65. package/components/QIntersection.vue +119 -0
  66. package/components/QItem.vue +60 -0
  67. package/components/QItemSection.vue +47 -0
  68. package/components/QLinearProgress.vue +94 -0
  69. package/components/QList.vue +39 -0
  70. package/components/QLoading.vue +77 -0
  71. package/components/QLoadingProvider.vue +41 -0
  72. package/components/QMarquee.vue +56 -0
  73. package/components/QMessageScroller.vue +10 -0
  74. package/components/QMessageScrollerButton.vue +48 -0
  75. package/components/QMessageScrollerContent.vue +31 -0
  76. package/components/QMessageScrollerItem.vue +49 -0
  77. package/components/QMessageScrollerProvider.vue +198 -0
  78. package/components/QMessageScrollerViewport.vue +109 -0
  79. package/components/QNavMenu.vue +75 -0
  80. package/components/QNavMenuContent.vue +10 -0
  81. package/components/QNavMenuItem.vue +60 -0
  82. package/components/QNavMenuTrigger.vue +96 -0
  83. package/components/QNotifyProvider.vue +33 -0
  84. package/components/QNotifyToast.vue +60 -0
  85. package/components/QPage.vue +56 -0
  86. package/components/QPagination.vue +195 -0
  87. package/components/QParallax.vue +124 -0
  88. package/components/QPullToRefresh.vue +138 -0
  89. package/components/QRadio.vue +99 -0
  90. package/components/QRating.vue +105 -0
  91. package/components/QReorder.vue +191 -0
  92. package/components/QRollingText.vue +225 -0
  93. package/components/QRow.vue +23 -0
  94. package/components/QSafeArea.vue +39 -0
  95. package/components/QScrollArea.vue +118 -0
  96. package/components/QSelect.vue +698 -0
  97. package/components/QSeparator.vue +39 -0
  98. package/components/QSidebar.vue +153 -0
  99. package/components/QSidebarContent.vue +14 -0
  100. package/components/QSidebarFooter.vue +14 -0
  101. package/components/QSidebarHeader.vue +14 -0
  102. package/components/QSidebarMenu.vue +9 -0
  103. package/components/QSidebarMenuButton.vue +59 -0
  104. package/components/QSidebarMenuItem.vue +9 -0
  105. package/components/QSidebarTrigger.vue +32 -0
  106. package/components/QSkeleton.vue +95 -0
  107. package/components/QSlider.vue +205 -0
  108. package/components/QSpace.vue +10 -0
  109. package/components/QSpinner.vue +172 -0
  110. package/components/QSplitter.vue +145 -0
  111. package/components/QSticky.vue +181 -0
  112. package/components/QSwipeCell.vue +217 -0
  113. package/components/QSwiper.vue +102 -0
  114. package/components/QSwiperSlide.vue +19 -0
  115. package/components/QSyntax.vue +171 -0
  116. package/components/QTab.vue +175 -0
  117. package/components/QTabPanel.vue +50 -0
  118. package/components/QTabPanels.vue +106 -0
  119. package/components/QTable.vue +575 -0
  120. package/components/QTabs.vue +188 -0
  121. package/components/QText.vue +316 -0
  122. package/components/QTextCaption.vue +122 -0
  123. package/components/QToolbar.vue +28 -0
  124. package/components/QTooltip.vue +313 -0
  125. package/components/QUploader.vue +473 -0
  126. package/components/QVideo.vue +289 -0
  127. package/components/QVirtualScroll.vue +190 -0
  128. package/components/internal/QDateCalendar.vue +153 -0
  129. package/index.ts +151 -0
  130. package/lib/breakpoints.ts +44 -0
  131. package/lib/colors.ts +24 -0
  132. package/lib/config.ts +34 -0
  133. package/lib/countries.ts +261 -0
  134. package/lib/formula.ts +630 -0
  135. package/lib/icons.ts +38 -0
  136. package/lib/loading.ts +75 -0
  137. package/lib/overlayBack.ts +124 -0
  138. package/lib/platform.ts +146 -0
  139. package/lib/primitive.ts +25 -0
  140. package/lib/q.ts +287 -0
  141. package/lib/screen.ts +105 -0
  142. package/lib/search.ts +35 -0
  143. package/lib/themeVars.ts +22 -0
  144. package/lib/useComponentProps.ts +54 -0
  145. package/lib/utils.ts +7 -0
  146. package/module.ts +89 -0
  147. package/package.json +41 -0
  148. package/scripts/generate-exports.ts +56 -0
  149. package/styles/main.css +7627 -0
  150. package/tsconfig.json +33 -0
@@ -0,0 +1,473 @@
1
+ <script setup lang="ts">
2
+ // QUploader — zone d'upload de fichiers façon Vant Uploader, design dnax :
3
+ // <q-uploader v-model="files" max-count="5" max-size="2097152" accept="image/*" />
4
+ // v-model = tableau d'UploaderFile ({ file, url, name, size, status, message }).
5
+ // Grille de tuiles d'aperçu (images via object URL, autres via icône fichier),
6
+ // clic sur une image → preview plein écran (QImagePreview), bouton × pour supprimer.
7
+ // Props : accept, multiple, max-count, max-size, deletable, preview-full-image,
8
+ // disabled, readonly, label, before-read. Events : oversize, delete, click-preview.
9
+ import { computed, onBeforeUnmount, ref } from "vue"
10
+ import { Icon } from "@iconify/vue"
11
+ import { icons } from "../lib/icons"
12
+ import { cn } from "../lib/utils"
13
+ import QImagePreview from "./QImagePreview.vue"
14
+
15
+ /** Un fichier de la liste : fichier réel + métadonnées d'affichage */
16
+ export interface UploaderFile {
17
+ /** Fichier réel (absent pour les items pré-remplis par url) */
18
+ file?: File
19
+ /** URL d'aperçu (créée pour les images, ou fournie pour les items pré-remplis) */
20
+ url?: string
21
+ name?: string
22
+ size?: number
23
+ /** Statut libre (ex. "uploading" | "done" | "failed") */
24
+ status?: string
25
+ /** Message affiché sous la tuile (ex. erreur) */
26
+ message?: string
27
+ }
28
+
29
+ interface Props {
30
+ /** Liste des fichiers (v-model) */
31
+ modelValue?: UploaderFile[]
32
+ /** Types acceptés par le sélecteur (défaut image/*) */
33
+ accept?: string
34
+ /** Sélection multiple */
35
+ multiple?: boolean
36
+ /** Nombre max de fichiers (0 = illimité) */
37
+ maxCount?: number
38
+ /** Taille max en octets (0 = illimité) — fichiers plus gros → event oversize */
39
+ maxSize?: number
40
+ /** Affiche le bouton supprimer sur les tuiles (défaut true) */
41
+ deletable?: boolean
42
+ /** Clic sur une image → preview plein écran (défaut true) */
43
+ previewFullImage?: boolean
44
+ /** Champ désactivé */
45
+ disabled?: boolean
46
+ /** Lecture seule (pas d'ajout ni de suppression) */
47
+ readonly?: boolean
48
+ /** Libellé sous l'icône du bouton d'ajout (défaut "Upload") — remplacé par placeholder */
49
+ label?: string
50
+ /** Texte du bouton d'ajout (prioritaire sur label) */
51
+ placeholder?: string
52
+ /** Icône du bouton d'ajout (défaut lucide:image-plus) */
53
+ uploadIcon?: string
54
+ /** Icône et texte du bouton d'ajout sur la même ligne */
55
+ inlinePlaceholder?: boolean
56
+ /** Affiche le nom et la taille du fichier sous chaque tuile (défaut false) */
57
+ details?: boolean
58
+ /** Hook de validation avant ajout : retourner false (ou Promise false) rejette le fichier */
59
+ beforeRead?: (file: File) => boolean | Promise<boolean>
60
+ class?: string
61
+ }
62
+
63
+ const props = withDefaults(defineProps<Props>(), {
64
+ modelValue: () => [],
65
+ accept: "image/*",
66
+ multiple: false,
67
+ maxCount: 0,
68
+ maxSize: 0,
69
+ deletable: true,
70
+ previewFullImage: true,
71
+ disabled: false,
72
+ readonly: false,
73
+ label: "Upload",
74
+ placeholder: "",
75
+ uploadIcon: icons.imagePlus,
76
+ inlinePlaceholder: false,
77
+ details: false,
78
+ beforeRead: undefined,
79
+ class: "",
80
+ })
81
+
82
+ const emit = defineEmits<{
83
+ "update:modelValue": [value: UploaderFile[]]
84
+ oversize: [file: File]
85
+ delete: [file: UploaderFile]
86
+ "click-preview": [file: UploaderFile]
87
+ }>()
88
+
89
+ const inputRef = ref<HTMLInputElement | null>(null)
90
+
91
+ const items = computed(() => props.modelValue)
92
+
93
+ const canAdd = computed(
94
+ () =>
95
+ !props.disabled &&
96
+ !props.readonly &&
97
+ (!props.maxCount || items.value.length < props.maxCount),
98
+ )
99
+
100
+ /** Crée un UploaderFile : URL d'aperçu pour les images, icône pour les autres */
101
+ const createItem = (file: File): UploaderFile => {
102
+ const item: UploaderFile = { file, name: file.name, size: file.size }
103
+ if (file.type.startsWith("image/")) item.url = URL.createObjectURL(file)
104
+ return item
105
+ }
106
+
107
+ const pickFiles = () => {
108
+ if (!canAdd.value) return
109
+ inputRef.value?.click()
110
+ }
111
+
112
+ const handleChange = async (e: Event) => {
113
+ const input = e.target as HTMLInputElement
114
+ const files = Array.from(input.files ?? [])
115
+ const added: UploaderFile[] = []
116
+
117
+ for (const file of files) {
118
+ if (!canAdd.value && !added.length) break
119
+ if (props.maxSize && file.size > props.maxSize) {
120
+ emit("oversize", file)
121
+ continue
122
+ }
123
+ if (props.beforeRead) {
124
+ const ok = await props.beforeRead(file)
125
+ if (ok === false) continue
126
+ }
127
+ added.push(createItem(file))
128
+ }
129
+
130
+ if (added.length) {
131
+ const next = [...items.value, ...added]
132
+ emit(
133
+ "update:modelValue",
134
+ props.maxCount ? next.slice(0, props.maxCount) : next,
135
+ )
136
+ }
137
+ input.value = ""
138
+ }
139
+
140
+ const removeItem = (index: number) => {
141
+ if (props.disabled || props.readonly) return
142
+ const item = items.value[index]
143
+ if (!item) return
144
+ if (item.url?.startsWith("blob:")) URL.revokeObjectURL(item.url)
145
+ const next = [...items.value]
146
+ next.splice(index, 1)
147
+ emit("update:modelValue", next)
148
+ emit("delete", item)
149
+ }
150
+
151
+ // — Preview plein écran (QImagePreview) —
152
+ const previewOpen = ref(false)
153
+ const previewIndex = ref(0)
154
+ const imageItems = computed(() => items.value.filter((i) => i.url))
155
+ const previewImages = computed(() => imageItems.value.map((i) => i.url!))
156
+
157
+ const openPreview = (item: UploaderFile) => {
158
+ emit("click-preview", item)
159
+ if (!props.previewFullImage || !item.url) return
160
+ previewIndex.value = Math.max(
161
+ 0,
162
+ imageItems.value.findIndex((i) => i === item),
163
+ )
164
+ previewOpen.value = true
165
+ }
166
+
167
+ onBeforeUnmount(() => {
168
+ // Libère les object URLs créées
169
+ for (const item of items.value) {
170
+ if (item.url?.startsWith("blob:")) URL.revokeObjectURL(item.url)
171
+ }
172
+ })
173
+
174
+ /** Taille lisible (octets → "1.4 MB") */
175
+ const formatSize = (bytes?: number): string => {
176
+ if (bytes === undefined || bytes === null || Number.isNaN(bytes)) return ""
177
+ if (bytes < 1024) return `${bytes} B`
178
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
179
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
180
+ }
181
+ </script>
182
+
183
+ <template>
184
+ <div class="q-uploader" :class="cn(disabled && 'q-uploader--disabled', props.class)">
185
+ <div class="q-uploader__grid">
186
+ <!-- Tuiles des fichiers -->
187
+ <div
188
+ v-for="(item, i) in items"
189
+ :key="item.url ?? item.file?.name ?? i"
190
+ class="q-uploader__item"
191
+ :class="{
192
+ 'q-uploader__item--failed': item.status === 'failed',
193
+ 'q-uploader__item--details': details,
194
+ }"
195
+ >
196
+ <slot v-if="$slots.file" name="file" :item="item" :index="i" />
197
+ <template v-else>
198
+ <button
199
+ v-if="item.url"
200
+ type="button"
201
+ class="q-uploader__preview"
202
+ :aria-label="`Preview ${item.name ?? i + 1}`"
203
+ @click="openPreview(item)"
204
+ >
205
+ <img class="q-uploader__img" :src="item.url" :alt="item.name ?? `File ${i + 1}`" />
206
+ </button>
207
+ <div v-else class="q-uploader__file">
208
+ <Icon :icon="icons.file" class="q-uploader__file-icon" aria-hidden="true" />
209
+ <span class="q-uploader__file-name">{{ item.name ?? `File ${i + 1}` }}</span>
210
+ </div>
211
+ </template>
212
+
213
+ <span v-if="item.message" class="q-uploader__message">{{ item.message }}</span>
214
+
215
+ <button
216
+ v-if="deletable && !disabled && !readonly"
217
+ type="button"
218
+ class="q-uploader__remove"
219
+ :aria-label="`Remove ${item.name ?? i + 1}`"
220
+ @click.stop="removeItem(i)"
221
+ >
222
+ <Icon :icon="icons.x" aria-hidden="true" />
223
+ </button>
224
+
225
+ <!-- Nom + taille sous la tuile (prop details) -->
226
+ <div v-if="details" class="q-uploader__details">
227
+ <span class="q-uploader__details-name">{{ item.name ?? `File ${i + 1}` }}</span>
228
+ <span v-if="item.size != null" class="q-uploader__details-size">
229
+ {{ formatSize(item.size) }}
230
+ </span>
231
+ </div>
232
+ </div>
233
+
234
+ <!-- Bouton d'ajout -->
235
+ <div
236
+ v-if="canAdd"
237
+ class="q-uploader__add"
238
+ :class="{ 'q-uploader__add--inline': inlinePlaceholder }"
239
+ role="button"
240
+ tabindex="0"
241
+ @click="pickFiles"
242
+ @keydown.enter.prevent="pickFiles"
243
+ >
244
+ <slot v-if="$slots.default" />
245
+ <template v-else>
246
+ <Icon :icon="uploadIcon" class="q-uploader__add-icon" aria-hidden="true" />
247
+ <span class="q-uploader__add-label">{{ placeholder || label }}</span>
248
+ </template>
249
+ </div>
250
+ </div>
251
+
252
+ <input
253
+ ref="inputRef"
254
+ class="q-uploader__input"
255
+ type="file"
256
+ :accept="accept"
257
+ :multiple="multiple"
258
+ @change="handleChange"
259
+ />
260
+ <q-image-preview
261
+ v-model="previewOpen"
262
+ :images="previewImages"
263
+ v-model:index="previewIndex"
264
+ transition="up"
265
+ close-btn
266
+ :counter="true"
267
+ />
268
+ </div>
269
+ </template>
270
+
271
+ <style scoped>
272
+ .q-uploader {
273
+ display: inline-block;
274
+ max-width: 100%;
275
+ }
276
+ .q-uploader--disabled {
277
+ opacity: 0.55;
278
+ }
279
+
280
+ .q-uploader__grid {
281
+ display: flex;
282
+ flex-wrap: wrap;
283
+ gap: 10px;
284
+ }
285
+
286
+ .q-uploader__item {
287
+ position: relative;
288
+ width: 88px;
289
+ height: 88px;
290
+ border-radius: 10px;
291
+ overflow: hidden;
292
+ border: 1px solid rgb(0 0 0 / 0.1);
293
+ background: var(--muted);
294
+ }
295
+ .dark .q-uploader__item {
296
+ border-color: var(--border);
297
+ }
298
+
299
+ .q-uploader__preview {
300
+ display: block;
301
+ width: 100%;
302
+ height: 100%;
303
+ padding: 0;
304
+ border: none;
305
+ cursor: pointer;
306
+ background: transparent;
307
+ }
308
+ .q-uploader__img {
309
+ display: block;
310
+ width: 100%;
311
+ height: 100%;
312
+ object-fit: cover;
313
+ }
314
+
315
+ /* mode details : la tuile devient une colonne (aperçu + nom/taille) */
316
+ .q-uploader__item--details {
317
+ display: flex;
318
+ flex-direction: column;
319
+ height: auto;
320
+ border: none;
321
+ background: transparent;
322
+ overflow: visible;
323
+ }
324
+ .q-uploader__item--details .q-uploader__preview,
325
+ .q-uploader__item--details .q-uploader__file {
326
+ height: 88px;
327
+ border-radius: 10px;
328
+ overflow: hidden;
329
+ }
330
+ .q-uploader__item--details .q-uploader__file {
331
+ background: var(--muted);
332
+ border: 1px solid rgb(0 0 0 / 0.1);
333
+ }
334
+ .q-uploader__details {
335
+ width: 100%;
336
+ margin-top: 6px;
337
+ display: flex;
338
+ flex-direction: column;
339
+ gap: 1px;
340
+ }
341
+ .q-uploader__details-name {
342
+ font-size: 11px;
343
+ font-weight: 600;
344
+ color: var(--foreground);
345
+ overflow: hidden;
346
+ text-overflow: ellipsis;
347
+ white-space: nowrap;
348
+ }
349
+ .q-uploader__details-size {
350
+ font-size: 10.5px;
351
+ color: #8b93a1;
352
+ }
353
+
354
+ /* fichier non-image : icône + nom */
355
+ .q-uploader__file {
356
+ display: flex;
357
+ flex-direction: column;
358
+ align-items: center;
359
+ justify-content: center;
360
+ gap: 6px;
361
+ height: 100%;
362
+ padding: 8px;
363
+ color: var(--foreground);
364
+ }
365
+ .q-uploader__file-icon {
366
+ font-size: 26px;
367
+ color: var(--primary);
368
+ }
369
+ .q-uploader__file-name {
370
+ max-width: 100%;
371
+ overflow: hidden;
372
+ text-overflow: ellipsis;
373
+ white-space: nowrap;
374
+ font-size: 10.5px;
375
+ color: #8b93a1;
376
+ }
377
+
378
+ /* message d'état (erreur…) */
379
+ .q-uploader__message {
380
+ position: absolute;
381
+ left: 0;
382
+ right: 0;
383
+ bottom: 0;
384
+ padding: 3px 6px;
385
+ background: rgb(198 40 40 / 0.9);
386
+ color: #fff;
387
+ font-size: 10px;
388
+ text-align: center;
389
+ overflow: hidden;
390
+ text-overflow: ellipsis;
391
+ white-space: nowrap;
392
+ }
393
+ .q-uploader__item--failed {
394
+ border-color: var(--negative);
395
+ }
396
+
397
+ /* bouton supprimer */
398
+ .q-uploader__remove {
399
+ position: absolute;
400
+ top: 5px;
401
+ right: 5px;
402
+ display: flex;
403
+ align-items: center;
404
+ justify-content: center;
405
+ width: 20px;
406
+ height: 20px;
407
+ padding: 0;
408
+ border: none;
409
+ border-radius: 50%;
410
+ background: rgb(0 0 0 / 0.55);
411
+ color: #fff;
412
+ cursor: pointer;
413
+ }
414
+ .q-uploader__remove:hover {
415
+ background: rgb(0 0 0 / 0.75);
416
+ }
417
+ .q-uploader__remove svg {
418
+ width: 11px;
419
+ height: 11px;
420
+ }
421
+
422
+ /* bouton d'ajout : tuile en pointillés */
423
+ .q-uploader__add {
424
+ display: flex;
425
+ flex-direction: column;
426
+ align-items: center;
427
+ justify-content: center;
428
+ gap: 6px;
429
+ width: 88px;
430
+ height: 88px;
431
+ border: 1.5px dashed rgb(0 0 0 / 0.25);
432
+ border-radius: 10px;
433
+ color: #8b93a1;
434
+ cursor: pointer;
435
+ transition:
436
+ border-color 0.15s ease,
437
+ color 0.15s ease,
438
+ background-color 0.15s ease;
439
+ }
440
+ .dark .q-uploader__add {
441
+ border-color: var(--border);
442
+ }
443
+ .q-uploader__add:hover {
444
+ border-color: var(--primary);
445
+ color: var(--primary);
446
+ background: rgb(25 118 210 / 0.06);
447
+ }
448
+ .q-uploader__add-icon {
449
+ font-size: 24px;
450
+ }
451
+ .q-uploader__add-label {
452
+ font-size: 11.5px;
453
+ font-weight: 600;
454
+ }
455
+
456
+ /* mode inline : icône et texte sur la même ligne */
457
+ .q-uploader__add--inline {
458
+ flex-direction: row;
459
+ gap: 8px;
460
+ width: auto;
461
+ height: 44px;
462
+ padding: 0 18px;
463
+ border-radius: 10px;
464
+ }
465
+ .q-uploader__add--inline .q-uploader__add-icon {
466
+ font-size: 18px;
467
+ }
468
+
469
+ /* input caché */
470
+ .q-uploader__input {
471
+ display: none;
472
+ }
473
+ </style>