Merge branch 'master' of https://github.com/Budibase/budibase
This commit is contained in:
commit
02b512ffd9
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"javascript.format.enable": false,
|
||||
"svelte.plugin.svelte.format.enable": false,
|
||||
"html.format.enable": false,
|
||||
"json.format.enable": false,
|
||||
"editor.trimAutoWhitespace": false,
|
||||
"sass.format.deleteWhitespace": false
|
||||
}
|
|
@ -11,9 +11,10 @@ import { fade } from "svelte/transition";
|
|||
<div class="root">
|
||||
|
||||
<div class="top-nav">
|
||||
<IconButton icon="home"
|
||||
<button class="home-logo"><img src="/assets/budibase-logo-only.png"/></button>
|
||||
<!-- <IconButton icon="home"
|
||||
color="var(--slate)"
|
||||
hoverColor="var(--secondary75)"/>
|
||||
hoverColor="var(--secondary75)"/> -->
|
||||
<span class:active={$store.isBackend}
|
||||
class="topnavitem"
|
||||
on:click={store.showBackend}>
|
||||
|
@ -47,14 +48,18 @@ import { fade } from "svelte/transition";
|
|||
width:100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
flex: 0 0 auto;
|
||||
height: 25px;
|
||||
height: 48px;
|
||||
background: white;
|
||||
padding: 5px;
|
||||
padding: 0px 15px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -66,14 +71,20 @@ import { fade } from "svelte/transition";
|
|||
.content > div {
|
||||
height:100%;
|
||||
width:100%;
|
||||
|
||||
}
|
||||
|
||||
.topnavitem {
|
||||
cursor: pointer;
|
||||
color: var(--secondary50);
|
||||
padding: 0px 15px;
|
||||
margin: 0px 15px;
|
||||
padding-top: 4px;
|
||||
font-weight: 600;
|
||||
font-size: .9rem;
|
||||
font-size: 1rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.topnavitem:hover {
|
||||
|
@ -84,8 +95,31 @@ import { fade } from "svelte/transition";
|
|||
|
||||
.active {
|
||||
color: var(--primary100);
|
||||
font-weight: 900;
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid var(--primary100);
|
||||
border-top: 2px solid transparent;
|
||||
}
|
||||
|
||||
.home-logo {
|
||||
border-style: none;
|
||||
background-color: rgba(0,0,0,0);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
height: 40px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.home-logo:hover {
|
||||
color: var(--hovercolor);
|
||||
}
|
||||
|
||||
.home-logo:active {
|
||||
outline:none;
|
||||
}
|
||||
|
||||
|
||||
.home-logo img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -10,14 +10,14 @@ import {
|
|||
pipe, getNode, validate,
|
||||
constructHierarchy, templateApi
|
||||
} from "../common/core";
|
||||
import {writable} from "svelte/store";
|
||||
import { writable } from "svelte/store";
|
||||
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
||||
import { buildPropsHierarchy } from "../userInterface/pagesParsing/buildPropsHierarchy"
|
||||
import api from "./api";
|
||||
import { isRootComponent, getExactComponent } from "../userInterface/pagesParsing/searchComponents";
|
||||
import { rename } from "../userInterface/pagesParsing/renameScreen";
|
||||
import {
|
||||
getNewComponentInfo, getScreenInfo
|
||||
getNewComponentInfo, getScreenInfo, getComponentInfo
|
||||
} from "../userInterface/pagesParsing/createProps";
|
||||
import {
|
||||
loadLibs, loadLibUrls, loadGeneratorLibs
|
||||
|
@ -28,30 +28,30 @@ let appname = "";
|
|||
export const getStore = () => {
|
||||
|
||||
const initial = {
|
||||
apps:[],
|
||||
appname:"",
|
||||
apps: [],
|
||||
appname: "",
|
||||
hierarchy: {},
|
||||
actions: [],
|
||||
triggers: [],
|
||||
pages:defaultPagesObject(),
|
||||
mainUi:{},
|
||||
unauthenticatedUi:{},
|
||||
components:[],
|
||||
currentFrontEndItem:null,
|
||||
currentComponentInfo:null,
|
||||
currentFrontEndType:"none",
|
||||
pages: defaultPagesObject(),
|
||||
mainUi: {},
|
||||
unauthenticatedUi: {},
|
||||
components: [],
|
||||
currentFrontEndItem: null,
|
||||
currentComponentInfo: null,
|
||||
currentFrontEndType: "none",
|
||||
currentPageName: "",
|
||||
currentComponentProps:null,
|
||||
currentComponentProps: null,
|
||||
currentNodeIsNew: false,
|
||||
errors: [],
|
||||
activeNav: "database",
|
||||
isBackend:true,
|
||||
isBackend: true,
|
||||
hasAppPackage: false,
|
||||
accessLevels: {version:0, levels:[]},
|
||||
accessLevels: { version: 0, levels: [] },
|
||||
currentNode: null,
|
||||
libraries:null,
|
||||
showSettings:false,
|
||||
useAnalytics:true,
|
||||
libraries: null,
|
||||
showSettings: false,
|
||||
useAnalytics: true,
|
||||
};
|
||||
|
||||
const store = writable(initial);
|
||||
|
@ -82,7 +82,7 @@ export const getStore = () => {
|
|||
store.setCurrentScreen = setCurrentScreen(store);
|
||||
store.setCurrentPage = setCurrentPage(store);
|
||||
store.createScreen = createScreen(store);
|
||||
store.removeComponentLibrary =removeComponentLibrary(store);
|
||||
store.removeComponentLibrary = removeComponentLibrary(store);
|
||||
store.addStylesheet = addStylesheet(store);
|
||||
store.removeStylesheet = removeStylesheet(store);
|
||||
store.savePage = savePage(store);
|
||||
|
@ -91,6 +91,10 @@ export const getStore = () => {
|
|||
store.showSettings = showSettings(store);
|
||||
store.useAnalytics = useAnalytics(store);
|
||||
store.createGeneratedComponents = createGeneratedComponents(store);
|
||||
store.addChildComponent = addChildComponent(store);
|
||||
store.selectComponent = selectComponent(store);
|
||||
store.updateComponentProp = updateComponentProp(store);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
|
@ -102,7 +106,7 @@ const initialise = (store, initial) => async () => {
|
|||
? last(window.location.hash.substr(1).split("/"))
|
||||
: "";
|
||||
|
||||
if(!appname) {
|
||||
if (!appname) {
|
||||
initial.apps = await api.get(`/_builder/api/apps`).then(r => r.json());
|
||||
initial.hasAppPackage = false;
|
||||
store.set(initial);
|
||||
|
@ -126,14 +130,15 @@ const initialise = (store, initial) => async () => {
|
|||
initial.actions = values(pkg.appDefinition.actions);
|
||||
initial.triggers = pkg.appDefinition.triggers;
|
||||
|
||||
if(!!initial.hierarchy && !isEmpty(initial.hierarchy)) {
|
||||
if (!!initial.hierarchy && !isEmpty(initial.hierarchy)) {
|
||||
initial.hierarchy = constructHierarchy(initial.hierarchy);
|
||||
const shadowHierarchy = createShadowHierarchy(initial.hierarchy);
|
||||
if(initial.currentNode !== null)
|
||||
if (initial.currentNode !== null)
|
||||
initial.currentNode = getNode(
|
||||
shadowHierarchy, initial.currentNode.nodeId
|
||||
);
|
||||
}
|
||||
|
||||
store.set(initial);
|
||||
return initial;
|
||||
}
|
||||
|
@ -224,7 +229,7 @@ const saveCurrentNode = (store) => () => {
|
|||
|
||||
const errors = validate.node(s.currentNode);
|
||||
s.errors = errors;
|
||||
if(errors.length > 0) {
|
||||
if (errors.length > 0) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -235,7 +240,7 @@ const saveCurrentNode = (store) => () => {
|
|||
s.hierarchy, s.currentNode.nodeId);
|
||||
|
||||
let index = parentNode.children.length;
|
||||
if(!!existingNode) {
|
||||
if (!!existingNode) {
|
||||
// remove existing
|
||||
index = existingNode.parent().children.indexOf(existingNode);
|
||||
existingNode.parent().children = pipe(existingNode.parent().children, [
|
||||
|
@ -251,7 +256,7 @@ const saveCurrentNode = (store) => () => {
|
|||
);
|
||||
|
||||
const newIndexOfchild = child => {
|
||||
if(child === cloned) return index;
|
||||
if (child === cloned) return index;
|
||||
const currentIndex = parentNode.children.indexOf(child);
|
||||
return currentIndex >= index ? currentIndex + 1 : currentIndex;
|
||||
}
|
||||
|
@ -260,7 +265,7 @@ const saveCurrentNode = (store) => () => {
|
|||
sortBy(newIndexOfchild)
|
||||
]);
|
||||
|
||||
if(!existingNode && s.currentNode.type === "record") {
|
||||
if (!existingNode && s.currentNode.type === "record") {
|
||||
const defaultIndex = templateApi(s.hierarchy)
|
||||
.getNewIndexTemplate(cloned.parent());
|
||||
defaultIndex.name = `all_${cloned.collectionName}`;
|
||||
|
@ -295,7 +300,7 @@ const deleteCurrentNode = store => () => {
|
|||
? find(n => n != s.currentNode)
|
||||
(s.hierarchy.children)
|
||||
: nodeToDelete.parent();
|
||||
if(hierarchyFunctions.isRecord(nodeToDelete)) {
|
||||
if (hierarchyFunctions.isRecord(nodeToDelete)) {
|
||||
nodeToDelete.parent().children = filter(c => c.nodeId !== nodeToDelete.nodeId)
|
||||
(nodeToDelete.parent().children);
|
||||
} else {
|
||||
|
@ -329,14 +334,14 @@ const deleteField = databaseStore => field => {
|
|||
}
|
||||
|
||||
|
||||
const saveAction = store => (newAction, isNew, oldAction=null) => {
|
||||
const saveAction = store => (newAction, isNew, oldAction = null) => {
|
||||
store.update(s => {
|
||||
|
||||
const existingAction = isNew
|
||||
? null
|
||||
: find(a => a.name === oldAction.name)(s.actions);
|
||||
|
||||
if(existingAction) {
|
||||
if (existingAction) {
|
||||
s.actions = pipe(s.actions, [
|
||||
map(a => a === existingAction ? newAction : a)
|
||||
]);
|
||||
|
@ -356,14 +361,14 @@ const deleteAction = store => action => {
|
|||
});
|
||||
}
|
||||
|
||||
const saveTrigger = store => (newTrigger, isNew, oldTrigger=null) => {
|
||||
const saveTrigger = store => (newTrigger, isNew, oldTrigger = null) => {
|
||||
store.update(s => {
|
||||
|
||||
const existingTrigger = isNew
|
||||
? null
|
||||
: find(a => a.name === oldTrigger.name)(s.triggers);
|
||||
|
||||
if(existingTrigger) {
|
||||
if (existingTrigger) {
|
||||
s.triggers = pipe(s.triggers, [
|
||||
map(a => a === existingTrigger ? newTrigger : a)
|
||||
]);
|
||||
|
@ -385,7 +390,7 @@ const deleteTrigger = store => trigger => {
|
|||
const incrementAccessLevelsVersion = (s) =>
|
||||
s.accessLevels.version = (s.accessLevels.version || 0) + 1;
|
||||
|
||||
const saveLevel = store => (newLevel, isNew, oldLevel=null) => {
|
||||
const saveLevel = store => (newLevel, isNew, oldLevel = null) => {
|
||||
store.update(s => {
|
||||
|
||||
const levels = s.accessLevels.levels;
|
||||
|
@ -394,7 +399,7 @@ const saveLevel = store => (newLevel, isNew, oldLevel=null) => {
|
|||
? null
|
||||
: find(a => a.name === oldLevel.name)(levels);
|
||||
|
||||
if(existingLevel) {
|
||||
if (existingLevel) {
|
||||
s.accessLevels.levels = pipe(levels, [
|
||||
map(a => a === existingLevel ? newLevel : a)
|
||||
]);
|
||||
|
@ -470,7 +475,7 @@ const createGeneratedComponents = store => components => {
|
|||
s.screens = [...s.screens, ...components];
|
||||
|
||||
const doCreate = async () => {
|
||||
for(let c of components) {
|
||||
for (let c of components) {
|
||||
await api.post(`/_builder/api/${s.appname}/screen`, c);
|
||||
}
|
||||
|
||||
|
@ -496,7 +501,7 @@ const deleteScreen = store => name => {
|
|||
|
||||
s.components = components;
|
||||
s.screens = screens;
|
||||
if(s.currentFrontEndItem.name === name) {
|
||||
if (s.currentFrontEndItem.name === name) {
|
||||
s.currentFrontEndItem = null;
|
||||
s.currentFrontEndType = "";
|
||||
}
|
||||
|
@ -514,18 +519,18 @@ const renameScreen = store => (oldname, newname) => {
|
|||
screens, pages, error, changedScreens
|
||||
} = rename(s.pages, s.screens, oldname, newname);
|
||||
|
||||
if(error) {
|
||||
if (error) {
|
||||
// should really do something with this
|
||||
return s;
|
||||
}
|
||||
|
||||
s.screens = screens;
|
||||
s.pages = pages;
|
||||
if(s.currentFrontEndItem.name === oldname)
|
||||
if (s.currentFrontEndItem.name === oldname)
|
||||
s.currentFrontEndItem.name = newname;
|
||||
|
||||
const saveAllChanged = async () => {
|
||||
for(let screenName of changedScreens) {
|
||||
for (let screenName of changedScreens) {
|
||||
const changedScreen
|
||||
= getExactComponent(screens, screenName);
|
||||
await api.post(`/_builder/api/${s.appname}/screen`, changedScreen);
|
||||
|
@ -546,7 +551,7 @@ const renameScreen = store => (oldname, newname) => {
|
|||
|
||||
const savePage = store => async page => {
|
||||
store.update(s => {
|
||||
if(s.currentFrontEndType !== "page" || !s.currentPageName) {
|
||||
if (s.currentFrontEndType !== "page" || !s.currentPageName) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -559,7 +564,7 @@ const savePage = store => async page => {
|
|||
const addComponentLibrary = store => async lib => {
|
||||
|
||||
const response =
|
||||
await api.get(`/_builder/api/${appname}/componentlibrary?lib=${encodeURI(lib)}`,undefined, false);
|
||||
await api.get(`/_builder/api/${appname}/componentlibrary?lib=${encodeURI(lib)}`, undefined, false);
|
||||
|
||||
const success = response.status === 200;
|
||||
|
||||
|
@ -574,10 +579,10 @@ const addComponentLibrary = store => async lib => {
|
|||
: [];
|
||||
|
||||
store.update(s => {
|
||||
if(success) {
|
||||
if (success) {
|
||||
|
||||
const componentsArray = [];
|
||||
for(let c in components) {
|
||||
for (let c in components) {
|
||||
componentsArray.push(components[c]);
|
||||
}
|
||||
|
||||
|
@ -632,7 +637,7 @@ const refreshComponents = store => async () => {
|
|||
|
||||
const components = pipe(componentsAndGenerators.components, [
|
||||
keys,
|
||||
map(k => ({...componentsAndGenerators[k], name:k}))
|
||||
map(k => ({ ...componentsAndGenerators[k], name: k }))
|
||||
]);
|
||||
|
||||
store.update(s => {
|
||||
|
@ -648,8 +653,8 @@ const refreshComponents = store => async () => {
|
|||
const savePackage = (store, s) => {
|
||||
|
||||
const appDefinition = {
|
||||
hierarchy:s.hierarchy,
|
||||
triggers:s.triggers,
|
||||
hierarchy: s.hierarchy,
|
||||
triggers: s.triggers,
|
||||
actions: keyBy("name")(s.actions),
|
||||
props: {
|
||||
main: buildPropsHierarchy(
|
||||
|
@ -665,8 +670,8 @@ const savePackage = (store, s) => {
|
|||
|
||||
const data = {
|
||||
appDefinition,
|
||||
accessLevels:s.accessLevels,
|
||||
pages:s.pages,
|
||||
accessLevels: s.accessLevels,
|
||||
pages: s.pages,
|
||||
}
|
||||
|
||||
return api.post(`/_builder/api/${s.appname}/appPackage`, data);
|
||||
|
@ -689,3 +694,44 @@ const setCurrentPage = store => pageName => {
|
|||
return s;
|
||||
})
|
||||
}
|
||||
|
||||
const addChildComponent = store => component => {
|
||||
|
||||
store.update(s => {
|
||||
const newComponent = getNewComponentInfo(
|
||||
s.components, component);
|
||||
|
||||
const children = s.currentFrontEndItem.props._children;
|
||||
|
||||
const component_definition = Object.assign(
|
||||
cloneDeep(newComponent.fullProps), {
|
||||
_component: component,
|
||||
})
|
||||
|
||||
s.currentFrontEndItem.props._children =
|
||||
children ?
|
||||
children.concat(component_definition) :
|
||||
[component_definition];
|
||||
|
||||
return s;
|
||||
})
|
||||
}
|
||||
|
||||
const selectComponent = store => component => {
|
||||
store.update(s => {
|
||||
s.currentComponentInfo = component;
|
||||
return s;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const updateComponentProp = store => (name, value) => {
|
||||
store.update(s => {
|
||||
const current_component = s.currentComponentInfo;
|
||||
s.currentComponentInfo[name] = value;
|
||||
_saveScreen(store, s, s.currentFrontEndItem);
|
||||
s.currentComponentInfo = current_component;
|
||||
return s;
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor" d="M4.828 21l-.02.02-.021-.02H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H4.828zM20 15V5H4v14L14 9l6 6zm0 2.828l-6-6L6.828 19H20v-1.172zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 400 B |
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor" d="M5 5v14h14V5H5zM4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm5.869 12l-.82 2H6.833L11 7h2l4.167 10H14.95l-.82-2H9.87zm.82-2h2.622L12 9.8 10.689 13z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 339 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor" d="M21 20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v16zM11 5H5v14h6V5zm8 8h-6v6h6v-6zm0-8h-6v6h6V5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 280 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor" d="M19.228 18.732l1.768-1.768 1.767 1.768a2.5 2.5 0 1 1-3.535 0zM8.878 1.08l11.314 11.313a1 1 0 0 1 0 1.415l-8.485 8.485a1 1 0 0 1-1.414 0l-8.485-8.485a1 1 0 0 1 0-1.415l7.778-7.778-2.122-2.121L8.88 1.08zM11 6.03L3.929 13.1 11 20.173l7.071-7.071L11 6.029z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 415 B |
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor" d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm8 10h6v2h-6v-2zm-3.333-3L5.838 9.172l1.415-1.415L11.495 12l-4.242 4.243-1.415-1.415L8.667 12z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 346 B |
|
@ -0,0 +1,5 @@
|
|||
export { default as LayoutIcon } from './Layout.svelte';
|
||||
export { default as PaintIcon } from './Paint.svelte';
|
||||
export { default as TerminalIcon } from './Terminal.svelte';
|
||||
export { default as InputIcon } from './Input.svelte';
|
||||
export { default as ImageIcon } from './Image.svelte';
|
|
@ -0,0 +1,54 @@
|
|||
<script>
|
||||
export let meta = [];
|
||||
export let size = '';
|
||||
export let values = [];
|
||||
</script>
|
||||
|
||||
<div class="inputs {size}">
|
||||
{#each meta as { placeholder }, i}
|
||||
<input type="number" placeholder="{placeholder}" bind:value={values[i]}/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.inputs {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
width: 83px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #163057;
|
||||
opacity: 0.7;
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #DBDBDB;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.small > input {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.small > input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
:root {
|
||||
--primary100: #454CA0FF;
|
||||
--primary100: #173157FF;
|
||||
--primary75: #454CA0BF;
|
||||
--primary50: #454CA080;
|
||||
--primary25: #454CA040;
|
||||
|
@ -7,12 +7,12 @@
|
|||
--primary5: #454ca00c;
|
||||
--primarydark: #3F448A;
|
||||
|
||||
--secondary100: #162B4DFF;
|
||||
--secondary100:#828fa5;
|
||||
--secondary75: #162B4DBF;
|
||||
--secondary50: #162B4D80;
|
||||
--secondary25: #162B4D40;
|
||||
--secondary10: #162B4D1A;
|
||||
--secondary5: rgba(22, 43, 77, 0.068);
|
||||
--secondary5:#fff;
|
||||
--secondarydark: #3F448A;
|
||||
|
||||
--tertiary: #F2F5F7;
|
||||
|
@ -52,6 +52,9 @@
|
|||
--heavybodytext: var(--fontbold) "regular" var(--secondary100) 16pt;
|
||||
--quotation: var(--fontnormal) "italics" var(--darkslate) 16pt;
|
||||
--smallheavybodytext: var(--fontbold) "regular" var(--secondary100) 14pt;
|
||||
|
||||
--background-button: #e6eeff;
|
||||
--button-text: #0055ff;
|
||||
}
|
||||
|
||||
html, body {
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
<script>
|
||||
let snippets = [];
|
||||
let current_snippet = 0;
|
||||
let snippet_text = ''
|
||||
let id = 0;
|
||||
|
||||
function save_snippet() {
|
||||
if (!snippet_text) return;
|
||||
|
||||
const index = snippets.findIndex(({ id }) => current_snippet === id);
|
||||
|
||||
if (index > -1) {
|
||||
snippets[index].snippet = snippet_text;
|
||||
} else {
|
||||
snippets = snippets.concat({ snippet: snippet_text , id: id });
|
||||
}
|
||||
snippet_text = '';
|
||||
current_snippet = ++id;
|
||||
}
|
||||
|
||||
function edit_snippet(id) {
|
||||
const { snippet, id: _id } = snippets.find(({ id:_id }) => _id === id);
|
||||
current_snippet = id
|
||||
snippet_text = snippet;
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3>Code</h3>
|
||||
|
||||
<p>Use the code box below to add snippets of javascript to enhance your webapp</p>
|
||||
|
||||
<div class="editor">
|
||||
<textarea class="code" bind:value={snippet_text} />
|
||||
<button on:click={save_snippet}>Save</button>
|
||||
</div>
|
||||
|
||||
<div class="snippets">
|
||||
<h3>Snippets added</h3>
|
||||
{#each snippets as { id, snippet } }
|
||||
<div class="snippet">
|
||||
<pre class="code">{snippet}</pre>
|
||||
<button on:click={() => edit_snippet(id)}>Edit</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h3 {
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #8997ab;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code {
|
||||
width: 100%;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: #173157;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
white-space: pre;
|
||||
color: #eee;
|
||||
padding: 10px;
|
||||
font-family: monospace;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.editor textarea {
|
||||
resize: none;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
box-shadow: 0 0 black;
|
||||
color: #eee;
|
||||
right: 5px;
|
||||
bottom: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.snippets {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.snippet {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.snippet pre {
|
||||
background: #f9f9f9;
|
||||
color: #333;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.snippet button {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,150 @@
|
|||
<script>
|
||||
|
||||
import PropsView from "./PropsView.svelte";
|
||||
import { store } from "../builderStore";
|
||||
import { isRootComponent } from "./pagesParsing/searchComponents";
|
||||
import IconButton from "../common/IconButton.svelte";
|
||||
import Textbox from "../common/Textbox.svelte";
|
||||
import { pipe } from "../common/core";
|
||||
import {
|
||||
getScreenInfo
|
||||
} from "./pagesParsing/createProps";
|
||||
import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/';
|
||||
import CodeEditor from './CodeEditor.svelte';
|
||||
import LayoutEditor from './LayoutEditor.svelte';
|
||||
|
||||
import {
|
||||
cloneDeep,
|
||||
join,
|
||||
split,
|
||||
map,
|
||||
keys,
|
||||
isUndefined,
|
||||
last
|
||||
} from "lodash/fp";
|
||||
import { assign } from "lodash";
|
||||
|
||||
let component;
|
||||
let name = "";
|
||||
let description = "";
|
||||
let tagsString = "";
|
||||
let nameInvalid = "";
|
||||
let componentInfo = {};
|
||||
let modalElement
|
||||
let propsValidationErrors = [];
|
||||
let originalName="";
|
||||
let components;
|
||||
let ignoreStore = false;
|
||||
|
||||
// $: shortName = last(name.split("/"));
|
||||
|
||||
store.subscribe(s => {
|
||||
if(ignoreStore) return;
|
||||
component = s.currentComponentInfo;
|
||||
if(!component) return;
|
||||
originalName = component.name;
|
||||
name = component.name;
|
||||
description = component.description;
|
||||
tagsString = join(", ")(component.tags);
|
||||
componentInfo = s.currentComponentInfo;
|
||||
components = s.components;
|
||||
});
|
||||
|
||||
const onPropsChanged = store.updateComponentProp;
|
||||
|
||||
let current_view = 'props';
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<ul>
|
||||
<li>
|
||||
<button class:selected={current_view === 'props'} on:click={() => current_view = 'props'}>
|
||||
<PaintIcon />
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class:selected={current_view === 'layout'} on:click={() => current_view = 'layout'}>
|
||||
<LayoutIcon />
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class:selected={current_view === 'code'} on:click={() => current_view = 'code'}>
|
||||
<TerminalIcon />
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{#if !componentInfo.component}
|
||||
<div class="component-props-container">
|
||||
|
||||
{#if current_view === 'props'}
|
||||
<PropsView {componentInfo} {components} {onPropsChanged} />
|
||||
{:else if current_view === 'layout'}
|
||||
<LayoutEditor />
|
||||
{:else}
|
||||
<CodeEditor />
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<h1> This is a screen, this will be dealt with later</h1>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
.title > div:nth-child(1) {
|
||||
grid-column-start: name;
|
||||
color: var(--secondary100);
|
||||
}
|
||||
|
||||
.title > div:nth-child(2) {
|
||||
grid-column-start: actions;
|
||||
}
|
||||
|
||||
.component-props-container {
|
||||
margin-top: 10px;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 20px;
|
||||
background: none;
|
||||
border-radius: 5px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
li button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: var(--button-text);
|
||||
background: var(--background-button)!important;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import ComponentsHierarchyChildren from './ComponentsHierarchyChildren.svelte';
|
||||
|
||||
import {
|
||||
last,
|
||||
|
@ -18,13 +19,6 @@ import getIcon from "../common/icon";
|
|||
import { store } from "../builderStore";
|
||||
|
||||
export let components = []
|
||||
export let thisLevel = "";
|
||||
|
||||
let pathPartsThisLevel;
|
||||
let componentsThisLevel;
|
||||
let subfolders;
|
||||
|
||||
let expandedFolders = [];
|
||||
|
||||
const joinPath = join("/");
|
||||
|
||||
|
@ -35,39 +29,8 @@ const normalizedName = name => pipe(name, [
|
|||
trimChars(" ")
|
||||
]);
|
||||
|
||||
|
||||
const isOnThisLevel = (c) =>
|
||||
normalizedName(c.name).split("/").length === pathPartsThisLevel
|
||||
&&
|
||||
(!thisLevel || normalizedName(c.name).startsWith(normalizedName(thisLevel)));
|
||||
|
||||
const notOnThisLevel = (c) => !isOnThisLevel(c);
|
||||
|
||||
const isInSubfolder = (subfolder, c) =>
|
||||
normalizedName(c.name).startsWith(
|
||||
trimCharsStart("/")(
|
||||
joinPath([thisLevel, subfolder])));
|
||||
|
||||
const isOnNextLevel = (c) =>
|
||||
normalizedName(c.name).split("/").length === pathPartsThisLevel + 1
|
||||
|
||||
const lastPartOfName = (c) =>
|
||||
last(c.name.split("/"))
|
||||
|
||||
const subFolder = (c) => {
|
||||
const cname = normalizedName(c.name);
|
||||
const folderName = cname.substring(thisLevel.length, cname.length).split("/")[0];
|
||||
|
||||
return ({
|
||||
name: folderName,
|
||||
isExpanded: includes(folderName)(expandedFolders),
|
||||
path: thisLevel + "/" + folderName
|
||||
});
|
||||
}
|
||||
|
||||
const subComponents = (subfolder) => pipe(components, [
|
||||
filter(c => isInSubfolder(subfolder, c))
|
||||
]);
|
||||
last(c.name ? c.name.split("/") : c._component.split("/"))
|
||||
|
||||
const expandFolder = folder => {
|
||||
const expandedFolder = {...folder};
|
||||
|
@ -97,49 +60,33 @@ const isFolderSelected = (current, folder) =>
|
|||
|
||||
|
||||
|
||||
$: {
|
||||
pathPartsThisLevel = !thisLevel
|
||||
? 1
|
||||
: normalizedName(thisLevel).split("/").length + 1;
|
||||
|
||||
componentsThisLevel =
|
||||
$: _components =
|
||||
pipe(components, [
|
||||
filter(isOnThisLevel),
|
||||
map(c => ({component:c, title:lastPartOfName(c)})),
|
||||
map(c => ({component: c, title:lastPartOfName(c)})),
|
||||
sortBy("title")
|
||||
]);
|
||||
|
||||
subfolders =
|
||||
pipe(components, [
|
||||
filter(notOnThisLevel),
|
||||
sortBy("name"),
|
||||
map(subFolder),
|
||||
uniqWith((f1,f2) => f1.path === f2.path)
|
||||
]);
|
||||
function select_component(screen, component) {
|
||||
store.setCurrentScreen(screen);
|
||||
store.selectComponent(component);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="root" style={`padding-left: calc(10px * ${pathPartsThisLevel})`}>
|
||||
<div class="root">
|
||||
|
||||
{#each subfolders as folder}
|
||||
<div class="hierarchy-item folder"
|
||||
on:click|stopPropagation={() => expandFolder(folder)}>
|
||||
<span>{@html getIcon(folder.isExpanded ? "chevron-down" : "chevron-right", "16")}</span>
|
||||
<span class="title" class:currentfolder={$store.currentFrontEndItem && isInSubfolder(folder.name, $store.currentFrontEndItem)}>{folder.name}</span>
|
||||
{#if folder.isExpanded}
|
||||
<svelte:self components={subComponents(folder.name)}
|
||||
thisLevel={folder.path} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#each componentsThisLevel as component}
|
||||
<div class="hierarchy-item component" class:selected={isComponentSelected($store.currentFrontEndType, $store.currentFrontEndItem, component.component)}
|
||||
{#each _components as component}
|
||||
<div class="hierarchy-item component"
|
||||
class:selected={isComponentSelected($store.currentFrontEndType, $store.currentFrontEndItem, component.component)}
|
||||
on:click|stopPropagation={() => store.setCurrentScreen(component.component.name)}>
|
||||
<span>{@html getIcon("circle", "7")}</span>
|
||||
|
||||
<span class="title">{component.title}</span>
|
||||
</div>
|
||||
{#if component.component.props && component.component.props._children}
|
||||
<ComponentsHierarchyChildren components={component.component.props._children}
|
||||
onSelect={child => select_component(component.component.name, child)} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
|
@ -147,31 +94,33 @@ $: {
|
|||
<style>
|
||||
|
||||
.root {
|
||||
color: var(--secondary50);
|
||||
font-size: .9rem;
|
||||
font-weight: bold;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
color: #828fa5;
|
||||
}
|
||||
|
||||
.hierarchy-item {
|
||||
cursor: pointer;
|
||||
padding: 5px 0px;
|
||||
padding: 11px 7px;
|
||||
|
||||
margin: 5px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.hierarchy-item:hover {
|
||||
color: var(--secondary);
|
||||
/* color: var(--secondary); */
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.component {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.currentfolder {
|
||||
color: var(--secondary100);
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: var(--primary100);
|
||||
font-weight: bold;
|
||||
color: var(--button-text);
|
||||
background: var(--background-button)!important;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<script>
|
||||
import { last } from "lodash/fp";
|
||||
import { pipe } from "../common/core";
|
||||
|
||||
export let components = [];
|
||||
export let onSelect = () => {};
|
||||
|
||||
const capitalise = s => s.substring(0,1).toUpperCase() + s.substring(1);
|
||||
const get_name = s => last(s.split('/'));
|
||||
const get_capitalised_name = name => pipe(name, [get_name,capitalise]);
|
||||
</script>
|
||||
|
||||
{#each components as component}
|
||||
<ul>
|
||||
<li on:click|stopPropagation={() => onSelect(component)}>
|
||||
{get_capitalised_name(component._component)}
|
||||
|
||||
{#if component._children}
|
||||
<svelte:self components={component._children}/>
|
||||
{/if}
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
{/each}
|
|
@ -8,6 +8,7 @@ import {
|
|||
groupBy, keys, find, sortBy
|
||||
} from "lodash/fp";
|
||||
import { pipe } from "../common/core";
|
||||
import { ImageIcon, InputIcon, LayoutIcon } from '../common/Icons/';
|
||||
|
||||
let componentLibraries=[];
|
||||
|
||||
|
@ -29,9 +30,7 @@ const addRootComponent = (c, all) => {
|
|||
|
||||
};
|
||||
|
||||
const onComponentChosen = (component) => {
|
||||
|
||||
};
|
||||
const onComponentChosen = store.addChildComponent;
|
||||
|
||||
store.subscribe(s => {
|
||||
|
||||
|
@ -46,9 +45,7 @@ store.subscribe(s => {
|
|||
componentLibraries = newComponentLibraries;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
let current_view = 'text';
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -59,22 +56,31 @@ store.subscribe(s => {
|
|||
</div>
|
||||
|
||||
<div class="library-container">
|
||||
<ul>
|
||||
<li>
|
||||
<button class:selected={current_view === 'text'} on:click={() => current_view = 'text'}>
|
||||
<InputIcon />
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class:selected={current_view === 'layout'} on:click={() => current_view = 'layout'}>
|
||||
<LayoutIcon />
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class:selected={current_view === 'media'} on:click={() => current_view = 'media'}>
|
||||
<ImageIcon />
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<div class="inner-header">
|
||||
Components
|
||||
</div>
|
||||
|
||||
{#each lib.components as component}
|
||||
{#each lib.components.filter(_ => true) as component}
|
||||
|
||||
<div class="component"
|
||||
on:click={() => onComponentChosen(component)}>
|
||||
on:click={() => onComponentChosen(component.name)}>
|
||||
<div class="name">
|
||||
{splitName(component.name).componentName}
|
||||
</div>
|
||||
<div class="description">
|
||||
{component.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/each}
|
||||
|
@ -117,8 +123,16 @@ store.subscribe(s => {
|
|||
}
|
||||
|
||||
.component {
|
||||
padding: 2px 0px;
|
||||
padding: 0 15px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
margin: 10px 0;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
color: #163057;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.component:hover {
|
||||
|
@ -126,8 +140,11 @@ store.subscribe(s => {
|
|||
}
|
||||
|
||||
.component > .name {
|
||||
color: var(--secondary100);
|
||||
color: #163057;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.component > .description {
|
||||
|
@ -137,6 +154,34 @@ store.subscribe(s => {
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 20px;
|
||||
background: none;
|
||||
border-radius: 5px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
li button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: var(--button-text);
|
||||
background: var(--background-button)!important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import EditComponentProps from "./EditComponentProps.svelte";
|
||||
import ComponentPanel from "./ComponentPanel.svelte";
|
||||
import ComponentsList from "./ComponentsList.svelte";
|
||||
|
||||
let selected="properties";
|
||||
|
@ -33,7 +33,7 @@ const selectTab = tab =>
|
|||
|
||||
<div class="panel">
|
||||
{#if selected==="properties"}
|
||||
<EditComponentProps />
|
||||
<ComponentPanel />
|
||||
{/if}
|
||||
|
||||
{#if selected==="components"}
|
||||
|
@ -50,24 +50,29 @@ const selectTab = tab =>
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2rem 1.5rem 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.switcher {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.switcher > button {
|
||||
display: inline-block;
|
||||
background-color: rgba(0,0,0,0);
|
||||
border-style: solid;
|
||||
border-color: var(--slate);
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.switcher > .selected {
|
||||
background-color: red;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.panel {
|
||||
|
|
|
@ -52,7 +52,8 @@ store.subscribe(s => {
|
|||
window["##BUDIBASE_APPDEFINITION##"] = ${JSON.stringify(appDefinition)};
|
||||
import('/_builder/budibase-client.esm.mjs')
|
||||
.then(module => {
|
||||
module.loadBudibase();
|
||||
console.log(module, window);
|
||||
module.loadBudibase({ window, localStorage });
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from "./pagesParsing/createProps";
|
||||
import Button from "../common/Button.svelte";
|
||||
import ButtonGroup from "../common/ButtonGroup.svelte";
|
||||
import { LayoutIcon, PaintIcon, TerminalIcon } from '../common/Icons/';
|
||||
|
||||
import {
|
||||
cloneDeep,
|
||||
|
@ -50,43 +51,6 @@ store.subscribe(s => {
|
|||
components = s.components;
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
|
||||
ignoreStore = true;
|
||||
if(!validate()) {
|
||||
ignoreStore = false;
|
||||
return;
|
||||
}
|
||||
|
||||
component.name = originalName || name;
|
||||
component.description = description;
|
||||
component.tags = pipe(tagsString, [
|
||||
split(","),
|
||||
map(s => s.trim())
|
||||
]);
|
||||
|
||||
store.saveScreen(component);
|
||||
|
||||
ignoreStore = false;
|
||||
// now do the rename
|
||||
if(name !== originalName) {
|
||||
store.renameScreen(originalName, name);
|
||||
}
|
||||
}
|
||||
|
||||
const deleteComponent = () => {
|
||||
showDialog();
|
||||
}
|
||||
|
||||
const confirmDeleteComponent = () => {
|
||||
store.deleteScreen(component.name);
|
||||
hideDialog();
|
||||
}
|
||||
|
||||
const onPropsValidate = result => {
|
||||
propsValidationErrors = result;
|
||||
}
|
||||
|
||||
const updateComponent = doChange => {
|
||||
const newComponent = cloneDeep(component);
|
||||
doChange(newComponent);
|
||||
|
@ -97,51 +61,22 @@ const updateComponent = doChange => {
|
|||
const onPropsChanged = newProps => {
|
||||
updateComponent(newComponent =>
|
||||
assign(newComponent.props, newProps));
|
||||
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
const fieldInvalid = (field, err) =>
|
||||
errors[field] = err;
|
||||
const fieldValid = field =>
|
||||
errors[field] && delete errors[field];
|
||||
|
||||
if(!name) nameInvalid = "component name i not supplied";
|
||||
else nameInvalid = "";
|
||||
|
||||
return (!nameInvalid && propsValidationErrors.length === 0);
|
||||
}
|
||||
|
||||
const hideDialog = () => {
|
||||
UIkit.modal(modalElement).hide();
|
||||
}
|
||||
|
||||
const showDialog = () => {
|
||||
UIkit.modal(modalElement).show();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
||||
<div class="title">
|
||||
<div>{shortName}</div>
|
||||
<div>
|
||||
<IconButton icon="save"
|
||||
on:click={save}
|
||||
color="var(--secondary100)"
|
||||
hoverColor="var(--primary100)"/>
|
||||
<IconButton icon="trash"
|
||||
on:click={deleteComponent}
|
||||
color="var(--secondary100)"
|
||||
hoverColor="var(--primary100)"/>
|
||||
</div>
|
||||
</div>
|
||||
<ul>
|
||||
<li><button><PaintIcon /></button></li>
|
||||
<li><button><LayoutIcon /></button></li>
|
||||
<li><button><TerminalIcon /></button></li>
|
||||
</ul>
|
||||
|
||||
<div class="component-props-container">
|
||||
|
||||
|
||||
<PropsView onValidate={onPropsValidate}
|
||||
<PropsView
|
||||
{componentInfo}
|
||||
{onPropsChanged} />
|
||||
|
||||
|
@ -150,45 +85,13 @@ const showDialog = () => {
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
<div bind:this={modalElement} uk-modal>
|
||||
<div class="uk-modal-dialog">
|
||||
|
||||
<div class="uk-modal-header">
|
||||
Delete {name} ?
|
||||
</div>
|
||||
|
||||
<div class="uk-modal-body">
|
||||
Are you sure you want to delete this component ?
|
||||
</div>
|
||||
|
||||
<div class="uk-modal-footer">
|
||||
<ButtonGroup>
|
||||
<Button grouped
|
||||
on:click={confirmDeleteComponent}>
|
||||
OK
|
||||
</Button>
|
||||
<Button grouped
|
||||
on:click={hideDialog}
|
||||
color="secondary" >
|
||||
Cancel
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0 0;
|
||||
border-color: var(--slate);
|
||||
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -213,4 +116,32 @@ const showDialog = () => {
|
|||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 20px;
|
||||
background: none;
|
||||
border-radius: 5px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
li button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: lightblue;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -5,6 +5,7 @@ import {
|
|||
filter
|
||||
} from "lodash/fp";
|
||||
import {EVENT_TYPE_MEMBER_NAME} from "../common/eventHandlers";
|
||||
|
||||
export let parentProps;
|
||||
export let propDef;
|
||||
export let onValueChanged;
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<script>
|
||||
import InputGroup from '../common/Inputs/InputGroup.svelte';
|
||||
|
||||
let grid_values = ['', '', '', ''];
|
||||
let column_values = ['', ''];
|
||||
let row_values = ['', ''];
|
||||
let gap_values = [''];
|
||||
let margin_values = ['', '', '', ''];
|
||||
let padding_values = ['', '', '', ''];
|
||||
let zindex_values = [''];
|
||||
|
||||
const tbrl = [
|
||||
{ placeholder: 'T' },
|
||||
{ placeholder: 'R' },
|
||||
{ placeholder: 'B' },
|
||||
{ placeholder: 'L' }
|
||||
];
|
||||
|
||||
const se = [
|
||||
{ placeholder: 'START' },
|
||||
{ placeholder: 'END' },
|
||||
]
|
||||
|
||||
const single = [{ placeholder: '' }];
|
||||
</script>
|
||||
|
||||
|
||||
<h3>Layout</h3>
|
||||
|
||||
<h4>Positioning</h4>
|
||||
|
||||
<div class="layout-pos">
|
||||
<div class="grid">
|
||||
<h5>Grid Area:</h5>
|
||||
<InputGroup meta={tbrl} bind:values={grid_values} size="small"/>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<h5>Column:</h5>
|
||||
<InputGroup meta={se} bind:values={column_values} />
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<h5>Row:</h5>
|
||||
<InputGroup meta={se} bind:values={row_values} />
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<h5>Gap:</h5>
|
||||
<InputGroup meta={single} bind:values={gap_values} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Spacing</h4>
|
||||
|
||||
<div class="layout-spacing">
|
||||
<div class="grid">
|
||||
<h5>Margin:</h5>
|
||||
<InputGroup meta={tbrl} bind:values={margin_values} size="small"/>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<h5>Padding:</h5>
|
||||
<InputGroup meta={tbrl} bind:values={padding_values} size="small"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Z-Index</h4>
|
||||
|
||||
<div class="layout-layer">
|
||||
<div class="grid">
|
||||
<h5>Z-Index:</h5>
|
||||
<InputGroup meta={single} bind:values={zindex_values}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
h3 {
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #8997ab;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: #163057;
|
||||
opacity: 0.3;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #163057;
|
||||
opacity: 0.6;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div > div {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-gap: 10px;
|
||||
height: 40px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: 70px 1fr;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -29,7 +29,6 @@ let name="";
|
|||
let saveAttempted=false;
|
||||
|
||||
store.subscribe(s => {
|
||||
|
||||
layoutComponents = pipe(s.components, [
|
||||
filter(c => c.container),
|
||||
map(c => ({name:c.name, ...splitName(c.name)}))
|
||||
|
|
|
@ -7,54 +7,70 @@ const getPage = (s, name) => {
|
|||
return ({name, props});
|
||||
}
|
||||
|
||||
const pages = [{
|
||||
title: 'Main',
|
||||
id: 'main'
|
||||
}, {
|
||||
title: 'Login',
|
||||
id: 'unauthenticated'
|
||||
}]
|
||||
|
||||
store.setCurrentPage('main')
|
||||
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="hierarchy-item component" class:selected={$store.currentFrontEndType === "page" && $store.currentPageName === "main"}
|
||||
on:click|stopPropagation={() => store.setCurrentPage("main")}>
|
||||
<span>{@html getIcon("circle", "7")}</span>
|
||||
<span class="title">Main</span>
|
||||
</div>
|
||||
<select id="page" name="select" on:change={({target}) => store.setCurrentPage(target.value)}>
|
||||
|
||||
<div class="hierarchy-item component" class:selected={$store.currentFrontEndType === "page" && $store.currentPageName === "unauthenticated"}
|
||||
on:click|stopPropagation={() => store.setCurrentPage("unauthenticated")}>
|
||||
<span>{@html getIcon("circle", "7")}</span>
|
||||
<span class="title">Login</span>
|
||||
</div>
|
||||
{#each pages as {title, id}}
|
||||
<option value="{id}">Page: {title}</option>
|
||||
{/each}
|
||||
|
||||
</select>
|
||||
<span class="arrow">{@html getIcon("chevron-down","24")}</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
.root {
|
||||
padding-bottom: 10px;
|
||||
padding-left: 10px;
|
||||
font-size: .9rem;
|
||||
color: var(--secondary50);
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hierarchy-item {
|
||||
cursor: pointer;
|
||||
padding: 5px 0px;
|
||||
|
||||
select {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
font-weight: 700;
|
||||
color: #444;
|
||||
line-height: 1.3;
|
||||
padding: 1em 2.6em 0.9em 1.4em;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-radius: .5em;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.hierarchy-item:hover {
|
||||
color: var(--secondary100);
|
||||
}
|
||||
|
||||
.component {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
.arrow {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
pointer-events: none;
|
||||
color: var(--primary100);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -6,13 +6,12 @@ import Dropdown from "../common/Dropdown.svelte";
|
|||
import EventListSelector from "./EventListSelector.svelte";
|
||||
import StateBindingControl from "./StateBindingControl.svelte";
|
||||
|
||||
export let errors = [];
|
||||
export let setProp = () => {};
|
||||
export let fieldHasError =() => {};
|
||||
export let propDef = {};
|
||||
export let props = {};
|
||||
export let disabled;
|
||||
export let index;
|
||||
export let prop_name;
|
||||
export let prop_value;
|
||||
export let prop_type = {};
|
||||
|
||||
$: isOdd = (index % 2 !== 0);
|
||||
|
||||
|
@ -25,20 +24,20 @@ const setComponentProp = (props) => {
|
|||
|
||||
<div class="root" >
|
||||
|
||||
{#if propDef.type === "event"}
|
||||
{#if prop_type === "event"}
|
||||
|
||||
<div class="prop-label">{propDef.____name}</div>
|
||||
<!-- <h5>{prop_name}</h5>
|
||||
<EventListSelector parentProps={props}
|
||||
{propDef}
|
||||
onValueChanged={setComponentProp} />
|
||||
onValueChanged={setComponentProp} /> -->
|
||||
|
||||
{:else }
|
||||
|
||||
<div class="prop-label">{propDef.____name}</div>
|
||||
<StateBindingControl value={props[propDef.____name]}
|
||||
type={propDef.type}
|
||||
options={propDef.options}
|
||||
onChanged={v => setProp(propDef.____name, v)}/>
|
||||
<h5>{prop_name}</h5>
|
||||
<StateBindingControl value={prop_value}
|
||||
type={prop_type}
|
||||
options={prop_type.options}
|
||||
onChanged={v => setProp(prop_name, v)}/>
|
||||
|
||||
{/if}
|
||||
|
||||
|
@ -47,13 +46,21 @@ const setComponentProp = (props) => {
|
|||
<style>
|
||||
|
||||
.root {
|
||||
padding: 1rem 1rem 0rem 1rem;
|
||||
height: 40px;
|
||||
margin-bottom: 15px;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-columns: 70px 1fr;
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
||||
.prop-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--secondary100);
|
||||
font-weight: bold;
|
||||
}
|
||||
h5 {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #163057;
|
||||
opacity: 0.6;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -10,67 +10,30 @@ import { getInstanceProps } from "./pagesParsing/createProps";
|
|||
import Checkbox from "../common/Checkbox.svelte";
|
||||
import Textbox from "../common/Textbox.svelte";
|
||||
import Dropdown from "../common/Dropdown.svelte";
|
||||
import { validateProps } from "./pagesParsing/validateProps";
|
||||
import PropControl from "./PropControl.svelte";
|
||||
import IconButton from "../common/IconButton.svelte";
|
||||
|
||||
export let shouldValidate = true;
|
||||
export let onValidate = () => {};
|
||||
export let componentInfo;
|
||||
export let instanceProps = null;
|
||||
export let onPropsChanged = () => {};
|
||||
export let components;
|
||||
|
||||
let errors = [];
|
||||
let props = {};
|
||||
let propsDefinitions = [];
|
||||
let isInstance = false;
|
||||
|
||||
const props_to_ignore = ['_component','_children', '_layout'];
|
||||
|
||||
$: {
|
||||
if(componentInfo)
|
||||
{
|
||||
isInstance = !!instanceProps;
|
||||
props = isInstance
|
||||
? getInstanceProps(componentInfo, instanceProps)
|
||||
: cloneDeep(componentInfo.fullProps);
|
||||
$: propDefs = componentInfo && Object.entries(componentInfo).filter(([name])=> !props_to_ignore.includes(name));
|
||||
|
||||
propsDefinitions = pipe(componentInfo.propsDefinition, [
|
||||
keys,
|
||||
map(k => ({...componentInfo.propsDefinition[k], ____name:k})),
|
||||
sortBy("____name")
|
||||
]);
|
||||
}
|
||||
function find_type(prop_name) {
|
||||
if(!componentInfo._component) return;
|
||||
return components.find(({name}) => name === componentInfo._component).props[prop_name];
|
||||
}
|
||||
|
||||
|
||||
let setProp = (name, value) => {
|
||||
const newProps = cloneDeep(props);
|
||||
|
||||
let finalProps = isInstance ? newProps : cloneDeep(componentInfo.component.props);
|
||||
|
||||
if(!isInstance) {
|
||||
const nowSet = [];
|
||||
for(let p of componentInfo.unsetProps) {
|
||||
if(!isEqual(newProps[p])(componentInfo.rootDefaultProps[p])) {
|
||||
finalProps[p] = newProps[p];
|
||||
nowSet.push(p);
|
||||
}
|
||||
}
|
||||
componentInfo.unsetProps = difference(nowSet)(componentInfo.unsetProps);
|
||||
}
|
||||
|
||||
newProps[name] = value;
|
||||
finalProps[name] = value;
|
||||
props = newProps;
|
||||
if(validate(finalProps))
|
||||
onPropsChanged(finalProps);
|
||||
|
||||
}
|
||||
|
||||
const validate = (finalProps) => {
|
||||
errors = validateProps(componentInfo.rootComponent, finalProps, [], false);
|
||||
onValidate(errors);
|
||||
return errors.length === 0;
|
||||
onPropsChanged(name, value);
|
||||
}
|
||||
|
||||
const fieldHasError = (propName) =>
|
||||
|
@ -81,14 +44,14 @@ const fieldHasError = (propName) =>
|
|||
<div class="root">
|
||||
|
||||
<form class="uk-form-stacked form-root">
|
||||
{#each propsDefinitions as propDef, index}
|
||||
{#each propDefs as [prop_name, prop_value], index}
|
||||
|
||||
<div class="prop-container">
|
||||
|
||||
<PropControl {setProp}
|
||||
{fieldHasError}
|
||||
{propDef}
|
||||
{props}
|
||||
{prop_name}
|
||||
{prop_value}
|
||||
prop_type={find_type(prop_name)}
|
||||
{index}
|
||||
disabled={false} />
|
||||
|
||||
|
@ -98,9 +61,6 @@ const fieldHasError = (propName) =>
|
|||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
@ -66,10 +66,10 @@ const setBindingSource = ev => {
|
|||
bind(bindingPath, bindingFallbackValue, ev.target.value);
|
||||
}
|
||||
|
||||
const makeBinding = () => {
|
||||
forceIsBound=true;
|
||||
isExpanded=true;
|
||||
}
|
||||
// const makeBinding = () => {
|
||||
// forceIsBound=true;
|
||||
// isExpanded=true;
|
||||
// }
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -117,7 +117,7 @@ const makeBinding = () => {
|
|||
<div>
|
||||
<IconButton icon={value == true ? "check-square" : "square"}
|
||||
size="19"
|
||||
on:click={() => onChanged(!value)}/>
|
||||
on:click={() => onChanged(!value)} />
|
||||
</div>
|
||||
|
||||
{:else if type === "options"}
|
||||
|
@ -132,49 +132,46 @@ const makeBinding = () => {
|
|||
|
||||
{:else}
|
||||
|
||||
<input class="uk-input uk-form-small"
|
||||
on:change={ev => onChanged(ev.target.value)}
|
||||
<input on:change={ev => onChanged(ev.target.value)}
|
||||
bind:value={value}
|
||||
style="flex: 1 0 auto;" >
|
||||
style="flex: 1 0 auto;" />
|
||||
|
||||
|
||||
{/if}
|
||||
<IconButton icon="link"
|
||||
size="12"
|
||||
on:click={makeBinding} />
|
||||
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
.unbound-container {
|
||||
.unbound-container {
|
||||
display:flex;
|
||||
margin: .5rem 0rem .5rem 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
.unbound-container > *:nth-child(1) {
|
||||
width:auto;
|
||||
flex: 1 0 auto;
|
||||
font-size: 0.8rem;
|
||||
color: var(--secondary100);
|
||||
border-radius: .2rem;
|
||||
}
|
||||
|
||||
.bound-header {
|
||||
.bound-header {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.bound-header > div:nth-child(1) {
|
||||
.bound-header > div:nth-child(1) {
|
||||
flex: 1 0 auto;
|
||||
width: 30px;
|
||||
color: var(--secondary50);
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.binding-prop-label {
|
||||
.binding-prop-label {
|
||||
color: var(--secondary50);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #163057;
|
||||
opacity: 0.7;
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #DBDBDB;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
|
@ -29,16 +29,27 @@ const settings = () => {
|
|||
|
||||
<div class="ui-nav">
|
||||
|
||||
<div class="pages-list-container">
|
||||
<div class="nav-group-header">
|
||||
|
||||
<span class="navigator-title">Navigator</span>
|
||||
</div>
|
||||
<div class="nav-items-container">
|
||||
<PagesList />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="components-list-container">
|
||||
<div class="nav-group-header">
|
||||
<div>{@html getIcon("sidebar","18")}</div>
|
||||
|
||||
<span class="components-nav-header">Screens</span>
|
||||
<div>
|
||||
<IconButton icon="settings"
|
||||
<!-- <IconButton icon="settings"
|
||||
size="14px"
|
||||
on:click={settings}/>
|
||||
<IconButton icon="plus"
|
||||
on:click={newComponent}/>
|
||||
on:click={settings}/> -->
|
||||
<!-- <IconButton icon="plus"
|
||||
on:click={newComponent}/> -->
|
||||
<button on:click={newComponent}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-items-container">
|
||||
|
@ -46,16 +57,6 @@ const settings = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pages-list-container">
|
||||
<div class="nav-group-header">
|
||||
<div>{@html getIcon("grid","18")}</div>
|
||||
<span>Pages</span>
|
||||
</div>
|
||||
<div class="nav-items-container">
|
||||
<PagesList />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="preview-pane">
|
||||
|
@ -80,22 +81,47 @@ const settings = () => {
|
|||
|
||||
|
||||
<style>
|
||||
button {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: var(--background-button);
|
||||
|
||||
width: 1.8rem;
|
||||
height: 1.8rem;
|
||||
padding-bottom: 10px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--button-text);
|
||||
}
|
||||
|
||||
.root {
|
||||
display: grid;
|
||||
grid-template-columns: 250px 1fr 300px;
|
||||
grid-template-columns: 290px 1fr 300px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.ui-nav {
|
||||
grid-column: 1;
|
||||
background-color: var(--secondary5);
|
||||
height: 100%;
|
||||
padding: 0 1.5rem 0rem 1.5rem
|
||||
}
|
||||
|
||||
.preview-pane {
|
||||
grid-column: 2;
|
||||
margin: 80px 60px;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0px 6px rgba(0,0,0,0.05)
|
||||
}
|
||||
|
||||
.components-pane {
|
||||
|
@ -105,12 +131,10 @@ const settings = () => {
|
|||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.pages-list-container {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.components-nav-header {
|
||||
font-size: .9rem;
|
||||
font-size: 0.75rem;
|
||||
color: #999;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nav-group-header {
|
||||
|
@ -119,15 +143,16 @@ const settings = () => {
|
|||
}
|
||||
|
||||
.nav-items-container {
|
||||
padding: 1rem 1rem 0rem 1rem;
|
||||
padding: 1rem 0rem 0rem 0rem;
|
||||
}
|
||||
|
||||
.nav-group-header {
|
||||
display:grid;
|
||||
grid-template-columns: [icon] auto [title] 1fr [button] auto;
|
||||
padding: 2rem 1rem 0rem 1rem;
|
||||
display: flex;
|
||||
padding: 2rem 0 0 0;
|
||||
font-size: .9rem;
|
||||
font-weight: bold;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-group-header>div:nth-child(1) {
|
||||
|
@ -155,4 +180,10 @@ const settings = () => {
|
|||
color: var(--primary75);
|
||||
}
|
||||
|
||||
.navigator-title {
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
isString, isUndefined, find, keys, uniq,
|
||||
some, filter, reduce, cloneDeep, includes,last
|
||||
some, filter, reduce, cloneDeep, includes, last
|
||||
} from "lodash/fp";
|
||||
import { types, expandComponentDefinition } from "./types";
|
||||
import { assign } from "lodash";
|
||||
|
@ -11,7 +11,7 @@ import { ensureShardNameIsInShardMap } from "../../../../core/src/indexing/shard
|
|||
export const getInstanceProps = (componentInfo, props) => {
|
||||
const finalProps = cloneDeep(componentInfo.fullProps);
|
||||
|
||||
for(let p in props) {
|
||||
for (let p in props) {
|
||||
finalProps[p] = props[p];
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,8 @@ export const getInstanceProps = (componentInfo, props) => {
|
|||
export const getNewComponentInfo = (components, rootComponent, name) => {
|
||||
const component = {
|
||||
name: name || "",
|
||||
description:"",
|
||||
props:{
|
||||
description: "",
|
||||
props: {
|
||||
_component: rootComponent
|
||||
}
|
||||
};
|
||||
|
@ -44,11 +44,11 @@ export const getComponentInfo = (components, comp) => {
|
|||
: comp;
|
||||
let component;
|
||||
let subComponent;
|
||||
if(isRootComponent(targetComponent)) {
|
||||
if (isRootComponent(targetComponent)) {
|
||||
component = targetComponent;
|
||||
} else {
|
||||
subComponent = targetComponent;
|
||||
component = find(c => c.name === subComponent.props._component)(
|
||||
component = find(c => c.name === (subComponent.props ? subComponent.props._component : subComponent._component))(
|
||||
components);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ export const getComponentInfo = (components, comp) => {
|
|||
fullProps._component = targetComponent.name;
|
||||
|
||||
return ({
|
||||
propsDefinition:expandComponentDefinition(component),
|
||||
propsDefinition: expandComponentDefinition(component),
|
||||
rootDefaultProps: rootProps.props,
|
||||
unsetProps,
|
||||
fullProps: fullProps,
|
||||
|
@ -78,7 +78,7 @@ export const getComponentInfo = (components, comp) => {
|
|||
export const createProps = (componentDefinition, derivedFromProps) => {
|
||||
|
||||
const error = (propName, error) =>
|
||||
errors.push({propName, error});
|
||||
errors.push({ propName, error });
|
||||
|
||||
const props = {
|
||||
_component: componentDefinition.name
|
||||
|
@ -86,23 +86,23 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
|||
|
||||
const errors = [];
|
||||
|
||||
if(!componentDefinition.name)
|
||||
if (!componentDefinition.name)
|
||||
error("_component", "Component name not supplied");
|
||||
|
||||
const propsDef = componentDefinition.props;
|
||||
for(let propDef in propsDef) {
|
||||
for (let propDef in propsDef) {
|
||||
const parsedPropDef = parsePropDef(propsDef[propDef]);
|
||||
if(parsedPropDef.error)
|
||||
if (parsedPropDef.error)
|
||||
error(propDef, parsedPropDef.error);
|
||||
else
|
||||
props[propDef] = parsedPropDef;
|
||||
}
|
||||
|
||||
if(derivedFromProps) {
|
||||
if (derivedFromProps) {
|
||||
assign(props, derivedFromProps);
|
||||
}
|
||||
|
||||
if(componentDefinition.children !== false
|
||||
if (componentDefinition.children !== false
|
||||
&& isUndefined(props._children)) {
|
||||
props._children = [];
|
||||
}
|
||||
|
@ -114,26 +114,26 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
|||
|
||||
|
||||
const parsePropDef = propDef => {
|
||||
const error = message => ({error:message, propDef});
|
||||
const error = message => ({ error: message, propDef });
|
||||
|
||||
if(isString(propDef)) {
|
||||
if(!types[propDef])
|
||||
if (isString(propDef)) {
|
||||
if (!types[propDef])
|
||||
return error(`Do not recognise type ${propDef}`);
|
||||
|
||||
return types[propDef].default();
|
||||
}
|
||||
|
||||
if(!propDef.type)
|
||||
if (!propDef.type)
|
||||
return error("Property Definition must declare a type");
|
||||
|
||||
const type = types[propDef.type];
|
||||
if(!type)
|
||||
if (!type)
|
||||
return error(`Do not recognise type ${propDef.type}`);
|
||||
|
||||
if(isUndefined(propDef.default))
|
||||
if (isUndefined(propDef.default))
|
||||
return type.default(propDef);
|
||||
|
||||
if(!type.isOfType(propDef.default))
|
||||
if (!type.isOfType(propDef.default))
|
||||
return error(`${propDef.default} is not of type ${type}`);
|
||||
|
||||
return propDef.default;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { recursivelyValidate } from "./validateProps";
|
||||
import {
|
||||
isString,
|
||||
keys,
|
||||
|
@ -15,11 +14,11 @@ export const validatePage = (page, getComponent) => {
|
|||
const error = message => errors.push(message);
|
||||
|
||||
const noIndex = !page.index;
|
||||
if(noIndex) {
|
||||
if (noIndex) {
|
||||
error("Page does not define an index member");
|
||||
}
|
||||
|
||||
if(!page.appBody
|
||||
if (!page.appBody
|
||||
|| !isString(page.appBody)
|
||||
|| !page.appBody.endsWith(".json")) {
|
||||
error("App body must be set toa valid JSON file");
|
||||
|
@ -44,15 +43,15 @@ export const validatePages = (pages, getComponent) => {
|
|||
let errors = [];
|
||||
const error = message => errors.push(message);
|
||||
|
||||
if(!pages.main) {
|
||||
if (!pages.main) {
|
||||
error("must have a 'main' page");
|
||||
}
|
||||
|
||||
if(!pages.unauthenticated) {
|
||||
if (!pages.unauthenticated) {
|
||||
error("must have a 'unauthenticated' (login) page");
|
||||
}
|
||||
|
||||
if(!pages.componentLibraries
|
||||
if (!pages.componentLibraries
|
||||
|| !isArray(pages.componentLibraries)
|
||||
|| pages.componentLibraries.length === 0) {
|
||||
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
import { types } from "./types";
|
||||
import {
|
||||
createProps, arrayElementComponentName
|
||||
} from "./createProps";
|
||||
import { isString } from "util";
|
||||
import {
|
||||
includes, filter, map, keys,
|
||||
flatten, flattenDeep, each,
|
||||
indexOf, isUndefined
|
||||
} from "lodash/fp";
|
||||
import { common } from "../../../../core/src";
|
||||
import {
|
||||
isBinding
|
||||
} from "../../common/binding";
|
||||
|
||||
const pipe = common.$;
|
||||
|
||||
const makeError = (errors, propName, stack) => (message) =>
|
||||
errors.push({
|
||||
stack,
|
||||
propName,
|
||||
error:message});
|
||||
|
||||
export const recursivelyValidate = (rootProps, getComponent, stack=[]) => {
|
||||
|
||||
if(!rootProps._component) {
|
||||
const errs = [];
|
||||
makeError(errs, "_component", stack)("Component is not set");
|
||||
return errs;
|
||||
// this would break everything else anyway
|
||||
}
|
||||
|
||||
const componentDef = getComponent(
|
||||
rootProps._component);
|
||||
|
||||
|
||||
const errors = validateProps(
|
||||
componentDef,
|
||||
rootProps,
|
||||
stack,
|
||||
true);
|
||||
|
||||
const validateChildren = (_props, _stack) =>
|
||||
!_props._children
|
||||
? []
|
||||
: pipe(_props._children, [
|
||||
map(child => recursivelyValidate(
|
||||
child,
|
||||
getComponent,
|
||||
[..._stack, _props._children.indexOf(child)]))
|
||||
]);
|
||||
|
||||
const childErrors = validateChildren(
|
||||
rootProps, stack);
|
||||
|
||||
return flattenDeep([errors, ...childErrors]);
|
||||
}
|
||||
|
||||
const expandPropDef = propDef =>
|
||||
isString(propDef)
|
||||
? types[propDef].defaultDefinition()
|
||||
: propDef;
|
||||
|
||||
|
||||
|
||||
export const validateProps = (componentDefinition, props, stack=[], isFinal=true) => {
|
||||
|
||||
const errors = [];
|
||||
|
||||
if(isFinal && !props._component) {
|
||||
makeError(errors, "_component", stack)("Component is not set");
|
||||
return errors;
|
||||
// this would break everything else anyway
|
||||
}
|
||||
|
||||
const propsDefinition = componentDefinition.props;
|
||||
|
||||
for(let propDefName in props) {
|
||||
|
||||
if(propDefName === "_component") continue;
|
||||
if(propDefName === "_children") continue;
|
||||
if(propDefName === "_layout") continue;
|
||||
|
||||
const propDef = expandPropDef(propsDefinition[propDefName]);
|
||||
|
||||
const type = types[propDef.type];
|
||||
|
||||
const error = makeError(errors, propDefName, stack);
|
||||
|
||||
const propValue = props[propDefName];
|
||||
|
||||
// component declarations dont need to define al props.
|
||||
if(!isFinal && isUndefined(propValue)) continue;
|
||||
|
||||
if(isFinal && propDef.required && propValue) {
|
||||
error(`Property ${propDefName} is required`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isBinding(propValue)) {
|
||||
if(propDef.type === "event") {
|
||||
error(`Cannot apply binding to type ${propDef.type}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(!type.isOfType(propValue)) {
|
||||
error(`Property ${propDefName} is not of type ${propDef.type}. Actual value ${propValue}`)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(propDef.type === "options"
|
||||
&& propValue
|
||||
&& !isBinding(propValue)
|
||||
&& !includes(propValue)(propDef.options)) {
|
||||
error(`Property ${propDefName} is not one of allowed options. Acutal value is ${propValue}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export const validateComponentDefinition = (componentDefinition) => {
|
||||
const { errors } = createProps(componentDefinition);
|
||||
|
||||
const propDefinitions = expandPropDef(componentDefinition.props);
|
||||
|
||||
pipe(propDefinitions, [
|
||||
keys,
|
||||
map(k => ({
|
||||
propDef:propDefinitions[k],
|
||||
propName:k
|
||||
})),
|
||||
filter(d => d.propDef.type === "options"
|
||||
&& (!d.propDef.options || d.propDef.options.length === 0)),
|
||||
each(d => makeError(errors, d.propName)(`${d.propName} does not have any options`))
|
||||
]);
|
||||
|
||||
return errors;
|
||||
|
||||
}
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
import {
|
||||
validateComponentDefinition,
|
||||
validateProps,
|
||||
recursivelyValidate
|
||||
} from "../src/userInterface/pagesParsing/validateProps";
|
||||
import { createProps } from "../src/userInterface/pagesParsing/createProps";
|
||||
import {
|
||||
setBinding
|
||||
} from "../src/common/binding";
|
||||
|
||||
// not that allot of this functionality is covered
|
||||
// in createDefaultProps - as validate props uses that.
|
||||
|
||||
describe("validateComponentDefinition", () => {
|
||||
|
||||
|
||||
it("should return error when no options for options field", () => {
|
||||
|
||||
const compDef = {
|
||||
name:"some_component",
|
||||
props: {
|
||||
size: {
|
||||
type: "options",
|
||||
options: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const errors = validateComponentDefinition(compDef);
|
||||
|
||||
expect(errors.length).toEqual(1);
|
||||
expect(errors[0].propName).toBe("size");
|
||||
|
||||
});
|
||||
|
||||
it("should not return error when options field has options", () => {
|
||||
|
||||
const compDef = {
|
||||
name: "some_component",
|
||||
props: {
|
||||
size: {
|
||||
type: "options",
|
||||
options: ["small", "medium", "large"]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const errors = validateComponentDefinition(compDef);
|
||||
|
||||
expect(errors).toEqual([]);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
const validComponentDef = {
|
||||
name: "some_component",
|
||||
props: {
|
||||
size: {
|
||||
type: "options",
|
||||
options: ["small", "medium", "large"],
|
||||
default:"medium"
|
||||
},
|
||||
rowCount : "number"
|
||||
}
|
||||
};
|
||||
|
||||
const childComponentDef = {
|
||||
name: "child_component",
|
||||
props: {
|
||||
width: "number",
|
||||
units: {
|
||||
type: "string",
|
||||
default: "px"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const validProps = () => {
|
||||
|
||||
const { props } = createProps(validComponentDef);
|
||||
props._children.push(
|
||||
createProps(childComponentDef));
|
||||
return props;
|
||||
}
|
||||
|
||||
describe("validateProps", () => {
|
||||
|
||||
it("should have no errors with a big list of valid props", () => {
|
||||
|
||||
const errors = validateProps(validComponentDef, validProps(), [], true);
|
||||
expect(errors).toEqual([]);
|
||||
|
||||
});
|
||||
|
||||
it("should return error with invalid value", () => {
|
||||
|
||||
const props = validProps();
|
||||
props.rowCount = "1";
|
||||
const errors = validateProps(validComponentDef, props, [], true);
|
||||
expect(errors.length).toEqual(1);
|
||||
expect(errors[0].propName).toBe("rowCount");
|
||||
|
||||
});
|
||||
|
||||
it("should return error with invalid option", () => {
|
||||
|
||||
const props = validProps();
|
||||
props.size = "really_small";
|
||||
const errors = validateProps(validComponentDef, props, [], true);
|
||||
expect(errors.length).toEqual(1);
|
||||
expect(errors[0].propName).toBe("size");
|
||||
|
||||
});
|
||||
|
||||
it("should not return error when has binding", () => {
|
||||
const props = validProps();
|
||||
props._children[0].width = setBinding({path:"some_path"});
|
||||
props.size = setBinding({path:"other path", fallback:"small"});
|
||||
const errors = validateProps(validComponentDef, props, [], true);
|
||||
expect(errors.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("recursivelyValidateProps", () => {
|
||||
|
||||
const rootComponent = {
|
||||
name: "rootComponent",
|
||||
children: true,
|
||||
props: {
|
||||
width: "number"
|
||||
}
|
||||
};
|
||||
|
||||
const todoListComponent = {
|
||||
name: "todoListComponent",
|
||||
props:{
|
||||
showTitle: "bool"
|
||||
}
|
||||
};
|
||||
|
||||
const headerComponent = {
|
||||
name: "headerComponent",
|
||||
props: {
|
||||
text: "string"
|
||||
}
|
||||
};
|
||||
|
||||
const iconComponent = {
|
||||
name: "iconComponent",
|
||||
props: {
|
||||
iconName: "string"
|
||||
}
|
||||
};
|
||||
|
||||
const navItemComponent = {
|
||||
name: "navItemComponent",
|
||||
props: {
|
||||
text: "string"
|
||||
}
|
||||
};
|
||||
|
||||
const getComponent = name => ({
|
||||
rootComponent,
|
||||
todoListComponent,
|
||||
headerComponent,
|
||||
iconComponent,
|
||||
navItemComponent
|
||||
})[name];
|
||||
|
||||
const rootProps = () => ({
|
||||
_component: "rootComponent",
|
||||
width: 100,
|
||||
_children: [{
|
||||
_component: "todoListComponent",
|
||||
showTitle: true,
|
||||
_children : [
|
||||
{
|
||||
_component: "navItemComponent",
|
||||
text: "todos"
|
||||
},
|
||||
{
|
||||
_component: "headerComponent",
|
||||
text: "Your todo list"
|
||||
},
|
||||
{
|
||||
_component: "iconComponent",
|
||||
iconName: "fa fa-list"
|
||||
},
|
||||
{
|
||||
_component: "iconComponent",
|
||||
iconName:"fa fa-cog"
|
||||
}
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
it("should return no errors for valid structure", () => {
|
||||
const result = recursivelyValidate(
|
||||
rootProps(),
|
||||
getComponent);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return error on root component", () => {
|
||||
const root = rootProps();
|
||||
root.width = "yeeeoooo";
|
||||
const result = recursivelyValidate(root, getComponent);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].propName).toBe("width");
|
||||
});
|
||||
|
||||
it("should return error on first nested child component", () => {
|
||||
const root = rootProps();
|
||||
root._children[0].showTitle = "yeeeoooo";
|
||||
const result = recursivelyValidate(root, getComponent);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].stack).toEqual([0]);
|
||||
expect(result[0].propName).toBe("showTitle");
|
||||
});
|
||||
|
||||
it("should return error on second nested child component", () => {
|
||||
const root = rootProps();
|
||||
root._children[0]._children[0].text = false;
|
||||
const result = recursivelyValidate(root, getComponent);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].stack).toEqual([0,0]);
|
||||
expect(result[0].propName).toBe("text");
|
||||
});
|
||||
|
||||
});
|
|
@ -40,3 +40,4 @@ flow-typed/npm/*
|
|||
|
||||
.idea
|
||||
npm-debug.log.*
|
||||
dist
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -59,7 +59,7 @@ export default {
|
|||
external: [
|
||||
"lodash", "lodash/fp", "date-fns",
|
||||
"lunr", "safe-buffer", "shortid",
|
||||
"@nx-js/compiler-util"
|
||||
"@nx-js/compiler-util", "bcryptjs"
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
@ -3,9 +3,17 @@
|
|||
"h1": {
|
||||
"name": "H1",
|
||||
"description": "An HTML H1 tag",
|
||||
"props" : {
|
||||
"props": {
|
||||
"text": "string",
|
||||
"className":"string"
|
||||
"className": "string"
|
||||
},
|
||||
"tags": []
|
||||
},
|
||||
"button": {
|
||||
"name": "Button",
|
||||
"description": "A button",
|
||||
"props": {
|
||||
"raised": "bool"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -13,18 +13,22 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@budibase/client": "^0.0.16",
|
||||
"@material/button": "^4.0.0",
|
||||
"@nx-js/compiler-util": "^2.0.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"fs-extra": "^8.1.0",
|
||||
"lodash": "^4.17.15",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.11.0",
|
||||
"rollup-plugin-alias": "^2.2.0",
|
||||
"rollup-plugin-commonjs": "^10.0.2",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-livereload": "^1.0.1",
|
||||
"rollup-plugin-node-resolve": "^5.0.0",
|
||||
"rollup-plugin-postcss": "^2.0.5",
|
||||
"rollup-plugin-svelte": "^5.0.0",
|
||||
"rollup-plugin-terser": "^5.1.1",
|
||||
"sass": "^1.25.1-test.1",
|
||||
"shortid": "^2.2.15",
|
||||
"sirv-cli": "^0.4.4",
|
||||
"svelte": "^3.12.1"
|
||||
|
|
|
@ -1,20 +1,37 @@
|
|||
import svelte from 'rollup-plugin-svelte';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import svelte from "rollup-plugin-svelte";
|
||||
import postcss from "rollup-plugin-postcss";
|
||||
import resolve from "rollup-plugin-node-resolve";
|
||||
import path from "path";
|
||||
|
||||
const postcssOptions = () => ({
|
||||
extensions: [".scss", ".sass"],
|
||||
extract: false,
|
||||
minimize: true,
|
||||
use: [
|
||||
[
|
||||
"sass",
|
||||
{
|
||||
includePaths: ["./node_modules"]
|
||||
}
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
export default {
|
||||
input: 'src/index.js',
|
||||
input: "src/index.js",
|
||||
output: [
|
||||
{
|
||||
file: "dist/index.js",
|
||||
format: 'esm',
|
||||
name:"budibaseStandardComponents",
|
||||
format: "esm",
|
||||
name: "budibaseStandardComponents",
|
||||
sourcemap: "inline"
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
svelte({
|
||||
hydratable:true
|
||||
hydratable: true
|
||||
}),
|
||||
resolve()
|
||||
resolve(),
|
||||
postcss(postcssOptions())
|
||||
]
|
||||
};
|
||||
|
|
|
@ -1,59 +1,113 @@
|
|||
import svelte from 'rollup-plugin-svelte';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import json from 'rollup-plugin-json';
|
||||
import svelte from "rollup-plugin-svelte";
|
||||
import resolve from "rollup-plugin-node-resolve";
|
||||
import commonjs from "rollup-plugin-commonjs";
|
||||
import livereload from "rollup-plugin-livereload";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import json from "rollup-plugin-json";
|
||||
import alias from "rollup-plugin-alias";
|
||||
import path from "path";
|
||||
|
||||
const aliases = {
|
||||
resolve: [".js", ".svelte"],
|
||||
entries: [
|
||||
{ find: "@BBMD", replacement: path.resolve(__dirname, "dist/index.js") }
|
||||
]
|
||||
};
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
const lodash_fp_exports = [
|
||||
"find", "isUndefined", "split", "max",
|
||||
"last", "union", "reduce", "isObject",
|
||||
"cloneDeep", "some", "isArray", "map",
|
||||
"filter", "keys", "isFunction", "isEmpty",
|
||||
"countBy", "join", "includes", "flatten",
|
||||
"constant", "first", "intersection", "take",
|
||||
"has", "mapValues", "isString", "isBoolean",
|
||||
"isNull", "isNumber", "isObjectLike", "isDate",
|
||||
"clone", "values", "keyBy", "isNaN",
|
||||
"isInteger", "toNumber"];
|
||||
"find",
|
||||
"isUndefined",
|
||||
"split",
|
||||
"max",
|
||||
"last",
|
||||
"union",
|
||||
"reduce",
|
||||
"isObject",
|
||||
"cloneDeep",
|
||||
"some",
|
||||
"isArray",
|
||||
"map",
|
||||
"filter",
|
||||
"keys",
|
||||
"isFunction",
|
||||
"isEmpty",
|
||||
"countBy",
|
||||
"join",
|
||||
"includes",
|
||||
"flatten",
|
||||
"constant",
|
||||
"first",
|
||||
"intersection",
|
||||
"take",
|
||||
"has",
|
||||
"mapValues",
|
||||
"isString",
|
||||
"isBoolean",
|
||||
"isNull",
|
||||
"isNumber",
|
||||
"isObjectLike",
|
||||
"isDate",
|
||||
"clone",
|
||||
"values",
|
||||
"keyBy",
|
||||
"isNaN",
|
||||
"isInteger",
|
||||
"toNumber"
|
||||
];
|
||||
|
||||
const lodash_exports = [
|
||||
"flow", "head", "find","each",
|
||||
"tail", "findIndex", "startsWith",
|
||||
"dropRight", "takeRight",
|
||||
"trim", "split", "replace",
|
||||
"merge", "assign"];
|
||||
"flow",
|
||||
"head",
|
||||
"find",
|
||||
"each",
|
||||
"tail",
|
||||
"findIndex",
|
||||
"startsWith",
|
||||
"dropRight",
|
||||
"takeRight",
|
||||
"trim",
|
||||
"split",
|
||||
"replace",
|
||||
"merge",
|
||||
"assign"
|
||||
];
|
||||
|
||||
const coreExternal = [
|
||||
"lodash", "lodash/fp", "date-fns",
|
||||
"lunr", "safe-buffer", "shortid",
|
||||
"@nx-js/compiler-util", "bcryptjs"
|
||||
"lodash",
|
||||
"lodash/fp",
|
||||
"date-fns",
|
||||
"lunr",
|
||||
"safe-buffer",
|
||||
"shortid",
|
||||
"@nx-js/compiler-util",
|
||||
"bcryptjs"
|
||||
];
|
||||
|
||||
export default {
|
||||
input: 'src/Test/testMain.js',
|
||||
input: "src/Test/testMain.js",
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/bundle.js',
|
||||
format: "iife",
|
||||
name: "app",
|
||||
file: "public/bundle.js",
|
||||
globals: {
|
||||
"crypto": "crypto"
|
||||
crypto: "crypto"
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
alias(aliases),
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file — better for performance
|
||||
css: css => {
|
||||
css.write('public/bundle.css');
|
||||
css.write("public/bundle.css");
|
||||
},
|
||||
|
||||
hydratable:true
|
||||
hydratable: true
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
|
@ -64,24 +118,26 @@ export default {
|
|||
resolve({
|
||||
browser: true,
|
||||
dedupe: importee => {
|
||||
return importee === 'svelte'
|
||||
|| importee.startsWith('svelte/')
|
||||
|| coreExternal.includes(importee);
|
||||
return (
|
||||
importee === "svelte" ||
|
||||
importee.startsWith("svelte/") ||
|
||||
coreExternal.includes(importee)
|
||||
);
|
||||
},
|
||||
preferBuiltins: true
|
||||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
"lodash/fp": lodash_fp_exports,
|
||||
"lodash":lodash_exports,
|
||||
"shortid": ["generate"]
|
||||
lodash: lodash_exports,
|
||||
shortid: ["generate"]
|
||||
}
|
||||
}),
|
||||
json(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
!production && livereload("public"),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import "@material/button/mdc-button.scss";
|
||||
export let raised = false;
|
||||
|
||||
let c = raised ? "mdc-button mdc-button--raised" : "mdc-button";
|
||||
</script>
|
||||
|
||||
<button class={c}>
|
||||
<div class="mdc-button__ripple" />
|
||||
|
||||
<span class="mdc-button__label">Button</span>
|
||||
</button>
|
|
@ -1,40 +1,35 @@
|
|||
<script>
|
||||
import createApp from "./createApp";
|
||||
import { props } from "./props";
|
||||
import createApp from "./createApp";
|
||||
import { props } from "./props";
|
||||
|
||||
let _bb;
|
||||
let _bb;
|
||||
|
||||
const _appPromise = createApp();
|
||||
_appPromise.then(a => _bb = a);
|
||||
const _appPromise = createApp();
|
||||
_appPromise.then(a => (_bb = a));
|
||||
|
||||
const testProps = props.justAnH1;
|
||||
const testProps = props.justAnH1;
|
||||
const button = props.button;
|
||||
|
||||
let currentComponent;
|
||||
let currentComponent;
|
||||
|
||||
$: {
|
||||
if(_bb && currentComponent) {
|
||||
_bb.hydrateChildren([testProps], currentComponent);
|
||||
$: {
|
||||
if (_bb && currentComponent) {
|
||||
_bb.hydrateChildren([testProps, button], currentComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{#await _appPromise}
|
||||
loading
|
||||
{:then _bb}
|
||||
|
||||
<div id="current_component" bind:this={currentComponent}>
|
||||
</div>
|
||||
|
||||
{/await}
|
||||
|
||||
|
||||
<style>
|
||||
#current_component {
|
||||
#current_component {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{#await _appPromise}
|
||||
loading
|
||||
{:then _bb}
|
||||
|
||||
<div id="current_component" bind:this={currentComponent} />
|
||||
|
||||
{/await}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
|
||||
export const props = {
|
||||
|
||||
justAnH1 : {
|
||||
_component:"@budibase/materialdesign-components/h1",
|
||||
justAnH1: {
|
||||
_component: "@budibase/materialdesign-components/h1",
|
||||
_children: [],
|
||||
text: "This is a Header"
|
||||
},
|
||||
button: {
|
||||
_component: "@budibase/materialdesign-components/button",
|
||||
_children: [],
|
||||
raised: true
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import h1 from "../H1.svelte";
|
||||
import { button } from "@BBMD";
|
||||
|
||||
export default {h1};
|
||||
export default { h1, button };
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export {default as h1} from "./H1.svelte";
|
||||
export { default as h1 } from "./H1.svelte";
|
||||
export { default as button } from "./Button.svelte";
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
myapps/
|
||||
config.js
|
||||
<<<<<<< HEAD
|
||||
/builder/*
|
||||
!/builder/assets/
|
||||
=======
|
||||
builder/
|
||||
>>>>>>> ee5a4e8c962b29242152cbbd8065d8f3ccf65eaf
|
||||
|
|
Binary file not shown.
|
@ -3,27 +3,27 @@
|
|||
|
||||
|
||||
"@budibase/client@file:../../../client":
|
||||
version "0.0.3"
|
||||
version "0.0.16"
|
||||
dependencies:
|
||||
"@nx-js/compiler-util" "^2.0.0"
|
||||
date-fns "^1.29.0"
|
||||
bcryptjs "^2.4.3"
|
||||
lodash "^4.17.15"
|
||||
lunr "^2.3.5"
|
||||
shortid "^2.2.8"
|
||||
svelte "^3.9.2"
|
||||
|
||||
"@budibase/standard-components@file:../../../standard-components":
|
||||
version "0.0.5"
|
||||
version "0.0.16"
|
||||
|
||||
"@nx-js/compiler-util@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297"
|
||||
integrity sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ==
|
||||
|
||||
date-fns@^1.29.0:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
||||
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
||||
bcryptjs@^2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb"
|
||||
integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=
|
||||
|
||||
lodash@^4.17.15:
|
||||
version "4.17.15"
|
||||
|
|
|
@ -131,29 +131,6 @@
|
|||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@budibase/client@^0.0.16":
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.0.16.tgz#4eef9373816448e99cafd2714f417c6610af5e69"
|
||||
integrity sha512-fx4ptePj7+IehM37xdNPHdu5jEUgAFmDx23jI0yb4sI6Z5U4gxH10FZYyoJ/A9KdzmShsIfgrmudV5ffvoehdg==
|
||||
dependencies:
|
||||
"@nx-js/compiler-util" "^2.0.0"
|
||||
lodash "^4.17.15"
|
||||
lunr "^2.3.5"
|
||||
shortid "^2.2.8"
|
||||
svelte "^3.9.2"
|
||||
|
||||
"@budibase/core@^0.0.16":
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/core/-/core-0.0.16.tgz#efff16876f906b2aa59803c3312ec7593664b623"
|
||||
integrity sha512-DvzfurHHp9KkSjkvbGbKsVczR5ne38bMLRA2hHEJxAmC0Tshld06cEq7HMy2BmPb6kaC1URYHlFs/gPhW2cSFQ==
|
||||
dependencies:
|
||||
"@nx-js/compiler-util" "^2.0.0"
|
||||
date-fns "^1.29.0"
|
||||
lodash "^4.17.13"
|
||||
lunr "^2.3.5"
|
||||
safe-buffer "^5.1.2"
|
||||
shortid "^2.2.8"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
|
||||
|
@ -322,11 +299,6 @@
|
|||
path-to-regexp "^1.1.1"
|
||||
urijs "^1.19.0"
|
||||
|
||||
"@nx-js/compiler-util@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297"
|
||||
integrity sha512-AxSQbwj9zqt8DYPZ6LwZdytqnwfiOEdcFdq4l8sdjkZmU2clTht7RDLCI8xvkp7KqgcNaOGlTeCM55TULWruyQ==
|
||||
|
||||
"@types/babel__core@^7.1.0":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f"
|
||||
|
@ -1073,11 +1045,6 @@ data-urls@^1.0.0:
|
|||
whatwg-mimetype "^2.2.0"
|
||||
whatwg-url "^7.0.0"
|
||||
|
||||
date-fns@^1.29.0:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
||||
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
||||
|
||||
debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
|
@ -2785,7 +2752,7 @@ lodash.sortby@^4.7.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||
|
||||
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15:
|
||||
lodash@^4.17.11, lodash@^4.17.13:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
|
@ -2810,11 +2777,6 @@ lru-cache@^4.0.1:
|
|||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
lunr@^2.3.5:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072"
|
||||
integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
|
@ -2976,11 +2938,6 @@ nan@^2.12.1:
|
|||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
|
||||
nanoid@^2.1.0:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.8.tgz#2dbb0224231b246e3b4c819de7bfea6384dabf08"
|
||||
integrity sha512-g1z+n5s26w0TGKh7gjn7HCqurNKMZWzH08elXzh/gM/csQHd/UqDV6uxMghQYg9IvqRPm1QpeMk50YMofHvEjQ==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||
|
@ -3816,13 +3773,6 @@ shellwords@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
shortid@^2.2.8:
|
||||
version "2.2.15"
|
||||
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122"
|
||||
integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==
|
||||
dependencies:
|
||||
nanoid "^2.1.0"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
|
@ -4100,11 +4050,6 @@ supports-color@^6.1.0:
|
|||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
svelte@^3.9.2:
|
||||
version "3.16.7"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.16.7.tgz#9ade80a4bbbac95595c676dd817222f632fa2c07"
|
||||
integrity sha512-egrva1UklB1n7KAv179IhDpQzMGAvubJUlOQ9PitmmZmAfrCUEgrQnx2vPxn2s+mGV3aYegXvJ/yQ35N2SfnYQ==
|
||||
|
||||
symbol-tree@^3.2.2:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,7 +8,7 @@ export let _bb;
|
|||
let rootDiv;
|
||||
$:{
|
||||
if(_bb && rootDiv && _children && _children.length)
|
||||
_bb.hydrateChildren(_children, theButton);
|
||||
_bb.hydrateChildren(_children, rootDiv);
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,4 +16,3 @@ $:{
|
|||
|
||||
<div class="{className}" bind:this={rootDiv}>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue