@budibase/bbui 1.0.0 → 1.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.
- package/LICENSE +375 -0
- package/README.md +19 -17
- package/dist/bbui.es.js +2 -0
- package/dist/bbui.es.js.map +1 -0
- package/package.json +68 -26
- package/src/ActionButton/ActionButton.svelte +83 -0
- package/src/ActionGroup/ActionGroup.svelte +26 -0
- package/src/ActionMenu/ActionMenu.svelte +43 -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 +49 -0
- package/src/Badge/Badge.svelte +29 -0
- package/src/Button/Button.svelte +52 -0
- package/src/Button/Button.svench +138 -0
- package/src/ButtonGroup/ButtonGroup.svelte +19 -0
- package/src/ClearButton/ClearButton.svelte +19 -0
- package/src/ColorPicker/ColorPicker.svelte +292 -0
- package/src/DetailSummary/DetailSummary.svelte +83 -0
- package/src/Divider/Divider.svelte +25 -0
- package/src/Drawer/Drawer.svelte +85 -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 +44 -0
- package/src/Form/Core/Checkbox.svelte +67 -0
- package/src/Form/Core/Combobox.svelte +156 -0
- package/src/Form/Core/DatePicker.svelte +183 -0
- package/src/Form/Core/Dropzone.svelte +393 -0
- package/src/Form/Core/Multiselect.svelte +88 -0
- package/src/Form/Core/Picker.svelte +241 -0
- package/src/Form/Core/RadioGroup.svelte +47 -0
- package/src/Form/Core/Search.svelte +101 -0
- package/src/Form/Core/Select.svelte +81 -0
- package/src/Form/Core/Stepper.svelte +172 -0
- package/src/Form/Core/Switch.svelte +35 -0
- package/src/Form/Core/TextArea.svelte +60 -0
- package/src/Form/Core/TextField.svelte +104 -0
- package/src/Form/Core/index.js +12 -0
- package/src/Form/DatePicker.svelte +33 -0
- package/src/Form/Dropzone.svelte +34 -0
- package/src/Form/Field.svelte +41 -0
- package/src/Form/FieldLabel.svelte +27 -0
- package/src/Form/Input.svelte +38 -45
- package/src/Form/Multiselect.svelte +39 -0
- package/src/Form/RadioGroup.svelte +38 -0
- package/src/Form/Search.svelte +32 -0
- package/src/Form/Select.svelte +45 -43
- package/src/Form/Stepper.svelte +45 -0
- package/src/Form/TextArea.svelte +25 -44
- package/src/Form/Toggle.svelte +23 -0
- package/src/Icon/Icon.svelte +47 -0
- package/src/InlineAlert/InlineAlert.svelte +56 -0
- package/src/Label/Label.svelte +67 -0
- package/src/Layout/Layout.svelte +75 -0
- package/src/Layout/Page.svelte +25 -0
- package/src/Link/Link.svelte +22 -0
- package/src/List/Items/DetailSummary.svench +53 -0
- package/src/Menu/Item.svelte +45 -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 +133 -0
- package/src/Modal/Modal.svench +117 -0
- package/src/Modal/ModalContent.svelte +139 -0
- package/src/Modal/QuizModal.svelte +54 -0
- package/src/Notification/Notification.svelte +20 -0
- package/src/Notification/NotificationDisplay.svelte +36 -0
- package/src/Pagination/Pagination.svelte +57 -0
- package/src/Popover/Popover.svelte +52 -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 +45 -0
- package/src/SideNavigation/Navigation.svelte +12 -0
- package/src/StatusLight/StatusLight.svelte +41 -0
- package/src/Stores/Notifications.svench.svx +78 -0
- package/src/Stores/notifications.js +56 -0
- package/src/Table/ArrayRenderer.svelte +17 -0
- package/src/Table/AttachmentRenderer.svelte +55 -0
- package/src/Table/BooleanRenderer.svelte +42 -0
- package/src/Table/CellRenderer.svelte +37 -0
- package/src/Table/DateTimeRenderer.svelte +13 -0
- package/src/Table/InternalRenderer.svelte +28 -0
- package/src/Table/RelationshipRenderer.svelte +40 -0
- package/src/Table/SelectEditRenderer.svelte +16 -0
- package/src/Table/StringRenderer.svelte +13 -0
- package/src/Table/Table.svelte +506 -0
- package/src/Table/index.js +1 -0
- package/src/Tabs/Tab.svelte +66 -0
- package/src/Tabs/Tabs.svelte +105 -0
- package/src/Tags/Tag.svelte +43 -0
- package/src/Tags/Tags.svelte +7 -0
- package/src/Tooltip/Tooltip.svelte +24 -0
- package/src/TreeView/Item.svelte +41 -0
- package/src/TreeView/Tree.svelte +16 -0
- package/src/Typography/Body.svelte +19 -0
- package/src/Typography/Code.svelte +10 -0
- package/src/Typography/Detail.svelte +15 -0
- package/src/Typography/Heading.svelte +16 -0
- package/src/bbui.css +96 -0
- package/src/context.js +3 -0
- package/src/index.js +75 -10
- package/src/spectrum-icons-rollup.js +31 -0
- package/src/spectrum-icons-vite.js +31 -0
- package/src/utils/helpers.js +8 -0
- package/dist/bundle.css +0 -6
- package/dist/bundle.css.map +0 -18
- package/dist/index.js +0 -919
- package/dist/index.mjs +0 -906
- 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 -52
- package/src/Form/Button.svench +0 -26
- 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/actions/autoresize_textarea.js +0 -14
- package/src/icons/Contribution.svelte +0 -14
- package/src/index.svench.svx +0 -35
|
@@ -0,0 +1,393 @@
|
|
|
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 { generateID } from "../../utils/helpers"
|
|
7
|
+
import Icon from "../../Icon/Icon.svelte"
|
|
8
|
+
import Link from "../../Link/Link.svelte"
|
|
9
|
+
|
|
10
|
+
const BYTES_IN_KB = 1000
|
|
11
|
+
const BYTES_IN_MB = 1000000
|
|
12
|
+
|
|
13
|
+
export let value = []
|
|
14
|
+
export let id = null
|
|
15
|
+
export let disabled = false
|
|
16
|
+
export let fileSizeLimit = BYTES_IN_MB * 20
|
|
17
|
+
export let processFiles = null
|
|
18
|
+
export let handleFileTooLarge = null
|
|
19
|
+
export let gallery = true
|
|
20
|
+
export let error = null
|
|
21
|
+
|
|
22
|
+
const dispatch = createEventDispatcher()
|
|
23
|
+
const imageExtensions = [
|
|
24
|
+
"png",
|
|
25
|
+
"tiff",
|
|
26
|
+
"gif",
|
|
27
|
+
"raw",
|
|
28
|
+
"jpg",
|
|
29
|
+
"jpeg",
|
|
30
|
+
"svg",
|
|
31
|
+
"bmp",
|
|
32
|
+
"jfif",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
const fieldId = id || generateID()
|
|
36
|
+
let selectedImageIdx = 0
|
|
37
|
+
let fileDragged = false
|
|
38
|
+
let selectedUrl
|
|
39
|
+
$: selectedImage = value?.[selectedImageIdx] ?? null
|
|
40
|
+
$: fileCount = value?.length ?? 0
|
|
41
|
+
$: isImage =
|
|
42
|
+
imageExtensions.includes(selectedImage?.extension?.toLowerCase()) ||
|
|
43
|
+
selectedImage?.type?.startsWith("image")
|
|
44
|
+
|
|
45
|
+
$: {
|
|
46
|
+
if (selectedImage?.url) {
|
|
47
|
+
selectedUrl = selectedImage?.url
|
|
48
|
+
} else if (selectedImage) {
|
|
49
|
+
try {
|
|
50
|
+
let reader = new FileReader()
|
|
51
|
+
reader.readAsDataURL(selectedImage)
|
|
52
|
+
reader.onload = e => {
|
|
53
|
+
selectedUrl = e.target.result
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
selectedUrl = null
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function processFileList(fileList) {
|
|
62
|
+
if (
|
|
63
|
+
handleFileTooLarge &&
|
|
64
|
+
Array.from(fileList).some(file => file.size >= fileSizeLimit)
|
|
65
|
+
) {
|
|
66
|
+
handleFileTooLarge(fileSizeLimit, value)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (processFiles) {
|
|
70
|
+
const processedFiles = await processFiles(fileList)
|
|
71
|
+
const newValue = [...value, ...processedFiles]
|
|
72
|
+
dispatch("change", newValue)
|
|
73
|
+
selectedImageIdx = newValue.length - 1
|
|
74
|
+
} else {
|
|
75
|
+
dispatch("change", fileList)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function removeFile() {
|
|
80
|
+
dispatch(
|
|
81
|
+
"change",
|
|
82
|
+
value.filter((x, idx) => idx !== selectedImageIdx)
|
|
83
|
+
)
|
|
84
|
+
selectedImageIdx = 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function navigateLeft() {
|
|
88
|
+
selectedImageIdx -= 1
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function navigateRight() {
|
|
92
|
+
selectedImageIdx += 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handleFile(evt) {
|
|
96
|
+
processFileList(evt.target.files)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function handleDragOver(evt) {
|
|
100
|
+
evt.preventDefault()
|
|
101
|
+
fileDragged = true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function handleDragLeave(evt) {
|
|
105
|
+
evt.preventDefault()
|
|
106
|
+
fileDragged = false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function handleDrop(evt) {
|
|
110
|
+
evt.preventDefault()
|
|
111
|
+
processFileList(evt.dataTransfer.files)
|
|
112
|
+
fileDragged = false
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<div class="container">
|
|
117
|
+
{#if selectedImage}
|
|
118
|
+
{#if gallery}
|
|
119
|
+
<div class="gallery">
|
|
120
|
+
<div class="title">
|
|
121
|
+
<div class="filename">
|
|
122
|
+
{#if selectedUrl}
|
|
123
|
+
<Link href={selectedUrl}>{selectedImage.name}</Link>
|
|
124
|
+
{:else}
|
|
125
|
+
{selectedImage.name}
|
|
126
|
+
{/if}
|
|
127
|
+
</div>
|
|
128
|
+
{#if selectedImage.size}
|
|
129
|
+
<div class="filesize">
|
|
130
|
+
{#if selectedImage.size <= BYTES_IN_MB}
|
|
131
|
+
{`${selectedImage.size / BYTES_IN_KB} KB`}
|
|
132
|
+
{:else}{`${selectedImage.size / BYTES_IN_MB} MB`}{/if}
|
|
133
|
+
</div>
|
|
134
|
+
{/if}
|
|
135
|
+
{#if !disabled}
|
|
136
|
+
<div class="delete-button" on:click={removeFile}>
|
|
137
|
+
<Icon name="Close" />
|
|
138
|
+
</div>
|
|
139
|
+
{/if}
|
|
140
|
+
</div>
|
|
141
|
+
{#if isImage}
|
|
142
|
+
<img alt="preview" src={selectedUrl} />
|
|
143
|
+
{:else}
|
|
144
|
+
<div class="placeholder">
|
|
145
|
+
<div class="extension">{selectedImage.extension}</div>
|
|
146
|
+
<div>Preview not supported</div>
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
149
|
+
<div
|
|
150
|
+
class="nav left"
|
|
151
|
+
class:visible={selectedImageIdx > 0}
|
|
152
|
+
on:click={navigateLeft}
|
|
153
|
+
>
|
|
154
|
+
<Icon name="ChevronLeft" />
|
|
155
|
+
</div>
|
|
156
|
+
<div
|
|
157
|
+
class="nav right"
|
|
158
|
+
class:visible={selectedImageIdx < fileCount - 1}
|
|
159
|
+
on:click={navigateRight}
|
|
160
|
+
>
|
|
161
|
+
<Icon name="ChevronRight" />
|
|
162
|
+
</div>
|
|
163
|
+
<div class="footer">File {selectedImageIdx + 1} of {fileCount}</div>
|
|
164
|
+
</div>
|
|
165
|
+
{:else if value?.length}
|
|
166
|
+
{#each value as file}
|
|
167
|
+
<div class="gallery">
|
|
168
|
+
<div class="title">
|
|
169
|
+
<div class="filename">{file.name}</div>
|
|
170
|
+
{#if file.size}
|
|
171
|
+
<div class="filesize">
|
|
172
|
+
{#if file.size <= BYTES_IN_MB}
|
|
173
|
+
{`${file.size / BYTES_IN_KB} KB`}
|
|
174
|
+
{:else}{`${file.size / BYTES_IN_MB} MB`}{/if}
|
|
175
|
+
</div>
|
|
176
|
+
{/if}
|
|
177
|
+
{#if !disabled}
|
|
178
|
+
<div class="delete-button" on:click={removeFile}>
|
|
179
|
+
<Icon name="Close" />
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
{/each}
|
|
185
|
+
{/if}
|
|
186
|
+
{/if}
|
|
187
|
+
<div
|
|
188
|
+
class="spectrum-Dropzone"
|
|
189
|
+
class:is-invalid={!!error}
|
|
190
|
+
class:disabled
|
|
191
|
+
role="region"
|
|
192
|
+
tabindex="0"
|
|
193
|
+
on:dragover={handleDragOver}
|
|
194
|
+
on:dragleave={handleDragLeave}
|
|
195
|
+
on:dragenter={handleDragOver}
|
|
196
|
+
on:drop={handleDrop}
|
|
197
|
+
class:is-dragged={fileDragged}
|
|
198
|
+
>
|
|
199
|
+
<div class="spectrum-IllustratedMessage spectrum-IllustratedMessage--cta">
|
|
200
|
+
<input
|
|
201
|
+
id={fieldId}
|
|
202
|
+
{disabled}
|
|
203
|
+
type="file"
|
|
204
|
+
multiple
|
|
205
|
+
on:change={handleFile}
|
|
206
|
+
/>
|
|
207
|
+
<svg
|
|
208
|
+
class="spectrum-IllustratedMessage-illustration"
|
|
209
|
+
width="125"
|
|
210
|
+
height="60"
|
|
211
|
+
viewBox="0 0 199 97.7"
|
|
212
|
+
>
|
|
213
|
+
<defs>
|
|
214
|
+
<style>
|
|
215
|
+
.cls-1,
|
|
216
|
+
.cls-2 {
|
|
217
|
+
fill: none;
|
|
218
|
+
stroke-linecap: round;
|
|
219
|
+
stroke-linejoin: round;
|
|
220
|
+
}
|
|
221
|
+
.cls-1 {
|
|
222
|
+
stroke-width: 3px;
|
|
223
|
+
}
|
|
224
|
+
.cls-2 {
|
|
225
|
+
stroke-width: 2px;
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
228
|
+
</defs>
|
|
229
|
+
<path
|
|
230
|
+
class="cls-1"
|
|
231
|
+
d="M110.53,85.66,100.26,95.89a1.09,1.09,0,0,1-1.52,0L88.47,85.66"
|
|
232
|
+
/>
|
|
233
|
+
<line class="cls-1" x1="99.5" y1="95.5" x2="99.5" y2="58.5" />
|
|
234
|
+
<path class="cls-1" d="M105.5,73.5h19a2,2,0,0,0,2-2v-43" />
|
|
235
|
+
<path
|
|
236
|
+
class="cls-1"
|
|
237
|
+
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"
|
|
238
|
+
/>
|
|
239
|
+
<line class="cls-1" x1="105.5" y1="1.5" x2="126.5" y2="22.5" />
|
|
240
|
+
<path
|
|
241
|
+
class="cls-2"
|
|
242
|
+
d="M47.93,50.49a5,5,0,1,0-4.83-5A4.93,4.93,0,0,0,47.93,50.49Z"
|
|
243
|
+
/>
|
|
244
|
+
<path
|
|
245
|
+
class="cls-2"
|
|
246
|
+
d="M36.6,65.93,42.05,60A2.06,2.06,0,0,1,45,60l12.68,13.2"
|
|
247
|
+
/>
|
|
248
|
+
<path
|
|
249
|
+
class="cls-2"
|
|
250
|
+
d="M3.14,73.23,22.42,53.76a1.65,1.65,0,0,1,2.38,0l19.05,19.7"
|
|
251
|
+
/>
|
|
252
|
+
<path
|
|
253
|
+
class="cls-1"
|
|
254
|
+
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"
|
|
255
|
+
/>
|
|
256
|
+
<rect
|
|
257
|
+
class="cls-1"
|
|
258
|
+
x="1.5"
|
|
259
|
+
y="34.5"
|
|
260
|
+
width="58"
|
|
261
|
+
height="39"
|
|
262
|
+
rx="2"
|
|
263
|
+
ry="2"
|
|
264
|
+
/>
|
|
265
|
+
</svg>
|
|
266
|
+
<h2
|
|
267
|
+
class="spectrum-Heading spectrum-Heading--sizeL spectrum-Heading--light spectrum-IllustratedMessage-heading"
|
|
268
|
+
>
|
|
269
|
+
Drag and drop your file
|
|
270
|
+
</h2>
|
|
271
|
+
{#if !disabled}
|
|
272
|
+
<p
|
|
273
|
+
class="spectrum-Body spectrum-Body--sizeS spectrum-IllustratedMessage-description"
|
|
274
|
+
>
|
|
275
|
+
<label for={fieldId} class="spectrum-Link">
|
|
276
|
+
Select a file to upload
|
|
277
|
+
</label>
|
|
278
|
+
<br />
|
|
279
|
+
from your computer
|
|
280
|
+
</p>
|
|
281
|
+
{/if}
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<style>
|
|
287
|
+
.container {
|
|
288
|
+
--spectrum-dropzone-padding: var(--spectrum-global-dimension-size-400);
|
|
289
|
+
--spectrum-heading-l-text-size: var(
|
|
290
|
+
--spectrum-global-dimension-font-size-400
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.gallery,
|
|
295
|
+
.spectrum-Dropzone {
|
|
296
|
+
user-select: none;
|
|
297
|
+
}
|
|
298
|
+
.spectrum-Dropzone.is-invalid {
|
|
299
|
+
border-color: var(--spectrum-global-color-red-400);
|
|
300
|
+
}
|
|
301
|
+
input[type="file"] {
|
|
302
|
+
display: none;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.gallery {
|
|
306
|
+
display: flex;
|
|
307
|
+
flex-direction: column;
|
|
308
|
+
justify-content: flex-start;
|
|
309
|
+
align-items: stretch;
|
|
310
|
+
background-color: var(--spectrum-global-color-gray-50);
|
|
311
|
+
color: var(--spectrum-alias-text-color);
|
|
312
|
+
font-size: var(--spectrum-alias-item-text-size-m);
|
|
313
|
+
box-sizing: border-box;
|
|
314
|
+
border: var(--spectrum-alias-border-size-thin)
|
|
315
|
+
var(--spectrum-alias-border-color) solid;
|
|
316
|
+
border-radius: var(--spectrum-alias-border-radius-regular);
|
|
317
|
+
padding: 10px;
|
|
318
|
+
margin-bottom: 10px;
|
|
319
|
+
position: relative;
|
|
320
|
+
}
|
|
321
|
+
.placeholder,
|
|
322
|
+
img {
|
|
323
|
+
height: 120px;
|
|
324
|
+
max-width: 100%;
|
|
325
|
+
object-fit: contain;
|
|
326
|
+
margin: 20px 30px;
|
|
327
|
+
}
|
|
328
|
+
.title {
|
|
329
|
+
display: flex;
|
|
330
|
+
flex-direction: row;
|
|
331
|
+
justify-content: flex-start;
|
|
332
|
+
align-items: center;
|
|
333
|
+
}
|
|
334
|
+
.filename {
|
|
335
|
+
flex: 1 1 auto;
|
|
336
|
+
overflow: hidden;
|
|
337
|
+
text-overflow: ellipsis;
|
|
338
|
+
white-space: nowrap;
|
|
339
|
+
width: 0;
|
|
340
|
+
margin-right: 10px;
|
|
341
|
+
}
|
|
342
|
+
.placeholder {
|
|
343
|
+
display: flex;
|
|
344
|
+
flex-direction: column;
|
|
345
|
+
justify-content: center;
|
|
346
|
+
align-items: center;
|
|
347
|
+
}
|
|
348
|
+
.extension {
|
|
349
|
+
color: var(--spectrum-global-color-gray-600);
|
|
350
|
+
text-transform: uppercase;
|
|
351
|
+
font-weight: 600;
|
|
352
|
+
margin-bottom: 5px;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.nav {
|
|
356
|
+
padding: var(--spacing-xs);
|
|
357
|
+
position: absolute;
|
|
358
|
+
top: 50%;
|
|
359
|
+
border-radius: 5px;
|
|
360
|
+
display: none;
|
|
361
|
+
transition: all 0.3s;
|
|
362
|
+
}
|
|
363
|
+
.nav.visible {
|
|
364
|
+
display: block;
|
|
365
|
+
}
|
|
366
|
+
.nav:hover {
|
|
367
|
+
cursor: pointer;
|
|
368
|
+
color: var(--blue);
|
|
369
|
+
}
|
|
370
|
+
.left {
|
|
371
|
+
left: 5px;
|
|
372
|
+
}
|
|
373
|
+
.right {
|
|
374
|
+
right: 5px;
|
|
375
|
+
}
|
|
376
|
+
.delete-button {
|
|
377
|
+
transition: all 0.3s;
|
|
378
|
+
margin-left: 10px;
|
|
379
|
+
display: flex;
|
|
380
|
+
}
|
|
381
|
+
.delete-button:hover {
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
color: var(--red);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.spectrum-Dropzone.disabled {
|
|
387
|
+
pointer-events: none;
|
|
388
|
+
background-color: var(--spectrum-global-color-gray-200);
|
|
389
|
+
}
|
|
390
|
+
.disabled .spectrum-Heading--sizeL {
|
|
391
|
+
color: var(--spectrum-alias-text-color-disabled);
|
|
392
|
+
}
|
|
393
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Picker from "./Picker.svelte"
|
|
3
|
+
import { createEventDispatcher } from "svelte"
|
|
4
|
+
|
|
5
|
+
export let value = []
|
|
6
|
+
export let id = null
|
|
7
|
+
export let placeholder = null
|
|
8
|
+
export let disabled = false
|
|
9
|
+
export let error = null
|
|
10
|
+
export let options = []
|
|
11
|
+
export let getOptionLabel = option => option
|
|
12
|
+
export let getOptionValue = option => option
|
|
13
|
+
export let readonly = false
|
|
14
|
+
export let autocomplete = false
|
|
15
|
+
export let sort = false
|
|
16
|
+
|
|
17
|
+
const dispatch = createEventDispatcher()
|
|
18
|
+
$: selectedLookupMap = getSelectedLookupMap(value)
|
|
19
|
+
$: optionLookupMap = getOptionLookupMap(options)
|
|
20
|
+
$: fieldText = getFieldText(value, optionLookupMap, placeholder)
|
|
21
|
+
$: isOptionSelected = optionValue => selectedLookupMap[optionValue] === true
|
|
22
|
+
$: toggleOption = makeToggleOption(selectedLookupMap, value)
|
|
23
|
+
|
|
24
|
+
const getFieldText = (value, map, placeholder) => {
|
|
25
|
+
if (value?.length) {
|
|
26
|
+
if (!map) {
|
|
27
|
+
return ""
|
|
28
|
+
}
|
|
29
|
+
const vals = value.map(option => map[option] || option).join(", ")
|
|
30
|
+
return `(${value.length}) ${vals}`
|
|
31
|
+
} else {
|
|
32
|
+
return placeholder || "Choose some options"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const getSelectedLookupMap = value => {
|
|
37
|
+
let map = {}
|
|
38
|
+
if (value?.length) {
|
|
39
|
+
value.forEach(option => {
|
|
40
|
+
if (option) {
|
|
41
|
+
map[option] = true
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return map
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const getOptionLookupMap = options => {
|
|
49
|
+
let map = null
|
|
50
|
+
if (options?.length) {
|
|
51
|
+
map = {}
|
|
52
|
+
options.forEach((option, idx) => {
|
|
53
|
+
const optionValue = getOptionValue(option, idx)
|
|
54
|
+
if (optionValue != null) {
|
|
55
|
+
map[optionValue] = getOptionLabel(option, idx) || ""
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
return map
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const makeToggleOption = (map, value) => {
|
|
63
|
+
return optionValue => {
|
|
64
|
+
if (map[optionValue]) {
|
|
65
|
+
const filtered = value.filter(option => option !== optionValue)
|
|
66
|
+
dispatch("change", filtered)
|
|
67
|
+
} else {
|
|
68
|
+
dispatch("change", [...value, optionValue])
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<Picker
|
|
75
|
+
{id}
|
|
76
|
+
{error}
|
|
77
|
+
{disabled}
|
|
78
|
+
{readonly}
|
|
79
|
+
{fieldText}
|
|
80
|
+
{options}
|
|
81
|
+
isPlaceholder={!value?.length}
|
|
82
|
+
{autocomplete}
|
|
83
|
+
{isOptionSelected}
|
|
84
|
+
{getOptionLabel}
|
|
85
|
+
{getOptionValue}
|
|
86
|
+
onSelectOption={toggleOption}
|
|
87
|
+
{sort}
|
|
88
|
+
/>
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import "@spectrum-css/picker/dist/index-vars.css"
|
|
3
|
+
import "@spectrum-css/popover/dist/index-vars.css"
|
|
4
|
+
import "@spectrum-css/menu/dist/index-vars.css"
|
|
5
|
+
import { fly } from "svelte/transition"
|
|
6
|
+
import { createEventDispatcher } from "svelte"
|
|
7
|
+
import clickOutside from "../../Actions/click_outside"
|
|
8
|
+
import Search from "./Search.svelte"
|
|
9
|
+
|
|
10
|
+
export let id = null
|
|
11
|
+
export let disabled = false
|
|
12
|
+
export let error = null
|
|
13
|
+
export let fieldText = ""
|
|
14
|
+
export let fieldIcon = ""
|
|
15
|
+
export let isPlaceholder = false
|
|
16
|
+
export let placeholderOption = null
|
|
17
|
+
export let options = []
|
|
18
|
+
export let isOptionSelected = () => false
|
|
19
|
+
export let onSelectOption = () => {}
|
|
20
|
+
export let getOptionLabel = option => option
|
|
21
|
+
export let getOptionValue = option => option
|
|
22
|
+
export let getOptionIcon = () => null
|
|
23
|
+
export let open = false
|
|
24
|
+
export let readonly = false
|
|
25
|
+
export let quiet = false
|
|
26
|
+
export let autoWidth = false
|
|
27
|
+
export let autocomplete = false
|
|
28
|
+
export let sort = false
|
|
29
|
+
|
|
30
|
+
const dispatch = createEventDispatcher()
|
|
31
|
+
let searchTerm = null
|
|
32
|
+
|
|
33
|
+
$: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
|
|
34
|
+
$: filteredOptions = getFilteredOptions(
|
|
35
|
+
sortedOptions,
|
|
36
|
+
searchTerm,
|
|
37
|
+
getOptionLabel
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const onClick = () => {
|
|
41
|
+
dispatch("click")
|
|
42
|
+
if (readonly) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
searchTerm = null
|
|
46
|
+
open = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const getSortedOptions = (options, getLabel, sort) => {
|
|
50
|
+
if (!options?.length || !Array.isArray(options)) {
|
|
51
|
+
return []
|
|
52
|
+
}
|
|
53
|
+
if (!sort) {
|
|
54
|
+
return options
|
|
55
|
+
}
|
|
56
|
+
return options.sort((a, b) => {
|
|
57
|
+
const labelA = getLabel(a)
|
|
58
|
+
const labelB = getLabel(b)
|
|
59
|
+
return labelA > labelB ? 1 : -1
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const getFilteredOptions = (options, term, getLabel) => {
|
|
64
|
+
if (autocomplete && term) {
|
|
65
|
+
const lowerCaseTerm = term.toLowerCase()
|
|
66
|
+
return options.filter(option =>
|
|
67
|
+
getLabel(option)?.toLowerCase().includes(lowerCaseTerm)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
return options
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<button
|
|
75
|
+
{id}
|
|
76
|
+
class="spectrum-Picker spectrum-Picker--sizeM"
|
|
77
|
+
class:spectrum-Picker--quiet={quiet}
|
|
78
|
+
{disabled}
|
|
79
|
+
class:is-invalid={!!error}
|
|
80
|
+
class:is-open={open}
|
|
81
|
+
aria-haspopup="listbox"
|
|
82
|
+
on:mousedown={onClick}
|
|
83
|
+
>
|
|
84
|
+
{#if fieldIcon}
|
|
85
|
+
<span class="icon-Placeholder-Padding">
|
|
86
|
+
<img src={fieldIcon} alt="icon" width="20" height="15" />
|
|
87
|
+
</span>
|
|
88
|
+
{/if}
|
|
89
|
+
|
|
90
|
+
<span
|
|
91
|
+
class="spectrum-Picker-label"
|
|
92
|
+
class:is-placeholder={isPlaceholder}
|
|
93
|
+
class:auto-width={autoWidth}
|
|
94
|
+
>
|
|
95
|
+
{fieldText}
|
|
96
|
+
</span>
|
|
97
|
+
{#if error}
|
|
98
|
+
<svg
|
|
99
|
+
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
|
|
100
|
+
focusable="false"
|
|
101
|
+
aria-hidden="true"
|
|
102
|
+
aria-label="Folder"
|
|
103
|
+
>
|
|
104
|
+
<use xlink:href="#spectrum-icon-18-Alert" />
|
|
105
|
+
</svg>
|
|
106
|
+
{/if}
|
|
107
|
+
<svg
|
|
108
|
+
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
|
109
|
+
focusable="false"
|
|
110
|
+
aria-hidden="true"
|
|
111
|
+
>
|
|
112
|
+
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
|
113
|
+
</svg>
|
|
114
|
+
</button>
|
|
115
|
+
{#if open}
|
|
116
|
+
<div
|
|
117
|
+
use:clickOutside={() => (open = false)}
|
|
118
|
+
transition:fly|local={{ y: -20, duration: 200 }}
|
|
119
|
+
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
|
120
|
+
class:auto-width={autoWidth}
|
|
121
|
+
>
|
|
122
|
+
{#if autocomplete}
|
|
123
|
+
<Search
|
|
124
|
+
value={searchTerm}
|
|
125
|
+
on:change={event => (searchTerm = event.detail)}
|
|
126
|
+
{disabled}
|
|
127
|
+
placeholder="Search"
|
|
128
|
+
/>
|
|
129
|
+
{/if}
|
|
130
|
+
<ul class="spectrum-Menu" role="listbox">
|
|
131
|
+
{#if placeholderOption}
|
|
132
|
+
<li
|
|
133
|
+
class="spectrum-Menu-item placeholder"
|
|
134
|
+
class:is-selected={isPlaceholder}
|
|
135
|
+
role="option"
|
|
136
|
+
aria-selected="true"
|
|
137
|
+
tabindex="0"
|
|
138
|
+
on:click={() => onSelectOption(null)}
|
|
139
|
+
>
|
|
140
|
+
<span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
|
|
141
|
+
<svg
|
|
142
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
143
|
+
focusable="false"
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
>
|
|
146
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
147
|
+
</svg>
|
|
148
|
+
</li>
|
|
149
|
+
{/if}
|
|
150
|
+
{#if filteredOptions.length}
|
|
151
|
+
{#each filteredOptions as option, idx}
|
|
152
|
+
<li
|
|
153
|
+
class="spectrum-Menu-item"
|
|
154
|
+
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
|
155
|
+
role="option"
|
|
156
|
+
aria-selected="true"
|
|
157
|
+
tabindex="0"
|
|
158
|
+
on:click={() => onSelectOption(getOptionValue(option, idx))}
|
|
159
|
+
>
|
|
160
|
+
{#if getOptionIcon(option, idx)}
|
|
161
|
+
<span class="icon-Padding">
|
|
162
|
+
<img
|
|
163
|
+
src={getOptionIcon(option, idx)}
|
|
164
|
+
alt="icon"
|
|
165
|
+
width="20"
|
|
166
|
+
height="15"
|
|
167
|
+
/>
|
|
168
|
+
</span>
|
|
169
|
+
{/if}
|
|
170
|
+
<span class="spectrum-Menu-itemLabel">
|
|
171
|
+
{getOptionLabel(option, idx)}
|
|
172
|
+
</span>
|
|
173
|
+
<svg
|
|
174
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
175
|
+
focusable="false"
|
|
176
|
+
aria-hidden="true"
|
|
177
|
+
>
|
|
178
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
179
|
+
</svg>
|
|
180
|
+
</li>
|
|
181
|
+
{/each}
|
|
182
|
+
{/if}
|
|
183
|
+
</ul>
|
|
184
|
+
</div>
|
|
185
|
+
{/if}
|
|
186
|
+
|
|
187
|
+
<style>
|
|
188
|
+
.spectrum-Popover {
|
|
189
|
+
max-height: 240px;
|
|
190
|
+
z-index: 999;
|
|
191
|
+
top: 100%;
|
|
192
|
+
}
|
|
193
|
+
.spectrum-Popover:not(.auto-width) {
|
|
194
|
+
width: 100%;
|
|
195
|
+
}
|
|
196
|
+
.spectrum-Popover.auto-width :global(.spectrum-Menu-itemLabel) {
|
|
197
|
+
white-space: nowrap;
|
|
198
|
+
}
|
|
199
|
+
.spectrum-Picker {
|
|
200
|
+
width: 100%;
|
|
201
|
+
}
|
|
202
|
+
.spectrum-Picker-label:not(.auto-width) {
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
text-overflow: ellipsis;
|
|
205
|
+
white-space: nowrap;
|
|
206
|
+
width: 0;
|
|
207
|
+
}
|
|
208
|
+
.placeholder {
|
|
209
|
+
font-style: italic;
|
|
210
|
+
}
|
|
211
|
+
.spectrum-Picker-label.auto-width.is-placeholder {
|
|
212
|
+
padding-right: 2px;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.icon-Padding {
|
|
216
|
+
padding-right: 10px;
|
|
217
|
+
}
|
|
218
|
+
.icon-Placeholder-Padding {
|
|
219
|
+
padding-top: 5px;
|
|
220
|
+
padding-right: 10px;
|
|
221
|
+
}
|
|
222
|
+
.spectrum-Popover :global(.spectrum-Search) {
|
|
223
|
+
margin-top: -1px;
|
|
224
|
+
margin-left: -1px;
|
|
225
|
+
width: calc(100% + 2px);
|
|
226
|
+
}
|
|
227
|
+
.spectrum-Popover :global(.spectrum-Search input) {
|
|
228
|
+
height: auto;
|
|
229
|
+
border-bottom-left-radius: 0;
|
|
230
|
+
border-bottom-right-radius: 0;
|
|
231
|
+
padding-top: var(--spectrum-global-dimension-size-100);
|
|
232
|
+
padding-bottom: var(--spectrum-global-dimension-size-100);
|
|
233
|
+
}
|
|
234
|
+
.spectrum-Popover :global(.spectrum-Search .spectrum-ClearButton) {
|
|
235
|
+
right: 1px;
|
|
236
|
+
top: 2px;
|
|
237
|
+
}
|
|
238
|
+
.spectrum-Popover :global(.spectrum-Search .spectrum-Textfield-icon) {
|
|
239
|
+
top: 9px;
|
|
240
|
+
}
|
|
241
|
+
</style>
|