@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.
Files changed (149) hide show
  1. package/LICENSE +375 -0
  2. package/README.md +42 -0
  3. package/dist/bbui.es.js +8 -0
  4. package/dist/bbui.es.js.map +1 -0
  5. package/package.json +94 -0
  6. package/src/Accordion/Accordion.svelte +58 -0
  7. package/src/ActionButton/ActionButton.svelte +132 -0
  8. package/src/ActionGroup/ActionGroup.svelte +26 -0
  9. package/src/ActionMenu/ActionMenu.svelte +43 -0
  10. package/src/Actions/ClickOutside.svench.svx +30 -0
  11. package/src/Actions/PositionDropdown.svench.svx +81 -0
  12. package/src/Actions/autoresize_textarea.js +14 -0
  13. package/src/Actions/click_outside.js +76 -0
  14. package/src/Actions/position_dropdown.js +116 -0
  15. package/src/Avatar/Avatar.svelte +63 -0
  16. package/src/Badge/Badge.svelte +42 -0
  17. package/src/Banner/Banner.svelte +72 -0
  18. package/src/Banner/BannerDisplay.svelte +41 -0
  19. package/src/Button/Button.svelte +118 -0
  20. package/src/Button/Button.svench +138 -0
  21. package/src/ButtonGroup/ButtonGroup.svelte +30 -0
  22. package/src/ClearButton/ClearButton.svelte +19 -0
  23. package/src/ColorPicker/ColorPicker.svelte +304 -0
  24. package/src/DetailSummary/DetailSummary.svelte +92 -0
  25. package/src/Divider/Divider.svelte +28 -0
  26. package/src/Drawer/Drawer.svelte +113 -0
  27. package/src/Drawer/Drawer.svench +42 -0
  28. package/src/Drawer/DrawerContent.svelte +45 -0
  29. package/src/FancyForm/FancyButton.svelte +30 -0
  30. package/src/FancyForm/FancyButtonRadio.svelte +70 -0
  31. package/src/FancyForm/FancyCheckbox.svelte +53 -0
  32. package/src/FancyForm/FancyField.svelte +126 -0
  33. package/src/FancyForm/FancyFieldLabel.svelte +25 -0
  34. package/src/FancyForm/FancyForm.svelte +40 -0
  35. package/src/FancyForm/FancyInput.svelte +109 -0
  36. package/src/FancyForm/FancySelect.svelte +147 -0
  37. package/src/FancyForm/index.js +6 -0
  38. package/src/Form/Checkbox.svelte +23 -0
  39. package/src/Form/Combobox.svelte +45 -0
  40. package/src/Form/Core/Checkbox.svelte +69 -0
  41. package/src/Form/Core/CheckboxGroup.svelte +68 -0
  42. package/src/Form/Core/Combobox.svelte +142 -0
  43. package/src/Form/Core/DatePicker.svelte +265 -0
  44. package/src/Form/Core/Dropzone.svelte +482 -0
  45. package/src/Form/Core/EnvDropdown.svelte +276 -0
  46. package/src/Form/Core/File.svelte +115 -0
  47. package/src/Form/Core/InputDropdown.svelte +235 -0
  48. package/src/Form/Core/Multiselect.svelte +99 -0
  49. package/src/Form/Core/Picker.svelte +314 -0
  50. package/src/Form/Core/PickerDropdown.svelte +431 -0
  51. package/src/Form/Core/RadioGroup.svelte +65 -0
  52. package/src/Form/Core/RichTextField.svelte +42 -0
  53. package/src/Form/Core/Search.svelte +105 -0
  54. package/src/Form/Core/Select.svelte +90 -0
  55. package/src/Form/Core/Slider.svelte +86 -0
  56. package/src/Form/Core/Stepper.svelte +172 -0
  57. package/src/Form/Core/Switch.svelte +35 -0
  58. package/src/Form/Core/TextArea.svelte +78 -0
  59. package/src/Form/Core/TextField.svelte +115 -0
  60. package/src/Form/Core/index.js +16 -0
  61. package/src/Form/DatePicker.svelte +46 -0
  62. package/src/Form/Dropzone.svelte +44 -0
  63. package/src/Form/EnvDropdown.svelte +50 -0
  64. package/src/Form/Field.svelte +42 -0
  65. package/src/Form/FieldLabel.svelte +32 -0
  66. package/src/Form/File.svelte +37 -0
  67. package/src/Form/Input.svelte +45 -0
  68. package/src/Form/InputDropdown.svelte +53 -0
  69. package/src/Form/Multiselect.svelte +49 -0
  70. package/src/Form/PickerDropdown.svelte +131 -0
  71. package/src/Form/RadioGroup.svelte +42 -0
  72. package/src/Form/RichTextField.svelte +36 -0
  73. package/src/Form/Search.svelte +36 -0
  74. package/src/Form/Select.svelte +67 -0
  75. package/src/Form/Slider.svelte +24 -0
  76. package/src/Form/Stepper.svelte +45 -0
  77. package/src/Form/TextArea.svelte +34 -0
  78. package/src/Form/Toggle.svelte +22 -0
  79. package/src/Icon/Icon.svelte +94 -0
  80. package/src/Icon/IconAvatar.svelte +61 -0
  81. package/src/IconPicker/IconPicker.svelte +184 -0
  82. package/src/IconSideNav/IconSideNav.svelte +14 -0
  83. package/src/IconSideNav/IconSideNavItem.svelte +56 -0
  84. package/src/InlineAlert/InlineAlert.svelte +63 -0
  85. package/src/Input/CopyInput.svelte +59 -0
  86. package/src/Label/Label.svelte +31 -0
  87. package/src/Layout/Layout.svelte +94 -0
  88. package/src/Layout/Page.svelte +101 -0
  89. package/src/Link/Link.svelte +27 -0
  90. package/src/List/List.svelte +28 -0
  91. package/src/List/ListItem.svelte +93 -0
  92. package/src/Markdown/MarkdownEditor.svelte +60 -0
  93. package/src/Markdown/MarkdownViewer.svelte +70 -0
  94. package/src/Markdown/SpectrumMDE.svelte +184 -0
  95. package/src/Menu/Item.svelte +94 -0
  96. package/src/Menu/Menu.svelte +7 -0
  97. package/src/Menu/Menu.svench +19 -0
  98. package/src/Menu/Section.svelte +10 -0
  99. package/src/Menu/Separator.svelte +1 -0
  100. package/src/Modal/Content.svelte +6 -0
  101. package/src/Modal/CustomContent.svelte +37 -0
  102. package/src/Modal/Modal.svelte +178 -0
  103. package/src/Modal/Modal.svench +116 -0
  104. package/src/Modal/ModalContent.svelte +190 -0
  105. package/src/Modal/QuizModal.svelte +53 -0
  106. package/src/Notification/Notification.svelte +74 -0
  107. package/src/Notification/NotificationDisplay.svelte +43 -0
  108. package/src/Pagination/Pagination.svelte +57 -0
  109. package/src/Popover/Popover.svelte +95 -0
  110. package/src/Popover/Popover.svench +120 -0
  111. package/src/ProgressBar/ProgressBar.svelte +67 -0
  112. package/src/ProgressCircle/ProgressCircle.svelte +62 -0
  113. package/src/SideNavigation/Item.svelte +59 -0
  114. package/src/SideNavigation/Navigation.svelte +12 -0
  115. package/src/StatusLight/StatusLight.svelte +80 -0
  116. package/src/Stores/Notifications.svench.svx +78 -0
  117. package/src/Stores/banner.js +65 -0
  118. package/src/Stores/notifications.js +84 -0
  119. package/src/Table/ArrayRenderer.svelte +18 -0
  120. package/src/Table/AttachmentRenderer.svelte +73 -0
  121. package/src/Table/BoldRenderer.svelte +15 -0
  122. package/src/Table/BooleanRenderer.svelte +42 -0
  123. package/src/Table/CellRenderer.svelte +70 -0
  124. package/src/Table/CodeRenderer.svelte +14 -0
  125. package/src/Table/DateTimeRenderer.svelte +29 -0
  126. package/src/Table/InternalRenderer.svelte +24 -0
  127. package/src/Table/RelationshipRenderer.svelte +35 -0
  128. package/src/Table/SelectEditRenderer.svelte +28 -0
  129. package/src/Table/StringRenderer.svelte +22 -0
  130. package/src/Table/Table.svelte +657 -0
  131. package/src/Table/index.js +1 -0
  132. package/src/Tabs/Tab.svelte +88 -0
  133. package/src/Tabs/Tabs.svelte +130 -0
  134. package/src/Tags/Tag.svelte +43 -0
  135. package/src/Tags/Tags.svelte +17 -0
  136. package/src/Tooltip/Tooltip.svelte +34 -0
  137. package/src/Tooltip/TooltipWrapper.svelte +60 -0
  138. package/src/TreeView/Item.svelte +41 -0
  139. package/src/TreeView/Tree.svelte +16 -0
  140. package/src/Typography/Body.svelte +22 -0
  141. package/src/Typography/Code.svelte +10 -0
  142. package/src/Typography/Detail.svelte +19 -0
  143. package/src/Typography/Heading.svelte +24 -0
  144. package/src/bbui.css +118 -0
  145. package/src/context.js +4 -0
  146. package/src/helpers.js +116 -0
  147. package/src/index.js +109 -0
  148. package/src/spectrum-icons-rollup.js +31 -0
  149. 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
+ />