Hover component tree preview (#12465)

* wip

* wip

* linting

* remove console log

* highlighting a selected component in nav

* PR feeddback

* wip

* pr feedback

---------

Co-authored-by: Andrew Kingston <andrew@kingston.dev>
This commit is contained in:
Gerard Burns 2023-12-08 09:41:12 +00:00 committed by GitHub
parent e7f1692cd5
commit 56cb92aa34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 9 deletions

View File

@ -85,6 +85,7 @@ const INITIAL_FRONTEND_STATE = {
selectedScreenId: null, selectedScreenId: null,
selectedComponentId: null, selectedComponentId: null,
selectedLayoutId: null, selectedLayoutId: null,
hoverComponentId: null,
// Client state // Client state
selectedComponentInstance: null, selectedComponentInstance: null,

View File

@ -23,6 +23,7 @@
export let showTooltip = false export let showTooltip = false
export let selectedBy = null export let selectedBy = null
export let compact = false export let compact = false
export let hovering = false
const scrollApi = getContext("scroll") const scrollApi = getContext("scroll")
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -61,6 +62,7 @@
<div <div
class="nav-item" class="nav-item"
class:hovering
class:border class:border
class:selected class:selected
class:withActions class:withActions
@ -71,6 +73,8 @@
on:dragstart on:dragstart
on:dragover on:dragover
on:drop on:drop
on:mouseenter
on:mouseleave
on:click={onClick} on:click={onClick}
ondragover="return false" ondragover="return false"
ondragenter="return false" ondragenter="return false"
@ -152,15 +156,17 @@
--avatars-background: var(--spectrum-global-color-gray-200); --avatars-background: var(--spectrum-global-color-gray-200);
} }
.nav-item.selected { .nav-item.selected {
background-color: var(--spectrum-global-color-gray-300); background-color: var(--spectrum-global-color-gray-300) !important;
--avatars-background: var(--spectrum-global-color-gray-300); --avatars-background: var(--spectrum-global-color-gray-300);
color: var(--ink); color: var(--ink);
} }
.nav-item:hover { .nav-item:hover,
background-color: var(--spectrum-global-color-gray-300); .hovering {
background-color: var(--spectrum-global-color-gray-200);
--avatars-background: var(--spectrum-global-color-gray-300); --avatars-background: var(--spectrum-global-color-gray-300);
} }
.nav-item:hover .actions { .nav-item:hover .actions,
.hovering .actions {
visibility: visible; visibility: visible;
} }
.nav-item-content { .nav-item-content {

View File

@ -36,12 +36,14 @@
// Determine selected component ID // Determine selected component ID
$: selectedComponentId = $store.selectedComponentId $: selectedComponentId = $store.selectedComponentId
$: hoverComponentId = $store.hoverComponentId
$: previewData = { $: previewData = {
appId: $store.appId, appId: $store.appId,
layout, layout,
screen, screen,
selectedComponentId, selectedComponentId,
hoverComponentId,
theme: $store.theme, theme: $store.theme,
customTheme: $store.customTheme, customTheme: $store.customTheme,
previewDevice: $store.previewDevice, previewDevice: $store.previewDevice,
@ -117,6 +119,8 @@
error = event.error || "An unknown error occurred" error = event.error || "An unknown error occurred"
} else if (type === "select-component" && data.id) { } else if (type === "select-component" && data.id) {
$store.selectedComponentId = data.id $store.selectedComponentId = data.id
} else if (type === "hover-component" && data.id) {
$store.hoverComponentId = data.id
} else if (type === "update-prop") { } else if (type === "update-prop") {
await store.actions.components.updateSetting(data.prop, data.value) await store.actions.components.updateSetting(data.prop, data.value)
} else if (type === "update-styles") { } else if (type === "update-styles") {

View File

@ -89,6 +89,17 @@
} }
return findComponentPath($selectedComponent, component._id)?.length > 0 return findComponentPath($selectedComponent, component._id)?.length > 0
} }
const handleMouseover = componentId => {
if ($store.hoverComponentId !== componentId) {
$store.hoverComponentId = componentId
}
}
const handleMouseout = componentId => {
if ($store.hoverComponentId === componentId) {
$store.hoverComponentId = null
}
}
</script> </script>
<ul> <ul>
@ -109,6 +120,9 @@
on:dragover={dragover(component, index)} on:dragover={dragover(component, index)}
on:iconClick={() => toggleNodeOpen(component._id)} on:iconClick={() => toggleNodeOpen(component._id)}
on:drop={onDrop} on:drop={onDrop}
hovering={$store.hoverComponentId === component._id}
on:mouseenter={() => handleMouseover(component._id)}
on:mouseleave={() => handleMouseout(component._id)}
text={getComponentText(component)} text={getComponentText(component)}
icon={getComponentIcon(component)} icon={getComponentIcon(component)}
iconTooltip={getComponentName(component)} iconTooltip={getComponentName(component)}

