@budibase/bbui 2.26.3 → 2.27.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.
@@ -1,78 +1,123 @@
1
1
  <script>
2
- import { getContext, onMount, createEventDispatcher } from "svelte"
2
+ import { getContext, onDestroy, createEventDispatcher } from "svelte"
3
3
  import Portal from "svelte-portal"
4
4
 
5
5
  export let title
6
6
  export let icon = ""
7
7
  export let id
8
+ export let href = "#"
9
+ export let link = false
8
10
 
9
11
  const dispatch = createEventDispatcher()
10
12
  let selected = getContext("tab")
11
- let tab_internal
12
- let tabInfo
13
+ let observer
14
+ let ref
15
+
16
+ $: isSelected = $selected.title === title
17
+ $: {
18
+ if (isSelected && ref) {
19
+ observe()
20
+ } else {
21
+ stopObserving()
22
+ }
23
+ }
13
24
 
14
25
  const setTabInfo = () => {
15
- // If the tabs are being rendered inside a component which uses
16
- // a svelte transition to enter, then this initial getBoundingClientRect
17
- // will return an incorrect position.
18
- // We just need to get this off the main thread to fix this, by using
19
- // a 0ms timeout.
20
- setTimeout(() => {
21
- tabInfo = tab_internal?.getBoundingClientRect()
22
- if (tabInfo && $selected.title === title) {
23
- $selected.info = tabInfo
24
- }
25
- }, 0)
26
+ const tabInfo = ref?.getBoundingClientRect()
27
+ if (tabInfo) {
28
+ $selected.info = tabInfo
29
+ }
26
30
  }
27
31
 
28
- onMount(() => {
29
- setTabInfo()
30
- })
32
+ const onAnchorClick = e => {
33
+ if (e.metaKey || e.shiftKey || e.altKey || e.ctrlKey) return
31
34
 
32
- //Ensure that the underline is in the correct location
33
- $: {
34
- if ($selected.title === title && tab_internal) {
35
- if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) {
36
- setTabInfo()
37
- }
35
+ e.preventDefault()
36
+ $selected = {
37
+ ...$selected,
38
+ title,
39
+ info: ref.getBoundingClientRect(),
38
40
  }
41
+ dispatch("click")
39
42
  }
40
43
 
41
44
  const onClick = () => {
42
45
  $selected = {
43
46
  ...$selected,
44
47
  title,
45
- info: tab_internal.getBoundingClientRect(),
48
+ info: ref.getBoundingClientRect(),
46
49
  }
47
- dispatch("click")
48
50
  }
51
+
52
+ const observe = () => {
53
+ if (!observer) {
54
+ observer = new ResizeObserver(setTabInfo)
55
+ observer.observe(ref)
56
+ }
57
+ }
58
+
59
+ const stopObserving = () => {
60
+ if (observer) {
61
+ observer.unobserve(ref)
62
+ observer = null
63
+ }
64
+ }
65
+
66
+ onDestroy(stopObserving)
49
67
  </script>
50
68
 
51
- <!-- svelte-ignore a11y-no-static-element-interactions -->
52
- <!-- svelte-ignore a11y-click-events-have-key-events -->
53
- <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
54
- <div
55
- {id}
56
- bind:this={tab_internal}
57
- on:click={onClick}
58
- class:is-selected={$selected.title === title}
59
- class="spectrum-Tabs-item"
60
- class:emphasized={$selected.title === title && $selected.emphasized}
61
- tabindex="0"
62
- >
63
- {#if icon}
64
- <svg
65
- class="spectrum-Icon spectrum-Icon--sizeM"
66
- focusable="false"
67
- aria-hidden="true"
68
- aria-label="Folder"
69
- >
70
- <use xlink:href="#spectrum-icon-18-{icon}" />
71
- </svg>
72
- {/if}
73
- <span class="spectrum-Tabs-itemLabel">{title}</span>
74
- </div>
75
- {#if $selected.title === title}
69
+ {#if link}
70
+ <a
71
+ {href}
72
+ {id}
73
+ bind:this={ref}
74
+ on:click={onAnchorClick}
75
+ class="spectrum-Tabs-item link"
76
+ class:is-selected={isSelected}
77
+ class:emphasized={isSelected && $selected.emphasized}
78
+ tabindex="0"
79
+ >
80
+ {#if icon}
81
+ <svg
82
+ class="spectrum-Icon spectrum-Icon--sizeM"
83
+ focusable="false"
84
+ aria-hidden="true"
85
+ aria-label="Folder"
86
+ >
87
+ <use xlink:href="#spectrum-icon-18-{icon}" />
88
+ </svg>
89
+ {/if}
90
+ <span class="spectrum-Tabs-itemLabel">{title}</span>
91
+ </a>
92
+ {:else}
93
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
94
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
95
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
96
+ <div
97
+ {id}
98
+ bind:this={ref}
99
+ on:click={onClick}
100
+ on:click
101
+ class="spectrum-Tabs-item"
102
+ class:is-selected={isSelected}
103
+ class:emphasized={isSelected && $selected.emphasized}
104
+ tabindex="0"
105
+ >
106
+ {#if icon}
107
+ <svg
108
+ class="spectrum-Icon spectrum-Icon--sizeM"
109
+ focusable="false"
110
+ aria-hidden="true"
111
+ aria-label="Folder"
112
+ >
113
+ <use xlink:href="#spectrum-icon-18-{icon}" />
114
+ </svg>
115
+ {/if}
116
+ <span class="spectrum-Tabs-itemLabel">{title}</span>
117
+ </div>
118
+ {/if}
119
+
120
+ {#if isSelected}
76
121
  <Portal target=".spectrum-Tabs-content-{$selected.id}">
77
122
  <slot />
78
123
  </Portal>
@@ -89,4 +134,7 @@
89
134
  .spectrum-Tabs-item:hover {
90
135
  color: var(--spectrum-global-color-gray-900);
91
136
  }
137
+ .link {
138
+ user-select: none;
139
+ }
92
140
  </style>
@@ -0,0 +1,79 @@
1
+ <script>
2
+ import Portal from "svelte-portal"
3
+ import { getContext } from "svelte"
4
+ import Context from "../context"
5
+
6
+ export let anchor
7
+ export let visible = false
8
+ export let offset = 0
9
+
10
+ $: target = getContext(Context.PopoverRoot) || "#app"
11
+
12
+ let hovering = false
13
+ let tooltip
14
+ let x = 0
15
+ let y = 0
16
+
17
+ const updatePosition = (anchor, tooltip) => {
18
+ if (anchor == null || tooltip == null) {
19
+ return
20
+ }
21
+
22
+ requestAnimationFrame(() => {
23
+ const rect = anchor.getBoundingClientRect()
24
+ const windowOffset =
25
+ window.innerHeight - offset - (tooltip.clientHeight + rect.y)
26
+ const tooltipWidth = tooltip.clientWidth
27
+
28
+ x = rect.x - tooltipWidth - offset
29
+ y = windowOffset < 0 ? rect.y + windowOffset : rect.y
30
+ })
31
+ }
32
+
33
+ $: updatePosition(anchor, tooltip)
34
+
35
+ const handleMouseenter = () => {
36
+ hovering = true
37
+ }
38
+
39
+ const handleMouseleave = () => {
40
+ hovering = false
41
+ }
42
+ </script>
43
+
44
+ <Portal {target}>
45
+ <div
46
+ role="tooltip"
47
+ on:mouseenter={handleMouseenter}
48
+ on:mouseleave={handleMouseleave}
49
+ style:left={`${x}px`}
50
+ style:top={`${y}px`}
51
+ class="wrapper"
52
+ class:visible={visible || hovering}
53
+ >
54
+ <div bind:this={tooltip} class="tooltip">
55
+ <slot />
56
+ </div>
57
+ </div>
58
+ </Portal>
59
+
60
+ <style>
61
+ .wrapper {
62
+ background-color: var(--spectrum-global-color-gray-100);
63
+ box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.42);
64
+ opacity: 0;
65
+ overflow: hidden;
66
+
67
+ border-radius: 5px;
68
+ box-sizing: border-box;
69
+ border: 1px solid var(--grey-4);
70
+ position: absolute;
71
+ pointer-events: none;
72
+ z-index: 1000;
73
+ }
74
+
75
+ .visible {
76
+ opacity: 1;
77
+ pointer-events: auto;
78
+ }
79
+ </style>
package/src/helpers.js CHANGED
@@ -166,7 +166,7 @@ export const stringifyDate = (
166
166
  const offsetForTimezone = (enableTime && ignoreTimezones) || timeOnly
167
167
  if (offsetForTimezone) {
168
168
  // Ensure we use the correct offset for the date
169
- const referenceDate = timeOnly ? new Date() : value.toDate()
169
+ const referenceDate = value.toDate()
170
170
  const offset = referenceDate.getTimezoneOffset() * 60000
171
171
  return new Date(value.valueOf() - offset).toISOString().slice(0, -1)
172
172
  }
package/src/index.js CHANGED
@@ -53,6 +53,7 @@ export { default as Link } from "./Link/Link.svelte"
53
53
  export { default as Tooltip } from "./Tooltip/Tooltip.svelte"
54
54
  export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte"
55
55
  export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte"
56
+ export { default as ContextTooltip } from "./Tooltip/Context.svelte"
56
57
  export { default as Menu } from "./Menu/Menu.svelte"
57
58
  export { default as MenuSection } from "./Menu/Section.svelte"
58
59
  export { default as MenuSeparator } from "./Menu/Separator.svelte"