@budibase/bbui 2.2.12-alpha.21 → 2.2.12-alpha.23

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/bbui",
3
3
  "description": "A UI solution used in the different Budibase projects.",
4
- "version": "2.2.12-alpha.21",
4
+ "version": "2.2.12-alpha.23",
5
5
  "license": "MPL-2.0",
6
6
  "svelte": "src/index.js",
7
7
  "module": "dist/bbui.es.js",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "dependencies": {
40
40
  "@adobe/spectrum-css-workflow-icons": "1.2.1",
41
- "@budibase/string-templates": "2.2.12-alpha.21",
41
+ "@budibase/string-templates": "2.2.12-alpha.23",
42
42
  "@spectrum-css/actionbutton": "1.0.1",
43
43
  "@spectrum-css/actiongroup": "1.0.1",
44
44
  "@spectrum-css/avatar": "3.0.2",
@@ -88,5 +88,5 @@
88
88
  "resolutions": {
89
89
  "loader-utils": "1.4.1"
90
90
  },
91
- "gitHead": "4aadbcd8a1ad4becff6f719fe24553fb8287f9fc"
91
+ "gitHead": "305178a2ade6480d207d9933d8e52e33e6fa4931"
92
92
  }
@@ -1,11 +1,11 @@
1
- const ignoredClasses = [".flatpickr-calendar", ".modal-container"]
1
+ const ignoredClasses = [".flatpickr-calendar"]
2
2
  let clickHandlers = []
3
3
 
4
4
  /**
5
5
  * Handle a body click event
6
6
  */
7
7
  const handleClick = event => {
8
- // Ignore click if needed
8
+ // Ignore click if this is an ignored class
9
9
  for (let className of ignoredClasses) {
10
10
  if (event.target.closest(className)) {
11
11
  return
@@ -14,9 +14,18 @@ const handleClick = event => {
14
14
 
15
15
  // Process handlers
16
16
  clickHandlers.forEach(handler => {
17
- if (!handler.element.contains(event.target)) {
18
- handler.callback?.(event)
17
+ if (handler.element.contains(event.target)) {
18
+ return
19
19
  }
20
+
21
+ // Ignore clicks for modals, unless the handler is registered from a modal
22
+ const sourceInModal = handler.element.closest(".spectrum-Modal") != null
23
+ const clickInModal = event.target.closest(".spectrum-Modal") != null
24
+ if (clickInModal && !sourceInModal) {
25
+ return
26
+ }
27
+
28
+ handler.callback?.(event)
20
29
  })
21
30
  }
22
31
  document.documentElement.addEventListener("click", handleClick, true)
@@ -1,75 +1,68 @@
1
- export default function positionDropdown(element, { anchor, align, maxWidth }) {
2
- let positionSide = "top"
3
- let maxHeight = 0
4
- let dimensions = getDimensions(anchor)
5
-
6
- function getDimensions() {
7
- const {
8
- bottom,
9
- top: spaceAbove,
10
- left,
11
- width,
12
- } = anchor.getBoundingClientRect()
13
- const spaceBelow = window.innerHeight - bottom
14
- const containerRect = element.getBoundingClientRect()
15
-
16
- let y
1
+ export default function positionDropdown(
2
+ element,
3
+ { anchor, align, maxWidth, useAnchorWidth }
4
+ ) {
5
+ const update = () => {
6
+ const anchorBounds = anchor.getBoundingClientRect()
7
+ const elementBounds = element.getBoundingClientRect()
8
+ let styles = {
9
+ maxHeight: null,
10
+ minWidth: null,
11
+ maxWidth,
12
+ left: null,
13
+ top: null,
14
+ }
17
15
 
18
- if (spaceAbove > spaceBelow) {
19
- positionSide = "bottom"
20
- maxHeight = spaceAbove - 20
21
- y = window.innerHeight - spaceAbove + 5
16
+ // Determine vertical styles
17
+ if (window.innerHeight - anchorBounds.bottom < 100) {
18
+ styles.top = anchorBounds.top - elementBounds.height - 5
22
19
  } else {
23
- positionSide = "top"
24
- y = bottom + 5
25
- maxHeight = spaceBelow - 20
20
+ styles.top = anchorBounds.bottom + 5
21
+ styles.maxHeight = window.innerHeight - anchorBounds.bottom - 20
26
22
  }
27
23
 
28
- return {
29
- [positionSide]: y,
30
- left,
31
- width,
32
- containerWidth: containerRect.width,
24
+ // Determine horizontal styles
25
+ if (!maxWidth && useAnchorWidth) {
26
+ styles.maxWidth = anchorBounds.width
33
27
  }
34
- }
35
-
36
- function calcLeftPosition() {
37
- let left
38
-
39
- if (align == "right") {
40
- left = dimensions.left + dimensions.width - dimensions.containerWidth
41
- } else if (align == "right-side") {
42
- left = dimensions.left + dimensions.width
28
+ if (useAnchorWidth) {
29
+ styles.minWidth = anchorBounds.width
30
+ }
31
+ if (align === "right") {
32
+ styles.left = anchorBounds.left + anchorBounds.width - elementBounds.width
33
+ } else if (align === "right-side") {
34
+ styles.left = anchorBounds.left + anchorBounds.width
43
35
  } else {
44
- left = dimensions.left
36
+ styles.left = anchorBounds.left
45
37
  }
46
38
 
47
- return left
39
+ // Apply styles
40
+ Object.entries(styles).forEach(([style, value]) => {
41
+ if (value) {
42
+ element.style[style] = `${value.toFixed(0)}px`
43
+ } else {
44
+ element.style[style] = null
45
+ }
46
+ })
48
47
  }
49
48
 
49
+ // Apply initial styles which don't need to change
50
50
  element.style.position = "absolute"
51
51
  element.style.zIndex = "9999"
52
- if (maxWidth) {
53
- element.style.maxWidth = `${maxWidth}px`
54
- }
55
- element.style.minWidth = `${dimensions.width}px`
56
- element.style.maxHeight = `${maxHeight.toFixed(0)}px`
57
- element.style.transformOrigin = `center ${positionSide}`
58
- element.style[positionSide] = `${dimensions[positionSide]}px`
59
- element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
60
52
 
53
+ // Observe both anchor and element and resize the popover as appropriate
61
54
  const resizeObserver = new ResizeObserver(entries => {
62
- entries.forEach(() => {
63
- dimensions = getDimensions()
64
- element.style[positionSide] = `${dimensions[positionSide]}px`
65
- element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
66
- })
55
+ entries.forEach(update)
67
56
  })
68
57
  resizeObserver.observe(anchor)
69
58
  resizeObserver.observe(element)
59
+
60
+ document.addEventListener("scroll", update, true)
61
+
70
62
  return {
71
63
  destroy() {
72
64
  resizeObserver.disconnect()
65
+ document.removeEventListener("scroll", update, true)
73
66
  },
74
67
  }
75
68
  }
@@ -58,5 +58,6 @@
58
58
  overflow: hidden;
59
59
  user-select: none;
60
60
  text-transform: uppercase;
61
+ flex-shrink: 0;
61
62
  }
62
63
  </style>
@@ -14,7 +14,7 @@
14
14
  export let active = false
15
15
  export let tooltip = undefined
16
16
  export let dataCy
17
- export let newStyles = false
17
+ export let newStyles = true
18
18
 
19
19
  let showTooltip = false
20
20
  </script>
@@ -28,6 +28,7 @@
28
28
  class:spectrum-Button--quiet={quiet}
29
29
  class:new-styles={newStyles}
30
30
  class:active
31
+ class:disabled
31
32
  class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
32
33
  {disabled}
33
34
  data-cy={dataCy}
@@ -108,7 +109,10 @@
108
109
  border-color: transparent;
109
110
  color: var(--spectrum-global-color-gray-900);
110
111
  }
111
- .spectrum-Button--secondary.new-styles:hover {
112
+ .spectrum-Button--secondary.new-styles:not(.disabled):hover {
112
113
  background: var(--spectrum-global-color-gray-300);
113
114
  }
115
+ .spectrum-Button--secondary.new-styles.disabled {
116
+ color: var(--spectrum-global-color-gray-500);
117
+ }
114
118
  </style>
@@ -34,7 +34,6 @@
34
34
  display: none;
35
35
  }
36
36
  .main {
37
- font-family: var(--font-sans);
38
37
  padding: var(--spacing-xl);
39
38
  }
40
39
  .main :global(textarea) {
@@ -264,7 +264,7 @@
264
264
  max-height: 100%;
265
265
  }
266
266
  :global(.flatpickr-calendar) {
267
- font-family: "Source Sans Pro", sans-serif;
267
+ font-family: var(--font-sans);
268
268
  }
269
269
  .is-disabled {
270
270
  pointer-events: none !important;
@@ -2,12 +2,12 @@
2
2
  import "@spectrum-css/picker/dist/index-vars.css"
3
3
  import "@spectrum-css/popover/dist/index-vars.css"
4
4
  import "@spectrum-css/menu/dist/index-vars.css"
5
- import { fly } from "svelte/transition"
6
5
  import { createEventDispatcher } from "svelte"
7
6
  import clickOutside from "../../Actions/click_outside"
8
7
  import Search from "./Search.svelte"
9
8
  import Icon from "../../Icon/Icon.svelte"
10
9
  import StatusLight from "../../StatusLight/StatusLight.svelte"
10
+ import Popover from "../../Popover/Popover.svelte"
11
11
 
12
12
  export let id = null
13
13
  export let disabled = false
@@ -33,7 +33,10 @@
33
33
  export let sort = false
34
34
 
35
35
  const dispatch = createEventDispatcher()
36
+
36
37
  let searchTerm = null
38
+ let button
39
+ let popover
37
40
 
38
41
  $: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
39
42
  $: filteredOptions = getFilteredOptions(
@@ -76,77 +79,117 @@
76
79
  }
77
80
  </script>
78
81
 
79
- <div use:clickOutside={() => (open = false)}>
80
- <button
81
- {id}
82
- class="spectrum-Picker spectrum-Picker--sizeM"
83
- class:spectrum-Picker--quiet={quiet}
84
- {disabled}
85
- class:is-invalid={!!error}
86
- class:is-open={open}
87
- aria-haspopup="listbox"
88
- on:click={onClick}
89
- >
90
- {#if fieldIcon}
91
- <span class="option-extra">
92
- <Icon name={fieldIcon} />
93
- </span>
94
- {/if}
95
- {#if fieldColour}
96
- <span class="option-extra">
97
- <StatusLight square color={fieldColour} />
98
- </span>
99
- {/if}
100
- <span
101
- class="spectrum-Picker-label"
102
- class:is-placeholder={isPlaceholder}
103
- class:auto-width={autoWidth}
104
- >
105
- {fieldText}
82
+ <button
83
+ {id}
84
+ class="spectrum-Picker spectrum-Picker--sizeM"
85
+ class:spectrum-Picker--quiet={quiet}
86
+ {disabled}
87
+ class:is-invalid={!!error}
88
+ class:is-open={open}
89
+ aria-haspopup="listbox"
90
+ on:click={onClick}
91
+ use:clickOutside={() => (open = false)}
92
+ bind:this={button}
93
+ >
94
+ {#if fieldIcon}
95
+ <span class="option-extra icon">
96
+ <Icon size="S" name={fieldIcon} />
106
97
  </span>
107
- {#if error}
108
- <svg
109
- class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
110
- focusable="false"
111
- aria-hidden="true"
112
- aria-label="Folder"
113
- >
114
- <use xlink:href="#spectrum-icon-18-Alert" />
115
- </svg>
116
- {/if}
98
+ {/if}
99
+ {#if fieldColour}
100
+ <span class="option-extra">
101
+ <StatusLight square color={fieldColour} />
102
+ </span>
103
+ {/if}
104
+ <span
105
+ class="spectrum-Picker-label"
106
+ class:is-placeholder={isPlaceholder}
107
+ class:auto-width={autoWidth}
108
+ >
109
+ {fieldText}
110
+ </span>
111
+ {#if error}
117
112
  <svg
118
- class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
113
+ class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
119
114
  focusable="false"
120
115
  aria-hidden="true"
116
+ aria-label="Folder"
121
117
  >
122
- <use xlink:href="#spectrum-css-icon-Chevron100" />
118
+ <use xlink:href="#spectrum-icon-18-Alert" />
123
119
  </svg>
124
- </button>
125
- {#if open}
126
- <div
127
- transition:fly|local={{ y: -20, duration: 200 }}
128
- class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
129
- class:auto-width={autoWidth}
130
- >
131
- {#if autocomplete}
132
- <Search
133
- value={searchTerm}
134
- on:change={event => (searchTerm = event.detail)}
135
- {disabled}
136
- placeholder="Search"
137
- />
120
+ {/if}
121
+ <svg
122
+ class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
123
+ focusable="false"
124
+ aria-hidden="true"
125
+ >
126
+ <use xlink:href="#spectrum-css-icon-Chevron100" />
127
+ </svg>
128
+ </button>
129
+
130
+ <Popover
131
+ anchor={button}
132
+ align="left"
133
+ portalTarget={document.documentElement}
134
+ bind:this={popover}
135
+ {open}
136
+ on:close={() => (open = false)}
137
+ useAnchorWidth={!autoWidth}
138
+ maxWidth={autoWidth ? 400 : null}
139
+ >
140
+ <div class="popover-content" class:auto-width={autoWidth}>
141
+ {#if autocomplete}
142
+ <Search
143
+ value={searchTerm}
144
+ on:change={event => (searchTerm = event.detail)}
145
+ {disabled}
146
+ placeholder="Search"
147
+ />
148
+ {/if}
149
+ <ul class="spectrum-Menu" role="listbox">
150
+ {#if placeholderOption}
151
+ <li
152
+ class="spectrum-Menu-item placeholder"
153
+ class:is-selected={isPlaceholder}
154
+ role="option"
155
+ aria-selected="true"
156
+ tabindex="0"
157
+ on:click={() => onSelectOption(null)}
158
+ >
159
+ <span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
160
+ <svg
161
+ class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
162
+ focusable="false"
163
+ aria-hidden="true"
164
+ >
165
+ <use xlink:href="#spectrum-css-icon-Checkmark100" />
166
+ </svg>
167
+ </li>
138
168
  {/if}
139
- <ul class="spectrum-Menu" role="listbox">
140
- {#if placeholderOption}
169
+ {#if filteredOptions.length}
170
+ {#each filteredOptions as option, idx}
141
171
  <li
142
- class="spectrum-Menu-item placeholder"
143
- class:is-selected={isPlaceholder}
172
+ class="spectrum-Menu-item"
173
+ class:is-selected={isOptionSelected(getOptionValue(option, idx))}
144
174
  role="option"
145
175
  aria-selected="true"
146
176
  tabindex="0"
147
- on:click={() => onSelectOption(null)}
177
+ on:click={() => onSelectOption(getOptionValue(option, idx))}
178
+ class:is-disabled={!isOptionEnabled(option)}
148
179
  >
149
- <span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
180
+ {#if getOptionIcon(option, idx)}
181
+ <span class="option-extra icon">
182
+ <Icon size="S" name={getOptionIcon(option, idx)} />
183
+ </span>
184
+ {/if}
185
+ {#if getOptionColour(option, idx)}
186
+ <span class="option-extra">
187
+ <StatusLight square color={getOptionColour(option, idx)} />
188
+ </span>
189
+ {/if}
190
+ <span class="spectrum-Menu-itemLabel">
191
+ {getOptionLabel(option, idx)}
192
+ </span>
150
193
  <svg
151
194
  class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
152
195
  focusable="false"
@@ -155,61 +198,13 @@
155
198
  <use xlink:href="#spectrum-css-icon-Checkmark100" />
156
199
  </svg>
157
200
  </li>
158
- {/if}
159
- {#if filteredOptions.length}
160
- {#each filteredOptions as option, idx}
161
- <li
162
- class="spectrum-Menu-item"
163
- class:is-selected={isOptionSelected(getOptionValue(option, idx))}
164
- role="option"
165
- aria-selected="true"
166
- tabindex="0"
167
- on:click={() => onSelectOption(getOptionValue(option, idx))}
168
- class:is-disabled={!isOptionEnabled(option)}
169
- >
170
- {#if getOptionIcon(option, idx)}
171
- <span class="option-extra">
172
- <Icon name={getOptionIcon(option, idx)} />
173
- </span>
174
- {/if}
175
- {#if getOptionColour(option, idx)}
176
- <span class="option-extra">
177
- <StatusLight square color={getOptionColour(option, idx)} />
178
- </span>
179
- {/if}
180
- <span class="spectrum-Menu-itemLabel">
181
- {getOptionLabel(option, idx)}
182
- </span>
183
- <svg
184
- class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
185
- focusable="false"
186
- aria-hidden="true"
187
- >
188
- <use xlink:href="#spectrum-css-icon-Checkmark100" />
189
- </svg>
190
- </li>
191
- {/each}
192
- {/if}
193
- </ul>
194
- </div>
195
- {/if}
196
- </div>
201
+ {/each}
202
+ {/if}
203
+ </ul>
204
+ </div>
205
+ </Popover>
197
206
 
198
207
  <style>
199
- .spectrum-Popover {
200
- max-height: 240px;
201
- z-index: 999;
202
- top: 100%;
203
- }
204
- .spectrum-Popover:not(.auto-width) {
205
- width: 100%;
206
- }
207
- .spectrum-Popover.auto-width :global(.spectrum-Menu-itemLabel) {
208
- max-width: 400px;
209
- white-space: nowrap;
210
- overflow: hidden;
211
- text-overflow: ellipsis;
212
- }
213
208
  .spectrum-Picker {
214
209
  width: 100%;
215
210
  box-shadow: none;
@@ -229,9 +224,6 @@
229
224
  .spectrum-Picker-label.auto-width.is-placeholder {
230
225
  padding-right: 2px;
231
226
  }
232
- .auto-width .spectrum-Menu-item {
233
- padding-right: var(--spacing-xl);
234
- }
235
227
 
236
228
  /* Icon and colour alignment */
237
229
  .spectrum-Menu-checkmark {
@@ -241,27 +233,48 @@
241
233
  .option-extra {
242
234
  padding-right: 8px;
243
235
  }
236
+ .option-extra.icon {
237
+ margin: 0 -1px;
238
+ }
239
+
240
+ /* Popover */
241
+ .popover-content {
242
+ display: contents;
243
+ }
244
+ .popover-content.auto-width .spectrum-Menu-itemLabel {
245
+ white-space: nowrap;
246
+ overflow: hidden;
247
+ text-overflow: ellipsis;
248
+ }
249
+ .popover-content:not(.auto-width) .spectrum-Menu-itemLabel {
250
+ width: 0;
251
+ flex: 1 1 auto;
252
+ }
253
+ .popover-content.auto-width .spectrum-Menu-item {
254
+ padding-right: var(--spacing-xl);
255
+ }
256
+ .spectrum-Menu-item.is-disabled {
257
+ pointer-events: none;
258
+ }
244
259
 
245
- .spectrum-Popover :global(.spectrum-Search) {
260
+ /* Search styles inside popover */
261
+ .popover-content :global(.spectrum-Search) {
246
262
  margin-top: -1px;
247
263
  margin-left: -1px;
248
264
  width: calc(100% + 2px);
249
265
  }
250
- .spectrum-Popover :global(.spectrum-Search input) {
266
+ .popover-content :global(.spectrum-Search input) {
251
267
  height: auto;
252
268
  border-bottom-left-radius: 0;
253
269
  border-bottom-right-radius: 0;
254
270
  padding-top: var(--spectrum-global-dimension-size-100);
255
271
  padding-bottom: var(--spectrum-global-dimension-size-100);
256
272
  }
257
- .spectrum-Popover :global(.spectrum-Search .spectrum-ClearButton) {
273
+ .popover-content :global(.spectrum-Search .spectrum-ClearButton) {
258
274
  right: 1px;
259
275
  top: 2px;
260
276
  }
261
- .spectrum-Popover :global(.spectrum-Search .spectrum-Textfield-icon) {
277
+ .popover-content :global(.spectrum-Search .spectrum-Textfield-icon) {
262
278
  top: 9px;
263
279
  }
264
- .spectrum-Menu-item.is-disabled {
265
- pointer-events: none;
266
- }
267
280
  </style>
@@ -112,8 +112,4 @@
112
112
  .spectrum-Textfield {
113
113
  width: 100%;
114
114
  }
115
- input:disabled {
116
- color: var(--spectrum-global-color-gray-600) !important;
117
- -webkit-text-fill-color: var(--spectrum-global-color-gray-600) !important;
118
- }
119
115
  </style>
@@ -19,6 +19,7 @@
19
19
  .icon {
20
20
  width: 28px;
21
21
  height: 28px;
22
+ flex: 0 0 28px;
22
23
  display: grid;
23
24
  place-items: center;
24
25
  border-radius: 50%;
@@ -34,6 +35,7 @@
34
35
  .icon.size--S {
35
36
  width: 22px;
36
37
  height: 22px;
38
+ flex: 0 0 22px;
37
39
  }
38
40
  .icon.size--S :global(.spectrum-Icon) {
39
41
  width: 16px;
@@ -46,6 +48,7 @@
46
48
  .icon.size--L {
47
49
  width: 40px;
48
50
  height: 40px;
51
+ flex: 0 0 40px;
49
52
  }
50
53
  .icon.size--L :global(.spectrum-Icon) {
51
54
  width: 28px;
@@ -56,5 +56,6 @@
56
56
  --spectrum-semantic-positive-icon-color: #2d9d78;
57
57
  --spectrum-semantic-negative-icon-color: #e34850;
58
58
  min-width: 100px;
59
+ margin: 0;
59
60
  }
60
61
  </style>
@@ -21,6 +21,7 @@
21
21
  label {
22
22
  padding: 0;
23
23
  white-space: nowrap;
24
+ color: var(--spectrum-global-color-gray-600);
24
25
  }
25
26
 
26
27
  .muted {