forked from tsai/budibase
21 changed files with 487 additions and 281 deletions
@ -0,0 +1,71 @@ |
|||||
|
<script> |
||||
|
import { onMount, beforeUpdate, afterUpdate } from "svelte" |
||||
|
|
||||
|
export let value = null |
||||
|
export let onChanged = () => {} |
||||
|
export let swatches = [] |
||||
|
|
||||
|
let picker |
||||
|
let cp = null |
||||
|
|
||||
|
function getRecentColors() { |
||||
|
let colorStore = localStorage.getItem("bb:recentColors") |
||||
|
if (!!colorStore) { |
||||
|
swatches = JSON.parse(colorStore) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function setRecentColor(color) { |
||||
|
if (swatches.length >= 15) { |
||||
|
swatches.splice(0, 1) |
||||
|
picker.removeSwatch(0) |
||||
|
} |
||||
|
if (!swatches.includes(color)) { |
||||
|
swatches = [...swatches, color] |
||||
|
picker.addSwatch(color) |
||||
|
localStorage.setItem("bb:recentColors", JSON.stringify(swatches)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function createPicker() { |
||||
|
picker = Pickr.create({ |
||||
|
el: cp, |
||||
|
theme: "nano", |
||||
|
default: value || "#000000", |
||||
|
|
||||
|
swatches, |
||||
|
closeWithKey: "Escape", |
||||
|
|
||||
|
components: { |
||||
|
preview: true, |
||||
|
opacity: true, |
||||
|
hue: true, |
||||
|
|
||||
|
interaction: { |
||||
|
hex: true, |
||||
|
rgba: true, |
||||
|
input: true, |
||||
|
save: true, |
||||
|
}, |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
afterUpdate(() => { |
||||
|
picker.setColor(value) |
||||
|
}) |
||||
|
|
||||
|
onMount(() => { |
||||
|
getRecentColors() |
||||
|
createPicker() |
||||
|
|
||||
|
picker.on("save", (colour, instance) => { |
||||
|
let color = colour.toHEXA().toString() |
||||
|
onChanged(color) |
||||
|
setRecentColor(color) |
||||
|
picker.hide() |
||||
|
}) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<div bind:this={cp} class="color-picker" /> |
||||
@ -1,233 +0,0 @@ |
|||||
<script> |
|
||||
import ComponentsHierarchy from "./ComponentsHierarchy.svelte" |
|
||||
import ComponentsHierarchyChildren from "./ComponentsHierarchyChildren.svelte" |
|
||||
import PageLayout from "./PageLayout.svelte" |
|
||||
import PagesList from "./PagesList.svelte" |
|
||||
import { store } from "builderStore" |
|
||||
import IconButton from "components/common/IconButton.svelte" |
|
||||
import NewScreen from "./NewScreen.svelte" |
|
||||
import AppPreview from "./AppPreview" |
|
||||
import PageView from "./PageView.svelte" |
|
||||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte" |
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|
||||
import { last } from "lodash/fp" |
|
||||
import { AddIcon } from "components/common/Icons" |
|
||||
|
|
||||
let newScreenPicker |
|
||||
let confirmDeleteDialog |
|
||||
let componentToDelete = "" |
|
||||
|
|
||||
const newScreen = () => { |
|
||||
newScreenPicker.show() |
|
||||
} |
|
||||
|
|
||||
const confirmDeleteComponent = component => { |
|
||||
componentToDelete = component |
|
||||
confirmDeleteDialog.show() |
|
||||
} |
|
||||
|
|
||||
const lastPartOfName = c => (c ? last(c.split("/")) : "") |
|
||||
</script> |
|
||||
|
|
||||
<div class="root"> |
|
||||
|
|
||||
<div class="ui-nav"> |
|
||||
|
|
||||
<div class="pages-list-container"> |
|
||||
<div class="nav-header"> |
|
||||
<span class="navigator-title">Navigator</span> |
|
||||
<div class="border-line" /> |
|
||||
|
|
||||
<span class="components-nav-page">Pages</span> |
|
||||
</div> |
|
||||
|
|
||||
<div class="nav-items-container"> |
|
||||
<PagesList /> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div class="border-line" /> |
|
||||
|
|
||||
<PageLayout layout={$store.pages[$store.currentPageName]} /> |
|
||||
|
|
||||
<div class="border-line" /> |
|
||||
|
|
||||
<div class="components-list-container"> |
|
||||
<div class="nav-group-header"> |
|
||||
<span class="components-nav-header" style="margin-top: 0;"> |
|
||||
Screens |
|
||||
</span> |
|
||||
<div> |
|
||||
<button on:click={newScreen}> |
|
||||
<AddIcon /> |
|
||||
</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="nav-items-container"> |
|
||||
<ComponentsHierarchy screens={$store.screens} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
<div class="preview-pane"> |
|
||||
<AppPreview /> |
|
||||
</div> |
|
||||
|
|
||||
{#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'} |
|
||||
<div class="components-pane"> |
|
||||
<ComponentsPaneSwitcher /> |
|
||||
</div> |
|
||||
{/if} |
|
||||
|
|
||||
</div> |
|
||||
|
|
||||
<NewScreen bind:this={newScreenPicker} /> |
|
||||
|
|
||||
<ConfirmDialog |
|
||||
bind:this={confirmDeleteDialog} |
|
||||
title="Confirm Delete" |
|
||||
body={`Are you sure you wish to delete this '${lastPartOfName(componentToDelete)}' component`} |
|
||||
okText="Delete Component" |
|
||||
onOk={() => store.deleteComponent(componentToDelete)} /> |
|
||||
|
|
||||
<style> |
|
||||
button { |
|
||||
cursor: pointer; |
|
||||
outline: none; |
|
||||
border: none; |
|
||||
border-radius: 5px; |
|
||||
width: 20px; |
|
||||
padding-bottom: 10px; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.root { |
|
||||
display: grid; |
|
||||
grid-template-columns: 275px 1fr 275px; |
|
||||
height: 100%; |
|
||||
width: 100%; |
|
||||
background: #fafafa; |
|
||||
} |
|
||||
|
|
||||
@media only screen and (min-width: 1800px) { |
|
||||
.root { |
|
||||
display: grid; |
|
||||
grid-template-columns: 300px 1fr 300px; |
|
||||
height: 100%; |
|
||||
width: 100%; |
|
||||
background: #fafafa; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.ui-nav { |
|
||||
grid-column: 1; |
|
||||
background-color: var(--white); |
|
||||
height: calc(100vh - 49px); |
|
||||
padding: 0; |
|
||||
overflow: scroll; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
} |
|
||||
|
|
||||
.preview-pane { |
|
||||
grid-column: 2; |
|
||||
margin: 40px; |
|
||||
background: #fff; |
|
||||
border-radius: 5px; |
|
||||
box-shadow: 0 0px 6px rgba(0, 0, 0, 0.05); |
|
||||
} |
|
||||
|
|
||||
.components-pane { |
|
||||
grid-column: 3; |
|
||||
background-color: var(--white); |
|
||||
min-height: 0px; |
|
||||
overflow-y: scroll; |
|
||||
} |
|
||||
|
|
||||
.components-nav-page { |
|
||||
font-size: 13px; |
|
||||
color: #000333; |
|
||||
text-transform: uppercase; |
|
||||
padding-left: 20px; |
|
||||
margin-top: 20px; |
|
||||
font-weight: 600; |
|
||||
opacity: 0.4; |
|
||||
letter-spacing: 1px; |
|
||||
} |
|
||||
|
|
||||
.components-nav-header { |
|
||||
font-size: 13px; |
|
||||
color: #000333; |
|
||||
text-transform: uppercase; |
|
||||
margin-top: 20px; |
|
||||
font-weight: 600; |
|
||||
opacity: 0.4; |
|
||||
letter-spacing: 1px; |
|
||||
} |
|
||||
|
|
||||
.nav-header { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
margin-top: 20px; |
|
||||
} |
|
||||
|
|
||||
.nav-items-container { |
|
||||
padding: 1rem 0rem 0rem 0rem; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header { |
|
||||
display: flex; |
|
||||
padding: 0px 20px 0px 20px; |
|
||||
font-size: 0.9rem; |
|
||||
font-weight: bold; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(1) { |
|
||||
padding: 0rem 0.5rem 0rem 0rem; |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: icon; |
|
||||
margin-right: 5px; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > span:nth-child(3) { |
|
||||
margin-left: 5px; |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: title; |
|
||||
margin-top: auto; |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(3) { |
|
||||
vertical-align: bottom; |
|
||||
grid-column-start: button; |
|
||||
cursor: pointer; |
|
||||
color: var(--primary75); |
|
||||
} |
|
||||
|
|
||||
.nav-group-header > div:nth-child(3):hover { |
|
||||
color: var(--primary75); |
|
||||
} |
|
||||
|
|
||||
.navigator-title { |
|
||||
font-size: 14px; |
|
||||
color: var(--secondary100); |
|
||||
font-weight: 600; |
|
||||
text-transform: uppercase; |
|
||||
padding: 0 20px 20px 20px; |
|
||||
line-height: 1rem !important; |
|
||||
letter-spacing: 1px; |
|
||||
} |
|
||||
|
|
||||
.border-line { |
|
||||
border-bottom: 1px solid #d8d8d8; |
|
||||
} |
|
||||
|
|
||||
.components-list-container { |
|
||||
padding: 20px 0px 0 0; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,51 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { params, leftover } from "@sveltech/routify" |
||||
|
import { store } from "builderStore" |
||||
|
|
||||
|
// Get any leftover params not caught by Routifys params store. |
||||
|
const componentIds = $leftover.split("/").filter(id => id !== "") |
||||
|
|
||||
|
// It's a screen, set it to that screen |
||||
|
if ($params.screen !== "page-layout") { |
||||
|
store.setCurrentScreen($params.screen) |
||||
|
|
||||
|
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it. |
||||
|
if ($leftover) { |
||||
|
// Get the correct screen children. |
||||
|
const screenChildren = $store.pages[$params.page]._screens.find( |
||||
|
screen => screen.name === $params.screen |
||||
|
).props._children |
||||
|
findComponent(componentIds, screenChildren) |
||||
|
} |
||||
|
} else { |
||||
|
// It's a page, so set the screentype to page. |
||||
|
store.setScreenType("page") |
||||
|
|
||||
|
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it. |
||||
|
if ($leftover) { |
||||
|
findComponent(componentIds, $store.pages[$params.page].props._children) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Find Component with ID and continue |
||||
|
function findComponent(ids, children) { |
||||
|
// Setup stuff |
||||
|
let componentToSelect |
||||
|
let currentChildren = children |
||||
|
|
||||
|
// Loop through each ID |
||||
|
ids.forEach(id => { |
||||
|
// Find ID and select it |
||||
|
componentToSelect = currentChildren.find(child => child._id === id) |
||||
|
|
||||
|
// Update childrens array to selected components children |
||||
|
currentChildren = componentToSelect._children |
||||
|
}) |
||||
|
|
||||
|
// Select Component! |
||||
|
store.selectComponent(componentToSelect) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<slot /> |
||||
@ -0,0 +1,8 @@ |
|||||
|
<script> |
||||
|
import { params } from "@sveltech/routify" |
||||
|
import { store } from "builderStore" |
||||
|
|
||||
|
store.setCurrentPage($params.page) |
||||
|
</script> |
||||
|
|
||||
|
<slot /> |
||||
@ -1,5 +1,6 @@ |
|||||
<script> |
<script> |
||||
import UserInterfaceRoot from "components/userInterface/UserInterfaceRoot.svelte" |
import { goto } from "@sveltech/routify" |
||||
|
$goto("../main") |
||||
</script> |
</script> |
||||
|
|
||||
<UserInterfaceRoot /> |
<!-- routify:options index=false --> |
||||
|
|||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue