@budibase/bbui 2.33.13 → 3.0.0

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.33.13",
4
+ "version": "3.0.0",
5
5
  "license": "MPL-2.0",
6
6
  "svelte": "src/index.js",
7
7
  "module": "dist/bbui.es.js",
@@ -35,8 +35,8 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@adobe/spectrum-css-workflow-icons": "1.2.1",
38
- "@budibase/shared-core": "2.33.13",
39
- "@budibase/string-templates": "2.33.13",
38
+ "@budibase/shared-core": "3.0.0",
39
+ "@budibase/string-templates": "3.0.0",
40
40
  "@spectrum-css/accordion": "3.0.24",
41
41
  "@spectrum-css/actionbutton": "1.0.1",
42
42
  "@spectrum-css/actiongroup": "1.0.1",
@@ -104,5 +104,5 @@
104
104
  }
105
105
  }
106
106
  },
107
- "gitHead": "ed7f4dc5c5d683fa08ba76c60d8304666e09bbfd"
107
+ "gitHead": "1272ac91c894cc5036dd188e969c77f33ed41ced"
108
108
  }
@@ -1,15 +1,11 @@
1
1
  <script>
2
2
  import "@spectrum-css/actionbutton/dist/index-vars.css"
3
- import { createEventDispatcher } from "svelte"
4
3
  import Tooltip from "../Tooltip/Tooltip.svelte"
5
4
  import { fade } from "svelte/transition"
6
-
7
- const dispatch = createEventDispatcher()
5
+ import { hexToRGBA } from "../helpers"
8
6
 
9
7
  export let quiet = false
10
- export let emphasized = false
11
8
  export let selected = false
12
- export let longPressable = false
13
9
  export let disabled = false
14
10
  export let icon = ""
15
11
  export let size = "M"
@@ -17,82 +13,64 @@
17
13
  export let fullWidth = false
18
14
  export let noPadding = false
19
15
  export let tooltip = ""
16
+ export let accentColor = null
20
17
 
21
18
  let showTooltip = false
22
19
 
