Creating CSS generation capabilities in the server.

This commit is contained in:
mike12345567 2020-11-24 18:11:18 +00:00
parent b1bb7abdef
commit 5eec4d7a47
6 changed files with 131 additions and 3 deletions

View File

@ -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,

View File

@ -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") {

View File

@ -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),

View File

@ -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("")
})
})

View File

@ -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",
} }

View File

@ -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}`
}
}