@budibase/bbui 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +375 -0
- package/README.md +42 -0
- package/dist/bbui.es.js +8 -0
- package/dist/bbui.es.js.map +1 -0
- package/package.json +94 -0
- package/src/Accordion/Accordion.svelte +58 -0
- package/src/ActionButton/ActionButton.svelte +132 -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 +76 -0
- package/src/Actions/position_dropdown.js +116 -0
- package/src/Avatar/Avatar.svelte +63 -0
- package/src/Badge/Badge.svelte +42 -0
- package/src/Banner/Banner.svelte +72 -0
- package/src/Banner/BannerDisplay.svelte +41 -0
- package/src/Button/Button.svelte +118 -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 +304 -0
- package/src/DetailSummary/DetailSummary.svelte +92 -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 +45 -0
- package/src/FancyForm/FancyButton.svelte +30 -0
- package/src/FancyForm/FancyButtonRadio.svelte +70 -0
- package/src/FancyForm/FancyCheckbox.svelte +53 -0
- package/src/FancyForm/FancyField.svelte +126 -0
- package/src/FancyForm/FancyFieldLabel.svelte +25 -0
- package/src/FancyForm/FancyForm.svelte +40 -0
- package/src/FancyForm/FancyInput.svelte +109 -0
- package/src/FancyForm/FancySelect.svelte +147 -0
- package/src/FancyForm/index.js +6 -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 +265 -0
- package/src/Form/Core/Dropzone.svelte +482 -0
- package/src/Form/Core/EnvDropdown.svelte +276 -0
- package/src/Form/Core/File.svelte +115 -0
- package/src/Form/Core/InputDropdown.svelte +235 -0
- package/src/Form/Core/Multiselect.svelte +99 -0
- package/src/Form/Core/Picker.svelte +314 -0
- package/src/Form/Core/PickerDropdown.svelte +431 -0
- package/src/Form/Core/RadioGroup.svelte +65 -0
- package/src/Form/Core/RichTextField.svelte +42 -0
- package/src/Form/Core/Search.svelte +105 -0
- package/src/Form/Core/Select.svelte +90 -0
- package/src/Form/Core/Slider.svelte +86 -0
- package/src/Form/Core/Stepper.svelte +172 -0
- package/src/Form/Core/Switch.svelte +35 -0
- package/src/Form/Core/TextArea.svelte +78 -0
- package/src/Form/Core/TextField.svelte +115 -0
- package/src/Form/Core/index.js +16 -0
- package/src/Form/DatePicker.svelte +46 -0
- package/src/Form/Dropzone.svelte +44 -0
- package/src/Form/EnvDropdown.svelte +50 -0
- package/src/Form/Field.svelte +42 -0
- package/src/Form/FieldLabel.svelte +32 -0
- package/src/Form/File.svelte +37 -0
- package/src/Form/Input.svelte +45 -0
- package/src/Form/InputDropdown.svelte +53 -0
- package/src/Form/Multiselect.svelte +49 -0
- package/src/Form/PickerDropdown.svelte +131 -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 +67 -0
- package/src/Form/Slider.svelte +24 -0
- package/src/Form/Stepper.svelte +45 -0
- package/src/Form/TextArea.svelte +34 -0
- package/src/Form/Toggle.svelte +22 -0
- package/src/Icon/Icon.svelte +94 -0
- package/src/Icon/IconAvatar.svelte +61 -0
- package/src/IconPicker/IconPicker.svelte +184 -0
- package/src/IconSideNav/IconSideNav.svelte +14 -0
- package/src/IconSideNav/IconSideNavItem.svelte +56 -0
- package/src/InlineAlert/InlineAlert.svelte +63 -0
- package/src/Input/CopyInput.svelte +59 -0
- package/src/Label/Label.svelte +31 -0
- package/src/Layout/Layout.svelte +94 -0
- package/src/Layout/Page.svelte +101 -0
- package/src/Link/Link.svelte +27 -0
- package/src/List/List.svelte +28 -0
- package/src/List/ListItem.svelte +93 -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 +94 -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 +37 -0
- package/src/Modal/Modal.svelte +178 -0
- package/src/Modal/Modal.svench +116 -0
- package/src/Modal/ModalContent.svelte +190 -0
- package/src/Modal/QuizModal.svelte +53 -0
- package/src/Notification/Notification.svelte +74 -0
- package/src/Notification/NotificationDisplay.svelte +43 -0
- package/src/Pagination/Pagination.svelte +57 -0
- package/src/Popover/Popover.svelte +95 -0
- package/src/Popover/Popover.svench +120 -0
- package/src/ProgressBar/ProgressBar.svelte +67 -0
- package/src/ProgressCircle/ProgressCircle.svelte +62 -0
- package/src/SideNavigation/Item.svelte +59 -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 +65 -0
- package/src/Stores/notifications.js +84 -0
- package/src/Table/ArrayRenderer.svelte +18 -0
- package/src/Table/AttachmentRenderer.svelte +73 -0
- package/src/Table/BoldRenderer.svelte +15 -0
- package/src/Table/BooleanRenderer.svelte +42 -0
- package/src/Table/CellRenderer.svelte +70 -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 +35 -0
- package/src/Table/SelectEditRenderer.svelte +28 -0
- package/src/Table/StringRenderer.svelte +22 -0
- package/src/Table/Table.svelte +657 -0
- package/src/Table/index.js +1 -0
- package/src/Tabs/Tab.svelte +88 -0
- package/src/Tabs/Tabs.svelte +130 -0
- package/src/Tags/Tag.svelte +43 -0
- package/src/Tags/Tags.svelte +17 -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 +22 -0
- package/src/Typography/Code.svelte +10 -0
- package/src/Typography/Detail.svelte +19 -0
- package/src/Typography/Heading.svelte +24 -0
- package/src/bbui.css +118 -0
- package/src/context.js +4 -0
- package/src/helpers.js +116 -0
- package/src/index.js +109 -0
- package/src/spectrum-icons-rollup.js +31 -0
- package/src/spectrum-icons-vite.js +31 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import "@spectrum-css/textfield/dist/index-vars.css"
|
|
3
|
+
import { createEventDispatcher, onMount } from "svelte"
|
|
4
|
+
import clickOutside from "../../Actions/click_outside"
|
|
5
|
+
import Divider from "../../Divider/Divider.svelte"
|
|
6
|
+
|
|
7
|
+
export let value = null
|
|
8
|
+
export let placeholder = null
|
|
9
|
+
export let type = "text"
|
|
10
|
+
export let disabled = false
|
|
11
|
+
export let id = null
|
|
12
|
+
export let readonly = false
|
|
13
|
+
export let updateOnChange = true
|
|
14
|
+
export let align
|
|
15
|
+
export let autofocus = false
|
|
16
|
+
export let variables
|
|
17
|
+
export let showModal
|
|
18
|
+
export let environmentVariablesEnabled
|
|
19
|
+
export let handleUpgradePanel
|
|
20
|
+
const dispatch = createEventDispatcher()
|
|
21
|
+
|
|
22
|
+
let field
|
|
23
|
+
let focus = false
|
|
24
|
+
let iconFocused = false
|
|
25
|
+
let open = false
|
|
26
|
+
|
|
27
|
+
//eslint-disable-next-line
|
|
28
|
+
const STRIP_NAME_REGEX = /(\w+?)(?=\ })/g
|
|
29
|
+
|
|
30
|
+
// Strips the name out of the value which is {{ env.Variable }} resulting in an array like ["Variable"]
|
|
31
|
+
$: hbsValue = String(value)?.match(STRIP_NAME_REGEX) || []
|
|
32
|
+
|
|
33
|
+
const updateValue = newValue => {
|
|
34
|
+
if (readonly) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if (type === "number") {
|
|
38
|
+
const float = parseFloat(newValue)
|
|
39
|
+
newValue = isNaN(float) ? null : float
|
|
40
|
+
}
|
|
41
|
+
dispatch("change", newValue)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const onFocus = () => {
|
|
45
|
+
if (readonly) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
focus = true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const onBlur = event => {
|
|
52
|
+
if (readonly) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
focus = false
|
|
56
|
+
updateValue(event.target.value)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const onInput = event => {
|
|
60
|
+
if (readonly || !updateOnChange) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
updateValue(event.target.value)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const handleOutsideClick = event => {
|
|
67
|
+
if (open) {
|
|
68
|
+
event.stopPropagation()
|
|
69
|
+
open = false
|
|
70
|
+
focus = false
|
|
71
|
+
iconFocused = false
|
|
72
|
+
dispatch("closed")
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleVarSelect = variable => {
|
|
77
|
+
open = false
|
|
78
|
+
focus = false
|
|
79
|
+
iconFocused = false
|
|
80
|
+
updateValue(`{{ env.${variable} }}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onMount(() => {
|
|
84
|
+
focus = autofocus
|
|
85
|
+
if (focus) field.focus()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
function removeVariable() {
|
|
89
|
+
updateValue("")
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function openPopover() {
|
|
93
|
+
open = true
|
|
94
|
+
focus = true
|
|
95
|
+
iconFocused = true
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<div class="spectrum-InputGroup">
|
|
100
|
+
<div
|
|
101
|
+
class:is-disabled={disabled || hbsValue.length}
|
|
102
|
+
class:is-focused={focus}
|
|
103
|
+
class="spectrum-Textfield"
|
|
104
|
+
>
|
|
105
|
+
<svg
|
|
106
|
+
class:close-color={hbsValue.length}
|
|
107
|
+
class:focused={iconFocused}
|
|
108
|
+
class="hoverable icon-position spectrum-Icon spectrum-Icon--sizeS spectrum-Textfield-validationIcon"
|
|
109
|
+
focusable="false"
|
|
110
|
+
aria-hidden="true"
|
|
111
|
+
on:click={() => {
|
|
112
|
+
hbsValue.length ? removeVariable() : openPopover()
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
<use
|
|
116
|
+
xlink:href={`#spectrum-icon-18-${!hbsValue.length ? "Key" : "Close"}`}
|
|
117
|
+
/>
|
|
118
|
+
</svg>
|
|
119
|
+
|
|
120
|
+
<input
|
|
121
|
+
bind:this={field}
|
|
122
|
+
disabled={hbsValue.length || disabled}
|
|
123
|
+
{readonly}
|
|
124
|
+
{id}
|
|
125
|
+
value={hbsValue.length ? `{{ ${hbsValue[0]} }}` : value}
|
|
126
|
+
placeholder={placeholder || ""}
|
|
127
|
+
on:click
|
|
128
|
+
on:blur
|
|
129
|
+
on:focus
|
|
130
|
+
on:input
|
|
131
|
+
on:keyup
|
|
132
|
+
on:blur={onBlur}
|
|
133
|
+
on:focus={onFocus}
|
|
134
|
+
on:input={onInput}
|
|
135
|
+
type={hbsValue.length ? "text" : type}
|
|
136
|
+
style={align ? `text-align: ${align};` : ""}
|
|
137
|
+
class="spectrum-Textfield-input"
|
|
138
|
+
inputmode={type === "number" ? "decimal" : "text"}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
{#if open}
|
|
142
|
+
<div
|
|
143
|
+
use:clickOutside={handleOutsideClick}
|
|
144
|
+
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
|
145
|
+
>
|
|
146
|
+
<ul class="spectrum-Menu" role="listbox">
|
|
147
|
+
{#if !environmentVariablesEnabled}
|
|
148
|
+
<div class="no-variables-text primary-text">
|
|
149
|
+
Upgrade your plan to get environment variables
|
|
150
|
+
</div>
|
|
151
|
+
{:else if variables.length}
|
|
152
|
+
<div style="max-height: 100px">
|
|
153
|
+
{#each variables as variable, idx}
|
|
154
|
+
<li
|
|
155
|
+
class="spectrum-Menu-item"
|
|
156
|
+
role="option"
|
|
157
|
+
aria-selected="true"
|
|
158
|
+
tabindex="0"
|
|
159
|
+
on:click={() => handleVarSelect(variable.name)}
|
|
160
|
+
>
|
|
161
|
+
<span class="spectrum-Menu-itemLabel">
|
|
162
|
+
<div class="primary-text">
|
|
163
|
+
{variable.name}
|
|
164
|
+
<span />
|
|
165
|
+
</div>
|
|
166
|
+
<svg
|
|
167
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
168
|
+
focusable="false"
|
|
169
|
+
aria-hidden="true"
|
|
170
|
+
>
|
|
171
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
172
|
+
</svg>
|
|
173
|
+
</span>
|
|
174
|
+
</li>
|
|
175
|
+
{/each}
|
|
176
|
+
</div>
|
|
177
|
+
{:else}
|
|
178
|
+
<div class="no-variables-text primary-text">
|
|
179
|
+
You don't have any environment variables yet
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
182
|
+
</ul>
|
|
183
|
+
<Divider noMargin />
|
|
184
|
+
{#if environmentVariablesEnabled}
|
|
185
|
+
<div on:click={() => showModal()} class="add-variable">
|
|
186
|
+
<svg
|
|
187
|
+
class="spectrum-Icon spectrum-Icon--sizeS "
|
|
188
|
+
focusable="false"
|
|
189
|
+
aria-hidden="true"
|
|
190
|
+
>
|
|
191
|
+
<use xlink:href="#spectrum-icon-18-Add" />
|
|
192
|
+
</svg>
|
|
193
|
+
<div class="primary-text">Add Variable</div>
|
|
194
|
+
</div>
|
|
195
|
+
{:else}
|
|
196
|
+
<div on:click={() => handleUpgradePanel()} class="add-variable">
|
|
197
|
+
<svg
|
|
198
|
+
class="spectrum-Icon spectrum-Icon--sizeS "
|
|
199
|
+
focusable="false"
|
|
200
|
+
aria-hidden="true"
|
|
201
|
+
>
|
|
202
|
+
<use xlink:href="#spectrum-icon-18-ArrowUp" />
|
|
203
|
+
</svg>
|
|
204
|
+
<div class="primary-text">Upgrade plan</div>
|
|
205
|
+
</div>
|
|
206
|
+
{/if}
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<style>
|
|
212
|
+
.spectrum-Textfield {
|
|
213
|
+
width: 100%;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.icon-position {
|
|
217
|
+
position: absolute;
|
|
218
|
+
top: 25%;
|
|
219
|
+
right: 2%;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.hoverable:hover {
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
color: var(--spectrum-global-color-blue-400);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.primary-text {
|
|
228
|
+
white-space: nowrap;
|
|
229
|
+
overflow: hidden;
|
|
230
|
+
text-overflow: ellipsis;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.spectrum-InputGroup {
|
|
234
|
+
min-width: 0;
|
|
235
|
+
width: 100%;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.spectrum-Popover {
|
|
239
|
+
max-height: 240px;
|
|
240
|
+
z-index: 999;
|
|
241
|
+
top: 100%;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.spectrum-Popover.spectrum-Popover--bottom.spectrum-Picker-popover.is-open {
|
|
245
|
+
width: 100%;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.no-variables-text {
|
|
249
|
+
padding: var(--spacing-m);
|
|
250
|
+
color: var(--spectrum-global-color-gray-600);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.add-variable {
|
|
254
|
+
display: flex;
|
|
255
|
+
padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
|
|
256
|
+
align-items: center;
|
|
257
|
+
gap: var(--spacing-s);
|
|
258
|
+
cursor: pointer;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.focused {
|
|
262
|
+
color: var(--spectrum-global-color-blue-400);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.add-variable:hover {
|
|
266
|
+
background: var(--grey-1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.close-color {
|
|
270
|
+
color: var(--spectrum-global-color-gray-900) !important;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.close-color:hover {
|
|
274
|
+
color: var(--spectrum-global-color-blue-400) !important;
|
|
275
|
+
}
|
|
276
|
+
</style>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ActionButton from "../../ActionButton/ActionButton.svelte"
|
|
3
|
+
import { uuid } from "../../helpers"
|
|
4
|
+
import Icon from "../../Icon/Icon.svelte"
|
|
5
|
+
import { createEventDispatcher } from "svelte"
|
|
6
|
+
|
|
7
|
+
export let value = null
|
|
8
|
+
export let title = "Upload file"
|
|
9
|
+
export let disabled = false
|
|
10
|
+
export let allowClear = null
|
|
11
|
+
export let extensions = null
|
|
12
|
+
export let handleFileTooLarge = null
|
|
13
|
+
export let fileSizeLimit = BYTES_IN_MB * 20
|
|
14
|
+
export let id = null
|
|
15
|
+
export let previewUrl = null
|
|
16
|
+
|
|
17
|
+
const fieldId = id || uuid()
|
|
18
|
+
const BYTES_IN_KB = 1000
|
|
19
|
+
const BYTES_IN_MB = 1000000
|
|
20
|
+
|
|
21
|
+
const dispatch = createEventDispatcher()
|
|
22
|
+
|
|
23
|
+
let fileInput
|
|
24
|
+
|
|
25
|
+
$: inputAccept = Array.isArray(extensions) ? extensions.join(",") : "*"
|
|
26
|
+
|
|
27
|
+
async function processFile(targetFile) {
|
|
28
|
+
if (handleFileTooLarge && targetFile?.size >= fileSizeLimit) {
|
|
29
|
+
handleFileTooLarge(targetFile)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
dispatch("change", targetFile)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function handleFile(evt) {
|
|
36
|
+
processFile(evt.target.files[0])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function clearFile() {
|
|
40
|
+
dispatch("change", null)
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<input
|
|
45
|
+
id={fieldId}
|
|
46
|
+
{disabled}
|
|
47
|
+
type="file"
|
|
48
|
+
accept={inputAccept}
|
|
49
|
+
bind:this={fileInput}
|
|
50
|
+
on:change={handleFile}
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
<div class="field">
|
|
54
|
+
{#if value}
|
|
55
|
+
<div class="file-view">
|
|
56
|
+
{#if previewUrl}
|
|
57
|
+
<img class="preview" alt="" src={previewUrl} />
|
|
58
|
+
{/if}
|
|
59
|
+
<div class="filename">{value.name}</div>
|
|
60
|
+
{#if value.size}
|
|
61
|
+
<div class="filesize">
|
|
62
|
+
{#if value.size <= BYTES_IN_MB}
|
|
63
|
+
{`${value.size / BYTES_IN_KB} KB`}
|
|
64
|
+
{:else}
|
|
65
|
+
{`${value.size / BYTES_IN_MB} MB`}
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
69
|
+
{#if !disabled || (allowClear === true && disabled)}
|
|
70
|
+
<div class="delete-button" on:click={clearFile}>
|
|
71
|
+
<Icon name="Close" size="XS" />
|
|
72
|
+
</div>
|
|
73
|
+
{/if}
|
|
74
|
+
</div>
|
|
75
|
+
{/if}
|
|
76
|
+
<ActionButton {disabled} on:click={fileInput.click()}>{title}</ActionButton>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
.field {
|
|
81
|
+
display: flex;
|
|
82
|
+
gap: var(--spacing-m);
|
|
83
|
+
}
|
|
84
|
+
.file-view {
|
|
85
|
+
display: flex;
|
|
86
|
+
gap: var(--spacing-l);
|
|
87
|
+
align-items: center;
|
|
88
|
+
border: 1px solid var(--spectrum-alias-border-color);
|
|
89
|
+
border-radius: var(--spectrum-global-dimension-size-50);
|
|
90
|
+
padding: 0px var(--spectrum-alias-item-padding-m);
|
|
91
|
+
}
|
|
92
|
+
input[type="file"] {
|
|
93
|
+
display: none;
|
|
94
|
+
}
|
|
95
|
+
.delete-button {
|
|
96
|
+
transition: all 0.3s;
|
|
97
|
+
margin-left: 10px;
|
|
98
|
+
display: flex;
|
|
99
|
+
}
|
|
100
|
+
.delete-button:hover {
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
color: var(--red);
|
|
103
|
+
}
|
|
104
|
+
.filesize {
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
}
|
|
107
|
+
.filename {
|
|
108
|
+
overflow: hidden;
|
|
109
|
+
white-space: nowrap;
|
|
110
|
+
text-overflow: ellipsis;
|
|
111
|
+
}
|
|
112
|
+
.preview {
|
|
113
|
+
height: 1.5em;
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import "@spectrum-css/inputgroup/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
|
+
|
|
9
|
+
export let inputValue
|
|
10
|
+
export let dropdownValue
|
|
11
|
+
export let id = null
|
|
12
|
+
export let inputType = "text"
|
|
13
|
+
export let placeholder = "Choose an option or type"
|
|
14
|
+
export let disabled = false
|
|
15
|
+
export let readonly = false
|
|
16
|
+
export let updateOnChange = true
|
|
17
|
+
export let error = null
|
|
18
|
+
export let options = []
|
|
19
|
+
export let getOptionLabel = option => extractProperty(option, "label")
|
|
20
|
+
export let getOptionValue = option => extractProperty(option, "value")
|
|
21
|
+
|
|
22
|
+
export let isOptionSelected = () => false
|
|
23
|
+
|
|
24
|
+
const dispatch = createEventDispatcher()
|
|
25
|
+
let open = false
|
|
26
|
+
let focus = false
|
|
27
|
+
|
|
28
|
+
$: fieldText = getFieldText(dropdownValue, options, placeholder)
|
|
29
|
+
|
|
30
|
+
const getFieldText = (dropdownValue, options, placeholder) => {
|
|
31
|
+
// Always use placeholder if no value
|
|
32
|
+
if (dropdownValue == null || dropdownValue === "") {
|
|
33
|
+
return placeholder || "Choose an option or type"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Wait for options to load if there is a value but no options
|
|
37
|
+
if (!options?.length) {
|
|
38
|
+
return ""
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Render the label if the selected option is found, otherwise raw value
|
|
42
|
+
const selected = options.find(
|
|
43
|
+
option => getOptionValue(option) === dropdownValue
|
|
44
|
+
)
|
|
45
|
+
return selected ? getOptionLabel(selected) : dropdownValue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const updateValue = newValue => {
|
|
49
|
+
if (readonly) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
dispatch("change", newValue)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const onFocus = () => {
|
|
56
|
+
if (readonly) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
focus = true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const onBlur = event => {
|
|
63
|
+
if (readonly) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
focus = false
|
|
67
|
+
updateValue(event.target.value)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const onInput = event => {
|
|
71
|
+
if (readonly || !updateOnChange) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
updateValue(event.target.value)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const updateValueOnEnter = event => {
|
|
78
|
+
if (readonly) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (event.key === "Enter") {
|
|
82
|
+
updateValue(event.target.value)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const onClick = () => {
|
|
87
|
+
dispatch("click")
|
|
88
|
+
if (readonly) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
open = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const onPick = newValue => {
|
|
95
|
+
dispatch("pick", newValue)
|
|
96
|
+
open = false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const extractProperty = (value, property) => {
|
|
100
|
+
if (value && typeof value === "object") {
|
|
101
|
+
return value[property]
|
|
102
|
+
}
|
|
103
|
+
return value
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const handleOutsideClick = event => {
|
|
107
|
+
if (open) {
|
|
108
|
+
event.stopPropagation()
|
|
109
|
+
open = false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<div
|
|
115
|
+
class="spectrum-InputGroup"
|
|
116
|
+
class:is-invalid={!!error}
|
|
117
|
+
class:is-disabled={disabled}
|
|
118
|
+
>
|
|
119
|
+
<div
|
|
120
|
+
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
|
121
|
+
class:is-invalid={!!error}
|
|
122
|
+
class:is-disabled={disabled}
|
|
123
|
+
class:is-focused={focus}
|
|
124
|
+
>
|
|
125
|
+
{#if error}
|
|
126
|
+
<svg
|
|
127
|
+
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
|
|
128
|
+
focusable="false"
|
|
129
|
+
aria-hidden="true"
|
|
130
|
+
>
|
|
131
|
+
<use xlink:href="#spectrum-icon-18-Alert" />
|
|
132
|
+
</svg>
|
|
133
|
+
{/if}
|
|
134
|
+
|
|
135
|
+
<input
|
|
136
|
+
{id}
|
|
137
|
+
on:click
|
|
138
|
+
on:blur
|
|
139
|
+
on:focus
|
|
140
|
+
on:input
|
|
141
|
+
on:keyup
|
|
142
|
+
on:blur={onBlur}
|
|
143
|
+
on:focus={onFocus}
|
|
144
|
+
on:input={onInput}
|
|
145
|
+
on:keyup={updateValueOnEnter}
|
|
146
|
+
value={inputValue || ""}
|
|
147
|
+
placeholder={placeholder || ""}
|
|
148
|
+
{disabled}
|
|
149
|
+
{readonly}
|
|
150
|
+
{inputType}
|
|
151
|
+
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
<div style="width: 30%">
|
|
155
|
+
<button
|
|
156
|
+
{id}
|
|
157
|
+
class="spectrum-Picker spectrum-Picker--sizeM override-borders"
|
|
158
|
+
{disabled}
|
|
159
|
+
class:is-open={open}
|
|
160
|
+
aria-haspopup="listbox"
|
|
161
|
+
on:click={onClick}
|
|
162
|
+
>
|
|
163
|
+
<span class="spectrum-Picker-label">
|
|
164
|
+
<div>
|
|
165
|
+
{fieldText}
|
|
166
|
+
</div></span
|
|
167
|
+
>
|
|
168
|
+
<svg
|
|
169
|
+
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
|
170
|
+
focusable="false"
|
|
171
|
+
aria-hidden="true"
|
|
172
|
+
>
|
|
173
|
+
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
|
174
|
+
</svg>
|
|
175
|
+
</button>
|
|
176
|
+
{#if open}
|
|
177
|
+
<div
|
|
178
|
+
use:clickOutside={handleOutsideClick}
|
|
179
|
+
transition:fly|local={{ y: -20, duration: 200 }}
|
|
180
|
+
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
|
181
|
+
>
|
|
182
|
+
<ul class="spectrum-Menu" role="listbox">
|
|
183
|
+
{#each options as option, idx}
|
|
184
|
+
<li
|
|
185
|
+
class="spectrum-Menu-item"
|
|
186
|
+
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
|
|
187
|
+
role="option"
|
|
188
|
+
aria-selected="true"
|
|
189
|
+
tabindex="0"
|
|
190
|
+
on:click={() => onPick(getOptionValue(option, idx))}
|
|
191
|
+
>
|
|
192
|
+
<span class="spectrum-Menu-itemLabel">
|
|
193
|
+
{getOptionLabel(option, idx)}
|
|
194
|
+
</span>
|
|
195
|
+
<svg
|
|
196
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
197
|
+
focusable="false"
|
|
198
|
+
aria-hidden="true"
|
|
199
|
+
>
|
|
200
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
201
|
+
</svg>
|
|
202
|
+
</li>
|
|
203
|
+
{/each}
|
|
204
|
+
</ul>
|
|
205
|
+
</div>
|
|
206
|
+
{/if}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<style>
|
|
211
|
+
.spectrum-InputGroup {
|
|
212
|
+
min-width: 0;
|
|
213
|
+
width: 100%;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.spectrum-InputGroup-input {
|
|
217
|
+
border-right-width: 1px;
|
|
218
|
+
}
|
|
219
|
+
.spectrum-Textfield {
|
|
220
|
+
width: 100%;
|
|
221
|
+
}
|
|
222
|
+
.spectrum-Textfield-input {
|
|
223
|
+
width: 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.override-borders {
|
|
227
|
+
border-top-left-radius: 0px;
|
|
228
|
+
border-bottom-left-radius: 0px;
|
|
229
|
+
}
|
|
230
|
+
.spectrum-Popover {
|
|
231
|
+
max-height: 240px;
|
|
232
|
+
z-index: 999;
|
|
233
|
+
top: 100%;
|
|
234
|
+
}
|
|
235
|
+
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
export let autoWidth = false
|
|
17
|
+
export let fetchTerm = null
|
|
18
|
+
export let useFetch = false
|
|
19
|
+
export let customPopoverHeight
|
|
20
|
+
|
|
21
|
+
const dispatch = createEventDispatcher()
|
|
22
|
+
|
|
23
|
+
$: arrayValue = Array.isArray(value) ? value : [value].filter(x => !!x)
|
|
24
|
+
$: selectedLookupMap = getSelectedLookupMap(arrayValue)
|
|
25
|
+
$: optionLookupMap = getOptionLookupMap(options)
|
|
26
|
+
|
|
27
|
+
$: fieldText = getFieldText(arrayValue, optionLookupMap, placeholder)
|
|
28
|
+
$: isOptionSelected = optionValue => selectedLookupMap[optionValue] === true
|
|
29
|
+
$: toggleOption = makeToggleOption(selectedLookupMap, arrayValue)
|
|
30
|
+
|
|
31
|
+
const getFieldText = (value, map, placeholder) => {
|
|
32
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
33
|
+
if (!map) {
|
|
34
|
+
return ""
|
|
35
|
+
}
|
|
36
|
+
const vals = value.map(option => map[option] || option).join(", ")
|
|
37
|
+
return `(${value.length}) ${vals}`
|
|
38
|
+
} else {
|
|
39
|
+
return placeholder || "Choose some options"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const getSelectedLookupMap = value => {
|
|
44
|
+
let map = {}
|
|
45
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
46
|
+
value.forEach(option => {
|
|
47
|
+
if (option) {
|
|
48
|
+
map[option] = true
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
return map
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const getOptionLookupMap = options => {
|
|
56
|
+
let map = null
|
|
57
|
+
if (options?.length) {
|
|
58
|
+
map = {}
|
|
59
|
+
options.forEach((option, idx) => {
|
|
60
|
+
const optionValue = getOptionValue(option, idx)
|
|
61
|
+
if (optionValue != null) {
|
|
62
|
+
map[optionValue] = getOptionLabel(option, idx) || ""
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
return map
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const makeToggleOption = (map, value) => {
|
|
70
|
+
return optionValue => {
|
|
71
|
+
if (map[optionValue]) {
|
|
72
|
+
const filtered = value.filter(option => option !== optionValue)
|
|
73
|
+
dispatch("change", filtered)
|
|
74
|
+
} else {
|
|
75
|
+
dispatch("change", [...value, optionValue])
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<Picker
|
|
82
|
+
{id}
|
|
83
|
+
{error}
|
|
84
|
+
{disabled}
|
|
85
|
+
{readonly}
|
|
86
|
+
{fieldText}
|
|
87
|
+
{options}
|
|
88
|
+
isPlaceholder={!arrayValue.length}
|
|
89
|
+
{autocomplete}
|
|
90
|
+
bind:fetchTerm
|
|
91
|
+
{useFetch}
|
|
92
|
+
{isOptionSelected}
|
|
93
|
+
{getOptionLabel}
|
|
94
|
+
{getOptionValue}
|
|
95
|
+
onSelectOption={toggleOption}
|
|
96
|
+
{sort}
|
|
97
|
+
{autoWidth}
|
|
98
|
+
{customPopoverHeight}
|
|
99
|
+
/>
|