component store refactor - remove concept of currentPreviewItem and currentComponentInfo

This commit is contained in:
Martin McKeaveney 2020-12-07 15:27:46 +00:00
parent a8c715efa5
commit 81fe27f8f3
22 changed files with 153 additions and 133 deletions

View File

@ -3,6 +3,7 @@ import { get_capitalised_name } from "../helpers"
import { get } from "svelte/store"
import { allScreens } from "builderStore"
import { FrontendTypes } from "../constants"
import { currentAsset } from "."
export default function(component, state) {
const capitalised = get_capitalised_name(
@ -27,7 +28,7 @@ export default function(component, state) {
// if viewing screen, check current screen for duplicate
if (state.currentFrontEndType === FrontendTypes.SCREEN) {
findMatches(state.currentPreviewItem.props)
findMatches(get(currentAsset).props)
} else {
// viewing a layout - need to find against all screens
for (let screen of get(allScreens)) {

View File

@ -5,6 +5,7 @@ import { getThemeStore } from "./store/theme"
import { derived } from "svelte/store"
import analytics from "analytics"
import { LAYOUT_NAMES } from "../constants"
import { makePropsSafe } from "components/userInterface/assetParsing/createProps"
export const store = getFrontendStore()
export const backendUiStore = getBackendUiStore()
@ -27,6 +28,37 @@ export const currentAsset = derived(store, $store => {
return null
})
export const selectedComponent = derived(
[store, currentAsset],
([$store, $currentAsset]) => {
if (!$currentAsset || !$store.selectedComponentId) return null
function traverse(node, callback) {
if (node._id === $store.selectedComponentId) return callback(node)
if (node._children) {
node._children.forEach(child => traverse(child, callback))
}
if (node.props) {
traverse(node.props, callback)
}
}
let component
traverse($currentAsset, found => {
const componentIdentifier = found._component ?? found.props._component
const componentDef = componentIdentifier.startsWith("##")
? found
: $store.components[componentIdentifier]
component = makePropsSafe(componentDef, found)
})
return component
}
)
export const currentAssetName = derived(store, () => {
return currentAsset.name
})

View File

@ -10,6 +10,7 @@ import {
backendUiStore,
currentAsset,
mainLayout,
selectedComponent,
} from "builderStore"
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api"
@ -20,7 +21,7 @@ import {
findChildComponentType,
generateNewIdsForComponent,
getComponentDefinition,
getParent,
findParent,
} from "../storeUtils"
const INITIAL_FRONTEND_STATE = {
@ -29,14 +30,10 @@ const INITIAL_FRONTEND_STATE = {
description: "",
layouts: [],
screens: [],
mainUi: {},
unauthenticatedUi: {},
components: [],
currentPreviewItem: null,
currentComponentInfo: null,
currentFrontEndType: "none",
currentAssetId: "",
currentComponentProps: null,
selectedComponentId: "",
errors: [],
hasAppPackage: false,
libraries: null,
@ -74,18 +71,6 @@ export const getFrontendStore = () => {
await backendUiStore.actions.database.select(pkg.application.instance)
},
selectAssetType: type => {
store.update(state => {
state.currentFrontEndType = type
const asset = get(currentAsset)
state.currentComponentInfo = asset && asset.props ? asset.props : null
state.currentPreviewItem = asset
state.currentView = "detail"
return state
})
},
routing: {
fetch: async () => {
const response = await api.get("/api/routing")
@ -102,7 +87,6 @@ export const getFrontendStore = () => {
let promise
store.update(state => {
const screen = get(allScreens).find(screen => screen._id === screenId)
state.currentPreviewItem = screen
state.currentFrontEndType = FrontendTypes.SCREEN
state.currentAssetId = screenId
state.currentView = "detail"
@ -113,7 +97,7 @@ export const getFrontendStore = () => {
screen.props
)
screen.props = safeProps
state.currentComponentInfo = safeProps
state.selectedComponentId = safeProps._id
return state
})
await promise
@ -121,8 +105,8 @@ export const getFrontendStore = () => {
create: async screen => {
screen = await store.actions.screens.save(screen)
store.update(state => {
state.currentPreviewItem = screen
state.currentComponentInfo = screen.props
state.selectedComponentId = screen._id
state.selectedAssetId = screen._id
state.currentFrontEndType = FrontendTypes.SCREEN
return state
})
@ -143,12 +127,11 @@ export const getFrontendStore = () => {
state.screens.push(screen)
if (creatingNewScreen) {
state.currentPreviewItem = screen
const safeProps = makePropsSafe(
state.components[screen.props._component],
screen.props
)
state.currentComponentInfo = safeProps
state.selectedComponentId = safeProps._id
screen.props = safeProps
}
return state
@ -160,9 +143,9 @@ export const getFrontendStore = () => {
asset._css = (await response.json())?.css
},
regenerateCssForCurrentScreen: async () => {
const { currentPreviewItem } = get(store)
if (currentPreviewItem) {
await store.actions.screens.regenerateCss(currentPreviewItem)
const asset = get(currentAsset)
if (asset) {
await store.actions.screens.regenerateCss(asset)
}
},
delete: async screens => {
@ -186,9 +169,9 @@ export const getFrontendStore = () => {
},
},
preview: {
saveSelected: async () => {
saveSelected: async asset => {
const state = get(store)
const selectedAsset = get(currentAsset)
const selectedAsset = asset || get(currentAsset)
if (state.currentFrontEndType !== FrontendTypes.LAYOUT) {
await store.actions.screens.save(selectedAsset)
} else {
@ -203,19 +186,9 @@ export const getFrontendStore = () => {
state.currentFrontEndType = FrontendTypes.LAYOUT
state.currentView = "detail"
state.currentAssetId = layout._id
// This is the root of many problems.
// Uncaught (in promise) TypeError: Cannot read property '_component' of undefined
// it appears that the currentLayout sometimes has _props instead of props
// why
const safeProps = makePropsSafe(
state.components[layout.props._component],
layout.props
)
state.currentComponentInfo = safeProps
layout.props = safeProps
state.currentPreviewItem = layout
state.currentAssetId = layout._id
state.selectedComponentId = layout._id
return state
})
@ -284,15 +257,14 @@ export const getFrontendStore = () => {
components: {
select: component => {
store.update(state => {
const componentDef = component._component.startsWith("##")
? component
: state.components[component._component]
state.currentComponentInfo = makePropsSafe(componentDef, component)
state.selectedComponentId = component._id
state.currentView = "component"
return state
})
},
create: (componentToAdd, presetProps) => {
const selectedAsset = get(currentAsset)
store.update(state => {
function findSlot(component_array) {
if (!component_array) {
@ -310,7 +282,7 @@ export const getFrontendStore = () => {
if (
componentToAdd.startsWith("##") &&
findSlot(get(currentAsset)?.props._children)
findSlot(selectedAsset?.props._children)
) {
return state
}
@ -326,29 +298,34 @@ export const getFrontendStore = () => {
_instanceName: instanceName,
})
const currentComponent =
state.components[state.currentComponentInfo._component]
const selected = get(selectedComponent)
const targetParent = currentComponent.children
? state.currentComponentInfo
: getParent(
state.currentPreviewItem.props,
state.currentComponentInfo
)
const currentComponentDefinition =
state.components[selected._component]
// Don't continue if there's no parent
if (!targetParent) {
return state
const allowsChildren = currentComponentDefinition.children
// Determine where to put the new component.
let targetParent
if (allowsChildren) {
// Child of the selected component
targetParent = selected
} else {
// Sibling of selected component
targetParent = findParent(selectedAsset.props, selected)
}
targetParent._children = targetParent._children.concat(
newComponent.props
)
// Don't continue if there's no parent
if (!targetParent) return state
// Push the new component
targetParent._children.push(newComponent.props)
store.actions.preview.saveSelected()
state.currentView = "component"
state.currentComponentInfo = newComponent.props
state.selectedComponentId = newComponent.props._id
analytics.captureEvent("Added Component", {
name: newComponent.props._component,
})
@ -356,14 +333,12 @@ export const getFrontendStore = () => {
})
},
copy: (component, cut = false) => {
const selectedAsset = get(currentAsset)
store.update(state => {
state.componentToPaste = cloneDeep(component)
state.componentToPaste.isCut = cut
if (cut) {
const parent = getParent(
state.currentPreviewItem.props,
component._id
)
const parent = findParent(selectedAsset.props, component._id)
parent._children = parent._children.filter(
child => child._id !== component._id
)
@ -374,6 +349,7 @@ export const getFrontendStore = () => {
})
},
paste: async (targetComponent, mode) => {
const selectedAsset = get(currentAsset)
let promises = []
store.update(state => {
if (!state.componentToPaste) return state
@ -393,10 +369,7 @@ export const getFrontendStore = () => {
return state
}
const parent = getParent(
state.currentPreviewItem.props,
targetComponent
)
const parent = findParent(selectedAsset.props, targetComponent)
const targetIndex = parent._children.indexOf(targetComponent)
const index = mode === "above" ? targetIndex : targetIndex + 1
@ -412,11 +385,13 @@ export const getFrontendStore = () => {
},
updateStyle: async (type, name, value) => {
let promises = []
const selected = get(selectedComponent)
store.update(state => {
if (!state.currentComponentInfo._styles) {
state.currentComponentInfo._styles = {}
if (!selected._styles) {
selected._styles = {}
}
state.currentComponentInfo._styles[type][name] = value
selected._styles[type][name] = value
promises.push(store.actions.screens.regenerateCssForCurrentScreen())
@ -428,22 +403,22 @@ export const getFrontendStore = () => {
},
updateProp: (name, value) => {
store.update(state => {
let current_component = state.currentComponentInfo
let current_component = get(selectedComponent)
current_component[name] = value
state.currentComponentInfo = current_component
state.selectedComponentId = current_component._id
store.actions.preview.saveSelected()
return state
})
},
findRoute: component => {
// Gets all the components to needed to construct a path.
const tempStore = get(store)
const selectedAsset = get(currentAsset)
let pathComponents = []
let parent = component
let root = false
while (!root) {
parent = getParent(tempStore.currentPreviewItem.props, parent)
parent = findParent(selectedAsset.props, parent)
if (!parent) {
root = true
} else {

View File

@ -2,7 +2,12 @@ import { getBuiltin } from "components/userInterface/assetParsing/createProps"
import { uuid } from "./uuid"
import getNewComponentName from "./getNewComponentName"
export const getParent = (rootProps, child) => {
/**
* Find the parent component of the passed in child.
* @param {Object} rootProps - props to search for the parent in
* @param {String|Object} child - id of the child or the child itself to find the parent of
*/
export const findParent = (rootProps, child) => {
let parent
walkProps(rootProps, (props, breakWalk) => {
if (

View File

@ -30,7 +30,7 @@
)
}
}
$: selectedComponentId = $store.currentComponentInfo?._id ?? ""
$: selectedComponentId = $store.selectedComponentId ?? ""
$: previewData = {
layout,
screen,
@ -71,7 +71,7 @@
</script>
<div class="component-container">
{#if $store.currentPreviewItem}
{#if $currentAsset}
<iframe
style="height: 100%; width: 100%"
title="componentPreview"

View File

@ -1,10 +1,11 @@
<script>
import { goto } from "@sveltech/routify"
import { store } from "builderStore"
import { get } from "svelte/store"
import { store, currentAsset } from "builderStore"
import { getComponentDefinition } from "builderStore/storeUtils"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { last } from "lodash/fp"
import { getParent } from "builderStore/storeUtils"
import { findParent } from "builderStore/storeUtils"
import { DropdownMenu } from "@budibase/bbui"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
@ -33,7 +34,8 @@
const moveUpComponent = () => {
store.update(state => {
const parent = getParent(state.currentPreviewItem.props, component)
const asset = get(currentAsset)
const parent = findParent(asset.props, component)
if (parent) {
const currentIndex = parent._children.indexOf(component)
@ -43,7 +45,7 @@
newChildren.splice(currentIndex - 1, 0, component)
parent._children = newChildren
}
state.currentComponentInfo = component
state.selectedComponentId = component._id
store.actions.preview.saveSelected()
return state
@ -52,7 +54,8 @@
const moveDownComponent = () => {
store.update(state => {
const parent = getParent(state.currentPreviewItem.props, component)
const asset = get(currentAsset)
const parent = findParent(asset.props, component)
if (parent) {
const currentIndex = parent._children.indexOf(component)
@ -62,7 +65,7 @@
newChildren.splice(currentIndex + 1, 0, component)
parent._children = newChildren
}
state.currentComponentInfo = component
state.selectedComponentId = component._id
store.actions.preview.saveSelected()
return state
@ -76,7 +79,8 @@
const deleteComponent = () => {
store.update(state => {
const parent = getParent(state.currentPreviewItem.props, component)
const asset = get(currentAsset)
const parent = findParent(asset.props, component)
if (parent) {
parent._children = parent._children.filter(child => child !== component)

View File

@ -22,7 +22,7 @@
const path = store.actions.components.findRoute(component)
// Go to correct URL
$goto(`./${$store.currentAssetId}/${path}`)
$goto(`./${$store.currentAssetId}/${path}`)
}
const dragstart = component => e => {
@ -72,7 +72,7 @@
text={isScreenslot(component._component) ? 'Screenslot' : component._instanceName}
withArrow
indentLevel={level + 3}
selected={currentComponent === component}>
selected={$store.selectedComponentId === component._id}>
<ComponentDropdownMenu {component} />
</NavItem>

View File

@ -1,7 +1,7 @@
<script>
import { writable } from "svelte/store"
import { goto } from "@sveltech/routify"
import { store } from "builderStore"
import { store, selectedComponent, currentAsset } from "builderStore"
import instantiateStore from "./dragDropStore"
import ComponentTree from "./ComponentTree.svelte"
@ -14,7 +14,7 @@
export let path
export let indent
$: selectedScreen = $store.currentPreviewItem
$: selectedScreen = $currentAsset
const changeScreen = screenId => {
// select the route
@ -34,8 +34,8 @@
<NavItem
icon="ri-artboard-2-line"
indentLevel={indent || 1}
selected={$store.currentPreviewItem._id === screenId}
opened={$store.currentPreviewItem._id === screenId}
selected={$store.currentAssetId === screenId}
opened={$store.currentAssetId === screenId}
text={url === '/' ? 'Home' : url}
withArrow={route.subpaths}
on:click={() => changeScreen(screenId)}>
@ -44,7 +44,7 @@
{#if selectedScreen?._id === screenId}
<ComponentTree
components={selectedScreen.props._children}
currentComponent={$store.currentComponentInfo}
currentComponent={$selectedComponent}
{dragDropStore} />
{/if}
{/each}

View File

@ -1,5 +1,5 @@
<script>
import { store } from "builderStore"
import { store, selectedComponent, currentAsset } from "builderStore"
import { FrontendTypes } from "constants"
import panelStructure from "./temporaryPanelStructure.js"
import CategoryTab from "./CategoryTab.svelte"
@ -15,8 +15,8 @@
$: componentInstance =
$store.currentView !== "component"
? { ...$store.currentPreviewItem, ...$store.currentComponentInfo }
: $store.currentComponentInfo
? { ...$currentAsset, ...$selectedComponent }
: $selectedComponent
$: componentDefinition = $store.components[componentInstance._component]
$: componentPropDefinition =
flattenedPanel.find(
@ -60,14 +60,15 @@
}
function setAssetProps(name, value) {
const selectedAsset = get(currentAsset)
store.update(state => {
if (
name === "_instanceName" &&
state.currentFrontEndType === FrontendTypes.SCREEN
) {
state.currentPreviewItem.props._instanceName = value
selectedAsset.props._instanceName = value
} else {
state.currentPreviewItem[name] = value
selectedAsset[name] = value
}
return state
})
@ -99,7 +100,7 @@
displayNameField={displayName}
onChange={store.actions.components.updateProp}
onScreenPropChange={setAssetProps}
assetInstance={$store.currentView !== 'component' && $store.currentPreviewItem} />
assetInstance={$store.currentView !== 'component' && $currentAsset} />
{/if}
</div>

View File

@ -1,6 +1,6 @@
<script>
import { goto, url } from "@sveltech/routify"
import { store, currentAssetName } from "builderStore"
import { store, currentAssetName, selectedComponent } from "builderStore"
import components from "./temporaryPanelStructure.js"
import { DropdownMenu } from "@budibase/bbui"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
@ -26,7 +26,7 @@
const onComponentChosen = component => {
store.actions.components.create(component._component, component.presetProps)
const path = store.actions.components.findRoute($store.currentComponentInfo)
const path = store.actions.components.findRoute($selectedComponent)
$goto(`./${$store.currentAssetId}/${path}`)
close()
}

View File

@ -1,15 +1,15 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
import SaveFields from "./SaveFields.svelte"
export let parameters
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -1,6 +1,6 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
export let parameters
@ -8,9 +8,9 @@
let idFields
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -1,7 +1,7 @@
<script>
// accepts an array of field names, and outputs an object of { FieldName: value }
import { DataList, Label, TextButton, Spacer, Select } from "@budibase/bbui"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
import { CloseCircleIcon, AddIcon } from "components/common/Icons"
import {
@ -32,9 +32,9 @@
}))
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -1,6 +1,6 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
import SaveFields from "./SaveFields.svelte"
import {
@ -16,9 +16,9 @@
let schemaFields
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -1,6 +1,6 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
import SaveFields from "./SaveFields.svelte"
import {
@ -11,9 +11,9 @@
export let parameters
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -1,7 +1,7 @@
<script>
import { onMount } from "svelte"
import { goto, params, url } from "@sveltech/routify"
import { store, currentAsset } from "builderStore"
import { store, currentAsset, selectedComponent } from "builderStore"
import { FrontendTypes } from "constants"
import ComponentNavigationTree from "components/userInterface/ComponentNavigationTree/index.svelte"
import Layout from "components/userInterface/Layout.svelte"
@ -24,6 +24,8 @@
let routes = {}
let tab = $params.assetType
$: console.log("selected", $selectedComponent)
function navigate({ detail }) {
if (!detail) return
$goto(`./${detail.heading.key}`)

View File

@ -6,7 +6,7 @@
import initDragDropStore from "./ComponentNavigationTree/dragDropStore"
import NavItem from "components/common/NavItem.svelte"
import { last } from "lodash/fp"
import { store, currentAsset } from "builderStore"
import { store, currentAsset, selectedComponent } from "builderStore"
import { writable } from "svelte/store"
export let layout
@ -27,7 +27,7 @@
icon="ri-layout-3-line"
text={layout.name}
withArrow
selected={$store.currentComponentInfo?._id === layout.props?._id}
selected={$selectedComponent._id === layout.props?._id}
opened={$store.currentAssetId === layout._id}
on:click={selectLayout}>
<LayoutDropdownMenu {layout} />
@ -36,6 +36,6 @@
{#if $store.currentAssetId === layout._id && layout.props?._children}
<ComponentTree
components={layout.props._children}
currentComponent={$store.currentComponentInfo}
currentComponent={$selectedComponent}
{dragDropStore} />
{/if}

View File

@ -1,7 +1,7 @@
<script>
import { Icon } from "@budibase/bbui"
import Input from "./PropertyPanelControls/Input.svelte"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
import {
readableToRuntimeBinding,
@ -35,9 +35,9 @@
function getBindableProperties() {
// Get all bindableProperties
bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})
}

View File

@ -1,7 +1,7 @@
<script>
import { DataList } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { store, allScreens, backendUiStore } from "builderStore"
import { store, allScreens, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "builderStore/fetchBindableProperties"
const dispatch = createEventDispatcher()
@ -27,9 +27,9 @@
]
const bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -5,7 +5,7 @@
import LayoutSelect from "./LayoutSelect.svelte"
import Input from "./PropertyPanelControls/Input.svelte"
import { excludeProps } from "./propertyCategories.js"
import { store, allScreens } from "builderStore"
import { store, allScreens, currentAsset } from "builderStore"
import { walkProps } from "builderStore/storeUtils"
export let panelDefinition = []
@ -65,7 +65,7 @@
}
// if viewing screen, check current screen for duplicate
if ($store.currentFrontEndType === FrontendTypes.SCREEN) {
lookForDuplicate($store.currentPreviewItem.props)
lookForDuplicate($currentAsset.props)
} else {
// need to dedupe against all screens
for (let screen of $allScreens) {

View File

@ -1,7 +1,7 @@
<script>
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { store, backendUiStore } from "builderStore"
import { store, backendUiStore, currentAsset } from "builderStore"
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
const dispatch = createEventDispatcher()
@ -32,9 +32,9 @@
}, [])
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $store.currentPreviewItem,
screen: $currentAsset,
tables: $backendUiStore.tables,
})

View File

@ -38,6 +38,6 @@ function replicateLocal() {
})
}
replicateLocal()
// replicateLocal()
module.exports = Pouch