mirror of https://github.com/Budibase/budibase.git
63 changed files with 1651 additions and 1124 deletions
@ -0,0 +1,59 @@ |
|||
import { makePropsSafe } from "components/userInterface/pagesParsing/createProps" |
|||
import api from "./api" |
|||
|
|||
export const selectComponent = (state, component) => { |
|||
const componentDef = component._component.startsWith("##") |
|||
? component |
|||
: state.components[component._component] |
|||
state.currentComponentInfo = makePropsSafe(componentDef, component) |
|||
state.currentView = "component" |
|||
return state |
|||
} |
|||
|
|||
export const getParent = (rootProps, child) => { |
|||
let parent |
|||
walkProps(rootProps, (p, breakWalk) => { |
|||
if ( |
|||
p._children && |
|||
(p._children.includes(child) || p._children.some(c => c._id === child)) |
|||
) { |
|||
parent = p |
|||
breakWalk() |
|||
} |
|||
}) |
|||
return parent |
|||
} |
|||
|
|||
export const saveCurrentPreviewItem = s => |
|||
s.currentFrontEndType === "page" |
|||
? savePage(s) |
|||
: saveScreenApi(s.currentPreviewItem, s) |
|||
|
|||
export const savePage = async s => { |
|||
const page = s.pages[s.currentPageName] |
|||
await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, { |
|||
page: { componentLibraries: s.pages.componentLibraries, ...page }, |
|||
uiFunctions: s.currentPageFunctions, |
|||
screens: page._screens, |
|||
}) |
|||
} |
|||
|
|||
export const saveScreenApi = (screen, s) => { |
|||
api |
|||
.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`, screen) |
|||
.then(() => savePage(s)) |
|||
} |
|||
|
|||
export const walkProps = (props, action, cancelToken = null) => { |
|||
cancelToken = cancelToken || { cancelled: false } |
|||
action(props, () => { |
|||
cancelToken.cancelled = true |
|||
}) |
|||
|
|||
if (props._children) { |
|||
for (let child of props._children) { |
|||
if (cancelToken.cancelled) return |
|||
walkProps(child, action, cancelToken) |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 292 B |
|
After Width: | Height: | Size: 271 B |
|
After Width: | Height: | Size: 419 B |
@ -0,0 +1,69 @@ |
|||
<script> |
|||
export let tabs = [] |
|||
export const selectTab = tabName => { |
|||
selected = tabName |
|||
selectedIndex = tabs.indexOf(selected) |
|||
} |
|||
|
|||
let selected = tabs.length > 0 && tabs[0] |
|||
let selectedIndex = 0 |
|||
|
|||
const isSelected = tab => selected === tab |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<div class="switcher"> |
|||
|
|||
{#each tabs as tab} |
|||
<button class:selected={selected === tab} on:click={() => selectTab(tab)}> |
|||
{tab} |
|||
</button> |
|||
{/each} |
|||
|
|||
</div> |
|||
|
|||
<div class="panel"> |
|||
{#if selectedIndex === 0} |
|||
<slot name="0" /> |
|||
{:else if selectedIndex === 1} |
|||
<slot name="1" /> |
|||
{:else if selectedIndex === 2} |
|||
<slot name="2" /> |
|||
{:else if selectedIndex === 3} |
|||
<slot name="3" /> |
|||
{/if} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 20px 20px; |
|||
border-left: solid 1px var(--grey); |
|||
} |
|||
|
|||
.switcher { |
|||
display: flex; |
|||
margin: 0px 20px 20px 0px; |
|||
} |
|||
|
|||
.switcher > button { |
|||
display: inline-block; |
|||
border: none; |
|||
margin: 0; |
|||
padding: 0; |
|||
cursor: pointer; |
|||
font-size: 18px; |
|||
font-weight: 700; |
|||
color: var(--ink-lighter); |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
.switcher > .selected { |
|||
color: var(--ink); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,68 @@ |
|||
<script> |
|||
import Button from "components/common/Button.svelte" |
|||
export let name, |
|||
description = `A minimalist CRM which removes the noise and allows you to focus |
|||
on your business.`, |
|||
_id |
|||
</script> |
|||
|
|||
<div class="apps-card"> |
|||
<h3 class="app-title">{name}</h3> |
|||
<p class="app-desc">{description}</p> |
|||
<div class="card-footer"> |
|||
<div class="modified-date">Last Edited - 25th May 2020</div> |
|||
<a href={`/_builder/${_id}`} class="app-button">Open Web App</a> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.apps-card { |
|||
background-color: var(--white); |
|||
padding: 20px; |
|||
max-width: 400px; |
|||
max-height: 150px; |
|||
border-radius: 5px; |
|||
border: 1px solid var(--grey-medium); |
|||
} |
|||
|
|||
.app-button:hover { |
|||
background-color: var(--grey-light); |
|||
text-decoration: none; |
|||
} |
|||
|
|||
.app-title { |
|||
font-size: 18px; |
|||
font-weight: 700; |
|||
color: var(--ink); |
|||
text-transform: capitalize; |
|||
} |
|||
|
|||
.app-desc { |
|||
color: var(--ink-light); |
|||
} |
|||
|
|||
.card-footer { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: baseline; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.modified-date { |
|||
font-size: 14px; |
|||
color: var(--ink-light); |
|||
} |
|||
|
|||
.app-button { |
|||
background-color: var(--white); |
|||
color: var(--ink); |
|||
padding: 12px 20px; |
|||
border-radius: 5px; |
|||
border: 1px var(--grey) solid; |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
cursor: pointer; |
|||
transition: all 0.2s; |
|||
box-sizing: border-box; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,197 @@ |
|||
<script> |
|||
import Spinner from "components/common/Spinner.svelte" |
|||
import { Input, TextArea, Button } from "@budibase/bbui" |
|||
import { goto } from "@sveltech/routify" |
|||
import { AppsIcon, InfoIcon, CloseIcon } from "components/common/Icons/" |
|||
import { getContext } from "svelte" |
|||
import { fade } from "svelte/transition" |
|||
|
|||
const { open, close } = getContext("simple-modal") |
|||
|
|||
let name = "" |
|||
let description = "" |
|||
let loading = false |
|||
let error = {} |
|||
|
|||
const createNewApp = async () => { |
|||
if ((name.length > 100 || name.length < 1) && description.length < 1) { |
|||
error = { |
|||
name: true, |
|||
description: true, |
|||
} |
|||
} else if (description.length < 1) { |
|||
error = { |
|||
name: false, |
|||
description: true, |
|||
} |
|||
} else if (name.length > 100 || name.length < 1) { |
|||
error = { |
|||
name: true, |
|||
} |
|||
} else { |
|||
error = {} |
|||
const data = { name, description } |
|||
loading = true |
|||
try { |
|||
const response = await fetch("/api/applications", { |
|||
method: "POST", // *GET, POST, PUT, DELETE, etc. |
|||
credentials: "same-origin", // include, *same-origin, omit |
|||
headers: { |
|||
"Content-Type": "application/json", |
|||
// 'Content-Type': 'application/x-www-form-urlencoded', |
|||
}, |
|||
body: JSON.stringify(data), // body data type must match "Content-Type" header |
|||
}) |
|||
|
|||
const res = await response.json() |
|||
|
|||
$goto(`./${res._id}`) |
|||
} catch (error) { |
|||
console.error(error) |
|||
} |
|||
} |
|||
} |
|||
|
|||
let value |
|||
let onChange = () => {} |
|||
|
|||
function _onCancel() { |
|||
close() |
|||
} |
|||
|
|||
async function _onOkay() { |
|||
await createNewApp() |
|||
} |
|||
</script> |
|||
|
|||
<div class="container"> |
|||
<div class="body"> |
|||
<div class="heading"> |
|||
<span class="icon"> |
|||
<AppsIcon /> |
|||
</span> |
|||
<h3>Create new web app</h3> |
|||
</div> |
|||
<Input |
|||
name="name" |
|||
label="Name" |
|||
placeholder="Enter application name" |
|||
on:change={e => (name = e.target.value)} |
|||
on:input={e => (name = e.target.value)} /> |
|||
{#if error.name} |
|||
<span class="error">You need to enter a name for your application.</span> |
|||
{/if} |
|||
<TextArea |
|||
bind:value={description} |
|||
name="description" |
|||
label="Description" |
|||
placeholder="Describe your application" /> |
|||
{#if error.description} |
|||
<span class="error"> |
|||
Please enter a short description of your application |
|||
</span> |
|||
{/if} |
|||
</div> |
|||
<div class="footer"> |
|||
<a href="./#" class="info"> |
|||
<InfoIcon /> |
|||
How to get started |
|||
</a> |
|||
<Button outline thin on:click={_onCancel}>Cancel</Button> |
|||
<Button primary thin on:click={_onOkay}>Save</Button> |
|||
</div> |
|||
<div class="close-button" on:click={_onCancel}> |
|||
<CloseIcon /> |
|||
</div> |
|||
{#if loading} |
|||
<div in:fade class="spinner-container"> |
|||
<Spinner /> |
|||
<span class="spinner-text">Creating your app...</span> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.container { |
|||
position: relative; |
|||
} |
|||
|
|||
.close-button { |
|||
cursor: pointer; |
|||
position: absolute; |
|||
top: 20px; |
|||
right: 20px; |
|||
} |
|||
.close-button :global(svg) { |
|||
width: 24px; |
|||
height: 24px; |
|||
} |
|||
.heading { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
margin-bottom: 20px; |
|||
} |
|||
h3 { |
|||
margin: 0; |
|||
font-size: 24px; |
|||
font-weight: bold; |
|||
} |
|||
.icon { |
|||
display: grid; |
|||
border-radius: 3px; |
|||
align-content: center; |
|||
justify-content: center; |
|||
margin-right: 12px; |
|||
height: 20px; |
|||
width: 20px; |
|||
padding: 10px; |
|||
background-color: var(--blue-light); |
|||
} |
|||
.info { |
|||
color: var(--primary100); |
|||
text-decoration-color: var(--primary100); |
|||
} |
|||
.info :global(svg) { |
|||
fill: var(--primary100); |
|||
margin-right: 8px; |
|||
width: 24px; |
|||
height: 24px; |
|||
} |
|||
.body { |
|||
padding: 40px 40px 80px 40px; |
|||
display: grid; |
|||
grid-gap: 20px; |
|||
} |
|||
.footer { |
|||
display: grid; |
|||
grid-gap: 20px; |
|||
align-items: center; |
|||
grid-template-columns: 1fr auto auto; |
|||
padding: 30px 40px; |
|||
border-bottom-left-radius: 5px; |
|||
border-bottom-right-radius: 50px; |
|||
background-color: var(--grey-light); |
|||
} |
|||
.spinner-container { |
|||
background: white; |
|||
position: absolute; |
|||
border-radius: 5px; |
|||
left: 0; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
display: grid; |
|||
justify-items: center; |
|||
align-content: center; |
|||
grid-gap: 50px; |
|||
} |
|||
.spinner-text { |
|||
font-size: 2em; |
|||
} |
|||
.error { |
|||
color: var(--deletion100); |
|||
font-weight: bold; |
|||
font-size: 0.8em; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,232 @@ |
|||
<script> |
|||
import { MoreIcon } from "components/common/Icons" |
|||
import { store } from "builderStore" |
|||
import { getComponentDefinition } from "builderStore/store" |
|||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|||
import { last, cloneDeep } from "lodash/fp" |
|||
import UIkit from "uikit" |
|||
import { |
|||
selectComponent, |
|||
getParent, |
|||
walkProps, |
|||
saveCurrentPreviewItem, |
|||
} from "builderStore/storeUtils" |
|||
import { uuid } from "builderStore/uuid" |
|||
|
|||
export let component |
|||
|
|||
let confirmDeleteDialog |
|||
let dropdownEl |
|||
|
|||
$: dropdown = UIkit.dropdown(dropdownEl, { |
|||
mode: "click", |
|||
offset: 0, |
|||
pos: "bottom-right", |
|||
"delay-hide": 0, |
|||
animation: false, |
|||
}) |
|||
$: dropdown && UIkit.util.on(dropdown, "shown", () => (hidden = false)) |
|||
$: noChildrenAllowed = |
|||
!component || |
|||
getComponentDefinition($store, component._component).children === false |
|||
$: noPaste = |
|||
!$store.componentToPaste || $store.componentToPaste._id === component._id |
|||
|
|||
const lastPartOfName = c => (c ? last(c._component.split("/")) : "") |
|||
|
|||
const hideDropdown = () => { |
|||
dropdown.hide() |
|||
} |
|||
|
|||
const moveUpComponent = () => { |
|||
store.update(s => { |
|||
const parent = getParent(s.currentPreviewItem.props, component) |
|||
|
|||
if (parent) { |
|||
const currentIndex = parent._children.indexOf(component) |
|||
if (currentIndex === 0) return s |
|||
|
|||
const newChildren = parent._children.filter(c => c !== component) |
|||
newChildren.splice(currentIndex - 1, 0, component) |
|||
parent._children = newChildren |
|||
} |
|||
s.currentComponentInfo = component |
|||
saveCurrentPreviewItem(s) |
|||
|
|||
return s |
|||
}) |
|||
} |
|||
|
|||
const moveDownComponent = () => { |
|||
store.update(s => { |
|||
const parent = getParent(s.currentPreviewItem.props, component) |
|||
|
|||
if (parent) { |
|||
const currentIndex = parent._children.indexOf(component) |
|||
if (currentIndex === parent._children.length - 1) return s |
|||
|
|||
const newChildren = parent._children.filter(c => c !== component) |
|||
newChildren.splice(currentIndex + 1, 0, component) |
|||
parent._children = newChildren |
|||
} |
|||
s.currentComponentInfo = component |
|||
saveCurrentPreviewItem(s) |
|||
|
|||
return s |
|||
}) |
|||
} |
|||
|
|||
const copyComponent = () => { |
|||
store.update(s => { |
|||
const parent = getParent(s.currentPreviewItem.props, component) |
|||
const copiedComponent = cloneDeep(component) |
|||
walkProps(copiedComponent, p => { |
|||
p._id = uuid() |
|||
}) |
|||
parent._children = [...parent._children, copiedComponent] |
|||
saveCurrentPreviewItem(s) |
|||
s.currentComponentInfo = copiedComponent |
|||
return s |
|||
}) |
|||
} |
|||
|
|||
const deleteComponent = () => { |
|||
store.update(state => { |
|||
const parent = getParent(state.currentPreviewItem.props, component) |
|||
|
|||
if (parent) { |
|||
parent._children = parent._children.filter(c => c !== component) |
|||
} |
|||
|
|||
saveCurrentPreviewItem(state) |
|||
|
|||
return state |
|||
}) |
|||
} |
|||
|
|||
const generateNewIdsForComponent = c => |
|||
walkProps(c, p => { |
|||
p._id = uuid() |
|||
}) |
|||
|
|||
const storeComponentForCopy = (cut = false) => { |
|||
store.update(s => { |
|||
const copiedComponent = cloneDeep(component) |
|||
s.componentToPaste = copiedComponent |
|||
if (cut) { |
|||
const parent = getParent(s.currentPreviewItem.props, component._id) |
|||
parent._children = parent._children.filter(c => c._id !== component._id) |
|||
selectComponent(s, parent) |
|||
} |
|||
|
|||
return s |
|||
}) |
|||
} |
|||
|
|||
const pasteComponent = mode => { |
|||
store.update(s => { |
|||
if (!s.componentToPaste) return s |
|||
|
|||
const componentToPaste = cloneDeep(s.componentToPaste) |
|||
generateNewIdsForComponent(componentToPaste) |
|||
delete componentToPaste._cutId |
|||
|
|||
if (mode === "inside") { |
|||
component._children.push(componentToPaste) |
|||
return s |
|||
} |
|||
|
|||
const parent = getParent(s.currentPreviewItem.props, component) |
|||
|
|||
const targetIndex = parent._children.indexOf(component) |
|||
const index = mode === "above" ? targetIndex : targetIndex + 1 |
|||
parent._children.splice(index, 0, cloneDeep(componentToPaste)) |
|||
|
|||
saveCurrentPreviewItem(s) |
|||
selectComponent(s, componentToPaste) |
|||
|
|||
return s |
|||
}) |
|||
} |
|||
</script> |
|||
|
|||
<div class="root" on:click|stopPropagation={() => {}}> |
|||
<button> |
|||
<MoreIcon /> |
|||
</button> |
|||
<ul class="menu" bind:this={dropdownEl} on:click={hideDropdown}> |
|||
<li on:click={() => confirmDeleteDialog.show()}>Delete</li> |
|||
<li on:click={moveUpComponent}>Move up</li> |
|||
<li on:click={moveDownComponent}>Move down</li> |
|||
<li on:click={copyComponent}>Duplicate</li> |
|||
<li on:click={() => storeComponentForCopy(true)}>Cut</li> |
|||
<li on:click={() => storeComponentForCopy(false)}>Copy</li> |
|||
<hr /> |
|||
<li class:disabled={noPaste} on:click={() => pasteComponent('above')}> |
|||
Paste above |
|||
</li> |
|||
<li class:disabled={noPaste} on:click={() => pasteComponent('below')}> |
|||
Paste below |
|||
</li> |
|||
<li |
|||
class:disabled={noPaste || noChildrenAllowed} |
|||
on:click={() => pasteComponent('inside')}> |
|||
Paste inside |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<ConfirmDialog |
|||
bind:this={confirmDeleteDialog} |
|||
title="Confirm Delete" |
|||
body={`Are you sure you wish to delete this '${lastPartOfName(component)}' component?`} |
|||
okText="Delete Component" |
|||
onOk={deleteComponent} /> |
|||
|
|||
<style> |
|||
.root { |
|||
overflow: hidden; |
|||
z-index: 9; |
|||
} |
|||
|
|||
.root button { |
|||
border-style: none; |
|||
border-radius: 2px; |
|||
padding: 5px; |
|||
background: transparent; |
|||
cursor: pointer; |
|||
color: var(--button-text); |
|||
outline: none; |
|||
} |
|||
|
|||
.menu { |
|||
z-index: 100000; |
|||
overflow: visible; |
|||
padding: 10px 0; |
|||
} |
|||
|
|||
.menu li { |
|||
border-style: none; |
|||
background-color: transparent; |
|||
list-style-type: none; |
|||
padding: 4px 5px 4px 15px; |
|||
margin: 0; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.menu li:not(.disabled) { |
|||
cursor: pointer; |
|||
color: var(--ink); |
|||
} |
|||
|
|||
.menu li:not(.disabled):hover { |
|||
color: var(--button-text); |
|||
background-color: var(--grey-light); |
|||
} |
|||
|
|||
.disabled { |
|||
color: var(--grey-dark); |
|||
cursor: default; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,48 @@ |
|||
<script> |
|||
import { store, backendUiStore } from "builderStore" |
|||
import ComponentsHierarchy from "components/userInterface/ComponentsHierarchy.svelte" |
|||
import PageLayout from "components/userInterface/PageLayout.svelte" |
|||
import PagesList from "components/userInterface/PagesList.svelte" |
|||
import NewScreen from "components/userInterface/NewScreen.svelte" |
|||
|
|||
const newScreen = () => { |
|||
newScreenPicker.show() |
|||
} |
|||
|
|||
let newScreenPicker |
|||
</script> |
|||
|
|||
<PagesList /> |
|||
|
|||
<button class="newscreen" on:click={newScreen}>Create New Screen</button> |
|||
|
|||
<PageLayout layout={$store.pages[$store.currentPageName]} /> |
|||
|
|||
<div class="nav-items-container"> |
|||
<ComponentsHierarchy screens={$store.screens} /> |
|||
</div> |
|||
|
|||
<NewScreen bind:this={newScreenPicker} /> |
|||
|
|||
<style> |
|||
.newscreen { |
|||
cursor: pointer; |
|||
border: 1px solid var(--grey-dark); |
|||
border-radius: 3px; |
|||
width: 100%; |
|||
padding: 8px 16px; |
|||
margin: 12px 0px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: white; |
|||
color: var(--ink); |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
transition: all 2ms; |
|||
} |
|||
|
|||
.newscreen:hover { |
|||
background: var(--grey-light); |
|||
} |
|||
</style> |
|||
@ -1,66 +1,56 @@ |
|||
<script> |
|||
import { fly } from "svelte/transition" |
|||
export let item |
|||
</script> |
|||
|
|||
<div class="item-item" on:click> |
|||
<div class="item-item" in:fly={{ y: 100, duration: 1000 }} on:click> |
|||
<div class="item-icon"> |
|||
<i class={item.icon} /> |
|||
</div> |
|||
<div class="item-text"> |
|||
<div class="item-name">{item.name}</div> |
|||
<div class="item-description"> |
|||
<p>{item.description}</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.item-item { |
|||
display: flex; |
|||
flex-direction: row; |
|||
padding: 10px 0px 8px 10px; |
|||
align-items: center; |
|||
flex-direction: column; |
|||
cursor: pointer; |
|||
margin-bottom: 8px; |
|||
padding: 8px 0px 16px 0px; |
|||
width: 120px; |
|||
height: 80px; |
|||
justify-content: center; |
|||
align-items: center; |
|||
margin-right: 8px; |
|||
background-color: var(--grey-light); |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.item-item:hover { |
|||
background: #fbfbfb; |
|||
border-radius: 5px; |
|||
background: var(--grey); |
|||
border-radius: 3px; |
|||
transition: all 0.2s; |
|||
} |
|||
|
|||
.item-icon { |
|||
flex: 0 0 40px; |
|||
background: #f1f4fc; |
|||
height: 40px; |
|||
border-radius: 5px; |
|||
border-radius: 3px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.item-text { |
|||
display: flex; |
|||
padding-left: 16px; |
|||
padding-top: 8px; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.item-name { |
|||
font-size: 14px; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.item-description { |
|||
font-size: 12px; |
|||
color: #808192; |
|||
} |
|||
|
|||
p { |
|||
line-height: 15px; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
i { |
|||
font-size: 24px; |
|||
color: #808192; |
|||
color: var(--ink-light); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,207 @@ |
|||
<script> |
|||
import Modal from "svelte-simple-modal" |
|||
import { |
|||
SettingsIcon, |
|||
AppsIcon, |
|||
UpdatesIcon, |
|||
HostingIcon, |
|||
DocumentationIcon, |
|||
TutorialsIcon, |
|||
CommunityIcon, |
|||
ContributionIcon, |
|||
BugIcon, |
|||
EmailIcon, |
|||
TwitterIcon, |
|||
} from "components/common/Icons/" |
|||
</script> |
|||
|
|||
<Modal> |
|||
<div class="root"> |
|||
<div class="ui-nav"> |
|||
<div class="home-logo"> |
|||
<img src="/_builder/assets/bb-logo.svg" alt="Budibase icon" /> |
|||
</div> |
|||
|
|||
<div class="nav-section"> |
|||
<div class="nav-section-title">Build</div> |
|||
<div class="nav-item-home"> |
|||
<span class="nav-item-icon"> |
|||
<AppsIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Apps</div> |
|||
</div> |
|||
<div class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<SettingsIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Settings</div> |
|||
</div> |
|||
<a href="https://budibase.con/login" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<UpdatesIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Updates</div> |
|||
</a> |
|||
<a href="https://budibase.con/login" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<HostingIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Hosting</div> |
|||
</a> |
|||
</div> |
|||
|
|||
<div class="nav-section"> |
|||
<div class="nav-section-title">Learn</div> |
|||
<a href="https://docs.budibase.com/" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<DocumentationIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Documentation</div> |
|||
</a> |
|||
<a |
|||
href="https://docs.budibase.com/tutorial/quick-start" |
|||
target="_blank" |
|||
class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<TutorialsIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Tutorials</div> |
|||
</a> |
|||
<a href="https://forum.budibase.com/" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<CommunityIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Community</div> |
|||
</a> |
|||
</div> |
|||
|
|||
<div class="nav-section"> |
|||
<div class="nav-section-title">Contact</div> |
|||
<a |
|||
href="https://github.com/Budibase/budibase/blob/master/CONTRIBUTING.md" |
|||
target="_blank" |
|||
class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<ContributionIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Contribute to our product</div> |
|||
</a> |
|||
<a |
|||
href="https://github.com/Budibase/budibase/issues" |
|||
target="_blank" |
|||
class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<BugIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Report bug</div> |
|||
</a> |
|||
<a href="mailto:support@budibase.com" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<EmailIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Email</div> |
|||
</a> |
|||
<a href="https://twitter.com/budibase" target="_blank" class="nav-item"> |
|||
<span class="nav-item-icon"> |
|||
<TwitterIcon /> |
|||
</span> |
|||
<div class="nav-item-title">Twitter</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="main"> |
|||
<slot /> |
|||
</div> |
|||
</div> |
|||
</Modal> |
|||
|
|||
<style> |
|||
.root { |
|||
display: grid; |
|||
grid-template-columns: 300px 1fr; |
|||
height: 100%; |
|||
width: 100%; |
|||
background: var(--grey-light); |
|||
} |
|||
|
|||
.main { |
|||
grid-column: 2; |
|||
} |
|||
|
|||
.ui-nav { |
|||
grid-column: 1; |
|||
background-color: var(--white); |
|||
padding: 20px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
border-right: 1px solid var(--grey-medium); |
|||
} |
|||
|
|||
.home-logo { |
|||
cursor: pointer; |
|||
height: 40px; |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.home-logo img { |
|||
height: 40px; |
|||
} |
|||
|
|||
.nav-section { |
|||
margin: 20px 0px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.nav-section-title { |
|||
font-size: 20px; |
|||
color: var(--ink); |
|||
font-weight: 700; |
|||
margin-bottom: 12px; |
|||
} |
|||
|
|||
.nav-item { |
|||
cursor: pointer; |
|||
margin: 0px 0px 4px 0px; |
|||
padding: 0px 0px 0px 12px; |
|||
height: 40px; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.nav-item-home { |
|||
cursor: pointer; |
|||
margin: 0px 0px 4px 0px; |
|||
padding: 0px 0px 0px 12px; |
|||
height: 40px; |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
box-sizing: border-box; |
|||
background-color: var(--blue-light); |
|||
} |
|||
|
|||
.nav-item:hover { |
|||
background-color: var(--grey-light); |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.nav-item::selection { |
|||
background-color: var(--blue-light); |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.nav-item-title { |
|||
font-size: 14px; |
|||
color: var(--ink); |
|||
font-weight: 500; |
|||
margin-left: 12px; |
|||
} |
|||
|
|||
.nav-item-icon { |
|||
color: var(--ink-light); |
|||
} |
|||
</style> |
|||
@ -1,40 +0,0 @@ |
|||
const WORKFLOW_SCHEMA = { |
|||
properties: { |
|||
type: "workflow", |
|||
pageId: { |
|||
type: "string", |
|||
}, |
|||
screenId: { |
|||
type: "string", |
|||
}, |
|||
live: { |
|||
type: "boolean", |
|||
}, |
|||
uiTree: { |
|||
type: "object", |
|||
}, |
|||
definition: { |
|||
type: "object", |
|||
properties: { |
|||
triggers: { type: "array" }, |
|||
steps: { type: "array" }, |
|||
// next: {
|
|||
// type: "object",
|
|||
// properties: {
|
|||
// environment: { environment: "string" },
|
|||
// type: { type: "string" },
|
|||
// actionId: { type: "string" },
|
|||
// args: { type: "object" },
|
|||
// conditions: { type: "array" },
|
|||
// errorHandling: { type: "object" },
|
|||
// next: { type: "object" },
|
|||
// },
|
|||
// },
|
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
module.exports = { |
|||
WORKFLOW_SCHEMA, |
|||
} |
|||
@ -0,0 +1 @@ |
|||
dist/ |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"name": "name", |
|||
"version": "1.0.0", |
|||
"description": "", |
|||
"author": "", |
|||
"license": "ISC", |
|||
"dependencies": { |
|||
"@budibase/standard-components": "0.x", |
|||
"@budibase/materialdesign-components": "0.x" |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
{ |
|||
"title": "Test App", |
|||
"favicon": "./_shared/favicon.png", |
|||
"stylesheets": [], |
|||
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"], |
|||
"props" : { |
|||
"_component": "@budibase/standard-components/container", |
|||
"_children": [], |
|||
"_id": 0, |
|||
"type": "div", |
|||
"_styles": { |
|||
"layout": {}, |
|||
"position": {} |
|||
}, |
|||
"_code": "" |
|||
}, |
|||
"_css": "", |
|||
"uiFunctions": "" |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
{ |
|||
"title": "Test App", |
|||
"favicon": "./_shared/favicon.png", |
|||
"stylesheets": [], |
|||
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"], |
|||
"props" : { |
|||
"_component": "@budibase/standard-components/container", |
|||
"_children": [], |
|||
"_id": 1, |
|||
"type": "div", |
|||
"_styles": { |
|||
"layout": {}, |
|||
"position": {} |
|||
}, |
|||
"_code": "" |
|||
}, |
|||
"_css": "", |
|||
"uiFunctions": "" |
|||
} |
|||
@ -0,0 +1 @@ |
|||
module.exports = () => ({}) |
|||
Loading…
Reference in new issue