@umbra.ui/core 0.1.18 → 0.1.19

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 (146) hide show
  1. package/dist/components/controls/Button/Button.vue +417 -0
  2. package/dist/components/controls/Button/README.md +348 -0
  3. package/dist/components/controls/Button/theme.css +200 -0
  4. package/dist/components/controls/Checkbox/Checkbox.vue +164 -0
  5. package/dist/components/controls/Checkbox/README.md +441 -0
  6. package/dist/components/controls/Checkbox/theme.css +36 -0
  7. package/dist/components/controls/Dropdown/Dropdown.vue +476 -0
  8. package/dist/components/controls/Dropdown/README.md +370 -0
  9. package/dist/components/controls/Dropdown/theme.css +50 -0
  10. package/dist/components/controls/Dropdown/types.ts +6 -0
  11. package/dist/components/controls/IconButton/IconButton.vue +267 -0
  12. package/dist/components/controls/IconButton/README.md +502 -0
  13. package/dist/components/controls/IconButton/theme.css +89 -0
  14. package/dist/components/controls/Radio/README.md +591 -0
  15. package/dist/components/controls/Radio/Radio.vue +89 -0
  16. package/dist/components/controls/Radio/theme.css +14 -0
  17. package/dist/components/controls/RangeSlider/README.md +608 -0
  18. package/dist/components/controls/RangeSlider/RangeSlider.vue +535 -0
  19. package/dist/components/controls/RangeSlider/theme.css +80 -0
  20. package/dist/components/controls/SegmentedControl/README.md +587 -0
  21. package/dist/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
  22. package/dist/components/controls/SegmentedControl/theme.css +60 -0
  23. package/dist/components/controls/SegmentedControl/types.ts +5 -0
  24. package/dist/components/controls/Slider/README.md +627 -0
  25. package/dist/components/controls/Slider/Slider.vue +260 -0
  26. package/dist/components/controls/Slider/theme.css +74 -0
  27. package/dist/components/controls/Stepper/README.md +601 -0
  28. package/dist/components/controls/Stepper/Stepper.vue +103 -0
  29. package/dist/components/controls/Stepper/theme.css +53 -0
  30. package/dist/components/controls/Switch/README.md +667 -0
  31. package/dist/components/controls/Switch/Switch.vue +127 -0
  32. package/dist/components/controls/Switch/theme.css +42 -0
  33. package/dist/components/dialogs/Alert/Alert.vue +218 -0
  34. package/dist/components/dialogs/Alert/README.md +450 -0
  35. package/dist/components/dialogs/Alert/theme.css +44 -0
  36. package/dist/components/dialogs/Alert/types.ts +11 -0
  37. package/dist/components/dialogs/Toast/README.md +522 -0
  38. package/dist/components/dialogs/Toast/Toast.vue +296 -0
  39. package/dist/components/dialogs/Toast/ToastContainer.vue +330 -0
  40. package/dist/components/dialogs/Toast/theme.css +44 -0
  41. package/dist/components/dialogs/Toast/types.ts +46 -0
  42. package/dist/components/dialogs/Toast/useToast.ts +127 -0
  43. package/dist/components/indicators/ProgressBar/ProgressBar.vue +98 -0
  44. package/dist/components/indicators/ProgressBar/README.md +744 -0
  45. package/dist/components/indicators/ProgressBar/theme.css +36 -0
  46. package/dist/components/indicators/Tooltip/README.md +723 -0
  47. package/dist/components/indicators/Tooltip/TooltipProvider.vue +142 -0
  48. package/dist/components/indicators/Tooltip/theme.css +18 -0
  49. package/dist/components/indicators/Tooltip/tooltip.ts +48 -0
  50. package/dist/components/indicators/Tooltip/types.ts +15 -0
  51. package/dist/components/indicators/Tooltip/useTooltip.ts +71 -0
  52. package/dist/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
  53. package/dist/components/inputs/AutogrowTextView/README.md +643 -0
  54. package/dist/components/inputs/AutogrowTextView/theme.css +28 -0
  55. package/dist/components/inputs/InputCard/InputCard.vue +600 -0
  56. package/dist/components/inputs/InputCard/README.md +636 -0
  57. package/dist/components/inputs/InputEmail/InputEmail.vue +698 -0
  58. package/dist/components/inputs/InputEmail/README.md +764 -0
  59. package/dist/components/inputs/InputNumber/InputNumber.vue +300 -0
  60. package/dist/components/inputs/InputNumber/README.md +749 -0
  61. package/dist/components/inputs/InputPhone/InputPhone.vue +645 -0
  62. package/dist/components/inputs/InputPhone/README.md +636 -0
  63. package/dist/components/inputs/InputSecure/InputSecure.vue +646 -0
  64. package/dist/components/inputs/InputSecure/README.md +771 -0
  65. package/dist/components/inputs/InputText/InputText.vue +225 -0
  66. package/dist/components/inputs/InputText/README.md +844 -0
  67. package/dist/components/inputs/OTP/OTP.vue +349 -0
  68. package/dist/components/inputs/OTP/README.md +736 -0
  69. package/dist/components/inputs/OTP/theme.css +50 -0
  70. package/dist/components/inputs/StringCapture/README.md +718 -0
  71. package/dist/components/inputs/StringCapture/StringCapture.vue +315 -0
  72. package/dist/components/inputs/StringCapture/theme.css +86 -0
  73. package/dist/components/inputs/Tags/README.md +897 -0
  74. package/dist/components/inputs/Tags/TagBar.vue +793 -0
  75. package/dist/components/inputs/Tags/TagCreation.vue +219 -0
  76. package/dist/components/inputs/Tags/TagPicker.vue +380 -0
  77. package/dist/components/inputs/Tags/tag-bar-styles.ts +354 -0
  78. package/dist/components/inputs/Tags/theme.css +121 -0
  79. package/dist/components/inputs/Tags/types.ts +346 -0
  80. package/dist/components/inputs/search/README.md +759 -0
  81. package/dist/components/inputs/search/SearchBar.vue +394 -0
  82. package/dist/components/inputs/search/SearchResults.vue +310 -0
  83. package/dist/components/inputs/search/theme.css +187 -0
  84. package/dist/components/inputs/search/types.ts +8 -0
  85. package/dist/components/inputs/theme.css +102 -0
  86. package/dist/components/menus/ActionMenu/ActionMenu.vue +383 -0
  87. package/dist/components/menus/ActionMenu/README.md +825 -0
  88. package/dist/components/menus/ActionMenu/theme.css +93 -0
  89. package/dist/components/models/Popover/Popover.vue +551 -0
  90. package/dist/components/models/Popover/README.md +885 -0
  91. package/dist/components/models/Popover/theme.css +52 -0
  92. package/dist/components/models/Sheet/README.md +1159 -0
  93. package/dist/components/models/Sheet/Sheet.vue +465 -0
  94. package/dist/components/models/Sheet/theme.css +72 -0
  95. package/dist/components/models/Sidebar/README.md +1228 -0
  96. package/dist/components/models/Sidebar/Sidebar.vue +480 -0
  97. package/dist/components/models/Sidebar/theme.css +90 -0
  98. package/dist/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
  99. package/dist/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
  100. package/dist/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
  101. package/dist/components/navigation/adaptive/README.md +768 -0
  102. package/dist/components/navigation/adaptive/types.ts +19 -0
  103. package/dist/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
  104. package/dist/components/navigation/adaptive/useBreakpoints.ts +41 -0
  105. package/dist/components/navigation/adaptive/useContainerMonitor.ts +214 -0
  106. package/dist/components/navigation/adaptive/useViewAnimation.ts +721 -0
  107. package/dist/components/navigation/adaptive/useViewResize.ts +211 -0
  108. package/dist/components/navigation/navstack/NavigationStack.vue +180 -0
  109. package/dist/components/navigation/navstack/README.md +994 -0
  110. package/dist/components/navigation/navstack/useNavigationStack.ts +164 -0
  111. package/dist/components/navigation/slideover/README.md +1275 -0
  112. package/dist/components/navigation/slideover/SlideoverController.vue +287 -0
  113. package/dist/components/navigation/slideover/useSlideoverController.ts +320 -0
  114. package/dist/components/navigation/splitview/README.md +1115 -0
  115. package/dist/components/navigation/splitview/SplitViewController.vue +176 -0
  116. package/dist/components/navigation/splitview/useSplitViewController.ts +388 -0
  117. package/dist/components/navigation/tabcontroller/README.md +919 -0
  118. package/dist/components/navigation/tabcontroller/TabController.vue +307 -0
  119. package/dist/components/navigation/tabcontroller/TabItem.vue +57 -0
  120. package/dist/components/navigation/tabcontroller/types.ts +24 -0
  121. package/dist/components/navigation/tabcontroller/useTabController.ts +18 -0
  122. package/dist/components/navigation/theme.css +91 -0
  123. package/dist/components/navigation/types.ts +7 -0
  124. package/dist/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
  125. package/dist/components/pickers/CollectionPicker/README.md +1115 -0
  126. package/dist/components/pickers/CollectionPicker/theme.css +14 -0
  127. package/dist/components/pickers/CollectionPicker/types.ts +11 -0
  128. package/dist/components/pickers/ColorPicker/ColorPicker.vue +376 -0
  129. package/dist/components/pickers/ColorPicker/README.md +1439 -0
  130. package/dist/components/pickers/ColorPicker/colors.ts +299 -0
  131. package/dist/components/pickers/ColorPicker/theme.css +32 -0
  132. package/dist/components/pickers/DatePicker/DatePicker.vue +660 -0
  133. package/dist/components/pickers/DatePicker/README.md +1195 -0
  134. package/dist/components/pickers/DatePicker/theme.css +22 -0
  135. package/dist/components/pickers/FilePicker/FilePicker.vue +534 -0
  136. package/dist/components/pickers/FilePicker/README.md +1542 -0
  137. package/dist/components/pickers/FilePicker/theme.css +48 -0
  138. package/dist/components/pickers/FilePicker/types.ts +10 -0
  139. package/dist/components/pickers/IconPicker/IconPicker.vue +327 -0
  140. package/dist/components/pickers/IconPicker/README.md +1161 -0
  141. package/dist/components/pickers/IconPicker/theme.css +28 -0
  142. package/dist/components/pickers/theme.css +82 -0
  143. package/dist/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
  144. package/dist/components/views/MarkdownViewer/README.md +833 -0
  145. package/dist/components/views/MarkdownViewer/theme.css +130 -0
  146. package/package.json +3 -2
