Starting work on builder, very broken.
This commit is contained in:
parent
e26baa6faf
commit
e1314b0d88
|
@ -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}`
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`)
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
import { params } from "@sveltech/routify"
|
||||
store.actions.pages.select($params.page)
|
||||
store.actions.layouts.select($params.page)
|
||||
</script>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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("")
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue