Merge branch 'master' of github.com:Budibase/budibase into feature/security-update
This commit is contained in:
commit
77f049d1bd
|
@ -1,115 +1,115 @@
|
||||||
/ ***********************************************
|
// ***********************************************
|
||||||
// This example commands.js shows you how to
|
// This example commands.js shows you how to
|
||||||
// create various custom commands and overwrite
|
// create various custom commands and overwrite
|
||||||
// existing commands.
|
// existing commands.
|
||||||
//
|
//
|
||||||
// For more comprehensive examples of custom
|
// For more comprehensive examples of custom
|
||||||
// commands please read more here:
|
// commands please read more here:
|
||||||
// https://on.cypress.io/custom-commands
|
// https://on.cypress.io/custom-commands
|
||||||
// ***********************************************
|
// ***********************************************
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// -- This is a parent command --
|
// -- This is a parent command --
|
||||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// -- This is a child command --
|
// -- This is a child command --
|
||||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// -- This is a dual command --
|
// -- This is a dual command --
|
||||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
Cypress.Commands.add("createApp", name => {
|
Cypress.Commands.add("createApp", name => {
|
||||||
cy.contains("Create New Web App").click()
|
cy.contains("Create New Web App").click()
|
||||||
|
|
||||||
cy.get("body")
|
cy.get("body")
|
||||||
.then($body => {
|
.then($body => {
|
||||||
if ($body.find("input[name=apiKey]").length) {
|
if ($body.find("input[name=apiKey]").length) {
|
||||||
// input was found, do something else here
|
// input was found, do something else here
|
||||||
cy.get("input[name=apiKey]")
|
cy.get("input[name=apiKey]")
|
||||||
.type(name)
|
.type(name)
|
||||||
.should("have.value", name)
|
.should("have.value", name)
|
||||||
cy.contains("Next").click()
|
cy.contains("Next").click()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
cy.get("input[name=applicationName]")
|
cy.get("input[name=applicationName]")
|
||||||
.type(name)
|
.type(name)
|
||||||
.should("have.value", name)
|
.should("have.value", name)
|
||||||
|
|
||||||
cy.contains("Next").click()
|
cy.contains("Next").click()
|
||||||
|
|
||||||
cy.get("input[name=email]")
|
cy.get("input[name=email]")
|
||||||
.click()
|
.click()
|
||||||
.type("test@test.com")
|
.type("test@test.com")
|
||||||
cy.get("input[name=password]")
|
cy.get("input[name=password]")
|
||||||
.click()
|
.click()
|
||||||
.type("test")
|
.type("test")
|
||||||
cy.contains("Submit").click()
|
cy.contains("Submit").click()
|
||||||
cy.get("[data-cy=new-table]", {
|
cy.get("[data-cy=new-table]", {
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
}).should("be.visible")
|
}).should("be.visible")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createTestTableWithData", () => {
|
Cypress.Commands.add("createTestTableWithData", () => {
|
||||||
cy.createTable("dog")
|
cy.createTable("dog")
|
||||||
cy.addColumn("dog", "name", "Text")
|
cy.addColumn("dog", "name", "Text")
|
||||||
cy.addColumn("dog", "age", "Number")
|
cy.addColumn("dog", "age", "Number")
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createTable", tableName => {
|
Cypress.Commands.add("createTable", tableName => {
|
||||||
// Enter table name
|
// Enter table name
|
||||||
cy.get("[data-cy=new-table]").click()
|
cy.get("[data-cy=new-table]").click()
|
||||||
cy.get(".modal").within(() => {
|
cy.get(".modal").within(() => {
|
||||||
cy.get("input")
|
cy.get("input")
|
||||||
.first()
|
.first()
|
||||||
.type(tableName)
|
.type(tableName)
|
||||||
cy.get(".buttons")
|
cy.get(".buttons")
|
||||||
.contains("Create")
|
.contains("Create")
|
||||||
.click()
|
.click()
|
||||||
})
|
})
|
||||||
cy.contains(tableName).should("be.visible")
|
cy.contains(tableName).should("be.visible")
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
|
Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
|
||||||
// Select Table
|
// Select Table
|
||||||
cy.contains(".nav-item", tableName).click()
|
cy.contains(".nav-item", tableName).click()
|
||||||
cy.contains("Create New Column").click()
|
cy.contains("Create New Column").click()
|
||||||
|
|
||||||
// Configure column
|
// Configure column
|
||||||
cy.get(".actions").within(() => {
|
cy.get(".actions").within(() => {
|
||||||
cy.get("input")
|
cy.get("input")
|
||||||
.first()
|
.first()
|
||||||
.type(columnName)
|
.type(columnName)
|
||||||
// Unset table display column
|
// Unset table display column
|
||||||
cy.contains("display column").click()
|
cy.contains("display column").click()
|
||||||
cy.get("select").select(type)
|
cy.get("select").select(type)
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("addRow", values => {
|
Cypress.Commands.add("addRow", values => {
|
||||||
cy.contains("Create New Row").click()
|
cy.contains("Create New Row").click()
|
||||||
|
|
||||||
cy.get(".modal").within(() => {
|
cy.get(".modal").within(() => {
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
cy.get("input")
|
cy.get("input")
|
||||||
.eq(i)
|
.eq(i)
|
||||||
.type(values[i])
|
.type(values[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.get(".buttons")
|
cy.get(".buttons")
|
||||||
.contains("Create")
|
.contains("Create")
|
||||||
.click()
|
.click()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createUser", (email, password, role) => {
|
Cypress.Commands.add("createUser", (email, password, role) => {
|
||||||
// Create User
|
// Create User
|
||||||
|
@ -120,17 +120,17 @@ Cypress.Commands.add("createUser", (email, password, role) => {
|
||||||
cy.get(".modal").within(() => {
|
cy.get(".modal").within(() => {
|
||||||
cy.get("input")
|
cy.get("input")
|
||||||
.first()
|
.first()
|
||||||
.type(password)
|
.type(email)
|
||||||
cy.get("input")
|
cy.get("input")
|
||||||
.eq(1)
|
.eq(1)
|
||||||
.type(email)
|
.type(password)
|
||||||
cy.get("select")
|
cy.get("select")
|
||||||
.first()
|
.first()
|
||||||
.select(role)
|
.select(role)
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.get(".buttons")
|
cy.get(".buttons")
|
||||||
.contains("Create Row")
|
.contains("Create User")
|
||||||
.click()
|
.click()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.51.0",
|
"@budibase/bbui": "^1.52.2",
|
||||||
"@budibase/client": "^0.3.8",
|
"@budibase/client": "^0.3.8",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@budibase/svelte-ag-grid": "^0.0.16",
|
"@budibase/svelte-ag-grid": "^0.0.16",
|
||||||
|
@ -107,6 +107,7 @@
|
||||||
"rollup-plugin-alias": "^1.5.2",
|
"rollup-plugin-alias": "^1.5.2",
|
||||||
"rollup-plugin-copy": "^3.0.0",
|
"rollup-plugin-copy": "^3.0.0",
|
||||||
"rollup-plugin-css-only": "^2.1.0",
|
"rollup-plugin-css-only": "^2.1.0",
|
||||||
|
"rollup-plugin-html": "^0.2.1",
|
||||||
"rollup-plugin-livereload": "^1.0.0",
|
"rollup-plugin-livereload": "^1.0.0",
|
||||||
"rollup-plugin-node-builtins": "^2.1.2",
|
"rollup-plugin-node-builtins": "^2.1.2",
|
||||||
"rollup-plugin-node-globals": "^1.4.0",
|
"rollup-plugin-node-globals": "^1.4.0",
|
||||||
|
|
|
@ -11,6 +11,7 @@ import copy from "rollup-plugin-copy"
|
||||||
import css from "rollup-plugin-css-only"
|
import css from "rollup-plugin-css-only"
|
||||||
import replace from "rollup-plugin-replace"
|
import replace from "rollup-plugin-replace"
|
||||||
import json from "@rollup/plugin-json"
|
import json from "@rollup/plugin-json"
|
||||||
|
import html from "rollup-plugin-html"
|
||||||
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
|
@ -75,10 +76,6 @@ export default {
|
||||||
{ src: "src/index.html", dest: outputpath },
|
{ src: "src/index.html", dest: outputpath },
|
||||||
{ src: "src/favicon.png", dest: outputpath },
|
{ src: "src/favicon.png", dest: outputpath },
|
||||||
{ src: "assets", dest: outputpath },
|
{ src: "assets", dest: outputpath },
|
||||||
{
|
|
||||||
src: "node_modules/@budibase/client/dist/budibase-client.esm.mjs",
|
|
||||||
dest: outputpath,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
src: "node_modules/@budibase/bbui/dist/bbui.css",
|
src: "node_modules/@budibase/bbui/dist/bbui.css",
|
||||||
dest: outputpath,
|
dest: outputpath,
|
||||||
|
@ -147,5 +144,6 @@ export default {
|
||||||
// instead of npm run dev), minify
|
// instead of npm run dev), minify
|
||||||
production && terser(),
|
production && terser(),
|
||||||
json(),
|
json(),
|
||||||
|
html(),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { walkProps } from "./storeUtils"
|
||||||
import { get_capitalised_name } from "../helpers"
|
import { get_capitalised_name } from "../helpers"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { allScreens } from "builderStore"
|
import { allScreens } from "builderStore"
|
||||||
|
import { FrontendTypes } from "../constants"
|
||||||
|
import { currentAsset } from "."
|
||||||
|
|
||||||
export default function(component, state) {
|
export default function(component, state) {
|
||||||
const capitalised = get_capitalised_name(
|
const capitalised = get_capitalised_name(
|
||||||
|
@ -19,14 +21,16 @@ export default function(component, state) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// check page first
|
// check layouts first
|
||||||
findMatches(state.pages[state.currentPageName].props)
|
for (let layout of state.layouts) {
|
||||||
|
findMatches(layout.props)
|
||||||
|
}
|
||||||
|
|
||||||
// if viewing screen, check current screen for duplicate
|
// if viewing screen, check current screen for duplicate
|
||||||
if (state.currentFrontEndType === "screen") {
|
if (state.currentFrontEndType === FrontendTypes.SCREEN) {
|
||||||
findMatches(state.currentPreviewItem.props)
|
findMatches(get(currentAsset).props)
|
||||||
} else {
|
} else {
|
||||||
// viewing master page - need to find against all screens
|
// viewing a layout - need to find against all screens
|
||||||
for (let screen of get(allScreens)) {
|
for (let screen of get(allScreens)) {
|
||||||
findMatches(screen.props)
|
findMatches(screen.props)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,37 +4,74 @@ import { getAutomationStore } from "./store/automation/"
|
||||||
import { getThemeStore } from "./store/theme"
|
import { getThemeStore } from "./store/theme"
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
|
import { LAYOUT_NAMES } from "../constants"
|
||||||
|
import { makePropsSafe } from "components/userInterface/assetParsing/createProps"
|
||||||
|
|
||||||
export const store = getFrontendStore()
|
export const store = getFrontendStore()
|
||||||
export const backendUiStore = getBackendUiStore()
|
export const backendUiStore = getBackendUiStore()
|
||||||
export const automationStore = getAutomationStore()
|
export const automationStore = getAutomationStore()
|
||||||
export const themeStore = getThemeStore()
|
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 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
|
||||||
|
})
|
||||||
|
|
||||||
|
// leave this as before for consistency
|
||||||
export const allScreens = derived(store, $store => {
|
export const allScreens = derived(store, $store => {
|
||||||
let screens = []
|
return $store.screens
|
||||||
if ($store.pages == null) {
|
|
||||||
return screens
|
|
||||||
}
|
|
||||||
for (let page of Object.values($store.pages)) {
|
|
||||||
screens = screens.concat(page._screens)
|
|
||||||
}
|
|
||||||
return screens
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const currentScreens = derived(store, $store => {
|
export const mainLayout = derived(store, $store => {
|
||||||
const currentScreens = $store.pages[$store.currentPageName]?._screens
|
return $store.layouts?.find(
|
||||||
if (currentScreens == null) {
|
layout => layout.props?._id === LAYOUT_NAMES.MASTER.PRIVATE
|
||||||
return []
|
)
|
||||||
}
|
|
||||||
return Array.isArray(currentScreens)
|
|
||||||
? currentScreens
|
|
||||||
: Object.values(currentScreens)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const selectedPage = derived(store, $store => {
|
|
||||||
if (!$store.pages) return null
|
|
||||||
|
|
||||||
return $store.pages[$store.currentPageName || "main"]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const initialise = async () => {
|
export const initialise = async () => {
|
||||||
|
|
|
@ -4,34 +4,36 @@ import {
|
||||||
createProps,
|
createProps,
|
||||||
getBuiltin,
|
getBuiltin,
|
||||||
makePropsSafe,
|
makePropsSafe,
|
||||||
} from "components/userInterface/pagesParsing/createProps"
|
} from "components/userInterface/assetParsing/createProps"
|
||||||
import { allScreens, backendUiStore, selectedPage } from "builderStore"
|
import {
|
||||||
import { generate_screen_css } from "../generate_css"
|
allScreens,
|
||||||
|
backendUiStore,
|
||||||
|
currentAsset,
|
||||||
|
mainLayout,
|
||||||
|
selectedComponent,
|
||||||
|
} from "builderStore"
|
||||||
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
|
||||||
import api from "../api"
|
import api from "../api"
|
||||||
import { DEFAULT_PAGES_OBJECT } from "../../constants"
|
import { FrontendTypes } from "../../constants"
|
||||||
import getNewComponentName from "../getNewComponentName"
|
import getNewComponentName from "../getNewComponentName"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
import {
|
import {
|
||||||
findChildComponentType,
|
findChildComponentType,
|
||||||
generateNewIdsForComponent,
|
generateNewIdsForComponent,
|
||||||
getComponentDefinition,
|
getComponentDefinition,
|
||||||
getParent,
|
findParent,
|
||||||
} from "../storeUtils"
|
} from "../storeUtils"
|
||||||
|
|
||||||
const INITIAL_FRONTEND_STATE = {
|
const INITIAL_FRONTEND_STATE = {
|
||||||
apps: [],
|
apps: [],
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
pages: DEFAULT_PAGES_OBJECT,
|
layouts: [],
|
||||||
mainUi: {},
|
screens: [],
|
||||||
unauthenticatedUi: {},
|
|
||||||
components: [],
|
components: [],
|
||||||
currentPreviewItem: null,
|
|
||||||
currentComponentInfo: null,
|
|
||||||
currentFrontEndType: "none",
|
currentFrontEndType: "none",
|
||||||
currentPageName: "",
|
currentAssetId: "",
|
||||||
currentComponentProps: null,
|
selectedComponentId: "",
|
||||||
errors: [],
|
errors: [],
|
||||||
hasAppPackage: false,
|
hasAppPackage: false,
|
||||||
libraries: null,
|
libraries: null,
|
||||||
|
@ -43,52 +45,13 @@ export const getFrontendStore = () => {
|
||||||
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
const store = writable({ ...INITIAL_FRONTEND_STATE })
|
||||||
|
|
||||||
store.actions = {
|
store.actions = {
|
||||||
// TODO: REFACTOR
|
|
||||||
initialise: async pkg => {
|
initialise: async pkg => {
|
||||||
|
const { layouts, screens, application } = pkg
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.appId = pkg.application._id
|
state.appId = application._id
|
||||||
return state
|
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)
|
const components = await fetchComponentLibDefinitions(pkg.application._id)
|
||||||
|
|
||||||
|
@ -99,7 +62,8 @@ export const getFrontendStore = () => {
|
||||||
name: pkg.application.name,
|
name: pkg.application.name,
|
||||||
description: pkg.application.description,
|
description: pkg.application.description,
|
||||||
appId: pkg.application._id,
|
appId: pkg.application._id,
|
||||||
pages: pkg.pages,
|
layouts,
|
||||||
|
screens,
|
||||||
hasAppPackage: true,
|
hasAppPackage: true,
|
||||||
builtins: [getBuiltin("##builtin/screenslot")],
|
builtins: [getBuiltin("##builtin/screenslot")],
|
||||||
appInstance: pkg.application.instance,
|
appInstance: pkg.application.instance,
|
||||||
|
@ -107,20 +71,6 @@ export const getFrontendStore = () => {
|
||||||
|
|
||||||
await backendUiStore.actions.database.select(pkg.application.instance)
|
await backendUiStore.actions.database.select(pkg.application.instance)
|
||||||
},
|
},
|
||||||
selectPageOrScreen: type => {
|
|
||||||
store.update(state => {
|
|
||||||
state.currentFrontEndType = type
|
|
||||||
|
|
||||||
const page = get(selectedPage)
|
|
||||||
|
|
||||||
const pageOrScreen = type === "page" ? page : page._screens[0]
|
|
||||||
|
|
||||||
state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
|
|
||||||
state.currentPreviewItem = pageOrScreen
|
|
||||||
state.currentView = "detail"
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
},
|
|
||||||
routing: {
|
routing: {
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
const response = await api.get("/api/routing")
|
const response = await api.get("/api/routing")
|
||||||
|
@ -133,167 +83,166 @@ export const getFrontendStore = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
screens: {
|
screens: {
|
||||||
select: screenId => {
|
select: async screenId => {
|
||||||
|
let promise
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const screen = get(allScreens).find(screen => screen._id === screenId)
|
const screen = get(allScreens).find(screen => screen._id === screenId)
|
||||||
state.currentPreviewItem = screen
|
state.currentFrontEndType = FrontendTypes.SCREEN
|
||||||
state.currentFrontEndType = "screen"
|
state.currentAssetId = screenId
|
||||||
state.currentView = "detail"
|
state.currentView = "detail"
|
||||||
|
|
||||||
store.actions.screens.regenerateCssForCurrentScreen()
|
promise = store.actions.screens.regenerateCss(screen)
|
||||||
const safeProps = makePropsSafe(
|
state.selectedComponentId = screen.props._id
|
||||||
state.components[screen.props._component],
|
|
||||||
screen.props
|
|
||||||
)
|
|
||||||
screen.props = safeProps
|
|
||||||
state.currentComponentInfo = safeProps
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
await promise
|
||||||
},
|
},
|
||||||
create: async screen => {
|
create: async screen => {
|
||||||
let savePromise
|
screen = await store.actions.screens.save(screen)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.currentPreviewItem = screen
|
state.currentAssetId = screen._id
|
||||||
state.currentComponentInfo = screen.props
|
state.selectedComponentId = screen.props._id
|
||||||
state.currentFrontEndType = "screen"
|
state.currentFrontEndType = FrontendTypes.SCREEN
|
||||||
|
|
||||||
if (state.currentPreviewItem) {
|
|
||||||
store.actions.screens.regenerateCss(state.currentPreviewItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
savePromise = store.actions.screens.save(screen)
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
return screen
|
||||||
await savePromise
|
|
||||||
},
|
},
|
||||||
save: async screen => {
|
save: async screen => {
|
||||||
const page = get(selectedPage)
|
|
||||||
const currentPageScreens = page._screens
|
|
||||||
|
|
||||||
const creatingNewScreen = screen._id === undefined
|
const creatingNewScreen = screen._id === undefined
|
||||||
|
const response = await api.post(`/api/screens`, screen)
|
||||||
|
screen = await response.json()
|
||||||
|
|
||||||
let savePromise
|
|
||||||
const response = await api.post(`/api/screens/${page._id}`, screen)
|
|
||||||
const json = await response.json()
|
|
||||||
screen._rev = json.rev
|
|
||||||
screen._id = json.id
|
|
||||||
const foundScreen = page._screens.findIndex(el => el._id === screen._id)
|
|
||||||
if (foundScreen !== -1) {
|
|
||||||
page._screens.splice(foundScreen, 1)
|
|
||||||
}
|
|
||||||
page._screens.push(screen)
|
|
||||||
|
|
||||||
// TODO: should carry out all server updates to screen in a single call
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
page._screens = currentPageScreens
|
const foundScreen = state.screens.findIndex(
|
||||||
|
el => el._id === screen._id
|
||||||
|
)
|
||||||
|
if (foundScreen !== -1) {
|
||||||
|
state.screens.splice(foundScreen, 1)
|
||||||
|
}
|
||||||
|
state.screens.push(screen)
|
||||||
|
|
||||||
if (creatingNewScreen) {
|
if (creatingNewScreen) {
|
||||||
state.currentPreviewItem = screen
|
|
||||||
const safeProps = makePropsSafe(
|
const safeProps = makePropsSafe(
|
||||||
state.components[screen.props._component],
|
state.components[screen.props._component],
|
||||||
screen.props
|
screen.props
|
||||||
)
|
)
|
||||||
state.currentComponentInfo = safeProps
|
state.selectedComponentId = safeProps._id
|
||||||
screen.props = safeProps
|
screen.props = safeProps
|
||||||
}
|
}
|
||||||
savePromise = store.actions.pages.save()
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
if (savePromise) await savePromise
|
return screen
|
||||||
},
|
},
|
||||||
regenerateCss: screen => {
|
regenerateCss: async asset => {
|
||||||
screen._css = generate_screen_css([screen.props])
|
const response = await api.post("/api/css/generate", asset)
|
||||||
|
asset._css = (await response.json())?.css
|
||||||
},
|
},
|
||||||
regenerateCssForCurrentScreen: () => {
|
regenerateCssForCurrentScreen: async () => {
|
||||||
const { currentPreviewItem } = get(store)
|
const asset = get(currentAsset)
|
||||||
if (currentPreviewItem) {
|
if (asset) {
|
||||||
store.actions.screens.regenerateCss(currentPreviewItem)
|
await store.actions.screens.regenerateCss(asset)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
delete: async screens => {
|
delete: async screens => {
|
||||||
let deletePromise
|
|
||||||
|
|
||||||
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
const screensToDelete = Array.isArray(screens) ? screens : [screens]
|
||||||
|
|
||||||
|
const screenDeletePromises = []
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const currentPage = get(selectedPage)
|
|
||||||
|
|
||||||
for (let screenToDelete of screensToDelete) {
|
for (let screenToDelete of screensToDelete) {
|
||||||
// Remove screen from current page as well
|
state.screens = state.screens.filter(
|
||||||
// TODO: Should be done server side
|
screen => screen._id !== screenToDelete._id
|
||||||
currentPage._screens = currentPage._screens.filter(
|
|
||||||
scr => scr._id !== screenToDelete._id
|
|
||||||
)
|
)
|
||||||
|
screenDeletePromises.push(
|
||||||
deletePromise = api.delete(
|
api.delete(
|
||||||
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
|
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
await deletePromise
|
await Promise.all(screenDeletePromises)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
saveSelected: async () => {
|
saveSelected: async () => {
|
||||||
const state = get(store)
|
const state = get(store)
|
||||||
if (state.currentFrontEndType !== "page") {
|
const selectedAsset = get(currentAsset)
|
||||||
await store.actions.screens.save(state.currentPreviewItem)
|
|
||||||
|
if (state.currentFrontEndType !== FrontendTypes.LAYOUT) {
|
||||||
|
await store.actions.screens.save(selectedAsset)
|
||||||
|
} else {
|
||||||
|
await store.actions.layouts.save(selectedAsset)
|
||||||
}
|
}
|
||||||
await store.actions.pages.save()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
layouts: {
|
||||||
select: pageName => {
|
select: async layoutId => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const currentPage = state.pages[pageName]
|
const layout = store.actions.layouts.find(layoutId)
|
||||||
|
|
||||||
state.currentFrontEndType = "page"
|
state.currentFrontEndType = FrontendTypes.LAYOUT
|
||||||
state.currentView = "detail"
|
state.currentView = "detail"
|
||||||
state.currentPageName = pageName
|
|
||||||
|
|
||||||
// This is the root of many problems.
|
state.currentAssetId = layout._id
|
||||||
// Uncaught (in promise) TypeError: Cannot read property '_component' of undefined
|
state.selectedComponentId = layout.props._id
|
||||||
// it appears that the currentPage sometimes has _props instead of props
|
|
||||||
// why
|
|
||||||
const safeProps = makePropsSafe(
|
|
||||||
state.components[currentPage.props._component],
|
|
||||||
currentPage.props
|
|
||||||
)
|
|
||||||
state.currentComponentInfo = safeProps
|
|
||||||
currentPage.props = safeProps
|
|
||||||
state.currentPreviewItem = state.pages[pageName]
|
|
||||||
store.actions.screens.regenerateCssForCurrentScreen()
|
|
||||||
|
|
||||||
for (let screen of get(allScreens)) {
|
|
||||||
screen._css = generate_screen_css([screen.props])
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
let cssPromises = []
|
||||||
save: async page => {
|
cssPromises.push(store.actions.screens.regenerateCssForCurrentScreen())
|
||||||
const storeContents = get(store)
|
|
||||||
const pageName = storeContents.currentPageName || "main"
|
|
||||||
const pageToSave = page || storeContents.pages[pageName]
|
|
||||||
|
|
||||||
// TODO: revisit. This sends down a very weird payload
|
for (let screen of get(allScreens)) {
|
||||||
const response = await api.post(`/api/pages/${pageToSave._id}`, {
|
cssPromises.push(store.actions.screens.regenerateCss(screen))
|
||||||
page: {
|
}
|
||||||
componentLibraries: storeContents.pages.componentLibraries,
|
await Promise.all(cssPromises)
|
||||||
...pageToSave,
|
},
|
||||||
},
|
save: async layout => {
|
||||||
screens: pageToSave._screens,
|
const layoutToSave = cloneDeep(layout)
|
||||||
})
|
delete layoutToSave._css
|
||||||
|
|
||||||
|
const response = await api.post(`/api/layouts`, layoutToSave)
|
||||||
|
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
|
||||||
if (!json.ok) throw new Error("Error updating page")
|
store.update(state => {
|
||||||
|
const layoutIdx = state.layouts.findIndex(
|
||||||
|
stateLayout => stateLayout._id === json._id
|
||||||
|
)
|
||||||
|
|
||||||
|
if (layoutIdx >= 0) {
|
||||||
|
// update existing layout
|
||||||
|
state.layouts.splice(layoutIdx, 1, json)
|
||||||
|
} else {
|
||||||
|
// save new layout
|
||||||
|
state.layouts.push(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.currentAssetId = json._id
|
||||||
|
state.selectedComponentId = json.props._id
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
|
find: layoutId => {
|
||||||
|
if (!layoutId) {
|
||||||
|
return get(mainLayout)
|
||||||
|
}
|
||||||
|
const storeContents = get(store)
|
||||||
|
return storeContents.layouts.find(layout => layout._id === layoutId)
|
||||||
|
},
|
||||||
|
delete: async layoutToDelete => {
|
||||||
|
const response = await api.delete(
|
||||||
|
`/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}`
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
const json = await response.json()
|
||||||
|
throw new Error(json.message)
|
||||||
|
}
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.pages[pageName]._rev = json.rev
|
state.layouts = state.layouts.filter(
|
||||||
|
layout => layout._id !== layoutToDelete._id
|
||||||
|
)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -301,17 +250,19 @@ export const getFrontendStore = () => {
|
||||||
components: {
|
components: {
|
||||||
select: component => {
|
select: component => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const componentDef = component._component.startsWith("##")
|
state.selectedComponentId = component._id
|
||||||
? component
|
|
||||||
: state.components[component._component]
|
|
||||||
state.currentComponentInfo = makePropsSafe(componentDef, component)
|
|
||||||
state.currentView = "component"
|
state.currentView = "component"
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
create: (componentToAdd, presetProps) => {
|
create: (componentToAdd, presetProps) => {
|
||||||
|
const selectedAsset = get(currentAsset)
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
function findSlot(component_array) {
|
function findSlot(component_array) {
|
||||||
|
if (!component_array) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for (let component of component_array) {
|
for (let component of component_array) {
|
||||||
if (component._component === "##builtin/screenslot") {
|
if (component._component === "##builtin/screenslot") {
|
||||||
return true
|
return true
|
||||||
|
@ -324,7 +275,7 @@ export const getFrontendStore = () => {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
componentToAdd.startsWith("##") &&
|
componentToAdd.startsWith("##") &&
|
||||||
findSlot(state.pages[state.currentPageName].props._children)
|
findSlot(selectedAsset?.props._children)
|
||||||
) {
|
) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@ -340,29 +291,34 @@ export const getFrontendStore = () => {
|
||||||
_instanceName: instanceName,
|
_instanceName: instanceName,
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentComponent =
|
const selected = get(selectedComponent)
|
||||||
state.components[state.currentComponentInfo._component]
|
|
||||||
|
|
||||||
const targetParent = currentComponent.children
|
const currentComponentDefinition =
|
||||||
? state.currentComponentInfo
|
state.components[selected._component]
|
||||||
: getParent(
|
|
||||||
state.currentPreviewItem.props,
|
|
||||||
state.currentComponentInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
// Don't continue if there's no parent
|
const allowsChildren = currentComponentDefinition.children
|
||||||
if (!targetParent) {
|
|
||||||
return state
|
// 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(
|
// Don't continue if there's no parent
|
||||||
newComponent.props
|
if (!targetParent) return state
|
||||||
)
|
|
||||||
|
// Push the new component
|
||||||
|
targetParent._children.push(newComponent.props)
|
||||||
|
|
||||||
store.actions.preview.saveSelected()
|
store.actions.preview.saveSelected()
|
||||||
|
|
||||||
state.currentView = "component"
|
state.currentView = "component"
|
||||||
state.currentComponentInfo = newComponent.props
|
state.selectedComponentId = newComponent.props._id
|
||||||
|
|
||||||
analytics.captureEvent("Added Component", {
|
analytics.captureEvent("Added Component", {
|
||||||
name: newComponent.props._component,
|
name: newComponent.props._component,
|
||||||
})
|
})
|
||||||
|
@ -370,14 +326,12 @@ export const getFrontendStore = () => {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
copy: (component, cut = false) => {
|
copy: (component, cut = false) => {
|
||||||
|
const selectedAsset = get(currentAsset)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.componentToPaste = cloneDeep(component)
|
state.componentToPaste = cloneDeep(component)
|
||||||
state.componentToPaste.isCut = cut
|
state.componentToPaste.isCut = cut
|
||||||
if (cut) {
|
if (cut) {
|
||||||
const parent = getParent(
|
const parent = findParent(selectedAsset.props, component._id)
|
||||||
state.currentPreviewItem.props,
|
|
||||||
component._id
|
|
||||||
)
|
|
||||||
parent._children = parent._children.filter(
|
parent._children = parent._children.filter(
|
||||||
child => child._id !== component._id
|
child => child._id !== component._id
|
||||||
)
|
)
|
||||||
|
@ -387,7 +341,9 @@ export const getFrontendStore = () => {
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
paste: (targetComponent, mode) => {
|
paste: async (targetComponent, mode) => {
|
||||||
|
const selectedAsset = get(currentAsset)
|
||||||
|
let promises = []
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (!state.componentToPaste) return state
|
if (!state.componentToPaste) return state
|
||||||
|
|
||||||
|
@ -406,54 +362,56 @@ export const getFrontendStore = () => {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent = getParent(
|
const parent = findParent(selectedAsset.props, targetComponent)
|
||||||
state.currentPreviewItem.props,
|
|
||||||
targetComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
const targetIndex = parent._children.indexOf(targetComponent)
|
const targetIndex = parent._children.indexOf(targetComponent)
|
||||||
const index = mode === "above" ? targetIndex : targetIndex + 1
|
const index = mode === "above" ? targetIndex : targetIndex + 1
|
||||||
parent._children.splice(index, 0, cloneDeep(componentToPaste))
|
parent._children.splice(index, 0, cloneDeep(componentToPaste))
|
||||||
|
|
||||||
store.actions.screens.regenerateCssForCurrentScreen()
|
promises.push(store.actions.screens.regenerateCssForCurrentScreen())
|
||||||
store.actions.preview.saveSelected()
|
promises.push(store.actions.preview.saveSelected())
|
||||||
store.actions.components.select(componentToPaste)
|
store.actions.components.select(componentToPaste)
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
await Promise.all(promises)
|
||||||
},
|
},
|
||||||
updateStyle: (type, name, value) => {
|
updateStyle: async (type, name, value) => {
|
||||||
store.update(state => {
|
let promises = []
|
||||||
if (!state.currentComponentInfo._styles) {
|
const selected = get(selectedComponent)
|
||||||
state.currentComponentInfo._styles = {}
|
|
||||||
}
|
|
||||||
state.currentComponentInfo._styles[type][name] = value
|
|
||||||
|
|
||||||
store.actions.screens.regenerateCssForCurrentScreen()
|
store.update(state => {
|
||||||
|
if (!selected._styles) {
|
||||||
|
selected._styles = {}
|
||||||
|
}
|
||||||
|
selected._styles[type][name] = value
|
||||||
|
|
||||||
|
promises.push(store.actions.screens.regenerateCssForCurrentScreen())
|
||||||
|
|
||||||
// save without messing with the store
|
// save without messing with the store
|
||||||
store.actions.preview.saveSelected()
|
promises.push(store.actions.preview.saveSelected())
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
await Promise.all(promises)
|
||||||
},
|
},
|
||||||
updateProp: (name, value) => {
|
updateProp: (name, value) => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
let current_component = state.currentComponentInfo
|
let current_component = get(selectedComponent)
|
||||||
current_component[name] = value
|
current_component[name] = value
|
||||||
|
|
||||||
state.currentComponentInfo = current_component
|
state.selectedComponentId = current_component._id
|
||||||
store.actions.preview.saveSelected()
|
store.actions.preview.saveSelected()
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
findRoute: component => {
|
findRoute: component => {
|
||||||
// Gets all the components to needed to construct a path.
|
// Gets all the components to needed to construct a path.
|
||||||
const tempStore = get(store)
|
const selectedAsset = get(currentAsset)
|
||||||
let pathComponents = []
|
let pathComponents = []
|
||||||
let parent = component
|
let parent = component
|
||||||
let root = false
|
let root = false
|
||||||
while (!root) {
|
while (!root) {
|
||||||
parent = getParent(tempStore.currentPreviewItem.props, parent)
|
parent = findParent(selectedAsset.props, parent)
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
root = true
|
root = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -461,7 +419,7 @@ export const getFrontendStore = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove root entry since it's the screen or page layout.
|
// Remove root entry since it's the screen or layout.
|
||||||
// Reverse array since we need the correct order of the IDs
|
// Reverse array since we need the correct order of the IDs
|
||||||
const reversedComponents = pathComponents.reverse().slice(1)
|
const reversedComponents = pathComponents.reverse().slice(1)
|
||||||
|
|
||||||
|
@ -476,11 +434,12 @@ export const getFrontendStore = () => {
|
||||||
},
|
},
|
||||||
links: {
|
links: {
|
||||||
save: async (url, title) => {
|
save: async (url, title) => {
|
||||||
let savePromise
|
let promises = []
|
||||||
|
const layout = get(mainLayout)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
// Try to extract a nav component from the master screen
|
// Try to extract a nav component from the master layout
|
||||||
const nav = findChildComponentType(
|
const nav = findChildComponentType(
|
||||||
state.pages.main,
|
layout,
|
||||||
"@budibase/standard-components/navigation"
|
"@budibase/standard-components/navigation"
|
||||||
)
|
)
|
||||||
if (nav) {
|
if (nav) {
|
||||||
|
@ -513,18 +472,18 @@ export const getFrontendStore = () => {
|
||||||
}).props
|
}).props
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save page and regenerate all CSS because otherwise weird things happen
|
// Save layout and regenerate all CSS because otherwise weird things happen
|
||||||
nav._children = [...nav._children, newLink]
|
nav._children = [...nav._children, newLink]
|
||||||
state.currentPageName = "main"
|
state.currentAssetId = layout._id
|
||||||
store.actions.screens.regenerateCss(state.pages.main)
|
promises.push(store.actions.screens.regenerateCss(layout))
|
||||||
for (let screen of state.pages.main._screens) {
|
for (let screen of get(allScreens)) {
|
||||||
store.actions.screens.regenerateCss(screen)
|
promises.push(store.actions.screens.regenerateCss(screen))
|
||||||
}
|
}
|
||||||
savePromise = store.actions.pages.save()
|
promises.push(store.actions.layouts.save(layout))
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
await savePromise
|
await Promise.all(promises)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,7 +14,6 @@ export class Component extends BaseStructure {
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
_code: "",
|
|
||||||
type: "",
|
type: "",
|
||||||
_instanceName: "",
|
_instanceName: "",
|
||||||
_children: [],
|
_children: [],
|
||||||
|
|
|
@ -4,6 +4,7 @@ export class Screen extends BaseStructure {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(true)
|
super(true)
|
||||||
this._json = {
|
this._json = {
|
||||||
|
layoutId: "layout_private_master",
|
||||||
props: {
|
props: {
|
||||||
_id: "",
|
_id: "",
|
||||||
_component: "",
|
_component: "",
|
||||||
|
@ -18,7 +19,7 @@ export class Screen extends BaseStructure {
|
||||||
},
|
},
|
||||||
routing: {
|
routing: {
|
||||||
route: "",
|
route: "",
|
||||||
roleId: "",
|
roleId: "BASIC",
|
||||||
},
|
},
|
||||||
name: "screen-id",
|
name: "screen-id",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
import { getBuiltin } from "components/userInterface/pagesParsing/createProps"
|
import { getBuiltin } from "components/userInterface/assetParsing/createProps"
|
||||||
import { uuid } from "./uuid"
|
import { uuid } from "./uuid"
|
||||||
import getNewComponentName from "./getNewComponentName"
|
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
|
let parent
|
||||||
walkProps(rootProps, (p, breakWalk) => {
|
walkProps(rootProps, (props, breakWalk) => {
|
||||||
if (
|
if (
|
||||||
p._children &&
|
props._children &&
|
||||||
(p._children.includes(child) || p._children.some(c => c._id === child))
|
(props._children.includes(child) ||
|
||||||
|
props._children.some(c => c._id === child))
|
||||||
) {
|
) {
|
||||||
parent = p
|
parent = props
|
||||||
breakWalk()
|
breakWalk()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -51,14 +51,13 @@
|
||||||
const screens = screenTemplates($store, [table])
|
const screens = screenTemplates($store, [table])
|
||||||
.filter(template => defaultScreens.includes(template.id))
|
.filter(template => defaultScreens.includes(template.id))
|
||||||
.map(template => template.create())
|
.map(template => template.create())
|
||||||
store.actions.pages.select("main")
|
|
||||||
for (let screen of screens) {
|
for (let screen of screens) {
|
||||||
// Record the table that created this screen so we can link it later
|
// Record the table that created this screen so we can link it later
|
||||||
screen.autoTableId = table._id
|
screen.autoTableId = table._id
|
||||||
await store.actions.screens.create(screen)
|
await store.actions.screens.create(screen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create autolink to newly created list page
|
// Create autolink to newly created list screen
|
||||||
const listScreen = screens.find(screen =>
|
const listScreen = screens.find(screen =>
|
||||||
screen.props._instanceName.endsWith("List")
|
screen.props._instanceName.endsWith("List")
|
||||||
)
|
)
|
||||||
|
|
|
@ -153,9 +153,8 @@
|
||||||
const pkg = await applicationPkg.json()
|
const pkg = await applicationPkg.json()
|
||||||
if (applicationPkg.ok) {
|
if (applicationPkg.ok) {
|
||||||
backendUiStore.actions.reset()
|
backendUiStore.actions.reset()
|
||||||
pkg.justCreated = true
|
|
||||||
await store.actions.initialise(pkg)
|
await store.actions.initialise(pkg)
|
||||||
automationStore.actions.fetch()
|
await automationStore.actions.fetch()
|
||||||
} else {
|
} else {
|
||||||
throw new Error(pkg)
|
throw new Error(pkg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { store } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import iframeTemplate from "./iframeTemplate"
|
import iframeTemplate from "./iframeTemplate"
|
||||||
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
|
||||||
|
import { FrontendTypes } from "../../../constants"
|
||||||
|
|
||||||
let iframe
|
let iframe
|
||||||
|
let layout
|
||||||
|
let screen
|
||||||
|
|
||||||
// Create screen slot placeholder for use when a page is selected rather
|
// Create screen slot placeholder for use when a page is selected rather
|
||||||
// than a screen
|
// than a screen
|
||||||
|
@ -16,12 +19,21 @@
|
||||||
.json()
|
.json()
|
||||||
|
|
||||||
// Extract data to pass to the iframe
|
// Extract data to pass to the iframe
|
||||||
$: page = $store.pages[$store.currentPageName]
|
$: {
|
||||||
$: screen =
|
if ($store.currentFrontEndType === FrontendTypes.LAYOUT) {
|
||||||
$store.currentFrontEndType === "page"
|
layout = $currentAsset
|
||||||
? screenPlaceholder
|
screen = screenPlaceholder
|
||||||
: $store.currentPreviewItem
|
} else {
|
||||||
$: selectedComponentId = $store.currentComponentInfo?._id ?? ""
|
screen = $currentAsset
|
||||||
|
layout = $store.layouts.find(layout => layout._id === screen?.layoutId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$: selectedComponentId = $store.selectedComponentId ?? ""
|
||||||
|
$: previewData = {
|
||||||
|
layout,
|
||||||
|
screen,
|
||||||
|
selectedComponentId,
|
||||||
|
}
|
||||||
|
|
||||||
// Saving pages and screens to the DB causes them to have _revs.
|
// Saving pages and screens to the DB causes them to have _revs.
|
||||||
// These revisions change every time a save happens and causes
|
// These revisions change every time a save happens and causes
|
||||||
|
@ -29,7 +41,7 @@
|
||||||
// definition hasn't changed.
|
// definition hasn't changed.
|
||||||
// By deleting all _rev properties we can avoid this and increase
|
// By deleting all _rev properties we can avoid this and increase
|
||||||
// performance.
|
// performance.
|
||||||
$: json = JSON.stringify({ page, screen, selectedComponentId })
|
$: json = JSON.stringify(previewData)
|
||||||
$: strippedJson = json.replaceAll(/"_rev":\s*"[^"]+"/g, `"_rev":""`)
|
$: strippedJson = json.replaceAll(/"_rev":\s*"[^"]+"/g, `"_rev":""`)
|
||||||
|
|
||||||
// Update the iframe with the builder info to render the correct preview
|
// Update the iframe with the builder info to render the correct preview
|
||||||
|
@ -57,13 +69,11 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="component-container">
|
<div class="component-container">
|
||||||
{#if $store.currentPreviewItem}
|
<iframe
|
||||||
<iframe
|
style="height: 100%; width: 100%"
|
||||||
style="height: 100%; width: 100%"
|
title="componentPreview"
|
||||||
title="componentPreview"
|
bind:this={iframe}
|
||||||
bind:this={iframe}
|
srcdoc={iframeTemplate} />
|
||||||
srcdoc={iframeTemplate} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
height: 100% !important;
|
||||||
|
font-family: Inter, sans-serif !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src='/assets/budibase-client.js'></script>
|
||||||
|
<script>
|
||||||
|
function receiveMessage(event) {
|
||||||
|
if (!event.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract data from message
|
||||||
|
const { selectedComponentId, layout, screen } = JSON.parse(event.data)
|
||||||
|
|
||||||
|
// Set some flags so the app knows we're in the builder
|
||||||
|
window["##BUDIBASE_IN_BUILDER##"] = true
|
||||||
|
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
|
||||||
|
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
|
||||||
|
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
|
||||||
|
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
|
||||||
|
|
||||||
|
// Initialise app
|
||||||
|
if (window.loadBudibase) {
|
||||||
|
loadBudibase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore clicks
|
||||||
|
["click", "mousedown"].forEach(type => {
|
||||||
|
document.addEventListener(type, function(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
return false
|
||||||
|
}, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener("message", receiveMessage)
|
||||||
|
window.dispatchEvent(new Event("bb-ready"))
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,55 +1 @@
|
||||||
export default `<html>
|
export { default } from "./iframeTemplate.html"
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono">
|
|
||||||
<style>
|
|
||||||
body, html {
|
|
||||||
height: 100% !important;
|
|
||||||
font-family: Inter !important;
|
|
||||||
margin: 0px !important;
|
|
||||||
}
|
|
||||||
*, *:before, *:after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src='/assets/budibase-client.js'></script>
|
|
||||||
<script>
|
|
||||||
function receiveMessage(event) {
|
|
||||||
if (!event.data) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract data from message
|
|
||||||
const { selectedComponentId, page, screen } = JSON.parse(event.data)
|
|
||||||
|
|
||||||
// Set some flags so the app knows we're in the builder
|
|
||||||
window["##BUDIBASE_IN_BUILDER##"] = true
|
|
||||||
window["##BUDIBASE_PREVIEW_PAGE##"] = page
|
|
||||||
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
|
|
||||||
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
|
|
||||||
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
|
|
||||||
|
|
||||||
// Initialise app
|
|
||||||
if (window.loadBudibase) {
|
|
||||||
loadBudibase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let selectedComponentStyle
|
|
||||||
|
|
||||||
// Ignore clicks
|
|
||||||
["click", "mousedown"].forEach(type => {
|
|
||||||
document.addEventListener(type, function(e) {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
return false
|
|
||||||
}, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
window.addEventListener("message", receiveMessage)
|
|
||||||
window.dispatchEvent(new Event("bb-ready"))
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>`
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
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 { getComponentDefinition } from "builderStore/storeUtils"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { last } from "lodash/fp"
|
import { last } from "lodash/fp"
|
||||||
import { getParent } from "builderStore/storeUtils"
|
import { findParent } from "builderStore/storeUtils"
|
||||||
import { DropdownMenu } from "@budibase/bbui"
|
import { DropdownMenu } from "@budibase/bbui"
|
||||||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
|
||||||
|
@ -28,12 +29,13 @@
|
||||||
const selectComponent = component => {
|
const selectComponent = component => {
|
||||||
store.actions.components.select(component)
|
store.actions.components.select(component)
|
||||||
const path = store.actions.components.findRoute(component)
|
const path = store.actions.components.findRoute(component)
|
||||||
$goto(`./:page/:screen/${path}`)
|
$goto(`./${$store.currentFrontEndType}/${path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveUpComponent = () => {
|
const moveUpComponent = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const parent = getParent(state.currentPreviewItem.props, component)
|
const asset = get(currentAsset)
|
||||||
|
const parent = findParent(asset.props, component)
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const currentIndex = parent._children.indexOf(component)
|
const currentIndex = parent._children.indexOf(component)
|
||||||
|
@ -43,7 +45,7 @@
|
||||||
newChildren.splice(currentIndex - 1, 0, component)
|
newChildren.splice(currentIndex - 1, 0, component)
|
||||||
parent._children = newChildren
|
parent._children = newChildren
|
||||||
}
|
}
|
||||||
state.currentComponentInfo = component
|
state.selectedComponentId = component._id
|
||||||
store.actions.preview.saveSelected()
|
store.actions.preview.saveSelected()
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
@ -52,7 +54,8 @@
|
||||||
|
|
||||||
const moveDownComponent = () => {
|
const moveDownComponent = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const parent = getParent(state.currentPreviewItem.props, component)
|
const asset = get(currentAsset)
|
||||||
|
const parent = findParent(asset.props, component)
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
const currentIndex = parent._children.indexOf(component)
|
const currentIndex = parent._children.indexOf(component)
|
||||||
|
@ -62,7 +65,7 @@
|
||||||
newChildren.splice(currentIndex + 1, 0, component)
|
newChildren.splice(currentIndex + 1, 0, component)
|
||||||
parent._children = newChildren
|
parent._children = newChildren
|
||||||
}
|
}
|
||||||
state.currentComponentInfo = component
|
state.selectedComponentId = component._id
|
||||||
store.actions.preview.saveSelected()
|
store.actions.preview.saveSelected()
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
@ -76,10 +79,11 @@
|
||||||
|
|
||||||
const deleteComponent = () => {
|
const deleteComponent = () => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const parent = getParent(state.currentPreviewItem.props, component)
|
const asset = get(currentAsset)
|
||||||
|
const parent = findParent(asset.props, component)
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent._children = parent._children.filter(c => c !== component)
|
parent._children = parent._children.filter(child => child !== component)
|
||||||
selectComponent(parent)
|
selectComponent(parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { store } from "builderStore"
|
import { store, currentAsset } from "builderStore"
|
||||||
import { getComponentDefinition } from "builderStore/storeUtils"
|
import { getComponentDefinition } from "builderStore/storeUtils"
|
||||||
import { DropEffect, DropPosition } from "./dragDropStore"
|
import { DropEffect, DropPosition } from "./dragDropStore"
|
||||||
import ComponentDropdownMenu from "../ComponentDropdownMenu.svelte"
|
import ComponentDropdownMenu from "../ComponentDropdownMenu.svelte"
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
export let currentComponent
|
export let currentComponent
|
||||||
export let onSelect = () => {}
|
export let onSelect = () => {}
|
||||||
export let level = 0
|
export let level = 0
|
||||||
|
|
||||||
export let dragDropStore
|
export let dragDropStore
|
||||||
|
|
||||||
const isScreenslot = name => name === "##builtin/screenslot"
|
const isScreenslot = name => name === "##builtin/screenslot"
|
||||||
|
@ -23,7 +22,7 @@
|
||||||
const path = store.actions.components.findRoute(component)
|
const path = store.actions.components.findRoute(component)
|
||||||
|
|
||||||
// Go to correct URL
|
// Go to correct URL
|
||||||
$goto(`./:page/:screen/${path}`)
|
$goto(`./${$store.currentAssetId}/${path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const dragstart = component => e => {
|
const dragstart = component => e => {
|
||||||
|
@ -73,7 +72,7 @@
|
||||||
text={isScreenslot(component._component) ? 'Screenslot' : component._instanceName}
|
text={isScreenslot(component._component) ? 'Screenslot' : component._instanceName}
|
||||||
withArrow
|
withArrow
|
||||||
indentLevel={level + 3}
|
indentLevel={level + 3}
|
||||||
selected={currentComponent === component}>
|
selected={$store.selectedComponentId === component._id}>
|
||||||
<ComponentDropdownMenu {component} />
|
<ComponentDropdownMenu {component} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<script>
|
||||||
|
import { goto } from "@sveltech/routify"
|
||||||
|
import { store } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import { DropdownMenu, Modal, ModalContent, Input } from "@budibase/bbui"
|
||||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
export let layout
|
||||||
|
|
||||||
|
let confirmDeleteDialog
|
||||||
|
let editLayoutNameModal
|
||||||
|
let dropdown
|
||||||
|
let anchor
|
||||||
|
let name = layout.name
|
||||||
|
|
||||||
|
const deleteLayout = async () => {
|
||||||
|
try {
|
||||||
|
await store.actions.layouts.delete(layout)
|
||||||
|
notifier.success(`Layout ${layout.name} deleted successfully.`)
|
||||||
|
} catch (err) {
|
||||||
|
notifier.danger(`Error deleting layout: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveLayout = async () => {
|
||||||
|
try {
|
||||||
|
const layoutToSave = cloneDeep(layout)
|
||||||
|
layoutToSave.name = name
|
||||||
|
await store.actions.layouts.save(layoutToSave)
|
||||||
|
notifier.success(`Layout saved successfully.`)
|
||||||
|
} catch (err) {
|
||||||
|
notifier.danger(`Error saving layout: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor} on:click|stopPropagation>
|
||||||
|
<div class="icon" on:click={() => dropdown.show()}>
|
||||||
|
<i class="ri-more-line" />
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
<DropdownContainer>
|
||||||
|
<DropdownItem
|
||||||
|
icon="ri-pencil-line"
|
||||||
|
title="Edit"
|
||||||
|
on:click={() => editLayoutNameModal.show()} />
|
||||||
|
<DropdownItem
|
||||||
|
icon="ri-delete-bin-line"
|
||||||
|
title="Delete"
|
||||||
|
on:click={() => confirmDeleteDialog.show()} />
|
||||||
|
</DropdownContainer>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<ConfirmDialog
|
||||||
|
bind:this={confirmDeleteDialog}
|
||||||
|
title="Confirm Deletion"
|
||||||
|
body={'Are you sure you wish to delete this layout?'}
|
||||||
|
okText="Delete Layout"
|
||||||
|
onOk={deleteLayout} />
|
||||||
|
|
||||||
|
<Modal bind:this={editLayoutNameModal}>
|
||||||
|
<ModalContent
|
||||||
|
title="Edit Layout Name"
|
||||||
|
confirmText="Save"
|
||||||
|
onConfirm={saveLayout}
|
||||||
|
disabled={!name}>
|
||||||
|
<Input thin type="text" label="Name" bind:value={name} />
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,25 +1,32 @@
|
||||||
<script>
|
<script>
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { store } from "builderStore"
|
import { store, selectedComponent, currentAsset } from "builderStore"
|
||||||
import instantiateStore from "./dragDropStore"
|
import instantiateStore from "./dragDropStore"
|
||||||
|
|
||||||
import ComponentsTree from "./ComponentTree.svelte"
|
import ComponentTree from "./ComponentTree.svelte"
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
|
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
|
||||||
|
|
||||||
|
const ROUTE_NAME_MAP = {
|
||||||
|
"/": {
|
||||||
|
BASIC: "Home",
|
||||||
|
PUBLIC: "Login",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const dragDropStore = instantiateStore()
|
const dragDropStore = instantiateStore()
|
||||||
|
|
||||||
export let route
|
export let route
|
||||||
export let path
|
export let path
|
||||||
export let indent
|
export let indent
|
||||||
|
|
||||||
$: selectedScreen = $store.currentPreviewItem
|
$: selectedScreen = $currentAsset
|
||||||
|
|
||||||
const changeScreen = screenId => {
|
const changeScreen = screenId => {
|
||||||
// select the route
|
// select the route
|
||||||
store.actions.screens.select(screenId)
|
store.actions.screens.select(screenId)
|
||||||
$goto(`./:page/${screenId}`)
|
$goto(`./${screenId}`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -30,21 +37,21 @@
|
||||||
withArrow={route.subpaths} />
|
withArrow={route.subpaths} />
|
||||||
|
|
||||||
{#each Object.entries(route.subpaths) as [url, subpath]}
|
{#each Object.entries(route.subpaths) as [url, subpath]}
|
||||||
{#each Object.values(subpath.screens) as screenId}
|
{#each Object.entries(subpath.screens) as [role, screenId]}
|
||||||
<NavItem
|
<NavItem
|
||||||
icon="ri-artboard-2-line"
|
icon="ri-artboard-2-line"
|
||||||
indentLevel={indent || 1}
|
indentLevel={indent || 1}
|
||||||
selected={$store.currentPreviewItem._id === screenId}
|
selected={$store.currentAssetId === screenId}
|
||||||
opened={$store.currentPreviewItem._id === screenId}
|
opened={$store.currentAssetId === screenId}
|
||||||
text={url === '/' ? 'Home' : url}
|
text={ROUTE_NAME_MAP[url]?.[role] || url}
|
||||||
withArrow={route.subpaths}
|
withArrow={route.subpaths}
|
||||||
on:click={() => changeScreen(screenId)}>
|
on:click={() => changeScreen(screenId)}>
|
||||||
<ScreenDropdownMenu screen={screenId} />
|
<ScreenDropdownMenu {screenId} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
{#if selectedScreen?._id === screenId}
|
{#if selectedScreen?._id === screenId}
|
||||||
<ComponentsTree
|
<ComponentTree
|
||||||
components={selectedScreen.props._children}
|
components={selectedScreen.props._children}
|
||||||
currentComponent={$store.currentComponentInfo}
|
currentComponent={$selectedComponent}
|
||||||
{dragDropStore} />
|
{dragDropStore} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -3,28 +3,20 @@
|
||||||
import { store, allScreens } from "builderStore"
|
import { store, allScreens } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { DropdownMenu } from "@budibase/bbui"
|
import { DropdownMenu, Modal, ModalContent } from "@budibase/bbui"
|
||||||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
|
||||||
export let screen
|
export let screenId
|
||||||
|
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let dropdown
|
let dropdown
|
||||||
let anchor
|
let anchor
|
||||||
|
|
||||||
|
$: screen = $allScreens.find(screen => screen._id === screenId)
|
||||||
|
|
||||||
const deleteScreen = () => {
|
const deleteScreen = () => {
|
||||||
const screenToDelete = $allScreens.find(scr => scr._id === screen)
|
store.actions.screens.delete(screen)
|
||||||
store.actions.screens.delete(screenToDelete)
|
|
||||||
store.actions.routing.fetch()
|
store.actions.routing.fetch()
|
||||||
// update the page if required
|
|
||||||
store.update(state => {
|
|
||||||
if (state.currentPreviewItem._id === screen) {
|
|
||||||
store.actions.pages.select($store.currentPageName)
|
|
||||||
notifier.success(`Screen ${screenToDelete.name} deleted successfully.`)
|
|
||||||
$goto(`./:page/page-layout`)
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default function() {
|
||||||
if (mousePosition > 0.4 && mousePosition < 0.8) {
|
if (mousePosition > 0.4 && mousePosition < 0.8) {
|
||||||
state.dropPosition = DropPosition.INSIDE
|
state.dropPosition = DropPosition.INSIDE
|
||||||
}
|
}
|
||||||
return
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
// bottom half
|
// bottom half
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
{#each Object.keys($store.routes) as path}
|
{#each Object.keys($store.routes || {}) as path}
|
||||||
<PathTree {path} route={$store.routes[path]} />
|
<PathTree {path} route={$store.routes[path]} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { store } from "builderStore"
|
import { get } from "svelte/store"
|
||||||
|
import { store, selectedComponent, currentAsset } from "builderStore"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
import panelStructure from "./temporaryPanelStructure.js"
|
import panelStructure from "./temporaryPanelStructure.js"
|
||||||
import CategoryTab from "./CategoryTab.svelte"
|
import CategoryTab from "./CategoryTab.svelte"
|
||||||
import DesignView from "./DesignView.svelte"
|
import DesignView from "./DesignView.svelte"
|
||||||
|
@ -14,12 +16,12 @@
|
||||||
|
|
||||||
$: componentInstance =
|
$: componentInstance =
|
||||||
$store.currentView !== "component"
|
$store.currentView !== "component"
|
||||||
? { ...$store.currentPreviewItem, ...$store.currentComponentInfo }
|
? { ...$currentAsset, ...$selectedComponent }
|
||||||
: $store.currentComponentInfo
|
: $selectedComponent
|
||||||
$: componentDefinition = $store.components[componentInstance._component]
|
$: componentDefinition = $store.components[componentInstance._component]
|
||||||
$: componentPropDefinition =
|
$: componentPropDefinition =
|
||||||
flattenedPanel.find(
|
flattenedPanel.find(
|
||||||
//use for getting controls for each component property
|
// use for getting controls for each component property
|
||||||
c => c._component === componentInstance._component
|
c => c._component === componentInstance._component
|
||||||
) || {}
|
) || {}
|
||||||
|
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
|
|
||||||
$: isComponentOrScreen =
|
$: isComponentOrScreen =
|
||||||
$store.currentView === "component" ||
|
$store.currentView === "component" ||
|
||||||
$store.currentFrontEndType === "screen"
|
$store.currentFrontEndType === FrontendTypes.SCREEN
|
||||||
$: isNotScreenslot = componentInstance._component !== "##builtin/screenslot"
|
$: isNotScreenslot = componentInstance._component !== "##builtin/screenslot"
|
||||||
|
|
||||||
$: displayName =
|
$: displayName =
|
||||||
|
@ -58,16 +60,20 @@
|
||||||
return components
|
return components
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPageOrScreenProp(name, value) {
|
function setAssetProps(name, value) {
|
||||||
|
const selectedAsset = get(currentAsset)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (name === "_instanceName" && state.currentFrontEndType === "screen") {
|
if (
|
||||||
state.currentPreviewItem.props[name] = value
|
name === "_instanceName" &&
|
||||||
|
state.currentFrontEndType === FrontendTypes.SCREEN
|
||||||
|
) {
|
||||||
|
selectedAsset.props._instanceName = value
|
||||||
} else {
|
} else {
|
||||||
state.currentPreviewItem[name] = value
|
selectedAsset[name] = value
|
||||||
}
|
}
|
||||||
store.actions.preview.saveSelected()
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
store.actions.preview.saveSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProps(obj, keys) {
|
function getProps(obj, keys) {
|
||||||
|
@ -94,21 +100,12 @@
|
||||||
{panelDefinition}
|
{panelDefinition}
|
||||||
displayNameField={displayName}
|
displayNameField={displayName}
|
||||||
onChange={store.actions.components.updateProp}
|
onChange={store.actions.components.updateProp}
|
||||||
onScreenPropChange={setPageOrScreenProp}
|
onScreenPropChange={setAssetProps}
|
||||||
screenOrPageInstance={$store.currentView !== 'component' && $store.currentPreviewItem} />
|
assetInstance={$store.currentView !== 'component' && $currentAsset} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.title > div:nth-child(1) {
|
|
||||||
grid-column-start: name;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title > div:nth-child(2) {
|
|
||||||
grid-column-start: actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-props-container {
|
.component-props-container {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto, url } from "@sveltech/routify"
|
||||||
import { store } from "builderStore"
|
import { store, currentAssetName, selectedComponent } from "builderStore"
|
||||||
import components from "./temporaryPanelStructure.js"
|
import components from "./temporaryPanelStructure.js"
|
||||||
import { DropdownMenu } from "@budibase/bbui"
|
import { DropdownMenu } from "@budibase/bbui"
|
||||||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
const onComponentChosen = component => {
|
const onComponentChosen = component => {
|
||||||
store.actions.components.create(component._component, component.presetProps)
|
store.actions.components.create(component._component, component.presetProps)
|
||||||
const path = store.actions.components.findRoute($store.currentComponentInfo)
|
const path = store.actions.components.findRoute($selectedComponent)
|
||||||
$goto(`./:page/:screen/${path}`)
|
$goto(`./${$store.currentAssetId}/${path}`)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
align="left">
|
align="left">
|
||||||
<DropdownContainer>
|
<DropdownContainer>
|
||||||
{#each categories[selectedIndex].children as item}
|
{#each categories[selectedIndex].children as item}
|
||||||
{#if !item.showOnPages || item.showOnPages.includes($store.currentPageName)}
|
{#if !item.showOnAsset || item.showOnAsset.includes($currentAssetName)}
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
title={item.name}
|
title={item.name}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { store, allScreens } from "builderStore"
|
import { store, allScreens } from "builderStore"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
import ComponentPropertiesPanel from "./ComponentPropertiesPanel.svelte"
|
import ComponentPropertiesPanel from "./ComponentPropertiesPanel.svelte"
|
||||||
import ComponentSelectionList from "./ComponentSelectionList.svelte"
|
import ComponentSelectionList from "./ComponentSelectionList.svelte"
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
{#if $store.currentFrontEndType === 'page' || $allScreens.length}
|
{#if $store.currentFrontEndType === FrontendTypes.LAYOUT || $allScreens.length}
|
||||||
<div class="switcher">
|
<div class="switcher">
|
||||||
<button
|
<button
|
||||||
class:selected={selected === COMPONENT_SELECTION_TAB}
|
class:selected={selected === COMPONENT_SELECTION_TAB}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label } from "@budibase/bbui"
|
import { Select, Label } from "@budibase/bbui"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
|
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label } from "@budibase/bbui"
|
import { Select, Label } from "@budibase/bbui"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
export let parameters
|
export let parameters
|
||||||
|
@ -8,9 +8,9 @@
|
||||||
let idFields
|
let idFields
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
// accepts an array of field names, and outputs an object of { FieldName: value }
|
// accepts an array of field names, and outputs an object of { FieldName: value }
|
||||||
import { DataList, Label, TextButton, Spacer, Select } from "@budibase/bbui"
|
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 fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
import { CloseCircleIcon, AddIcon } from "components/common/Icons"
|
import { CloseCircleIcon, AddIcon } from "components/common/Icons"
|
||||||
import {
|
import {
|
||||||
|
@ -32,9 +32,9 @@
|
||||||
}))
|
}))
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label } from "@budibase/bbui"
|
import { Select, Label } from "@budibase/bbui"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
import {
|
import {
|
||||||
|
@ -16,9 +16,9 @@
|
||||||
let schemaFields
|
let schemaFields
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label } from "@budibase/bbui"
|
import { Select, Label } from "@budibase/bbui"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
import {
|
import {
|
||||||
|
@ -11,9 +11,9 @@
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,33 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { store, currentScreens } from "builderStore"
|
import { goto, params, url } from "@sveltech/routify"
|
||||||
import api from "builderStore/api"
|
import { store, currentAsset, selectedComponent } from "builderStore"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
import ComponentNavigationTree from "components/userInterface/ComponentNavigationTree/index.svelte"
|
import ComponentNavigationTree from "components/userInterface/ComponentNavigationTree/index.svelte"
|
||||||
import PageLayout from "components/userInterface/PageLayout.svelte"
|
import Layout from "components/userInterface/Layout.svelte"
|
||||||
import PagesList from "components/userInterface/PagesList.svelte"
|
|
||||||
import NewScreenModal from "components/userInterface/NewScreenModal.svelte"
|
import NewScreenModal from "components/userInterface/NewScreenModal.svelte"
|
||||||
import { Modal } from "@budibase/bbui"
|
import NewLayoutModal from "components/userInterface/NewLayoutModal.svelte"
|
||||||
|
import { Modal, Switcher } from "@budibase/bbui"
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: "Screens",
|
||||||
|
key: "screen",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Layouts",
|
||||||
|
key: "layout",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
|
|
||||||
let routes = {}
|
let routes = {}
|
||||||
|
let tab = $params.assetType
|
||||||
|
|
||||||
|
function navigate({ detail }) {
|
||||||
|
if (!detail) return
|
||||||
|
$goto(`../${detail.heading.key}`)
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
store.actions.routing.fetch()
|
store.actions.routing.fetch()
|
||||||
|
@ -18,32 +35,48 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h1>Screens</h1>
|
<Switcher headings={tabs} bind:value={tab} on:change={navigate}>
|
||||||
<i on:click={modal.show} data-cy="new-screen" class="ri-add-circle-fill" />
|
{#if tab === FrontendTypes.SCREEN}
|
||||||
|
<i
|
||||||
|
on:click={modal.show}
|
||||||
|
data-cy="new-screen"
|
||||||
|
class="ri-add-circle-fill" />
|
||||||
|
{#if $currentAsset}
|
||||||
|
<div class="nav-items-container">
|
||||||
|
<ComponentNavigationTree />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<NewScreenModal />
|
||||||
|
</Modal>
|
||||||
|
{:else if tab === FrontendTypes.LAYOUT}
|
||||||
|
<i
|
||||||
|
on:click={modal.show}
|
||||||
|
data-cy="new-layout"
|
||||||
|
class="ri-add-circle-fill" />
|
||||||
|
{#each $store.layouts as layout (layout._id)}
|
||||||
|
<Layout {layout} />
|
||||||
|
{/each}
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<NewLayoutModal />
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
</Switcher>
|
||||||
</div>
|
</div>
|
||||||
<PagesList />
|
|
||||||
<div class="nav-items-container">
|
|
||||||
<PageLayout layout={$store.pages[$store.currentPageName]} />
|
|
||||||
<ComponentNavigationTree />
|
|
||||||
</div>
|
|
||||||
<Modal bind:this={modal}>
|
|
||||||
<NewScreenModal />
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
}
|
position: relative;
|
||||||
.title h1 {
|
|
||||||
font-size: var(--font-size-m);
|
|
||||||
font-weight: 500;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
.title i {
|
.title i {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
.title i:hover {
|
.title i:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
import { goto } from "@sveltech/routify"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
|
import ComponentTree from "./ComponentNavigationTree/ComponentTree.svelte"
|
||||||
|
import LayoutDropdownMenu from "./ComponentNavigationTree/LayoutDropdownMenu.svelte"
|
||||||
|
import initDragDropStore from "./ComponentNavigationTree/dragDropStore"
|
||||||
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
|
import { last } from "lodash/fp"
|
||||||
|
import { store, currentAsset, selectedComponent } from "builderStore"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
|
export let layout
|
||||||
|
|
||||||
|
let confirmDeleteDialog
|
||||||
|
let componentToDelete = ""
|
||||||
|
|
||||||
|
const dragDropStore = initDragDropStore()
|
||||||
|
|
||||||
|
const selectLayout = () => {
|
||||||
|
store.actions.layouts.select(layout._id)
|
||||||
|
$goto(`./${layout._id}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<NavItem
|
||||||
|
border={false}
|
||||||
|
icon="ri-layout-3-line"
|
||||||
|
text={layout.name}
|
||||||
|
withArrow
|
||||||
|
selected={$store.currentAssetId === layout._id}
|
||||||
|
opened={$store.currentAssetId === layout._id}
|
||||||
|
on:click={selectLayout}>
|
||||||
|
<LayoutDropdownMenu {layout} />
|
||||||
|
</NavItem>
|
||||||
|
|
||||||
|
{#if $store.currentAssetId === layout._id && layout.props?._children}
|
||||||
|
<ComponentTree
|
||||||
|
components={layout.props._children}
|
||||||
|
currentComponent={$selectedComponent}
|
||||||
|
{dragDropStore} />
|
||||||
|
{/if}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { store, currentAsset } from "builderStore"
|
||||||
|
import { Select } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select bind:value extraThin secondary on:change>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each $store.layouts as layout}
|
||||||
|
<option value={layout._id}>{layout.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
|
@ -2,12 +2,7 @@
|
||||||
import { params, goto } from "@sveltech/routify"
|
import { params, goto } from "@sveltech/routify"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
|
||||||
const getPage = (state, name) => {
|
const layouts = [
|
||||||
const props = state.pages[name]
|
|
||||||
return { name, props }
|
|
||||||
}
|
|
||||||
|
|
||||||
const pages = [
|
|
||||||
{
|
{
|
||||||
title: "Private",
|
title: "Private",
|
||||||
id: "main",
|
id: "main",
|
||||||
|
@ -18,18 +13,22 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!$store.currentPageName)
|
if (!$store.currentAssetId) {
|
||||||
store.actions.pages.select($params.page ? $params.page : "main")
|
// refactor so the right layout is chosen
|
||||||
|
store.actions.layouts.select($params.layout)
|
||||||
|
}
|
||||||
|
|
||||||
const changePage = id => {
|
const changeLayout = id => {
|
||||||
store.actions.pages.select(id)
|
store.actions.layouts.select(id)
|
||||||
$goto(`./${id}/page-layout`)
|
$goto(`./${id}/layout`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
{#each pages as { title, id }}
|
{#each layouts as { title, id }}
|
||||||
<button class:active={id === $params.page} on:click={() => changePage(id)}>
|
<button
|
||||||
|
class:active={id === $params.layout}
|
||||||
|
on:click={() => changeLayout(id)}>
|
||||||
{title}
|
{title}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { goto } from "@sveltech/routify"
|
||||||
|
import api from "builderStore/api"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { store, backendUiStore, allScreens } from "builderStore"
|
||||||
|
import { Input, ModalContent } from "@budibase/bbui"
|
||||||
|
import analytics from "analytics"
|
||||||
|
|
||||||
|
const CONTAINER = "@budibase/standard-components/container"
|
||||||
|
|
||||||
|
let name = ""
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
try {
|
||||||
|
await store.actions.layouts.save({ name })
|
||||||
|
$goto(`./${$store.currentAssetId}`)
|
||||||
|
notifier.success(`Layout ${name} created successfully`)
|
||||||
|
} catch (err) {
|
||||||
|
notifier.danger(`Error creating layout ${name}.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent title="Create Layout" confirmText="Create" onConfirm={save}>
|
||||||
|
<Input thin label="Name" bind:value={name} />
|
||||||
|
</ModalContent>
|
|
@ -72,7 +72,7 @@
|
||||||
// TODO: need to fix this up correctly
|
// TODO: need to fix this up correctly
|
||||||
draftScreen.routing = { route, roleId: "ADMIN" }
|
draftScreen.routing = { route, roleId: "ADMIN" }
|
||||||
|
|
||||||
await store.actions.screens.create(draftScreen)
|
const createdScreen = await store.actions.screens.create(draftScreen)
|
||||||
if (createLink) {
|
if (createLink) {
|
||||||
await store.actions.components.links.save(route, name)
|
await store.actions.components.links.save(route, name)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$goto(`./:page/${name}`)
|
$goto(`./screen/${createdScreen._id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeNameExists = route => {
|
const routeNameExists = route => {
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
<script>
|
|
||||||
import { goto } from "@sveltech/routify"
|
|
||||||
import ComponentTree from "./ComponentNavigationTree/ComponentTree.svelte"
|
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
|
||||||
import { last } from "lodash/fp"
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import { writable } from "svelte/store"
|
|
||||||
|
|
||||||
export let layout
|
|
||||||
|
|
||||||
let confirmDeleteDialog
|
|
||||||
let componentToDelete = ""
|
|
||||||
|
|
||||||
const dragDropStore = writable({})
|
|
||||||
|
|
||||||
const lastPartOfName = c =>
|
|
||||||
c && last(c.name ? c.name.split("/") : c._component.split("/"))
|
|
||||||
|
|
||||||
$: _layout = {
|
|
||||||
component: layout,
|
|
||||||
title: lastPartOfName(layout),
|
|
||||||
}
|
|
||||||
|
|
||||||
const setCurrentScreenToLayout = () => {
|
|
||||||
store.actions.selectPageOrScreen("page")
|
|
||||||
$goto("./:page/page-layout")
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<NavItem
|
|
||||||
border={false}
|
|
||||||
icon="ri-layout-3-line"
|
|
||||||
text="Master Screen"
|
|
||||||
withArrow
|
|
||||||
selected={$store.currentComponentInfo?._id === _layout.component.props._id}
|
|
||||||
opened={$store.currentPreviewItem?.name === _layout.title}
|
|
||||||
on:click={setCurrentScreenToLayout} />
|
|
||||||
|
|
||||||
{#if $store.currentPreviewItem?.name === _layout.title && _layout.component.props._children}
|
|
||||||
<ComponentTree
|
|
||||||
components={_layout.component.props._children}
|
|
||||||
currentComponent={$store.currentComponentInfo}
|
|
||||||
{dragDropStore} />
|
|
||||||
{/if}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<script>
|
|
||||||
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte"
|
|
||||||
</script>
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import Input from "./PropertyPanelControls/Input.svelte"
|
import Input from "./PropertyPanelControls/Input.svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
import {
|
import {
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
|
@ -35,9 +35,9 @@
|
||||||
function getBindableProperties() {
|
function getBindableProperties() {
|
||||||
// Get all bindableProperties
|
// Get all bindableProperties
|
||||||
bindableProperties = fetchBindableProperties({
|
bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { DataList } from "@budibase/bbui"
|
import { DataList } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store, allScreens, backendUiStore } from "builderStore"
|
import { store, allScreens, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -27,9 +27,9 @@
|
||||||
]
|
]
|
||||||
|
|
||||||
const bindableProperties = fetchBindableProperties({
|
const bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
import PropertyControl from "./PropertyControl.svelte"
|
import PropertyControl from "./PropertyControl.svelte"
|
||||||
|
import LayoutSelect from "./LayoutSelect.svelte"
|
||||||
import Input from "./PropertyPanelControls/Input.svelte"
|
import Input from "./PropertyPanelControls/Input.svelte"
|
||||||
import { goto } from "@sveltech/routify"
|
|
||||||
import { excludeProps } from "./propertyCategories.js"
|
import { excludeProps } from "./propertyCategories.js"
|
||||||
import { store, allScreens } from "builderStore"
|
import { store, allScreens, currentAsset } from "builderStore"
|
||||||
import { walkProps } from "builderStore/storeUtils"
|
import { walkProps } from "builderStore/storeUtils"
|
||||||
|
|
||||||
export let panelDefinition = []
|
export let panelDefinition = []
|
||||||
|
@ -13,13 +14,13 @@
|
||||||
export let onChange = () => {}
|
export let onChange = () => {}
|
||||||
export let onScreenPropChange = () => {}
|
export let onScreenPropChange = () => {}
|
||||||
export let displayNameField = false
|
export let displayNameField = false
|
||||||
export let screenOrPageInstance
|
export let assetInstance
|
||||||
|
|
||||||
let pageScreenProps = ["title", "favicon", "description", "route"]
|
let assetProps = ["title", "description", "route", "layoutId"]
|
||||||
let duplicateName = false
|
let duplicateName = false
|
||||||
|
|
||||||
const propExistsOnComponentDef = prop =>
|
const propExistsOnComponentDef = prop =>
|
||||||
pageScreenProps.includes(prop) || prop in componentDefinition.props
|
assetProps.includes(prop) || prop in componentDefinition.props
|
||||||
|
|
||||||
function handleChange(key, data) {
|
function handleChange(key, data) {
|
||||||
data.target ? onChange(key, data.target.value) : onChange(key, data)
|
data.target ? onChange(key, data.target.value) : onChange(key, data)
|
||||||
|
@ -28,12 +29,10 @@
|
||||||
const screenDefinition = [
|
const screenDefinition = [
|
||||||
{ key: "description", label: "Description", control: Input },
|
{ key: "description", label: "Description", control: Input },
|
||||||
{ key: "route", label: "Route", control: Input },
|
{ key: "route", label: "Route", control: Input },
|
||||||
|
{ key: "layoutId", label: "Layout", control: LayoutSelect },
|
||||||
]
|
]
|
||||||
|
|
||||||
const pageDefinition = [
|
const layoutDefinition = [{ key: "title", label: "Title", control: Input }]
|
||||||
{ key: "title", label: "Title", control: Input },
|
|
||||||
{ key: "favicon", label: "Favicon", control: Input },
|
|
||||||
]
|
|
||||||
|
|
||||||
const canRenderControl = (key, dependsOn) => {
|
const canRenderControl = (key, dependsOn) => {
|
||||||
let test = !isEmpty(componentInstance[dependsOn])
|
let test = !isEmpty(componentInstance[dependsOn])
|
||||||
|
@ -44,8 +43,8 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
$: isPage = screenOrPageInstance && screenOrPageInstance.favicon
|
$: isLayout = assetInstance && assetInstance.favicon
|
||||||
$: screenOrPageDefinition = isPage ? pageDefinition : screenDefinition
|
$: assetDefinition = isLayout ? layoutDefinition : screenDefinition
|
||||||
|
|
||||||
const isDuplicateName = name => {
|
const isDuplicateName = name => {
|
||||||
let duplicate = false
|
let duplicate = false
|
||||||
|
@ -58,15 +57,15 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// check page first
|
// check against layouts
|
||||||
lookForDuplicate($store.pages[$store.currentPageName].props)
|
for (let layout of $store.layouts) {
|
||||||
if (duplicate) return true
|
lookForDuplicate(layout.props)
|
||||||
|
}
|
||||||
// if viewing screen, check current screen for duplicate
|
// if viewing screen, check current screen for duplicate
|
||||||
if ($store.currentFrontEndType === "screen") {
|
if ($store.currentFrontEndType === FrontendTypes.SCREEN) {
|
||||||
lookForDuplicate($store.currentPreviewItem.props)
|
lookForDuplicate($currentAsset.props)
|
||||||
} else {
|
} else {
|
||||||
// viewing master page - need to dedupe against all screens
|
// need to dedupe against all screens
|
||||||
for (let screen of $allScreens) {
|
for (let screen of $allScreens) {
|
||||||
lookForDuplicate(screen.props)
|
lookForDuplicate(screen.props)
|
||||||
}
|
}
|
||||||
|
@ -86,14 +85,14 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="settings-view-container">
|
<div class="settings-view-container">
|
||||||
{#if screenOrPageInstance}
|
{#if assetInstance}
|
||||||
{#each screenOrPageDefinition as def}
|
{#each assetDefinition as def}
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
bindable={false}
|
bindable={false}
|
||||||
control={def.control}
|
control={def.control}
|
||||||
label={def.label}
|
label={def.label}
|
||||||
key={def.key}
|
key={def.key}
|
||||||
value={screenOrPageInstance[def.key]}
|
value={assetInstance[def.key]}
|
||||||
onChange={onScreenPropChange}
|
onChange={onScreenPropChange}
|
||||||
props={{ ...excludeProps(def, ['control', 'label']) }} />
|
props={{ ...excludeProps(def, ['control', 'label']) }} />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
|
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore, currentAsset } from "builderStore"
|
||||||
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
@ -32,9 +32,9 @@
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.currentComponentInfo._id,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
components: $store.components,
|
components: $store.components,
|
||||||
screen: $store.currentPreviewItem,
|
screen: $currentAsset,
|
||||||
tables: $backendUiStore.tables,
|
tables: $backendUiStore.tables,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
||||||
_id: uuid(),
|
_id: uuid(),
|
||||||
_component: componentDefinition._component,
|
_component: componentDefinition._component,
|
||||||
_styles: { normal: {}, hover: {}, active: {}, selected: {} },
|
_styles: { normal: {}, hover: {}, active: {}, selected: {} },
|
||||||
_code: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = []
|
const errors = []
|
||||||
|
@ -96,6 +95,3 @@ const parsePropDef = propDef => {
|
||||||
|
|
||||||
return cloneDeep(propDef.default)
|
return cloneDeep(propDef.default)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const arrayElementComponentName = (parentComponentName, arrayPropName) =>
|
|
||||||
`${parentComponentName}:${arrayPropName}`
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { isPlainObject, isArray, cloneDeep } from "lodash/fp"
|
|
||||||
import { getExactComponent } from "./searchComponents"
|
|
||||||
|
|
||||||
export const rename = (pages, screens, oldname, newname) => {
|
|
||||||
pages = cloneDeep(pages)
|
|
||||||
screens = cloneDeep(screens)
|
|
||||||
const changedScreens = []
|
|
||||||
|
|
||||||
const existingWithNewName = getExactComponent(screens, newname, true)
|
|
||||||
if (existingWithNewName)
|
|
||||||
return {
|
|
||||||
components: screens,
|
|
||||||
pages,
|
|
||||||
error: "Component by that name already exists",
|
|
||||||
}
|
|
||||||
|
|
||||||
const traverseProps = props => {
|
|
||||||
let hasEdited = false
|
|
||||||
if (props._component && props._component === oldname) {
|
|
||||||
props._component = newname
|
|
||||||
hasEdited = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let propName in props) {
|
|
||||||
const prop = props[propName]
|
|
||||||
if (isPlainObject(prop) && prop._component) {
|
|
||||||
hasEdited = traverseProps(prop) || hasEdited
|
|
||||||
}
|
|
||||||
if (isArray(prop)) {
|
|
||||||
for (let element of prop) {
|
|
||||||
hasEdited = traverseProps(element) || hasEdited
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasEdited
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let screen of screens) {
|
|
||||||
let hasEdited = false
|
|
||||||
|
|
||||||
if (screen.props.instanceName === oldname) {
|
|
||||||
screen.props.instanceName = newname
|
|
||||||
hasEdited = true
|
|
||||||
}
|
|
||||||
|
|
||||||
hasEdited = traverseProps(screen.props) || hasEdited
|
|
||||||
|
|
||||||
if (hasEdited && screen.props.instanceName !== newname)
|
|
||||||
changedScreens.push(screen.props.instanceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let pageName in pages) {
|
|
||||||
const page = pages[pageName]
|
|
||||||
if (page.appBody === oldname) {
|
|
||||||
page.appBody = newname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { screens, pages, changedScreens }
|
|
||||||
}
|
|
|
@ -1197,7 +1197,7 @@ export default {
|
||||||
_component: "##builtin/screenslot",
|
_component: "##builtin/screenslot",
|
||||||
name: "Screen Slot",
|
name: "Screen Slot",
|
||||||
description:
|
description:
|
||||||
"This component is a placeholder for the rendering of a screen within a page.",
|
"This component is a placeholder for the rendering of a screen within a layout.",
|
||||||
icon: "ri-crop-2-line",
|
icon: "ri-crop-2-line",
|
||||||
properties: { design: { ...all } },
|
properties: { design: { ...all } },
|
||||||
commonProps: {},
|
commonProps: {},
|
||||||
|
@ -1222,7 +1222,7 @@ export default {
|
||||||
"A component that automatically generates a login screen for your app.",
|
"A component that automatically generates a login screen for your app.",
|
||||||
icon: "ri-login-box-line",
|
icon: "ri-login-box-line",
|
||||||
children: [],
|
children: [],
|
||||||
showOnPages: ["unauthenticated"],
|
showOnAsset: ["login-screen"],
|
||||||
properties: {
|
properties: {
|
||||||
design: { ...all },
|
design: { ...all },
|
||||||
settings: [
|
settings: [
|
||||||
|
|
|
@ -2,22 +2,19 @@ export const TableNames = {
|
||||||
USERS: "ta_users",
|
USERS: "ta_users",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FrontendTypes = {
|
||||||
|
PAGE: "page",
|
||||||
|
SCREEN: "screen",
|
||||||
|
LAYOUT: "layout",
|
||||||
|
NONE: "none",
|
||||||
|
}
|
||||||
|
|
||||||
// fields on the user table that cannot be edited
|
// fields on the user table that cannot be edited
|
||||||
export const UNEDITABLE_USER_FIELDS = ["email", "password", "roleId"]
|
export const UNEDITABLE_USER_FIELDS = ["email", "password", "roleId"]
|
||||||
|
|
||||||
export const DEFAULT_PAGES_OBJECT = {
|
export const LAYOUT_NAMES = {
|
||||||
main: {
|
MASTER: {
|
||||||
props: {
|
PRIVATE: "layout_private_master",
|
||||||
_component: "@budibase/standard-components/container",
|
PUBLIC: "layout_private_master",
|
||||||
},
|
|
||||||
_screens: {},
|
|
||||||
},
|
},
|
||||||
unauthenticated: {
|
|
||||||
props: {
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
},
|
|
||||||
_screens: {},
|
|
||||||
},
|
|
||||||
componentLibraries: [],
|
|
||||||
stylesheets: [],
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { store } from "builderStore"
|
||||||
import { params } from "@sveltech/routify"
|
import { params } from "@sveltech/routify"
|
||||||
store.actions.pages.select($params.page)
|
|
||||||
|
store.actions.layouts.select($params.layout)
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
|
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- routify:options index=1 -->
|
<!-- routify:options index=0 -->
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<TableNavigator />
|
<TableNavigator />
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script>
|
||||||
|
import { params, leftover, goto } from "@sveltech/routify"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
|
import { store, allScreens } from "builderStore"
|
||||||
|
|
||||||
|
// Get any leftover params not caught by Routifys params store.
|
||||||
|
const componentIds = $leftover.split("/").filter(id => id !== "")
|
||||||
|
|
||||||
|
const currentAssetId = decodeURI($params.asset)
|
||||||
|
|
||||||
|
let assetList
|
||||||
|
let actions
|
||||||
|
|
||||||
|
// Determine screens or layouts based on the URL
|
||||||
|
if ($params.assetType === FrontendTypes.SCREEN) {
|
||||||
|
assetList = $allScreens
|
||||||
|
actions = store.actions.screens
|
||||||
|
} else {
|
||||||
|
assetList = $store.layouts
|
||||||
|
actions = store.actions.layouts
|
||||||
|
}
|
||||||
|
|
||||||
|
// select the screen or layout in the UI
|
||||||
|
actions.select(currentAssetId)
|
||||||
|
|
||||||
|
// 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 assetChildren = assetList.find(
|
||||||
|
asset =>
|
||||||
|
asset._id === $params.asset ||
|
||||||
|
asset._id === decodeURIComponent($params.asset)
|
||||||
|
).props._children
|
||||||
|
findComponent(componentIds, assetChildren)
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Find Component with ID and continue
|
||||||
|
function findComponent(ids, children) {
|
||||||
|
// Setup stuff
|
||||||
|
let componentToSelect
|
||||||
|
let currentChildren = children
|
||||||
|
|
||||||
|
// Loop through each ID
|
||||||
|
ids.forEach(id => {
|
||||||
|
// Find ID
|
||||||
|
const component = currentChildren.find(child => child._id === id)
|
||||||
|
|
||||||
|
// If it does not exist, ignore (use last valid route)
|
||||||
|
if (!component) return
|
||||||
|
|
||||||
|
componentToSelect = component
|
||||||
|
|
||||||
|
// Update childrens array to selected components children
|
||||||
|
currentChildren = componentToSelect._children
|
||||||
|
})
|
||||||
|
|
||||||
|
// Select Component!
|
||||||
|
if (componentToSelect) store.actions.components.select(componentToSelect)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
import CurrentItemPreview from "components/userInterface/AppPreview"
|
import CurrentItemPreview from "components/userInterface/AppPreview"
|
||||||
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
||||||
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
||||||
|
@ -26,8 +27,6 @@
|
||||||
const settings = () => {
|
const settings = () => {
|
||||||
settingsView.show()
|
settingsView.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastPartOfName = c => (c ? last(c.split("/")) : "")
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- routify:options index=1 -->
|
<!-- routify:options index=1 -->
|
||||||
|
@ -37,7 +36,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-pane">
|
<div class="preview-pane">
|
||||||
{#if $store.currentPageName && $store.currentPageName.length > 0}
|
{#if $store.currentAssetId && $store.currentAssetId.length > 0}
|
||||||
<ComponentSelectionList />
|
<ComponentSelectionList />
|
||||||
<div class="preview-content">
|
<div class="preview-content">
|
||||||
<CurrentItemPreview />
|
<CurrentItemPreview />
|
||||||
|
@ -45,7 +44,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'}
|
{#if $store.currentFrontEndType === FrontendTypes.SCREEN || $store.currentFrontEndType === FrontendTypes.LAYOUT}
|
||||||
<div class="components-pane">
|
<div class="components-pane">
|
||||||
<ComponentPropertiesPanel />
|
<ComponentPropertiesPanel />
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
import { store, allScreens } from "builderStore"
|
||||||
|
import { FrontendTypes } from "constants"
|
||||||
|
import { goto, params } from "@sveltech/routify"
|
||||||
|
|
||||||
|
// Go to first layout
|
||||||
|
if ($params.assetType === FrontendTypes.LAYOUT) {
|
||||||
|
$goto(`../${$store.layouts[0]?._id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to first screen
|
||||||
|
if ($params.assetType === FrontendTypes.SCREEN) {
|
||||||
|
$goto(`../${$allScreens[0]?._id}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- routify:options index=false -->
|
|
@ -1,69 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { params, leftover, goto } from "@sveltech/routify"
|
|
||||||
import { store, allScreens } from "builderStore"
|
|
||||||
|
|
||||||
// Get any leftover params not caught by Routifys params store.
|
|
||||||
const componentIds = $leftover.split("/").filter(id => id !== "")
|
|
||||||
|
|
||||||
// It's a screen, set it to that screen
|
|
||||||
if ($params.screen !== "page-layout") {
|
|
||||||
const currentScreenName = decodeURI($params.screen)
|
|
||||||
const validScreen =
|
|
||||||
$allScreens.findIndex(screen => screen._id === currentScreenName) !== -1
|
|
||||||
|
|
||||||
if (!validScreen) {
|
|
||||||
// Go to main layout if URL set to invalid screen
|
|
||||||
store.actions.pages.select("main")
|
|
||||||
$goto("../../main")
|
|
||||||
} else {
|
|
||||||
// Otherwise proceed to set screen
|
|
||||||
store.actions.screens.select(currentScreenName)
|
|
||||||
|
|
||||||
// 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(
|
|
||||||
screen =>
|
|
||||||
screen._id === $params.screen ||
|
|
||||||
screen._id === decodeURIComponent($params.screen)
|
|
||||||
).props._children
|
|
||||||
findComponent(componentIds, screenChildren)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// It's a page, so set the screentype to page.
|
|
||||||
store.actions.selectPageOrScreen("page")
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find Component with ID and continue
|
|
||||||
function findComponent(ids, children) {
|
|
||||||
// Setup stuff
|
|
||||||
let componentToSelect
|
|
||||||
let currentChildren = children
|
|
||||||
|
|
||||||
// Loop through each ID
|
|
||||||
ids.forEach(id => {
|
|
||||||
// Find ID
|
|
||||||
const component = currentChildren.find(child => child._id === id)
|
|
||||||
|
|
||||||
// If it does not exist, ignore (use last valid route)
|
|
||||||
if (!component) return
|
|
||||||
|
|
||||||
componentToSelect = component
|
|
||||||
|
|
||||||
// Update childrens array to selected components children
|
|
||||||
currentChildren = componentToSelect._children
|
|
||||||
})
|
|
||||||
|
|
||||||
// Select Component!
|
|
||||||
if (componentToSelect) store.actions.components.select(componentToSelect)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<slot />
|
|
|
@ -1,8 +0,0 @@
|
||||||
<script>
|
|
||||||
import { params } from "@sveltech/routify"
|
|
||||||
import { store } from "builderStore"
|
|
||||||
|
|
||||||
store.actions.pages.select($params.page)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<slot />
|
|
|
@ -1,4 +0,0 @@
|
||||||
<script>
|
|
||||||
import { goto } from "@sveltech/routify"
|
|
||||||
$goto("../page-layout")
|
|
||||||
</script>
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
$goto("../main")
|
import { FrontendTypes } from "constants"
|
||||||
|
|
||||||
|
$goto(`../${FrontendTypes.SCREEN}`)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- routify:options index=false -->
|
<!-- routify:options index=1 -->
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createProps } from "../src/components/userInterface/pagesParsing/createProps"
|
import { createProps } from "../src/components/userInterface/assetParsing/createProps"
|
||||||
import { keys, some } from "lodash/fp"
|
import { keys, some } from "lodash/fp"
|
||||||
import { stripStandardProps } from "./testData"
|
import { stripStandardProps } from "./testData"
|
||||||
|
|
||||||
|
@ -158,8 +158,6 @@ describe("createDefaultProps", () => {
|
||||||
const comp = getcomponent()
|
const comp = getcomponent()
|
||||||
comp.props.fieldName = { type: "string", default: 1 }
|
comp.props.fieldName = { type: "string", default: 1 }
|
||||||
const { props } = createProps(comp)
|
const { props } = createProps(comp)
|
||||||
expect(props._code).toBeDefined()
|
|
||||||
expect(props._styles).toBeDefined()
|
expect(props._styles).toBeDefined()
|
||||||
expect(props._code).toBeDefined()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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("")
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -2,7 +2,7 @@ import {
|
||||||
searchAllComponents,
|
searchAllComponents,
|
||||||
getExactComponent,
|
getExactComponent,
|
||||||
getAncestorProps,
|
getAncestorProps,
|
||||||
} from "../src/components/userInterface/pagesParsing/searchComponents"
|
} from "../src/components/userInterface/assetParsing/searchComponents"
|
||||||
import { componentsAndScreens } from "./testData"
|
import { componentsAndScreens } from "./testData"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,6 @@ export const componentsAndScreens = () => ({
|
||||||
})
|
})
|
||||||
|
|
||||||
export const stripStandardProps = props => {
|
export const stripStandardProps = props => {
|
||||||
delete props._code
|
|
||||||
delete props._id
|
delete props._id
|
||||||
delete props._styles
|
delete props._styles
|
||||||
}
|
}
|
||||||
|
|
|
@ -842,10 +842,10 @@
|
||||||
lodash "^4.17.19"
|
lodash "^4.17.19"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@budibase/bbui@^1.51.0":
|
"@budibase/bbui@^1.52.2":
|
||||||
version "1.52.0"
|
version "1.52.2"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.52.0.tgz#81fc8d3f80cb74f01a4f2d62e58389fa8fbfedeb"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.52.2.tgz#a0774880fb755eb81c762bc355550af7f4562b09"
|
||||||
integrity sha512-ytDZj/lQKUiwmF4wzz45yAQFTajEFW3Z7HWD7AmfTfxRtbwGXefbX+mF3JkWrYq5OclUtaA2+kluOX7tP1oZmw==
|
integrity sha512-PxiN5xvr+Z/RpypMDYh3lNhCUnejH1moMoWW7PiuCiho5VXGauR+M8T49p5eTKoFSqRMC7BUdFJJ9ye/cnQxNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
markdown-it "^12.0.2"
|
markdown-it "^12.0.2"
|
||||||
quill "^1.3.7"
|
quill "^1.3.7"
|
||||||
|
@ -854,6 +854,15 @@
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
turndown "^7.0.0"
|
turndown "^7.0.0"
|
||||||
|
|
||||||
|
"@budibase/client@^0.3.8":
|
||||||
|
version "0.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.3.8.tgz#75df7e97e8f0d9b58c00e2bb0d3b4a55f8d04735"
|
||||||
|
integrity sha512-tnFdmCdXKS+uZGoipr69Wa0oVoFHmyoV0ydihI6q0gKQH0KutypVHAaul2qPB8t5a/mTZopC//2WdmCeX1GKVg==
|
||||||
|
dependencies:
|
||||||
|
deep-equal "^2.0.1"
|
||||||
|
mustache "^4.0.1"
|
||||||
|
regexparam "^1.3.0"
|
||||||
|
|
||||||
"@budibase/colorpicker@^1.0.1":
|
"@budibase/colorpicker@^1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
resolved "https://registry.yarnpkg.com/@budibase/colorpicker/-/colorpicker-1.0.1.tgz#940c180e7ebba0cb0756c4c8ef13f5dfab58e810"
|
||||||
|
@ -1646,6 +1655,11 @@ array-equal@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
||||||
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
|
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
|
||||||
|
|
||||||
|
array-filter@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
|
||||||
|
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
|
||||||
|
|
||||||
array-union@^2.1.0:
|
array-union@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||||
|
@ -1713,6 +1727,13 @@ atob@^2.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
|
available-typed-arrays@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
|
||||||
|
integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
|
||||||
|
dependencies:
|
||||||
|
array-filter "^1.0.0"
|
||||||
|
|
||||||
aws-sign2@~0.7.0:
|
aws-sign2@~0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||||
|
@ -2047,6 +2068,14 @@ callsites@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
|
camel-case@3.0.x:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||||
|
integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
|
||||||
|
dependencies:
|
||||||
|
no-case "^2.2.0"
|
||||||
|
upper-case "^1.1.1"
|
||||||
|
|
||||||
camelcase@^5.0.0, camelcase@^5.3.1:
|
camelcase@^5.0.0, camelcase@^5.3.1:
|
||||||
version "5.3.1"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||||
|
@ -2157,6 +2186,13 @@ class-utils@^0.3.5:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
|
clean-css@4.2.x:
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
||||||
|
integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
|
||||||
|
dependencies:
|
||||||
|
source-map "~0.6.0"
|
||||||
|
|
||||||
cli-cursor@^1.0.2:
|
cli-cursor@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
||||||
|
@ -2272,11 +2308,21 @@ commander@2, commander@^2.20.0:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
commander@2.17.x:
|
||||||
|
version "2.17.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||||
|
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
|
||||||
|
|
||||||
commander@^5.0.0, commander@^5.1.0:
|
commander@^5.0.0, commander@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||||
|
|
||||||
|
commander@~2.19.0:
|
||||||
|
version "2.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
|
||||||
|
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
|
||||||
|
|
||||||
common-tags@^1.8.0:
|
common-tags@^1.8.0:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
|
||||||
|
@ -2851,6 +2897,27 @@ deep-equal@^1.0.1:
|
||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
regexp.prototype.flags "^1.2.0"
|
regexp.prototype.flags "^1.2.0"
|
||||||
|
|
||||||
|
deep-equal@^2.0.1:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9"
|
||||||
|
integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
es-get-iterator "^1.1.1"
|
||||||
|
get-intrinsic "^1.0.1"
|
||||||
|
is-arguments "^1.0.4"
|
||||||
|
is-date-object "^1.0.2"
|
||||||
|
is-regex "^1.1.1"
|
||||||
|
isarray "^2.0.5"
|
||||||
|
object-is "^1.1.4"
|
||||||
|
object-keys "^1.1.1"
|
||||||
|
object.assign "^4.1.2"
|
||||||
|
regexp.prototype.flags "^1.3.0"
|
||||||
|
side-channel "^1.0.3"
|
||||||
|
which-boxed-primitive "^1.0.1"
|
||||||
|
which-collection "^1.0.1"
|
||||||
|
which-typed-array "^1.1.2"
|
||||||
|
|
||||||
deep-is@~0.1.3:
|
deep-is@~0.1.3:
|
||||||
version "0.1.3"
|
version "0.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||||
|
@ -3059,7 +3126,7 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2:
|
||||||
string.prototype.trimend "^1.0.1"
|
string.prototype.trimend "^1.0.1"
|
||||||
string.prototype.trimstart "^1.0.1"
|
string.prototype.trimstart "^1.0.1"
|
||||||
|
|
||||||
es-abstract@^1.18.0-next.1:
|
es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1:
|
||||||
version "1.18.0-next.1"
|
version "1.18.0-next.1"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
|
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
|
||||||
integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
|
integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
|
||||||
|
@ -3077,6 +3144,20 @@ es-abstract@^1.18.0-next.1:
|
||||||
string.prototype.trimend "^1.0.1"
|
string.prototype.trimend "^1.0.1"
|
||||||
string.prototype.trimstart "^1.0.1"
|
string.prototype.trimstart "^1.0.1"
|
||||||
|
|
||||||
|
es-get-iterator@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9"
|
||||||
|
integrity sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
get-intrinsic "^1.0.1"
|
||||||
|
has-symbols "^1.0.1"
|
||||||
|
is-arguments "^1.0.4"
|
||||||
|
is-map "^2.0.1"
|
||||||
|
is-set "^2.0.1"
|
||||||
|
is-string "^1.0.5"
|
||||||
|
isarray "^2.0.5"
|
||||||
|
|
||||||
es-to-primitive@^1.2.1:
|
es-to-primitive@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||||
|
@ -3130,6 +3211,11 @@ estraverse@^4.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
|
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
|
||||||
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
|
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
|
||||||
|
|
||||||
|
estree-walker@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e"
|
||||||
|
integrity sha1-va/oCVOD2EFNXcLs9MkXO225QS4=
|
||||||
|
|
||||||
estree-walker@^0.5.2:
|
estree-walker@^0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39"
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39"
|
||||||
|
@ -3455,7 +3541,7 @@ for-in@^1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||||
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
|
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
|
||||||
|
|
||||||
foreach@~2.0.1:
|
foreach@^2.0.5, foreach@~2.0.1:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||||
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
|
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
|
||||||
|
@ -3545,7 +3631,7 @@ get-caller-file@^2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.0.0:
|
get-intrinsic@^1.0.0, get-intrinsic@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
|
||||||
integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
|
integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
|
||||||
|
@ -3742,6 +3828,11 @@ hash.js@^1.0.0, hash.js@^1.0.3:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
minimalistic-assert "^1.0.1"
|
minimalistic-assert "^1.0.1"
|
||||||
|
|
||||||
|
he@1.2.x:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
|
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||||
|
|
||||||
hmac-drbg@^1.0.0:
|
hmac-drbg@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||||
|
@ -3775,6 +3866,19 @@ html-escaper@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||||
|
|
||||||
|
html-minifier@^3.0.2:
|
||||||
|
version "3.5.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
|
||||||
|
integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
|
||||||
|
dependencies:
|
||||||
|
camel-case "3.0.x"
|
||||||
|
clean-css "4.2.x"
|
||||||
|
commander "2.17.x"
|
||||||
|
he "1.2.x"
|
||||||
|
param-case "2.1.x"
|
||||||
|
relateurl "0.2.x"
|
||||||
|
uglify-js "3.4.x"
|
||||||
|
|
||||||
http-signature@~1.2.0:
|
http-signature@~1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||||
|
@ -3903,6 +4007,11 @@ is-arrayish@^0.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||||
|
|
||||||
|
is-bigint@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2"
|
||||||
|
integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==
|
||||||
|
|
||||||
is-binary-path@~2.1.0:
|
is-binary-path@~2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||||
|
@ -3910,6 +4019,13 @@ is-binary-path@~2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^2.0.0"
|
binary-extensions "^2.0.0"
|
||||||
|
|
||||||
|
is-boolean-object@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
|
||||||
|
integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
|
||||||
is-buffer@^1.1.5:
|
is-buffer@^1.1.5:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
@ -3948,7 +4064,7 @@ is-data-descriptor@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
kind-of "^6.0.0"
|
kind-of "^6.0.0"
|
||||||
|
|
||||||
is-date-object@^1.0.1:
|
is-date-object@^1.0.1, is-date-object@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
||||||
|
@ -4025,6 +4141,11 @@ is-installed-globally@^0.3.2:
|
||||||
global-dirs "^2.0.1"
|
global-dirs "^2.0.1"
|
||||||
is-path-inside "^3.0.1"
|
is-path-inside "^3.0.1"
|
||||||
|
|
||||||
|
is-map@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
|
||||||
|
integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
|
||||||
|
|
||||||
is-module@^1.0.0:
|
is-module@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||||
|
@ -4035,6 +4156,11 @@ is-negative-zero@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
|
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
|
||||||
integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
|
integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
|
||||||
|
|
||||||
|
is-number-object@^1.0.3:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
|
||||||
|
integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
|
||||||
|
|
||||||
is-number@^3.0.0:
|
is-number@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
|
||||||
|
@ -4100,6 +4226,11 @@ is-regex@^1.0.4, is-regex@^1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
|
is-set@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
|
||||||
|
integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==
|
||||||
|
|
||||||
is-stream@^1.1.0:
|
is-stream@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||||
|
@ -4110,6 +4241,11 @@ is-stream@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
|
||||||
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
|
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
|
||||||
|
|
||||||
|
is-string@^1.0.4, is-string@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
||||||
|
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
||||||
|
|
||||||
is-symbol@^1.0.2:
|
is-symbol@^1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
||||||
|
@ -4117,11 +4253,32 @@ is-symbol@^1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
|
is-typed-array@^1.1.3:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.4.tgz#1f66f34a283a3c94a4335434661ca53fff801120"
|
||||||
|
integrity sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==
|
||||||
|
dependencies:
|
||||||
|
available-typed-arrays "^1.0.2"
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
es-abstract "^1.18.0-next.1"
|
||||||
|
foreach "^2.0.5"
|
||||||
|
has-symbols "^1.0.1"
|
||||||
|
|
||||||
is-typedarray@~1.0.0:
|
is-typedarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||||
|
|
||||||
|
is-weakmap@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
||||||
|
integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
|
||||||
|
|
||||||
|
is-weakset@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
|
||||||
|
integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==
|
||||||
|
|
||||||
is-windows@^1.0.2:
|
is-windows@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||||
|
@ -4147,6 +4304,11 @@ isarray@1.0.0, isarray@~1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||||
|
|
||||||
|
isarray@^2.0.5:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||||
|
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
||||||
|
|
||||||
isbuffer@~0.0.0:
|
isbuffer@~0.0.0:
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b"
|
resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b"
|
||||||
|
@ -5051,6 +5213,11 @@ loose-envify@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
|
lower-case@^1.1.1:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||||
|
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||||
|
|
||||||
ltgt@^2.1.2:
|
ltgt@^2.1.2:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
|
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
|
||||||
|
@ -5224,7 +5391,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||||
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
|
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
|
||||||
|
|
||||||
minimatch@^3.0.4:
|
minimatch@^3.0.2, minimatch@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||||
|
@ -5318,6 +5485,13 @@ nice-try@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||||
|
|
||||||
|
no-case@^2.2.0:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||||
|
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||||
|
dependencies:
|
||||||
|
lower-case "^1.1.1"
|
||||||
|
|
||||||
node-fetch@^2.6.0:
|
node-fetch@^2.6.0:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
|
@ -5432,6 +5606,14 @@ object-is@^1.0.1:
|
||||||
define-properties "^1.1.3"
|
define-properties "^1.1.3"
|
||||||
es-abstract "^1.18.0-next.1"
|
es-abstract "^1.18.0-next.1"
|
||||||
|
|
||||||
|
object-is@^1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068"
|
||||||
|
integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
object-keys@^1.0.12, object-keys@^1.1.1:
|
object-keys@^1.0.12, object-keys@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||||
|
@ -5458,7 +5640,7 @@ object-visit@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
|
|
||||||
object.assign@^4.1.0, object.assign@^4.1.1:
|
object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
|
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
|
||||||
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
|
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
|
||||||
|
@ -5582,6 +5764,13 @@ p-try@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||||
|
|
||||||
|
param-case@2.1.x:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
|
||||||
|
integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
|
||||||
|
dependencies:
|
||||||
|
no-case "^2.2.0"
|
||||||
|
|
||||||
parchment@^1.1.4:
|
parchment@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5"
|
resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5"
|
||||||
|
@ -6031,7 +6220,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||||
extend-shallow "^3.0.2"
|
extend-shallow "^3.0.2"
|
||||||
safe-regex "^1.1.0"
|
safe-regex "^1.1.0"
|
||||||
|
|
||||||
regexp.prototype.flags@^1.2.0:
|
regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
|
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
|
||||||
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
|
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
|
||||||
|
@ -6039,6 +6228,11 @@ regexp.prototype.flags@^1.2.0:
|
||||||
define-properties "^1.1.3"
|
define-properties "^1.1.3"
|
||||||
es-abstract "^1.17.0-next.1"
|
es-abstract "^1.17.0-next.1"
|
||||||
|
|
||||||
|
regexparam@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f"
|
||||||
|
integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g==
|
||||||
|
|
||||||
regexpu-core@^4.7.1:
|
regexpu-core@^4.7.1:
|
||||||
version "4.7.1"
|
version "4.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
|
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
|
||||||
|
@ -6063,6 +6257,11 @@ regjsparser@^0.6.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
jsesc "~0.5.0"
|
jsesc "~0.5.0"
|
||||||
|
|
||||||
|
relateurl@0.2.x:
|
||||||
|
version "0.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||||
|
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
|
||||||
|
|
||||||
remixicon@^2.5.0:
|
remixicon@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/remixicon/-/remixicon-2.5.0.tgz#b5e245894a1550aa23793f95daceadbf96ad1a41"
|
resolved "https://registry.yarnpkg.com/remixicon/-/remixicon-2.5.0.tgz#b5e245894a1550aa23793f95daceadbf96ad1a41"
|
||||||
|
@ -6256,6 +6455,14 @@ rollup-plugin-css-only@^2.1.0:
|
||||||
"@rollup/pluginutils" "^3.0.0"
|
"@rollup/pluginutils" "^3.0.0"
|
||||||
fs-extra "^9.0.0"
|
fs-extra "^9.0.0"
|
||||||
|
|
||||||
|
rollup-plugin-html@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup-plugin-html/-/rollup-plugin-html-0.2.1.tgz#a1862eca87ae54b677689d0d4133911e8226463d"
|
||||||
|
integrity sha1-oYYuyoeuVLZ3aJ0NQTORHoImRj0=
|
||||||
|
dependencies:
|
||||||
|
html-minifier "^3.0.2"
|
||||||
|
rollup-pluginutils "^1.5.0"
|
||||||
|
|
||||||
rollup-plugin-livereload@^1.0.0:
|
rollup-plugin-livereload@^1.0.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-1.3.0.tgz#8da90df13df6502b9d982997d6ac871092f15fdd"
|
resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-1.3.0.tgz#8da90df13df6502b9d982997d6ac871092f15fdd"
|
||||||
|
@ -6324,6 +6531,14 @@ rollup-plugin-url@^2.2.2:
|
||||||
mkdirp "^0.5.1"
|
mkdirp "^0.5.1"
|
||||||
rollup-pluginutils "^2.8.2"
|
rollup-pluginutils "^2.8.2"
|
||||||
|
|
||||||
|
rollup-pluginutils@^1.5.0:
|
||||||
|
version "1.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408"
|
||||||
|
integrity sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=
|
||||||
|
dependencies:
|
||||||
|
estree-walker "^0.2.1"
|
||||||
|
minimatch "^3.0.2"
|
||||||
|
|
||||||
rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2:
|
rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2:
|
||||||
version "2.8.2"
|
version "2.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
|
||||||
|
@ -6502,6 +6717,14 @@ shortid@^2.2.15:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^2.1.0"
|
nanoid "^2.1.0"
|
||||||
|
|
||||||
|
side-channel@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
|
||||||
|
integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==
|
||||||
|
dependencies:
|
||||||
|
es-abstract "^1.18.0-next.0"
|
||||||
|
object-inspect "^1.8.0"
|
||||||
|
|
||||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||||
|
@ -6615,7 +6838,7 @@ source-map@^0.5.0, source-map@^0.5.6:
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||||
|
|
||||||
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
|
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
@ -7108,6 +7331,14 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||||
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||||
|
|
||||||
|
uglify-js@3.4.x:
|
||||||
|
version "3.4.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
|
||||||
|
integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==
|
||||||
|
dependencies:
|
||||||
|
commander "~2.19.0"
|
||||||
|
source-map "~0.6.1"
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
|
||||||
|
@ -7169,6 +7400,11 @@ untildify@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
||||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
||||||
|
|
||||||
|
upper-case@^1.1.1:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||||
|
integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
|
||||||
|
|
||||||
uri-js@^4.2.2:
|
uri-js@^4.2.2:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
|
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
|
||||||
|
@ -7334,11 +7570,45 @@ whatwg-url@^8.0.0:
|
||||||
tr46 "^2.0.2"
|
tr46 "^2.0.2"
|
||||||
webidl-conversions "^6.1.0"
|
webidl-conversions "^6.1.0"
|
||||||
|
|
||||||
|
which-boxed-primitive@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
|
||||||
|
integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==
|
||||||
|
dependencies:
|
||||||
|
is-bigint "^1.0.0"
|
||||||
|
is-boolean-object "^1.0.0"
|
||||||
|
is-number-object "^1.0.3"
|
||||||
|
is-string "^1.0.4"
|
||||||
|
is-symbol "^1.0.2"
|
||||||
|
|
||||||
|
which-collection@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
|
||||||
|
integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
|
||||||
|
dependencies:
|
||||||
|
is-map "^2.0.1"
|
||||||
|
is-set "^2.0.1"
|
||||||
|
is-weakmap "^2.0.1"
|
||||||
|
is-weakset "^2.0.1"
|
||||||
|
|
||||||
which-module@^2.0.0:
|
which-module@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||||
|
|
||||||
|
which-typed-array@^1.1.2:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff"
|
||||||
|
integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==
|
||||||
|
dependencies:
|
||||||
|
available-typed-arrays "^1.0.2"
|
||||||
|
call-bind "^1.0.0"
|
||||||
|
es-abstract "^1.18.0-next.1"
|
||||||
|
foreach "^2.0.5"
|
||||||
|
function-bind "^1.1.1"
|
||||||
|
has-symbols "^1.0.1"
|
||||||
|
is-typed-array "^1.1.3"
|
||||||
|
|
||||||
which@^1.2.9, which@^1.3.0:
|
which@^1.2.9, which@^1.3.0:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"version": "0.3.8",
|
"version": "0.3.8",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
"module": "dist/budibase-client.esm.mjs",
|
"module": "dist/budibase-client.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if loaded}
|
{#if loaded && $screenStore.activeLayout}
|
||||||
<Component definition={$screenStore.page.props} />
|
<!-- // TODO: need to get the active screen as well -->
|
||||||
|
<Component definition={$screenStore.activeLayout.props} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
// Get the screen definition for the current route
|
// Get the screen definition for the current route
|
||||||
$: screenDefinition = $screenStore.activeScreen?.props
|
$: screenDefinition = $screenStore.activeScreen?.props
|
||||||
|
|
||||||
// Redirect to home page if no matching route
|
// Redirect to home layout if no matching route
|
||||||
$: screenDefinition == null && routeStore.actions.navigate("/")
|
$: screenDefinition == null && routeStore.actions.navigate("/")
|
||||||
|
|
||||||
// Make a screen array so we can use keying to properly re-render each screen
|
// Make a screen array so we can use keying to properly re-render each screen
|
||||||
|
|
|
@ -7,7 +7,7 @@ const loadBudibase = () => {
|
||||||
// Update builder store with any builder flags
|
// Update builder store with any builder flags
|
||||||
builderStore.set({
|
builderStore.set({
|
||||||
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
|
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
|
||||||
page: window["##BUDIBASE_PREVIEW_PAGE##"],
|
layout: window["##BUDIBASE_PREVIEW_LAYOUT##"],
|
||||||
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
|
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
|
||||||
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
|
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
|
||||||
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { writable } from "svelte/store"
|
||||||
const createBuilderStore = () => {
|
const createBuilderStore = () => {
|
||||||
const initialState = {
|
const initialState = {
|
||||||
inBuilder: false,
|
inBuilder: false,
|
||||||
page: null,
|
layout: null,
|
||||||
screen: null,
|
screen: null,
|
||||||
selectedComponentId: null,
|
selectedComponentId: null,
|
||||||
previewId: null,
|
previewId: null,
|
||||||
|
|
|
@ -34,7 +34,7 @@ const createRouteStore = () => {
|
||||||
}
|
}
|
||||||
const setActiveRoute = route => {
|
const setActiveRoute = route => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.activeRoute = route
|
state.activeRoute = state.routes.find(x => x.path === route)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,30 +7,35 @@ import { getAppId } from "../utils/getAppId"
|
||||||
const createScreenStore = () => {
|
const createScreenStore = () => {
|
||||||
const config = writable({
|
const config = writable({
|
||||||
screens: [],
|
screens: [],
|
||||||
page: {},
|
layouts: [],
|
||||||
})
|
})
|
||||||
const store = derived(
|
const store = derived(
|
||||||
[config, routeStore, builderStore],
|
[config, routeStore, builderStore],
|
||||||
([$config, $routeStore, $builderStore]) => {
|
([$config, $routeStore, $builderStore]) => {
|
||||||
let page
|
let activeLayout
|
||||||
let activeScreen
|
let activeScreen
|
||||||
if ($builderStore.inBuilder) {
|
if ($builderStore.inBuilder) {
|
||||||
// Use builder defined definitions if inside the builder preview
|
// Use builder defined definitions if inside the builder preview
|
||||||
page = $builderStore.page
|
activeLayout = $builderStore.layout
|
||||||
activeScreen = $builderStore.screen
|
activeScreen = $builderStore.screen
|
||||||
} else {
|
} else {
|
||||||
// Otherwise find the correct screen by matching the current route
|
// Otherwise find the correct screen by matching the current route
|
||||||
page = $config.page
|
const { screens, layouts } = $config
|
||||||
const { screens } = $config
|
activeLayout = layouts[0]
|
||||||
if (screens.length === 1) {
|
if (screens.length === 1) {
|
||||||
activeScreen = screens[0]
|
activeScreen = screens[0]
|
||||||
} else {
|
} else if ($routeStore.activeRoute) {
|
||||||
activeScreen = screens.find(
|
activeScreen = screens.find(
|
||||||
screen => screen.routing.route === $routeStore.activeRoute
|
screen => screen._id === $routeStore.activeRoute.screenId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (activeScreen) {
|
||||||
|
activeLayout = layouts.find(
|
||||||
|
layout => layout._id === activeScreen.layoutId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { page, activeScreen }
|
return { activeLayout, activeScreen }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +43,7 @@ const createScreenStore = () => {
|
||||||
const appDefinition = await API.fetchAppDefinition(getAppId())
|
const appDefinition = await API.fetchAppDefinition(getAppId())
|
||||||
config.set({
|
config.set({
|
||||||
screens: appDefinition.screens,
|
screens: appDefinition.screens,
|
||||||
page: appDefinition.page,
|
layouts: appDefinition.layouts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const compileStaticAssetsForPage = require("../../utilities/builder/compileStaticAssetsForPage")
|
const compileStaticAssets = require("../../utilities/builder/compileStaticAssets")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const { existsSync } = require("fs-extra")
|
const { existsSync } = require("fs-extra")
|
||||||
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
||||||
|
@ -14,34 +14,54 @@ const {
|
||||||
generateAppID,
|
generateAppID,
|
||||||
DocumentTypes,
|
DocumentTypes,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
getPageParams,
|
getLayoutParams,
|
||||||
getScreenParams,
|
getScreenParams,
|
||||||
generatePageID,
|
|
||||||
generateScreenID,
|
generateScreenID,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
|
const {
|
||||||
|
BUILTIN_ROLE_IDS,
|
||||||
|
AccessController,
|
||||||
|
} = require("../../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
downloadExtractComponentLibraries,
|
downloadExtractComponentLibraries,
|
||||||
} = require("../../utilities/createAppPackage")
|
} = require("../../utilities/createAppPackage")
|
||||||
const { MAIN, UNAUTHENTICATED, PageTypes } = require("../../constants/pages")
|
const { BASE_LAYOUTS } = require("../../constants/layouts")
|
||||||
const { HOME_SCREEN } = require("../../constants/screens")
|
const {
|
||||||
|
createHomeScreen,
|
||||||
|
createLoginScreen,
|
||||||
|
} = require("../../constants/screens")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
const { recurseMustache } = require("../../utilities/mustache")
|
||||||
|
const { generateAssetCss } = require("../../utilities/builder/generateCss")
|
||||||
const { USERS_TABLE_SCHEMA } = require("../../constants")
|
const { USERS_TABLE_SCHEMA } = require("../../constants")
|
||||||
|
|
||||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
|
|
||||||
// utility function, need to do away with this
|
// utility function, need to do away with this
|
||||||
async function getMainAndUnauthPage(db) {
|
async function getLayouts(db) {
|
||||||
let pages = await db.allDocs(
|
return (
|
||||||
getPageParams(null, {
|
await db.allDocs(
|
||||||
include_docs: true,
|
getLayoutParams(null, {
|
||||||
})
|
include_docs: true,
|
||||||
)
|
})
|
||||||
pages = pages.rows.map(row => row.doc)
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
const mainPage = pages.find(page => page.name === PageTypes.MAIN)
|
async function getScreens(db) {
|
||||||
const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED)
|
return (
|
||||||
return { mainPage, unauthPage }
|
await db.allDocs(
|
||||||
|
getScreenParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserRoleId(ctx) {
|
||||||
|
return !ctx.user.role || !ctx.user.role._id
|
||||||
|
? BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
: ctx.user.role._id
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInstance(template) {
|
async function createInstance(template) {
|
||||||
|
@ -92,25 +112,16 @@ exports.fetch = async function(ctx) {
|
||||||
|
|
||||||
exports.fetchAppDefinition = async function(ctx) {
|
exports.fetchAppDefinition = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = new CouchDB(ctx.params.appId)
|
||||||
// TODO: need to get rid of pages here, they shouldn't be needed anymore
|
const layouts = await getLayouts(db)
|
||||||
const { mainPage, unauthPage } = await getMainAndUnauthPage(db)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const userRoleId =
|
const accessController = new AccessController(ctx.params.appId)
|
||||||
!ctx.user.role || !ctx.user.role._id
|
const screens = await accessController.checkScreensAccess(
|
||||||
? BUILTIN_ROLE_IDS.PUBLIC
|
await getScreens(db),
|
||||||
: ctx.user.role._id
|
userRoleId
|
||||||
const correctPage =
|
)
|
||||||
userRoleId === BUILTIN_ROLE_IDS.PUBLIC ? unauthPage : mainPage
|
|
||||||
const screens = (
|
|
||||||
await db.allDocs(
|
|
||||||
getScreenParams(correctPage._id, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).rows.map(row => row.doc)
|
|
||||||
// TODO: need to handle access control here, limit screens to user role
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
page: correctPage,
|
layouts,
|
||||||
screens: screens,
|
screens,
|
||||||
libraries: ["@budibase/standard-components"],
|
libraries: ["@budibase/standard-components"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,16 +129,19 @@ 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, screens] = await Promise.all([getLayouts(db), getScreens(db)])
|
||||||
|
|
||||||
const { mainPage, unauthPage } = await getMainAndUnauthPage(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,
|
||||||
pages: {
|
screens,
|
||||||
main: mainPage,
|
layouts,
|
||||||
unauthenticated: unauthPage,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await setBuilderToken(ctx, ctx.params.appId, application.version)
|
await setBuilderToken(ctx, ctx.params.appId, application.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,27 +214,26 @@ const createEmptyAppPackage = async (ctx, app) => {
|
||||||
|
|
||||||
fs.mkdirpSync(newAppFolder)
|
fs.mkdirpSync(newAppFolder)
|
||||||
|
|
||||||
const mainPage = cloneDeep(MAIN)
|
let screensAndLayouts = []
|
||||||
mainPage._id = generatePageID()
|
for (let layout of BASE_LAYOUTS) {
|
||||||
mainPage.title = app.name
|
const cloned = cloneDeep(layout)
|
||||||
|
cloned.title = app.name
|
||||||
|
screensAndLayouts.push(recurseMustache(cloned, app))
|
||||||
|
}
|
||||||
|
|
||||||
const unauthPage = cloneDeep(UNAUTHENTICATED)
|
const homeScreen = createHomeScreen(app)
|
||||||
unauthPage._id = generatePageID()
|
homeScreen._id = generateScreenID()
|
||||||
unauthPage.title = app.name
|
screensAndLayouts.push(homeScreen)
|
||||||
unauthPage.props._children[0].title = `Log in to ${app.name}`
|
|
||||||
|
|
||||||
const homeScreen = cloneDeep(HOME_SCREEN)
|
const loginScreen = createLoginScreen(app)
|
||||||
homeScreen._id = generateScreenID(mainPage._id)
|
loginScreen._id = generateScreenID()
|
||||||
await db.bulkDocs([mainPage, unauthPage, homeScreen])
|
screensAndLayouts.push(loginScreen)
|
||||||
|
|
||||||
await compileStaticAssetsForPage(app._id, "main", {
|
|
||||||
page: mainPage,
|
|
||||||
screens: [homeScreen],
|
|
||||||
})
|
|
||||||
await compileStaticAssetsForPage(app._id, "unauthenticated", {
|
|
||||||
page: unauthPage,
|
|
||||||
screens: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
|
await db.bulkDocs(screensAndLayouts)
|
||||||
|
// at the end add CSS to all the structures
|
||||||
|
for (let asset of screensAndLayouts) {
|
||||||
|
asset._css = generateAssetCss([asset.props])
|
||||||
|
}
|
||||||
|
await compileStaticAssets(app._id, screensAndLayouts)
|
||||||
return newAppFolder
|
return newAppFolder
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ const { budibaseAppsDir } = require("../../../utilities/budibaseDir")
|
||||||
const PouchDB = require("../../../db")
|
const PouchDB = require("../../../db")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
|
||||||
|
const EXCLUDED_DIRECTORIES = ["css"]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalises the deployment, updating the quota for the user API key
|
* Finalises the deployment, updating the quota for the user API key
|
||||||
* The verification process returns the levels to update to.
|
* The verification process returns the levels to update to.
|
||||||
|
@ -136,25 +138,28 @@ exports.uploadAppAssets = async function({ appId, bucket, accountId }) {
|
||||||
|
|
||||||
const appAssetsPath = join(budibaseAppsDir(), appId, "public")
|
const appAssetsPath = join(budibaseAppsDir(), appId, "public")
|
||||||
|
|
||||||
const appPages = fs.readdirSync(appAssetsPath)
|
|
||||||
|
|
||||||
let uploads = []
|
let uploads = []
|
||||||
|
|
||||||
for (let page of appPages) {
|
// Upload HTML, CSS and JS of the web app
|
||||||
// Upload HTML, CSS and JS for each page of the web app
|
walkDir(appAssetsPath, function(filePath) {
|
||||||
walkDir(join(appAssetsPath, page), function(filePath) {
|
const filePathParts = filePath.split("/")
|
||||||
const appAssetUpload = prepareUploadForS3({
|
const publicIndex = filePathParts.indexOf("public")
|
||||||
file: {
|
const directory = filePathParts[publicIndex + 1]
|
||||||
path: filePath,
|
// don't include these top level directories
|
||||||
name: [...filePath.split("/")].pop(),
|
if (EXCLUDED_DIRECTORIES.indexOf(directory) !== -1) {
|
||||||
},
|
return
|
||||||
s3Key: filePath.replace(appAssetsPath, `assets/${appId}`),
|
}
|
||||||
s3,
|
const appAssetUpload = prepareUploadForS3({
|
||||||
metadata: { accountId },
|
file: {
|
||||||
})
|
path: filePath,
|
||||||
uploads.push(appAssetUpload)
|
name: filePathParts.pop(),
|
||||||
|
},
|
||||||
|
s3Key: filePath.replace(appAssetsPath, `assets/${appId}`),
|
||||||
|
s3,
|
||||||
|
metadata: { accountId },
|
||||||
})
|
})
|
||||||
}
|
uploads.push(appAssetUpload)
|
||||||
|
})
|
||||||
|
|
||||||
// Upload file attachments
|
// Upload file attachments
|
||||||
const db = new PouchDB(appId)
|
const db = new PouchDB(appId)
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
const { EMPTY_LAYOUT } = require("../../constants/layouts")
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const { generateLayoutID, getScreenParams } = require("../../db/utils")
|
||||||
|
|
||||||
|
exports.save = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
let layout = ctx.request.body
|
||||||
|
|
||||||
|
if (!layout.props) {
|
||||||
|
layout = {
|
||||||
|
...layout,
|
||||||
|
...EMPTY_LAYOUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout._id = layout._id || generateLayoutID()
|
||||||
|
const response = await db.put(layout)
|
||||||
|
layout._rev = response.rev
|
||||||
|
|
||||||
|
ctx.body = layout
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.destroy = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
const layoutId = ctx.params.layoutId,
|
||||||
|
layoutRev = ctx.params.layoutRev
|
||||||
|
|
||||||
|
const layoutsUsedByScreens = (
|
||||||
|
await db.allDocs(
|
||||||
|
getScreenParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(element => element.doc.layoutId)
|
||||||
|
if (layoutsUsedByScreens.includes(layoutId)) {
|
||||||
|
ctx.throw(400, "Cannot delete a base layout")
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.remove(layoutId, layoutRev)
|
||||||
|
ctx.message = "Layout deleted successfully"
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
const CouchDB = require("../../db/client")
|
|
||||||
const { generatePageID } = require("../../db/utils")
|
|
||||||
const compileStaticAssetsForPage = require("../../utilities/builder/compileStaticAssetsForPage")
|
|
||||||
|
|
||||||
exports.save = async function(ctx) {
|
|
||||||
const db = new CouchDB(ctx.user.appId)
|
|
||||||
|
|
||||||
const appPackage = ctx.request.body
|
|
||||||
|
|
||||||
const page = await db.get(ctx.params.pageId)
|
|
||||||
await compileStaticAssetsForPage(ctx.user.appId, page.name, ctx.request.body)
|
|
||||||
|
|
||||||
// remove special doc props which couch will complain about
|
|
||||||
delete appPackage.page._css
|
|
||||||
delete appPackage.page._screens
|
|
||||||
appPackage.page._id = appPackage.page._id || generatePageID()
|
|
||||||
ctx.body = await db.put(appPackage.page)
|
|
||||||
ctx.status = 200
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
||||||
const { AccessController } = require("../../utilities/security/roles")
|
const { AccessController } = require("../../utilities/security/roles")
|
||||||
|
const { generateAssetCss } = require("../../utilities/builder/generateCss")
|
||||||
|
const compileStaticAssets = require("../../utilities/builder/compileStaticAssets")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const appId = ctx.user.appId
|
const appId = ctx.user.appId
|
||||||
|
@ -20,40 +22,31 @@ exports.fetch = async ctx => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async ctx => {
|
|
||||||
const appId = ctx.user.appId
|
|
||||||
const db = new CouchDB(appId)
|
|
||||||
|
|
||||||
const screens = await db.allDocs(
|
|
||||||
getScreenParams(ctx.params.pageId, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx.body = await new AccessController(appId).checkScreensAccess(
|
|
||||||
screens,
|
|
||||||
ctx.user.role._id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.save = async ctx => {
|
exports.save = async ctx => {
|
||||||
const appId = ctx.user.appId
|
const appId = ctx.user.appId
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
const screen = ctx.request.body
|
let screen = ctx.request.body
|
||||||
|
|
||||||
if (!screen._id) {
|
if (!screen._id) {
|
||||||
screen._id = generateScreenID(ctx.params.pageId)
|
screen._id = generateScreenID()
|
||||||
}
|
}
|
||||||
delete screen._css
|
|
||||||
const response = await db.put(screen)
|
const response = await db.put(screen)
|
||||||
|
|
||||||
|
// update CSS so client doesn't need to make a call directly after
|
||||||
|
screen._css = generateAssetCss([screen.props])
|
||||||
|
await compileStaticAssets(appId, screen)
|
||||||
|
|
||||||
ctx.message = `Screen ${screen.name} saved.`
|
ctx.message = `Screen ${screen.name} saved.`
|
||||||
ctx.body = response
|
ctx.body = {
|
||||||
|
...screen,
|
||||||
|
_id: response.id,
|
||||||
|
_rev: response.rev,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async ctx => {
|
exports.destroy = async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
await db.remove(ctx.params.screenId, ctx.params.revId)
|
await db.remove(ctx.params.screenId, ctx.params.screenRev)
|
||||||
ctx.message = "Screen deleted successfully"
|
ctx.message = "Screen deleted successfully"
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,20 @@ const {
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const setBuilderToken = require("../../../utilities/builder/setBuilderToken")
|
const setBuilderToken = require("../../../utilities/builder/setBuilderToken")
|
||||||
const fileProcessor = require("../../../utilities/fileProcessor")
|
const fileProcessor = require("../../../utilities/fileProcessor")
|
||||||
const { AuthTypes } = require("../../../constants")
|
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
const { generateAssetCss } = require("../../../utilities/builder/generateCss")
|
||||||
|
const compileStaticAssets = require("../../../utilities/builder/compileStaticAssets")
|
||||||
|
|
||||||
// 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
|
||||||
|
structure._css = generateAssetCss([structure.props])
|
||||||
|
await compileStaticAssets(ctx.appId, structure)
|
||||||
|
ctx.body = { css: structure._css }
|
||||||
|
}
|
||||||
|
|
||||||
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") {
|
||||||
|
@ -142,15 +150,11 @@ exports.performLocalFileProcessing = async function(ctx) {
|
||||||
|
|
||||||
exports.serveApp = async function(ctx) {
|
exports.serveApp = async function(ctx) {
|
||||||
const App = require("./templates/BudibaseApp.svelte").default
|
const App = require("./templates/BudibaseApp.svelte").default
|
||||||
|
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = new CouchDB(ctx.params.appId)
|
||||||
|
|
||||||
const appInfo = await db.get(ctx.params.appId)
|
const appInfo = await db.get(ctx.params.appId)
|
||||||
|
|
||||||
const { head, html, css } = App.render({
|
const { head, html, css } = App.render({
|
||||||
title: appInfo.name,
|
title: appInfo.name,
|
||||||
pageName:
|
|
||||||
ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated",
|
|
||||||
production: env.CLOUD,
|
production: env.CLOUD,
|
||||||
appId: ctx.params.appId,
|
appId: ctx.params.appId,
|
||||||
})
|
})
|
||||||
|
@ -185,15 +189,7 @@ exports.serveAttachment = async function(ctx) {
|
||||||
|
|
||||||
exports.serveAppAsset = async function(ctx) {
|
exports.serveAppAsset = async function(ctx) {
|
||||||
// default to homedir
|
// default to homedir
|
||||||
const mainOrAuth =
|
const appPath = resolve(budibaseAppsDir(), ctx.user.appId, "public")
|
||||||
ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated"
|
|
||||||
|
|
||||||
const appPath = resolve(
|
|
||||||
budibaseAppsDir(),
|
|
||||||
ctx.user.appId,
|
|
||||||
"public",
|
|
||||||
mainOrAuth
|
|
||||||
)
|
|
||||||
|
|
||||||
await send(ctx, ctx.file, { root: ctx.devPath || appPath })
|
await send(ctx, ctx.file, { root: ctx.devPath || appPath })
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
export let favicon = ""
|
export let favicon = ""
|
||||||
|
|
||||||
export let appId
|
export let appId
|
||||||
export let pageName = ""
|
|
||||||
export let production
|
export let production
|
||||||
|
|
||||||
export const PRODUCTION_ASSETS_URL = `https://${appId}.app.budi.live`
|
export const PRODUCTION_ASSETS_URL = `https://${appId}.app.budi.live`
|
||||||
|
|
||||||
function publicPath(path) {
|
function publicPath(path) {
|
||||||
if (production) {
|
if (production) {
|
||||||
return `${PRODUCTION_ASSETS_URL}/assets/${appId}/${pageName}/${path}`
|
return `${PRODUCTION_ASSETS_URL}/assets/${appId}/${path}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `/assets/${path}`
|
return `/assets/${path}`
|
||||||
|
@ -49,8 +48,6 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<body id="app">
|
<body id="app">
|
||||||
<script src={publicPath('clientFrontendDefinition.js')}>
|
|
||||||
</script>
|
|
||||||
<script src={publicPath('budibase-client.js')}>
|
<script src={publicPath('budibase-client.js')}>
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const authRoutes = require("./auth")
|
const authRoutes = require("./auth")
|
||||||
const pageRoutes = require("./pages")
|
const layoutRoutes = require("./layout")
|
||||||
const screenRoutes = require("./screen")
|
const screenRoutes = require("./screen")
|
||||||
const userRoutes = require("./user")
|
const userRoutes = require("./user")
|
||||||
const applicationRoutes = require("./application")
|
const applicationRoutes = require("./application")
|
||||||
|
@ -20,7 +20,7 @@ const permissionRoutes = require("./permission")
|
||||||
|
|
||||||
exports.mainRoutes = [
|
exports.mainRoutes = [
|
||||||
deployRoutes,
|
deployRoutes,
|
||||||
pageRoutes,
|
layoutRoutes,
|
||||||
screenRoutes,
|
screenRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
applicationRoutes,
|
applicationRoutes,
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
const controller = require("../controllers/layout")
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.post("/api/layouts", authorized(BUILDER), controller.save)
|
||||||
|
.delete(
|
||||||
|
"/api/layouts/:layoutId/:layoutRev",
|
||||||
|
authorized(BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -1,10 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("../../utilities/security/permissions")
|
|
||||||
const controller = require("../controllers/page")
|
|
||||||
|
|
||||||
const router = Router()
|
|
||||||
|
|
||||||
router.post("/api/pages/:pageId", authorized(BUILDER), controller.save)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -24,21 +24,21 @@ function generateSaveValidation() {
|
||||||
_styles: Joi.object().required(),
|
_styles: Joi.object().required(),
|
||||||
type: Joi.string().optional(),
|
type: Joi.string().optional(),
|
||||||
table: Joi.string().optional(),
|
table: Joi.string().optional(),
|
||||||
|
layoutId: Joi.string().optional(),
|
||||||
}).required().unknown(true),
|
}).required().unknown(true),
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/screens", authorized(BUILDER), controller.fetch)
|
.get("/api/screens", authorized(BUILDER), controller.fetch)
|
||||||
.get("/api/screens/:pageId", authorized(BUILDER), controller.find)
|
|
||||||
.post(
|
.post(
|
||||||
"/api/screens/:pageId",
|
"/api/screens",
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
generateSaveValidation(),
|
generateSaveValidation(),
|
||||||
controller.save
|
controller.save
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/screens/:screenId/:revId",
|
"/api/screens/:screenId/:screenRev",
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
controller.destroy
|
controller.destroy
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,22 @@ const env = require("../../environment")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/security/permissions")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
const usage = require("../../middleware/usageQuota")
|
const usage = require("../../middleware/usageQuota")
|
||||||
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
|
const Joi = require("joi")
|
||||||
|
|
||||||
|
function generateCssValidator() {
|
||||||
|
return joiValidator.body(
|
||||||
|
Joi.object({
|
||||||
|
_id: Joi.string().required(),
|
||||||
|
_rev: Joi.string().required(),
|
||||||
|
props: Joi.object()
|
||||||
|
.required()
|
||||||
|
.unknown(true),
|
||||||
|
})
|
||||||
|
.required()
|
||||||
|
.unknown(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
@ -24,6 +40,12 @@ if (env.NODE_ENV !== "production") {
|
||||||
}
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.post(
|
||||||
|
"/api/css/generate",
|
||||||
|
authorized(BUILDER),
|
||||||
|
generateCssValidator(),
|
||||||
|
controller.generateCss
|
||||||
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/attachments/process",
|
"/api/attachments/process",
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
|
|
|
@ -17,7 +17,6 @@ const AUTOMATION_ID = generateAutomationID()
|
||||||
const TEST_AUTOMATION = {
|
const TEST_AUTOMATION = {
|
||||||
_id: AUTOMATION_ID,
|
_id: AUTOMATION_ID,
|
||||||
name: "My Automation",
|
name: "My Automation",
|
||||||
pageId: "123123123",
|
|
||||||
screenId: "kasdkfldsafkl",
|
screenId: "kasdkfldsafkl",
|
||||||
live: true,
|
live: true,
|
||||||
uiTree: {
|
uiTree: {
|
||||||
|
|
|
@ -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("")
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,41 +1,5 @@
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
|
|
||||||
/**
|
|
||||||
* When running mustache statements to execute on the context of the automation it possible user's may input mustache
|
|
||||||
* in a few different forms, some of which are invalid but are logically valid. An example of this would be the mustache
|
|
||||||
* statement "{{steps[0].revision}}" here it is obvious the user is attempting to access an array or object using array
|
|
||||||
* like operators. These are not supported by Mustache and therefore the statement will fail. This function will clean up
|
|
||||||
* the mustache statement so it instead reads as "{{steps.0.revision}}" which is valid and will work. It may also be expanded
|
|
||||||
* to include any other mustache statement cleanup that has been deemed necessary for the system.
|
|
||||||
*
|
|
||||||
* @param {string} string The string which *may* contain mustache statements, it is OK if it does not contain any.
|
|
||||||
* @returns {string} The string that was input with cleaned up mustache statements as required.
|
|
||||||
*/
|
|
||||||
module.exports.cleanMustache = string => {
|
|
||||||
let charToReplace = {
|
|
||||||
"[": ".",
|
|
||||||
"]": "",
|
|
||||||
}
|
|
||||||
let regex = new RegExp(/{{[^}}]*}}/g)
|
|
||||||
let matches = string.match(regex)
|
|
||||||
if (matches == null) {
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
for (let match of matches) {
|
|
||||||
let baseIdx = string.indexOf(match)
|
|
||||||
for (let key of Object.keys(charToReplace)) {
|
|
||||||
let idxChar = match.indexOf(key)
|
|
||||||
if (idxChar !== -1) {
|
|
||||||
string =
|
|
||||||
string.slice(baseIdx, baseIdx + idxChar) +
|
|
||||||
charToReplace[key] +
|
|
||||||
string.slice(baseIdx + idxChar + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When values are input to the system generally they will be of type string as this is required for mustache. This can
|
* When values are input to the system generally they will be of type string as this is required for mustache. This can
|
||||||
* generate some odd scenarios as the Schema of the automation requires a number but the builder might supply a string
|
* generate some odd scenarios as the Schema of the automation requires a number but the builder might supply a string
|
||||||
|
|
|
@ -3,6 +3,7 @@ const actions = require("./actions")
|
||||||
const logic = require("./logic")
|
const logic = require("./logic")
|
||||||
const automationUtils = require("./automationUtils")
|
const automationUtils = require("./automationUtils")
|
||||||
const AutomationEmitter = require("../events/AutomationEmitter")
|
const AutomationEmitter = require("../events/AutomationEmitter")
|
||||||
|
const { recurseMustache } = require("../utilities/mustache")
|
||||||
|
|
||||||
handlebars.registerHelper("object", value => {
|
handlebars.registerHelper("object", value => {
|
||||||
return new handlebars.SafeString(JSON.stringify(value))
|
return new handlebars.SafeString(JSON.stringify(value))
|
||||||
|
@ -10,22 +11,6 @@ handlebars.registerHelper("object", value => {
|
||||||
|
|
||||||
const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId
|
const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId
|
||||||
|
|
||||||
function recurseMustache(inputs, context) {
|
|
||||||
for (let key of Object.keys(inputs)) {
|
|
||||||
let val = inputs[key]
|
|
||||||
if (typeof val === "string") {
|
|
||||||
val = automationUtils.cleanMustache(inputs[key])
|
|
||||||
const template = handlebars.compile(val)
|
|
||||||
inputs[key] = template(context)
|
|
||||||
}
|
|
||||||
// this covers objects and arrays
|
|
||||||
else if (typeof val === "object") {
|
|
||||||
inputs[key] = recurseMustache(inputs[key], context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The automation orchestrator is a class responsible for executing automations.
|
* The automation orchestrator is a class responsible for executing automations.
|
||||||
* It handles the context of the automation and makes sure each step gets the correct
|
* It handles the context of the automation and makes sure each step gets the correct
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
const BASE_LAYOUT_PROP_IDS = {
|
||||||
|
PRIVATE: "layout_private_master",
|
||||||
|
PUBLIC: "layout_public_master",
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_LAYOUT = {
|
||||||
|
componentLibraries: ["@budibase/standard-components"],
|
||||||
|
title: "{{ name }}",
|
||||||
|
favicon: "./_shared/favicon.png",
|
||||||
|
stylesheets: [],
|
||||||
|
props: {
|
||||||
|
_id: "30b8822a-d07b-49f4-9531-551e37c6899b",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967",
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
_styles: {
|
||||||
|
normal: {},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: "div",
|
||||||
|
_styles: {
|
||||||
|
active: {},
|
||||||
|
hover: {},
|
||||||
|
normal: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_LAYOUTS = [
|
||||||
|
{
|
||||||
|
_id: BASE_LAYOUT_PROP_IDS.PRIVATE,
|
||||||
|
componentLibraries: ["@budibase/standard-components"],
|
||||||
|
title: "{{ name }}",
|
||||||
|
favicon: "./_shared/favicon.png",
|
||||||
|
stylesheets: [],
|
||||||
|
name: "Top Navigation Layout",
|
||||||
|
props: {
|
||||||
|
_id: "4f569166-a4f3-47ea-a09e-6d218c75586f",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "c74f07266980c4b6eafc33e2a6caa783d",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "row",
|
||||||
|
"justify-content": "flex-start",
|
||||||
|
"align-items": "flex-start",
|
||||||
|
background: "#fff",
|
||||||
|
width: "100%",
|
||||||
|
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
type: "div",
|
||||||
|
_instanceName: "Header",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "49e0e519-9e5e-4127-885a-ee6a0a49e2c1",
|
||||||
|
_component: "@budibase/standard-components/navigation",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
"max-width": "1400px",
|
||||||
|
"margin-left": "auto",
|
||||||
|
"margin-right": "auto",
|
||||||
|
padding: "20px",
|
||||||
|
color: "#757575",
|
||||||
|
"font-weight": "400",
|
||||||
|
"font-size": "16px",
|
||||||
|
flex: "1 1 auto",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
logoUrl:
|
||||||
|
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg",
|
||||||
|
title: "",
|
||||||
|
backgroundColor: "",
|
||||||
|
color: "",
|
||||||
|
borderWidth: "",
|
||||||
|
borderColor: "",
|
||||||
|
borderStyle: "",
|
||||||
|
_instanceName: "Navigation",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "48b35328-4c91-4343-a6a3-1a1fd77b3386",
|
||||||
|
_component: "@budibase/standard-components/link",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
"font-family": "Inter",
|
||||||
|
"font-weight": "500",
|
||||||
|
color: "#000000",
|
||||||
|
"text-decoration-line": "none",
|
||||||
|
"font-size": "16px",
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
color: "#4285f4",
|
||||||
|
},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
url: "/",
|
||||||
|
openInNewTab: false,
|
||||||
|
text: "Home",
|
||||||
|
color: "",
|
||||||
|
hoverColor: "",
|
||||||
|
underline: false,
|
||||||
|
fontSize: "",
|
||||||
|
fontFamily: "initial",
|
||||||
|
_instanceName: "Home Link",
|
||||||
|
_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967",
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
flex: "1 1 auto",
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "column",
|
||||||
|
"justify-content": "flex-start",
|
||||||
|
"align-items": "stretch",
|
||||||
|
"max-width": "100%",
|
||||||
|
"margin-left": "20px",
|
||||||
|
"margin-right": "20px",
|
||||||
|
width: "1400px",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: "div",
|
||||||
|
_styles: {
|
||||||
|
active: {},
|
||||||
|
hover: {},
|
||||||
|
normal: {
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "column",
|
||||||
|
"align-items": "center",
|
||||||
|
"justify-content": "flex-start",
|
||||||
|
"margin-right": "auto",
|
||||||
|
"margin-left": "auto",
|
||||||
|
"min-height": "100%",
|
||||||
|
"background-image":
|
||||||
|
"linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);",
|
||||||
|
},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: BASE_LAYOUT_PROP_IDS.PUBLIC,
|
||||||
|
componentLibraries: ["@budibase/standard-components"],
|
||||||
|
title: "{{ name }}",
|
||||||
|
favicon: "./_shared/favicon.png",
|
||||||
|
stylesheets: [],
|
||||||
|
name: "Empty Layout",
|
||||||
|
props: {
|
||||||
|
_id: "3723ffa1-f9e0-4c05-8013-98195c788ed6",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967",
|
||||||
|
_component: "##builtin/screenslot",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
flex: "1 1 auto",
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "column",
|
||||||
|
"justify-content": "flex-start",
|
||||||
|
"align-items": "stretch",
|
||||||
|
"max-width": "100%",
|
||||||
|
"margin-left": "20px",
|
||||||
|
"margin-right": "20px",
|
||||||
|
width: "1400px",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: "div",
|
||||||
|
_styles: {
|
||||||
|
active: {},
|
||||||
|
hover: {},
|
||||||
|
normal: {
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "column",
|
||||||
|
"align-items": "center",
|
||||||
|
"justify-content": "center",
|
||||||
|
"margin-right": "auto",
|
||||||
|
"margin-left": "auto",
|
||||||
|
"min-height": "100%",
|
||||||
|
"background-image":
|
||||||
|
"linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);",
|
||||||
|
},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
BASE_LAYOUTS,
|
||||||
|
BASE_LAYOUT_PROP_IDS,
|
||||||
|
EMPTY_LAYOUT,
|
||||||
|
}
|
|
@ -1,198 +0,0 @@
|
||||||
const PageTypes = {
|
|
||||||
MAIN: "main",
|
|
||||||
UNAUTHENTICATED: "unauthenticated",
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAIN = {
|
|
||||||
componentLibraries: ["@budibase/standard-components"],
|
|
||||||
title: "{{ name }}",
|
|
||||||
favicon: "./_shared/favicon.png",
|
|
||||||
stylesheets: [],
|
|
||||||
name: PageTypes.MAIN,
|
|
||||||
props: {
|
|
||||||
_id: "private-master-root",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "c74f07266980c4b6eafc33e2a6caa783d",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "row",
|
|
||||||
"justify-content": "flex-start",
|
|
||||||
"align-items": "flex-start",
|
|
||||||
background: "#fff",
|
|
||||||
width: "100%",
|
|
||||||
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
type: "div",
|
|
||||||
_appId: "inst_app_80b_f158d4057d2c4bedb0042d42fda8abaf",
|
|
||||||
_instanceName: "Header",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "49e0e519-9e5e-4127-885a-ee6a0a49e2c1",
|
|
||||||
_component: "@budibase/standard-components/navigation",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"max-width": "1400px",
|
|
||||||
"margin-left": "auto",
|
|
||||||
"margin-right": "auto",
|
|
||||||
padding: "20px",
|
|
||||||
color: "#757575",
|
|
||||||
"font-weight": "400",
|
|
||||||
"font-size": "16px",
|
|
||||||
flex: "1 1 auto",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
logoUrl:
|
|
||||||
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg",
|
|
||||||
_appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394",
|
|
||||||
_instanceName: "Navigation",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "48b35328-4c91-4343-a6a3-1a1fd77b3386",
|
|
||||||
_component: "@budibase/standard-components/link",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"font-family": "Inter",
|
|
||||||
"font-weight": "500",
|
|
||||||
color: "#000000",
|
|
||||||
"text-decoration-line": "none",
|
|
||||||
"font-size": "16px",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
color: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
url: "/",
|
|
||||||
openInNewTab: false,
|
|
||||||
text: "Home",
|
|
||||||
_appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394",
|
|
||||||
_instanceName: "Home Link",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967",
|
|
||||||
_component: "##builtin/screenslot",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
flex: "1 1 auto",
|
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "column",
|
|
||||||
"justify-content": "flex-start",
|
|
||||||
"align-items": "stretch",
|
|
||||||
"max-width": "100%",
|
|
||||||
"margin-left": "20px",
|
|
||||||
"margin-right": "20px",
|
|
||||||
width: "1400px",
|
|
||||||
padding: "20px",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
type: "div",
|
|
||||||
_styles: {
|
|
||||||
active: {},
|
|
||||||
hover: {},
|
|
||||||
normal: {
|
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "column",
|
|
||||||
"align-items": "center",
|
|
||||||
"justify-content": "flex-start",
|
|
||||||
"margin-right": "auto",
|
|
||||||
"margin-left": "auto",
|
|
||||||
"min-height": "100%",
|
|
||||||
"background-image":
|
|
||||||
"linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);",
|
|
||||||
},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const UNAUTHENTICATED = {
|
|
||||||
componentLibraries: ["@budibase/standard-components"],
|
|
||||||
title: "{{ name }}",
|
|
||||||
favicon: "./_shared/favicon.png",
|
|
||||||
stylesheets: [],
|
|
||||||
name: PageTypes.UNAUTHENTICATED,
|
|
||||||
props: {
|
|
||||||
_id: "public-master-root",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "686c252d-dbf2-4e28-9078-414ba4719759",
|
|
||||||
_component: "@budibase/standard-components/login",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
padding: "64px",
|
|
||||||
background: "rgba(255, 255, 255, 0.4)",
|
|
||||||
"border-radius": "0.5rem",
|
|
||||||
"margin-top": "0px",
|
|
||||||
margin: "0px",
|
|
||||||
"line-height": "1",
|
|
||||||
"box-shadow":
|
|
||||||
"0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
|
|
||||||
"font-size": "16px",
|
|
||||||
"font-family": "Inter",
|
|
||||||
flex: "0 1 auto",
|
|
||||||
transform: "0",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
_instanceName: "Login",
|
|
||||||
_children: [],
|
|
||||||
title: "Log in to {{ name }}",
|
|
||||||
buttonText: "Log In",
|
|
||||||
logo:
|
|
||||||
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
type: "div",
|
|
||||||
_styles: {
|
|
||||||
active: {},
|
|
||||||
hover: {},
|
|
||||||
normal: {
|
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "column",
|
|
||||||
"align-items": "center",
|
|
||||||
"justify-content": "center",
|
|
||||||
"margin-right": "auto",
|
|
||||||
"margin-left": "auto",
|
|
||||||
"min-height": "100%",
|
|
||||||
"background-image":
|
|
||||||
"linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);",
|
|
||||||
},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { MAIN, UNAUTHENTICATED, PageTypes }
|
|
|
@ -1,8 +1,10 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles")
|
||||||
|
const { BASE_LAYOUT_PROP_IDS } = require("./layouts")
|
||||||
|
|
||||||
exports.HOME_SCREEN = {
|
exports.createHomeScreen = () => ({
|
||||||
description: "",
|
description: "",
|
||||||
url: "",
|
url: "",
|
||||||
|
layoutId: BASE_LAYOUT_PROP_IDS.PRIVATE,
|
||||||
props: {
|
props: {
|
||||||
_id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
_id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
||||||
_component: "@budibase/standard-components/container",
|
_component: "@budibase/standard-components/container",
|
||||||
|
@ -18,7 +20,6 @@ exports.HOME_SCREEN = {
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
_code: "",
|
|
||||||
type: "div",
|
type: "div",
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
|
@ -32,10 +33,8 @@ exports.HOME_SCREEN = {
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
_code: "",
|
|
||||||
text: "Welcome to your Budibase App 👋",
|
text: "Welcome to your Budibase App 👋",
|
||||||
type: "h2",
|
type: "h2",
|
||||||
_appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394",
|
|
||||||
_instanceName: "Heading",
|
_instanceName: "Heading",
|
||||||
_children: [],
|
_children: [],
|
||||||
},
|
},
|
||||||
|
@ -57,9 +56,7 @@ exports.HOME_SCREEN = {
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
_code: "",
|
|
||||||
type: "div",
|
type: "div",
|
||||||
_appId: "inst_app_2cc_ca3383f896034e9295345c05f7dfca0c",
|
|
||||||
_instanceName: "Video Container",
|
_instanceName: "Video Container",
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
|
@ -83,10 +80,8 @@ exports.HOME_SCREEN = {
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
_code: "",
|
|
||||||
embed:
|
embed:
|
||||||
'<iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
'<iframe width="560" height="315" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||||
_appId: "inst_app_2cc_ca3383f896034e9295345c05f7dfca0c",
|
|
||||||
_instanceName: "Rick Astley Video",
|
_instanceName: "Rick Astley Video",
|
||||||
_children: [],
|
_children: [],
|
||||||
},
|
},
|
||||||
|
@ -99,5 +94,62 @@ exports.HOME_SCREEN = {
|
||||||
route: "/",
|
route: "/",
|
||||||
roleId: BUILTIN_ROLE_IDS.BASIC,
|
roleId: BUILTIN_ROLE_IDS.BASIC,
|
||||||
},
|
},
|
||||||
name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
name: "home-screen",
|
||||||
}
|
})
|
||||||
|
|
||||||
|
exports.createLoginScreen = app => ({
|
||||||
|
description: "",
|
||||||
|
url: "",
|
||||||
|
layoutId: BASE_LAYOUT_PROP_IDS.PUBLIC,
|
||||||
|
props: {
|
||||||
|
_instanceName: "LoginScreenContainer",
|
||||||
|
_id: "5beb4c7b-3c8b-49b2-b8b3-d447dc76dda7",
|
||||||
|
_component: "@budibase/standard-components/container",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
flex: "1 1 auto",
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "column",
|
||||||
|
"justify-content": "center",
|
||||||
|
"align-items": "center",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
type: "div",
|
||||||
|
_children: [
|
||||||
|
{
|
||||||
|
_id: "781e497e-2e7c-11eb-adc1-0242ac120002",
|
||||||
|
_component: "@budibase/standard-components/login",
|
||||||
|
_styles: {
|
||||||
|
normal: {
|
||||||
|
padding: "64px",
|
||||||
|
background: "rgba(255, 255, 255, 0.4)",
|
||||||
|
"border-radius": "0.5rem",
|
||||||
|
"margin-top": "0px",
|
||||||
|
"box-shadow":
|
||||||
|
"0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
|
||||||
|
"font-size": "16px",
|
||||||
|
"font-family": "Inter",
|
||||||
|
flex: "0 1 auto",
|
||||||
|
},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
logo:
|
||||||
|
"https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg",
|
||||||
|
title: `Log in to ${app.name}`,
|
||||||
|
buttonText: "Log In",
|
||||||
|
_children: [],
|
||||||
|
_instanceName: "Login",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
routing: {
|
||||||
|
route: "/",
|
||||||
|
roleId: BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
|
},
|
||||||
|
name: "login-screen",
|
||||||
|
})
|
||||||
|
|
|
@ -13,7 +13,7 @@ const DocumentTypes = {
|
||||||
ROLE: "role",
|
ROLE: "role",
|
||||||
WEBHOOK: "wh",
|
WEBHOOK: "wh",
|
||||||
INSTANCE: "inst",
|
INSTANCE: "inst",
|
||||||
PAGE: "page",
|
LAYOUT: "layout",
|
||||||
SCREEN: "screen",
|
SCREEN: "screen",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +179,36 @@ exports.getRoleParams = (roleId = null, otherProps = {}) => {
|
||||||
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
|
return getDocParams(DocumentTypes.ROLE, roleId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new layout ID.
|
||||||
|
* @returns {string} The new layout ID which the layout doc can be stored under.
|
||||||
|
*/
|
||||||
|
exports.generateLayoutID = id => {
|
||||||
|
return `${DocumentTypes.LAYOUT}${SEPARATOR}${id || newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving layout, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
exports.getLayoutParams = (layoutId = null, otherProps = {}) => {
|
||||||
|
return getDocParams(DocumentTypes.LAYOUT, layoutId, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new screen ID.
|
||||||
|
* @returns {string} The new screen ID which the screen doc can be stored under.
|
||||||
|
*/
|
||||||
|
exports.generateScreenID = () => {
|
||||||
|
return `${DocumentTypes.SCREEN}${SEPARATOR}${newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving screens, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
exports.getScreenParams = (screenId = null, otherProps = {}) => {
|
||||||
|
return getDocParams(DocumentTypes.SCREEN, screenId, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new webhook ID.
|
* Generates a new webhook ID.
|
||||||
* @returns {string} The new webhook ID which the webhook doc can be stored under.
|
* @returns {string} The new webhook ID which the webhook doc can be stored under.
|
||||||
|
@ -187,36 +217,6 @@ exports.generateWebhookID = () => {
|
||||||
return `${DocumentTypes.WEBHOOK}${SEPARATOR}${newid()}`
|
return `${DocumentTypes.WEBHOOK}${SEPARATOR}${newid()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new page ID.
|
|
||||||
* @returns {string} The new page ID which the page doc can be stored under.
|
|
||||||
*/
|
|
||||||
exports.generatePageID = () => {
|
|
||||||
return `${DocumentTypes.PAGE}${SEPARATOR}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving pages, this is a utility function for the getDocParams function.
|
|
||||||
*/
|
|
||||||
exports.getPageParams = (pageId = null, otherProps = {}) => {
|
|
||||||
return getDocParams(DocumentTypes.PAGE, pageId, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new screen ID.
|
|
||||||
* @returns {string} The new screen ID which the screen doc can be stored under.
|
|
||||||
*/
|
|
||||||
exports.generateScreenID = pageId => {
|
|
||||||
return `${DocumentTypes.SCREEN}${SEPARATOR}${pageId}${SEPARATOR}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving screens for a particular page, this is a utility function for the getDocParams function.
|
|
||||||
*/
|
|
||||||
exports.getScreenParams = (pageId = null, otherProps = {}) => {
|
|
||||||
return getDocParams(DocumentTypes.SCREEN, pageId, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets parameters for retrieving a webhook, this is a utility function for the getDocParams function.
|
* Gets parameters for retrieving a webhook, this is a utility function for the getDocParams function.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
const {
|
||||||
|
ensureDir,
|
||||||
|
constants,
|
||||||
|
copyFile,
|
||||||
|
writeFile,
|
||||||
|
readdir,
|
||||||
|
readFile,
|
||||||
|
existsSync,
|
||||||
|
} = require("fs-extra")
|
||||||
|
const { join } = require("../centralPath")
|
||||||
|
const { budibaseAppsDir } = require("../budibaseDir")
|
||||||
|
|
||||||
|
const CSS_DIRECTORY = "css"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile all the non-db static web assets that are required for the running of
|
||||||
|
* a budibase application. This includes CSS, the JSON structure of the DOM and
|
||||||
|
* the client library, a script responsible for reading the JSON structure
|
||||||
|
* and rendering the application.
|
||||||
|
* @param {string} appId id of the application we want to compile static assets for
|
||||||
|
* @param {array|object} assets a list of screens or screen layouts for which the CSS should be extracted and stored.
|
||||||
|
*/
|
||||||
|
module.exports = async (appId, assets) => {
|
||||||
|
const publicPath = join(budibaseAppsDir(), appId, "public")
|
||||||
|
await ensureDir(publicPath)
|
||||||
|
for (let asset of Array.isArray(assets) ? assets : [assets]) {
|
||||||
|
await buildCssBundle(publicPath, asset)
|
||||||
|
await copyClientLib(publicPath)
|
||||||
|
// remove props that shouldn't be present when written to DB
|
||||||
|
if (asset._css) {
|
||||||
|
delete asset._css
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the _css property of all screens and the screen layouts, and creates a singular CSS
|
||||||
|
* bundle for the app at <appId>/public/bundle.css
|
||||||
|
* @param {String} publicPath - path to the public assets directory of the budibase application
|
||||||
|
* @param {Object} asset a single screen or screen layout which is being updated
|
||||||
|
*/
|
||||||
|
const buildCssBundle = async (publicPath, asset) => {
|
||||||
|
const cssPath = join(publicPath, CSS_DIRECTORY)
|
||||||
|
let cssString = ""
|
||||||
|
|
||||||
|
// create a singular CSS file for this asset
|
||||||
|
const assetCss = asset._css ? asset._css.trim() : ""
|
||||||
|
if (assetCss.length !== 0) {
|
||||||
|
await ensureDir(cssPath)
|
||||||
|
await writeFile(join(cssPath, asset._id), assetCss)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bundle up all the CSS in the directory into one top level CSS file
|
||||||
|
if (existsSync(cssPath)) {
|
||||||
|
const cssFiles = await readdir(cssPath)
|
||||||
|
for (let filename of cssFiles) {
|
||||||
|
const css = await readFile(join(cssPath, filename))
|
||||||
|
cssString += css
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeFile(join(publicPath, "bundle.css"), cssString)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the budibase client library and sourcemap from NPM to <appId>/public/.
|
||||||
|
* The client library is then served as a static asset when the budibase application
|
||||||
|
* is running in preview or prod
|
||||||
|
* @param {String} publicPath - path to write the client library to
|
||||||
|
*/
|
||||||
|
const copyClientLib = async publicPath => {
|
||||||
|
const sourcepath = require.resolve("@budibase/client")
|
||||||
|
const destPath = join(publicPath, "budibase-client.js")
|
||||||
|
|
||||||
|
await copyFile(sourcepath, destPath, constants.COPYFILE_FICLONE)
|
||||||
|
|
||||||
|
await copyFile(
|
||||||
|
sourcepath + ".map",
|
||||||
|
destPath + ".map",
|
||||||
|
constants.COPYFILE_FICLONE
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,102 +0,0 @@
|
||||||
const { ensureDir, constants, copyFile, writeFile } = require("fs-extra")
|
|
||||||
const { join } = require("../centralPath")
|
|
||||||
const { budibaseAppsDir } = require("../budibaseDir")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile all the non-db static web assets that are required for the running of
|
|
||||||
* a budibase application. This includes CSS, the JSON structure of the DOM and
|
|
||||||
* the client library, a script responsible for reading the JSON structure
|
|
||||||
* and rendering the application.
|
|
||||||
* @param {} appId - id of the application we want to compile static assets for
|
|
||||||
* @param {*} pageName - name of the page that the assets will be served for
|
|
||||||
* @param {*} pkg - app package information/metadata
|
|
||||||
*/
|
|
||||||
module.exports = async (appId, pageName, pkg) => {
|
|
||||||
const pagePath = join(budibaseAppsDir(), appId, "public", pageName)
|
|
||||||
|
|
||||||
pkg.screens = pkg.screens || []
|
|
||||||
|
|
||||||
await ensureDir(pagePath)
|
|
||||||
|
|
||||||
await buildPageCssBundle(pagePath, pkg)
|
|
||||||
|
|
||||||
await buildFrontendAppDefinition(pagePath, pkg)
|
|
||||||
|
|
||||||
await copyClientLib(pagePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the _css property of a page and its screens, and creates a singular CSS
|
|
||||||
* bundle for the page at <appId>/public/<pageName>/bundle.css
|
|
||||||
* @param {String} publicPagePath - path to the public assets directory of the budibase application
|
|
||||||
* @param {Object} pkg - app package information
|
|
||||||
* @param {"main" | "unauthenticated"} pageName - the pagename of the page we are compiling CSS for.
|
|
||||||
*/
|
|
||||||
const buildPageCssBundle = async (publicPagePath, pkg) => {
|
|
||||||
let cssString = ""
|
|
||||||
|
|
||||||
for (let screen of pkg.screens || []) {
|
|
||||||
if (!screen._css) continue
|
|
||||||
if (screen._css.trim().length === 0) {
|
|
||||||
delete screen._css
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cssString += screen._css
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pkg.page._css) cssString += pkg.page._css
|
|
||||||
|
|
||||||
writeFile(join(publicPagePath, "bundle.css"), cssString)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy the budibase client library and sourcemap from NPM to <appId>/public/<pageName>.
|
|
||||||
* The client library is then served as a static asset when the budibase application
|
|
||||||
* is running in preview or prod
|
|
||||||
* @param {String} pagePath - path to write the client library to
|
|
||||||
*/
|
|
||||||
const copyClientLib = async pagePath => {
|
|
||||||
const sourcepath = require.resolve("@budibase/client")
|
|
||||||
const destPath = join(pagePath, "budibase-client.js")
|
|
||||||
|
|
||||||
await copyFile(sourcepath, destPath, constants.COPYFILE_FICLONE)
|
|
||||||
|
|
||||||
await copyFile(
|
|
||||||
sourcepath + ".map",
|
|
||||||
destPath + ".map",
|
|
||||||
constants.COPYFILE_FICLONE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the frontend definition for a budibase application. This includes all page and screen information,
|
|
||||||
* and is injected into the budibase client library to tell it how to start constructing
|
|
||||||
* the DOM from components defined in the frontendDefinition.
|
|
||||||
* @param {String} pagePath - path to the public folder of the page where the definition will be written
|
|
||||||
* @param {Object} pkg - app package information from which the frontendDefinition will be built.
|
|
||||||
*/
|
|
||||||
const buildFrontendAppDefinition = async (pagePath, pkg) => {
|
|
||||||
const filename = join(pagePath, "clientFrontendDefinition.js")
|
|
||||||
|
|
||||||
// Delete CSS code from the page and screens so it's not injected
|
|
||||||
delete pkg.page._css
|
|
||||||
|
|
||||||
for (let screen of pkg.screens) {
|
|
||||||
if (screen._css) {
|
|
||||||
delete pkg.page._css
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientUiDefinition = JSON.stringify({
|
|
||||||
page: pkg.page,
|
|
||||||
screens: pkg.screens,
|
|
||||||
libraries: ["@budibase/standard-components"],
|
|
||||||
})
|
|
||||||
|
|
||||||
await writeFile(
|
|
||||||
filename,
|
|
||||||
`
|
|
||||||
window['##BUDIBASE_FRONTEND_DEFINITION##'] = ${clientUiDefinition};
|
|
||||||
`
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,21 +1,21 @@
|
||||||
export const generate_screen_css = component_arr => {
|
exports.generateAssetCss = component_arr => {
|
||||||
let styles = ""
|
let styles = ""
|
||||||
for (const { _styles, _id, _children, _component } of component_arr) {
|
for (const { _styles, _id, _children, _component } of component_arr) {
|
||||||
let [componentName] = _component.match(/[a-z]*$/)
|
let [componentName] = _component.match(/[a-z]*$/)
|
||||||
Object.keys(_styles).forEach(selector => {
|
Object.keys(_styles).forEach(selector => {
|
||||||
const cssString = generate_css(_styles[selector])
|
const cssString = exports.generateCss(_styles[selector])
|
||||||
if (cssString) {
|
if (cssString) {
|
||||||
styles += apply_class(_id, componentName, cssString, selector)
|
styles += exports.applyClass(_id, componentName, cssString, selector)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (_children && _children.length) {
|
if (_children && _children.length) {
|
||||||
styles += generate_screen_css(_children) + "\n"
|
styles += exports.generateAssetCss(_children) + "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return styles.trim()
|
return styles.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generate_css = style => {
|
exports.generateCss = style => {
|
||||||
let cssString = Object.entries(style).reduce((str, [key, value]) => {
|
let cssString = Object.entries(style).reduce((str, [key, value]) => {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -33,7 +33,7 @@ export const generate_css = style => {
|
||||||
return (cssString || "").trim()
|
return (cssString || "").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apply_class = (id, name = "element", styles, selector) => {
|
exports.applyClass = (id, name = "element", styles, selector) => {
|
||||||
if (selector === "normal") {
|
if (selector === "normal") {
|
||||||
return `.${name}-${id} {\n${styles}\n}`
|
return `.${name}-${id} {\n${styles}\n}`
|
||||||
} else {
|
} else {
|
|
@ -0,0 +1,73 @@
|
||||||
|
const handlebars = require("handlebars")
|
||||||
|
|
||||||
|
handlebars.registerHelper("object", value => {
|
||||||
|
return new handlebars.SafeString(JSON.stringify(value))
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When running mustache statements to execute on the context of the automation it possible user's may input mustache
|
||||||
|
* in a few different forms, some of which are invalid but are logically valid. An example of this would be the mustache
|
||||||
|
* statement "{{steps[0].revision}}" here it is obvious the user is attempting to access an array or object using array
|
||||||
|
* like operators. These are not supported by Mustache and therefore the statement will fail. This function will clean up
|
||||||
|
* the mustache statement so it instead reads as "{{steps.0.revision}}" which is valid and will work. It may also be expanded
|
||||||
|
* to include any other mustache statement cleanup that has been deemed necessary for the system.
|
||||||
|
*
|
||||||
|
* @param {string} string The string which *may* contain mustache statements, it is OK if it does not contain any.
|
||||||
|
* @returns {string} The string that was input with cleaned up mustache statements as required.
|
||||||
|
*/
|
||||||
|
function cleanMustache(string) {
|
||||||
|
let charToReplace = {
|
||||||
|
"[": ".",
|
||||||
|
"]": "",
|
||||||
|
}
|
||||||
|
let regex = new RegExp(/{{[^}}]*}}/g)
|
||||||
|
let matches = string.match(regex)
|
||||||
|
if (matches == null) {
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
for (let match of matches) {
|
||||||
|
let baseIdx = string.indexOf(match)
|
||||||
|
for (let key of Object.keys(charToReplace)) {
|
||||||
|
let idxChar = match.indexOf(key)
|
||||||
|
if (idxChar !== -1) {
|
||||||
|
string =
|
||||||
|
string.slice(baseIdx, baseIdx + idxChar) +
|
||||||
|
charToReplace[key] +
|
||||||
|
string.slice(baseIdx + idxChar + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an input object this will recurse through all props to try and update
|
||||||
|
* any handlebars/mustache statements within.
|
||||||
|
* @param {object|array} inputs The input structure which is to be recursed, it is important to note that
|
||||||
|
* if the structure contains any cycles then this will fail.
|
||||||
|
* @param {object} context The context that handlebars should fill data from.
|
||||||
|
* @returns {object|array} The structure input, as fully updated as possible.
|
||||||
|
*/
|
||||||
|
function recurseMustache(inputs, context) {
|
||||||
|
// JSON stringify will fail if there are any cycles, stops infinite recursion
|
||||||
|
try {
|
||||||
|
JSON.stringify(inputs)
|
||||||
|
} catch (err) {
|
||||||
|
throw "Unable to process inputs to JSON, cannot recurse"
|
||||||
|
}
|
||||||
|
for (let key of Object.keys(inputs)) {
|
||||||
|
let val = inputs[key]
|
||||||
|
if (typeof val === "string") {
|
||||||
|
val = cleanMustache(inputs[key])
|
||||||
|
const template = handlebars.compile(val)
|
||||||
|
inputs[key] = template(context)
|
||||||
|
}
|
||||||
|
// this covers objects and arrays
|
||||||
|
else if (typeof val === "object") {
|
||||||
|
inputs[key] = recurseMustache(inputs[key], context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.recurseMustache = recurseMustache
|
Loading…
Reference in New Issue