@@ -0,0 +1,22 @@
1
+ @import "../theme.css";
2
+
3
+ /* Light theme */
4
+ :root {
5
+ /* Weekdays */
6
+ --datepicker-weekdays-opacity: 0.5;
7
+
8
+ /* Selected Day */
9
+ --datepicker-day-selected-bg: #acd8fc;
10
+ --datepicker-day-selected-border: #5eb1ef;
11
+ }
12
+
13
+ /* Dark theme */
14
+ .dark,
15
+ .dark-theme {
16
+ /* Weekdays */
17
+ --datepicker-weekdays-opacity: 0.5;
18
+
19
+ /* Selected Day */
20
+ --datepicker-day-selected-bg: #0090ff;
21
+ --datepicker-day-selected-border: #eeeeee;
22
+ }
@@ -0,0 +1,534 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, watch } from "vue";
3
+ import gsap from "gsap";
4
+ import { type FileError, type DropState } from "./types";
5
+ import { icons } from "@umbra.ui/icons";
6
+ import "./theme.css";
7
+
8
+ export interface FileDropProps {
9
+ accept?: string | string[];
10
+ multiple?: boolean;
11
+ disabled?: boolean;
12
+ maxSize?: number; // in bytes
13
+ maxFiles?: number;
14
+ recursive?: boolean; // for directory traversal
15
+ appendFiles?: boolean; // append new files or replace
16
+ }
17
+
18
+ // Props
19
+ const props = withDefaults(defineProps<FileDropProps>(), {
20
+ accept: "*/*",
21
+ multiple: true,
22
+ disabled: false,
23
+ maxSize: Infinity,
24
+ maxFiles: Infinity,
25
+ recursive: true,
26
+ appendFiles: true,
27
+ });
28
+
29
+ // Emits
30
+ const emit = defineEmits<{
31
+ "update:files": [files: File[]];
32
+ error: [errors: FileError[]];
33
+ drop: [files: File[]];
34
+ }>();
35
+
36
+ // Helper to get the correct icon component
37
+ const getIconComponent = (iconName: string) => {
38
+ return (
39
+ icons[iconName as keyof typeof icons] || icons.note || icons["circle-plus"]
40
+ );
41
+ };
42
+
43
+ // State
44
+ const dropState = ref<DropState>({
45
+ isDragging: false,
46
+ isProcessing: false,
47
+ dragCount: 0,
48
+ });
49
+
50
+ const droppedFiles = ref<File[]>([]);
51
+ const errors = ref<FileError[]>([]);
52
+
53
+ // Refs
54
+ const dropZone = ref<HTMLElement>();
55
+ const overlay = ref<HTMLElement>();
56
+
57
+ // Computed
58
+ const acceptedTypes = computed(() => {
59
+ if (Array.isArray(props.accept)) {
60
+ return props.accept;
61
+ }
62
+ return props.accept.split(",").map((type) => type.trim());
63
+ });
64
+
65
+ const isActive = computed(() => !props.disabled && dropState.value.isDragging);
66
+
67
+ // File validation
68
+ const validateFile = (file: File): string | null => {
69
+ // Check file type
70
+ if (
71
+ props.accept !== "*/*" &&
72
+ !acceptedTypes.value.some((type) => {
73
+ if (type.startsWith(".")) {
74
+ return file.name.toLowerCase().endsWith(type.toLowerCase());
75
+ }
76
+ if (type.endsWith("/*")) {
77
+ const category = type.split("/")[0];
78
+ return file.type.startsWith(category);
79
+ }
80
+ return file.type === type;
81
+ })
82
+ ) {
83
+ return `File type not accepted. Accepted types: ${acceptedTypes.value.join(
84
+ ", "
85
+ )}`;
86
+ }
87
+
88
+ // Check file size
89
+ if (file.size > props.maxSize) {
90
+ return `File size exceeds maximum of ${formatFileSize(props.maxSize)}`;
91
+ }
92
+
93
+ return null;
94
+ };
95
+
96
+ // Utility functions
97
+ const formatFileSize = (bytes: number): string => {
98
+ if (bytes === 0) return "0 Bytes";
99
+ const k = 1024;
100
+ const sizes = ["Bytes", "KB", "MB", "GB"];
101
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
102
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
103
+ };
104
+
105
+ // File processing
106
+ const processFiles = async (items: DataTransferItemList) => {
107
+ dropState.value.isProcessing = true;
108
+ const newFiles: File[] = [];
109
+ const fileErrors: FileError[] = [];
110
+
111
+ const processEntry = async (entry: FileSystemEntry): Promise<void> => {
112
+ if (entry.isFile) {
113
+ return new Promise((resolve) => {
114
+ (entry as FileSystemFileEntry).file((file) => {
115
+ const error = validateFile(file);
116
+ if (error) {
117
+ fileErrors.push({ file: file.name, error });
118
+ } else if (
119
+ droppedFiles.value.length + newFiles.length <
120
+ props.maxFiles
121
+ ) {
122
+ newFiles.push(file);
123
+ } else {
124
+ fileErrors.push({
125
+ file: file.name,
126
+ error: `Maximum number of files (${props.maxFiles}) exceeded`,
127
+ });
128
+ }
129
+ resolve();
130
+ });
131
+ });
132
+ } else if (entry.isDirectory && props.recursive) {
133
+ const directoryReader = (
134
+ entry as FileSystemDirectoryEntry
135
+ ).createReader();
136
+ return new Promise((resolve) => {
137
+ const readAllEntries = async () => {
138
+ directoryReader.readEntries(async (entries) => {
139
+ if (entries.length === 0) {
140
+ resolve();
141
+ return;
142
+ }
143
+ await Promise.all(entries.map(processEntry));
144
+ await readAllEntries();
145
+ });
146
+ };
147
+ readAllEntries();
148
+ });
149
+ }
150
+ };
151
+
152
+ // Convert items to entries and process
153
+ const promises: Promise<void>[] = [];
154
+ for (let i = 0; i < items.length; i++) {
155
+ const entry = items[i].webkitGetAsEntry();
156
+ if (entry) {
157
+ promises.push(processEntry(entry));
158
+ }
159
+ }
160
+
161
+ await Promise.all(promises);
162
+
163
+ // Handle multiple file restriction
164
+ if (!props.multiple) {
165
+ if (newFiles.length > 0) {
166
+ droppedFiles.value = [newFiles[0]];
167
+ if (newFiles.length > 1) {
168
+ fileErrors.push({
169
+ file: "Multiple files",
170
+ error: "Only one file allowed",
171
+ });
172
+ }
173
+ }
174
+ } else {
175
+ // Append or replace based on prop
176
+ if (props.appendFiles) {
177
+ droppedFiles.value = [...droppedFiles.value, ...newFiles];
178
+ } else {
179
+ droppedFiles.value = newFiles;
180
+ }
181
+ }
182
+
183
+ errors.value = fileErrors;
184
+ dropState.value.isProcessing = false;
185
+
186
+ emit("update:files", droppedFiles.value);
187
+ emit("drop", newFiles); // Emit only the newly dropped files
188
+ if (fileErrors.length > 0) {
189
+ emit("error", fileErrors);
190
+ }
191
+ };
192
+
193
+ // Drag handlers
194
+ const handleDragEnter = (e: DragEvent) => {
195
+ e.preventDefault();
196
+ if (props.disabled) return;
197
+
198
+ dropState.value.dragCount++;
199
+ if (dropState.value.dragCount === 1) {
200
+ dropState.value.isDragging = true;
201
+ showOverlay();
202
+ }
203
+ };
204
+
205
+ const handleDragLeave = (e: DragEvent) => {
206
+ e.preventDefault();
207
+ if (props.disabled) return;
208
+
209
+ dropState.value.dragCount--;
210
+ if (dropState.value.dragCount === 0) {
211
+ dropState.value.isDragging = false;
212
+ hideOverlay();
213
+ }
214
+ };
215
+
216
+ const handleDragOver = (e: DragEvent) => {
217
+ e.preventDefault();
218
+ if (props.disabled) return;
219
+
220
+ // Set the drop effect
221
+ if (e.dataTransfer) {
222
+ e.dataTransfer.dropEffect = "copy";
223
+ }
224
+ };
225
+
226
+ const handleDrop = async (e: DragEvent) => {
227
+ e.preventDefault();
228
+ dropState.value.dragCount = 0;
229
+ dropState.value.isDragging = false;
230
+ hideOverlay();
231
+
232
+ if (props.disabled || !e.dataTransfer) return;
233
+
234
+ await processFiles(e.dataTransfer.items);
235
+ };
236
+
237
+ // Animation functions
238
+ const showOverlay = () => {
239
+ if (!overlay.value) return;
240
+ gsap.to(overlay.value, {
241
+ duration: 0.2,
242
+ opacity: 1,
243
+ scale: 1,
244
+ ease: "power2.out",
245
+ });
246
+ };
247
+
248
+ const hideOverlay = () => {
249
+ if (!overlay.value) return;
250
+ gsap.to(overlay.value, {
251
+ duration: 0.2,
252
+ opacity: 0,
253
+ scale: 0.95,
254
+ ease: "power2.in",
255
+ });
256
+ };
257
+
258
+ // File input handler (for click-to-upload)
259
+ const handleFileInput = (e: Event) => {
260
+ const input = e.target as HTMLInputElement;
261
+ if (!input.files) return;
262
+
263
+ const files = Array.from(input.files);
264
+ const validFiles: File[] = [];
265
+ const fileErrors: FileError[] = [];
266
+
267
+ files.forEach((file) => {
268
+ const error = validateFile(file);
269
+ if (error) {
270
+ fileErrors.push({ file: file.name, error });
271
+ } else if (droppedFiles.value.length + validFiles.length < props.maxFiles) {
272
+ validFiles.push(file);
273
+ } else {
274
+ fileErrors.push({
275
+ file: file.name,
276
+ error: `Maximum number of files (${props.maxFiles}) exceeded`,
277
+ });
278
+ }
279
+ });
280
+
281
+ if (!props.multiple && validFiles.length > 0) {
282
+ droppedFiles.value = [validFiles[0]];
283
+ } else {
284
+ if (props.appendFiles) {
285
+ droppedFiles.value = [...droppedFiles.value, ...validFiles];
286
+ } else {
287
+ droppedFiles.value = validFiles;
288
+ }
289
+ }
290
+
291
+ errors.value = fileErrors;
292
+
293
+ emit("update:files", droppedFiles.value);
294
+ emit("drop", validFiles); // Emit only the newly added files
295
+ if (fileErrors.length > 0) {
296
+ emit("error", fileErrors);
297
+ }
298
+
299
+ // Reset input value to allow selecting the same file again
300
+ input.value = "";
301
+ };
302
+
303
+ // Public methods
304
+ const openFileDialog = () => {
305
+ const input = dropZone.value?.querySelector(
306
+ 'input[type="file"]'
307
+ ) as HTMLInputElement;
308
+ input?.click();
309
+ };
310
+
311
+ const clearFiles = () => {
312
+ droppedFiles.value = [];
313
+ errors.value = [];
314
+ emit("update:files", []);
315
+ };
316
+
317
+ // Expose public methods
318
+ defineExpose({
319
+ openFileDialog,
320
+ clearFiles,
321
+ files: droppedFiles,
322
+ errors,
323
+ });
324
+ </script>
325
+
326
+ <template>
327
+ <div
328
+ ref="dropZone"
329
+ :class="[
330
+ $style.container,
331
+ {
332
+ [$style.active]: isActive,
333
+ [$style.disabled]: disabled,
334
+ [$style.processing]: dropState.isProcessing,
335
+ },
336
+ ]"
337
+ @dragenter="handleDragEnter"
338
+ @dragleave="handleDragLeave"
339
+ @dragover="handleDragOver"
340
+ @drop="handleDrop"
341
+ >
342
+ <!-- Hidden file input -->
343
+ <input
344
+ type="file"
345
+ :class="$style.input"
346
+ :accept="acceptedTypes.join(',')"
347
+ :multiple="multiple"
348
+ :disabled="disabled"
349
+ @change="handleFileInput"
350
+ />
351
+
352
+ <!-- Default slot content -->
353
+ <div :class="$style.content" @click="openFileDialog">
354
+ <slot>
355
+ <div :class="$style.defaultContent">
356
+ <component
357
+ :class="$style.icon"
358
+ :is="getIconComponent('music')"
359
+ :size="30"
360
+ />
361
+ <div :class="$style.labels">
362
+ <p :class="[$style.title, 'body']">
363
+ Drop files here or click to upload
364
+ </p>
365
+ <p :class="[$style.subtitle, 'subheadline']">
366
+ <template v-if="accept !== '*/*'">
367
+ Accepts: {{ acceptedTypes.join(", ") }}
368
+ </template>
369
+ <template v-if="maxSize !== Infinity">
370
+ • Max size: {{ formatFileSize(maxSize) }}
371
+ </template>
372
+ </p>
373
+ </div>
374
+ </div>
375
+ </slot>
376
+ </div>
377
+
378
+ <!-- Drag overlay -->
379
+ <div ref="overlay" :class="$style.overlay">
380
+ <slot name="overlay">
381
+ <div :class="$style.overlayContent">
382
+ <component :is="getIconComponent('music')" :size="30" />
383
+ <p :class="[$style.overlayText, 'body']">Drop files here</p>
384
+ </div>
385
+ </slot>
386
+ </div>
387
+
388
+ <!-- Processing indicator -->
389
+ <div v-if="dropState.isProcessing" :class="$style.processing">
390
+ <slot name="processing">
391
+ <div :class="$style.spinner"></div>
392
+ </slot>
393
+ </div>
394
+ </div>
395
+ </template>
396
+
397
+ <style module>
398
+ .container {
399
+ position: relative;
400
+ width: 100%;
401
+ min-height: 200px;
402
+ border: 1px dashed var(--filepicker-container-border);
403
+ border-radius: 0.706rem;
404
+ background-color: var(--filepicker-container-bg);
405
+ transition: all 0.2s ease;
406
+ overflow: hidden;
407
+ }
408
+
409
+ .container:hover:not(.disabled) {
410
+ border-color: var(--filepicker-container-hover-border);
411
+ background-color: var(--filepicker-container-hover-bg);
412
+ }
413
+
414
+ .container.active {
415
+ border: 1px solid var(--filepicker-container-active-border);
416
+ background-color: var(--filepicker-container-active-bg);
417
+ color: var(--filepicker-container-active-text);
418
+ }
419
+
420
+ .container.active .title,
421
+ .container.active .subtitle,
422
+ .container.active .icon {
423
+ opacity: 0;
424
+ }
425
+
426
+ .container.disabled {
427
+ opacity: var(--filepicker-disabled-opacity);
428
+ cursor: not-allowed;
429
+ }
430
+
431
+ .container.processing {
432
+ pointer-events: none;
433
+ }
434
+
435
+ .input {
436
+ position: absolute;
437
+ width: 0;
438
+ height: 0;
439
+ opacity: 0;
440
+ overflow: hidden;
441
+ }
442
+
443
+ .content {
444
+ display: flex;
445
+ align-items: center;
446
+ justify-content: center;
447
+ min-height: 200px;
448
+ padding: 2rem;
449
+ cursor: pointer;
450
+ }
451
+
452
+ .disabled .content {
453
+ cursor: not-allowed;
454
+ }
455
+
456
+ .defaultContent {
457
+ display: flex;
458
+ align-items: center;
459
+ gap: 10px;
460
+ }
461
+
462
+ .icon {
463
+ color: var(--filepicker-icon-color);
464
+ }
465
+
466
+ .labels {
467
+ display: flex;
468
+ flex-direction: column;
469
+ align-items: start;
470
+ gap: 0.25rem;
471
+ }
472
+
473
+ .title {
474
+ color: var(--filepicker-title-color);
475
+ }
476
+
477
+ .subtitle {
478
+ color: var(--filepicker-subtitle-color);
479
+ }
480
+
481
+ .overlay {
482
+ position: absolute;
483
+ inset: 0;
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: center;
487
+ background-color: var(--filepicker-overlay-bg);
488
+ backdrop-filter: blur(4px);
489
+ opacity: 0;
490
+ scale: 0.95;
491
+ pointer-events: none;
492
+ }
493
+
494
+ .overlayContent {
495
+ display: flex;
496
+ align-items: center;
497
+ gap: 10px;
498
+ }
499
+
500
+ .overlayIcon {
501
+ width: 4rem;
502
+ height: 4rem;
503
+ margin: 0 auto 1rem;
504
+ color: var(--background-1);
505
+ }
506
+
507
+ .overlayText {
508
+ color: var(--filepicker-overlay-text);
509
+ }
510
+
511
+ .processing {
512
+ position: absolute;
513
+ inset: 0;
514
+ display: flex;
515
+ align-items: center;
516
+ justify-content: center;
517
+ background-color: var(--filepicker-processing-bg);
518
+ }
519
+
520
+ .spinner {
521
+ width: 2rem;
522
+ height: 2rem;
523
+ border: 3px solid var(--filepicker-spinner-border);
524
+ border-top-color: var(--filepicker-spinner-border-top);
525
+ border-radius: 50%;
526
+ animation: spin 0.8s linear infinite;
527
+ }
528
+
529
+ @keyframes spin {
530
+ to {
531
+ transform: rotate(360deg);
532
+ }
533
+ }
534
+ </style>