mirror of https://github.com/Budibase/budibase.git
199 changed files with 1793 additions and 8693 deletions
@ -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,62 +0,0 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { buildStyle } from "../../helpers.js" |
|||
export let value = "" |
|||
export let name = "" |
|||
export let textAlign = "left" |
|||
export let width = "160px" |
|||
export let placeholder = "" |
|||
export let suffix = "" |
|||
export let onChange = val => {} |
|||
|
|||
let centerPlaceholder = textAlign === "center" |
|||
|
|||
let style = buildStyle({ width, textAlign }) |
|||
|
|||
function handleChange(val) { |
|||
value = val |
|||
let _value = value !== "auto" ? value + suffix : value |
|||
onChange(_value) |
|||
} |
|||
|
|||
$: displayValue = |
|||
suffix && value && value.endsWith(suffix) |
|||
? value.replace(new RegExp(`${suffix}$`), "") |
|||
: value || "" |
|||
</script> |
|||
|
|||
<input |
|||
{name} |
|||
class:centerPlaceholder |
|||
type="text" |
|||
value={displayValue} |
|||
{placeholder} |
|||
{style} |
|||
on:change={e => handleChange(e.target.value)} /> |
|||
|
|||
<style> |
|||
input { |
|||
/* width: 32px; */ |
|||
height: 36px; |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
margin: 0px 0px 0px 2px; |
|||
color: var(--ink); |
|||
padding: 0px 8px; |
|||
font-family: inter; |
|||
width: 164px; |
|||
box-sizing: border-box; |
|||
background-color: var(--grey-2); |
|||
border-radius: 4px; |
|||
border: 1px solid var(--grey-2); |
|||
outline: none; |
|||
} |
|||
|
|||
input::placeholder { |
|||
text-align: left; |
|||
} |
|||
|
|||
.centerPlaceholder::placeholder { |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -1,50 +0,0 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import Input from "../Input.svelte" |
|||
|
|||
export let meta = [] |
|||
export let label = "" |
|||
export let value = ["0", "0", "0", "0"] |
|||
export let suffix = "" |
|||
|
|||
export let onChange = () => {} |
|||
|
|||
function handleChange(val, idx) { |
|||
value.splice(idx, 1, val !== "auto" && suffix ? val + suffix : val) |
|||
|
|||
value = value |
|||
let _value = value.map(v => |
|||
suffix && !v.endsWith(suffix) && v !== "auto" ? v + suffix : v |
|||
) |
|||
onChange(_value) |
|||
} |
|||
|
|||
$: displayValues = |
|||
value && suffix |
|||
? value.map(v => v.replace(new RegExp(`${suffix}$`), "")) |
|||
: value || [] |
|||
</script> |
|||
|
|||
<div class="input-container"> |
|||
<div class="label">{label}</div> |
|||
<div class="inputs-group"> |
|||
{#each meta as m, i} |
|||
<Input |
|||
width="37px" |
|||
textAlign="center" |
|||
placeholder={m.placeholder || ''} |
|||
value={!displayValues || displayValues[i] === '0' ? '' : displayValues[i]} |
|||
onChange={value => handleChange(value || 0, i)} /> |
|||
{/each} |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.label { |
|||
flex: 0; |
|||
} |
|||
|
|||
.inputs-group { |
|||
flex: 1; |
|||
} |
|||
</style> |
|||
@ -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,65 +0,0 @@ |
|||
<script> |
|||
import getIcon from "./icon" |
|||
|
|||
export let icon |
|||
export let value |
|||
</script> |
|||
|
|||
<div class="select-container"> |
|||
{#if icon} |
|||
<i class={icon} /> |
|||
{/if} |
|||
<select class:adjusted={icon} on:change bind:value> |
|||
<slot /> |
|||
</select> |
|||
<span class="arrow"> |
|||
{@html getIcon('chevron-down', '24')} |
|||
</span> |
|||
</div> |
|||
|
|||
<style> |
|||
.select-container { |
|||
font-size: 14px; |
|||
position: relative; |
|||
border: var(--grey-4) 1px solid; |
|||
} |
|||
|
|||
.adjusted { |
|||
padding-left: 30px; |
|||
} |
|||
|
|||
i { |
|||
position: absolute; |
|||
left: 10px; |
|||
top: 10px; |
|||
} |
|||
|
|||
select { |
|||
height: 40px; |
|||
display: block; |
|||
font-family: sans-serif; |
|||
font-weight: 400; |
|||
font-size: 14px; |
|||
color: var(--ink); |
|||
padding: 0 40px 0px 20px; |
|||
width: 100%; |
|||
max-width: 100%; |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
-moz-appearance: none; |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
background: var(--white); |
|||
} |
|||
|
|||
.arrow { |
|||
position: absolute; |
|||
right: 10px; |
|||
bottom: 0; |
|||
margin: auto; |
|||
width: 30px; |
|||
height: 30px; |
|||
pointer-events: none; |
|||
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> |
|||
@ -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,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> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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> |
|||
@ -1,170 +0,0 @@ |
|||
<script> |
|||
import InputGroup from "../common/Inputs/InputGroup.svelte" |
|||
import LayoutTemplateControls from "./LayoutTemplateControls.svelte" |
|||
|
|||
export let onStyleChanged = () => {} |
|||
export let component |
|||
|
|||
const tbrl = [ |
|||
{ placeholder: "T" }, |
|||
{ placeholder: "R" }, |
|||
{ placeholder: "B" }, |
|||
{ placeholder: "L" }, |
|||
] |
|||
|
|||
const se = [{ placeholder: "START" }, { placeholder: "END" }] |
|||
|
|||
const single = [{ placeholder: "" }] |
|||
|
|||
$: layout = { |
|||
...component._styles.position, |
|||
...component._styles.layout, |
|||
} |
|||
|
|||
$: layouts = { |
|||
templaterows: ["Grid Rows", single], |
|||
templatecolumns: ["Grid Columns", single], |
|||
} |
|||
|
|||
$: display = { |
|||
direction: ["Direction", single], |
|||
align: ["Align", single], |
|||
justify: ["Justify", single], |
|||
} |
|||
|
|||
$: positions = { |
|||
column: ["Column", se], |
|||
row: ["Row", se], |
|||
} |
|||
|
|||
$: spacing = { |
|||
margin: ["Margin", tbrl, "small"], |
|||
padding: ["Padding", tbrl, "small"], |
|||
} |
|||
|
|||
$: size = { |
|||
height: ["Height", single], |
|||
width: ["Width", single], |
|||
} |
|||
|
|||
$: zindex = { |
|||
zindex: ["Z-Index", single], |
|||
} |
|||
|
|||
const newValue = n => Array(n).fill("") |
|||
</script> |
|||
|
|||
<h3>Layout</h3> |
|||
<div class="layout-pos"> |
|||
{#each Object.entries(display) as [key, [name, meta, size]] (component._id + key)} |
|||
<div class="grid"> |
|||
<h5>{name}:</h5> |
|||
<LayoutTemplateControls |
|||
onStyleChanged={_value => onStyleChanged('layout', key, _value)} |
|||
values={layout[key] || newValue(meta.length)} |
|||
propertyName={name} |
|||
{meta} |
|||
{size} |
|||
type="text" /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<!-- <h4>Positioning</h4> |
|||
<div class="layout-pos"> |
|||
{#each Object.entries(positions) as [key, [name, meta, size]] (component._id + key)} |
|||
<div class="grid"> |
|||
<h5>{name}:</h5> |
|||
<InputGroup |
|||
onStyleChanged={_value => onStyleChanged('position', key, _value)} |
|||
values={layout[key] || newValue(meta.length)} |
|||
{meta} |
|||
{size} /> |
|||
</div> |
|||
{/each} |
|||
</div> --> |
|||
|
|||
<h3>Spacing</h3> |
|||
<div class="layout-spacing"> |
|||
{#each Object.entries(spacing) as [key, [name, meta, size]] (component._id + key)} |
|||
<div class="grid"> |
|||
<h5>{name}:</h5> |
|||
<InputGroup |
|||
onStyleChanged={_value => onStyleChanged('position', key, _value)} |
|||
values={layout[key] || newValue(meta.length)} |
|||
{meta} |
|||
{size} |
|||
type="text" /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<h3>Size</h3> |
|||
<div class="layout-layer"> |
|||
{#each Object.entries(size) as [key, [name, meta, size]] (component._id + key)} |
|||
<div class="grid"> |
|||
<h5>{name}:</h5> |
|||
<InputGroup |
|||
onStyleChanged={_value => onStyleChanged('position', key, _value)} |
|||
values={layout[key] || newValue(meta.length)} |
|||
type="text" |
|||
{meta} |
|||
{size} /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<h3>Order</h3> |
|||
<div class="layout-layer"> |
|||
{#each Object.entries(zindex) as [key, [name, meta, size]] (component._id + key)} |
|||
<div class="grid"> |
|||
<h5>{name}:</h5> |
|||
<InputGroup |
|||
onStyleChanged={_value => onStyleChanged('position', key, _value)} |
|||
values={layout[key] || newValue(meta.length)} |
|||
{meta} |
|||
{size} /> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
|
|||
<style> |
|||
h3 { |
|||
text-transform: uppercase; |
|||
font-size: 13px; |
|||
font-weight: 600; |
|||
color: var(--ink); |
|||
opacity: 0.6; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
h4 { |
|||
text-transform: uppercase; |
|||
font-size: 10px; |
|||
font-weight: 600; |
|||
color: var(--ink); |
|||
opacity: 0.4; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
h5 { |
|||
font-size: 13px; |
|||
font-weight: 400; |
|||
color: var(--ink); |
|||
opacity: 0.8; |
|||
padding-top: 13px; |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
div > div { |
|||
display: grid; |
|||
grid-template-rows: 1fr; |
|||
grid-gap: 10px; |
|||
height: 40px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.grid { |
|||
grid-template-columns: 70px 2fr; |
|||
} |
|||
</style> |
|||
@ -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> |
|||
@ -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,46 +0,0 @@ |
|||
<script> |
|||
import { store } from "builderStore" |
|||
import Checkbox from "../common/Checkbox.svelte" |
|||
import Textbox from "../common/Textbox.svelte" |
|||
import Dropdown from "../common/Dropdown.svelte" |
|||
import StateBindingControl from "./StateBindingControl.svelte" |
|||
|
|||
export let index |
|||
export let prop_name |
|||
export let prop_value |
|||
export let prop_definition = {} |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
{#if prop_definition.type !== 'event'} |
|||
<h5>{prop_name}</h5> |
|||
<StateBindingControl |
|||
value={prop_value} |
|||
type={prop_definition.type || prop_definition} |
|||
options={prop_definition.options} |
|||
styleBindingProperty={prop_definition.styleBindingProperty} |
|||
onChanged={v => store.setComponentProp(prop_name, v)} /> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
height: 40px; |
|||
margin-bottom: 15px; |
|||
display: grid; |
|||
grid-template-rows: 1fr; |
|||
grid-template-columns: 70px 1fr; |
|||
grid-gap: 10px; |
|||
align-items: baseline; |
|||
} |
|||
|
|||
h5 { |
|||
word-wrap: break-word; |
|||
font-size: 13px; |
|||
font-weight: 400; |
|||
color: var(--ink); |
|||
opacity: 0.8; |
|||
padding-top: 13px; |
|||
margin-bottom: 0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,11 @@ |
|||
<script> |
|||
/* |
|||
This file exists because of how we pass this control to the |
|||
properties panel - via a JS reference, not using svelte tags |
|||
... checkout the use of Input in propertyCategories to see what i mean |
|||
*/ |
|||
import { Input } from "@budibase/bbui" |
|||
export let name, value, placeholder, type |
|||
</script> |
|||
|
|||
<Input {name} {value} {placeholder} {type} thin on:change /> |
|||
@ -1,54 +0,0 @@ |
|||
<script> |
|||
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 IconButton from "../common/IconButton.svelte" |
|||
|
|||
export let component |
|||
export let components |
|||
|
|||
let errors = [] |
|||
const props_to_ignore = ["_component", "_children", "_styles", "_code", "_id"] |
|||
|
|||
$: componentDef = components[component._component] |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
|
|||
<form on:submit|preventDefault class="uk-form-stacked form-root"> |
|||
{#if componentDef} |
|||
{#each Object.entries(componentDef.props) as [prop_name, prop_def], index} |
|||
{#if prop_def !== 'event'} |
|||
<div class="prop-container"> |
|||
<PropControl |
|||
{prop_name} |
|||
prop_value={component[prop_name]} |
|||
prop_definition={prop_def} |
|||
{index} |
|||
disabled={false} /> |
|||
|
|||
</div> |
|||
{/if} |
|||
{/each} |
|||
{/if} |
|||
</form> |
|||
|
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
font-size: 10pt; |
|||
width: 100%; |
|||
} |
|||
|
|||
.form-root { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.prop-container { |
|||
flex: 1 1 auto; |
|||
min-width: 250px; |
|||
} |
|||
</style> |
|||
@ -1,63 +0,0 @@ |
|||
<script> |
|||
import { backendUiStore } from "builderStore" |
|||
import IconButton from "../common/IconButton.svelte" |
|||
import Input from "../common/Input.svelte" |
|||
|
|||
export let value = "" |
|||
export let onChanged = () => {} |
|||
export let type = "" |
|||
export let options = [] |
|||
export let styleBindingProperty = "" |
|||
|
|||
$: bindOptionToStyle = !!styleBindingProperty |
|||
</script> |
|||
|
|||
<div class="unbound-container"> |
|||
{#if type === 'bool'} |
|||
<div> |
|||
<IconButton |
|||
icon={value == true ? 'check-square' : 'square'} |
|||
size="19" |
|||
on:click={() => onChanged(!value)} /> |
|||
</div> |
|||
{:else if type === 'models'} |
|||
<select |
|||
class="uk-select uk-form-small" |
|||
bind:value |
|||
on:change={() => { |
|||
onChanged(value) |
|||
}}> |
|||
{#each $backendUiStore.models || [] as option} |
|||
<option value={option}>{option.name}</option> |
|||
{/each} |
|||
</select> |
|||
{:else if type === 'options' || type === 'models'} |
|||
<select |
|||
class="uk-select uk-form-small" |
|||
{value} |
|||
on:change={ev => onChanged(ev.target.value)}> |
|||
{#each options || [] as option} |
|||
{#if bindOptionToStyle} |
|||
<option style={`${styleBindingProperty}: ${option};`} value={option}> |
|||
{option} |
|||
</option> |
|||
{:else} |
|||
<option value={option}>{option}</option> |
|||
{/if} |
|||
{/each} |
|||
</select> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.unbound-container { |
|||
display: flex; |
|||
} |
|||
|
|||
.bound-header > div:nth-child(1) { |
|||
flex: 1 0 auto; |
|||
width: 30px; |
|||
color: var(--secondary50); |
|||
padding-left: 5px; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -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() |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue