@budibase/bbui 1.3.0 → 1.3.3

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 (152) hide show
  1. package/LICENSE +375 -0
  2. package/README.md +19 -17
  3. package/dist/bbui.es.js +210 -0
  4. package/dist/bbui.es.js.map +1 -0
  5. package/package.json +71 -26
  6. package/src/ActionButton/ActionButton.svelte +102 -0
  7. package/src/ActionGroup/ActionGroup.svelte +26 -0
  8. package/src/ActionMenu/ActionMenu.svelte +44 -0
  9. package/src/Actions/ClickOutside.svench.svx +30 -0
  10. package/src/Actions/PositionDropdown.svench.svx +81 -0
  11. package/src/Actions/autoresize_textarea.js +14 -0
  12. package/src/Actions/click_outside.js +18 -0
  13. package/src/Actions/position_dropdown.js +66 -0
  14. package/src/Avatar/Avatar.svelte +62 -0
  15. package/src/Badge/Badge.svelte +29 -0
  16. package/src/Banner/Banner.svelte +72 -0
  17. package/src/Banner/BannerDisplay.svelte +31 -0
  18. package/src/Button/Button.svelte +114 -0
  19. package/src/Button/Button.svench +138 -0
  20. package/src/ButtonGroup/ButtonGroup.svelte +30 -0
  21. package/src/ClearButton/ClearButton.svelte +19 -0
  22. package/src/ColorPicker/ColorPicker.svelte +297 -0
  23. package/src/DetailSummary/DetailSummary.svelte +83 -0
  24. package/src/Divider/Divider.svelte +28 -0
  25. package/src/Drawer/Drawer.svelte +113 -0
  26. package/src/Drawer/Drawer.svench +42 -0
  27. package/src/Drawer/DrawerContent.svelte +46 -0
  28. package/src/Form/Checkbox.svelte +23 -0
  29. package/src/Form/Combobox.svelte +45 -0
  30. package/src/Form/Core/Checkbox.svelte +69 -0
  31. package/src/Form/Core/CheckboxGroup.svelte +68 -0
  32. package/src/Form/Core/Combobox.svelte +142 -0
  33. package/src/Form/Core/DatePicker.svelte +257 -0
  34. package/src/Form/Core/Dropzone.svelte +450 -0
  35. package/src/Form/Core/InputDropdown.svelte +228 -0
  36. package/src/Form/Core/Multiselect.svelte +90 -0
  37. package/src/Form/Core/Picker.svelte +259 -0
  38. package/src/Form/Core/PickerDropdown.svelte +436 -0
  39. package/src/Form/Core/RadioGroup.svelte +48 -0
  40. package/src/Form/Core/RichTextField.svelte +42 -0
  41. package/src/Form/Core/Search.svelte +105 -0
  42. package/src/Form/Core/Select.svelte +75 -0
  43. package/src/Form/Core/Slider.svelte +86 -0
  44. package/src/Form/Core/Stepper.svelte +172 -0
  45. package/src/Form/Core/Switch.svelte +37 -0
  46. package/src/Form/Core/TextArea.svelte +78 -0
  47. package/src/Form/Core/TextField.svelte +119 -0
  48. package/src/Form/Core/index.js +15 -0
  49. package/src/Form/DatePicker.svelte +39 -0
  50. package/src/Form/Dropzone.svelte +42 -0
  51. package/src/Form/Field.svelte +42 -0
  52. package/src/Form/FieldLabel.svelte +32 -0
  53. package/src/Form/Input.svelte +40 -45
  54. package/src/Form/InputDropdown.svelte +55 -0
  55. package/src/Form/Multiselect.svelte +40 -0
  56. package/src/Form/PickerDropdown.svelte +134 -0
  57. package/src/Form/RadioGroup.svelte +42 -0
  58. package/src/Form/RichTextField.svelte +36 -0
  59. package/src/Form/Search.svelte +36 -0
  60. package/src/Form/Select.svelte +48 -43
  61. package/src/Form/Slider.svelte +24 -0
  62. package/src/Form/Stepper.svelte +45 -0
  63. package/src/Form/TextArea.svelte +29 -44
  64. package/src/Form/Toggle.svelte +23 -0
  65. package/src/Icon/Icon.svelte +91 -0
  66. package/src/IconPicker/IconPicker.svelte +177 -0
  67. package/src/IconSideNav/IconSideNav.svelte +14 -0
  68. package/src/IconSideNav/IconSideNavItem.svelte +56 -0
  69. package/src/InlineAlert/InlineAlert.svelte +56 -0
  70. package/src/Label/Label.svelte +20 -0
  71. package/src/Layout/Layout.svelte +94 -0
  72. package/src/Layout/Page.svelte +32 -0
  73. package/src/Link/Link.svelte +24 -0
  74. package/src/List/List.svelte +28 -0
  75. package/src/List/ListItem.svelte +98 -0
  76. package/src/Markdown/MarkdownEditor.svelte +60 -0
  77. package/src/Markdown/MarkdownViewer.svelte +70 -0
  78. package/src/Markdown/SpectrumMDE.svelte +184 -0
  79. package/src/Menu/Item.svelte +96 -0
  80. package/src/Menu/Menu.svelte +7 -0
  81. package/src/Menu/Menu.svench +19 -0
  82. package/src/Menu/Section.svelte +10 -0
  83. package/src/Menu/Separator.svelte +1 -0
  84. package/src/Modal/Content.svelte +6 -0
  85. package/src/Modal/CustomContent.svelte +38 -0
  86. package/src/Modal/Modal.svelte +165 -0
  87. package/src/Modal/Modal.svench +117 -0
  88. package/src/Modal/ModalContent.svelte +192 -0
  89. package/src/Modal/QuizModal.svelte +54 -0
  90. package/src/Notification/Notification.svelte +70 -0
  91. package/src/Notification/NotificationDisplay.svelte +43 -0
  92. package/src/Pagination/Pagination.svelte +57 -0
  93. package/src/Popover/Popover.svelte +77 -0
  94. package/src/Popover/Popover.svench +121 -0
  95. package/src/ProgressBar/ProgressBar.svelte +54 -0
  96. package/src/ProgressCircle/ProgressCircle.svelte +62 -0
  97. package/src/SideNavigation/Item.svelte +47 -0
  98. package/src/SideNavigation/Navigation.svelte +12 -0
  99. package/src/StatusLight/StatusLight.svelte +80 -0
  100. package/src/Stores/Notifications.svench.svx +78 -0
  101. package/src/Stores/banner.js +37 -0
  102. package/src/Stores/notifications.js +84 -0
  103. package/src/Table/ArrayRenderer.svelte +17 -0
  104. package/src/Table/AttachmentRenderer.svelte +67 -0
  105. package/src/Table/BoldRenderer.svelte +15 -0
  106. package/src/Table/BooleanRenderer.svelte +42 -0
  107. package/src/Table/CellRenderer.svelte +69 -0
  108. package/src/Table/CodeRenderer.svelte +14 -0
  109. package/src/Table/DateTimeRenderer.svelte +29 -0
  110. package/src/Table/InternalRenderer.svelte +24 -0
  111. package/src/Table/RelationshipRenderer.svelte +40 -0
  112. package/src/Table/SelectEditRenderer.svelte +28 -0
  113. package/src/Table/StringRenderer.svelte +14 -0
  114. package/src/Table/Table.svelte +635 -0
  115. package/src/Table/index.js +1 -0
  116. package/src/Tabs/Tab.svelte +88 -0
  117. package/src/Tabs/Tabs.svelte +125 -0
  118. package/src/Tags/Tag.svelte +43 -0
  119. package/src/Tags/Tags.svelte +7 -0
  120. package/src/Tooltip/Tooltip.svelte +34 -0
  121. package/src/Tooltip/TooltipWrapper.svelte +60 -0
  122. package/src/TreeView/Item.svelte +41 -0
  123. package/src/TreeView/Tree.svelte +16 -0
  124. package/src/Typography/Body.svelte +21 -0
  125. package/src/Typography/Code.svelte +10 -0
  126. package/src/Typography/Detail.svelte +19 -0
  127. package/src/Typography/Heading.svelte +17 -0
  128. package/src/bbui.css +96 -0
  129. package/src/context.js +3 -0
  130. package/src/helpers.js +134 -0
  131. package/src/index.js +97 -11
  132. package/src/spectrum-icons-rollup.js +31 -0
  133. package/src/spectrum-icons-vite.js +31 -0
  134. package/dist/bundle.css +0 -7
  135. package/dist/bundle.css.map +0 -20
  136. package/dist/index.js +0 -1100
  137. package/dist/index.mjs +0 -1086
  138. package/src/Branding/Budibase-logo.svelte +0 -68
  139. package/src/Branding/Logo.svelte +0 -16
  140. package/src/Branding/Logo.svench +0 -8
  141. package/src/Form/Button.svelte +0 -131
  142. package/src/Form/Button.svench +0 -49
  143. package/src/Form/Input.svench +0 -8
  144. package/src/Form/Select.svench +0 -8
  145. package/src/Form/TextArea.svench +0 -8
  146. package/src/Links/Home.svelte +0 -49
  147. package/src/Links/Home.svench +0 -19
  148. package/src/List/Items/DetailSummary.svelte +0 -66
  149. package/src/List/Items/DetailSummary.svench +0 -15
  150. package/src/actions/autoresize_textarea.js +0 -14
  151. package/src/icons/Contribution.svelte +0 -14
  152. package/src/index.svench.svx +0 -35
@@ -0,0 +1,257 @@
1
+ <script>
2
+ import Flatpickr from "svelte-flatpickr"
3
+ import "flatpickr/dist/flatpickr.css"
4
+ import "@spectrum-css/inputgroup/dist/index-vars.css"
5
+ import "@spectrum-css/textfield/dist/index-vars.css"
6
+ import "@spectrum-css/picker/dist/index-vars.css"
7
+ import { createEventDispatcher } from "svelte"
8
+ import { uuid } from "../../helpers"
9
+
10
+ export let id = null
11
+ export let disabled = false
12
+ export let error = null
13
+ export let enableTime = true
14
+ export let value = null
15
+ export let placeholder = null
16
+ export let appendTo = undefined
17
+ export let timeOnly = false
18
+ export let ignoreTimezones = false
19
+ export let time24hr = false
20
+
21
+ const dispatch = createEventDispatcher()
22
+ const flatpickrId = `${uuid()}-wrapper`
23
+ let open = false
24
+ let flatpickr, flatpickrOptions
25
+
26
+ const resolveTimeStamp = timestamp => {
27
+ let maskedDate = new Date(`0-${timestamp}`)
28
+
29
+ if (maskedDate instanceof Date && !isNaN(maskedDate.getTime())) {
30
+ return maskedDate
31
+ } else {
32
+ return null
33
+ }
34
+ }
35
+
36
+ $: flatpickrOptions = {
37
+ element: `#${flatpickrId}`,
38
+ enableTime: timeOnly || enableTime || false,
39
+ noCalendar: timeOnly || false,
40
+ altInput: true,
41
+ time_24hr: time24hr || false,
42
+ altFormat: timeOnly ? "H:i" : enableTime ? "F j Y, H:i" : "F j, Y",
43
+ wrap: true,
44
+ appendTo,
45
+ disableMobile: "true",
46
+ onReady: () => {
47
+ let timestamp = resolveTimeStamp(value)
48
+ if (timeOnly && timestamp) {
49
+ dispatch("change", timestamp.toISOString())
50
+ }
51
+ },
52
+ }
53
+
54
+ $: redrawOptions = {
55
+ timeOnly,
56
+ enableTime,
57
+ time24hr,
58
+ }
59
+
60
+ const handleChange = event => {
61
+ const [dates] = event.detail
62
+ const noTimezone = enableTime && !timeOnly && ignoreTimezones
63
+ let newValue = dates[0]
64
+ if (newValue) {
65
+ newValue = newValue.toISOString()
66
+ }
67
+
68
+ // If time only set date component to 2000-01-01
69
+ if (timeOnly) {
70
+ // Classic flackpickr causing issues.
71
+ // When selecting a value for the first time for a "time only" field,
72
+ // the time is always offset by 1 hour for some reason (regardless of time
73
+ // zone) so we need to correct it.
74
+ if (!value && newValue) {
75
+ newValue = new Date(dates[0].getTime() + 60 * 60 * 1000).toISOString()
76
+ }
77
+ newValue = `2000-01-01T${newValue.split("T")[1]}`
78
+ }
79
+
80
+ // For date-only fields, construct a manual timestamp string without a time
81
+ // or time zone
82
+ else if (!enableTime) {
83
+ const year = dates[0].getFullYear()
84
+ const month = `${dates[0].getMonth() + 1}`.padStart(2, "0")
85
+ const day = `${dates[0].getDate()}`.padStart(2, "0")
86
+ newValue = `${year}-${month}-${day}T00:00:00.000`
87
+ }
88
+
89
+ // For non-timezone-aware fields, create an ISO 8601 timestamp of the exact
90
+ // time picked, without timezone
91
+ else if (noTimezone) {
92
+ const offset = dates[0].getTimezoneOffset() * 60000
93
+ newValue = new Date(dates[0].getTime() - offset)
94
+ .toISOString()
95
+ .slice(0, -1)
96
+ }
97
+
98
+ dispatch("change", newValue)
99
+ }
100
+
101
+ const clearDateOnBackspace = event => {
102
+ if (["Backspace", "Clear", "Delete"].includes(event.key)) {
103
+ dispatch("change", null)
104
+ flatpickr.close()
105
+ }
106
+ }
107
+
108
+ const onOpen = () => {
109
+ open = true
110
+ document.addEventListener("keyup", clearDateOnBackspace)
111
+ }
112
+
113
+ const onClose = () => {
114
+ open = false
115
+ document.removeEventListener("keyup", clearDateOnBackspace)
116
+
117
+ // Manually blur all input fields since flatpickr creates a second
118
+ // duplicate input field.
119
+ // We need to blur both because the focus styling does not get properly
120
+ // applied.
121
+ const els = document.querySelectorAll(`#${flatpickrId} input`)
122
+ els.forEach(el => el.blur())
123
+ }
124
+
125
+ const parseDate = val => {
126
+ if (!val) {
127
+ return null
128
+ }
129
+ let date
130
+ let time
131
+
132
+ // it is a string like 00:00:00, just time
133
+ let ts = resolveTimeStamp(val)
134
+
135
+ if (timeOnly && ts) {
136
+ date = ts
137
+ } else if (val instanceof Date) {
138
+ // Use real date obj if already parsed
139
+ date = val
140
+ } else if (isNaN(val)) {
141
+ // Treat as date string of some sort
142
+ date = new Date(val)
143
+ } else {
144
+ // Treat as numerical timestamp
145
+ date = new Date(parseInt(val))
146
+ }
147
+
148
+ time = date.getTime()
149
+ if (isNaN(time)) {
150
+ return null
151
+ }
152
+
153
+ // By rounding to the nearest second we avoid locking up in an endless
154
+ // loop in the builder, caused by potentially enriching {{ now }} to every
155
+ // millisecond.
156
+ return new Date(Math.floor(time / 1000) * 1000)
157
+ }
158
+ </script>
159
+
160
+ {#key redrawOptions}
161
+ <Flatpickr
162
+ bind:flatpickr
163
+ value={parseDate(value)}
164
+ on:open={onOpen}
165
+ on:close={onClose}
166
+ options={flatpickrOptions}
167
+ on:change={handleChange}
168
+ element={`#${flatpickrId}`}
169
+ >
170
+ <div
171
+ id={flatpickrId}
172
+ class:is-disabled={disabled}
173
+ class:is-invalid={!!error}
174
+ class="flatpickr spectrum-InputGroup spectrum-Datepicker"
175
+ class:is-focused={open}
176
+ aria-readonly="false"
177
+ aria-required="false"
178
+ aria-haspopup="true"
179
+ >
180
+ <div
181
+ on:click={flatpickr?.open}
182
+ class="spectrum-Textfield spectrum-InputGroup-textfield"
183
+ class:is-disabled={disabled}
184
+ class:is-invalid={!!error}
185
+ >
186
+ {#if !!error}
187
+ <svg
188
+ class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
189
+ focusable="false"
190
+ aria-hidden="true"
191
+ >
192
+ <use xlink:href="#spectrum-icon-18-Alert" />
193
+ </svg>
194
+ {/if}
195
+ <input
196
+ data-input
197
+ type="text"
198
+ class="spectrum-Textfield-input spectrum-InputGroup-input"
199
+ class:is-disabled={disabled}
200
+ {placeholder}
201
+ {id}
202
+ {value}
203
+ />
204
+ </div>
205
+ <button
206
+ type="button"
207
+ class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
208
+ tabindex="-1"
209
+ class:is-disabled={disabled}
210
+ class:is-invalid={!!error}
211
+ on:click={flatpickr?.open}
212
+ >
213
+ <svg
214
+ class="spectrum-Icon spectrum-Icon--sizeM"
215
+ focusable="false"
216
+ aria-hidden="true"
217
+ aria-label="Calendar"
218
+ >
219
+ <use xlink:href="#spectrum-icon-18-Calendar" />
220
+ </svg>
221
+ </button>
222
+ </div>
223
+ </Flatpickr>
224
+ {/key}
225
+ {#if open}
226
+ <div class="overlay" on:mousedown|self={flatpickr?.close} />
227
+ {/if}
228
+
229
+ <style>
230
+ .spectrum-Textfield-input {
231
+ pointer-events: none;
232
+ }
233
+ .spectrum-Textfield:not(.is-disabled):hover {
234
+ cursor: pointer;
235
+ }
236
+ .flatpickr {
237
+ width: 100%;
238
+ overflow: hidden;
239
+ }
240
+ .flatpickr .spectrum-Textfield {
241
+ width: 100%;
242
+ }
243
+ .overlay {
244
+ position: fixed;
245
+ top: 0;
246
+ left: 0;
247
+ width: 100vw;
248
+ height: 100vh;
249
+ z-index: 999;
250
+ }
251
+ :global(.flatpickr-calendar) {
252
+ font-family: "Source Sans Pro", sans-serif;
253
+ }
254
+ .is-disabled {
255
+ pointer-events: none !important;
256
+ }
257
+ </style>
@@ -0,0 +1,450 @@
1
+ <script>
2
+ import "@spectrum-css/dropzone/dist/index-vars.css"
3
+ import "@spectrum-css/typography/dist/index-vars.css"
4
+ import "@spectrum-css/illustratedmessage/dist/index-vars.css"
5
+ import { createEventDispatcher } from "svelte"
6
+ import { uuid } from "../../helpers"
7
+ import Icon from "../../Icon/Icon.svelte"
8
+ import Link from "../../Link/Link.svelte"
9
+ import Tag from "../../Tags/Tag.svelte"
10
+ import Tags from "../../Tags/Tags.svelte"
11
+
12
+ const BYTES_IN_KB = 1000
13
+ const BYTES_IN_MB = 1000000
14
+
15
+ export let value = []
16
+ export let id = null
17
+ export let disabled = false
18
+ export let fileSizeLimit = BYTES_IN_MB * 20
19
+ export let processFiles = null
20
+ export let deleteAttachments = null
21
+ export let handleFileTooLarge = null
22
+ export let handleTooManyFiles = null
23
+ export let gallery = true
24
+ export let error = null
25
+ export let fileTags = []
26
+ export let maximum = null
27
+ export let extensions = "*"
28
+
29
+ const dispatch = createEventDispatcher()
30
+ const imageExtensions = [
31
+ "png",
32
+ "tiff",
33
+ "gif",
34
+ "raw",
35
+ "jpg",
36
+ "jpeg",
37
+ "svg",
38
+ "bmp",
39
+ "jfif",
40
+ ]
41
+
42
+ const fieldId = id || uuid()
43
+ let selectedImageIdx = 0
44
+ let fileDragged = false
45
+ let selectedUrl
46
+ $: selectedImage = value?.[selectedImageIdx] ?? null
47
+ $: fileCount = value?.length ?? 0
48
+ $: isImage =
49
+ imageExtensions.includes(selectedImage?.extension?.toLowerCase()) ||
50
+ selectedImage?.type?.startsWith("image")
51
+
52
+ $: {
53
+ if (selectedImage?.url) {
54
+ selectedUrl = selectedImage?.url
55
+ } else if (selectedImage) {
56
+ try {
57
+ let reader = new FileReader()
58
+ reader.readAsDataURL(selectedImage)
59
+ reader.onload = e => {
60
+ selectedUrl = e.target.result
61
+ }
62
+ } catch (error) {
63
+ selectedUrl = null
64
+ }
65
+ }
66
+ }
67
+
68
+ async function processFileList(fileList) {
69
+ if (
70
+ handleFileTooLarge &&
71
+ Array.from(fileList).some(file => file.size >= fileSizeLimit)
72
+ ) {
73
+ handleFileTooLarge(fileSizeLimit, value)
74
+ return
75
+ }
76
+
77
+ const fileCount = fileList.length + value.length
78
+ if (handleTooManyFiles && maximum && fileCount > maximum) {
79
+ handleTooManyFiles(maximum)
80
+ return
81
+ }
82
+
83
+ if (processFiles) {
84
+ const processedFiles = await processFiles(fileList)
85
+ const newValue = [...value, ...processedFiles]
86
+ dispatch("change", newValue)
87
+ selectedImageIdx = newValue.length - 1
88
+ } else {
89
+ dispatch("change", fileList)
90
+ }
91
+ }
92
+
93
+ async function removeFile() {
94
+ dispatch(
95
+ "change",
96
+ value.filter((x, idx) => idx !== selectedImageIdx)
97
+ )
98
+ if (deleteAttachments) {
99
+ await deleteAttachments(
100
+ value.filter((x, idx) => idx === selectedImageIdx).map(item => item.key)
101
+ )
102
+ }
103
+ selectedImageIdx = 0
104
+ }
105
+
106
+ function navigateLeft() {
107
+ selectedImageIdx -= 1
108
+ }
109
+
110
+ function navigateRight() {
111
+ selectedImageIdx += 1
112
+ }
113
+
114
+ function handleFile(evt) {
115
+ processFileList(evt.target.files)
116
+ }
117
+
118
+ function handleDragOver(evt) {
119
+ evt.preventDefault()
120
+ fileDragged = true
121
+ }
122
+
123
+ function handleDragLeave(evt) {
124
+ evt.preventDefault()
125
+ fileDragged = false
126
+ }
127
+
128
+ function handleDrop(evt) {
129
+ evt.preventDefault()
130
+ processFileList(evt.dataTransfer.files)
131
+ fileDragged = false
132
+ }
133
+ </script>
134
+
135
+ <div class="container">
136
+ {#if selectedImage}
137
+ {#if gallery}
138
+ <div class="gallery">
139
+ <div class="title">
140
+ <div class="filename">
141
+ {#if selectedUrl}
142
+ <Link
143
+ target="_blank"
144
+ download={selectedImage.name}
145
+ href={selectedUrl}
146
+ >
147
+ {selectedImage.name}
148
+ </Link>
149
+ {:else}
150
+ {selectedImage.name}
151
+ {/if}
152
+ </div>
153
+ {#if selectedImage.size}
154
+ <div class="filesize">
155
+ {#if selectedImage.size <= BYTES_IN_MB}
156
+ {`${selectedImage.size / BYTES_IN_KB} KB`}
157
+ {:else}{`${selectedImage.size / BYTES_IN_MB} MB`}{/if}
158
+ </div>
159
+ {/if}
160
+ {#if !disabled}
161
+ <div class="delete-button" on:click={removeFile}>
162
+ <Icon name="Close" />
163
+ </div>
164
+ {/if}
165
+ </div>
166
+ {#if isImage}
167
+ <img alt="preview" src={selectedUrl} />
168
+ {:else}
169
+ <div class="placeholder">
170
+ <div class="extension">
171
+ {selectedImage.name || "Unknown file"}
172
+ </div>
173
+ <div>Preview not supported</div>
174
+ </div>
175
+ {/if}
176
+ <div
177
+ class="nav left"
178
+ class:visible={selectedImageIdx > 0}
179
+ on:click={navigateLeft}
180
+ >
181
+ <Icon name="ChevronLeft" />
182
+ </div>
183
+ <div
184
+ class="nav right"
185
+ class:visible={selectedImageIdx < fileCount - 1}
186
+ on:click={navigateRight}
187
+ >
188
+ <Icon name="ChevronRight" />
189
+ </div>
190
+ <div class="footer">File {selectedImageIdx + 1} of {fileCount}</div>
191
+ </div>
192
+ {:else if value?.length}
193
+ {#each value as file}
194
+ <div class="gallery">
195
+ <div class="title">
196
+ <div class="filename">{file.name}</div>
197
+ {#if file.size}
198
+ <div class="filesize">
199
+ {#if file.size <= BYTES_IN_MB}
200
+ {`${file.size / BYTES_IN_KB} KB`}
201
+ {:else}{`${file.size / BYTES_IN_MB} MB`}{/if}
202
+ </div>
203
+ {/if}
204
+ {#if !disabled}
205
+ <div class="delete-button" on:click={removeFile}>
206
+ <Icon name="Close" />
207
+ </div>
208
+ {/if}
209
+ </div>
210
+ </div>
211
+ {/each}
212
+ {/if}
213
+ {/if}
214
+ {#if !maximum || (maximum && value?.length < maximum)}
215
+ <div
216
+ class="spectrum-Dropzone"
217
+ class:is-invalid={!!error}
218
+ class:disabled
219
+ role="region"
220
+ tabindex="0"
221
+ on:dragover={handleDragOver}
222
+ on:dragleave={handleDragLeave}
223
+ on:dragenter={handleDragOver}
224
+ on:drop={handleDrop}
225
+ class:is-dragged={fileDragged}
226
+ >
227
+ <div class="spectrum-IllustratedMessage spectrum-IllustratedMessage--cta">
228
+ <input
229
+ id={fieldId}
230
+ {disabled}
231
+ type="file"
232
+ multiple
233
+ accept={extensions}
234
+ on:change={handleFile}
235
+ />
236
+ <svg
237
+ class="spectrum-IllustratedMessage-illustration"
238
+ width="125"
239
+ height="60"
240
+ viewBox="0 0 199 97.7"
241
+ >
242
+ <defs>
243
+ <style>
244
+ .cls-1,
245
+ .cls-2 {
246
+ fill: none;
247
+ stroke-linecap: round;
248
+ stroke-linejoin: round;
249
+ }
250
+ .cls-1 {
251
+ stroke-width: 3px;
252
+ }
253
+ .cls-2 {
254
+ stroke-width: 2px;
255
+ }
256
+ </style>
257
+ </defs>
258
+ <path
259
+ class="cls-1"
260
+ d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66"
261
+ />
262
+ <line class="cls-1" x1="99.5" y1="95.5" x2="99.5" y2="58.5" />
263
+ <path class="cls-1" d="M105.5,73.5h19a2,2,0,0,0,2-2v-43" />
264
+ <path
265
+ class="cls-1"
266
+ d="M126.5,22.5h-19a2,2,0,0,1-2-2V1.5h-31a2,2,0,0,0-2,2v68a2,2,0,0,0,2,2h19"
267
+ />
268
+ <line class="cls-1" x1="105.5" y1="1.5" x2="126.5" y2="22.5" />
269
+ <path
270
+ class="cls-2"
271
+ d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z"
272
+ />
273
+ <path
274
+ class="cls-2"
275
+ d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2"
276
+ />
277
+ <path
278
+ class="cls-2"
279
+ d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7"
280
+ />
281
+ <path
282
+ class="cls-1"
283
+ d="M139.5,36.5H196A1.49,1.49,0,0,1,197.5,38V72A1.49,1.49,0,0,1,196,73.5H141A1.49,1.49,0,0,1,139.5,72V32A1.49,1.49,0,0,1,141,30.5H154a2.43,2.43,0,0,1,1.67.66l6,5.66"
284
+ />
285
+ <rect
286
+ class="cls-1"
287
+ x="1.5"
288
+ y="34.5"
289
+ width="58"
290
+ height="39"
291
+ rx="2"
292
+ ry="2"
293
+ />
294
+ </svg>
295
+ <h2
296
+ class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading"
297
+ >
298
+ Drag and drop your file
299
+ </h2>
300
+ {#if !disabled}
301
+ <p
302
+ class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description"
303
+ >
304
+ <label for={fieldId} class="spectrum-Link">
305
+ Select a file to upload
306
+ </label>
307
+ <br />
308
+ from your computer
309
+ </p>
310
+ {#if fileTags.length}
311
+ <Tags>
312
+ <div class="tags">
313
+ {#each fileTags as tag}
314
+ <div class="tag">
315
+ <Tag>
316
+ {tag}
317
+ </Tag>
318
+ </div>
319
+ {/each}
320
+ </div>
321
+ </Tags>
322
+ {/if}
323
+ {/if}
324
+ </div>
325
+ </div>
326
+ {/if}
327
+ </div>
328
+
329
+ <style>
330
+ .container {
331
+ --spectrum-dropzone-padding: var(--spectrum-global-dimension-size-400);
332
+ --spectrum-heading-l-text-size: var(
333
+ --spectrum-global-dimension-font-size-400
334
+ );
335
+ }
336
+
337
+ .gallery,
338
+ .spectrum-Dropzone {
339
+ user-select: none;
340
+ }
341
+ .spectrum-Dropzone.is-invalid {
342
+ border-color: var(--spectrum-global-color-red-400);
343
+ }
344
+ input[type="file"] {
345
+ display: none;
346
+ }
347
+
348
+ .gallery {
349
+ display: flex;
350
+ flex-direction: column;
351
+ justify-content: flex-start;
352
+ align-items: stretch;
353
+ background-color: var(--spectrum-global-color-gray-50);
354
+ color: var(--spectrum-alias-text-color);
355
+ font-size: var(--spectrum-alias-item-text-size-m);
356
+ box-sizing: border-box;
357
+ border: var(--spectrum-alias-border-size-thin)
358
+ var(--spectrum-alias-border-color) solid;
359
+ border-radius: var(--spectrum-alias-border-radius-regular);
360
+ padding: 10px;
361
+ margin-bottom: 10px;
362
+ position: relative;
363
+ }
364
+ .placeholder,
365
+ img {
366
+ height: 120px;
367
+ max-width: 100%;
368
+ object-fit: contain;
369
+ margin: 20px 30px;
370
+ }
371
+ .title {
372
+ display: flex;
373
+ flex-direction: row;
374
+ justify-content: flex-start;
375
+ align-items: center;
376
+ }
377
+ .filename {
378
+ flex: 1 1 auto;
379
+ overflow: hidden;
380
+ text-overflow: ellipsis;
381
+ white-space: nowrap;
382
+ width: 0;
383
+ margin-right: 10px;
384
+ user-select: all;
385
+ }
386
+ .placeholder {
387
+ display: flex;
388
+ flex-direction: column;
389
+ justify-content: center;
390
+ align-items: center;
391
+ text-align: center;
392
+ }
393
+ .extension {
394
+ color: var(--spectrum-global-color-gray-600);
395
+ text-transform: uppercase;
396
+ font-weight: 600;
397
+ margin-bottom: 5px;
398
+ user-select: all;
399
+ }
400
+
401
+ .nav {
402
+ padding: var(--spacing-xs);
403
+ position: absolute;
404
+ top: 50%;
405
+ border-radius: 5px;
406
+ display: none;
407
+ transition: all 0.3s;
408
+ }
409
+ .nav.visible {
410
+ display: block;
411
+ }
412
+ .nav:hover {
413
+ cursor: pointer;
414
+ color: var(--blue);
415
+ }
416
+ .left {
417
+ left: 5px;
418
+ }
419
+ .right {
420
+ right: 5px;
421
+ }
422
+ .delete-button {
423
+ transition: all 0.3s;
424
+ margin-left: 10px;
425
+ display: flex;
426
+ }
427
+ .delete-button:hover {
428
+ cursor: pointer;
429
+ color: var(--red);
430
+ }
431
+
432
+ .spectrum-Dropzone.disabled {
433
+ pointer-events: none;
434
+ background-color: var(--spectrum-global-color-gray-200);
435
+ }
436
+ .disabled .spectrum-Heading--sizeL {
437
+ color: var(--spectrum-alias-text-color-disabled);
438
+ }
439
+
440
+ .tags {
441
+ margin-top: 20px;
442
+ display: flex;
443
+ flex-wrap: wrap;
444
+ justify-content: center;
445
+ }
446
+
447
+ .tag {
448
+ margin-top: 8px;
449
+ }
450
+ </style>