@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.
- package/LICENSE +375 -0
- package/README.md +19 -17
- package/dist/bbui.es.js +210 -0
- package/dist/bbui.es.js.map +1 -0
- package/package.json +71 -26
- package/src/ActionButton/ActionButton.svelte +102 -0
- package/src/ActionGroup/ActionGroup.svelte +26 -0
- package/src/ActionMenu/ActionMenu.svelte +44 -0
- package/src/Actions/ClickOutside.svench.svx +30 -0
- package/src/Actions/PositionDropdown.svench.svx +81 -0
- package/src/Actions/autoresize_textarea.js +14 -0
- package/src/Actions/click_outside.js +18 -0
- package/src/Actions/position_dropdown.js +66 -0
- package/src/Avatar/Avatar.svelte +62 -0
- package/src/Badge/Badge.svelte +29 -0
- package/src/Banner/Banner.svelte +72 -0
- package/src/Banner/BannerDisplay.svelte +31 -0
- package/src/Button/Button.svelte +114 -0
- package/src/Button/Button.svench +138 -0
- package/src/ButtonGroup/ButtonGroup.svelte +30 -0
- package/src/ClearButton/ClearButton.svelte +19 -0
- package/src/ColorPicker/ColorPicker.svelte +297 -0
- package/src/DetailSummary/DetailSummary.svelte +83 -0
- package/src/Divider/Divider.svelte +28 -0
- package/src/Drawer/Drawer.svelte +113 -0
- package/src/Drawer/Drawer.svench +42 -0
- package/src/Drawer/DrawerContent.svelte +46 -0
- package/src/Form/Checkbox.svelte +23 -0
- package/src/Form/Combobox.svelte +45 -0
- package/src/Form/Core/Checkbox.svelte +69 -0
- package/src/Form/Core/CheckboxGroup.svelte +68 -0
- package/src/Form/Core/Combobox.svelte +142 -0
- package/src/Form/Core/DatePicker.svelte +257 -0
- package/src/Form/Core/Dropzone.svelte +450 -0
- package/src/Form/Core/InputDropdown.svelte +228 -0
- package/src/Form/Core/Multiselect.svelte +90 -0
- package/src/Form/Core/Picker.svelte +259 -0
- package/src/Form/Core/PickerDropdown.svelte +436 -0
- package/src/Form/Core/RadioGroup.svelte +48 -0
- package/src/Form/Core/RichTextField.svelte +42 -0
- package/src/Form/Core/Search.svelte +105 -0
- package/src/Form/Core/Select.svelte +75 -0
- package/src/Form/Core/Slider.svelte +86 -0
- package/src/Form/Core/Stepper.svelte +172 -0
- package/src/Form/Core/Switch.svelte +37 -0
- package/src/Form/Core/TextArea.svelte +78 -0
- package/src/Form/Core/TextField.svelte +119 -0
- package/src/Form/Core/index.js +15 -0
- package/src/Form/DatePicker.svelte +39 -0
- package/src/Form/Dropzone.svelte +42 -0
- package/src/Form/Field.svelte +42 -0
- package/src/Form/FieldLabel.svelte +32 -0
- package/src/Form/Input.svelte +40 -45
- package/src/Form/InputDropdown.svelte +55 -0
- package/src/Form/Multiselect.svelte +40 -0
- package/src/Form/PickerDropdown.svelte +134 -0
- package/src/Form/RadioGroup.svelte +42 -0
- package/src/Form/RichTextField.svelte +36 -0
- package/src/Form/Search.svelte +36 -0
- package/src/Form/Select.svelte +48 -43
- package/src/Form/Slider.svelte +24 -0
- package/src/Form/Stepper.svelte +45 -0
- package/src/Form/TextArea.svelte +29 -44
- package/src/Form/Toggle.svelte +23 -0
- package/src/Icon/Icon.svelte +91 -0
- package/src/IconPicker/IconPicker.svelte +177 -0
- package/src/IconSideNav/IconSideNav.svelte +14 -0
- package/src/IconSideNav/IconSideNavItem.svelte +56 -0
- package/src/InlineAlert/InlineAlert.svelte +56 -0
- package/src/Label/Label.svelte +20 -0
- package/src/Layout/Layout.svelte +94 -0
- package/src/Layout/Page.svelte +32 -0
- package/src/Link/Link.svelte +24 -0
- package/src/List/List.svelte +28 -0
- package/src/List/ListItem.svelte +98 -0
- package/src/Markdown/MarkdownEditor.svelte +60 -0
- package/src/Markdown/MarkdownViewer.svelte +70 -0
- package/src/Markdown/SpectrumMDE.svelte +184 -0
- package/src/Menu/Item.svelte +96 -0
- package/src/Menu/Menu.svelte +7 -0
- package/src/Menu/Menu.svench +19 -0
- package/src/Menu/Section.svelte +10 -0
- package/src/Menu/Separator.svelte +1 -0
- package/src/Modal/Content.svelte +6 -0
- package/src/Modal/CustomContent.svelte +38 -0
- package/src/Modal/Modal.svelte +165 -0
- package/src/Modal/Modal.svench +117 -0
- package/src/Modal/ModalContent.svelte +192 -0
- package/src/Modal/QuizModal.svelte +54 -0
- package/src/Notification/Notification.svelte +70 -0
- package/src/Notification/NotificationDisplay.svelte +43 -0
- package/src/Pagination/Pagination.svelte +57 -0
- package/src/Popover/Popover.svelte +77 -0
- package/src/Popover/Popover.svench +121 -0
- package/src/ProgressBar/ProgressBar.svelte +54 -0
- package/src/ProgressCircle/ProgressCircle.svelte +62 -0
- package/src/SideNavigation/Item.svelte +47 -0
- package/src/SideNavigation/Navigation.svelte +12 -0
- package/src/StatusLight/StatusLight.svelte +80 -0
- package/src/Stores/Notifications.svench.svx +78 -0
- package/src/Stores/banner.js +37 -0
- package/src/Stores/notifications.js +84 -0
- package/src/Table/ArrayRenderer.svelte +17 -0
- package/src/Table/AttachmentRenderer.svelte +67 -0
- package/src/Table/BoldRenderer.svelte +15 -0
- package/src/Table/BooleanRenderer.svelte +42 -0
- package/src/Table/CellRenderer.svelte +69 -0
- package/src/Table/CodeRenderer.svelte +14 -0
- package/src/Table/DateTimeRenderer.svelte +29 -0
- package/src/Table/InternalRenderer.svelte +24 -0
- package/src/Table/RelationshipRenderer.svelte +40 -0
- package/src/Table/SelectEditRenderer.svelte +28 -0
- package/src/Table/StringRenderer.svelte +14 -0
- package/src/Table/Table.svelte +635 -0
- package/src/Table/index.js +1 -0
- package/src/Tabs/Tab.svelte +88 -0
- package/src/Tabs/Tabs.svelte +125 -0
- package/src/Tags/Tag.svelte +43 -0
- package/src/Tags/Tags.svelte +7 -0
- package/src/Tooltip/Tooltip.svelte +34 -0
- package/src/Tooltip/TooltipWrapper.svelte +60 -0
- package/src/TreeView/Item.svelte +41 -0
- package/src/TreeView/Tree.svelte +16 -0
- package/src/Typography/Body.svelte +21 -0
- package/src/Typography/Code.svelte +10 -0
- package/src/Typography/Detail.svelte +19 -0
- package/src/Typography/Heading.svelte +17 -0
- package/src/bbui.css +96 -0
- package/src/context.js +3 -0
- package/src/helpers.js +134 -0
- package/src/index.js +97 -11
- package/src/spectrum-icons-rollup.js +31 -0
- package/src/spectrum-icons-vite.js +31 -0
- package/dist/bundle.css +0 -7
- package/dist/bundle.css.map +0 -20
- package/dist/index.js +0 -1100
- package/dist/index.mjs +0 -1086
- package/src/Branding/Budibase-logo.svelte +0 -68
- package/src/Branding/Logo.svelte +0 -16
- package/src/Branding/Logo.svench +0 -8
- package/src/Form/Button.svelte +0 -131
- package/src/Form/Button.svench +0 -49
- package/src/Form/Input.svench +0 -8
- package/src/Form/Select.svench +0 -8
- package/src/Form/TextArea.svench +0 -8
- package/src/Links/Home.svelte +0 -49
- package/src/Links/Home.svench +0 -19
- package/src/List/Items/DetailSummary.svelte +0 -66
- package/src/List/Items/DetailSummary.svench +0 -15
- package/src/actions/autoresize_textarea.js +0 -14
- package/src/icons/Contribution.svelte +0 -14
- 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>
|