Starting work on builder, very broken.

This commit is contained in:
mike12345567 2020-11-24 18:11:34 +00:00
parent 5eec4d7a47
commit 8b4657b196
16 changed files with 76 additions and 204 deletions

View File

@ -1,43 +0,0 @@
export const generate_screen_css = component_arr => {
let styles = ""
for (const { _styles, _id, _children, _component } of component_arr) {
let [componentName] = _component.match(/[a-z]*$/)
Object.keys(_styles).forEach(selector => {
const cssString = generate_css(_styles[selector])
if (cssString) {
styles += apply_class(_id, componentName, cssString, selector)
}
})
if (_children && _children.length) {
styles += generate_screen_css(_children) + "\n"
}
}
return styles.trim()
}
export const generate_css = style => {
let cssString = Object.entries(style).reduce((str, [key, value]) => {
if (typeof value === "string") {
if (value) {
return (str += `${key}: ${value};\n`)
}
} else if (Array.isArray(value)) {
if (value.length > 0 && !value.every(v => v === "")) {
return (str += `${key}: ${value.join(" ")};\n`)
}
}
return str
}, "")
return (cssString || "").trim()
}
export const apply_class = (id, name = "element", styles, selector) => {
if (selector === "normal") {
return `.${name}-${id} {\n${styles}\n}`
} else {
let sel = selector === "selected" ? "::selection" : `:${selector}`
return `.${name}-${id}${sel} {\n${styles}\n}`
}
}

View File

@ -10,19 +10,29 @@ export const backendUiStore = getBackendUiStore()
export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
export const currentAsset = derived(store, $store => {
const layout = $store.layouts ? $store.layouts.find(layout => layout._id === $store.currentAssetId) : null
if (layout) {
return layout
}
const screen = $store.screens ? $store.screens.find(screen => screen._id === $store.currentAssetId) : null
if (screen) {
return screen
}
return null
})
export const currentAssetName = derived(store, () => {
return currentAsset.name
})
// leave this as before for consistency
export const allScreens = derived(store, $store => {
let screens = []
if ($store.pages == null) {
return screens
}
for (let page of Object.values($store.pages)) {
screens = screens.concat(page._screens)
}
return screens
return $store.screens
})
export const currentScreens = derived(store, $store => {
const currentScreens = $store.pages[$store.currentPageName]?._screens
const currentScreens = $store.layouts[currentAssetName]?._screens
if (currentScreens == null) {
return []
}
@ -31,12 +41,6 @@ export const currentScreens = derived(store, $store => {
: Object.values(currentScreens)
})
export const selectedPage = derived(store, $store => {
if (!$store.pages) return null
return $store.pages[$store.currentPageName || "main"]
})
export const initialise = async () => {
try {
await analytics.activate()

View File

@ -5,8 +5,7 @@ import {
getBuiltin,
makePropsSafe,
} from "components/userInterface/pagesParsing/createProps"
import { allScreens, backendUiStore, selectedPage } from "builderStore"
import { generate_screen_css } from "../generate_css"
import { allScreens, backendUiStore, currentAsset } from "builderStore"
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api"
import { DEFAULT_PAGES_OBJECT } from "../../constants"
@ -23,14 +22,15 @@ const INITIAL_FRONTEND_STATE = {
apps: [],
name: "",
description: "",
pages: DEFAULT_PAGES_OBJECT,
layouts: DEFAULT_PAGES_OBJECT,
screens: [],
mainUi: {},
unauthenticatedUi: {},
components: [],
currentPreviewItem: null,
currentComponentInfo: null,
currentFrontEndType: "none",
currentPageName: "",
currentAssetId: "",
currentComponentProps: null,
errors: [],
hasAppPackage: false,
@ -43,52 +43,12 @@ export const getFrontendStore = () => {
const store = writable({ ...INITIAL_FRONTEND_STATE })
store.actions = {
// TODO: REFACTOR
initialise: async pkg => {
const layouts = pkg.layouts, screens = pkg.screens, application = pkg.application
store.update(state => {
state.appId = pkg.application._id
state.appId = application._id
return state
})
const screens = await api.get("/api/screens").then(r => r.json())
const mainScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.main._id)
),
unauthScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.unauthenticated._id)
)
pkg.pages = {
main: {
...pkg.pages.main,
_screens: mainScreens,
},
unauthenticated: {
...pkg.pages.unauthenticated,
_screens: unauthScreens,
},
}
// if the app has just been created
// we need to build the CSS and save
if (pkg.justCreated) {
for (let pageName of ["main", "unauthenticated"]) {
const page = pkg.pages[pageName]
store.actions.screens.regenerateCss(page)
for (let screen of page._screens) {
store.actions.screens.regenerateCss(screen)
}
await api.post(`/api/pages/${page._id}`, {
page: {
componentLibraries: pkg.application.componentLibraries,
...page,
},
screens: page._screens,
})
}
}
pkg.justCreated = false
const components = await fetchComponentLibDefinitions(pkg.application._id)
@ -99,7 +59,8 @@ export const getFrontendStore = () => {
name: pkg.application.name,
description: pkg.application.description,
appId: pkg.application._id,
pages: pkg.pages,
layouts,
screens,
hasAppPackage: true,
builtins: [getBuiltin("##builtin/screenslot")],
appInstance: pkg.application.instance,
@ -111,7 +72,7 @@ export const getFrontendStore = () => {
store.update(state => {
state.currentFrontEndType = type
const page = get(selectedPage)
const page = get(currentAsset)
const pageOrScreen = type === "page" ? page : page._screens[0]
@ -168,7 +129,7 @@ export const getFrontendStore = () => {
await savePromise
},
save: async screen => {
const page = get(selectedPage)
const page = get(currentAsset)
const currentPageScreens = page._screens
const creatingNewScreen = screen._id === undefined
@ -197,14 +158,15 @@ export const getFrontendStore = () => {
state.currentComponentInfo = safeProps
screen.props = safeProps
}
savePromise = store.actions.pages.save()
savePromise = store.actions.layouts.save()
return state
})
if (savePromise) await savePromise
},
regenerateCss: screen => {
screen._css = generate_screen_css([screen.props])
regenerateCss: async asset => {
const response = await api.post("/api/css/generate", asset)
asset._css = await response.json()
},
regenerateCssForCurrentScreen: () => {
const { currentPreviewItem } = get(store)
@ -218,7 +180,7 @@ export const getFrontendStore = () => {
const screensToDelete = Array.isArray(screens) ? screens : [screens]
store.update(state => {
const currentPage = get(selectedPage)
const currentPage = get(currentAsset)
for (let screenToDelete of screensToDelete) {
// Remove screen from current page as well
@ -242,17 +204,17 @@ export const getFrontendStore = () => {
if (state.currentFrontEndType !== "page") {
await store.actions.screens.save(state.currentPreviewItem)
}
await store.actions.pages.save()
await store.actions.layouts.save()
},
},
pages: {
layouts: {
select: pageName => {
store.update(state => {
const currentPage = state.pages[pageName]
const currentPage = state.layouts[pageName]
state.currentFrontEndType = "page"
state.currentView = "detail"
state.currentPageName = pageName
state.currentAssetId = pageName
// This is the root of many problems.
// Uncaught (in promise) TypeError: Cannot read property '_component' of undefined
@ -264,11 +226,11 @@ export const getFrontendStore = () => {
)
state.currentComponentInfo = safeProps
currentPage.props = safeProps
state.currentPreviewItem = state.pages[pageName]
state.currentPreviewItem = state.layouts[pageName]
store.actions.screens.regenerateCssForCurrentScreen()
for (let screen of get(allScreens)) {
screen._css = generate_screen_css([screen.props])
screen._css = store.actions.screens.regenerateCss(screen)
}
return state
@ -276,7 +238,7 @@ export const getFrontendStore = () => {
},
save: async page => {
const storeContents = get(store)
const pageName = storeContents.currentPageName || "main"
const pageName = storeContents.currentAssetId || "main"
const pageToSave = page || storeContents.pages[pageName]
// TODO: revisit. This sends down a very weird payload
@ -293,7 +255,7 @@ export const getFrontendStore = () => {
if (!json.ok) throw new Error("Error updating page")
store.update(state => {
state.pages[pageName]._rev = json.rev
state.layouts[pageName]._rev = json.rev
return state
})
},
@ -324,7 +286,7 @@ export const getFrontendStore = () => {
if (
componentToAdd.startsWith("##") &&
findSlot(state.pages[state.currentPageName].props._children)
findSlot(state.layouts[state.currentAssetId].props._children)
) {
return state
}
@ -480,7 +442,7 @@ export const getFrontendStore = () => {
store.update(state => {
// Try to extract a nav component from the master screen
const nav = findChildComponentType(
state.pages.main,
state.layouts.main,
"@budibase/standard-components/Navigation"
)
if (nav) {
@ -515,12 +477,12 @@ export const getFrontendStore = () => {
// Save page and regenerate all CSS because otherwise weird things happen
nav._children = [...nav._children, newLink]
state.currentPageName = "main"
store.actions.screens.regenerateCss(state.pages.main)
for (let screen of state.pages.main._screens) {
state.currentAssetId = "main"
store.actions.screens.regenerateCss(state.layouts.main)
for (let screen of state.layouts.main._screens) {
store.actions.screens.regenerateCss(screen)
}
savePromise = store.actions.pages.save()
savePromise = store.actions.layouts.save()
}
return state
})

View File

@ -51,7 +51,7 @@
const screens = screenTemplates($store, [table])
.filter(template => defaultScreens.includes(template.id))
.map(template => template.create())
store.actions.pages.select("main")
store.actions.layouts.select("main")
for (let screen of screens) {
// Record the table that created this screen so we can link it later
screen.autoTableId = table._id

View File

@ -155,9 +155,8 @@
const pkg = await applicationPkg.json()
if (applicationPkg.ok) {
backendUiStore.actions.reset()
pkg.justCreated = true
await store.actions.initialise(pkg)
automationStore.actions.fetch()
await automationStore.actions.fetch()
} else {
throw new Error(pkg)
}

View File

@ -56,7 +56,7 @@
screenPlaceholder.props._id = "screenslot-placeholder"
// Extract data to pass to the iframe
$: page = $store.pages[$store.currentPageName]
$: page = $store.layouts[$store.currentPageName]
$: screen =
$store.currentFrontEndType === "page"
? screenPlaceholder

View File

@ -19,7 +19,7 @@
// update the page if required
store.update(state => {
if (state.currentPreviewItem._id === screen) {
store.actions.pages.select($store.currentPageName)
store.actions.layouts.select($store.currentPageName)
notifier.success(`Screen ${screenToDelete.name} deleted successfully.`)
$goto(`./:page/page-layout`)
}

View File

@ -1,6 +1,6 @@
<script>
import { goto } from "@sveltech/routify"
import { store } from "builderStore"
import { store, currentAssetName } from "builderStore"
import components from "./temporaryPanelStructure.js"
import { DropdownMenu } from "@budibase/bbui"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
@ -52,7 +52,7 @@
align="left">
<DropdownContainer>
{#each categories[selectedIndex].children as item}
{#if !item.showOnPages || item.showOnPages.includes($store.currentPageName)}
{#if !item.showOnAsset || item.showOnAsset.includes($currentAssetName)}
<DropdownItem
icon={item.icon}
title={item.name}

View File

@ -4,7 +4,7 @@
import api from "builderStore/api"
import ComponentNavigationTree from "components/userInterface/ComponentNavigationTree/index.svelte"
import PageLayout from "components/userInterface/PageLayout.svelte"
import PagesList from "components/userInterface/PagesList.svelte"
import PagesList from "components/userInterface/LayoutsList.svelte"
import NewScreenModal from "components/userInterface/NewScreenModal.svelte"
import { Modal } from "@budibase/bbui"
@ -23,7 +23,7 @@
</div>
<PagesList />
<div class="nav-items-container">
<PageLayout layout={$store.pages[$store.currentPageName]} />
<PageLayout layout={$store.layouts[$store.currentPageName]} />
<ComponentNavigationTree />
</div>
<Modal bind:this={modal}>

View File

@ -7,7 +7,7 @@
return { name, props }
}
const pages = [
const layouts = [
{
title: "Private",
id: "main",
@ -18,18 +18,18 @@
},
]
if (!$store.currentPageName)
store.actions.pages.select($params.page ? $params.page : "main")
if (!$store.currentAssetId)
store.actions.layouts.select($params.layout ? $params.layout : "main")
const changePage = id => {
store.actions.pages.select(id)
const changeLayout = id => {
store.actions.layouts.select(id)
$goto(`./${id}/page-layout`)
}
</script>
<div class="root">
{#each pages as { title, id }}
<button class:active={id === $params.page} on:click={() => changePage(id)}>
{#each layouts as { title, id }}
<button class:active={id === $params.layout} on:click={() => changeLayout(id)}>
{title}
</button>
{/each}

View File

@ -4,7 +4,7 @@
import Input from "./PropertyPanelControls/Input.svelte"
import { goto } from "@sveltech/routify"
import { excludeProps } from "./propertyCategories.js"
import { store, allScreens } from "builderStore"
import { store, allScreens, currentAsset } from "builderStore"
import { walkProps } from "builderStore/storeUtils"
export let panelDefinition = []
@ -58,15 +58,15 @@
}
})
}
// check page first
lookForDuplicate($store.pages[$store.currentPageName].props)
if (duplicate) return true
// check against layouts
for (let layout of $store.layouts) {
lookForDuplicate(layout.props)
}
// if viewing screen, check current screen for duplicate
if ($store.currentFrontEndType === "screen") {
lookForDuplicate($store.currentPreviewItem.props)
} else {
// viewing master page - need to dedupe against all screens
// need to dedupe against all screens
for (let screen of $allScreens) {
lookForDuplicate(screen.props)
}

View File

@ -1185,6 +1185,7 @@ export default {
settings: [{ label: "Logo URL", key: "logoUrl", control: Input }],
},
},
// TODO: need to deal with this
{
name: "Login",
_component: "@budibase/standard-components/login",
@ -1192,7 +1193,7 @@ export default {
"A component that automatically generates a login screen for your app.",
icon: "ri-login-box-line",
children: [],
showOnPages: ["unauthenticated"],
showOnAsset: ["login-screen"],
properties: {
design: { ...all },
settings: [

View File

@ -1,4 +1,4 @@
<script>
import { params } from "@sveltech/routify"
store.actions.pages.select($params.page)
store.actions.layouts.select($params.page)
</script>

View File

@ -14,7 +14,7 @@
if (!validScreen) {
// Go to main layout if URL set to invalid screen
store.actions.pages.select("main")
store.actions.layouts.select("main")
$goto("../../main")
} else {
// Otherwise proceed to set screen
@ -23,7 +23,7 @@
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it.
if ($leftover) {
// Get the correct screen children.
const screenChildren = $store.pages[$params.page]._screens.find(
const screenChildren = $store.layouts[$params.page]._screens.find(
screen =>
screen._id === $params.screen ||
screen._id === decodeURIComponent($params.screen)
@ -37,7 +37,7 @@
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it.
if ($leftover) {
findComponent(componentIds, $store.pages[$params.page].props._children)
findComponent(componentIds, $store.layouts[$params.page].props._children)
}
}

View File

@ -2,7 +2,7 @@
import { params } from "@sveltech/routify"
import { store } from "builderStore"
store.actions.pages.select($params.page)
store.actions.layouts.select($params.page)
</script>
<slot />

View File

@ -1,51 +0,0 @@
import {
generate_css,
generate_screen_css,
} from "../src/builderStore/generate_css.js"
describe("generate_css", () => {
test("Check how array styles are output", () => {
expect(generate_css({ margin: ["0", "10", "0", "15"] })).toBe("margin: 0px 10px 0px 15px;")
})
test("Check handling of an array with empty string values", () => {
expect(generate_css({ padding: ["", "", "", ""] })).toBe("")
})
test("Check handling of an empty array", () => {
expect(generate_css({ margin: [] })).toBe("")
})
test("Check handling of valid font property", () => {
expect(generate_css({ "font-size": "10px" })).toBe("font-size: 10px;")
})
})
describe("generate_screen_css", () => {
const normalComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: { "font-size": "16px" }, hover: {}, active: {}, selected: {} } }
test("Test generation of normal css styles", () => {
expect(generate_screen_css([normalComponent])).toBe(".header-123-456 {\nfont-size: 16px;\n}")
})
const hoverComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {"font-size": "16px"}, active: {}, selected: {} } }
test("Test generation of hover css styles", () => {
expect(generate_screen_css([hoverComponent])).toBe(".header-123-456:hover {\nfont-size: 16px;\n}")
})
const selectedComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: { "font-size": "16px" } } }
test("Test generation of selection css styles", () => {
expect(generate_screen_css([selectedComponent])).toBe(".header-123-456::selection {\nfont-size: 16px;\n}")
})
const emptyComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: {} } }
test.only("Testing handling of empty component styles", () => {
expect(generate_screen_css([emptyComponent])).toBe("")
})
})