View File

@ -32,6 +32,17 @@
const handleScroll = e => { const handleScroll = e => {
scrolling = e.target.scrollTop !== 0 scrolling = e.target.scrollTop !== 0
} }
const handleMouseover = componentId => {
if ($store.hoverComponentId !== componentId) {
$store.hoverComponentId = componentId
}
}
const handleMouseout = componentId => {
if ($store.hoverComponentId === componentId) {
$store.hoverComponentId = null
}
}
</script> </script>
<div class="components"> <div class="components">
@ -57,6 +68,12 @@
on:click={() => { on:click={() => {
$store.selectedComponentId = `${$store.selectedScreenId}-screen` $store.selectedComponentId = `${$store.selectedScreenId}-screen`
}} }}
hovering={$store.hoverComponentId ===
`${$store.selectedScreenId}-screen`}
on:mouseenter={() =>
handleMouseover(`${$store.selectedScreenId}-screen`)}
on:mouseleave={() =>
handleMouseout(`${$store.selectedScreenId}-screen`)}
id={`component-screen`} id={`component-screen`}
selectedBy={$userSelectedResourceMap[ selectedBy={$userSelectedResourceMap[
`${$store.selectedScreenId}-screen` `${$store.selectedScreenId}-screen`
@ -78,6 +95,12 @@
on:click={() => { on:click={() => {
$store.selectedComponentId = `${$store.selectedScreenId}-navigation` $store.selectedComponentId = `${$store.selectedScreenId}-navigation`
}} }}
hovering={$store.hoverComponentId ===
`${$store.selectedScreenId}-navigation`}
on:mouseenter={() =>
handleMouseover(`${$store.selectedScreenId}-navigation`)}
on:mouseleave={() =>
handleMouseout(`${$store.selectedScreenId}-navigation`)}
id={`component-nav`} id={`component-nav`}
selectedBy={$userSelectedResourceMap[ selectedBy={$userSelectedResourceMap[
`${$store.selectedScreenId}-navigation` `${$store.selectedScreenId}-navigation`

View File

@ -3,8 +3,7 @@
import IndicatorSet from "./IndicatorSet.svelte" import IndicatorSet from "./IndicatorSet.svelte"
import { builderStore, dndIsDragging } from "stores" import { builderStore, dndIsDragging } from "stores"
let componentId $: componentId = $builderStore.hoverComponentId
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920 $: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
const onMouseOver = e => { const onMouseOver = e => {
@ -24,12 +23,12 @@
} }
if (newId !== componentId) { if (newId !== componentId) {
componentId = newId builderStore.actions.hoverComponent(newId)
} }
} }
const onMouseLeave = () => { const onMouseLeave = () => {
componentId = null builderStore.actions.hoverComponent(null)
} }
onMount(() => { onMount(() => {

View File

@ -32,6 +32,7 @@ const loadBudibase = async () => {
layout: window["##BUDIBASE_PREVIEW_LAYOUT##"], layout: window["##BUDIBASE_PREVIEW_LAYOUT##"],
screen: window["##BUDIBASE_PREVIEW_SCREEN##"], screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"], selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
hoverComponentId: window["##BUDIBASE_HOVER_COMPONENT_ID##"],
previewId: window["##BUDIBASE_PREVIEW_ID##"], previewId: window["##BUDIBASE_PREVIEW_ID##"],
theme: window["##BUDIBASE_PREVIEW_THEME##"], theme: window["##BUDIBASE_PREVIEW_THEME##"],
customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"], customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"],

View File

@ -8,6 +8,7 @@ const createBuilderStore = () => {
inBuilder: false, inBuilder: false,
screen: null, screen: null,
selectedComponentId: null, selectedComponentId: null,
hoverComponentId: null,
editMode: false, editMode: false,
previewId: null, previewId: null,
theme: null, theme: null,
@ -23,6 +24,16 @@ const createBuilderStore = () => {
} }
const store = writable(initialState) const store = writable(initialState)
const actions = { const actions = {
hoverComponent: id => {
if (id === get(store).hoverComponentId) {
return
}
store.update(state => ({
...state,
hoverComponentId: id,
}))
eventStore.actions.dispatchEvent("hover-component", { id })
},
selectComponent: id => { selectComponent: id => {
if (id === get(store).selectedComponentId) { if (id === get(store).selectedComponentId) {
return return

View File

@ -63,6 +63,7 @@
// Extract data from message // Extract data from message
const { const {
selectedComponentId, selectedComponentId,
hoverComponentId,
layout, layout,
screen, screen,
appId, appId,
@ -81,6 +82,7 @@
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
window["##BUDIBASE_HOVER_COMPONENT_ID##"] = hoverComponentId
window["##BUDIBASE_PREVIEW_ID##"] = Math.random() window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
window["##BUDIBASE_PREVIEW_THEME##"] = theme window["##BUDIBASE_PREVIEW_THEME##"] = theme
window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme