Merge branch 'master' into britecharts/separate-components-2
This commit is contained in:
commit
1156952b73
|
@ -28,9 +28,5 @@
|
||||||
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx,svelte}\"",
|
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx,svelte}\"",
|
||||||
"test:e2e": "lerna run cy:test",
|
"test:e2e": "lerna run cy:test",
|
||||||
"test:e2e:ci": "lerna run cy:ci"
|
"test:e2e:ci": "lerna run cy:ci"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@material/icon-button": "4.0.0",
|
|
||||||
"date-fns": "^2.10.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,24 +60,18 @@
|
||||||
"@budibase/bbui": "^1.23.1",
|
"@budibase/bbui": "^1.23.1",
|
||||||
"@budibase/client": "^0.1.17",
|
"@budibase/client": "^0.1.17",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@svelteschool/svelte-forms": "^0.7.0",
|
"@svelteschool/svelte-forms": "^0.7.0",
|
||||||
"britecharts": "^2.16.0",
|
"britecharts": "^2.16.0",
|
||||||
"codemirror": "^5.51.0",
|
|
||||||
"d3-selection": "^1.4.1",
|
"d3-selection": "^1.4.1",
|
||||||
"date-fns": "^1.29.0",
|
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"fast-sort": "^2.2.0",
|
"fast-sort": "^2.2.0",
|
||||||
"feather-icons": "^4.21.0",
|
"feather-icons": "^4.21.0",
|
||||||
"flatpickr": "^4.5.7",
|
"flatpickr": "^4.5.7",
|
||||||
"lodash": "^4.17.13",
|
"lodash": "^4.17.13",
|
||||||
"lunr": "^2.3.5",
|
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.0.1",
|
||||||
"posthog-js": "1.3.1",
|
"posthog-js": "1.3.1",
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"string_decoder": "^1.2.0",
|
|
||||||
"svelte-portal": "^0.1.0",
|
"svelte-portal": "^0.1.0",
|
||||||
"svelte-simple-modal": "^0.4.2",
|
"svelte-simple-modal": "^0.4.2",
|
||||||
"uikit": "^3.1.7",
|
"uikit": "^3.1.7",
|
||||||
|
@ -94,19 +88,15 @@
|
||||||
"@testing-library/jest-dom": "^5.11.0",
|
"@testing-library/jest-dom": "^5.11.0",
|
||||||
"@testing-library/svelte": "^3.0.0",
|
"@testing-library/svelte": "^3.0.0",
|
||||||
"babel-jest": "^24.8.0",
|
"babel-jest": "^24.8.0",
|
||||||
"browser-sync": "^2.26.7",
|
|
||||||
"cypress": "^4.8.0",
|
"cypress": "^4.8.0",
|
||||||
"cypress-terminal-report": "^1.4.1",
|
"cypress-terminal-report": "^1.4.1",
|
||||||
"eslint-plugin-cypress": "^2.11.1",
|
"eslint-plugin-cypress": "^2.11.1",
|
||||||
"http-proxy-middleware": "^0.19.1",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^24.8.0",
|
"jest": "^24.8.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^1.12.0",
|
"rollup": "^1.12.0",
|
||||||
"rollup-plugin-alias": "^1.5.2",
|
"rollup-plugin-alias": "^1.5.2",
|
||||||
"rollup-plugin-browsersync": "^1.0.0",
|
|
||||||
"rollup-plugin-commonjs": "^10.0.0",
|
"rollup-plugin-commonjs": "^10.0.0",
|
||||||
"rollup-plugin-copy": "^3.0.0",
|
"rollup-plugin-copy": "^3.0.0",
|
||||||
"rollup-plugin-livereload": "^1.0.0",
|
"rollup-plugin-livereload": "^1.0.0",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { values } from "lodash/fp"
|
import { values, cloneDeep } from "lodash/fp"
|
||||||
import { get_capitalised_name } from "../../helpers"
|
import { get_capitalised_name } from "../../helpers"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import * as backendStoreActions from "./backend"
|
import * as backendStoreActions from "./backend"
|
||||||
|
@ -24,8 +24,8 @@ import {
|
||||||
saveCurrentPreviewItem as _saveCurrentPreviewItem,
|
saveCurrentPreviewItem as _saveCurrentPreviewItem,
|
||||||
saveScreenApi as _saveScreenApi,
|
saveScreenApi as _saveScreenApi,
|
||||||
regenerateCssForCurrentScreen,
|
regenerateCssForCurrentScreen,
|
||||||
|
generateNewIdsForComponent,
|
||||||
} from "../storeUtils"
|
} from "../storeUtils"
|
||||||
|
|
||||||
export const getStore = () => {
|
export const getStore = () => {
|
||||||
const initial = {
|
const initial = {
|
||||||
apps: [],
|
apps: [],
|
||||||
|
@ -70,6 +70,8 @@ export const getStore = () => {
|
||||||
store.addTemplatedComponent = addTemplatedComponent(store)
|
store.addTemplatedComponent = addTemplatedComponent(store)
|
||||||
store.setMetadataProp = setMetadataProp(store)
|
store.setMetadataProp = setMetadataProp(store)
|
||||||
store.editPageOrScreen = editPageOrScreen(store)
|
store.editPageOrScreen = editPageOrScreen(store)
|
||||||
|
store.pasteComponent = pasteComponent(store)
|
||||||
|
store.storeComponentForCopy = storeComponentForCopy(store)
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,9 +293,14 @@ const addChildComponent = store => (componentToAdd, presetProps = {}) => {
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
|
|
||||||
state.currentComponentInfo._children = state.currentComponentInfo._children.concat(
|
const currentComponent =
|
||||||
newComponent.props
|
state.components[state.currentComponentInfo._component]
|
||||||
)
|
|
||||||
|
const targetParent = currentComponent.children
|
||||||
|
? state.currentComponentInfo
|
||||||
|
: getParent(state.currentPreviewItem.props, state.currentComponentInfo)
|
||||||
|
|
||||||
|
targetParent._children = targetParent._children.concat(newComponent.props)
|
||||||
|
|
||||||
state.currentFrontEndType === "page"
|
state.currentFrontEndType === "page"
|
||||||
? _savePage(state)
|
? _savePage(state)
|
||||||
|
@ -454,3 +461,50 @@ const setMetadataProp = store => (name, prop) => {
|
||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storeComponentForCopy = store => (component, cut = false) => {
|
||||||
|
store.update(s => {
|
||||||
|
const copiedComponent = cloneDeep(component)
|
||||||
|
s.componentToPaste = copiedComponent
|
||||||
|
s.componentToPaste.isCut = cut
|
||||||
|
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 = store => (targetComponent, mode) => {
|
||||||
|
store.update(s => {
|
||||||
|
if (!s.componentToPaste) return s
|
||||||
|
|
||||||
|
const componentToPaste = cloneDeep(s.componentToPaste)
|
||||||
|
// retain the same ids as things may be referencing this component
|
||||||
|
if (componentToPaste.isCut) {
|
||||||
|
// in case we paste a second time
|
||||||
|
s.componentToPaste.isCut = false
|
||||||
|
} else {
|
||||||
|
generateNewIdsForComponent(componentToPaste)
|
||||||
|
}
|
||||||
|
delete componentToPaste.isCut
|
||||||
|
|
||||||
|
if (mode === "inside") {
|
||||||
|
targetComponent._children.push(componentToPaste)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = getParent(s.currentPreviewItem.props, targetComponent)
|
||||||
|
|
||||||
|
const targetIndex = parent._children.indexOf(targetComponent)
|
||||||
|
const index = mode === "above" ? targetIndex : targetIndex + 1
|
||||||
|
parent._children.splice(index, 0, cloneDeep(componentToPaste))
|
||||||
|
regenerateCssForCurrentScreen(s)
|
||||||
|
_saveCurrentPreviewItem(s)
|
||||||
|
selectComponent(s, componentToPaste)
|
||||||
|
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { makePropsSafe } from "components/userInterface/pagesParsing/createProps"
|
import { makePropsSafe } from "components/userInterface/pagesParsing/createProps"
|
||||||
import api from "./api"
|
import api from "./api"
|
||||||
import { generate_screen_css } from "./generate_css"
|
import { generate_screen_css } from "./generate_css"
|
||||||
|
import { uuid } from "./uuid"
|
||||||
|
|
||||||
export const selectComponent = (state, component) => {
|
export const selectComponent = (state, component) => {
|
||||||
const componentDef = component._component.startsWith("##")
|
const componentDef = component._component.startsWith("##")
|
||||||
|
@ -79,3 +80,8 @@ export const regenerateCssForCurrentScreen = state => {
|
||||||
])
|
])
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const generateNewIdsForComponent = c =>
|
||||||
|
walkProps(c, p => {
|
||||||
|
p._id = uuid()
|
||||||
|
})
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
<script>
|
|
||||||
export let title
|
|
||||||
export let icon
|
|
||||||
|
|
||||||
export let primary
|
|
||||||
export let secondary
|
|
||||||
export let tertiary
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div on:click class:primary class:secondary class:tertiary>
|
|
||||||
<i class={icon} />
|
|
||||||
<span>{title}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
height: 80px;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: var(--ink);
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 15px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
transition: 0.3s transform;
|
|
||||||
background: var(--grey-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 24px;
|
|
||||||
color: var(--grey-7);
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 8px;
|
|
||||||
line-height: 1.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
div:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background: var(--grey-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
|
||||||
background: var(--ink);
|
|
||||||
color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary {
|
|
||||||
background: var(--blue-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tertiary {
|
|
||||||
background: var(--white);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<script>
|
|
||||||
import { JavaScriptIcon } from "../common/Icons"
|
|
||||||
// todo: use https://ace.c9.io
|
|
||||||
export let text = ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<textarea class="uk-textarea" bind:value={text} />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
textarea {
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
background: var(--grey-7);
|
|
||||||
color: var(--white);
|
|
||||||
font-family: "Courier New", Courier, monospace;
|
|
||||||
height: 200px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<script>
|
|
||||||
export let name = ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4>Coming Sometime: {name}</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
h4 {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,45 +0,0 @@
|
||||||
<script>
|
|
||||||
import { createEventDispatcher } from "svelte"
|
|
||||||
import Select from "../common/Select.svelte"
|
|
||||||
|
|
||||||
export let selected
|
|
||||||
export let label
|
|
||||||
export let options
|
|
||||||
export let valueMember
|
|
||||||
export let textMember
|
|
||||||
export let multiple = false
|
|
||||||
export let width = "medium"
|
|
||||||
export let size = "small"
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="uk-margin">
|
|
||||||
<label class="uk-form-label">{label}</label>
|
|
||||||
<div class="uk-form-controls">
|
|
||||||
{#if multiple}
|
|
||||||
<Select
|
|
||||||
class="uk-select uk-form-width-{width} uk-form-{size}"
|
|
||||||
multiple
|
|
||||||
bind:value={selected}
|
|
||||||
on:change>
|
|
||||||
{#each options as option}
|
|
||||||
<option value={!valueMember ? option : valueMember(option)}>
|
|
||||||
{!textMember ? option : textMember(option)}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
{:else}
|
|
||||||
<Select
|
|
||||||
class="uk-select uk-form-width-{width} uk-form-{size}"
|
|
||||||
bind:value={selected}
|
|
||||||
on:change>
|
|
||||||
{#each options as option}
|
|
||||||
<option value={!valueMember ? option : valueMember(option)}>
|
|
||||||
{!textMember ? option : textMember(option)}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<button on:click>
|
|
||||||
<slot>+</slot>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
min-width: 1.8rem;
|
|
||||||
min-height: 1.8rem;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<script>
|
|
||||||
export let text = ""
|
|
||||||
export let label = ""
|
|
||||||
export let width = "medium"
|
|
||||||
export let size = "small"
|
|
||||||
export let margin = true
|
|
||||||
export let infoText = ""
|
|
||||||
export let hasError = false
|
|
||||||
export let disabled = false
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class:uk-margin={margin}>
|
|
||||||
<label class="uk-form-label">{label}</label>
|
|
||||||
<div class="uk-form-controls">
|
|
||||||
<input
|
|
||||||
data-cy={label}
|
|
||||||
class="budibase__input"
|
|
||||||
class:uk-form-danger={hasError}
|
|
||||||
on:change
|
|
||||||
bind:value={text}
|
|
||||||
{disabled} />
|
|
||||||
</div>
|
|
||||||
{#if infoText}
|
|
||||||
<div class="info-text">{infoText}</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.info-text {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--secondary50);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<script>
|
|
||||||
import RecordCard from "./RecordCard.svelte"
|
|
||||||
|
|
||||||
export let record = {}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
<div class="title">{record.name}</div>
|
|
||||||
<div class="inner">
|
|
||||||
<div class="node-path">{record.nodeKey()}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -168,6 +168,9 @@
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
border: 1px solid var(--grey-4);
|
border: 1px solid var(--grey-4);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: pre;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr {
|
tbody tr {
|
||||||
|
@ -187,6 +190,7 @@
|
||||||
|
|
||||||
.popovers {
|
.popovers {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-data {
|
.no-data {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { Input, TextArea, Button, Select } from "@budibase/bbui"
|
import { Input, TextArea, Button, Select } from "@budibase/bbui"
|
||||||
|
import { cloneDeep, merge } from "lodash/fp"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
||||||
import NumberBox from "components/common/NumberBox.svelte"
|
import NumberBox from "components/common/NumberBox.svelte"
|
||||||
import ValuesList from "components/common/ValuesList.svelte"
|
import ValuesList from "components/common/ValuesList.svelte"
|
||||||
|
@ -19,6 +18,7 @@
|
||||||
export let onClosed
|
export let onClosed
|
||||||
export let field = {}
|
export let field = {}
|
||||||
|
|
||||||
|
let fieldDefinitions = cloneDeep(FIELDS)
|
||||||
let originalName = field.name
|
let originalName = field.name
|
||||||
|
|
||||||
$: required =
|
$: required =
|
||||||
|
@ -26,7 +26,10 @@
|
||||||
field.constraints.presence &&
|
field.constraints.presence &&
|
||||||
!field.constraints.presence.allowEmpty
|
!field.constraints.presence.allowEmpty
|
||||||
$: if (field.type) {
|
$: if (field.type) {
|
||||||
field.constraints = FIELDS[field.type.toUpperCase()].constraints
|
field.constraints = merge(
|
||||||
|
fieldDefinitions[field.type.toUpperCase()].constraints,
|
||||||
|
field.constraints
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveColumn() {
|
async function saveColumn() {
|
||||||
|
@ -46,7 +49,7 @@
|
||||||
<Input placeholder="Name" thin bind:value={field.name} />
|
<Input placeholder="Name" thin bind:value={field.name} />
|
||||||
|
|
||||||
<Select secondary thin bind:value={field.type}>
|
<Select secondary thin bind:value={field.type}>
|
||||||
{#each Object.values(FIELDS) as field}
|
{#each Object.values(fieldDefinitions) as field}
|
||||||
<option value={field.type}>{field.name}</option>
|
<option value={field.type}>{field.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
<script>
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import CodeArea from "components/common/CodeArea.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import { filter, some, map, compose } from "lodash/fp"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
|
|
||||||
const SNIPPET_EDITORS = {
|
|
||||||
MAP: "Map",
|
|
||||||
FILTER: "Filter",
|
|
||||||
REDUCE: "Reduce",
|
|
||||||
}
|
|
||||||
|
|
||||||
const COUCHDB_FUNCTION = `function(doc) {
|
|
||||||
|
|
||||||
}`
|
|
||||||
|
|
||||||
export let onClosed
|
|
||||||
export let view = {}
|
|
||||||
|
|
||||||
let currentSnippetEditor = SNIPPET_EDITORS.MAP
|
|
||||||
|
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
|
||||||
|
|
||||||
function deleteView() {}
|
|
||||||
|
|
||||||
async function saveView() {
|
|
||||||
const SAVE_VIEW_URL = `/api/views`
|
|
||||||
const response = await api.post(SAVE_VIEW_URL, view)
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.views = [...state.views, response.view]
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
onClosed()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<i class="ri-eye-line button--toggled" />
|
|
||||||
<h3 class="budibase__title--3">Create / Edit View</h3>
|
|
||||||
</div>
|
|
||||||
<form on:submit|preventDefault class="uk-form-stacked root">
|
|
||||||
{#if $store.errors && $store.errors.length > 0}
|
|
||||||
<ErrorsBox errors={$store.errors} />
|
|
||||||
{/if}
|
|
||||||
<div class="main">
|
|
||||||
<div class="uk-grid-small" uk-grid>
|
|
||||||
<div class="uk-width-1-2@s">
|
|
||||||
<Textbox bind:text={view.name} label="Name" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="code-snippets">
|
|
||||||
{#each Object.values(SNIPPET_EDITORS) as snippetType}
|
|
||||||
<span
|
|
||||||
class="snippet-selector__heading hoverable"
|
|
||||||
class:highlighted={currentSnippetEditor === snippetType}
|
|
||||||
on:click={() => (currentSnippetEditor = snippetType)}>
|
|
||||||
{snippetType}
|
|
||||||
</span>
|
|
||||||
{/each}
|
|
||||||
{#if currentSnippetEditor === SNIPPET_EDITORS.MAP}
|
|
||||||
<CodeArea bind:text={view.map} label="Map" />
|
|
||||||
{:else if currentSnippetEditor === SNIPPET_EDITORS.FILTER}
|
|
||||||
<CodeArea bind:text={view.filter} label="Filter" />
|
|
||||||
{:else if currentSnippetEditor === SNIPPET_EDITORS.REDUCE}
|
|
||||||
<CodeArea bind:text={view.reduce} label="Reduce" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="buttons">
|
|
||||||
<div class="button">
|
|
||||||
<ActionButton secondary on:click={deleteView}>Delete</ActionButton>
|
|
||||||
</div>
|
|
||||||
<ActionButton color="secondary" on:click={saveView}>Save</ActionButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlighted {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0 0 0 10px;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snippet-selector__heading {
|
|
||||||
margin-right: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--grey-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
padding: 20px 40px 0 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
margin: 20px 40px 0px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-snippets {
|
|
||||||
margin: 20px 0px 20px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
margin: 0 40px;
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,4 +1,3 @@
|
||||||
export { default as DeleteRecordModal } from "./DeleteRecord.svelte"
|
export { default as DeleteRecordModal } from "./DeleteRecord.svelte"
|
||||||
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte"
|
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte"
|
||||||
export { default as CreateEditViewModal } from "./CreateEditView.svelte"
|
|
||||||
export { default as CreateUserModal } from "./CreateUser.svelte"
|
export { default as CreateUserModal } from "./CreateUser.svelte"
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import { ModelSetupNav } from "components/nav/ModelSetupNav"
|
|
||||||
import ModelFieldEditor from "components/nav/ModelSetupNav/ModelFieldEditor.svelte"
|
|
||||||
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import { ModelSetupNav } from "components/nav/ModelSetupNav"
|
|
||||||
import ModelFieldEditor from "components/nav/ModelSetupNav/ModelFieldEditor.svelte"
|
|
||||||
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
export let field
|
export let field
|
||||||
|
@ -27,6 +25,7 @@
|
||||||
|
|
||||||
function deleteField() {
|
function deleteField() {
|
||||||
backendUiStore.actions.models.deleteField(field)
|
backendUiStore.actions.models.deleteField(field)
|
||||||
|
hideEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
function sort(direction, column) {
|
function sort(direction, column) {
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
<script>
|
|
||||||
import { tick, onMount } from "svelte"
|
|
||||||
import { goto } from "@sveltech/routify"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import { CheckIcon } from "../common/Icons"
|
|
||||||
|
|
||||||
$: instances = $store.appInstances
|
|
||||||
|
|
||||||
async function selectDatabase(database) {
|
|
||||||
backendUiStore.actions.database.select(database)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteDatabase(database) {
|
|
||||||
const DELETE_DATABASE_URL = `/api/instances/${database.name}`
|
|
||||||
const response = await api.delete(DELETE_DATABASE_URL)
|
|
||||||
store.update(state => {
|
|
||||||
state.appInstances = state.appInstances.filter(
|
|
||||||
db => db._id !== database._id
|
|
||||||
)
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
<ul>
|
|
||||||
{#each $store.appInstances as database}
|
|
||||||
<li>
|
|
||||||
<span class="icon">
|
|
||||||
{#if database._id === $backendUiStore.selectedDatabase._id}
|
|
||||||
<CheckIcon />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
class:active={database._id === $backendUiStore.selectedDatabase._id}
|
|
||||||
on:click={() => {
|
|
||||||
$goto(`./database/${database._id}`), selectDatabase(database)
|
|
||||||
}}>
|
|
||||||
{database.name}
|
|
||||||
</button>
|
|
||||||
<i
|
|
||||||
class="ri-delete-bin-7-line hoverable alignment"
|
|
||||||
on:click={() => deleteDatabase(database)} />
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--ink);
|
|
||||||
position: relative;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignment {
|
|
||||||
margin-left: auto;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0px 0px 10px 0px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 0 0 0 6px;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
font-size: 13px;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 14px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,45 +0,0 @@
|
||||||
<script>
|
|
||||||
import { getContext } from "svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
|
||||||
import getIcon from "../common/icon"
|
|
||||||
import { CreateEditViewModal } from "components/database/ModelDataTable/modals"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
export let node
|
|
||||||
export let type
|
|
||||||
export let onSelect
|
|
||||||
|
|
||||||
let navActive = ""
|
|
||||||
|
|
||||||
const ICON_MAP = {
|
|
||||||
index: "ri-eye-line",
|
|
||||||
model: "ri-list-settings-line",
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
on:click={() => onSelect(node)}
|
|
||||||
class="budibase__nav-item hierarchy-item"
|
|
||||||
class:capitalized={type === 'model'}
|
|
||||||
class:selected={$backendUiStore.selectedView === `all_${node._id}`}>
|
|
||||||
<i class={ICON_MAP[type]} />
|
|
||||||
<span style="margin-left: 1rem">{node.name}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.hierarchy-item {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.capitalized {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -17,6 +17,12 @@
|
||||||
})
|
})
|
||||||
notifier.success(`Table ${name} created successfully.`)
|
notifier.success(`Table ${name} created successfully.`)
|
||||||
$goto(`./model/${model._id}`)
|
$goto(`./model/${model._id}`)
|
||||||
|
name = ""
|
||||||
|
dropdown.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClosed = () => {
|
||||||
|
name = ""
|
||||||
dropdown.hide()
|
dropdown.hide()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -35,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<div class="button-margin-3">
|
<div class="button-margin-3">
|
||||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-margin-4">
|
<div class="button-margin-4">
|
||||||
<Button primary on:click={saveTable}>Save</Button>
|
<Button primary on:click={saveTable}>Save</Button>
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
<script>
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { Button } from "@budibase/bbui"
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import NumberBox from "components/common/NumberBox.svelte"
|
|
||||||
import ValuesList from "components/common/ValuesList.svelte"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
import Checkbox from "components/common/Checkbox.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import DatePicker from "components/common/DatePicker.svelte"
|
|
||||||
import { keys, cloneDeep } from "lodash/fp"
|
|
||||||
|
|
||||||
const FIELD_TYPES = ["string", "number", "boolean", "link"]
|
|
||||||
|
|
||||||
let field = {}
|
|
||||||
|
|
||||||
$: field =
|
|
||||||
$backendUiStore.draftModel.schema[$backendUiStore.selectedField] || {}
|
|
||||||
$: required =
|
|
||||||
field.constraints &&
|
|
||||||
field.constraints.presence &&
|
|
||||||
!field.constraints.presence.allowEmpty
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="info">
|
|
||||||
<div class="field-box">
|
|
||||||
<header>Name</header>
|
|
||||||
<input class="budibase__input" type="text" bind:value={field.name} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info">
|
|
||||||
<div class="field-box">
|
|
||||||
<header>Type</header>
|
|
||||||
<span>{field.type}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info">
|
|
||||||
<div class="field">
|
|
||||||
<label>Required</label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
bind:checked={required}
|
|
||||||
on:change={() => (field.constraints.presence.allowEmpty = required)} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if field.type === 'string'}
|
|
||||||
<NumberBox
|
|
||||||
label="Max Length"
|
|
||||||
bind:value={field.constraints.length.maximum} />
|
|
||||||
<ValuesList label="Categories" bind:values={field.constraints.inclusion} />
|
|
||||||
{:else if field.type === 'datetime'}
|
|
||||||
<DatePicker
|
|
||||||
label="Min Value"
|
|
||||||
bind:value={field.constraints.datetime.earliest} />
|
|
||||||
<DatePicker
|
|
||||||
label="Max Value"
|
|
||||||
bind:value={field.constraints.datetime.latest} />
|
|
||||||
{:else if field.type === 'number'}
|
|
||||||
<NumberBox
|
|
||||||
label="Min Value"
|
|
||||||
bind:value={field.constraints.numericality.greaterThanOrEqualTo} />
|
|
||||||
<NumberBox
|
|
||||||
label="Max Value"
|
|
||||||
bind:value={field.constraints.numericality.lessThanOrEqualTo} />
|
|
||||||
{:else if field.type === 'link'}
|
|
||||||
<div class="field">
|
|
||||||
<label>Link</label>
|
|
||||||
<select class="budibase__input" bind:value={field.modelId}>
|
|
||||||
<option value={''} />
|
|
||||||
{#each $backendUiStore.models as model}
|
|
||||||
{#if model._id !== $backendUiStore.draftModel._id}
|
|
||||||
<option value={model._id}>{model.name}</option>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.info {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
|
||||||
display: grid;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-box header {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-box span {
|
|
||||||
background: var(--grey-2);
|
|
||||||
color: var(--grey-6);
|
|
||||||
font-weight: 400;
|
|
||||||
height: 36px;
|
|
||||||
display: grid;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 12px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,156 +0,0 @@
|
||||||
<script>
|
|
||||||
import { getContext, onMount } from "svelte"
|
|
||||||
import { Button, Switcher } from "@budibase/bbui"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import ModelFieldEditor from "./ModelFieldEditor.svelte"
|
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
const ITEMS = [
|
|
||||||
{
|
|
||||||
title: "Setup",
|
|
||||||
key: "SETUP",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Delete",
|
|
||||||
key: "DELETE",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
let edited = false
|
|
||||||
|
|
||||||
$: selectedTab = $backendUiStore.tabs.SETUP_PANEL
|
|
||||||
|
|
||||||
$: edited =
|
|
||||||
$backendUiStore.selectedField ||
|
|
||||||
($backendUiStore.draftModel &&
|
|
||||||
$backendUiStore.draftModel.name !== $backendUiStore.selectedModel.name)
|
|
||||||
|
|
||||||
async function deleteModel() {
|
|
||||||
const model = $backendUiStore.selectedModel
|
|
||||||
const field = $backendUiStore.selectedField
|
|
||||||
|
|
||||||
if (field) {
|
|
||||||
const name = model.schema[field].name
|
|
||||||
delete model.schema[field]
|
|
||||||
backendUiStore.actions.models.save(model)
|
|
||||||
notifier.danger(`Field ${name} deleted.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const DELETE_MODEL_URL = `/api/models/${model._id}/${model._rev}`
|
|
||||||
const response = await api.delete(DELETE_MODEL_URL)
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.selectedView = null
|
|
||||||
state.selectedModel = {}
|
|
||||||
state.draftModel = {}
|
|
||||||
state.models = state.models.filter(({ _id }) => _id !== model._id)
|
|
||||||
notifier.danger(`${model.name} deleted successfully.`)
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function validate() {
|
|
||||||
let errors = []
|
|
||||||
for (let field of Object.values($backendUiStore.draftModel.schema)) {
|
|
||||||
const restrictedFieldNames = ["type", "modelId"]
|
|
||||||
if (field.name.startsWith("_")) {
|
|
||||||
errors.push(`field '${field.name}' - name cannot begin with '_''`)
|
|
||||||
} else if (restrictedFieldNames.includes(field.name)) {
|
|
||||||
errors.push(
|
|
||||||
`field '${field.name}' - is a restricted name, please rename`
|
|
||||||
)
|
|
||||||
} else if (!field.name || !field.name.trim()) {
|
|
||||||
errors.push("field name cannot be blank")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$backendUiStore.draftModel.name) {
|
|
||||||
errors.push("Table name cannot be blank")
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveModel() {
|
|
||||||
const errors = validate()
|
|
||||||
if (errors.length > 0) {
|
|
||||||
notifier.danger(errors.join("/n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await backendUiStore.actions.models.save($backendUiStore.draftModel)
|
|
||||||
notifier.success(
|
|
||||||
"Success! Your changes have been saved. Please continue on with your greatness."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="items-root">
|
|
||||||
<Switcher headings={ITEMS} bind:value={$backendUiStore.tabs.SETUP_PANEL}>
|
|
||||||
{#if selectedTab === 'SETUP'}
|
|
||||||
{#if $backendUiStore.selectedField}
|
|
||||||
<ModelFieldEditor />
|
|
||||||
{:else if $backendUiStore.draftModel.schema}
|
|
||||||
<div class="titled-input">
|
|
||||||
<header>Name</header>
|
|
||||||
<input
|
|
||||||
data-cy="table-name-input"
|
|
||||||
type="text"
|
|
||||||
class="budibase__input"
|
|
||||||
bind:value={$backendUiStore.draftModel.name} />
|
|
||||||
</div>
|
|
||||||
<!-- dont have this capability yet..
|
|
||||||
<div class="titled-input">
|
|
||||||
<header>Import Data</header>
|
|
||||||
<Button wide secondary>Import CSV</Button>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
{/if}
|
|
||||||
<footer>
|
|
||||||
<Button disabled={!edited} green={edited} wide on:click={saveModel}>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</footer>
|
|
||||||
{:else if selectedTab === 'DELETE'}
|
|
||||||
<div class="titled-input">
|
|
||||||
<header>Danger Zone</header>
|
|
||||||
<Button red wide on:click={deleteModel}>Delete</Button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</Switcher>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
header {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
width: 260px;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.items-root {
|
|
||||||
padding: 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-height: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: var(--white);
|
|
||||||
}
|
|
||||||
|
|
||||||
.titled-input {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titled-input header {
|
|
||||||
display: block;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1 +0,0 @@
|
||||||
export { default as ModelSetupNav } from "./ModelSetupNav.svelte"
|
|
|
@ -1,82 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import getIcon from "../common/icon"
|
|
||||||
import { CheckIcon } from "../common/Icons"
|
|
||||||
|
|
||||||
const getPage = (s, name) => {
|
|
||||||
const props = s.pages[name]
|
|
||||||
return { name, props }
|
|
||||||
}
|
|
||||||
|
|
||||||
$: currentAppInfo = {
|
|
||||||
name: $store.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchUsers() {
|
|
||||||
const FETCH_USERS_URL = `/api/users`
|
|
||||||
const response = await api.get(FETCH_USERS_URL)
|
|
||||||
const users = await response.json()
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.users = users
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(fetchUsers)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
<ul>
|
|
||||||
{#each $backendUiStore.users as user}
|
|
||||||
<li>
|
|
||||||
<i class="ri-user-4-line" />
|
|
||||||
<button class:active={user.username === $store.currentUserName}>
|
|
||||||
{user.name}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: var(--secondary50);
|
|
||||||
font-weight: bold;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 1.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 0 0 0 6px;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
font-size: 13px;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
background: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 14px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -13,35 +13,26 @@
|
||||||
<style>
|
<style>
|
||||||
.apps-card {
|
.apps-card {
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
padding: 20px 20px 20px 20px;
|
padding: var(--spacing-xl);
|
||||||
max-width: 400px;
|
max-width: 300px;
|
||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
border-radius: 5px;
|
border-radius: var(--border-radius-m);
|
||||||
border: 1px solid var(--grey-4);
|
border: var(--border-dark);
|
||||||
font-family: Inter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-button:hover {
|
.app-button:hover {
|
||||||
background-color: var(--grey-1);
|
background-color: var(--white);
|
||||||
|
color: var(--black);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-title {
|
.app-title {
|
||||||
font-size: 18px;
|
font-size: var(--font-size-l);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-desc {
|
|
||||||
color: var(--grey-7);
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 1;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -52,17 +43,18 @@
|
||||||
.app-button {
|
.app-button {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: var(--white);
|
background-color: var(--ink);
|
||||||
color: var(--ink);
|
color: var(--white);
|
||||||
|
border: 1.5px var(--ink) solid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 12px 20px;
|
padding: 8px 16px;
|
||||||
border-radius: 5px;
|
border-radius: var(--border-radius-s);
|
||||||
border: 1px var(--grey-2) solid;
|
font-size: var(--font-size-xs);
|
||||||
font-size: 14px;
|
font-weight: 500;
|
||||||
font-weight: 400;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: var(--font-sans);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
<style>
|
<style>
|
||||||
.apps {
|
.apps {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
grid-gap: 20px 40px;
|
grid-gap: var(--layout-m);
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,16 +208,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
{#if $createAppStore.currentStep > 0}
|
{#if $createAppStore.currentStep > 0}
|
||||||
<Button secondary on:click={back}>Back</Button>
|
<Button medium secondary on:click={back}>Back</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $createAppStore.currentStep < steps.length - 1}
|
{#if $createAppStore.currentStep < steps.length - 1}
|
||||||
<Button secondary on:click={next} disabled={!currentStepIsValid}>
|
<Button medium blue on:click={next} disabled={!currentStepIsValid}>
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $createAppStore.currentStep === steps.length - 1}
|
{#if $createAppStore.currentStep === steps.length - 1}
|
||||||
<Button
|
<Button
|
||||||
secondary
|
medium
|
||||||
|
blue
|
||||||
on:click={signUp}
|
on:click={signUp}
|
||||||
disabled={!fullFormIsValid || submitting}>
|
disabled={!fullFormIsValid || submitting}>
|
||||||
{submitting ? 'Loading...' : 'Submit'}
|
{submitting ? 'Loading...' : 'Submit'}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
let blurred = { appName: false }
|
let blurred = { appName: false }
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h2>Create your first web app</h2>
|
<h2>Create your web app</h2>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Input
|
<Input
|
||||||
on:input={() => (blurred.appName = true)}
|
on:input={() => (blurred.appName = true)}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type="pasword"
|
type="pasword"
|
||||||
error={blurred.password && validationErrors.password} />
|
error={blurred.password && validationErrors.password} />
|
||||||
<Select name="accessLevelId">
|
<Select secondary name="accessLevelId">
|
||||||
<option value="ADMIN">Admin</option>
|
<option value="ADMIN">Admin</option>
|
||||||
<option value="POWER_USER">Power User</option>
|
<option value="POWER_USER">Power User</option>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
<script>
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import UIkit from "uikit"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
|
||||||
import CodeMirror from "codemirror"
|
|
||||||
import "codemirror/mode/javascript/javascript.js"
|
|
||||||
|
|
||||||
export let onCodeChanged
|
|
||||||
export let code
|
|
||||||
|
|
||||||
export const show = () => {
|
|
||||||
UIkit.modal(codeModal).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
let codeModal
|
|
||||||
let editor
|
|
||||||
let cmInstance
|
|
||||||
|
|
||||||
$: currentCode = code
|
|
||||||
$: originalCode = code
|
|
||||||
$: {
|
|
||||||
if (editor) {
|
|
||||||
if (!cmInstance) {
|
|
||||||
cmInstance = CodeMirror.fromTextArea(editor, {
|
|
||||||
mode: "javascript",
|
|
||||||
lineNumbers: false,
|
|
||||||
lineWrapping: true,
|
|
||||||
smartIndent: true,
|
|
||||||
matchBrackets: true,
|
|
||||||
readOnly: false,
|
|
||||||
})
|
|
||||||
cmInstance.on("change", () => (currentCode = cmInstance.getValue()))
|
|
||||||
}
|
|
||||||
cmInstance.focus()
|
|
||||||
cmInstance.setValue(code || "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
UIkit.modal(codeModal).hide()
|
|
||||||
currentCode = originalCode
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = () => {
|
|
||||||
originalCode = currentCode
|
|
||||||
onCodeChanged(currentCode)
|
|
||||||
UIkit.modal(codeModal).hide()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={codeModal} uk-modal>
|
|
||||||
<div class="uk-modal-dialog" uk-overflow-auto>
|
|
||||||
|
|
||||||
<div class="uk-modal-header">
|
|
||||||
<h3>Code</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-modal-body uk-form-horizontal">
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Use the code box below to control how this component is displayed, with
|
|
||||||
javascript.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="editor-code-surround">
|
|
||||||
function(render, context, state, route) {'{'}
|
|
||||||
</div>
|
|
||||||
<div class="editor">
|
|
||||||
<textarea bind:this={editor} />
|
|
||||||
</div>
|
|
||||||
<div class="editor-code-surround">{'}'}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="uk-modal-footer">
|
|
||||||
<ButtonGroup>
|
|
||||||
<ActionButton primary on:click={save}>Save</ActionButton>
|
|
||||||
<ActionButton alert on:click={cancel}>Close</ActionButton>
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
h3 {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #8997ab;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #333;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor {
|
|
||||||
border-style: dotted;
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: gainsboro;
|
|
||||||
padding: 10px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-code-surround {
|
|
||||||
font-family: "Courier New", Courier, monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,320 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, createEventDispatcher } from "svelte"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import Swatch from "./Swatch.svelte"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
import { buildStyle } from "./helpers.js"
|
|
||||||
import {
|
|
||||||
getColorFormat,
|
|
||||||
convertToHSVA,
|
|
||||||
convertHsvaToFormat,
|
|
||||||
} from "./utils.js"
|
|
||||||
import Slider from "./Slider.svelte"
|
|
||||||
import Palette from "./Palette.svelte"
|
|
||||||
import ButtonGroup from "./ButtonGroup.svelte"
|
|
||||||
import Input from "./Input.svelte"
|
|
||||||
import Portal from "./Portal.svelte"
|
|
||||||
|
|
||||||
export let value = "#3ec1d3ff"
|
|
||||||
export let open = false
|
|
||||||
export let swatches = [] //TODO: Safe swatches - limit to 12. warn in console
|
|
||||||
export let disableSwatches = false
|
|
||||||
export let format = "hexa"
|
|
||||||
export let style = ""
|
|
||||||
export let pickerHeight = 0
|
|
||||||
export let pickerWidth = 0
|
|
||||||
|
|
||||||
let colorPicker = null
|
|
||||||
let adder = null
|
|
||||||
|
|
||||||
let h = null
|
|
||||||
let s = null
|
|
||||||
let v = null
|
|
||||||
let a = null
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!swatches.length > 0) {
|
|
||||||
//Don't use locally stored recent colors if swatches have been passed as props
|
|
||||||
getRecentColors()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorPicker) {
|
|
||||||
colorPicker.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format) {
|
|
||||||
convertAndSetHSVA()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function getRecentColors() {
|
|
||||||
let colorStore = localStorage.getItem("cp:recent-colors")
|
|
||||||
if (colorStore) {
|
|
||||||
swatches = JSON.parse(colorStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEscape(e) {
|
|
||||||
if (open && e.key === "Escape") {
|
|
||||||
open = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRecentColor(color) {
|
|
||||||
if (swatches.length === 12) {
|
|
||||||
swatches.splice(0, 1)
|
|
||||||
}
|
|
||||||
if (!swatches.includes(color)) {
|
|
||||||
swatches = [...swatches, color]
|
|
||||||
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertAndSetHSVA() {
|
|
||||||
let hsva = convertToHSVA(value, format)
|
|
||||||
setHSVA(hsva)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setHSVA([hue, sat, val, alpha]) {
|
|
||||||
h = hue
|
|
||||||
s = sat
|
|
||||||
v = val
|
|
||||||
a = alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
//fired by choosing a color from the palette
|
|
||||||
function setSaturationAndValue({ detail }) {
|
|
||||||
s = detail.s
|
|
||||||
v = detail.v
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setHue({ color, isDrag }) {
|
|
||||||
h = color
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
if (!isDrag) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAlpha({ color, isDrag }) {
|
|
||||||
a = color === "1.00" ? "1" : color
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
if (!isDrag) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchValue() {
|
|
||||||
dispatch("change", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeFormatAndConvert(f) {
|
|
||||||
format = f
|
|
||||||
value = convertHsvaToFormat([h, s, v, a], format)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleColorInput(text) {
|
|
||||||
let format = getColorFormat(text)
|
|
||||||
if (format) {
|
|
||||||
value = text
|
|
||||||
convertAndSetHSVA()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchInputChange() {
|
|
||||||
if (format) {
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSwatch() {
|
|
||||||
if (format) {
|
|
||||||
dispatch("addswatch", value)
|
|
||||||
setRecentColor(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeSwatch(idx) {
|
|
||||||
let removedSwatch = swatches.splice(idx, 1)
|
|
||||||
swatches = swatches
|
|
||||||
dispatch("removeswatch", removedSwatch)
|
|
||||||
localStorage.setItem("cp:recent-colors", JSON.stringify(swatches))
|
|
||||||
}
|
|
||||||
|
|
||||||
function applySwatch(color) {
|
|
||||||
if (value !== color) {
|
|
||||||
format = getColorFormat(color)
|
|
||||||
if (format) {
|
|
||||||
value = color
|
|
||||||
convertAndSetHSVA()
|
|
||||||
dispatchValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: border = v > 90 && s < 5 ? "1px dashed #dedada" : ""
|
|
||||||
$: selectedColorStyle = buildStyle({ background: value, border })
|
|
||||||
$: shrink = swatches.length > 0
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Portal>
|
|
||||||
<div
|
|
||||||
class="colorpicker-container"
|
|
||||||
transition:fade
|
|
||||||
bind:this={colorPicker}
|
|
||||||
{style}
|
|
||||||
tabindex="0"
|
|
||||||
on:keydown={handleEscape}
|
|
||||||
bind:clientHeight={pickerHeight}
|
|
||||||
bind:clientWidth={pickerWidth}>
|
|
||||||
|
|
||||||
<div class="palette-panel">
|
|
||||||
<Palette on:change={setSaturationAndValue} {h} {s} {v} {a} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-panel">
|
|
||||||
<div class="alpha-hue-panel">
|
|
||||||
<div>
|
|
||||||
<CheckedBackground borderRadius="50%" backgroundSize="8px">
|
|
||||||
<div class="selected-color" style={selectedColorStyle} />
|
|
||||||
</CheckedBackground>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Slider
|
|
||||||
type="hue"
|
|
||||||
value={h}
|
|
||||||
on:change={hue => setHue(hue.detail)}
|
|
||||||
on:dragend={dispatchValue} />
|
|
||||||
|
|
||||||
<CheckedBackground borderRadius="10px" backgroundSize="7px">
|
|
||||||
<Slider
|
|
||||||
type="alpha"
|
|
||||||
value={a}
|
|
||||||
on:change={(alpha, isDrag) => setAlpha(alpha.detail, isDrag)}
|
|
||||||
on:dragend={dispatchValue} />
|
|
||||||
</CheckedBackground>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if !disableSwatches}
|
|
||||||
<div transition:fade class="swatch-panel">
|
|
||||||
{#if swatches.length > 0}
|
|
||||||
{#each swatches as color, idx}
|
|
||||||
<Swatch
|
|
||||||
{color}
|
|
||||||
on:click={() => applySwatch(color)}
|
|
||||||
on:removeswatch={() => removeSwatch(idx)} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#if swatches.length !== 12}
|
|
||||||
<div
|
|
||||||
bind:this={adder}
|
|
||||||
transition:fade
|
|
||||||
class="adder"
|
|
||||||
on:click={addSwatch}
|
|
||||||
class:shrink>
|
|
||||||
<span>+</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="format-input-panel">
|
|
||||||
<ButtonGroup {format} onclick={changeFormatAndConvert} />
|
|
||||||
<Input
|
|
||||||
{value}
|
|
||||||
on:input={event => handleColorInput(event.target.value)}
|
|
||||||
on:change={dispatchInputChange} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Portal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.colorpicker-container {
|
|
||||||
position: absolute;
|
|
||||||
outline: none;
|
|
||||||
z-index: 3;
|
|
||||||
display: flex;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 400;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 5px 0px;
|
|
||||||
height: auto;
|
|
||||||
width: 220px;
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: 0 0.15em 1.5em 0 rgba(0, 0, 0, 0.1),
|
|
||||||
0 0 1em 0 rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-panel {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-panel {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 8px;
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #d2d2d2;
|
|
||||||
color: #777373;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alpha-hue-panel {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 25px 1fr;
|
|
||||||
grid-gap: 15px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-color {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.swatch-panel {
|
|
||||||
flex: 0 0 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
padding: 0 5px;
|
|
||||||
max-height: 56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.adder {
|
|
||||||
flex: 1;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
|
||||||
transition: flex 0.5s;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #f1f3f4;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #d4d4d4;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-top: 3px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shrink {
|
|
||||||
flex: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.format-input-panel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
padding-top: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,157 +0,0 @@
|
||||||
<script>
|
|
||||||
import Colorpicker from "./Colorpicker.svelte"
|
|
||||||
import CheckedBackground from "./CheckedBackground.svelte"
|
|
||||||
import { createEventDispatcher, beforeUpdate } from "svelte"
|
|
||||||
|
|
||||||
import { buildStyle } from "./helpers.js"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import { getColorFormat } from "./utils.js"
|
|
||||||
|
|
||||||
export let value = "#3ec1d3ff"
|
|
||||||
export let swatches = []
|
|
||||||
export let disableSwatches = false
|
|
||||||
export let open = false
|
|
||||||
export let width = "25px"
|
|
||||||
export let height = "25px"
|
|
||||||
|
|
||||||
let format = "hexa"
|
|
||||||
let dimensions = { top: 0, bottom: 0, right: 0, left: 0 }
|
|
||||||
let positionSide = "top"
|
|
||||||
let colorPreview = null
|
|
||||||
|
|
||||||
let previewHeight = null
|
|
||||||
let previewWidth = null
|
|
||||||
let pickerWidth = 0
|
|
||||||
let pickerHeight = 0
|
|
||||||
|
|
||||||
let errorMsg = null
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
beforeUpdate(() => {
|
|
||||||
format = getColorFormat(value)
|
|
||||||
if (!format) {
|
|
||||||
errorMsg = `Colorpicker - ${value} is an unknown color format. Please use a hex, rgb or hsl value`
|
|
||||||
console.error(errorMsg)
|
|
||||||
} else {
|
|
||||||
errorMsg = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function openColorpicker(event) {
|
|
||||||
if (colorPreview) {
|
|
||||||
open = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onColorChange(color) {
|
|
||||||
value = color.detail
|
|
||||||
dispatch("change", color.detail)
|
|
||||||
}
|
|
||||||
|
|
||||||
$: if (open && colorPreview) {
|
|
||||||
const {
|
|
||||||
top: spaceAbove,
|
|
||||||
width,
|
|
||||||
bottom,
|
|
||||||
right,
|
|
||||||
left,
|
|
||||||
} = colorPreview.getBoundingClientRect()
|
|
||||||
|
|
||||||
const spaceBelow = window.innerHeight - bottom
|
|
||||||
const previewCenter = previewWidth / 2
|
|
||||||
|
|
||||||
let y, x
|
|
||||||
|
|
||||||
if (spaceAbove > spaceBelow) {
|
|
||||||
positionSide = "bottom"
|
|
||||||
y = window.innerHeight - spaceAbove
|
|
||||||
} else {
|
|
||||||
positionSide = "top"
|
|
||||||
y = bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
x = left + previewCenter - pickerWidth / 2
|
|
||||||
|
|
||||||
dimensions = { [positionSide]: y.toFixed(1), left: x.toFixed(1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
$: previewStyle = buildStyle({ width, height, background: value })
|
|
||||||
$: errorPreviewStyle = buildStyle({ width, height })
|
|
||||||
$: pickerStyle = buildStyle({
|
|
||||||
[positionSide]: `${dimensions[positionSide]}px`,
|
|
||||||
left: `${dimensions.left}px`,
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="color-preview-container">
|
|
||||||
{#if !errorMsg}
|
|
||||||
<CheckedBackground borderRadius="3px" backgroundSize="8px">
|
|
||||||
<div
|
|
||||||
bind:this={colorPreview}
|
|
||||||
bind:clientHeight={previewHeight}
|
|
||||||
bind:clientWidth={previewWidth}
|
|
||||||
class="color-preview"
|
|
||||||
style={previewStyle}
|
|
||||||
on:click={openColorpicker} />
|
|
||||||
</CheckedBackground>
|
|
||||||
|
|
||||||
{#if open}
|
|
||||||
<Colorpicker
|
|
||||||
style={pickerStyle}
|
|
||||||
on:change={onColorChange}
|
|
||||||
on:addswatch
|
|
||||||
on:removeswatch
|
|
||||||
bind:format
|
|
||||||
bind:value
|
|
||||||
bind:pickerHeight
|
|
||||||
bind:pickerWidth
|
|
||||||
bind:open
|
|
||||||
{swatches}
|
|
||||||
{disableSwatches} />
|
|
||||||
<div on:click|self={() => (open = false)} class="overlay" />
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div class="color-preview preview-error" style={errorPreviewStyle}>
|
|
||||||
<span>×</span>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.color-preview-container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
height: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-preview {
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #dedada;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-error {
|
|
||||||
background: #cccccc;
|
|
||||||
color: #808080;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .picker-container {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 3;
|
|
||||||
width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,37 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
|
|
||||||
export let target = document.body
|
|
||||||
|
|
||||||
let targetEl
|
|
||||||
let portal
|
|
||||||
let componentInstance
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (typeof target === "string") {
|
|
||||||
targetEl = document.querySelector(target)
|
|
||||||
// Force exit
|
|
||||||
if (targetEl === null) {
|
|
||||||
return () => {}
|
|
||||||
}
|
|
||||||
} else if (target instanceof HTMLElement) {
|
|
||||||
targetEl = target
|
|
||||||
} else {
|
|
||||||
throw new TypeError(
|
|
||||||
`Unknown target type: ${typeof target}. Allowed types: String (CSS selector), HTMLElement.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
portal = document.createElement("div")
|
|
||||||
targetEl.appendChild(portal)
|
|
||||||
portal.appendChild(componentInstance)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
targetEl.removeChild(portal)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={componentInstance}>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
|
@ -28,8 +28,7 @@
|
||||||
})
|
})
|
||||||
$: dropdown && UIkit.util.on(dropdown, "shown", () => (hidden = false))
|
$: dropdown && UIkit.util.on(dropdown, "shown", () => (hidden = false))
|
||||||
$: noChildrenAllowed =
|
$: noChildrenAllowed =
|
||||||
!component ||
|
!component || !getComponentDefinition($store, component._component).children
|
||||||
getComponentDefinition($store, component._component).children === false
|
|
||||||
$: noPaste = !$store.componentToPaste
|
$: noPaste = !$store.componentToPaste
|
||||||
|
|
||||||
const lastPartOfName = c => (c ? last(c._component.split("/")) : "")
|
const lastPartOfName = c => (c ? last(c._component.split("/")) : "")
|
||||||
|
@ -105,49 +104,14 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateNewIdsForComponent = c =>
|
|
||||||
walkProps(c, p => {
|
|
||||||
p._id = uuid()
|
|
||||||
})
|
|
||||||
|
|
||||||
const storeComponentForCopy = (cut = false) => {
|
const storeComponentForCopy = (cut = false) => {
|
||||||
store.update(s => {
|
// lives in store - also used by drag drop
|
||||||
const copiedComponent = cloneDeep(component)
|
store.storeComponentForCopy(component, cut)
|
||||||
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 => {
|
const pasteComponent = mode => {
|
||||||
store.update(s => {
|
// lives in store - also used by drag drop
|
||||||
if (!s.componentToPaste) return s
|
store.pasteComponent(component, mode)
|
||||||
|
|
||||||
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))
|
|
||||||
regenerateCssForCurrentScreen(s)
|
|
||||||
saveCurrentPreviewItem(s)
|
|
||||||
selectComponent(s, componentToPaste)
|
|
||||||
|
|
||||||
return s
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
CircleIndicator,
|
CircleIndicator,
|
||||||
EventsIcon,
|
EventsIcon,
|
||||||
} from "components/common/Icons/"
|
} from "components/common/Icons/"
|
||||||
import CodeEditor from "./CodeEditor.svelte"
|
|
||||||
import LayoutEditor from "./LayoutEditor.svelte"
|
import LayoutEditor from "./LayoutEditor.svelte"
|
||||||
import EventsEditor from "./EventsEditor"
|
import EventsEditor from "./EventsEditor"
|
||||||
import panelStructure from "./temporaryPanelStructure.js"
|
import panelStructure from "./temporaryPanelStructure.js"
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
<script>
|
|
||||||
import { searchAllComponents } from "./pagesParsing/searchComponents"
|
|
||||||
import { store } from "../builderStore"
|
|
||||||
|
|
||||||
export let onComponentChosen = () => {}
|
|
||||||
|
|
||||||
let phrase = ""
|
|
||||||
|
|
||||||
components = $store.components
|
|
||||||
|
|
||||||
$: filteredComponents = !phrase ? [] : searchAllComponents(components, phrase)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
|
|
||||||
<form on:submit|preventDefault class="uk-search uk-search-large">
|
|
||||||
<span uk-search-icon />
|
|
||||||
<input
|
|
||||||
class="uk-search-input"
|
|
||||||
type="search"
|
|
||||||
placeholder="Based on component..."
|
|
||||||
bind:value={phrase} />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{#each filteredComponents as component}
|
|
||||||
<div class="component" on:click={() => onComponentChosen(component)}>
|
|
||||||
<div class="title">{component.name}</div>
|
|
||||||
<div class="description">{component.description}</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.component {
|
|
||||||
padding: 5px;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0 0 1px 0;
|
|
||||||
border-color: var(--grey-1);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component:hover {
|
|
||||||
background-color: var(--primary10);
|
|
||||||
}
|
|
||||||
|
|
||||||
.component > .title {
|
|
||||||
font-size: 13pt;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.component > .description {
|
|
||||||
font-size: 10pt;
|
|
||||||
color: var(--blue);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -7,9 +7,12 @@
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { ArrowDownIcon, ShapeIcon } from "components/common/Icons/"
|
import { ArrowDownIcon, ShapeIcon } from "components/common/Icons/"
|
||||||
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
|
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
export let screens = []
|
export let screens = []
|
||||||
|
|
||||||
|
const dragDropStore = writable({})
|
||||||
|
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let componentToDelete = ""
|
let componentToDelete = ""
|
||||||
|
|
||||||
|
@ -57,7 +60,8 @@
|
||||||
{#if $store.currentPreviewItem.props._instanceName && $store.currentPreviewItem.props._instanceName === screen.props._instanceName && screen.props._children}
|
{#if $store.currentPreviewItem.props._instanceName && $store.currentPreviewItem.props._instanceName === screen.props._instanceName && screen.props._children}
|
||||||
<ComponentsHierarchyChildren
|
<ComponentsHierarchyChildren
|
||||||
components={screen.props._children}
|
components={screen.props._children}
|
||||||
currentComponent={$store.currentComponentInfo} />
|
currentComponent={$store.currentComponentInfo}
|
||||||
|
{dragDropStore} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
@ -94,10 +98,6 @@
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(svg) {
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate :global(svg) {
|
.rotate :global(svg) {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
export let currentComponent
|
export let currentComponent
|
||||||
export let onSelect = () => {}
|
export let onSelect = () => {}
|
||||||
export let level = 0
|
export let level = 0
|
||||||
|
export let dragDropStore
|
||||||
|
|
||||||
|
let dropUnderComponent
|
||||||
|
let componentToDrop
|
||||||
|
|
||||||
const capitalise = s => s.substring(0, 1).toUpperCase() + s.substring(1)
|
const capitalise = s => s.substring(0, 1).toUpperCase() + s.substring(1)
|
||||||
const get_name = s => (!s ? "" : last(s.split("/")))
|
const get_name = s => (!s ? "" : last(s.split("/")))
|
||||||
|
@ -32,15 +36,92 @@
|
||||||
// Go to correct URL
|
// Go to correct URL
|
||||||
$goto(`./:page/:screen/${path}`)
|
$goto(`./:page/:screen/${path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dragstart = component => e => {
|
||||||
|
e.dataTransfer.dropEffect = "move"
|
||||||
|
dragDropStore.update(s => {
|
||||||
|
s.componentToDrop = component
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragover = (component, index) => e => {
|
||||||
|
const canHaveChildrenButIsEmpty =
|
||||||
|
$store.components[component._component].children &&
|
||||||
|
component._children.length === 0
|
||||||
|
|
||||||
|
e.dataTransfer.dropEffect = "copy"
|
||||||
|
dragDropStore.update(s => {
|
||||||
|
const isBottomHalf = e.offsetY > e.currentTarget.offsetHeight / 2
|
||||||
|
s.targetComponent = component
|
||||||
|
// only allow dropping inside when container type
|
||||||
|
// is empty. If it has children, the user can drag over
|
||||||
|
// it's existing children
|
||||||
|
if (canHaveChildrenButIsEmpty) {
|
||||||
|
if (index === 0) {
|
||||||
|
// when its the first component in the screen,
|
||||||
|
// we divide into 3, so we can paste above, inside or below
|
||||||
|
const pos = e.offsetY / e.currentTarget.offsetHeight
|
||||||
|
if (pos < 0.4) {
|
||||||
|
s.dropPosition = "above"
|
||||||
|
} else if (pos > 0.8) {
|
||||||
|
// purposely giving this the least space as it is often covered
|
||||||
|
// by the component below's "above" space
|
||||||
|
s.dropPosition = "below"
|
||||||
|
} else {
|
||||||
|
s.dropPosition = "inside"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.dropPosition = isBottomHalf ? "below" : "inside"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.dropPosition = isBottomHalf ? "below" : "above"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const drop = () => {
|
||||||
|
if ($dragDropStore.targetComponent !== $dragDropStore.componentToDrop) {
|
||||||
|
store.storeComponentForCopy($dragDropStore.componentToDrop, true)
|
||||||
|
store.pasteComponent(
|
||||||
|
$dragDropStore.targetComponent,
|
||||||
|
$dragDropStore.dropPosition
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dragDropStore.update(s => {
|
||||||
|
s.dropPosition = ""
|
||||||
|
s.targetComponent = null
|
||||||
|
s.componentToDrop = null
|
||||||
|
return s
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{#each components as component, index (component._id)}
|
{#each components as component, index (component._id)}
|
||||||
<li on:click|stopPropagation={() => selectComponent(component)}>
|
<li on:click|stopPropagation={() => selectComponent(component)}>
|
||||||
|
|
||||||
|
{#if $dragDropStore && $dragDropStore.targetComponent === component && $dragDropStore.dropPosition === 'above'}
|
||||||
|
<div
|
||||||
|
on:drop={drop}
|
||||||
|
ondragover="return false"
|
||||||
|
ondragenter="return false"
|
||||||
|
class="budibase__nav-item item drop-item"
|
||||||
|
style="margin-left: {level * 20 + 40}px" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="budibase__nav-item item"
|
class="budibase__nav-item item"
|
||||||
class:selected={currentComponent === component}
|
class:selected={currentComponent === component}
|
||||||
style="padding-left: {level * 20 + 40}px">
|
style="padding-left: {level * 20 + 40}px"
|
||||||
|
draggable={true}
|
||||||
|
on:dragstart={dragstart(component)}
|
||||||
|
on:dragover={dragover(component, index)}
|
||||||
|
on:drop={drop}
|
||||||
|
ondragover="return false"
|
||||||
|
ondragenter="return false">
|
||||||
<div class="nav-item">
|
<div class="nav-item">
|
||||||
<i class="icon ri-arrow-right-circle-line" />
|
<i class="icon ri-arrow-right-circle-line" />
|
||||||
{isScreenslot(component._component) ? 'Screenslot' : component._instanceName}
|
{isScreenslot(component._component) ? 'Screenslot' : component._instanceName}
|
||||||
|
@ -55,8 +136,18 @@
|
||||||
components={component._children}
|
components={component._children}
|
||||||
{currentComponent}
|
{currentComponent}
|
||||||
{onSelect}
|
{onSelect}
|
||||||
|
{dragDropStore}
|
||||||
level={level + 1} />
|
level={level + 1} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if $dragDropStore && $dragDropStore.targetComponent === component && ($dragDropStore.dropPosition === 'inside' || $dragDropStore.dropPosition === 'below')}
|
||||||
|
<div
|
||||||
|
on:drop={drop}
|
||||||
|
ondragover="return false"
|
||||||
|
ondragenter="return false"
|
||||||
|
class="budibase__nav-item item drop-item"
|
||||||
|
style="margin-left: {(level + ($dragDropStore.dropPosition === 'inside' ? 2 : 0)) * 20 + 40}px" />
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -78,6 +169,11 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drop-item {
|
||||||
|
background: var(--blue-light);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
display: none;
|
display: none;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
<script>
|
|
||||||
import PropsView from "./PropsView.svelte"
|
|
||||||
import { store } from "../builderStore"
|
|
||||||
import Textbox from "../common/Textbox.svelte"
|
|
||||||
import Button from "../common/Button.svelte"
|
|
||||||
import { LayoutIcon, PaintIcon, TerminalIcon } from "../common/Icons/"
|
|
||||||
|
|
||||||
import { cloneDeep, join, split, last } from "lodash/fp"
|
|
||||||
import { assign } from "lodash"
|
|
||||||
|
|
||||||
$: component = $store.currentPreviewItem
|
|
||||||
$: componentInfo = $store.currentComponentInfo
|
|
||||||
$: components = $store.components
|
|
||||||
|
|
||||||
const updateComponent = doChange => doChange(cloneDeep(component))
|
|
||||||
|
|
||||||
const onPropsChanged = newProps => {
|
|
||||||
updateComponent(newComponent => assign(newComponent.props, newProps))
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<button>
|
|
||||||
<PaintIcon />
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button>
|
|
||||||
<LayoutIcon />
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button>
|
|
||||||
<TerminalIcon />
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="component-props-container">
|
|
||||||
<PropsView {componentInfo} {onPropsChanged} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title > div:nth-child(1) {
|
|
||||||
grid-column-start: name;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title > div:nth-child(2) {
|
|
||||||
grid-column-start: actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-props-container {
|
|
||||||
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: 13px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -5,7 +5,6 @@
|
||||||
import HandlerSelector from "./HandlerSelector.svelte"
|
import HandlerSelector from "./HandlerSelector.svelte"
|
||||||
import IconButton from "../../common/IconButton.svelte"
|
import IconButton from "../../common/IconButton.svelte"
|
||||||
import ActionButton from "../../common/ActionButton.svelte"
|
import ActionButton from "../../common/ActionButton.svelte"
|
||||||
import PlusButton from "../../common/PlusButton.svelte"
|
|
||||||
import Select from "../../common/Select.svelte"
|
import Select from "../../common/Select.svelte"
|
||||||
import Input from "../../common/Input.svelte"
|
import Input from "../../common/Input.svelte"
|
||||||
import getIcon from "../../common/icon"
|
import getIcon from "../../common/icon"
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
} from "lodash/fp"
|
} from "lodash/fp"
|
||||||
import { pipe } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import Checkbox from "components/common/Checkbox.svelte"
|
import Checkbox from "components/common/Checkbox.svelte"
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import PlusButton from "components/common/PlusButton.svelte"
|
|
||||||
import IconButton from "components/common/IconButton.svelte"
|
import IconButton from "components/common/IconButton.svelte"
|
||||||
import EventEditorModal from "./EventEditorModal.svelte"
|
import EventEditorModal from "./EventEditorModal.svelte"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button } from "@budibase/bbui"
|
import { Button } from "@budibase/bbui"
|
||||||
import IconButton from "components/common/IconButton.svelte"
|
import IconButton from "components/common/IconButton.svelte"
|
||||||
import PlusButton from "components/common/PlusButton.svelte"
|
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import StateBindingCascader from "./StateBindingCascader.svelte"
|
import StateBindingCascader from "./StateBindingCascader.svelte"
|
||||||
import { find, map, keys, reduce, keyBy } from "lodash/fp"
|
import { find, map, keys, reduce, keyBy } from "lodash/fp"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input } from "@budibase/bbui"
|
import { Input } from "@budibase/bbui"
|
||||||
import IconButton from "components/common/IconButton.svelte"
|
import IconButton from "components/common/IconButton.svelte"
|
||||||
import PlusButton from "components/common/PlusButton.svelte"
|
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import { find, map, keys, reduce, keyBy } from "lodash/fp"
|
import { find, map, keys, reduce, keyBy } from "lodash/fp"
|
||||||
import { pipe } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import PlusButton from "../common/PlusButton.svelte"
|
|
||||||
|
|
||||||
export let meta = []
|
export let meta = []
|
||||||
export let size = ""
|
export let size = ""
|
||||||
export let values = []
|
export let values = []
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import PropsView from "./PropsView.svelte"
|
import PropsView from "./PropsView.svelte"
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
import Button from "components/common/Button.svelte"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
export let value = ""
|
|
||||||
export let onChange = value => {}
|
|
||||||
export let options = []
|
|
||||||
export let initialValue = ""
|
|
||||||
export let styleBindingProperty = ""
|
|
||||||
|
|
||||||
const handleStyleBind = value =>
|
|
||||||
!!styleBindingProperty ? { style: `${styleBindingProperty}: ${value}` } : {}
|
|
||||||
|
|
||||||
$: isOptionsObject = options.every(o => typeof o === "object")
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!value && !!initialValue) {
|
|
||||||
value = initialValue
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<select {value} on:change={ev => onChange(ev.target.value)}>
|
|
||||||
{#if isOptionsObject}
|
|
||||||
{#each options as { value, label }}
|
|
||||||
<option {...handleStyleBind(value || label)} value={value || label}>
|
|
||||||
{label}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
{#each options as value}
|
|
||||||
<option {...handleStyleBind(value)} {value}>{value}</option>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</select>
|
|
|
@ -16,12 +16,14 @@
|
||||||
import { pipe } from "components/common/core"
|
import { pipe } from "components/common/core"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { ArrowDownIcon, GridIcon } from "components/common/Icons/"
|
import { ArrowDownIcon, GridIcon } from "components/common/Icons/"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
export let layout
|
export let layout
|
||||||
|
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let componentToDelete = ""
|
let componentToDelete = ""
|
||||||
|
|
||||||
|
const dragDropStore = writable({})
|
||||||
const joinPath = join("/")
|
const joinPath = join("/")
|
||||||
|
|
||||||
const lastPartOfName = c =>
|
const lastPartOfName = c =>
|
||||||
|
@ -57,7 +59,8 @@
|
||||||
<ComponentsHierarchyChildren
|
<ComponentsHierarchyChildren
|
||||||
thisComponent={_layout.component.props}
|
thisComponent={_layout.component.props}
|
||||||
components={_layout.component.props._children}
|
components={_layout.component.props._children}
|
||||||
currentComponent={$store.currentComponentInfo} />
|
currentComponent={$store.currentComponentInfo}
|
||||||
|
{dragDropStore} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -81,10 +84,6 @@
|
||||||
color: var(--grey-7);
|
color: var(--grey-7);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(svg) {
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate :global(svg) {
|
.rotate :global(svg) {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
<script>
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Dropdown from "components/common/Dropdown.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import { isRootComponent } from "./pagesParsing/searchComponents"
|
|
||||||
import { pipe } from "components/common/core"
|
|
||||||
import { filter, find, concat } from "lodash/fp"
|
|
||||||
|
|
||||||
const notSelectedComponent = { name: "(none selected)" }
|
|
||||||
|
|
||||||
$: page = $store.pages[$store.currentPageName]
|
|
||||||
$: title = page.index.title
|
|
||||||
$: components = pipe($store.components, [
|
|
||||||
filter(store => !isRootComponent($store)),
|
|
||||||
concat([notSelectedComponent]),
|
|
||||||
])
|
|
||||||
$: entryComponent = components[page.appBody] || notSelectedComponent
|
|
||||||
|
|
||||||
const save = () => {
|
|
||||||
if (!title || !entryComponent || entryComponent === notSeletedComponent)
|
|
||||||
return
|
|
||||||
const page = {
|
|
||||||
index: {
|
|
||||||
title,
|
|
||||||
},
|
|
||||||
appBody: entryComponent.name,
|
|
||||||
}
|
|
||||||
store.savePage(page)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="root">
|
|
||||||
|
|
||||||
<h3>{$store.currentPageName}</h3>
|
|
||||||
|
|
||||||
<form on:submit|preventDefault class="uk-form-horizontal">
|
|
||||||
<Textbox bind:text={title} label="Title" hasError={!title} />
|
|
||||||
<div class="help-text">
|
|
||||||
The title of your page, displayed in the bowser tab
|
|
||||||
</div>
|
|
||||||
<Dropdown
|
|
||||||
label="App Entry Component"
|
|
||||||
options={components}
|
|
||||||
bind:selected={entryComponent}
|
|
||||||
textMember={v => v.name} />
|
|
||||||
|
|
||||||
<div class="help-text">
|
|
||||||
The component that will be loaded into the body of the page
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 20px" />
|
|
||||||
<Button on:click={save}>Save</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.root {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
.help-text {
|
|
||||||
color: var(--grey-2);
|
|
||||||
font-size: 10pt;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,8 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import Checkbox from "../common/Checkbox.svelte"
|
import Checkbox from "../common/Checkbox.svelte"
|
||||||
import Textbox from "../common/Textbox.svelte"
|
|
||||||
import Dropdown from "../common/Dropdown.svelte"
|
|
||||||
import StateBindingControl from "./StateBindingControl.svelte"
|
import StateBindingControl from "./StateBindingControl.svelte"
|
||||||
|
|
||||||
export let index
|
export let index
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { some, includes, filter } from "lodash/fp"
|
import { some, includes, filter } from "lodash/fp"
|
||||||
import Textbox from "../common/Textbox.svelte"
|
|
||||||
import Dropdown from "../common/Dropdown.svelte"
|
|
||||||
import PropControl from "./PropControl.svelte"
|
import PropControl from "./PropControl.svelte"
|
||||||
import IconButton from "../common/IconButton.svelte"
|
import IconButton from "../common/IconButton.svelte"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import NewScreen from "./NewScreen.svelte"
|
import NewScreen from "./NewScreen.svelte"
|
||||||
import CurrentItemPreview from "./CurrentItemPreview.svelte"
|
import CurrentItemPreview from "./CurrentItemPreview.svelte"
|
||||||
import SettingsView from "./SettingsView.svelte"
|
import SettingsView from "./SettingsView.svelte"
|
||||||
import PageView from "./PageView.svelte"
|
|
||||||
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"
|
import ComponentsPaneSwitcher from "./ComponentsPaneSwitcher.svelte"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { last } from "lodash/fp"
|
import { last } from "lodash/fp"
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const createProps = (componentDefinition, derivedFromProps) => {
|
||||||
assign(props, derivedFromProps)
|
assign(props, derivedFromProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentDefinition.children !== false && isUndefined(props._children)) {
|
if (isUndefined(props._children)) {
|
||||||
props._children = []
|
props._children = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,20 @@ export const layout = [
|
||||||
{ label: "no wrap", value: "noWrap" },
|
{ label: "no wrap", value: "noWrap" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Gap",
|
||||||
|
key: "gap",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: [
|
||||||
|
{ label: "None", value: "0px" },
|
||||||
|
{ label: "4px", value: "4px" },
|
||||||
|
{ label: "8px", value: "8px" },
|
||||||
|
{ label: "16px", value: "16px" },
|
||||||
|
{ label: "20px", value: "20px" },
|
||||||
|
{ label: "32px", value: "32px" },
|
||||||
|
{ label: "64px", value: "64px" },
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const margin = [
|
export const margin = [
|
||||||
|
|
|
@ -312,41 +312,171 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_component: "@budibase/materialdesign-components/BasicCard",
|
|
||||||
name: "Card",
|
name: "Card",
|
||||||
description:
|
description: "Card components",
|
||||||
"A basic card component that can contain content and actions.",
|
|
||||||
icon: "ri-layout-bottom-line",
|
icon: "ri-layout-bottom-line",
|
||||||
children: [],
|
commonProps: {},
|
||||||
properties: {
|
children: [
|
||||||
design: { ...all },
|
{
|
||||||
settings: [
|
_component: "@budibase/standard-components/card",
|
||||||
{
|
name: "Vertical",
|
||||||
label: "Heading",
|
description:
|
||||||
key: "heading",
|
"A basic card component that can contain content and actions.",
|
||||||
control: Input,
|
icon: "ri-layout-column-line",
|
||||||
placeholder: "text",
|
children: [],
|
||||||
|
properties: {
|
||||||
|
design: { ...all },
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
label: "Image",
|
||||||
|
key: "imageUrl",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Image",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Heading",
|
||||||
|
key: "heading",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Heading",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Description",
|
||||||
|
key: "description",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Text",
|
||||||
|
key: "linkText",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link Text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Url",
|
||||||
|
key: "linkUrl",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Color",
|
||||||
|
key: "color",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link Color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hover Color",
|
||||||
|
key: "linkHoverColor",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Hover Color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Image Height",
|
||||||
|
key: "imageHeight",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: ["12rem", "16rem", "20rem", "24rem"],
|
||||||
|
placeholder: "Image Height",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Card Width",
|
||||||
|
key: "cardWidth",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: ["16rem", "20rem", "24rem"],
|
||||||
|
placeholder: "Card Width",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
label: "Subheading",
|
{
|
||||||
key: "subheading",
|
_component: "@budibase/standard-components/cardhorizontal",
|
||||||
control: Input,
|
name: "Horizontal",
|
||||||
placeholder: "text",
|
description:
|
||||||
|
"A basic card component that can contain content and actions.",
|
||||||
|
icon: "ri-layout-row-line",
|
||||||
|
children: [],
|
||||||
|
properties: {
|
||||||
|
design: { ...all },
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
label: "Image",
|
||||||
|
key: "imageUrl",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Image",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Heading",
|
||||||
|
key: "heading",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Heading",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Description",
|
||||||
|
key: "description",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Subtext",
|
||||||
|
key: "subtext",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Subtext",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Text",
|
||||||
|
key: "linkText",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link Text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Url",
|
||||||
|
key: "linkUrl",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link URL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Link Color",
|
||||||
|
key: "color",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Link Color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hover Color",
|
||||||
|
key: "linkHoverColor",
|
||||||
|
control: Input,
|
||||||
|
placeholder: "Hover Color",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Card Width",
|
||||||
|
key: "cardWidth",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: [
|
||||||
|
"24rem",
|
||||||
|
"28rem",
|
||||||
|
"32rem",
|
||||||
|
"40rem",
|
||||||
|
"48rem",
|
||||||
|
"60rem",
|
||||||
|
"100%",
|
||||||
|
],
|
||||||
|
placeholder: "Card Height",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Image Width",
|
||||||
|
key: "imageWidth",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: ["8rem", "12rem", "16rem"],
|
||||||
|
placeholder: "Image Width",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Image Height",
|
||||||
|
key: "imageHeight",
|
||||||
|
control: OptionSelect,
|
||||||
|
options: ["8rem", "12rem", "16rem", "auto"],
|
||||||
|
placeholder: "Image Height",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
label: "Content",
|
],
|
||||||
key: "content",
|
|
||||||
control: Input,
|
|
||||||
placeholder: "text",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Image",
|
|
||||||
key: "imageUrl",
|
|
||||||
control: Input,
|
|
||||||
placeholder: "src",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Table",
|
name: "Table",
|
||||||
|
@ -396,11 +526,6 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
template: {
|
|
||||||
component: "@budibase/materialdesign-components/Form",
|
|
||||||
description: "Form for saving a record",
|
|
||||||
name: "@budibase/materialdesign-components/recordForm",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_component: "@budibase/standard-components/dataformwide",
|
_component: "@budibase/standard-components/dataformwide",
|
||||||
|
@ -429,38 +554,6 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Data Chart",
|
|
||||||
_component: "@budibase/standard-components/datachart",
|
|
||||||
description: "Shiny chart",
|
|
||||||
icon: "ri-bar-chart-line",
|
|
||||||
properties: {
|
|
||||||
design: { ...all },
|
|
||||||
settings: [
|
|
||||||
{ label: "Table", key: "model", control: ModelSelect },
|
|
||||||
{
|
|
||||||
label: "Chart Type",
|
|
||||||
key: "type",
|
|
||||||
control: OptionSelect,
|
|
||||||
options: [
|
|
||||||
"column2d",
|
|
||||||
"column3d",
|
|
||||||
"line",
|
|
||||||
"area2d",
|
|
||||||
"bar2d",
|
|
||||||
"bar3d",
|
|
||||||
"pie2d",
|
|
||||||
"pie3d",
|
|
||||||
"doughnut2d",
|
|
||||||
"doughnut3d",
|
|
||||||
"pareto2d",
|
|
||||||
"pareto3d",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Chart",
|
name: "Chart",
|
||||||
description: "Shiny chart",
|
description: "Shiny chart",
|
||||||
|
@ -920,35 +1013,6 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Data List",
|
|
||||||
_component: "@budibase/standard-components/datalist",
|
|
||||||
description: "Shiny list",
|
|
||||||
icon: "ri-file-list-fill",
|
|
||||||
properties: {
|
|
||||||
design: { ...all },
|
|
||||||
settings: [{ label: "Table", key: "model", control: ModelSelect }],
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "List",
|
|
||||||
_component: "@budibase/standard-components/list",
|
|
||||||
description: "Renders all children once per record, of a given table",
|
|
||||||
icon: "ri-file-list-fill",
|
|
||||||
properties: {
|
|
||||||
design: { ...all },
|
|
||||||
settings: [{ label: "Table", key: "model", control: ModelSelect }],
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
_component: "@budibase/standard-components/heading",
|
|
||||||
name: "Headline",
|
|
||||||
description: "A component for displaying heading text",
|
|
||||||
icon: "ri-heading",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// {
|
// {
|
||||||
// name: "Data List",
|
// name: "Data List",
|
||||||
// _component: "@budibase/standard-components/datalist",
|
// _component: "@budibase/standard-components/datalist",
|
||||||
|
|
|
@ -226,7 +226,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
width: 260px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import * as api from "components/database/ModelDataTable/api"
|
import * as api from "components/database/ModelDataTable/api"
|
||||||
|
|
||||||
import ModelNavigator from "components/nav/ModelNavigator/ModelNavigator.svelte"
|
import ModelNavigator from "components/nav/ModelNavigator/ModelNavigator.svelte"
|
||||||
import { ModelSetupNav } from "components/nav/ModelSetupNav"
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
import ComponentsHierarchyChildren from "components/userInterface/ComponentsHierarchyChildren.svelte"
|
import ComponentsHierarchyChildren from "components/userInterface/ComponentsHierarchyChildren.svelte"
|
||||||
import IconButton from "components/common/IconButton.svelte"
|
import IconButton from "components/common/IconButton.svelte"
|
||||||
import CurrentItemPreview from "components/userInterface/AppPreview"
|
import CurrentItemPreview from "components/userInterface/AppPreview"
|
||||||
import PageView from "components/userInterface/PageView.svelte"
|
|
||||||
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
|
||||||
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
|
||||||
import Switcher from "components/common/Switcher.svelte"
|
import Switcher from "components/common/Switcher.svelte"
|
||||||
|
|
|
@ -2,17 +2,11 @@
|
||||||
import Modal from "svelte-simple-modal"
|
import Modal from "svelte-simple-modal"
|
||||||
import { Home as Link } from "@budibase/bbui"
|
import { Home as Link } from "@budibase/bbui"
|
||||||
import {
|
import {
|
||||||
SettingsIcon,
|
|
||||||
AppsIcon,
|
AppsIcon,
|
||||||
UpdatesIcon,
|
|
||||||
HostingIcon,
|
HostingIcon,
|
||||||
DocumentationIcon,
|
DocumentationIcon,
|
||||||
TutorialsIcon,
|
|
||||||
CommunityIcon,
|
CommunityIcon,
|
||||||
ContributionIcon,
|
|
||||||
BugIcon,
|
BugIcon,
|
||||||
EmailIcon,
|
|
||||||
TwitterIcon,
|
|
||||||
} from "components/common/Icons/"
|
} from "components/common/Icons/"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -24,16 +18,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav-section">
|
<div class="nav-section">
|
||||||
<div class="nav-section-title">Build</div>
|
|
||||||
<Link icon={AppsIcon} title="Apps" href="/" active />
|
<Link icon={AppsIcon} title="Apps" href="/" active />
|
||||||
<Link
|
<Link
|
||||||
icon={HostingIcon}
|
icon={HostingIcon}
|
||||||
title="Hosting"
|
title="Hosting"
|
||||||
href="https://portal.budi.live/" />
|
href="https://portal.budi.live/" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-section">
|
|
||||||
<div class="nav-section-title">Learn</div>
|
|
||||||
<Link
|
<Link
|
||||||
icon={DocumentationIcon}
|
icon={DocumentationIcon}
|
||||||
title="Documentation"
|
title="Documentation"
|
||||||
|
@ -42,23 +31,11 @@
|
||||||
icon={CommunityIcon}
|
icon={CommunityIcon}
|
||||||
title="Community"
|
title="Community"
|
||||||
href="https://forum.budibase.com/" />
|
href="https://forum.budibase.com/" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-section">
|
|
||||||
<div class="nav-section-title">Contact</div>
|
|
||||||
<Link
|
|
||||||
icon={ContributionIcon}
|
|
||||||
title="Contribute"
|
|
||||||
href="https://github.com/Budibase/budibase" />
|
|
||||||
<Link
|
<Link
|
||||||
icon={BugIcon}
|
icon={BugIcon}
|
||||||
title="Report bug"
|
title="Raise an issue"
|
||||||
href="https://github.com/Budibase/budibase/issues" />
|
href="https://github.com/Budibase/budibase" />
|
||||||
<Link icon={EmailIcon} title="Email" href="mailto:hi@budibase.com" />
|
|
||||||
<Link
|
|
||||||
icon={TwitterIcon}
|
|
||||||
title="Twitter"
|
|
||||||
href="https://twitter.com/budibase" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -88,6 +65,7 @@
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border-right: var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-logo {
|
.home-logo {
|
||||||
|
@ -105,11 +83,4 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-section-title {
|
|
||||||
font-size: 20px;
|
|
||||||
color: var(--ink);
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -66,7 +66,9 @@
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="welcome">Welcome to the Budibase Beta</div>
|
<div class="welcome">Welcome to the Budibase Beta</div>
|
||||||
<Button purple large on:click={showCreateAppModal}>Create New Web App</Button>
|
<Button primary purple on:click={showCreateAppModal}>
|
||||||
|
Create New Web App
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
|
@ -95,9 +97,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.welcome {
|
.welcome {
|
||||||
font-size: 42px;
|
font-size: var(--font-size-3xl);
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner {
|
.banner {
|
||||||
|
@ -108,7 +110,7 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: white;
|
||||||
margin: 20px 80px 40px 80px;
|
margin: 20px 80px 40px 80px;
|
||||||
border-radius: 10px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.banner img {
|
.banner img {
|
||||||
|
|
|
@ -72,16 +72,6 @@ describe("createDefaultProps", () => {
|
||||||
expect(props._children).toEqual([])
|
expect(props._children).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a object without _children array, when children===false ", () => {
|
|
||||||
const comp = getcomponent()
|
|
||||||
comp.children = false
|
|
||||||
|
|
||||||
const { props, errors } = createProps(comp)
|
|
||||||
|
|
||||||
expect(errors).toEqual([])
|
|
||||||
expect(props._children).not.toBeDefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should create a object with single empty array, when prop definition is 'event' ", () => {
|
it("should create a object with single empty array, when prop definition is 'event' ", () => {
|
||||||
const comp = getcomponent()
|
const comp = getcomponent()
|
||||||
comp.props.onClick = "event"
|
comp.props.onClick = "event"
|
||||||
|
@ -104,24 +94,22 @@ describe("createDefaultProps", () => {
|
||||||
expect(props._children).toEqual([])
|
expect(props._children).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a _children array when children not defined ", () => {
|
it("should always create _children ", () => {
|
||||||
const comp = getcomponent()
|
|
||||||
|
|
||||||
const { props, errors } = createProps(comp)
|
|
||||||
|
|
||||||
expect(errors).toEqual([])
|
|
||||||
expect(props._children).toBeDefined()
|
|
||||||
expect(props._children).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should not create _children array when children=false ", () => {
|
|
||||||
const comp = getcomponent()
|
const comp = getcomponent()
|
||||||
comp.children = false
|
comp.children = false
|
||||||
|
|
||||||
const { props, errors } = createProps(comp)
|
const createRes1 = createProps(comp)
|
||||||
|
|
||||||
expect(errors).toEqual([])
|
expect(createRes1.errors).toEqual([])
|
||||||
expect(props._children).not.toBeDefined()
|
expect(createRes1.props._children).toBeDefined()
|
||||||
|
|
||||||
|
const comp2 = getcomponent()
|
||||||
|
comp2.children = true
|
||||||
|
|
||||||
|
const createRes2 = createProps(comp)
|
||||||
|
|
||||||
|
expect(createRes2.errors).toEqual([])
|
||||||
|
expect(createRes2.props._children).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create an object with multiple prop names", () => {
|
it("should create an object with multiple prop names", () => {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,6 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/standard-components": "0.x",
|
"@budibase/standard-components": "0.x"
|
||||||
"@budibase/materialdesign-components": "0.x"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"title": "Test App",
|
"title": "Test App",
|
||||||
"favicon": "./_shared/favicon.png",
|
"favicon": "./_shared/favicon.png",
|
||||||
"stylesheets": [],
|
"stylesheets": [],
|
||||||
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
|
"componentLibraries": ["@budibase/standard-components"],
|
||||||
"props" : {
|
"props" : {
|
||||||
"_component": "@budibase/standard-components/container",
|
"_component": "@budibase/standard-components/container",
|
||||||
"_children": [],
|
"_children": [],
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"title": "Test App",
|
"title": "Test App",
|
||||||
"favicon": "./_shared/favicon.png",
|
"favicon": "./_shared/favicon.png",
|
||||||
"stylesheets": [],
|
"stylesheets": [],
|
||||||
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
|
"componentLibraries": ["@budibase/standard-components"],
|
||||||
"props" : {
|
"props" : {
|
||||||
"_component": "@budibase/standard-components/container",
|
"_component": "@budibase/standard-components/container",
|
||||||
"_children": [],
|
"_children": [],
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"title": "Test App",
|
"title": "Test App",
|
||||||
"favicon": "./_shared/favicon.png",
|
"favicon": "./_shared/favicon.png",
|
||||||
"stylesheets": [],
|
"stylesheets": [],
|
||||||
"componentLibraries": ["@budibase/standard-components", "@budibase/materialdesign-components"],
|
"componentLibraries": ["@budibase/standard-components" ],
|
||||||
"props" : {
|
"props" : {
|
||||||
"_component": "@budibase/standard-components/container",
|
"_component": "@budibase/standard-components/container",
|
||||||
"_children": [],
|
"_children": [],
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
dist/*
|
|
||||||
public/build
|
|
|
@ -1,3 +0,0 @@
|
||||||
*
|
|
||||||
!dist/*
|
|
||||||
!components.json
|
|
|
@ -1,33 +0,0 @@
|
||||||
*Psst — looking for an app template? Go here --> [sveltejs/template](https://github.com/sveltejs/template)*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# component-template
|
|
||||||
|
|
||||||
A base for building shareable Svelte components. Clone it with [degit](https://github.com/Rich-Harris/degit):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx degit sveltejs/component-template my-new-component
|
|
||||||
cd my-new-component
|
|
||||||
npm install # or yarn
|
|
||||||
```
|
|
||||||
|
|
||||||
Your component's source code lives in `src/index.html`.
|
|
||||||
|
|
||||||
TODO
|
|
||||||
|
|
||||||
* [ ] some firm opinions about the best way to test components
|
|
||||||
* [ ] update `degit` so that it automates some of the setup work
|
|
||||||
|
|
||||||
|
|
||||||
## Setting up
|
|
||||||
|
|
||||||
* Run `npm init` (or `yarn init`)
|
|
||||||
* Replace this README with your own
|
|
||||||
|
|
||||||
|
|
||||||
## Consuming components
|
|
||||||
|
|
||||||
Your package.json has a `"svelte"` field pointing to `src/index.html`, which allows Svelte apps to import the source code directly, if they are using a bundler plugin like [rollup-plugin-svelte](https://github.com/rollup/rollup-plugin-svelte) or [svelte-loader](https://github.com/sveltejs/svelte-loader) (where [`resolve.mainFields`](https://webpack.js.org/configuration/resolve/#resolve-mainfields) in your webpack config includes `"svelte"`). **This is recommended.**
|
|
||||||
|
|
||||||
For everyone else, `npm run build` will bundle your component's source code into a plain JavaScript module (`index.mjs`) and a UMD script (`index.js`). This will happen automatically when you publish your component to npm, courtesy of the `prepublishOnly` hook in package.json.
|
|
|
@ -1,458 +0,0 @@
|
||||||
{
|
|
||||||
"_lib": "./dist/index.js",
|
|
||||||
"_templates": {
|
|
||||||
"indexDatatable": {
|
|
||||||
"description": "Datatable based on an Index",
|
|
||||||
"component": "Datatable"
|
|
||||||
},
|
|
||||||
"recordForm": {
|
|
||||||
"description": "Form for saving a record",
|
|
||||||
"component": "Form"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Body1": {
|
|
||||||
"name": "Body1",
|
|
||||||
"description": "Sets the font properties as Roboto Body 1",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Body2": {
|
|
||||||
"name": "Body2",
|
|
||||||
"description": "Sets the font properties as Roboto Body 2",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Select": {
|
|
||||||
"name": "Select",
|
|
||||||
"description": "A material design select (aka Dropdown, aka Combobox)",
|
|
||||||
"props": {
|
|
||||||
"onSelect": "event",
|
|
||||||
"value": "string",
|
|
||||||
"width": "string",
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": [
|
|
||||||
"filled", "outlined"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"disabled": "bool",
|
|
||||||
"required": "bool",
|
|
||||||
"label": "string",
|
|
||||||
"helperText": "string",
|
|
||||||
"persistent": "bool"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"List": {
|
|
||||||
"name": "List",
|
|
||||||
"description": "A Material Design List Component.",
|
|
||||||
"props": {
|
|
||||||
"onSelect": "event",
|
|
||||||
"singleSelection": "bool",
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["one-line", "two-line"],
|
|
||||||
"default": "one-line"
|
|
||||||
},
|
|
||||||
"inputElement": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["None", "Radiobutton", "Checkbox"],
|
|
||||||
"default": "None"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ListItem": {
|
|
||||||
"name": "ListItem",
|
|
||||||
"description": "Use as item in a 'List' or 'Select' component",
|
|
||||||
"props": {
|
|
||||||
"value": "string",
|
|
||||||
"text": "string",
|
|
||||||
"secondaryText": "string",
|
|
||||||
"leadingIcon": "string",
|
|
||||||
"trailingIcon": "string",
|
|
||||||
"disabled": "bool",
|
|
||||||
"dividerAfter": "bool"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Button": {
|
|
||||||
"name": "Button",
|
|
||||||
"children": false,
|
|
||||||
"description": "A Material Design button with different variations. It renders as an anchor if href is passed to it.",
|
|
||||||
"props": {
|
|
||||||
"onClick": "event",
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["text", "raised", "unelevated", "outlined"],
|
|
||||||
"default": "text"
|
|
||||||
},
|
|
||||||
"colour": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["primary", "secondary"],
|
|
||||||
"default": "primary"
|
|
||||||
},
|
|
||||||
"size": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["small", "medium", "large"],
|
|
||||||
"default": "medium"
|
|
||||||
},
|
|
||||||
"href": "string",
|
|
||||||
"icon": "string",
|
|
||||||
"trailingIcon": "bool",
|
|
||||||
"fullwidth": "bool",
|
|
||||||
"text": "string",
|
|
||||||
"disabled": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Caption": {
|
|
||||||
"name": "Caption",
|
|
||||||
"description": "Sets the font properties as Roboto Caption",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"BasicCard": {
|
|
||||||
"name": "BasicCard",
|
|
||||||
"description": "This is a basic card",
|
|
||||||
"props": {
|
|
||||||
"heading": "string",
|
|
||||||
"subheading": "string",
|
|
||||||
"content": "string",
|
|
||||||
"imageUrl": "string",
|
|
||||||
"button1Text": "string",
|
|
||||||
"button2Text": "string",
|
|
||||||
"cardClick": "event",
|
|
||||||
"button1Click": "event",
|
|
||||||
"button2Click": "event"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Card": {
|
|
||||||
"name": "Card",
|
|
||||||
"description": "A Material Card container. Accepts CardHeader, CardBody and CardFooter as possible children",
|
|
||||||
"props": {
|
|
||||||
"width": {"type": "string", "default": "350px"},
|
|
||||||
"height": "string",
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["standard", "outlined"],
|
|
||||||
"default": "outlined"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CardBody": {
|
|
||||||
"name": "CardBody",
|
|
||||||
"description": "A Material CardBody component. Contains the main content of a Material Card component",
|
|
||||||
"props": {
|
|
||||||
"onClick": "event"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CardImage": {
|
|
||||||
"name": "CardImage",
|
|
||||||
"description": "An image component for the Material Card component",
|
|
||||||
"props": {
|
|
||||||
"displayHorizontal": "bool",
|
|
||||||
"url": "string",
|
|
||||||
"title": "string",
|
|
||||||
"subtitle": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CardHeader": {
|
|
||||||
"name": "CardHeader",
|
|
||||||
"description": "Displays a icon, title and subtitle above main body of the Material Card component",
|
|
||||||
"props": {
|
|
||||||
"title": "string",
|
|
||||||
"subtitle": "string",
|
|
||||||
"icon": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CardFooter": {
|
|
||||||
"name": "CardFooter",
|
|
||||||
"description": "Displays buttons / icon buttons as actions for the Material Card component",
|
|
||||||
"props": {
|
|
||||||
"padding": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Checkbox": {
|
|
||||||
"name": "Checkbox",
|
|
||||||
"description": "A Material Design checkbox. Supports aligning label before or after checkbox.",
|
|
||||||
"props": {
|
|
||||||
"onClick": "event",
|
|
||||||
"id": "string",
|
|
||||||
"label": "string",
|
|
||||||
"disabled": "bool",
|
|
||||||
"alignEnd": "bool",
|
|
||||||
"indeterminate": "bool",
|
|
||||||
"checked": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Checkboxgroup": {
|
|
||||||
"name": "Checkboxgroup",
|
|
||||||
"description": "A group of material design checkboxes. Supports row and column orientation.",
|
|
||||||
"props": {
|
|
||||||
"onChange": "event",
|
|
||||||
"label":"string",
|
|
||||||
"orientation": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["row", "column"],
|
|
||||||
"default": "row"
|
|
||||||
},
|
|
||||||
"disabled": "bool",
|
|
||||||
"alignEnd": "bool"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Datatable": {
|
|
||||||
"name": "Datatable",
|
|
||||||
"description": "A Material Design component to represent tabular data.",
|
|
||||||
"props": {
|
|
||||||
"onLoad":"event"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"DatatableHead": {
|
|
||||||
"name": "DatatableHead",
|
|
||||||
"description": "Material Design <thead>.",
|
|
||||||
"props": {}
|
|
||||||
},
|
|
||||||
"DatatableCell": {
|
|
||||||
"name": "DatatableCell",
|
|
||||||
"description": "Material Design <td>.",
|
|
||||||
"props": {}
|
|
||||||
},
|
|
||||||
"DatatableBody": {
|
|
||||||
"name": "DatatableBody",
|
|
||||||
"description": "Material Design <tbody>.",
|
|
||||||
"props": {}
|
|
||||||
},
|
|
||||||
"DatatableRow": {
|
|
||||||
"name": "DatatableRow",
|
|
||||||
"description": "Material Design <tr>.",
|
|
||||||
"props": {}
|
|
||||||
},
|
|
||||||
"DatePicker": {
|
|
||||||
"name": "DatePicker",
|
|
||||||
"description": "Material Design DatePicker",
|
|
||||||
"props": {
|
|
||||||
"date": "string",
|
|
||||||
"label": "string",
|
|
||||||
"onSelect": "event"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H1": {
|
|
||||||
"name": "H1",
|
|
||||||
"description": "Sets the font properties as Roboto Headline1",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H2": {
|
|
||||||
"name": "H2",
|
|
||||||
"description": "Sets the font properties as Roboto Headline2",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H3": {
|
|
||||||
"name": "H3",
|
|
||||||
"description": "Sets the font properties as Roboto Headline3",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H4": {
|
|
||||||
"name": "H4",
|
|
||||||
"description": "Sets the font properties as Roboto Headline4",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H5": {
|
|
||||||
"name": "H5",
|
|
||||||
"description": "Sets the font properties as Roboto Headline5",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"H6": {
|
|
||||||
"name": "H6",
|
|
||||||
"description": "Sets the font properties as Roboto Headline6",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"IconButton": {
|
|
||||||
"name": "IconButton",
|
|
||||||
"description": "An icon button component",
|
|
||||||
"props": {
|
|
||||||
"onClick": "event",
|
|
||||||
"disabled": "bool",
|
|
||||||
"href": "string",
|
|
||||||
"icon": "string",
|
|
||||||
"size": {
|
|
||||||
"type":"options",
|
|
||||||
"options": ["small", "medium", "large"],
|
|
||||||
"default": "medium"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Label": {
|
|
||||||
"name": "Label",
|
|
||||||
"description": "A simple label component that displays its text in the standard Roboto Material Design font",
|
|
||||||
"props": {
|
|
||||||
"text": "string",
|
|
||||||
"bold": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Menu": {
|
|
||||||
"name": "Menu",
|
|
||||||
"description": "A Material Design menu component. Anchor to other components to create a pop-out menu.",
|
|
||||||
"props": {
|
|
||||||
"onSelect": "event",
|
|
||||||
"width": "string",
|
|
||||||
"open": "bool",
|
|
||||||
"useFixedPosition": "bool",
|
|
||||||
"absolutePositionX": "number",
|
|
||||||
"absolutePositionY": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Overline": {
|
|
||||||
"name": "Overline",
|
|
||||||
"description": "Sets the font properties as Roboto Overline",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Radiobutton": {
|
|
||||||
"name": "Radiobutton",
|
|
||||||
"description": "A Material Design radiobutton. Supports aligning label before or after radiobutton.",
|
|
||||||
"props": {
|
|
||||||
"onClick": "event",
|
|
||||||
"id": "string",
|
|
||||||
"label": "string",
|
|
||||||
"name": "string",
|
|
||||||
"disabled": "bool",
|
|
||||||
"alignEnd": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Radiobuttongroup": {
|
|
||||||
"name": "Radiobuttongroup",
|
|
||||||
"description": "A Material Design Radiobutton group. Supports row and column orientation.",
|
|
||||||
"props": {
|
|
||||||
"onChange": "event",
|
|
||||||
"label": "string",
|
|
||||||
"name": "string",
|
|
||||||
"orientation": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["row", "column"],
|
|
||||||
"default": "row"
|
|
||||||
},
|
|
||||||
"fullwidth": "bool",
|
|
||||||
"alignEnd": "bool",
|
|
||||||
"disabled": "bool"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Sub1": {
|
|
||||||
"name": "Sub1",
|
|
||||||
"description": "Sets the font properties as Roboto Subtitle1",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Sub2": {
|
|
||||||
"name": "Sub2",
|
|
||||||
"description": "Sets the font properties as Roboto Subtitle2",
|
|
||||||
"props": {
|
|
||||||
"text": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Textfield": {
|
|
||||||
"name": "Textfield",
|
|
||||||
"description": "A Material Design textfield with multiple variants. Can also be converted to a text area / multine text field.",
|
|
||||||
"props": {
|
|
||||||
"onChange": "event",
|
|
||||||
"value": "string",
|
|
||||||
"label": "string",
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["standard", "outlined", "filled"],
|
|
||||||
"default": "standard"
|
|
||||||
},
|
|
||||||
"disabled": "bool",
|
|
||||||
"fullwidth": "bool",
|
|
||||||
"colour": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["primary", "secondary"],
|
|
||||||
"default": "primary"
|
|
||||||
},
|
|
||||||
"size":{
|
|
||||||
"type": "options",
|
|
||||||
"options": ["small", "medium", "large"],
|
|
||||||
"default": "medium"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["text", "password"],
|
|
||||||
"default": "text"
|
|
||||||
},
|
|
||||||
"required": "bool",
|
|
||||||
"minLength": "number",
|
|
||||||
"maxLength": "number",
|
|
||||||
"helperText": "string",
|
|
||||||
"placeholder": "string",
|
|
||||||
"icon": "string",
|
|
||||||
"trailingIcon": "bool",
|
|
||||||
"textarea": "bool",
|
|
||||||
"persistent": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Switch": {
|
|
||||||
"name": "Switch",
|
|
||||||
"description": "A material design switch component",
|
|
||||||
"props": {
|
|
||||||
"alignEnd": "bool",
|
|
||||||
"disabled": "bool",
|
|
||||||
"checked": "bool",
|
|
||||||
"label": "string",
|
|
||||||
"id": "string"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"Slider": {
|
|
||||||
"name": "Slider",
|
|
||||||
"description": "A material design slider component",
|
|
||||||
"props": {
|
|
||||||
"variant": {
|
|
||||||
"type": "options",
|
|
||||||
"options": ["continuous", "discrete"],
|
|
||||||
"default": "continuous"
|
|
||||||
},
|
|
||||||
"showTicks": "bool",
|
|
||||||
"min": "number",
|
|
||||||
"max": "number",
|
|
||||||
"value": "number",
|
|
||||||
"step": "number",
|
|
||||||
"label": "string",
|
|
||||||
"disabled": "bool"
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@budibase/materialdesign-components",
|
|
||||||
"svelte": "src/index.svelte",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"module": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rollup -c",
|
|
||||||
"prepublishOnly": "npm run build",
|
|
||||||
"testbuild": "rollup -w -c rollup.testconfig.js",
|
|
||||||
"dev": "run-p start:dev testbuild",
|
|
||||||
"start:dev": "sirv public --single --dev",
|
|
||||||
"dev:builder": "rollup -cw"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@budibase/client": "^0.1.17",
|
|
||||||
"@budibase/standard-components": "^0.1.17",
|
|
||||||
"@material/button": "^4.0.0",
|
|
||||||
"@material/checkbox": "^4.0.0",
|
|
||||||
"@material/data-table": "4.0.0",
|
|
||||||
"@material/dialog": "4.0.0",
|
|
||||||
"@material/form-field": "^4.0.0",
|
|
||||||
"@material/icon-button": "4.0.0",
|
|
||||||
"@material/list": "4.0.0",
|
|
||||||
"@material/menu": "4.0.0",
|
|
||||||
"@material/radio": "^4.0.0",
|
|
||||||
"@material/select": "4.0.0",
|
|
||||||
"@material/slider": "4.0.0",
|
|
||||||
"@material/switch": "4.0.0",
|
|
||||||
"@material/textfield": "^4.0.0",
|
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
|
||||||
"date-fns": "^2.10.0",
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"svelte"
|
|
||||||
],
|
|
||||||
"version": "0.1.17",
|
|
||||||
"license": "MIT",
|
|
||||||
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691",
|
|
||||||
"dependencies": {
|
|
||||||
"@material/card": "4.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf8'>
|
|
||||||
<meta name='viewport' content='width=device-width'>
|
|
||||||
|
|
||||||
<title>Budibase-Material Design</title>
|
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
|
||||||
<link rel='stylesheet' href='/build/bundle.css'>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<script src='/build/bundle.js'></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,50 +0,0 @@
|
||||||
import svelte from "rollup-plugin-svelte"
|
|
||||||
import postcss from "rollup-plugin-postcss"
|
|
||||||
import resolve from "rollup-plugin-node-resolve"
|
|
||||||
import commonjs from "rollup-plugin-commonjs"
|
|
||||||
|
|
||||||
const postcssOptions = () => ({
|
|
||||||
extensions: [".scss", ".sass"],
|
|
||||||
extract: false,
|
|
||||||
minimize: true,
|
|
||||||
use: [
|
|
||||||
[
|
|
||||||
"sass",
|
|
||||||
{
|
|
||||||
includePaths: ["./node_modules"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const coreExternal = ["shortid"]
|
|
||||||
|
|
||||||
export default {
|
|
||||||
input: "src/index.js",
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
file: "dist/index.js",
|
|
||||||
format: "esm",
|
|
||||||
name: "budibaseStandardComponents",
|
|
||||||
sourcemap: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
svelte({
|
|
||||||
hydratable: true,
|
|
||||||
}),
|
|
||||||
resolve({
|
|
||||||
preferBuiltins: true,
|
|
||||||
browser: true,
|
|
||||||
dedupe: importee => {
|
|
||||||
return coreExternal.includes(importee)
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
commonjs({
|
|
||||||
namedExports: {
|
|
||||||
shortid: ["generate"],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
postcss(postcssOptions()),
|
|
||||||
],
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
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 postcss from "rollup-plugin-postcss"
|
|
||||||
import path from "path"
|
|
||||||
|
|
||||||
const aliases = {
|
|
||||||
resolve: [".js", ".svelte"],
|
|
||||||
entries: [
|
|
||||||
// { find: "@BBMD", replacement: path.resolve(__dirname, "dist/index.js") },
|
|
||||||
{ find: "@BBMD", replacement: path.resolve(__dirname, "src/index.js") },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
const postcssOptions = () => ({
|
|
||||||
extensions: [".scss", ".sass"],
|
|
||||||
extract: false,
|
|
||||||
minimize: true,
|
|
||||||
use: [
|
|
||||||
[
|
|
||||||
"sass",
|
|
||||||
{
|
|
||||||
includePaths: ["./node_modules"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
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",
|
|
||||||
]
|
|
||||||
|
|
||||||
const lodash_exports = [
|
|
||||||
"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",
|
|
||||||
]
|
|
||||||
|
|
||||||
export default {
|
|
||||||
input: "src/Test/testMain.js",
|
|
||||||
output: {
|
|
||||||
sourcemap: true,
|
|
||||||
format: "iife",
|
|
||||||
name: "app",
|
|
||||||
file: "public/build/bundle.js",
|
|
||||||
globals: {
|
|
||||||
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/build/bundle.css")
|
|
||||||
},
|
|
||||||
|
|
||||||
hydratable: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// If you have external dependencies installed from
|
|
||||||
// npm, you'll most likely need these plugins. In
|
|
||||||
// some cases you'll need additional configuration —
|
|
||||||
// consult the documentation for details:
|
|
||||||
// https://github.com/rollup/rollup-plugin-commonjs
|
|
||||||
resolve({
|
|
||||||
browser: true,
|
|
||||||
dedupe: importee => {
|
|
||||||
return (
|
|
||||||
importee === "svelte" ||
|
|
||||||
importee.startsWith("svelte/") ||
|
|
||||||
coreExternal.includes(importee)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
preferBuiltins: true,
|
|
||||||
}),
|
|
||||||
commonjs({
|
|
||||||
namedExports: {
|
|
||||||
"lodash/fp": lodash_fp_exports,
|
|
||||||
lodash: lodash_exports,
|
|
||||||
shortid: ["generate"],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
json(),
|
|
||||||
|
|
||||||
// Watch the `public` directory and refresh the
|
|
||||||
// browser on changes when not in production
|
|
||||||
!production && livereload("public"),
|
|
||||||
|
|
||||||
// If we're building for production (npm run build
|
|
||||||
// instead of npm run dev), minify
|
|
||||||
production && terser(),
|
|
||||||
postcss(postcssOptions()),
|
|
||||||
],
|
|
||||||
watch: {
|
|
||||||
clearScreen: false,
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import Icon from "../Common/Icon.svelte"
|
|
||||||
import ripple from "../Common/Ripple.js"
|
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
|
||||||
|
|
||||||
const cb = new ClassBuilder("button", ["primary", "medium", "text"])
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
export let onClick
|
|
||||||
|
|
||||||
export let variant = "text"
|
|
||||||
export let colour = "primary"
|
|
||||||
export let size = "medium"
|
|
||||||
|
|
||||||
export let href = ""
|
|
||||||
export let icon = ""
|
|
||||||
export let trailingIcon = false
|
|
||||||
export let fullwidth = false
|
|
||||||
|
|
||||||
export let text = ""
|
|
||||||
export let disabled = false
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
let ctx = _bb.getContext("BBMD:button:context")
|
|
||||||
extras = [ctx]
|
|
||||||
})
|
|
||||||
|
|
||||||
let extras = ""
|
|
||||||
let modifiers = {}
|
|
||||||
let customs = { size, colour }
|
|
||||||
|
|
||||||
if (!href) modifiers = { variant }
|
|
||||||
|
|
||||||
$: props = { modifiers, customs, extras }
|
|
||||||
|
|
||||||
$: blockClasses = cb.build({ props })
|
|
||||||
const labelClass = cb.elem("label")
|
|
||||||
|
|
||||||
const clicked = () => _bb.call(onClick)
|
|
||||||
|
|
||||||
$: renderLeadingIcon = !!icon && !trailingIcon
|
|
||||||
$: renderTrailingIcon = !!icon && trailingIcon
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if href}
|
|
||||||
<a class={blockClasses} {href} on:click={clicked}>
|
|
||||||
<span class={labelClass}>{text}</span>
|
|
||||||
</a>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
use:ripple={{ colour }}
|
|
||||||
class={blockClasses}
|
|
||||||
class:fullwidth
|
|
||||||
{disabled}
|
|
||||||
on:click={clicked}>
|
|
||||||
{#if renderLeadingIcon}
|
|
||||||
<Icon context="button__icon" {icon} />
|
|
||||||
{/if}
|
|
||||||
<span class={labelClass}>{text}</span>
|
|
||||||
{#if renderTrailingIcon}
|
|
||||||
<Icon context="button__icon" {icon} />
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.mdc-button:not(.fullwidth) {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
.fullwidth {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,5 +0,0 @@
|
||||||
@import "@material/button/mdc-button.scss";
|
|
||||||
@import "@material/ripple/mdc-ripple.scss";
|
|
||||||
@import "./mixins.scss";
|
|
||||||
|
|
||||||
@include bbmd-button-styles();
|
|
|
@ -1,39 +0,0 @@
|
||||||
@import "@material/feature-targeting/functions";
|
|
||||||
@import "@material/feature-targeting/mixins";
|
|
||||||
|
|
||||||
@mixin bbmd-button-styles($query: mdc-feature-all()) {
|
|
||||||
$feat-structure: mdc-feature-create-target($query, structure);
|
|
||||||
|
|
||||||
.bbmd-mdc-button--size-large {
|
|
||||||
@include button-sizing($feat-structure, 21px, 40px, 15px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bbmd-mdc-button--size-small {
|
|
||||||
@include button-sizing($feat-structure, 9px, 32px, 13px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bbmd-mdc-button--colour-secondary {
|
|
||||||
//no feature target as relying on theme variable custom property
|
|
||||||
@include mdc-button-ink-color(secondary);
|
|
||||||
|
|
||||||
&.mdc-button--raised,
|
|
||||||
&.mdc-button--unelevated {
|
|
||||||
@include mdc-button-container-fill-color(secondary);
|
|
||||||
@include mdc-button-ink-color(on-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mdc-button--outlined {
|
|
||||||
@include mdc-button-outline-color(secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin button-sizing($feat, $padding, $height, $fontSize) {
|
|
||||||
@include mdc-feature-targets($feat) {
|
|
||||||
padding: 0px $padding;
|
|
||||||
height: $height;
|
|
||||||
}
|
|
||||||
> .mdc-button__label {
|
|
||||||
font-size: $fontSize;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
import "./_index.scss"
|
|
||||||
export { default as Button } from "./Button.svelte"
|
|
|
@ -1,67 +0,0 @@
|
||||||
<script>
|
|
||||||
export let heading = ""
|
|
||||||
export let subheading = ""
|
|
||||||
export let content = ""
|
|
||||||
export let imageUrl = ""
|
|
||||||
export let button1Text = ""
|
|
||||||
export let button2Text = ""
|
|
||||||
export let cardClick = () => {}
|
|
||||||
export let button1Click = () => {}
|
|
||||||
export let button2Click = () => {}
|
|
||||||
|
|
||||||
$: showImage = !!imageUrl
|
|
||||||
$: showButton1 = !!button1Text
|
|
||||||
$: showButton2 = !!button2Text
|
|
||||||
$: showButtons = !!showButton1 && !!showButton2
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="mdc-card" on:click={cardClick}>
|
|
||||||
<div class="mdc-card__primary-action demo-card__primary-action" tabindex="0">
|
|
||||||
{#if showImage}
|
|
||||||
<div
|
|
||||||
class="mdc-card__media mdc-card__media--16-9 demo-card__media"
|
|
||||||
style="background-image: url("{imageUrl}");" />
|
|
||||||
{/if}
|
|
||||||
<div class="pad">
|
|
||||||
<div class="demo-card__primary">
|
|
||||||
<h2 class="demo-card__title mdc-typography mdc-typography--headline6">
|
|
||||||
{heading}
|
|
||||||
</h2>
|
|
||||||
<h3
|
|
||||||
class="demo-card__subtitle mdc-typography mdc-typography--subtitle2">
|
|
||||||
{subheading}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="demo-card__secondary mdc-typography mdc-typography--body2">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{#if showButtons}
|
|
||||||
<div class="mdc-card__actions">
|
|
||||||
<div class="mdc-card__action-buttons">
|
|
||||||
{#if showButton1}
|
|
||||||
<button class="mdc-button mdc-card__action mdc-card__action--button">
|
|
||||||
<span class="mdc-button__ripple" on:click={button1Click} />
|
|
||||||
{button1Text}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{#if showButton2}
|
|
||||||
<button
|
|
||||||
class="mdc-button mdc-card__action mdc-card__action--button"
|
|
||||||
on:click={button2Click}>
|
|
||||||
<span class="mdc-button__ripple" />
|
|
||||||
{button2Text}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.pad {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,39 +0,0 @@
|
||||||
<script>
|
|
||||||
import CardHeader from "./CardHeader.svelte"
|
|
||||||
import CardBody from "./CardBody.svelte"
|
|
||||||
import CardImage from "./CardImage.svelte"
|
|
||||||
import CardFooter from "./CardFooter.svelte"
|
|
||||||
import { H2, H6, Body2 } from "../Typography"
|
|
||||||
import { Button } from "../Button"
|
|
||||||
import { IconButton } from "../IconButton"
|
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
|
||||||
|
|
||||||
export let width = "350px"
|
|
||||||
export let height = "auto"
|
|
||||||
export let variant = "standard"
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
let card
|
|
||||||
|
|
||||||
const cb = new ClassBuilder("card", ["standard"])
|
|
||||||
|
|
||||||
$: modifiers = { variant }
|
|
||||||
$: props = { modifiers }
|
|
||||||
$: cardClass = cb.build({ props })
|
|
||||||
|
|
||||||
$: safeWidth = width !== "auto" && !/px$/.test(width) ? `${width}px` : width
|
|
||||||
|
|
||||||
$: safeHeight =
|
|
||||||
height !== "auto" && !/px$/.test(height) ? `${height}px` : height
|
|
||||||
|
|
||||||
$: card && _bb.attachChildren(card)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={card} class={`bbmd-card ${cardClass}`} />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bbmd-card {
|
|
||||||
width: 350px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,12 +0,0 @@
|
||||||
<script>
|
|
||||||
export let _bb
|
|
||||||
export let onClick = () => {}
|
|
||||||
|
|
||||||
export let clicked = () => _bb.call(onClick)
|
|
||||||
|
|
||||||
let cardBody
|
|
||||||
|
|
||||||
$: cardBody && _bb.attachChildren(cardBody)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={cardBody} class="mdc-card__primary-action" on:click={clicked} />
|
|
|
@ -1,31 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
let cardFooter
|
|
||||||
export let _bb
|
|
||||||
export let padding = "5px"
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
_bb.setContext(
|
|
||||||
"BBMD:icon-button:context",
|
|
||||||
"mdc-card__action mdc-card__action--icon"
|
|
||||||
)
|
|
||||||
_bb.setContext(
|
|
||||||
"BBMD:button:context",
|
|
||||||
"mdc-card__action mdc-card__action--button"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
$: cardFooter && _bb.attachChildren(cardFooter)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={cardFooter}
|
|
||||||
style={`padding: ${padding}`}
|
|
||||||
class="mdc-card__actions bbmd-card__actions" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bbmd-card__actions {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,44 +0,0 @@
|
||||||
<script>
|
|
||||||
import { H6, Sub2 } from "../Typography"
|
|
||||||
import Icon from "../Common/Icon.svelte"
|
|
||||||
export let _bb
|
|
||||||
export let title = ""
|
|
||||||
export let subtitle = ""
|
|
||||||
export let icon = ""
|
|
||||||
|
|
||||||
$: useIcon = !!icon
|
|
||||||
$: useSubtitle = !!subtitle
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="card-header">
|
|
||||||
{#if useIcon}
|
|
||||||
<div class="card-header__icon">
|
|
||||||
<Icon {icon} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="card-header__title">
|
|
||||||
<H6 text={title} />
|
|
||||||
{#if useSubtitle}
|
|
||||||
<Sub2 text={subtitle} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header__icon {
|
|
||||||
flex: 0;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header__title {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<script>
|
|
||||||
import { H6, Sub2 } from "../Typography"
|
|
||||||
export let _bb
|
|
||||||
export let displayHorizontal = false //aligns image on row with title and subtitle text
|
|
||||||
export let url = ""
|
|
||||||
export let title = ""
|
|
||||||
export let subtitle = ""
|
|
||||||
|
|
||||||
$: useTitle = !!title
|
|
||||||
$: useSubTitle = !!subtitle
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if !displayHorizontal}
|
|
||||||
<div
|
|
||||||
class="my-card__media mdc-card__media mdc-card__media--16-9"
|
|
||||||
style={`background-image: url(${url})`}>
|
|
||||||
<div class="mdc-card__media-content bbmd-card-media__content">
|
|
||||||
{#if useTitle}
|
|
||||||
<H6 text={title} verticalMargin={0} horizontalMargin={10} />
|
|
||||||
{#if useSubTitle}
|
|
||||||
<Sub2 text={subtitle} horizontalMargin={10} />
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="bbmd-card--horizontal">
|
|
||||||
<div
|
|
||||||
class="mdc-card__media mdc-card__media mdc-card__media--square
|
|
||||||
bbmd-card--horizontal-image"
|
|
||||||
style={`background-image: url(${url})`} />
|
|
||||||
<div class="bbmd-card--horizontal-text">
|
|
||||||
{#if useTitle}
|
|
||||||
<H6 text={title} verticalMargin={0} horizontalMargin={10} />
|
|
||||||
{#if useSubTitle}
|
|
||||||
<Sub2 text={subtitle} horizontalMargin={10} />
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bbmd-card--horizontal {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bbmd-card--horizontal-image {
|
|
||||||
flex: 0 0 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bbmd-card--horizontal-text {
|
|
||||||
flex: 1;
|
|
||||||
padding: 10px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bbmd-card-media__content {
|
|
||||||
top: 125px;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1 +0,0 @@
|
||||||
@import "@material/card/mdc-card.scss"
|
|
|
@ -1,7 +0,0 @@
|
||||||
import "./_styles.scss"
|
|
||||||
export { default as Card } from "./Card.svelte"
|
|
||||||
export { default as BasicCard } from "./BasicCard.svelte"
|
|
||||||
export { default as CardBody } from "./CardBody.svelte"
|
|
||||||
export { default as CardFooter } from "./CardFooter.svelte"
|
|
||||||
export { default as CardHeader } from "./CardHeader.svelte"
|
|
||||||
export { default as CardImage } from "./CardImage.svelte"
|
|
|
@ -1,146 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, onDestroy } from "svelte"
|
|
||||||
import Formfield from "../Common/Formfield.svelte"
|
|
||||||
import { fieldStore } from "../Common/FormfieldStore.js"
|
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
|
||||||
import { MDCCheckbox } from "@material/checkbox"
|
|
||||||
import { generate } from "shortid"
|
|
||||||
|
|
||||||
export let onChange = item => {}
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
|
|
||||||
export let id = ""
|
|
||||||
export let value = null
|
|
||||||
export let label = ""
|
|
||||||
export let disabled = false
|
|
||||||
export let alignEnd = false
|
|
||||||
export let indeterminate = false
|
|
||||||
export let checked = false
|
|
||||||
|
|
||||||
let _id
|
|
||||||
let instance = null
|
|
||||||
let checkbox = null
|
|
||||||
let selectedItems
|
|
||||||
let checkProps
|
|
||||||
|
|
||||||
let context = _bb.getContext("BBMD:input:context")
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
_id = generate()
|
|
||||||
|
|
||||||
if (!!checkbox) {
|
|
||||||
instance = new MDCCheckbox(checkbox)
|
|
||||||
instance.indeterminate = indeterminate
|
|
||||||
if (context !== "list-item") {
|
|
||||||
let fieldStore = _bb.getContext("BBMD:field-element")
|
|
||||||
if (fieldStore) fieldStore.setInput(instance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context === "checkboxgroup") {
|
|
||||||
selectedItems = _bb.getContext("BBMD:checkbox:selectedItemsStore")
|
|
||||||
checkProps = _bb.getContext("BBMD:checkbox:props")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let extras = null
|
|
||||||
|
|
||||||
if (context === "list-item") {
|
|
||||||
extras = ["mdc-list-item__meta"]
|
|
||||||
}
|
|
||||||
|
|
||||||
const cb = new ClassBuilder("checkbox")
|
|
||||||
let modifiers = { disabled }
|
|
||||||
let props = { modifiers, extras }
|
|
||||||
|
|
||||||
const blockClass = cb.build({ props })
|
|
||||||
|
|
||||||
function changed(e) {
|
|
||||||
const val = e.target.checked
|
|
||||||
checked = val
|
|
||||||
|
|
||||||
handleOnClick()
|
|
||||||
if (_bb.isBound(_bb.props.checked)) {
|
|
||||||
_bb.setStateFromBinding(_bb.props.checked, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleOnClick() {
|
|
||||||
let item = { _id, checked, label, value }
|
|
||||||
if (context === "checkboxgroup") {
|
|
||||||
let idx = selectedItems.getItemIdx($selectedItems, _id)
|
|
||||||
if (idx > -1) {
|
|
||||||
selectedItems.removeItem(_id)
|
|
||||||
} else {
|
|
||||||
selectedItems.addItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_bb.call(onChange, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
$: isChecked =
|
|
||||||
context === "checkboxgroup"
|
|
||||||
? $selectedItems && $selectedItems.findIndex(i => i._id === _id) > -1
|
|
||||||
: checked
|
|
||||||
|
|
||||||
$: isAlignedEnd =
|
|
||||||
context === "checkboxgroup" && !!checkProps ? checkProps.alignEnd : alignEnd
|
|
||||||
|
|
||||||
$: isDisabled =
|
|
||||||
context === "checkboxgroup" && !!checkProps ? checkProps.disabled : disabled
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- TODO: Customizing Colour and Density - What level of customization for these things does Budibase need here? -->
|
|
||||||
|
|
||||||
{#if context !== 'list-item'}
|
|
||||||
<div class="bbmd-checkbox">
|
|
||||||
<Formfield {label} {_bb} {id} alignEnd={isAlignedEnd}>
|
|
||||||
<div bind:this={checkbox} class={blockClass}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class={cb.elem`native-control`}
|
|
||||||
{id}
|
|
||||||
disabled={isDisabled}
|
|
||||||
checked={isChecked}
|
|
||||||
on:change={changed} />
|
|
||||||
<div class={cb.elem`background`}>
|
|
||||||
<svg class={cb.elem`checkmark`} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
class={cb.elem`checkmark-path`}
|
|
||||||
fill="none"
|
|
||||||
d="M1.73,12.91 8.1,19.28 22.79,4.59" />
|
|
||||||
</svg>
|
|
||||||
<div class={cb.elem`mixedmark`} />
|
|
||||||
</div>
|
|
||||||
<div class={cb.elem`ripple`} />
|
|
||||||
</div>
|
|
||||||
</Formfield>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div bind:this={checkbox} class={blockClass}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class={cb.elem`native-control`}
|
|
||||||
{id}
|
|
||||||
disabled={isDisabled}
|
|
||||||
checked={isChecked}
|
|
||||||
on:change={changed} />
|
|
||||||
<div class={cb.elem`background`}>
|
|
||||||
<svg class={cb.elem`checkmark`} viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
class={cb.elem`checkmark-path`}
|
|
||||||
fill="none"
|
|
||||||
d="M1.73,12.91 8.1,19.28 22.79,4.59" />
|
|
||||||
</svg>
|
|
||||||
<div class={cb.elem`mixedmark`} />
|
|
||||||
</div>
|
|
||||||
<div class={cb.elem`ripple`} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bbmd-checkbox {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,69 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import Checkbox from "./Checkbox.svelte"
|
|
||||||
import Label from "../Common/Label.svelte"
|
|
||||||
import createItemsStore from "../Common/ItemStore.js"
|
|
||||||
|
|
||||||
let selectedItemsStore
|
|
||||||
let checkItems
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
export let label = ""
|
|
||||||
export let orientation = "row"
|
|
||||||
export let onChange = selectedItems => {}
|
|
||||||
|
|
||||||
export let disabled = false
|
|
||||||
export let alignEnd = false
|
|
||||||
export let value = []
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
_bb.setContext("BBMD:input:context", "checkboxgroup")
|
|
||||||
selectedItemsStore = createItemsStore(() => {
|
|
||||||
value = $selectedItemsStore
|
|
||||||
if (_bb.isBound(_bb.props.value)) {
|
|
||||||
_bb.setStateFromBinding(_bb.props.value, value)
|
|
||||||
}
|
|
||||||
_bb.call(onChange, value)
|
|
||||||
}, value)
|
|
||||||
_bb.setContext("BBMD:checkbox:selectedItemsStore", selectedItemsStore)
|
|
||||||
_bb.setContext("BBMD:checkbox:props", { alignEnd, disabled })
|
|
||||||
})
|
|
||||||
|
|
||||||
$: checkItems && _bb.attachChildren(checkItems)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="checkbox-group">
|
|
||||||
<div class="checkbox-group__label">
|
|
||||||
<Label text={label} bold />
|
|
||||||
</div>
|
|
||||||
<div bind:this={checkItems} class={`checkbox-group__boxes ${orientation}`} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group__boxes.row > div:not(:first-child) {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group > div {
|
|
||||||
text-align: left;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row wrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column wrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,2 +0,0 @@
|
||||||
@import "@material/form-field/mdc-form-field";
|
|
||||||
@import "@material/checkbox/mdc-checkbox.scss";
|
|
|
@ -1,3 +0,0 @@
|
||||||
import "./_style.scss"
|
|
||||||
export { default as Checkbox } from "./Checkbox.svelte"
|
|
||||||
export { default as Checkboxgroup } from "./CheckboxGroup.svelte"
|
|
|
@ -1,74 +0,0 @@
|
||||||
export default class ClassBuilder {
|
|
||||||
constructor(block, defaultIgnoreList) {
|
|
||||||
this.block = `mdc-${block}`
|
|
||||||
this.defaultIgnoreList = defaultIgnoreList //will be ignored when building custom classes
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
handles both blocks and elementss (BEM MD Notation)
|
|
||||||
params = {elementName: string, props: {modifiers{}, customs:{}, extras: []}}
|
|
||||||
All are optional
|
|
||||||
*/
|
|
||||||
build(params) {
|
|
||||||
if (!params) return this.block //return block if nothing passed
|
|
||||||
const { props, elementName } = params
|
|
||||||
let base = elementName ? `${this.block}__${elementName}` : this.block
|
|
||||||
if (!props) return base
|
|
||||||
return this._handleProps(base, props)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Easily grab a simple element class
|
|
||||||
elem(elementName) {
|
|
||||||
return this.build({ elementName })
|
|
||||||
}
|
|
||||||
|
|
||||||
//use if a different base is needed than whats defined by this.block
|
|
||||||
debase(base, elementProps) {
|
|
||||||
if (!elementProps) return base
|
|
||||||
return this._handleProps(base, elementProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
//proxies bindProps and checks for which elementProps exist before binding
|
|
||||||
_handleProps(base, elementProps) {
|
|
||||||
let cls = base
|
|
||||||
const { modifiers, customs, extras } = elementProps
|
|
||||||
if (modifiers) cls += this._bindProps(modifiers, base)
|
|
||||||
if (customs) cls += this._bindProps(customs, base, true)
|
|
||||||
if (extras) cls += ` ${extras.join(" ")}`
|
|
||||||
return cls.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Handles both modifiers and customs. Use property, value or both depending
|
|
||||||
on whether it is passsed props for custom or modifiers
|
|
||||||
if custom uses the following convention for scss mixins:
|
|
||||||
bbmd-{this.block}--{property}-{value}
|
|
||||||
bbmd-mdc-button--size-large
|
|
||||||
*/
|
|
||||||
_bindProps(elementProps, base, isCustom = false) {
|
|
||||||
return Object.entries(elementProps)
|
|
||||||
.map(([property, value]) => {
|
|
||||||
//disregard falsy and values set by defaultIgnoreList constructor param
|
|
||||||
if (
|
|
||||||
!!value &&
|
|
||||||
(!this.defaultIgnoreList || !this.defaultIgnoreList.includes(value))
|
|
||||||
) {
|
|
||||||
let classBase = isCustom ? `bbmd-${base}` : `${base}`
|
|
||||||
let valueType = typeof value
|
|
||||||
|
|
||||||
if (valueType == "string" || valueType == "number") {
|
|
||||||
return isCustom
|
|
||||||
? ` ${classBase}--${this._convertCamel(property)}-${value}`
|
|
||||||
: ` ${classBase}--${value}`
|
|
||||||
} else if (valueType == "boolean") {
|
|
||||||
return ` ${classBase}--${this._convertCamel(property)}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.join("")
|
|
||||||
}
|
|
||||||
|
|
||||||
_convertCamel(str) {
|
|
||||||
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<script>
|
|
||||||
import "@material/floating-label/mdc-floating-label.scss"
|
|
||||||
export let forInput = ""
|
|
||||||
export let text = ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label for={forInput} class="mdc-floating-label">{text}</label>
|
|
|
@ -1,42 +0,0 @@
|
||||||
<script>
|
|
||||||
import "@material/form-field/mdc-form-field.scss"
|
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
|
||||||
import { fieldStore } from "./FormfieldStore.js"
|
|
||||||
import { MDCFormField } from "@material/form-field"
|
|
||||||
import { onMount, onDestroy } from "svelte"
|
|
||||||
|
|
||||||
const cb = new ClassBuilder("form-field")
|
|
||||||
|
|
||||||
let store
|
|
||||||
const unsubscribe = fieldStore.subscribe(s => (store = s))
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
export let id = ""
|
|
||||||
export let label = ""
|
|
||||||
export let alignEnd = false
|
|
||||||
|
|
||||||
let formField = null
|
|
||||||
|
|
||||||
$: modifiers = { alignEnd }
|
|
||||||
$: props = { modifiers, extras: ["bbmd-form-field"] }
|
|
||||||
|
|
||||||
$: blockClasses = cb.build({ props })
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
if (!!formField) fieldStore.set(new MDCFormField(formField))
|
|
||||||
_bb.setContext("BBMD:field-element", fieldStore)
|
|
||||||
})
|
|
||||||
|
|
||||||
onDestroy(unsubscribe)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={formField} class={blockClasses}>
|
|
||||||
<slot />
|
|
||||||
<label for={id}>{label}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bbmd-form-field {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { writable } from "svelte/store"
|
|
||||||
|
|
||||||
function store() {
|
|
||||||
const { set, update, subscribe } = writable({})
|
|
||||||
|
|
||||||
function setInput(inp) {
|
|
||||||
update(n => {
|
|
||||||
n.input = inp
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe,
|
|
||||||
set,
|
|
||||||
setInput,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fieldStore = store()
|
|
|
@ -1,8 +0,0 @@
|
||||||
<script>
|
|
||||||
export let icon = ""
|
|
||||||
export let context = ""
|
|
||||||
|
|
||||||
let cls = !!context ? `material-icons mdc-${context}` : "material-icons"
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<i class={cls}>{icon}</i>
|
|
|
@ -1,54 +0,0 @@
|
||||||
import { writable } from "svelte/store"
|
|
||||||
|
|
||||||
function createItemsStore(componentOnSelect, initialState = []) {
|
|
||||||
const { subscribe, set, update } = writable(initialState)
|
|
||||||
|
|
||||||
function addItem(item) {
|
|
||||||
update(items => {
|
|
||||||
return [...items, item]
|
|
||||||
})
|
|
||||||
if (componentOnSelect) {
|
|
||||||
componentOnSelect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSingleItem(item) {
|
|
||||||
set([item])
|
|
||||||
if (componentOnSelect) {
|
|
||||||
componentOnSelect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeItem(itemId) {
|
|
||||||
update(items => {
|
|
||||||
let index = getItemIdx(items, itemId)
|
|
||||||
items.splice(index, 1)
|
|
||||||
return items
|
|
||||||
})
|
|
||||||
if (componentOnSelect) {
|
|
||||||
componentOnSelect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearItems() {
|
|
||||||
set([])
|
|
||||||
if (componentOnSelect) {
|
|
||||||
componentOnSelect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getItemIdx(items, itemId) {
|
|
||||||
return items.findIndex(i => i && i._id === itemId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe,
|
|
||||||
addItem,
|
|
||||||
addSingleItem,
|
|
||||||
removeItem,
|
|
||||||
clearItems,
|
|
||||||
getItemIdx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createItemsStore
|
|
|
@ -1,12 +0,0 @@
|
||||||
<script>
|
|
||||||
export let bold = false
|
|
||||||
export let text = ""
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<span class="mdc-typography" class:bold>{text}</span>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bold {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<script>
|
|
||||||
import "@material/notched-outline/mdc-notched-outline.scss"
|
|
||||||
export let useLabel = true
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="mdc-notched-outline">
|
|
||||||
<div class="mdc-notched-outline__leading" />
|
|
||||||
{#if useLabel}
|
|
||||||
<div class="mdc-notched-outline__notch">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="mdc-notched-outline__trailing" />
|
|
||||||
</div>
|
|
|
@ -1,28 +0,0 @@
|
||||||
import { MDCRipple } from "@material/ripple"
|
|
||||||
|
|
||||||
export default function ripple(
|
|
||||||
node,
|
|
||||||
props = { colour: "primary", unbounded: true }
|
|
||||||
) {
|
|
||||||
node.classList.add("mdc-ripple-surface")
|
|
||||||
let component = new MDCRipple(node)
|
|
||||||
component.unbounded = props.unbounded
|
|
||||||
|
|
||||||
if (props.colour === "secondary") {
|
|
||||||
node.classList.remove("mdc-ripple-surface--primary")
|
|
||||||
node.classList.add("mdc-ripple-surface--accent")
|
|
||||||
} else {
|
|
||||||
node.classList.add("mdc-ripple-surface--primary")
|
|
||||||
node.classList.remove("mdc-ripple-surface--accent")
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
destroy() {
|
|
||||||
component.destroy()
|
|
||||||
node.classList.remove("mdc-ripple-surface")
|
|
||||||
node.classList.remove("mdc-ripple-surface--primary")
|
|
||||||
node.classList.remove("mdc-ripple-surface--accent")
|
|
||||||
component = null
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { MDCDataTable } from "@material/data-table"
|
|
||||||
import Row from "./DatatableRow.svelte"
|
|
||||||
import Cell from "./DatatableCell.svelte"
|
|
||||||
import { Button } from "../Button"
|
|
||||||
import ClassBuilder from "../ClassBuilder.js"
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
export let onLoad
|
|
||||||
|
|
||||||
const cb = new ClassBuilder("data-table")
|
|
||||||
_bb.setContext("BBMD:data-table:cb", cb)
|
|
||||||
|
|
||||||
let datatable = null
|
|
||||||
let instance = null
|
|
||||||
let tableElement
|
|
||||||
let initialied = false
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (tableElement && datatable && !initialied) {
|
|
||||||
const children = _bb.attachChildren(tableElement)
|
|
||||||
if (children.length > 0) {
|
|
||||||
instance = new MDCDataTable(datatable)
|
|
||||||
initialied = true
|
|
||||||
}
|
|
||||||
_bb.call(onLoad)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
return () => {
|
|
||||||
try {
|
|
||||||
!!instance && instance.destroy()
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
instance = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={datatable} class={cb.build()}>
|
|
||||||
<table
|
|
||||||
class={cb.elem`table`}
|
|
||||||
aria-label="Material Design Datatable"
|
|
||||||
bind:this={tableElement} />
|
|
||||||
</div>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<script>
|
|
||||||
import { getContext } from "svelte"
|
|
||||||
|
|
||||||
export let _bb
|
|
||||||
|
|
||||||
const cb = _bb.getContext("BBMD:data-table:cb")
|
|
||||||
|
|
||||||
let tbody
|
|
||||||
|
|
||||||
$: tbody && _bb.attachChildren(tbody)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<tbody bind:this={tbody} class={cb.elem`content`} />
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue