@budibase/bbui 2.2.12-alpha.3 → 2.2.12-alpha.31

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.3",
4
+ "version": "2.2.12-alpha.31",
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.3",
41
+ "@budibase/string-templates": "2.2.12-alpha.31",
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": "55f2a50064a4670de13e964f3b40c9f05189c5d6"
91
+ "gitHead": "f06b539fceaa40c8e8e95d206212b3fc41989cff"
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(
@@ -42,7 +45,9 @@
42
45
  getOptionLabel
43
46
  )
44
47
 
45
- const onClick = () => {
48
+ const onClick = e => {
49
+ e.preventDefault()
50
+ e.stopPropagation()
46
51
  dispatch("click")
47
52
  if (readonly) {
48
53
  return
@@ -76,77 +81,119 @@
76
81
  }
77
82
  </script>
78
83
 
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}
84
+ <button
85
+ {id}
86
+ class="spectrum-Picker spectrum-Picker--sizeM"
87
+ class:spectrum-Picker--quiet={quiet}
88
+ {disabled}
89
+ class:is-invalid={!!error}
90
+ class:is-open={open}
91
+ aria-haspopup="listbox"
92
+ on:click={onClick}
93
+ bind:this={button}
94
+ >
95
+ {#if fieldIcon}
96
+ <span class="option-extra icon">
97
+ <Icon size="S" name={fieldIcon} />
106
98
  </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}
99
+ {/if}
100
+ {#if fieldColour}
101
+ <span class="option-extra">
102
+ <StatusLight square color={fieldColour} />
103
+ </span>
104
+ {/if}
105
+ <span
106
+ class="spectrum-Picker-label"
107
+ class:is-placeholder={isPlaceholder}
108
+ class:auto-width={autoWidth}
109
+ >
110
+ {fieldText}
111
+ </span>
112
+ {#if error}
117
113
  <svg
118
- class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
114
+ class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
119
115
  focusable="false"
120
116
  aria-hidden="true"
117
+ aria-label="Folder"
121
118
  >
122
- <use xlink:href="#spectrum-css-icon-Chevron100" />
119
+ <use xlink:href="#spectrum-icon-18-Alert" />
123
120
  </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
- />
121
+ {/if}
122
+ <svg
123
+ class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
124
+ focusable="false"
125
+ aria-hidden="true"
126
+ >
127
+ <use xlink:href="#spectrum-css-icon-Chevron100" />
128
+ </svg>
129
+ </button>
130
+
131
+ <Popover
132
+ anchor={button}
133
+ align="left"
134
+ bind:this={popover}
135
+ {open}
136
+ on:close={() => (open = false)}
137
+ useAnchorWidth={!autoWidth}
138
+ maxWidth={autoWidth ? 400 : null}
139
+ >
140
+ <div
141
+ class="popover-content"
142
+ class:auto-width={autoWidth}
143
+ use:clickOutside={() => (open = false)}
144
+ >
145
+ {#if autocomplete}
146
+ <Search
147
+ value={searchTerm}
148
+ on:change={event => (searchTerm = event.detail)}
149
+ {disabled}
150
+ placeholder="Search"
151
+ />
152
+ {/if}
153
+ <ul class="spectrum-Menu" role="listbox">
154
+ {#if placeholderOption}
155
+ <li
156
+ class="spectrum-Menu-item placeholder"
157
+ class:is-selected={isPlaceholder}
158
+ role="option"
159
+ aria-selected="true"
160
+ tabindex="0"
161
+ on:click={() => onSelectOption(null)}
162
+ >
163
+ <span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
164
+ <svg
165
+ class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
166
+ focusable="false"
167
+ aria-hidden="true"
168
+ >
169
+ <use xlink:href="#spectrum-css-icon-Checkmark100" />
170
+ </svg>
171
+ </li>
138
172
  {/if}
139
- <ul class="spectrum-Menu" role="listbox">
140
- {#if placeholderOption}
173
+ {#if filteredOptions.length}
174
+ {#each filteredOptions as option, idx}
141
175
  <li
142
- class="spectrum-Menu-item placeholder"
143
- class:is-selected={isPlaceholder}
176
+ class="spectrum-Menu-item"
177
+ class:is-selected={isOptionSelected(getOptionValue(option, idx))}
144
178
  role="option"
145
179
  aria-selected="true"
146
180
  tabindex="0"
147
- on:click={() => onSelectOption(null)}
181
+ on:click={() => onSelectOption(getOptionValue(option, idx))}
182
+ class:is-disabled={!isOptionEnabled(option)}
148
183
  >
149
- <span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
184
+ {#if getOptionIcon(option, idx)}
185
+ <span class="option-extra icon">
186
+ <Icon size="S" name={getOptionIcon(option, idx)} />
187
+ </span>
188
+ {/if}
189
+ {#if getOptionColour(option, idx)}
190
+ <span class="option-extra">
191
+ <StatusLight square color={getOptionColour(option, idx)} />
192
+ </span>
193
+ {/if}
194
+ <span class="spectrum-Menu-itemLabel">
195
+ {getOptionLabel(option, idx)}
196
+ </span>
150
197
  <svg
151
198
  class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
152
199
  focusable="false"
@@ -155,61 +202,13 @@
155
202
  <use xlink:href="#spectrum-css-icon-Checkmark100" />
156
203
  </svg>
157
204
  </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>
205
+ {/each}
206
+ {/if}
207
+ </ul>
208
+ </div>
209
+ </Popover>
197
210
 
198
211
  <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
212
  .spectrum-Picker {
214
213
  width: 100%;
215
214
  box-shadow: none;
@@ -229,9 +228,6 @@
229
228
  .spectrum-Picker-label.auto-width.is-placeholder {
230
229
  padding-right: 2px;
231
230
  }
232
- .auto-width .spectrum-Menu-item {
233
- padding-right: var(--spacing-xl);
234
- }
235
231
 
236
232
  /* Icon and colour alignment */
237
233
  .spectrum-Menu-checkmark {
@@ -241,27 +237,48 @@
241
237
  .option-extra {
242
238
  padding-right: 8px;
243
239
  }
240
+ .option-extra.icon {
241
+ margin: 0 -1px;
242
+ }
243
+
244
+ /* Popover */
245
+ .popover-content {
246
+ display: contents;
247
+ }
248
+ .popover-content.auto-width .spectrum-Menu-itemLabel {
249
+ white-space: nowrap;
250
+ overflow: hidden;
251
+ text-overflow: ellipsis;
252
+ }
253
+ .popover-content:not(.auto-width) .spectrum-Menu-itemLabel {
254
+ width: 0;
255
+ flex: 1 1 auto;
256
+ }
257
+ .popover-content.auto-width .spectrum-Menu-item {
258
+ padding-right: var(--spacing-xl);
259
+ }
260
+ .spectrum-Menu-item.is-disabled {
261
+ pointer-events: none;
262
+ }
244
263
 
245
- .spectrum-Popover :global(.spectrum-Search) {
264
+ /* Search styles inside popover */
265
+ .popover-content :global(.spectrum-Search) {
246
266
  margin-top: -1px;
247
267
  margin-left: -1px;
248
268
  width: calc(100% + 2px);
249
269
  }
250
- .spectrum-Popover :global(.spectrum-Search input) {
270
+ .popover-content :global(.spectrum-Search input) {
251
271
  height: auto;
252
272
  border-bottom-left-radius: 0;
253
273
  border-bottom-right-radius: 0;
254
274
  padding-top: var(--spectrum-global-dimension-size-100);
255
275
  padding-bottom: var(--spectrum-global-dimension-size-100);
256
276
  }
257
- .spectrum-Popover :global(.spectrum-Search .spectrum-ClearButton) {
277
+ .popover-content :global(.spectrum-Search .spectrum-ClearButton) {
258
278
  right: 1px;
259
279
  top: 2px;
260
280
  }
261
- .spectrum-Popover :global(.spectrum-Search .spectrum-Textfield-icon) {
281
+ .popover-content :global(.spectrum-Search .spectrum-Textfield-icon) {
262
282
  top: 9px;
263
283
  }
264
- .spectrum-Menu-item.is-disabled {
265
- pointer-events: none;
266
- }
267
284
  </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 {