Creating CSS generation capabilities in the server.
This commit is contained in:
parent
ddca22245c
commit
e26baa6faf
|
@ -30,6 +30,7 @@ const { BASE_LAYOUTS } = require("../../constants/layouts")
|
||||||
const { HOME_SCREEN } = require("../../constants/screens")
|
const { HOME_SCREEN } = require("../../constants/screens")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { recurseMustache } = require("../../utilities/mustache")
|
const { recurseMustache } = require("../../utilities/mustache")
|
||||||
|
const { generateAssetCss } = require("../../utilities/builder/generateCss")
|
||||||
|
|
||||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
|
|
||||||
|
@ -119,9 +120,14 @@ exports.fetchAppDefinition = async function(ctx) {
|
||||||
exports.fetchAppPackage = async function(ctx) {
|
exports.fetchAppPackage = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = new CouchDB(ctx.params.appId)
|
||||||
const application = await db.get(ctx.params.appId)
|
const application = await db.get(ctx.params.appId)
|
||||||
const layouts = await getLayouts(db)
|
const [layouts, screens] = await Promise.all([getLayouts(db), getScreens(db)])
|
||||||
const screens = await getScreens(db)
|
|
||||||
|
|
||||||
|
for (let layout of layouts) {
|
||||||
|
layout._css = generateAssetCss([layout.props])
|
||||||
|
}
|
||||||
|
for (let screen of screens) {
|
||||||
|
screen._css = generateAssetCss([screen.props])
|
||||||
|
}
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
application,
|
application,
|
||||||
screens,
|
screens,
|
||||||
|
|
|
@ -17,10 +17,16 @@ const setBuilderToken = require("../../../utilities/builder/setBuilderToken")
|
||||||
const fileProcessor = require("../../../utilities/fileProcessor")
|
const fileProcessor = require("../../../utilities/fileProcessor")
|
||||||
const { AuthTypes } = require("../../../constants")
|
const { AuthTypes } = require("../../../constants")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
const { generateAssetCss } = require("../../../utilities/builder/generateCss")
|
||||||
|
|
||||||
// this was the version before we started versioning the component library
|
// this was the version before we started versioning the component library
|
||||||
const COMP_LIB_BASE_APP_VERSION = "0.2.5"
|
const COMP_LIB_BASE_APP_VERSION = "0.2.5"
|
||||||
|
|
||||||
|
exports.generateCss = async function(ctx) {
|
||||||
|
const structure = ctx.request.body
|
||||||
|
ctx.body = generateAssetCss(structure)
|
||||||
|
}
|
||||||
|
|
||||||
exports.serveBuilder = async function(ctx) {
|
exports.serveBuilder = async function(ctx) {
|
||||||
let builderPath = resolve(__dirname, "../../../../builder")
|
let builderPath = resolve(__dirname, "../../../../builder")
|
||||||
if (ctx.file === "index.html") {
|
if (ctx.file === "index.html") {
|
||||||
|
|
|
@ -24,6 +24,7 @@ if (env.NODE_ENV !== "production") {
|
||||||
}
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.post("/api/css/generate", authorized(BUILDER), controller.generateCss)
|
||||||
.post(
|
.post(
|
||||||
"/api/attachments/process",
|
"/api/attachments/process",
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
const { generateAssetCss, generateCss } = require("../../../utilities/builder/generateCss")
|
||||||
|
|
||||||
|
describe("generate_css", () => {
|
||||||
|
it("Check how array styles are output", () => {
|
||||||
|
expect(generateCss({ margin: ["0", "10", "0", "15"] })).toBe("margin: 0 10 0 15;")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Check handling of an array with empty string values", () => {
|
||||||
|
expect(generateCss({ padding: ["", "", "", ""] })).toBe("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Check handling of an empty array", () => {
|
||||||
|
expect(generateCss({ margin: [] })).toBe("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Check handling of valid font property", () => {
|
||||||
|
expect(generateCss({ "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: {} } }
|
||||||
|
|
||||||
|
it("Test generation of normal css styles", () => {
|
||||||
|
expect(generateAssetCss([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: {} } }
|
||||||
|
|
||||||
|
it("Test generation of hover css styles", () => {
|
||||||
|
expect(generateAssetCss([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" } } }
|
||||||
|
|
||||||
|
it("Test generation of selection css styles", () => {
|
||||||
|
expect(generateAssetCss([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: {} } }
|
||||||
|
|
||||||
|
it("Testing handling of empty component styles", () => {
|
||||||
|
expect(generateAssetCss([emptyComponent])).toBe("")
|
||||||
|
})
|
||||||
|
})
|
|
@ -104,5 +104,31 @@ exports.HOME_SCREEN = {
|
||||||
route: "/",
|
route: "/",
|
||||||
accessLevelId: BUILTIN_LEVEL_IDS.BASIC,
|
accessLevelId: BUILTIN_LEVEL_IDS.BASIC,
|
||||||
},
|
},
|
||||||
name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
name: "home-screen",
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.LOGIN_SCREEN = {
|
||||||
|
description: "",
|
||||||
|
url: "",
|
||||||
|
props: {
|
||||||
|
_id: "781e497e-2e7c-11eb-adc1-0242ac120002",
|
||||||
|
_component: "@budibase/standard-components/login",
|
||||||
|
_styles: {
|
||||||
|
normal: {},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_code: "",
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
type: "div",
|
||||||
|
_children: [],
|
||||||
|
_instanceName: "Login",
|
||||||
|
},
|
||||||
|
routing: {
|
||||||
|
route: "/",
|
||||||
|
accessLevelId: BUILTIN_LEVEL_IDS.PUBLIC,
|
||||||
|
},
|
||||||
|
name: "login-screen",
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
exports.generateAssetCss = 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 = exports.generateCss(_styles[selector])
|
||||||
|
if (cssString) {
|
||||||
|
styles += exports.applyClass(_id, componentName, cssString, selector)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (_children && _children.length) {
|
||||||
|
styles += exports.generateAssetCss(_children) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return styles.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.generateCss = 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.applyClass = (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}`
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue