Merge pull request #13724 from Budibase/tab-dynamic-resizing
Observe size changes in tabs
This commit is contained in:
commit
a2f0049ee4
3 changed files with 48 additions and 60 deletions
|
@ -1,40 +1,28 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount, createEventDispatcher } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
import Portal from "svelte-portal"
|
import Portal from "svelte-portal"
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let icon = ""
|
export let icon = ""
|
||||||
export let id
|
export let id
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
let selected = getContext("tab")
|
let selected = getContext("tab")
|
||||||
let tab_internal
|
let observer
|
||||||
let tabInfo
|
let ref
|
||||||
|
|
||||||
const setTabInfo = () => {
|
$: isSelected = $selected.title === title
|
||||||
// If the tabs are being rendered inside a component which uses
|
$: {
|
||||||
// a svelte transition to enter, then this initial getBoundingClientRect
|
if (isSelected && ref) {
|
||||||
// will return an incorrect position.
|
observe()
|
||||||
// We just need to get this off the main thread to fix this, by using
|
} else {
|
||||||
// a 0ms timeout.
|
stopObserving()
|
||||||
setTimeout(() => {
|
}
|
||||||
tabInfo = tab_internal?.getBoundingClientRect()
|
|
||||||
if (tabInfo && $selected.title === title) {
|
|
||||||
$selected.info = tabInfo
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
const setTabInfo = () => {
|
||||||
setTabInfo()
|
const tabInfo = ref?.getBoundingClientRect()
|
||||||
})
|
if (tabInfo) {
|
||||||
|
$selected.info = tabInfo
|
||||||
//Ensure that the underline is in the correct location
|
|
||||||
$: {
|
|
||||||
if ($selected.title === title && tab_internal) {
|
|
||||||
if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) {
|
|
||||||
setTabInfo()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +30,25 @@
|
||||||
$selected = {
|
$selected = {
|
||||||
...$selected,
|
...$selected,
|
||||||
title,
|
title,
|
||||||
info: tab_internal.getBoundingClientRect(),
|
info: ref.getBoundingClientRect(),
|
||||||
}
|
}
|
||||||
dispatch("click")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const observe = () => {
|
||||||
|
if (!observer) {
|
||||||
|
observer = new ResizeObserver(setTabInfo)
|
||||||
|
observer.observe(ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopObserving = () => {
|
||||||
|
if (observer) {
|
||||||
|
observer.unobserve(ref)
|
||||||
|
observer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(stopObserving)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
@ -53,11 +56,12 @@
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<div
|
<div
|
||||||
{id}
|
{id}
|
||||||
bind:this={tab_internal}
|
bind:this={ref}
|
||||||
on:click={onClick}
|
on:click={onClick}
|
||||||
class:is-selected={$selected.title === title}
|
on:click
|
||||||
class="spectrum-Tabs-item"
|
class="spectrum-Tabs-item"
|
||||||
class:emphasized={$selected.title === title && $selected.emphasized}
|
class:is-selected={isSelected}
|
||||||
|
class:emphasized={isSelected && $selected.emphasized}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
|
@ -72,7 +76,8 @@
|
||||||
{/if}
|
{/if}
|
||||||
<span class="spectrum-Tabs-itemLabel">{title}</span>
|
<span class="spectrum-Tabs-itemLabel">{title}</span>
|
||||||
</div>
|
</div>
|
||||||
{#if $selected.title === title}
|
|
||||||
|
{#if isSelected}
|
||||||
<Portal target=".spectrum-Tabs-content-{$selected.id}">
|
<Portal target=".spectrum-Tabs-content-{$selected.id}">
|
||||||
<slot />
|
<slot />
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
|
@ -105,10 +105,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
document.fonts.onloadingdone = e => {
|
|
||||||
builderStore.loadFonts(e.fontfaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasSynced && application) {
|
if (!hasSynced && application) {
|
||||||
try {
|
try {
|
||||||
await API.syncApp(application)
|
await API.syncApp(application)
|
||||||
|
@ -149,19 +145,17 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Tabs {selected} size="M">
|
<Tabs {selected} size="M">
|
||||||
{#key $builderStore?.fonts}
|
{#each $layout.children as { path, title }}
|
||||||
{#each $layout.children as { path, title }}
|
<TourWrap stepKeys={[`builder-${title}-section`]}>
|
||||||
<TourWrap stepKeys={[`builder-${title}-section`]}>
|
<Tab
|
||||||
<Tab
|
quiet
|
||||||
quiet
|
selected={$isActive(path)}
|
||||||
selected={$isActive(path)}
|
on:click={topItemNavigate(path)}
|
||||||
on:click={topItemNavigate(path)}
|
title={capitalise(title)}
|
||||||
title={capitalise(title)}
|
id={`builder-${title}-tab`}
|
||||||
id={`builder-${title}-tab`}
|
/>
|
||||||
/>
|
</TourWrap>
|
||||||
</TourWrap>
|
{/each}
|
||||||
{/each}
|
|
||||||
{/key}
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="topcenternav">
|
<div class="topcenternav">
|
||||||
|
|
|
@ -14,7 +14,6 @@ export const INITIAL_BUILDER_STATE = {
|
||||||
tourKey: null,
|
tourKey: null,
|
||||||
tourStepKey: null,
|
tourStepKey: null,
|
||||||
hoveredComponentId: null,
|
hoveredComponentId: null,
|
||||||
fonts: null,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BuilderStore extends BudiStore {
|
export class BuilderStore extends BudiStore {
|
||||||
|
@ -37,16 +36,6 @@ export class BuilderStore extends BudiStore {
|
||||||
this.websocket
|
this.websocket
|
||||||
}
|
}
|
||||||
|
|
||||||
loadFonts(fontFaces) {
|
|
||||||
const ff = fontFaces.map(
|
|
||||||
fontFace => `${fontFace.family}-${fontFace.weight}`
|
|
||||||
)
|
|
||||||
this.update(state => ({
|
|
||||||
...state,
|
|
||||||
fonts: [...(state.fonts || []), ...ff],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
init(app) {
|
init(app) {
|
||||||
if (!app?.appId) {
|
if (!app?.appId) {
|
||||||
console.error("BuilderStore: No appId supplied for websocket")
|
console.error("BuilderStore: No appId supplied for websocket")
|
||||||
|
|
Loading…
Reference in a new issue