@budibase/bbui 1.1.24 → 1.1.25-alpha.2
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/dist/bbui.es.js +35 -35
- package/dist/bbui.es.js.map +1 -1
- package/package.json +3 -3
- package/src/Avatar/Avatar.svelte +16 -3
- package/src/Form/Core/InputDropdown.svelte +218 -0
- package/src/Form/Core/Multiselect.svelte +2 -0
- package/src/Form/Core/PickerDropdown.svelte +430 -0
- package/src/Form/Core/Select.svelte +0 -1
- package/src/Form/InputDropdown.svelte +55 -0
- package/src/Form/Multiselect.svelte +2 -1
- package/src/Form/PickerDropdown.svelte +125 -0
- package/src/IconPicker/IconPicker.svelte +177 -0
- package/src/List/List.svelte +28 -0
- package/src/List/ListItem.svelte +92 -0
- package/src/Table/Table.svelte +19 -2
- package/src/index.js +6 -0
- package/src/List/Items/DetailSummary.svench +0 -53
|
@@ -0,0 +1,430 @@
|
|
|
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
|
+
import Icon from "../../Icon/Icon.svelte"
|
|
9
|
+
import StatusLight from "../../StatusLight/StatusLight.svelte"
|
|
10
|
+
import Detail from "../../Typography/Detail.svelte"
|
|
11
|
+
|
|
12
|
+
export let primaryLabel = ""
|
|
13
|
+
export let primaryValue = null
|
|
14
|
+
export let id = null
|
|
15
|
+
export let placeholder = "Choose an option or type"
|
|
16
|
+
export let disabled = false
|
|
17
|
+
export let readonly = false
|
|
18
|
+
export let updateOnChange = true
|
|
19
|
+
export let error = null
|
|
20
|
+
export let secondaryOptions = []
|
|
21
|
+
export let primaryOptions = []
|
|
22
|
+
export let secondaryFieldText = ""
|
|
23
|
+
export let secondaryFieldIcon = ""
|
|
24
|
+
export let secondaryFieldColour = ""
|
|
25
|
+
export let getPrimaryOptionLabel = option => option
|
|
26
|
+
export let getPrimaryOptionValue = option => option
|
|
27
|
+
export let getPrimaryOptionColour = () => null
|
|
28
|
+
export let getPrimaryOptionIcon = () => null
|
|
29
|
+
export let getSecondaryOptionLabel = option => option
|
|
30
|
+
export let getSecondaryOptionValue = option => option
|
|
31
|
+
export let getSecondaryOptionColour = () => null
|
|
32
|
+
export let onSelectOption = () => {}
|
|
33
|
+
export let autoWidth = false
|
|
34
|
+
export let autocomplete = false
|
|
35
|
+
export let isOptionSelected = () => false
|
|
36
|
+
export let isPlaceholder = false
|
|
37
|
+
export let placeholderOption = null
|
|
38
|
+
|
|
39
|
+
const dispatch = createEventDispatcher()
|
|
40
|
+
let primaryOpen = false
|
|
41
|
+
let secondaryOpen = false
|
|
42
|
+
let focus = false
|
|
43
|
+
let searchTerm = null
|
|
44
|
+
|
|
45
|
+
$: groupTitles = Object.keys(primaryOptions)
|
|
46
|
+
$: filteredOptions = getFilteredOptions(
|
|
47
|
+
primaryOptions,
|
|
48
|
+
searchTerm,
|
|
49
|
+
getPrimaryOptionLabel
|
|
50
|
+
)
|
|
51
|
+
let iconData
|
|
52
|
+
/*
|
|
53
|
+
$: iconData = primaryOptions?.find(x => {
|
|
54
|
+
return x.name === primaryFieldText
|
|
55
|
+
})
|
|
56
|
+
*/
|
|
57
|
+
const updateValue = newValue => {
|
|
58
|
+
if (readonly) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
dispatch("change", newValue)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const onClickSecondary = () => {
|
|
65
|
+
dispatch("click")
|
|
66
|
+
if (readonly) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
secondaryOpen = true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const onPickPrimary = newValue => {
|
|
73
|
+
dispatch("pickprimary", newValue)
|
|
74
|
+
primaryOpen = false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const onClearPrimary = () => {
|
|
78
|
+
dispatch("pickprimary", null)
|
|
79
|
+
primaryOpen = false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const onPickSecondary = newValue => {
|
|
83
|
+
dispatch("picksecondary", newValue)
|
|
84
|
+
secondaryOpen = false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const onBlur = event => {
|
|
88
|
+
if (readonly) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
focus = false
|
|
92
|
+
updateValue(event.target.value)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const onInput = event => {
|
|
96
|
+
if (readonly || !updateOnChange) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
updateValue(event.target.value)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const updateValueOnEnter = event => {
|
|
103
|
+
if (readonly) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
if (event.key === "Enter") {
|
|
107
|
+
updateValue(event.target.value)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const getFilteredOptions = (options, term, getLabel) => {
|
|
112
|
+
if (autocomplete && term) {
|
|
113
|
+
const lowerCaseTerm = term.toLowerCase()
|
|
114
|
+
return options.filter(option => {
|
|
115
|
+
return `${getLabel(option)}`.toLowerCase().includes(lowerCaseTerm)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
return options
|
|
119
|
+
}
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<div
|
|
123
|
+
class="spectrum-InputGroup"
|
|
124
|
+
class:is-invalid={!!error}
|
|
125
|
+
class:is-disabled={disabled}
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
|
129
|
+
class:is-invalid={!!error}
|
|
130
|
+
class:is-disabled={disabled}
|
|
131
|
+
class:is-focused={focus}
|
|
132
|
+
class:is-full-width={!secondaryOptions.length}
|
|
133
|
+
>
|
|
134
|
+
{#if iconData}
|
|
135
|
+
<svg
|
|
136
|
+
width="16px"
|
|
137
|
+
height="16px"
|
|
138
|
+
class="spectrum-Icon iconPadding"
|
|
139
|
+
style="color: {iconData?.color}"
|
|
140
|
+
focusable="false"
|
|
141
|
+
>
|
|
142
|
+
<use xlink:href="#spectrum-icon-18-{iconData?.icon}" />
|
|
143
|
+
</svg>
|
|
144
|
+
{/if}
|
|
145
|
+
<input
|
|
146
|
+
{id}
|
|
147
|
+
on:click={() => (primaryOpen = true)}
|
|
148
|
+
on:blur
|
|
149
|
+
on:focus
|
|
150
|
+
on:input
|
|
151
|
+
on:keyup
|
|
152
|
+
on:blur={onBlur}
|
|
153
|
+
on:input={onInput}
|
|
154
|
+
on:keyup={updateValueOnEnter}
|
|
155
|
+
value={primaryLabel || ""}
|
|
156
|
+
placeholder={placeholder || ""}
|
|
157
|
+
{disabled}
|
|
158
|
+
{readonly}
|
|
159
|
+
class="spectrum-Textfield-input spectrum-InputGroup-input"
|
|
160
|
+
class:labelPadding={iconData}
|
|
161
|
+
/>
|
|
162
|
+
{#if primaryValue}
|
|
163
|
+
<button
|
|
164
|
+
on:click={() => onClearPrimary()}
|
|
165
|
+
type="reset"
|
|
166
|
+
class="spectrum-ClearButton spectrum-Search-clearButton"
|
|
167
|
+
>
|
|
168
|
+
<svg
|
|
169
|
+
class="spectrum-Icon spectrum-UIIcon-Cross75"
|
|
170
|
+
focusable="false"
|
|
171
|
+
aria-hidden="true"
|
|
172
|
+
>
|
|
173
|
+
<use xlink:href="#spectrum-css-icon-Cross75" />
|
|
174
|
+
</svg>
|
|
175
|
+
</button>
|
|
176
|
+
{/if}
|
|
177
|
+
</div>
|
|
178
|
+
{#if primaryOpen}
|
|
179
|
+
<div
|
|
180
|
+
use:clickOutside={() => (primaryOpen = false)}
|
|
181
|
+
transition:fly|local={{ y: -20, duration: 200 }}
|
|
182
|
+
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
|
183
|
+
class:auto-width={autoWidth}
|
|
184
|
+
class:is-full-width={!secondaryOptions.length}
|
|
185
|
+
>
|
|
186
|
+
<ul class="spectrum-Menu" role="listbox">
|
|
187
|
+
{#if placeholderOption}
|
|
188
|
+
<li
|
|
189
|
+
class="spectrum-Menu-item placeholder"
|
|
190
|
+
class:is-selected={isPlaceholder}
|
|
191
|
+
role="option"
|
|
192
|
+
aria-selected="true"
|
|
193
|
+
tabindex="0"
|
|
194
|
+
on:click={() => onSelectOption(null)}
|
|
195
|
+
>
|
|
196
|
+
<span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
|
|
197
|
+
<svg
|
|
198
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
199
|
+
focusable="false"
|
|
200
|
+
aria-hidden="true"
|
|
201
|
+
>
|
|
202
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
203
|
+
</svg>
|
|
204
|
+
</li>
|
|
205
|
+
{/if}
|
|
206
|
+
{#each groupTitles as title}
|
|
207
|
+
<div class="spectrum-Menu-item">
|
|
208
|
+
<Detail>{title}</Detail>
|
|
209
|
+
</div>
|
|
210
|
+
{#if primaryOptions}
|
|
211
|
+
{#each primaryOptions[title].data as option, idx}
|
|
212
|
+
<li
|
|
213
|
+
class="spectrum-Menu-item"
|
|
214
|
+
class:is-selected={isOptionSelected(
|
|
215
|
+
getPrimaryOptionValue(option, idx)
|
|
216
|
+
)}
|
|
217
|
+
role="option"
|
|
218
|
+
aria-selected="true"
|
|
219
|
+
tabindex="0"
|
|
220
|
+
on:click={() =>
|
|
221
|
+
onPickPrimary({
|
|
222
|
+
value: primaryOptions[title].getValue(option),
|
|
223
|
+
label: primaryOptions[title].getLabel(option),
|
|
224
|
+
})}
|
|
225
|
+
>
|
|
226
|
+
{#if primaryOptions[title].getIcon(option)}
|
|
227
|
+
<div
|
|
228
|
+
style="background: {primaryOptions[title].getColour(
|
|
229
|
+
option
|
|
230
|
+
)};"
|
|
231
|
+
class="circle"
|
|
232
|
+
>
|
|
233
|
+
<div>
|
|
234
|
+
<Icon
|
|
235
|
+
size="S"
|
|
236
|
+
name={primaryOptions[title].getIcon(option)}
|
|
237
|
+
/>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
{:else if getPrimaryOptionColour(option, idx)}
|
|
241
|
+
<span class="option-left">
|
|
242
|
+
<StatusLight color={getPrimaryOptionColour(option, idx)} />
|
|
243
|
+
</span>
|
|
244
|
+
{/if}
|
|
245
|
+
<span class="spectrum-Menu-itemLabel">
|
|
246
|
+
<span
|
|
247
|
+
class:spacing-group={primaryOptions[title].getIcon(option)}
|
|
248
|
+
>
|
|
249
|
+
{primaryOptions[title].getLabel(option)}
|
|
250
|
+
<span />
|
|
251
|
+
</span>
|
|
252
|
+
<svg
|
|
253
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
254
|
+
focusable="false"
|
|
255
|
+
aria-hidden="true"
|
|
256
|
+
>
|
|
257
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
258
|
+
</svg>
|
|
259
|
+
{#if getPrimaryOptionIcon(option, idx) && getPrimaryOptionColour(option, idx)}
|
|
260
|
+
<span class="option-right">
|
|
261
|
+
<StatusLight
|
|
262
|
+
color={getPrimaryOptionColour(option, idx)}
|
|
263
|
+
/>
|
|
264
|
+
</span>
|
|
265
|
+
{/if}
|
|
266
|
+
</span>
|
|
267
|
+
</li>
|
|
268
|
+
{/each}
|
|
269
|
+
{/if}
|
|
270
|
+
{/each}
|
|
271
|
+
</ul>
|
|
272
|
+
</div>
|
|
273
|
+
{/if}
|
|
274
|
+
{#if secondaryOptions.length}
|
|
275
|
+
<div style="width: 30%">
|
|
276
|
+
<button
|
|
277
|
+
{id}
|
|
278
|
+
class="spectrum-Picker spectrum-Picker--sizeM override-borders"
|
|
279
|
+
{disabled}
|
|
280
|
+
class:is-open={secondaryOpen}
|
|
281
|
+
aria-haspopup="listbox"
|
|
282
|
+
on:mousedown={onClickSecondary}
|
|
283
|
+
>
|
|
284
|
+
{#if secondaryFieldIcon}
|
|
285
|
+
<span class="option-left">
|
|
286
|
+
<Icon name={secondaryFieldIcon} />
|
|
287
|
+
</span>
|
|
288
|
+
{:else if secondaryFieldColour}
|
|
289
|
+
<span class="option-left">
|
|
290
|
+
<StatusLight color={secondaryFieldColour} />
|
|
291
|
+
</span>
|
|
292
|
+
{/if}
|
|
293
|
+
|
|
294
|
+
<span class:auto-width={autoWidth} class="spectrum-Picker-label">
|
|
295
|
+
{secondaryFieldText}
|
|
296
|
+
</span>
|
|
297
|
+
<svg
|
|
298
|
+
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
|
|
299
|
+
focusable="false"
|
|
300
|
+
aria-hidden="true"
|
|
301
|
+
>
|
|
302
|
+
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
|
303
|
+
</svg>
|
|
304
|
+
</button>
|
|
305
|
+
{#if secondaryOpen}
|
|
306
|
+
<div
|
|
307
|
+
use:clickOutside={() => (secondaryOpen = false)}
|
|
308
|
+
transition:fly|local={{ y: -20, duration: 200 }}
|
|
309
|
+
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
|
|
310
|
+
style="width: 30%"
|
|
311
|
+
>
|
|
312
|
+
<ul class="spectrum-Menu" role="listbox">
|
|
313
|
+
{#each secondaryOptions as option, idx}
|
|
314
|
+
<li
|
|
315
|
+
class="spectrum-Menu-item"
|
|
316
|
+
class:is-selected={isOptionSelected(
|
|
317
|
+
getSecondaryOptionValue(option, idx)
|
|
318
|
+
)}
|
|
319
|
+
role="option"
|
|
320
|
+
aria-selected="true"
|
|
321
|
+
tabindex="0"
|
|
322
|
+
on:click={() =>
|
|
323
|
+
onPickSecondary(getSecondaryOptionValue(option, idx))}
|
|
324
|
+
>
|
|
325
|
+
{#if getSecondaryOptionColour(option, idx)}
|
|
326
|
+
<span class="option-left">
|
|
327
|
+
<StatusLight
|
|
328
|
+
color={getSecondaryOptionColour(option, idx)}
|
|
329
|
+
/>
|
|
330
|
+
</span>
|
|
331
|
+
{/if}
|
|
332
|
+
|
|
333
|
+
<span class="spectrum-Menu-itemLabel">
|
|
334
|
+
{getSecondaryOptionLabel(option, idx)}
|
|
335
|
+
</span>
|
|
336
|
+
<svg
|
|
337
|
+
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
|
338
|
+
focusable="false"
|
|
339
|
+
aria-hidden="true"
|
|
340
|
+
>
|
|
341
|
+
<use xlink:href="#spectrum-css-icon-Checkmark100" />
|
|
342
|
+
</svg>
|
|
343
|
+
</li>
|
|
344
|
+
{/each}
|
|
345
|
+
</ul>
|
|
346
|
+
</div>
|
|
347
|
+
{/if}
|
|
348
|
+
</div>
|
|
349
|
+
{/if}
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<style>
|
|
353
|
+
.spacing-group {
|
|
354
|
+
margin-left: var(--spacing-m);
|
|
355
|
+
}
|
|
356
|
+
.spectrum-InputGroup {
|
|
357
|
+
min-width: 0;
|
|
358
|
+
width: 100%;
|
|
359
|
+
}
|
|
360
|
+
.override-borders {
|
|
361
|
+
border-top-left-radius: 0px;
|
|
362
|
+
border-bottom-left-radius: 0px;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.spectrum-Popover {
|
|
366
|
+
max-height: 240px;
|
|
367
|
+
z-index: 999;
|
|
368
|
+
top: 100%;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.option-left {
|
|
372
|
+
padding-right: 8px;
|
|
373
|
+
}
|
|
374
|
+
.option-right {
|
|
375
|
+
padding-left: 8px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.circle {
|
|
379
|
+
border-radius: 50%;
|
|
380
|
+
height: 28px;
|
|
381
|
+
color: white;
|
|
382
|
+
font-weight: bold;
|
|
383
|
+
line-height: 48px;
|
|
384
|
+
font-size: 1.2em;
|
|
385
|
+
width: 28px;
|
|
386
|
+
position: relative;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.circle > div {
|
|
390
|
+
position: absolute;
|
|
391
|
+
text-decoration: none;
|
|
392
|
+
top: 50%;
|
|
393
|
+
left: 50%;
|
|
394
|
+
transform: translateX(-50%) translateY(-50%);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.iconPadding {
|
|
398
|
+
position: absolute;
|
|
399
|
+
top: 50%;
|
|
400
|
+
left: 10px;
|
|
401
|
+
transform: translateY(-50%);
|
|
402
|
+
color: silver;
|
|
403
|
+
margin-right: 10px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.labelPadding {
|
|
407
|
+
padding-left: calc(1em + 10px + 8px);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.spectrum-Textfield.spectrum-InputGroup-textfield {
|
|
411
|
+
width: 70%;
|
|
412
|
+
}
|
|
413
|
+
.spectrum-Textfield.spectrum-InputGroup-textfield.is-full-width {
|
|
414
|
+
width: 100%;
|
|
415
|
+
}
|
|
416
|
+
.spectrum-Textfield.spectrum-InputGroup-textfield.is-full-width input {
|
|
417
|
+
border-right-width: thin;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.spectrum-Popover.spectrum-Popover--bottom.spectrum-Picker-popover.is-open {
|
|
421
|
+
width: 70%;
|
|
422
|
+
}
|
|
423
|
+
.spectrum-Popover.spectrum-Popover--bottom.spectrum-Picker-popover.is-open.is-full-width {
|
|
424
|
+
width: 100%;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.spectrum-Search-clearButton {
|
|
428
|
+
position: absolute;
|
|
429
|
+
}
|
|
430
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Field from "./Field.svelte"
|
|
3
|
+
import InputDropdown from "./Core/InputDropdown.svelte"
|
|
4
|
+
import { createEventDispatcher } from "svelte"
|
|
5
|
+
|
|
6
|
+
export let inputValue = null
|
|
7
|
+
export let dropdownValue = null
|
|
8
|
+
export let inputType = "text"
|
|
9
|
+
export let label = null
|
|
10
|
+
export let labelPosition = "above"
|
|
11
|
+
export let placeholder = null
|
|
12
|
+
export let disabled = false
|
|
13
|
+
export let readonly = false
|
|
14
|
+
export let error = null
|
|
15
|
+
export let updateOnChange = true
|
|
16
|
+
export let quiet = false
|
|
17
|
+
export let dataCy
|
|
18
|
+
export let autofocus
|
|
19
|
+
export let options = []
|
|
20
|
+
|
|
21
|
+
const dispatch = createEventDispatcher()
|
|
22
|
+
|
|
23
|
+
const onPick = e => {
|
|
24
|
+
dropdownValue = e.detail
|
|
25
|
+
dispatch("pick", e.detail)
|
|
26
|
+
}
|
|
27
|
+
const onChange = e => {
|
|
28
|
+
inputValue = e.detail
|
|
29
|
+
dispatch("change", e.detail)
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<Field {label} {labelPosition} {error}>
|
|
34
|
+
<InputDropdown
|
|
35
|
+
{dataCy}
|
|
36
|
+
{updateOnChange}
|
|
37
|
+
{error}
|
|
38
|
+
{disabled}
|
|
39
|
+
{readonly}
|
|
40
|
+
{inputValue}
|
|
41
|
+
{dropdownValue}
|
|
42
|
+
{placeholder}
|
|
43
|
+
{inputType}
|
|
44
|
+
{quiet}
|
|
45
|
+
{autofocus}
|
|
46
|
+
{options}
|
|
47
|
+
on:change={onChange}
|
|
48
|
+
on:pick={onPick}
|
|
49
|
+
on:click
|
|
50
|
+
on:input
|
|
51
|
+
on:blur
|
|
52
|
+
on:focus
|
|
53
|
+
on:keyup
|
|
54
|
+
/>
|
|
55
|
+
</Field>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
export let getOptionLabel = option => option
|
|
15
15
|
export let getOptionValue = option => option
|
|
16
16
|
export let sort = false
|
|
17
|
-
|
|
17
|
+
export let autoWidth = false
|
|
18
18
|
const dispatch = createEventDispatcher()
|
|
19
19
|
const onChange = e => {
|
|
20
20
|
value = e.detail
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
{sort}
|
|
34
34
|
{getOptionLabel}
|
|
35
35
|
{getOptionValue}
|
|
36
|
+
{autoWidth}
|
|
36
37
|
on:change={onChange}
|
|
37
38
|
on:click
|
|
38
39
|
/>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Field from "./Field.svelte"
|
|
3
|
+
import PickerDropdown from "./Core/PickerDropdown.svelte"
|
|
4
|
+
import { createEventDispatcher } from "svelte"
|
|
5
|
+
|
|
6
|
+
export let primaryValue = null
|
|
7
|
+
export let secondaryValue = null
|
|
8
|
+
export let inputType = "text"
|
|
9
|
+
export let label = null
|
|
10
|
+
export let labelPosition = "above"
|
|
11
|
+
export let secondaryPlaceholder = null
|
|
12
|
+
export let autocomplete
|
|
13
|
+
export let placeholder = null
|
|
14
|
+
export let disabled = false
|
|
15
|
+
export let readonly = false
|
|
16
|
+
export let error = null
|
|
17
|
+
export let updateOnChange = true
|
|
18
|
+
export let getSecondaryOptionLabel = option =>
|
|
19
|
+
extractProperty(option, "label")
|
|
20
|
+
export let getSecondaryOptionValue = option =>
|
|
21
|
+
extractProperty(option, "value")
|
|
22
|
+
export let getSecondaryOptionColour = () => {}
|
|
23
|
+
export let getSecondaryOptionIcon = () => {}
|
|
24
|
+
export let quiet = false
|
|
25
|
+
export let dataCy
|
|
26
|
+
export let autofocus
|
|
27
|
+
export let primaryOptions = []
|
|
28
|
+
export let secondaryOptions = []
|
|
29
|
+
|
|
30
|
+
let primaryLabel
|
|
31
|
+
let secondaryLabel
|
|
32
|
+
const dispatch = createEventDispatcher()
|
|
33
|
+
|
|
34
|
+
$: secondaryFieldText = getSecondaryFieldText(
|
|
35
|
+
secondaryValue,
|
|
36
|
+
secondaryOptions,
|
|
37
|
+
secondaryPlaceholder
|
|
38
|
+
)
|
|
39
|
+
$: secondaryFieldIcon = getSecondaryFieldAttribute(
|
|
40
|
+
getSecondaryOptionIcon,
|
|
41
|
+
secondaryValue,
|
|
42
|
+
secondaryOptions
|
|
43
|
+
)
|
|
44
|
+
$: secondaryFieldColour = getSecondaryFieldAttribute(
|
|
45
|
+
getSecondaryOptionColour,
|
|
46
|
+
secondaryValue,
|
|
47
|
+
secondaryOptions
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const getSecondaryFieldAttribute = (getAttribute, value, options) => {
|
|
51
|
+
// Wait for options to load if there is a value but no options
|
|
52
|
+
|
|
53
|
+
if (!options?.length) {
|
|
54
|
+
return ""
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const index = options.findIndex(
|
|
58
|
+
(option, idx) => getSecondaryOptionValue(option, idx) === value
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return index !== -1 ? getAttribute(options[index], index) : null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getSecondaryFieldText = (value, options, placeholder) => {
|
|
65
|
+
// Always use placeholder if no value
|
|
66
|
+
if (value == null || value === "") {
|
|
67
|
+
return placeholder || "Choose an option"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return getSecondaryFieldAttribute(getSecondaryOptionLabel, value, options)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const onPickPrimary = e => {
|
|
74
|
+
primaryLabel = e?.detail?.label || null
|
|
75
|
+
primaryValue = e?.detail?.value || null
|
|
76
|
+
dispatch("pickprimary", e?.detail?.value || {})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const onPickSecondary = e => {
|
|
80
|
+
secondaryValue = e.detail
|
|
81
|
+
dispatch("picksecondary", e.detail)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const extractProperty = (value, property) => {
|
|
85
|
+
if (value && typeof value === "object") {
|
|
86
|
+
return value[property]
|
|
87
|
+
}
|
|
88
|
+
return value
|
|
89
|
+
}
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<Field {label} {labelPosition} {error}>
|
|
93
|
+
<PickerDropdown
|
|
94
|
+
{autocomplete}
|
|
95
|
+
{dataCy}
|
|
96
|
+
{updateOnChange}
|
|
97
|
+
{error}
|
|
98
|
+
{disabled}
|
|
99
|
+
{readonly}
|
|
100
|
+
{placeholder}
|
|
101
|
+
{inputType}
|
|
102
|
+
{quiet}
|
|
103
|
+
{autofocus}
|
|
104
|
+
{primaryOptions}
|
|
105
|
+
{secondaryOptions}
|
|
106
|
+
{getSecondaryOptionLabel}
|
|
107
|
+
{getSecondaryOptionValue}
|
|
108
|
+
{getSecondaryOptionIcon}
|
|
109
|
+
{getSecondaryOptionColour}
|
|
110
|
+
{secondaryFieldText}
|
|
111
|
+
{secondaryFieldIcon}
|
|
112
|
+
{secondaryFieldColour}
|
|
113
|
+
{primaryValue}
|
|
114
|
+
{secondaryValue}
|
|
115
|
+
{primaryLabel}
|
|
116
|
+
{secondaryLabel}
|
|
117
|
+
on:pickprimary={onPickPrimary}
|
|
118
|
+
on:picksecondary={onPickSecondary}
|
|
119
|
+
on:click
|
|
120
|
+
on:input
|
|
121
|
+
on:blur
|
|
122
|
+
on:focus
|
|
123
|
+
on:keyup
|
|
124
|
+
/>
|
|
125
|
+
</Field>
|