23
- function longPress(element) {
24
- if (!longPressable) return
25
- let timer
26
-
27
- const listener = () => {
28
- timer = setTimeout(() => {
29
- dispatch("longpress")
30
- }, 700)
31
- }
32
-
33
- element.addEventListener("pointerdown", listener)
20
+ $: accentStyle = getAccentStyle(accentColor)
34
21
 
35
- return {
36
- destroy() {
37
- clearTimeout(timer)
38
- element.removeEventListener("pointerdown", longPress)
39
- },
22
+ const getAccentStyle = color => {
23
+ if (!color) {
24
+ return ""
40
25
  }
26
+ let style = ""
27
+ style += `--accent-bg-color:${hexToRGBA(color, 0.15)};`
28
+ style += `--accent-border-color:${hexToRGBA(color, 0.35)};`
29
+ return style
41
30
  }
42
31
  </script>
43
32
 
44
- <!-- svelte-ignore a11y-no-static-element-interactions -->
45
- <span
46
- class="btn-wrap"
33
+ <button
34
+ class="spectrum-ActionButton spectrum-ActionButton--size{size}"
35
+ class:spectrum-ActionButton--quiet={quiet}
36
+ class:is-selected={selected}
37
+ class:noPadding
38
+ class:fullWidth
39
+ class:active
40
+ class:disabled
41
+ class:accent={accentColor != null}
42
+ on:click|preventDefault
47
43
  on:mouseover={() => (showTooltip = true)}
48
44
  on:mouseleave={() => (showTooltip = false)}
49
45
  on:focus={() => (showTooltip = true)}
46
+ {disabled}
47
+ style={accentStyle}
50
48
  >
51
- <button
52
- use:longPress
53
- class:spectrum-ActionButton--quiet={quiet}
54
- class:spectrum-ActionButton--emphasized={emphasized}
55
- class:is-selected={selected}
56
- class:noPadding
57
- class:fullWidth
58
- class="spectrum-ActionButton spectrum-ActionButton--size{size}"
59
- class:active
60
- class:disabled
61
- {disabled}
62
- on:longPress
63
- on:click|preventDefault
64
- >
65
- {#if longPressable}
66
- <svg
67
- class="spectrum-Icon spectrum-UIIcon-CornerTriangle100 spectrum-ActionButton-hold"
68
- focusable="false"
69
- aria-hidden="true"
70
- >
71
- <use xlink:href="#spectrum-css-icon-CornerTriangle100" />
72
- </svg>
73
- {/if}
74
- {#if icon}
75
- <svg
76
- class="spectrum-Icon spectrum-Icon--sizeS"
77
- focusable="false"
78
- aria-hidden="true"
79
- aria-label={icon}
80
- >
81
- <use xlink:href="#spectrum-icon-18-{icon}" />
82
- </svg>
83
- {/if}
84
- {#if $$slots}
85
- <span class="spectrum-ActionButton-label"><slot /></span>
86
- {/if}
87
- {#if tooltip && showTooltip}
88
- <div class="tooltip" in:fade={{ duration: 130, delay: 250 }}>
89
- <Tooltip textWrapping direction="bottom" text={tooltip} />
90
- </div>
91
- {/if}
92
- </button>
93
- </span>
49
+ {#if icon}
50
+ <svg
51
+ class="spectrum-Icon spectrum-Icon--sizeS"
52
+ focusable="false"
53
+ aria-hidden="true"
54
+ aria-label={icon}
55
+ >
56
+ <use xlink:href="#spectrum-icon-18-{icon}" />
57
+ </svg>
58
+ {/if}
59
+ {#if $$slots}
60
+ <span class="spectrum-ActionButton-label"><slot /></span>
61
+ {/if}
62
+ {#if tooltip && showTooltip}
63
+ <div class="tooltip" in:fade={{ duration: 130, delay: 250 }}>
64
+ <Tooltip textWrapping direction="bottom" text={tooltip} />
65
+ </div>
66
+ {/if}
67
+ </button>
94
68
 
95
69
  <style>
70
+ button {
71
+ transition: filter 130ms ease-out, background 130ms ease-out,
72
+ border 130ms ease-out, color 130ms ease-out;
73
+ }
96
74
  .fullWidth {
97
75
  width: 100%;
98
76
  }
@@ -104,9 +82,7 @@
104
82
  margin-left: 0;
105
83
  transition: color ease-out 130ms;
106
84
  }
107
- .is-selected:not(.spectrum-ActionButton--emphasized):not(
108
- .spectrum-ActionButton--quiet
109
- ) {
85
+ .is-selected:not(.spectrum-ActionButton--quiet) {
110
86
  background: var(--spectrum-global-color-gray-300);
111
87
  border-color: var(--spectrum-global-color-gray-500);
112
88
  }
@@ -115,12 +91,13 @@
115
91
  }
116
92
  .spectrum-ActionButton--quiet.is-selected {
117
93
  color: var(--spectrum-global-color-gray-900);
94
+ background: var(--spectrum-global-color-gray-300);
118
95
  }
119
96
  .noPadding {
120
97
  padding: 0;
121
98
  min-width: 0;
122
99
  }
123
- .is-selected:not(.emphasized) .spectrum-Icon {
100
+ .is-selected .spectrum-Icon {
124
101
  color: var(--spectrum-global-color-gray-900);
125
102
  }
126
103
  .is-selected.disabled .spectrum-Icon {
@@ -137,4 +114,12 @@
137
114
  text-align: center;
138
115
  z-index: 1;
139
116
  }
117
+ .accent.is-selected,
118
+ .accent:active {
119
+ border: 1px solid var(--accent-border-color);
120
+ background: var(--accent-bg-color);
121
+ }
122
+ .accent:hover {
123
+ filter: brightness(1.2);
124
+ }
140
125
  </style>
@@ -1,14 +1,20 @@
1
1
  <script>
2
- import { setContext } from "svelte"
2
+ import { setContext, getContext } from "svelte"
3
3
  import Popover from "../Popover/Popover.svelte"
4
4
  import Menu from "../Menu/Menu.svelte"
5
5
 
6
6
  export let disabled = false
7
7
  export let align = "left"
8
8
  export let portalTarget
9
+ export let openOnHover = false
10
+ export let animate
11
+ export let offset
12
+
13
+ const actionMenuContext = getContext("actionMenu")
9
14
 
10
15
  let anchor
11
16
  let dropdown
17
+ let timeout
12
18
 
13
19
  // This is needed because display: contents is considered "invisible".
14
20
  // It should only ever be an action button, so should be fine.
@@ -16,11 +22,19 @@
16
22
  anchor = node.firstChild
17
23
  }
18
24
 
25
+ export const show = () => {
26
+ cancelHide()
27
+ dropdown.show()
28
+ }
29
+
19
30
  export const hide = () => {
20
31
  dropdown.hide()
21
32
  }
22
- export const show = () => {
23
- dropdown.show()
33
+
34
+ // Hides this menu and all parent menus
35
+ const hideAll = () => {
36
+ hide()
37
+ actionMenuContext?.hide()
24
38
  }
25
39
 
26
40
  const openMenu = event => {
@@ -30,12 +44,25 @@
30
44
  }
31
45
  }
32
46
 
33
- setContext("actionMenu", { show, hide })
47
+ const queueHide = () => {
48
+ timeout = setTimeout(hide, 10)
49
+ }
50
+
51
+ const cancelHide = () => {
52
+ clearTimeout(timeout)
53
+ }
54
+
55
+ setContext("actionMenu", { show, hide, hideAll })
34
56
  </script>
35
57
 
36
58
  <!-- svelte-ignore a11y-no-static-element-interactions -->
37
59
  <!-- svelte-ignore a11y-click-events-have-key-events -->
38
- <div use:getAnchor on:click={openMenu}>
60
+ <div
61
+ use:getAnchor
62
+ on:click={openOnHover ? null : openMenu}
63
+ on:mouseenter={openOnHover ? show : null}
64
+ on:mouseleave={openOnHover ? queueHide : null}
65
+ >
39
66
  <slot name="control" />
40
67
  </div>
41
68
  <Popover
@@ -43,9 +70,13 @@
43
70
  {anchor}
44
71
  {align}
45
72
  {portalTarget}
73
+ {animate}
74
+ {offset}
46
75
  resizable={false}
47
76
  on:open
48
77
  on:close
78
+ on:mouseenter={openOnHover ? cancelHide : null}
79
+ on:mouseleave={openOnHover ? queueHide : null}
49
80
  >
50
81
  <Menu>
51
82
  <slot />
@@ -151,9 +151,9 @@ export default function positionDropdown(element, opts) {
151
151
  // Determine X strategy
152
152
  if (align === "right") {
153
153
  applyXStrategy(Strategies.EndToEnd)
154
- } else if (align === "right-outside") {
154
+ } else if (align === "right-outside" || align === "right-context-menu") {
155
155
  applyXStrategy(Strategies.StartToEnd)
156
- } else if (align === "left-outside") {
156
+ } else if (align === "left-outside" || align === "left-context-menu") {
157
157
  applyXStrategy(Strategies.EndToStart)
158
158
  } else if (align === "center") {
159
159
  applyXStrategy(Strategies.MidPoint)
@@ -164,6 +164,12 @@ export default function positionDropdown(element, opts) {
164
164
  // Determine Y strategy
165
165
  if (align === "right-outside" || align === "left-outside") {
166
166
  applyYStrategy(Strategies.MidPoint)
167
+ } else if (
168
+ align === "right-context-menu" ||
169
+ align === "left-context-menu"
170
+ ) {
171
+ applyYStrategy(Strategies.StartToStart)
172
+ styles.top -= 5 // Manual adjustment for action menu padding
167
173
  } else {
168
174
  applyYStrategy(Strategies.StartToEnd)
169
175
  }
@@ -240,7 +246,7 @@ export default function positionDropdown(element, opts) {
240
246
  }
241
247
 
242
248
  // Apply initial styles which don't need to change
243
- element.style.position = "absolute"
249
+ element.style.position = "fixed"
244
250
  element.style.zIndex = "9999"
245
251
 
246
252
  // Set up a scroll listener
@@ -17,6 +17,8 @@
17
17
  export let tooltip = undefined
18
18
  export let newStyles = true
19
19
  export let id
20
+ export let ref
21
+ export let reverse = false
20
22
 
21
23
  const dispatch = createEventDispatcher()
22
24
  </script>
@@ -25,6 +27,7 @@
25
27
  <button
26
28
  {id}
27
29
  {type}
30
+ bind:this={ref}
28
31
  class:spectrum-Button--cta={cta}
29
32
  class:spectrum-Button--primary={primary}
30
33
  class:spectrum-Button--secondary={secondary}
@@ -41,6 +44,9 @@
41
44
  }
42
45
  }}
43
46
  >
47
+ {#if $$slots && reverse}
48
+ <span class="spectrum-Button-label"><slot /></span>
49
+ {/if}
44
50
  {#if icon}
45
51
  <svg
46
52
  class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
@@ -51,7 +57,7 @@
51
57
  <use xlink:href="#spectrum-icon-18-{icon}" />
52
58
  </svg>
53
59
  {/if}
54
- {#if $$slots}
60
+ {#if $$slots && !reverse}
55
61
  <span class="spectrum-Button-label"><slot /></span>
56
62
  {/if}
57
63
  </button>
@@ -91,4 +97,11 @@
91
97
  .spectrum-Button--secondary.new-styles.is-disabled {
92
98
  color: var(--spectrum-global-color-gray-500);
93
99
  }
100
+ .spectrum-Button .spectrum-Button-label + .spectrum-Icon {
101
+ margin-left: var(--spectrum-button-primary-icon-gap);
102
+ margin-right: calc(
103
+ -1 * (var(--spectrum-button-primary-textonly-padding-left-adjusted) -
104
+ var(--spectrum-button-primary-padding-left-adjusted))
105
+ );
106
+ }
94
107
  </style>
@@ -0,0 +1,57 @@
1
+ <script>
2
+ import Button from "../Button/Button.svelte"
3
+ import Popover from "../Popover/Popover.svelte"
4
+ import Menu from "../Menu/Menu.svelte"
5
+ import MenuItem from "../Menu/Item.svelte"
6
+
7
+ export let buttons
8
+ export let text = "Action"
9
+ export let size = "M"
10
+ export let align = "left"
11
+ export let offset
12
+ export let animate
13
+ export let quiet = false
14
+
15
+ let anchor
16
+ let popover
17
+
18
+ const handleClick = async button => {
19
+ popover.hide()
20
+ await button.onClick?.()
21
+ }
22
+ </script>
23
+
24
+ <Button
25
+ bind:ref={anchor}
26
+ {size}
27
+ icon="ChevronDown"
28
+ {quiet}
29
+ primary={quiet}
30
+ cta={!quiet}
31
+ newStyles={!quiet}
32
+ on:click={() => popover?.show()}
33
+ on:click
34
+ reverse
35
+ >
36
+ {text || "Action"}
37
+ </Button>
38
+ <Popover
39
+ bind:this={popover}
40
+ {align}
41
+ {anchor}
42
+ {offset}
43
+ {animate}
44
+ resizable={false}
45
+ on:close
46
+ on:open
47
+ on:mouseenter
48
+ on:mouseleave
49
+ >
50
+ <Menu>
51
+ {#each buttons as button}
52
+ <MenuItem on:click={() => handleClick(button)} disabled={button.disabled}>
53
+ {button.text || "Button"}
54
+ </MenuItem>
55
+ {/each}
56
+ </Menu>
57
+ </Popover>
@@ -19,6 +19,7 @@
19
19
  {disabled}
20
20
  on:change={onChange}
21
21
  on:click
22
+ on:click|stopPropagation
22
23
  {id}
23
24
  type="checkbox"
24
25
  class="spectrum-Switch-input"
@@ -1,6 +1,6 @@
1
1
  <script>
2
2
  import "@spectrum-css/textfield/dist/index-vars.css"
3
- import { createEventDispatcher, onMount } from "svelte"
3
+ import { createEventDispatcher, onMount, tick } from "svelte"
4
4
 
5
5
  export let value = null
6
6
  export let placeholder = null
@@ -68,10 +68,13 @@
68
68
  return type === "number" ? "decimal" : "text"
69
69
  }
70
70
 
71
- onMount(() => {
71
+ onMount(async () => {
72
72
  if (disabled) return
73
73
  focus = autofocus
74
- if (focus) field.focus()
74
+ if (focus) {
75
+ await tick()
76
+ field.focus()
77
+ }
75
78
  })
76
79
  </script>
77
80
 
@@ -60,10 +60,11 @@
60
60
  .newStyles {
61
61
  color: var(--spectrum-global-color-gray-700);
62
62
  }
63
-
63
+ svg {
64
+ transition: color var(--spectrum-global-animation-duration-100, 130ms);
65
+ }
64
66
  svg.hoverable {
65
67
  pointer-events: all;
66
- transition: color var(--spectrum-global-animation-duration-100, 130ms);
67
68
  }
68
69
  svg.hoverable:hover {
69
70
  color: var(--hover-color) !important;
@@ -8,6 +8,7 @@
8
8
  export let onConfirm = undefined
9
9
  export let buttonText = ""
10
10
  export let cta = false
11
+
11
12
  $: icon = selectIcon(type)
12
13
  // if newlines used, convert them to different elements
13
14
  $: split = message.split("\n")
@@ -1,55 +1,68 @@
1
1
  <script>
2
- import Body from "../Typography/Body.svelte"
3
- import IconAvatar from "../Icon/IconAvatar.svelte"
4
- import Label from "../Label/Label.svelte"
5
- import Avatar from "../Avatar/Avatar.svelte"
2
+ import Icon from "../Icon/Icon.svelte"
3
+ import StatusLight from "../StatusLight/StatusLight.svelte"
6
4
 
7
5
  export let icon = null
8
- export let iconBackground = null
9
6
  export let iconColor = null
10
- export let avatar = false
11
7
  export let title = null
12
8
  export let subtitle = null
9
+ export let url = null
13
10
  export let hoverable = false
14
-
15
- $: initials = avatar ? title?.[0] : null
11
+ export let showArrow = false
12
+ export let selected = false
16
13
  </script>
17
14
 
18
- <!-- svelte-ignore a11y-no-static-element-interactions -->
19
- <!-- svelte-ignore a11y-click-events-have-key-events -->
20
- <div class="list-item" class:hoverable on:click>
21
- <div class="left">
22
- {#if icon}
23
- <IconAvatar {icon} color={iconColor} background={iconBackground} />
24
- {/if}
25
- {#if avatar}
26
- <Avatar {initials} />
15
+ <a
16
+ href={url}
17
+ class="list-item"
18
+ class:hoverable={hoverable || url != null}
19
+ class:large={!!subtitle}
20
+ on:click
21
+ class:selected
22
+ >
23
+ <div class="list-item__left">
24
+ {#if icon === "StatusLight"}
25
+ <StatusLight square size="L" color={iconColor} />
26
+ {:else if icon}
27
+ <div class="list-item__icon">
28
+ <Icon name={icon} color={iconColor} size={subtitle ? "XL" : "M"} />
29
+ </div>
27
30
  {/if}
28
- {#if title}
29
- <Body>{title}</Body>
30
- {/if}
31
- {#if subtitle}
32
- <Label>{subtitle}</Label>
31
+ <div class="list-item__text">
32
+ {#if title}
33
+ <div class="list-item__title">
34
+ {title}
35
+ </div>
36
+ {/if}
37
+ {#if subtitle}
38
+ <div class="list-item__subtitle">
39
+ {subtitle}
40
+ </div>
41
+ {/if}
42
+ </div>
43
+ </div>
44
+ <div class="list-item__right">
45
+ <slot name="right" />
46
+ {#if showArrow}
47
+ <Icon name="ChevronRight" />
33
48
  {/if}
34
49
  </div>
35
- {#if $$slots.default}
36
- <div class="right">
37
- <slot />
38
- </div>
39
- {/if}
40
- </div>
50
+ </a>
41
51
 
42
52
  <style>
43
53
  .list-item {
44
- padding: 0 16px;
45
- height: 56px;
46
- background: var(--spectrum-global-color-gray-50);
54
+ padding: var(--spacing-m) var(--spacing-l);
55
+ background: var(--spectrum-global-color-gray-75);
47
56
  display: flex;
48
57
  flex-direction: row;
49
58
  justify-content: space-between;
50
59
  border: 1px solid var(--spectrum-global-color-gray-300);
51
- transition: background 130ms ease-out;
60
+ transition: background 130ms ease-out, border-color 130ms ease-out;
52
61
  gap: var(--spacing-m);
62
+ color: var(--spectrum-global-color-gray-800);
63
+ cursor: pointer;
64
+ position: relative;
65
+ box-sizing: border-box;
53
66
  }
54
67
  .list-item:not(:first-child) {
55
68
  border-top: none;
@@ -64,32 +77,87 @@
64
77
  }
65
78
  .hoverable:hover {
66
79
  cursor: pointer;
67
- background: var(--spectrum-global-color-gray-75);
68
80
  }
69
- .left,
70
- .right {
81
+ .hoverable:not(.selected):hover {
82
+ background: var(--spectrum-global-color-gray-200);
83
+ border-color: var(--spectrum-global-color-gray-400);
84
+ }
85
+ .selected {
86
+ background: var(--spectrum-global-color-blue-100);
87
+ }
88
+
89
+ /* Selection is only meant for standalone list items (non stacked) so we just set a fixed border radius */
90
+ .list-item.selected {
91
+ background-color: var(--spectrum-global-color-blue-100);
92
+ border-color: var(--spectrum-global-color-blue-100);
93
+ }
94
+ .list-item.selected:after {
95
+ content: "";
96
+ position: absolute;
97
+ height: 100%;
98
+ width: 100%;
99
+ border: 1px solid var(--spectrum-global-color-blue-400);
100
+ pointer-events: none;
101
+ top: 0;
102
+ left: 0;
103
+ border-radius: 4px;
104
+ box-sizing: border-box;
105
+ z-index: 1;
106
+ opacity: 0.5;
107
+ }
108
+
109
+ /* Large icons */
110
+ .list-item.large .list-item__icon {
111
+ background-color: var(--spectrum-global-color-gray-200);
112
+ padding: 4px;
113
+ border-radius: 4px;
114
+ border: 1px solid var(--spectrum-global-color-gray-300);
115
+ transition: background-color 130ms ease-out, border-color 130ms ease-out,
116
+ color 130ms ease-out;
117
+ }
118
+ .list-item.large.hoverable:not(.selected):hover .list-item__icon {
119
+ background-color: var(--spectrum-global-color-gray-300);
120
+ }
121
+ .list-item.large.selected .list-item__icon {
122
+ background-color: var(--spectrum-global-color-blue-400);
123
+ color: white;
124
+ border-color: var(--spectrum-global-color-blue-100);
125
+ }
126
+
127
+ /* Internal layout */
128
+ .list-item__left,
129
+ .list-item__right {
71
130
  display: flex;
72
131
  flex-direction: row;
73
132
  align-items: center;
74
- gap: var(--spacing-xl);
133
+ gap: var(--spacing-m);
134
+ }
135
+ .list-item.large .list-item__left,
136
+ .list-item.large .list-item__right {
137
+ gap: var(--spacing-m);
75
138
  }
76
- .left {
139
+ .list-item__left {
77
140
  width: 0;
78
141
  flex: 1 1 auto;
79
142
  }
80
- .right {
81
- flex: 0 0 auto;
82
- }
83
- .list-item :global(.spectrum-Icon),
84
- .list-item :global(.spectrum-Avatar) {
143
+ .list-item__right {
85
144
  flex: 0 0 auto;
145
+ color: var(--spectrum-global-color-gray-600);
86
146
  }
87
- .list-item :global(.spectrum-Body) {
88
- color: var(--spectrum-global-color-gray-900);
147
+
148
+ /* Text */
149
+ .list-item__text {
150
+ flex: 1 1 auto;
151
+ width: 0;
89
152
  }
90
- .list-item :global(.spectrum-Body) {
153
+ .list-item__title,
154
+ .list-item__subtitle {
91
155
  white-space: nowrap;
92
156
  overflow: hidden;
93
157
  text-overflow: ellipsis;
94
158
  }
159
+ .list-item__subtitle {
160
+ color: var(--spectrum-global-color-gray-700);
161
+ font-size: 12px;
162
+ }
95
163
  </style>