1
0
Fork 0
mirror of synced 2024-06-02 02:25:17 +12:00

merge spectrum-bbui

This commit is contained in:
Keviin Åberg Kultalahti 2021-04-21 13:15:16 +02:00
commit 6db09715df
162 changed files with 2479 additions and 9516 deletions

View file

@ -2,6 +2,7 @@
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "1.58.13",
"license": "AGPL-3.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
"exports": {
@ -9,31 +10,24 @@
"import": "./dist/bbui.es.js"
},
"./package.json": "./package.json",
"./dist/style.css": "./dist/style.css"
"./spectrum-icons-rollup.js": "./src/spectrum-icons-rollup.js",
"./spectrum-icons-vite.js": "./src/spectrum-icons-vite.js"
},
"scripts": {
"dev:builder": "vite build",
"build": "vite build"
"build": "rollup -c"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.5",
"@rollup/plugin-node-resolve": "^11.2.1",
"cross-env": "^7.0.2",
"nollup": "^0.14.1",
"postcss": "^8.2.9",
"rollup": "^2.34.0",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-delete": "^1.2.0",
"rollup-plugin-hot": "^0.1.1",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup": "^2.45.2",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-svelte-hot": "^0.11.0",
"semantic-release": "^17.0.8",
"svelte": "^3.37.0",
"svench": "^0.0.10-7",
"vite": "^2.1.5"
"rollup-plugin-svelte": "^7.1.0",
"rollup-plugin-terser": "^7.0.2",
"svelte": "^3.37.0"
},
"keywords": [
"svelte"
@ -43,34 +37,39 @@
"dist"
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/button": "^3.0.1",
"@spectrum-css/buttongroup": "^3.0.1",
"@spectrum-css/checkbox": "^3.0.1",
"@spectrum-css/checkbox": "^3.0.2",
"@spectrum-css/dialog": "^3.0.1",
"@spectrum-css/divider": "^1.0.1",
"@spectrum-css/fieldgroup": "^3.0.2",
"@spectrum-css/fieldlabel": "^3.0.1",
"@spectrum-css/icon": "^3.0.1",
"@spectrum-css/inputgroup": "^3.0.2",
"@spectrum-css/label": "^2.0.9",
"@spectrum-css/link": "^3.1.1",
"@spectrum-css/menu": "^3.0.1",
"@spectrum-css/modal": "^3.0.1",
"@spectrum-css/picker": "^1.0.1",
"@spectrum-css/popover": "^3.0.1",
"@spectrum-css/progressbar": "^1.0.2",
"@spectrum-css/progresscircle": "^1.0.2",
"@spectrum-css/radio": "^3.0.2",
"@spectrum-css/search": "^3.0.2",
"@spectrum-css/switch": "^1.0.2",
"@spectrum-css/table": "^3.0.1",
"@spectrum-css/tabs": "^3.0.1",
"@spectrum-css/textfield": "^3.0.1",
"@spectrum-css/toast": "^3.0.1",
"@spectrum-css/treeview": "^3.0.2",
"@spectrum-css/typography": "^3.0.1",
"@spectrum-css/underlay": "^2.0.9",
"@spectrum-css/vars": "^3.0.1",
"dayjs": "^1.10.4",
"markdown-it": "^12.0.4",
"quill": "^1.3.7",
"sirv-cli": "^0.4.6",
"svelte-flatpickr": "^2.4.0",
"svelte-portal": "^1.0.0",
"turndown": "^7.0.0"
"svelte-flatpickr": "^3.1.0",
"svelte-portal": "^1.0.0"
}
}

View file

@ -1,140 +1,25 @@
import * as path from "path"
import svelte from "rollup-plugin-svelte-hot"
import svelte from "rollup-plugin-svelte"
import resolve from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import json from "@rollup/plugin-json"
import copy from "rollup-plugin-copy"
import hmr from "rollup-plugin-hot"
import del from "rollup-plugin-delete"
import { terser } from "rollup-plugin-terser"
import postcss from "rollup-plugin-postcss"
import { plugin as Svench } from "svench/rollup"
import builtins from "rollup-plugin-node-builtins"
const WATCH = !!process.env.ROLLUP_WATCH
const SVENCH = !!process.env.SVENCH
const HOT = WATCH
const PRODUCTION = !WATCH
const svench = Svench({
// The root dir that Svench will parse and watch.
//
// NOTE Watching the root of the project, to let Svench render *.md for us.
//
// NOTE By default, `node_modules` and `.git` dirs are ignored. This can be
// customized by passing a function to `ignore` option. Default ignore is:
//
// ignore: path => /(?:^|\/)(?:node_modules|\.git)\//.test(path),
//
dir: ".",
// Make `src` dir a section (that is, it will always be "expanded" in the
// menu).
autoSections: ["src"],
// Use custom index.html
index: {
source: "public/index.html",
export default {
input: "src/index.js",
output: {
sourcemap: true,
format: "esm",
file: "dist/bbui.es.js",
},
extensions: [".svench", ".svench.svelte", ".svench.svx", ".md"],
serve: WATCH && {
host: "0.0.0.0",
port: 4242,
public: "public",
nollup: "0.0.0.0:42421",
},
})
// NOTE configs are in function form to avoid instantiating plugins of the
// config that is not used for nothing (in particular, the HMR plugin launches
// a dev server on startup, this is not desired when just building for prod)
const configs = {
svench: () => ({
input: ".svench/svench.js",
output: {
format: "es",
dir: "public/svench",
},
plugins: [
builtins(),
// NOTE cleaning old builds is required to avoid serving stale static
// files from a previous build instead of in-memory files from the dev/hmr
// server
del({
targets: "public/svench/*",
runOnce: true,
}),
postcss({
hot: HOT,
extract: path.resolve("public/svench/theme.css"),
sourceMap: true,
}),
svench,
svelte({
dev: !PRODUCTION,
extensions: [".svelte", ".svench", ".svx", ".md"],
// Svench's "combined" preprocessor wraps both Mdsvex preprocessors
// (configured for Svench), and its own preprocessor (for static
// analysis -- eg extract source from views)
preprocess: svench.$.preprocess,
hot: HOT && {
optimistic: true,
noPreserveState: false,
},
}),
resolve({ browser: true }),
commonjs(),
json(),
HOT &&
hmr({
host: "0.0.0.0",
public: "public",
inMemory: true,
compatModuleHot: !HOT, // for terser
}),
],
watch: {
clearScreen: false,
// buildDelay is needed to ensure Svench's code (routes) generator will
// pick file changes before Rollup and prevent a double build (if Rollup
// first sees a change to src/Foo.svench, then to Svench's routes.js)
buildDelay: 100,
},
}),
lib: () => ({
input: "src/index.js",
output: [{ file: "dist/bundle.mjs", format: "es" }],
plugins: [
svelte({
dev: !PRODUCTION,
extensions: [".svelte"],
emitCss: true,
}),
postcss(),
copy({
targets: [
{
src: ".svench/svench.css",
dest: "public",
rename: "global.css",
},
],
}),
resolve(),
commonjs(),
json(),
],
}),
plugins: [
resolve(),
commonjs(),
svelte({
emitCss: true,
}),
postcss(),
terser(),
json(),
],
}
export default configs[SVENCH ? "svench" : "lib"]()

View file

@ -5,14 +5,14 @@ export default function clickOutside(element, callbackFunction) {
}
}
document.body.addEventListener("click", onClick, true)
document.body.addEventListener("mousedown", onClick, true)
return {
update(newCallbackFunction) {
callbackFunction = newCallbackFunction
},
destroy() {
document.body.removeEventListener("click", onClick, true)
document.body.removeEventListener("mousedown", onClick, true)
},
}
}

View file

@ -2,19 +2,13 @@
import "@spectrum-css/button/dist/index-vars.css"
export let disabled = false
/** @type {('S', 'M', 'L', 'XL')} Size of button */
export let size = "M";
// Types
export let cta, primary, secondary, warning, overBackground;
export let size = "M"
export let cta, primary, secondary, warning, overBackground
export let quiet = false
export let icon = undefined;
export let icon = undefined
export let active = false
</script>
<button
class:spectrum-Button--cta={cta}
class:spectrum-Button--primary={primary}
@ -22,19 +16,32 @@
class:spectrum-Button--warning={warning}
class:spectrum-Button--overBackground={overBackground}
class:spectrum-Button--quiet={quiet}
class:active
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
{disabled}
on:click|preventDefault>
{#if icon}
<svg class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}" focusable="false" aria-hidden="true" aria-label="{icon}">
<svg
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
focusable="false"
aria-hidden="true"
aria-label={icon}>
<use xlink:href="#spectrum-icon-18-{icon}" />
</svg>
{/if}
{#if $$slots}
{#if $$slots}
<span class="spectrum-Button-label"><slot /></span>
{/if}
</button>
<style>
.spectrum-Button-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
button.active {
color: var(--spectrum-semantic-cta-color-background-default);
}
</style>

View file

@ -1,45 +0,0 @@
<script>
import Flatpickr from "svelte-flatpickr"
import { Label } from "../"
import "flatpickr/dist/flatpickr.css"
const PICKER_OPTIONS = {
enableTime: true,
}
export let label
export let placeholder
export let value
export let thin = false
</script>
<div class:thin>
{#if label}
<Label extraSmall grey>{label}</Label>
{/if}
<Flatpickr {placeholder} options={PICKER_OPTIONS} on:change bind:value />
</div>
<style>
:global(.flatpickr-input) {
width: 100%;
min-width: 0;
box-sizing: border-box;
color: var(--ink);
border-radius: 5px;
border: none;
background-color: var(--grey-2);
padding: var(--spacing-m);
font-size: var(--font-size-s);
margin: 0;
outline: none;
border: var(--border-transparent);
}
:global(.flatpickr-input:focus) {
border: var(--border-blue);
}
div.thin :global(.flatpickr-input) {
font-size: var(--font-size-xs);
}
</style>

View file

@ -1,17 +0,0 @@
<script>
import { View } from "svench";
import DatePicker from "./DatePicker.svelte";
function handleChange(event) {
const [fullDate, shortDate, instance] = event.detail
alert("Date is " + fullDate)
}
</script>
<View name="default">
<DatePicker on:change={handleChange} label="Start Date" placeholder="Pick a date" />
</View>
<View name="thin">
<DatePicker on:change={handleChange} label="Start Date" thin placeholder="Pick a date" />
</View>

View file

@ -1,14 +1,16 @@
<script>
import "@spectrum-css/divider/dist/index-vars.css"
export let l = false
export let m = false
export let s = false
export let vertical = false
import "@spectrum-css/divider/dist/index-vars.css"
export let l = false
export let m = false
export let s = false
export let vertical = false
$: useDefault = ![l, m, s].includes(true)
</script>
<hr
class:spectrum-Divider--sizeL={l}
class:spectrum-Divider--sizeM={m}
class:spectrum-Divider--sizeS={s}
class="spectrum-Divider spectrum-Divider--{vertical ? 'vertical' : 'horizontal'} spectrum-Dialog-divider">
class:spectrum-Divider--sizeL={l}
class:spectrum-Divider--sizeM={m || useDefault}
class:spectrum-Divider--sizeS={s}
class="spectrum-Divider spectrum-Divider--{vertical ? 'vertical' : 'horizontal'} spectrum-Dialog-divider" />

View file

@ -1,7 +1,7 @@
<script>
import { slide } from "svelte/transition"
import Portal from "svelte-portal"
import ActionButton from '../ActionButton/ActionButton.svelte'
import ActionButton from "../ActionButton/ActionButton.svelte"
export let title
@ -84,5 +84,8 @@
}
.text {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
</style>

View file

@ -2,7 +2,6 @@
import "@spectrum-css/popover/dist/index-vars.css"
import Portal from "svelte-portal"
import { createEventDispatcher } from "svelte"
import buildStyle from "../utils/buildStyle"
import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside"
@ -10,7 +9,6 @@
export let anchor
export let align = "right"
export let borderColor = ""
export const show = () => {
dispatch("open")
@ -29,10 +27,6 @@
hide()
}
}
$: menuStyle = buildStyle({
borderColor,
})
</script>
{#if open}
@ -41,9 +35,9 @@
tabindex="0"
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
style={menuStyle}
on:keydown={handleEscape}
class="spectrum-Popover is-open" role="presentation">
class="spectrum-Popover is-open"
role="presentation">
<slot />
</div>
</Portal>

View file

@ -1,140 +1,22 @@
<script>
import Field from "./Field.svelte"
import Checkbox from "./Core/Checkbox.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let labelPosition = "above"
export let text = null
export let disabled = false
export let error = null
const dispatch = createEventDispatcher()
export let checked = false
export let value
export let name
export let disabled
function handleChange() {
if (disabled) return
checked = !checked
dispatch("change", checked)
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<div class="container">
<input
{disabled}
on:change={handleChange}
{value}
bind:checked
type="checkbox"
{name}
class="checkbox"
id={value} />
<div class="checkbox-container" on:click={handleChange}>
<div class:disabled class="check-div" class:checked>
<div class="tick_mark" />
</div>
</div>
<slot />
</div>
<style>
.container {
display: flex;
gap: var(--spacing-s);
}
.checkbox-container {
position: relative;
z-index: 0;
}
.checkbox {
display: none;
}
.check-div {
position: relative;
width: 20px;
height: 20px;
background-color: var(--grey-2);
cursor: pointer;
transition: 0.2s ease transform, 0.2s ease background-color,
0.2s ease box-shadow;
overflow: hidden;
border-radius: 4px;
}
.check-div:before {
content: "";
position: absolute;
top: 50%;
right: 0;
left: 0;
width: 12px;
height: 12px;
margin: 0 auto;
background-color: var(--background);
transform: translateY(-50%);
transition: 0.2s ease width, 0.2s ease height;
border-radius: 2px;
}
.check-div:active {
transform: translateY(-50%) scale(0.9);
}
.tick_mark {
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 4px;
margin: 0 auto;
transform: rotateZ(-40deg);
}
.tick_mark:before,
.tick_mark:after {
content: "";
position: absolute;
background-color: var(--ink);
border-radius: 2px;
opacity: 0;
transition: 0.2s ease transform, 0.2s ease opacity;
}
.tick_mark:before {
left: 0;
bottom: 0;
width: 2px;
height: 6px;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.23);
transform: translateY(-68px);
}
.tick_mark:after {
left: 0;
bottom: 0;
width: 12px;
height: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.23);
transform: translateX(78px);
}
.check-div.disabled:active {
transform: none;
}
.checked {
background-color: var(--grey-2);
}
.checked.disabled {
background-color: var(--grey-5);
}
.checked:before {
width: 0;
height: 0;
}
.checked .tick_mark:before,
.checked .tick_mark:after {
transform: translate(0);
opacity: 1;
}
</style>
<Field {label} {labelPosition} {disabled} {error}>
<Checkbox {error} {disabled} {text} {value} on:change={onChange} />
</Field>

View file

@ -1,67 +0,0 @@
<script>
import { View } from "svench";
import Checkbox from "./Checkbox.svelte";
let checked = false
let menu = [
{text: 'Cookies and cream', checked: false},
{text: 'Mint choc chip', checked: false},
{text: 'Raspberry ripple', checked: true}
];
</script>
<View name="Single checkbox">
<Checkbox bind:checked value="value">
<label for="value">One single checkbox with text</label>
</Checkbox>
</View>
<View name="Single disabled checkbox">
<Checkbox disabled checked value="value">
<label for="someOtherValue">A disabled checkbox</label>
</Checkbox>
</View>
<View name="No text">
<Checkbox bind:checked value="somevalue" />
</View>
## Multiple checkboxes
Use an array and an each block to use multiple checkboxes
```svelte
<script>
let menu = [
{text: 'Cookies and cream', checked: false},
{text: 'Mint choc chip', checked: false},
{text: 'Raspberry ripple', checked: true}
];
</script>
{#each menu as {text, checked}}
<Checkbox value={text} bind:checked>
<label for={text}>{text}</label>
</Checkbox>
{/each}
```
<View name="Multiple checkboxes">
<div class="container">
{#each menu as {text, checked}}
<Checkbox value={text} bind:checked>
<label for={text}>{text}</label>
</Checkbox>
{/each}
</div>
</View>
<style>
label {
display: grid;
place-items: center;
}
.container {
display: grid;
grid-gap: 10px;
}
</style>

View file

@ -0,0 +1,39 @@
<script>
import Field from "./Field.svelte"
import Combobox from "./Core/Combobox.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = undefined
export let disabled = false
export let labelPosition = "above"
export let error = null
export let placeholder = "Choose an option"
export let options = []
export let getOptionLabel = option => extractProperty(option, "label")
export let getOptionValue = option => extractProperty(option, "value")
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
const extractProperty = (value, property) => {
if (value && typeof value === "object") {
return value[property]
}
return value
}
</script>
<Field {label} {labelPosition} {disabled} {error}>
<Combobox
{error}
{disabled}
{value}
{options}
{placeholder}
{getOptionLabel}
{getOptionValue}
on:change={onChange} />
</Field>

View file

@ -0,0 +1,43 @@
<script>
import "@spectrum-css/checkbox/dist/index-vars.css"
import "@spectrum-css/fieldgroup/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = false
export let error = null
export let id = null
export let text = null
export let disabled = false
const dispatch = createEventDispatcher()
const onChange = event => {
dispatch("change", event.target.checked)
}
</script>
<label
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized"
class:is-invalid={!!error}>
<input
checked={value}
{disabled}
on:change={onChange}
type="checkbox"
class="spectrum-Checkbox-input"
{id} />
<span class="spectrum-Checkbox-box">
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
<svg
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Dash100" />
</svg>
</span>
<span class="spectrum-Checkbox-label">{text || ''}</span>
</label>

View file

@ -0,0 +1,128 @@
<script>
import "@spectrum-css/inputgroup/dist/index-vars.css"
import "@spectrum-css/popover/dist/index-vars.css"
import "@spectrum-css/menu/dist/index-vars.css"
import { fly } from "svelte/transition"
import { createEventDispatcher } from "svelte"
export let value = null
export let id = null
export let placeholder = "Choose an option"
export let disabled = false
export let error = null
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
const dispatch = createEventDispatcher()
let open = false
let focus = false
$: fieldText = getFieldText(value, options, placeholder)
const getFieldText = (value, options, placeholder) => {
// Always use placeholder if no value
if (value == null || value === "") {
return placeholder || "Choose an option"
}
// Wait for options to load if there is a value but no options
if (!options?.length) {
return ""
}
// Render the label if the selected option is found, otherwise raw value
const selected = options.find(option => getOptionValue(option) === value)
return selected ? getOptionLabel(selected) : value
}
const selectOption = value => {
dispatch("change", value)
open = false
}
const onChange = e => {
selectOption(e.target.value)
}
</script>
<div class="spectrum-InputGroup" class:is-focused={open || focus}>
<div
class="spectrum-Textfield spectrum-InputGroup-textfield"
class:is-disabled={!!error}
class:is-focused={open || focus}>
<input
type="text"
on:focus={() => (focus = true)}
on:blur={() => (focus = false)}
on:change={onChange}
{value}
{placeholder}
class="spectrum-Textfield-input spectrum-InputGroup-input" />
</div>
<button
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
tabindex="-1"
aria-haspopup="true"
disabled={!!error}
on:click={() => (open = true)}>
<svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon spectrum-InputGroup-icon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Chevron100" />
</svg>
</button>
{#if open}
<div class="overlay" on:mousedown|self={() => (open = false)} />
<div
transition:fly={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom is-open">
<ul class="spectrum-Menu" role="listbox">
{#if options && Array.isArray(options)}
{#each options as option}
<li
class="spectrum-Menu-item"
class:is-selected={getOptionValue(option) === value}
role="option"
aria-selected="true"
tabindex="0"
on:click={() => selectOption(getOptionValue(option))}>
<span
class="spectrum-Menu-itemLabel">{getOptionLabel(option)}</span>
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
</li>
{/each}
{/if}
</ul>
</div>
{/if}
</div>
<style>
.spectrum-InputGroup {
min-width: 0;
width: 100%;
}
.spectrum-Textfield-input {
width: 0;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999;
}
.spectrum-Popover {
max-height: 240px;
width: 100%;
z-index: 999;
top: 100%;
}
</style>

View file

@ -0,0 +1,142 @@
<script>
import Flatpickr from "svelte-flatpickr"
import "flatpickr/dist/flatpickr.css"
import "@spectrum-css/inputgroup/dist/index-vars.css"
import "@spectrum-css/textfield/dist/index-vars.css"
import "@spectrum-css/picker/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
import { generateID } from "../../utils/helpers"
export let id = null
export let disabled = false
export let error = null
export let isPlaceholder = false
export let enableTime = true
export let value = null
export let placeholder = null
const dispatch = createEventDispatcher()
const flatpickrId = `${generateID()}-wrapper`
let open = false
let flatpickr
$: flatpickrOptions = {
element: `#${flatpickrId}`,
enableTime: enableTime || false,
altInput: true,
altFormat: enableTime ? "F j Y, H:i" : "F j, Y",
wrap: true,
}
const handleChange = event => {
const [dates] = event.detail
dispatch("change", dates[0])
}
const clearDateOnBackspace = event => {
if (["Backspace", "Clear", "Delete"].includes(event.key)) {
dispatch("change", null)
flatpickr.close()
}
}
const onOpen = () => {
open = true
document.addEventListener("keyup", clearDateOnBackspace)
}
const onClose = () => {
open = false
document.removeEventListener("keyup", clearDateOnBackspace)
// Manually blur all input fields since flatpickr creates a second
// duplicate input field.
// We need to blur both because the focus styling does not get properly
// applied.
const els = document.querySelectorAll(`#${flatpickrId} input`)
els.forEach(el => el.blur())
}
</script>
<Flatpickr
bind:flatpickr
{value}
on:open={onOpen}
on:close={onClose}
options={flatpickrOptions}
on:change={handleChange}
element={`#${flatpickrId}`}>
<div
id={flatpickrId}
class:is-disabled={disabled}
class:is-invalid={!!error}
class="flatpickr spectrum-InputGroup spectrum-Datepicker"
class:is-focused={open}
aria-readonly="false"
aria-required="false"
aria-haspopup="true">
<div
on:click={flatpickr?.open}
class="spectrum-Textfield spectrum-InputGroup-textfield"
class:is-disabled={disabled}
class:is-invalid={!!error}>
{#if !!error}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-icon-18-Alert" />
</svg>
{/if}
<input
data-input
type="text"
{disabled}
class="spectrum-Textfield-input spectrum-InputGroup-input"
{placeholder}
{id}
{value} />
</div>
<button
type="button"
class="spectrum-Picker spectrum-Picker--sizeM spectrum-InputGroup-button"
tabindex="-1"
{disabled}
class:is-invalid={!!error}
on:click={flatpickr?.open}>
<svg
class="spectrum-Icon spectrum-Icon--sizeM"
focusable="false"
aria-hidden="true"
aria-label="Calendar">
<use xlink:href="#spectrum-icon-18-Calendar" />
</svg>
</button>
</div>
</Flatpickr>
{#if open}
<div class="overlay" on:mousedown|self={flatpickr?.close} />
{/if}
<style>
.spectrum-Textfield-input {
pointer-events: none;
}
.spectrum-Textfield:not(.is-disabled):hover {
cursor: pointer;
}
.flatpickr {
width: 100%;
overflow: hidden;
}
.flatpickr .spectrum-Textfield {
width: 100%;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999;
}
</style>

View file

@ -0,0 +1,81 @@
<script>
import Picker from "./Picker.svelte"
import { createEventDispatcher } from "svelte"
export let value = []
export let id = null
export let placeholder = null
export let disabled = false
export let error = null
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
const dispatch = createEventDispatcher()
$: selectedLookupMap = getSelectedLookupMap(value)
$: optionLookupMap = getOptionLookupMap(options)
$: fieldText = getFieldText(value, optionLookupMap, placeholder)
$: isOptionSelected = optionValue => selectedLookupMap[optionValue] === true
$: toggleOption = makeToggleOption(selectedLookupMap, value)
const getFieldText = (value, map, placeholder) => {
if (value?.length) {
if (!map) {
return ""
}
const vals = value.map(option => map[option] || option).join(", ")
return `(${value.length}) ${vals}`
} else {
return placeholder || "Choose some options"
}
}
const getSelectedLookupMap = value => {
let map = {}
if (value?.length) {
value.forEach(option => {
if (option) {
map[option] = true
}
})
}
return map
}
const getOptionLookupMap = options => {
let map = null
if (options?.length) {
map = {}
options.forEach((option, idx) => {
const optionValue = getOptionValue(option, idx)
if (optionValue != null) {
map[optionValue] = getOptionLabel(option, idx) || ""
}
})
}
return map
}
const makeToggleOption = (map, value) => {
return optionValue => {
if (map[optionValue]) {
const filtered = value.filter(option => option !== optionValue)
dispatch("change", filtered)
} else {
dispatch("change", [...value, optionValue])
}
}
}
</script>
<Picker
{id}
{error}
{disabled}
{fieldText}
{options}
isPlaceholder={!value?.length}
{isOptionSelected}
{getOptionLabel}
{getOptionValue}
onSelectOption={toggleOption} />

View file

@ -0,0 +1,123 @@
<script>
import "@spectrum-css/picker/dist/index-vars.css"
import "@spectrum-css/popover/dist/index-vars.css"
import "@spectrum-css/menu/dist/index-vars.css"
import { fly } from "svelte/transition"
import { createEventDispatcher } from "svelte"
import clickOutside from "../../Actions/click_outside"
export let id = null
export let disabled = false
export let error = null
export let fieldText = ""
export let isPlaceholder = false
export let placeholderOption = null
export let options = []
export let isOptionSelected = () => false
export let onSelectOption = () => {}
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let open = false
export let readonly = false
const dispatch = createEventDispatcher()
const onClick = e => {
dispatch("click")
if (readonly) {
return
}
open = true
}
</script>
<button
{id}
class="spectrum-Picker spectrum-Picker--sizeM"
{disabled}
class:is-invalid={!!error}
class:is-open={open}
aria-haspopup="listbox"
on:mousedown={onClick}>
<span class="spectrum-Picker-label" class:is-placeholder={isPlaceholder}>
{fieldText}
</span>
{#if error}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Picker-validationIcon"
focusable="false"
aria-hidden="true"
aria-label="Folder">
<use xlink:href="#spectrum-icon-18-Alert" />
</svg>
{/if}
<svg
class="spectrum-Icon spectrum-UIIcon-ChevronDown100 spectrum-Picker-menuIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Chevron100" />
</svg>
</button>
{#if open}
<div
use:clickOutside={() => (open = false)}
transition:fly={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open">
<ul class="spectrum-Menu" role="listbox">
{#if placeholderOption}
<li
class="spectrum-Menu-item"
class:is-selected={isPlaceholder}
role="option"
aria-selected="true"
tabindex="0"
on:click={() => onSelectOption(null)}>
<span class="spectrum-Menu-itemLabel">{placeholderOption}</span>
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
</li>
{/if}
{#if options && Array.isArray(options)}
{#each options as option, idx}
<li
class="spectrum-Menu-item"
class:is-selected={isOptionSelected(getOptionValue(option, idx))}
role="option"
aria-selected="true"
tabindex="0"
on:click={() => onSelectOption(getOptionValue(option, idx))}>
<span
class="spectrum-Menu-itemLabel">{getOptionLabel(option, idx)}</span>
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
</li>
{/each}
{/if}
</ul>
</div>
{/if}
<style>
.spectrum-Popover {
max-height: 240px;
width: 100%;
z-index: 999;
top: 100%;
}
.spectrum-Picker {
width: 100%;
}
.spectrum-Picker-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 0;
}
</style>

View file

@ -0,0 +1,36 @@
<script>
import "@spectrum-css/fieldgroup/dist/index-vars.css"
import "@spectrum-css/radio/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let direction = "vertical"
export let value = null
export let options = []
export let error = null
export let disabled = false
export let getOptionLabel = option => option
export let getOptionValue = option => option
const dispatch = createEventDispatcher()
const onChange = e => dispatch("change", e.target.value)
</script>
<div class={`spectrum-FieldGroup spectrum-FieldGroup--${direction}`}>
{#if options && Array.isArray(options)}
{#each options as option}
<div
class="spectrum-Radio spectrum-FieldGroup-item spectrum-Radio--emphasized"
class:is-invalid={!!error}>
<input
on:change={onChange}
bind:group={value}
value={getOptionValue(option)}
type="radio"
class="spectrum-Radio-input"
{disabled} />
<span class="spectrum-Radio-button" />
<label class="spectrum-Radio-label">{getOptionLabel(option)}</label>
</div>
{/each}
{/if}
</div>

View file

@ -0,0 +1,82 @@
<script>
import "@spectrum-css/search/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = ""
export let placeholder = null
export let disabled = false
export let id = null
const dispatch = createEventDispatcher()
let focus = false
const updateValue = value => {
dispatch("change", value)
}
const onFocus = () => {
focus = true
}
const onBlur = event => {
focus = false
updateValue(event.target.value)
}
const updateValueOnEnter = event => {
if (event.key === "Enter") {
updateValue(event.target.value)
}
}
</script>
<div class="spectrum-Search" class:is-disabled={disabled}>
<div
class="spectrum-Textfield"
class:is-focused={focus}
class:is-disabled={disabled}>
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-icon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-icon-18-Magnify" />
</svg>
<input
on:click
on:keyup={updateValueOnEnter}
{disabled}
{id}
value={value || ''}
placeholder={placeholder || ''}
on:blur={onBlur}
on:focus={onFocus}
on:input
type="search"
class="spectrum-Textfield-input spectrum-Search-input"
autocomplete="off" />
</div>
<button
on:click={() => updateValue('')}
type="reset"
class="spectrum-ClearButton spectrum-Search-clearButton">
<svg
class="spectrum-Icon spectrum-UIIcon-Cross75"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Cross75" />
</svg>
</button>
</div>
<style>
.spectrum-Search,
.spectrum-Textfield {
width: 100%;
}
.spectrum-Search-input {
padding-right: 24px;
}
.is-disabled {
pointer-events: none;
}
</style>

View file

@ -0,0 +1,57 @@
<script>
import { createEventDispatcher } from "svelte"
import Picker from "./Picker.svelte"
export let value = null
export let id = null
export let placeholder = "Choose an option"
export let disabled = false
export let error = null
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let readonly = false
const dispatch = createEventDispatcher()
let open = false
$: fieldText = getFieldText(value, options, placeholder)
const getFieldText = (value, options, placeholder) => {
// Always use placeholder if no value
if (value == null || value === "") {
return placeholder || "Choose an option"
}
// Wait for options to load if there is a value but no options
if (!options?.length) {
return ""
}
// Render the label if the selected option is found, otherwide raw value
const index = options.findIndex(
(option, idx) => getOptionValue(option, idx) === value
)
return index !== -1 ? getOptionLabel(options[index], index) : value
}
const selectOption = value => {
dispatch("change", value)
open = false
}
</script>
<Picker
on:click
bind:open
{id}
{error}
{disabled}
{readonly}
{fieldText}
{options}
{getOptionLabel}
{getOptionValue}
isPlaceholder={value == null || value === ''}
placeholderOption={placeholder}
isOptionSelected={option => option === value}
onSelectOption={selectOption} />

View file

@ -0,0 +1,27 @@
<script>
import "@spectrum-css/switch/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = false
export let error = null
export let id = null
export let text = null
export let disabled = false
const dispatch = createEventDispatcher()
const onChange = event => {
dispatch("change", event.target.checked)
}
</script>
<div class="spectrum-Switch spectrum-Switch--emphasized">
<input
checked={value}
{disabled}
on:change={onChange}
{id}
type="checkbox"
class="spectrum-Switch-input" />
<span class="spectrum-Switch-switch" />
<label class="spectrum-Switch-label" for={id}>{text}</label>
</div>

View file

@ -0,0 +1,55 @@
<script>
import "@spectrum-css/textfield/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = ""
export let placeholder = null
export let disabled = false
export let error = null
export let id = null
export const getCaretPosition = () => ({
start: textarea.selectionStart,
end: textarea.selectionEnd,
})
let focus = false
let textarea
const dispatch = createEventDispatcher()
const onChange = event => {
dispatch("change", event.target.value)
focus = false
}
</script>
<div
class="spectrum-Textfield spectrum-Textfield--multiline"
class:is-invalid={!!error}
class:is-disabled={disabled}
class:is-focused={focus}>
{#if error}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-icon-18-Alert" />
</svg>
{/if}
<textarea
bind:this={textarea}
placeholder={placeholder || ''}
class="spectrum-Textfield-input"
{disabled}
{id}
on:focus={() => (focus = true)}
on:blur={onChange}>{value || ''}</textarea>
</div>
<style>
.spectrum-Textfield {
width: 100%;
}
textarea {
resize: vertical;
min-height: 80px !important;
}
</style>

View file

@ -0,0 +1,84 @@
<script>
import "@spectrum-css/textfield/dist/index-vars.css"
import { createEventDispatcher } from "svelte"
export let value = ""
export let placeholder = null
export let type = "text"
export let disabled = false
export let error = null
export let id = null
export let readonly = false
const dispatch = createEventDispatcher()
let focus = false
const updateValue = value => {
if (readonly) {
return
}
if (type === "number") {
const float = parseFloat(value)
value = isNaN(float) ? null : float
}
dispatch("change", value)
}
const onFocus = () => {
if (readonly) {
return
}
focus = true
}
const onBlur = event => {
if (readonly) {
return
}
focus = false
updateValue(event.target.value)
}
const updateValueOnEnter = event => {
if (readonly) {
return
}
if (event.key === "Enter") {
updateValue(event.target.value)
}
}
</script>
<div
class="spectrum-Textfield"
class:is-invalid={!!error}
class:is-disabled={disabled}
class:is-focused={focus}>
{#if error}
<svg
class="spectrum-Icon spectrum-Icon--sizeM spectrum-Textfield-validationIcon"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-icon-18-Alert" />
</svg>
{/if}
<input
on:click
on:keyup={updateValueOnEnter}
{disabled}
{readonly}
{id}
value={value || ''}
placeholder={placeholder || ''}
on:blur={onBlur}
on:focus={onFocus}
on:input
{type}
class="spectrum-Textfield-input" />
</div>
<style>
.spectrum-Textfield {
width: 100%;
}
</style>

View file

@ -0,0 +1,10 @@
export { default as CoreTextField } from "./TextField.svelte"
export { default as CoreSelect } from "./Select.svelte"
export { default as CoreMultiselect } from "./Multiselect.svelte"
export { default as CoreCheckbox } from "./Checkbox.svelte"
export { default as CoreRadioGroup } from "./RadioGroup.svelte"
export { default as CoreTextArea } from "./TextArea.svelte"
export { default as CoreCombobox } from "./Combobox.svelte"
export { default as CoreSwitch } from "./Switch.svelte"
export { default as CoreSearch } from "./Search.svelte"
export { default as CoreDatePicker } from "./DatePicker.svelte"

View file

@ -1,158 +0,0 @@
<script>
import Icon from "../Icons/Icon.svelte"
import Label from "../Styleguide/Label.svelte"
import { createEventDispatcher } from "svelte"
export let label = undefined
export let value = ""
export let name = undefined
export let thin = false
export let extraThin = false
export let secondary = false
export let outline = false
export let disabled = false
const dispatch = createEventDispatcher()
let focus = false
const updateValue = e => {
value = e.target.value
}
function handleFocus(e) {
focus = true
dispatch("focus", e)
}
function handleBlur(e) {
focus = false
dispatch("blur", e)
}
</script>
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
<div class="container" class:disabled class:secondary class:outline class:focus>
<select
{name}
class:thin
class:extraThin
class:secondary
{disabled}
on:change
on:focus={handleFocus}
on:blur={handleBlur}
bind:value>
<slot />
</select>
<slot name="custom-input" />
<input
class:thin
class:extraThin
class:secondary
class:disabled
{disabled}
on:change={updateValue}
on:input={updateValue}
on:focus={handleFocus}
on:blur={e => {
updateValue(e)
handleBlur(e)
}}
value={value || ''}
type="text" />
<div class="pointer editable-pointer">
<Icon name="arrowdown" />
</div>
</div>
<style>
.container {
position: relative !important;
display: block;
border-radius: var(--border-radius-s);
border: var(--border-transparent);
background-color: var(--background);
}
.container.outline {
border: var(--border-dark);
}
.container.focus {
border: var(--border-blue);
}
input,
select {
border-radius: var(--border-radius-s);
font-size: var(--font-size-m);
outline: none;
border: none;
color: var(--ink);
text-align: left;
background-color: transparent;
}
select {
display: block !important;
width: 100% !important;
padding: var(--spacing-m) 2rem var(--spacing-m) var(--spacing-m);
appearance: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
align-items: center;
white-space: pre;
opacity: 0;
}
input {
position: absolute;
top: 0;
left: 0;
width: calc(100% - 30px);
height: 100%;
border: none;
box-sizing: border-box;
padding: var(--spacing-m) 0 var(--spacing-m) var(--spacing-m);
}
select.thin,
input.thin {
font-size: var(--font-size-xs);
}
select.extraThin,
input.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) 0 var(--spacing-s) var(--spacing-m);
}
.secondary {
background: var(--grey-2);
}
select:disabled,
input:disabled,
.disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.pointer {
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
position: absolute !important;
pointer-events: none !important;
align-items: center !important;
display: flex !important;
box-sizing: border-box;
}
.editable-pointer {
border-style: solid;
border-width: 0 0 0 1px;
border-color: var(--grey-4);
padding-left: var(--spacing-xs);
}
.editable-pointer :global(svg) {
margin-right: var(--spacing-xs);
fill: var(--ink);
}
</style>

View file

@ -1,57 +0,0 @@
<script>
import { View } from "svench";
import Select from "./Select.svelte";
import DataList from "./DataList.svelte";
import Spacer from "../Spacer/Spacer.svelte"
const options = ["Chocolate", "Vanilla", "Strawberry Cheesecake"];
</script>
<View name="default">
<DataList name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="secondary">
<DataList secondary name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="outline">
<DataList outline name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="disabled">
<DataList disabled name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="thin">
<DataList thin name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>
<View name="extraThin">
<DataList extraThin name="Test" label="Flavour">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</DataList>
</View>

View file

@ -0,0 +1,29 @@
<script>
import Field from "./Field.svelte"
import DatePicker from "./Core/DatePicker.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let labelPosition = "above"
export let disabled = false
export let error = null
export let enableTime = true
export let placeholder = null
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<Field {label} {labelPosition} {disabled} {error}>
<DatePicker
{error}
{disabled}
{value}
{placeholder}
{enableTime}
on:change={onChange} />
</Field>

View file

@ -0,0 +1,42 @@
<script>
import "@spectrum-css/fieldlabel/dist/index-vars.css"
import FieldLabel from "./FieldLabel.svelte"
export let id = null
export let label = null
export let labelPosition = "above"
export let disabled = false
export let error = null
</script>
<div class="spectrum-Form-item" class:above={labelPosition === 'above'}>
{#if label}
<FieldLabel forId={id} {label} position={labelPosition} />
{/if}
<div class="spectrum-Form-itemField">
<slot />
{#if error}
<div class="error">{error}</div>
{/if}
</div>
</div>
<style>
.spectrum-Form-item.above {
display: flex;
flex-direction: column;
}
.spectrum-Form-itemField {
position: relative;
width: 100%;
}
.error {
color: var(
--spectrum-semantic-negative-color-default,
var(--spectrum-global-color-red-500)
);
font-size: var(--spectrum-global-dimension-font-size-75);
margin-top: var(--spectrum-global-dimension-size-75);
}
</style>

View file

@ -0,0 +1,26 @@
<script>
import "@spectrum-css/fieldlabel/dist/index-vars.css"
export let forId
export let label
export let position = "above"
$: className = position === "above" ? "" : `spectrum-FieldLabel--${position}`
</script>
<label
for={forId}
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel ${className}`}>
{label || ''}
</label>
<style>
label {
white-space: nowrap;
}
.spectrum-FieldLabel--right,
.spectrum-FieldLabel--left {
padding-right: var(--spectrum-global-dimension-size-200);
}
</style>

View file

@ -1,190 +1,33 @@
<script>
import Field from "./Field.svelte"
import TextField from "./Core/TextField.svelte"
import { createEventDispatcher } from "svelte"
import Button from "../Button/Button.svelte"
import Label from "../Styleguide/Label.svelte"
const dispatch = createEventDispatcher()
export let name = undefined
export let label = undefined
export let outline = false
export let presentation = false
export let thin = false
export let extraThin = false
export let large = false
export let border = false
export let edit = false
export let value = null
export let label = null
export let labelPosition = "above"
export let placeholder = null
export let type = "text"
export let disabled = false
export let type = undefined
export let placeholder = ""
export let value = ""
export let error = false
export let validator = () => {}
export let readonly = false
export let error = null
// This section handles the edit mode and dispatching of things to the parent when saved
let editMode = false
const updateValue = e => {
if (type === "number") {
const num = parseFloat(e.target.value)
value = isNaN(num) ? "" : num
} else {
value = e.target.value
}
}
const save = () => {
editMode = false
dispatch("save", value)
}
const enableEdit = () => {
editMode = true
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<div class="container">
{#if label || edit}
<div class="label-container">
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
{#if edit}
<div class="controls">
<Button small secondary disabled={editMode} on:click={enableEdit}>
Edit
</Button>
<Button small blue disabled={!editMode} on:click={save}>Save</Button>
</div>
{/if}
</div>
{/if}
<input
class:outline
class:presentation
class:thin
class:extraThin
class:large
class:border
on:change
on:input
on:change={updateValue}
on:input={updateValue}
on:blur={updateValue}
use:validator
disabled={disabled || (edit && !editMode)}
value={value == null ? '' : value}
<Field {label} {labelPosition} {disabled} {error}>
<TextField
{error}
{disabled}
{readonly}
{value}
{placeholder}
{type}
{name}
{placeholder} />
{#if error}
<div class="error">{error}</div>
{/if}
</div>
<style>
.container {
min-width: 0;
display: flex;
flex-direction: column;
}
.label-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
margin-bottom: var(--spacing-s);
}
.label-container :global(label) {
margin-bottom: 0;
}
.controls {
align-items: center;
display: grid;
grid-template-columns: auto auto;
grid-gap: 12px;
margin-left: auto;
padding-left: 12px;
}
.controls :global(button) {
min-width: 100px;
font-size: var(--font-size-s);
border-radius: var(--rounded-small);
}
input {
min-width: 0;
box-sizing: border-box;
color: var(--ink);
font-size: var(--font-size-s);
border-radius: var(--border-radius-s);
border: none;
background-color: var(--grey-2);
padding: var(--spacing-m);
margin: 0;
outline: none;
font-family: var(--font-sans);
border: var(--border-transparent);
transition: all 0.2s ease-in-out;
}
input.presentation {
background-color: var(--background);
border: var(--background) 2px solid;
}
input.presentation:hover {
background-color: var(--grey-2);
border: var(--grey-4) 2px solid;
}
input.thin {
font-size: var(--font-size-xs);
}
input.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) var(--spacing-m);
}
input.large {
font-size: var(--font-size-m);
padding: var(--spacing-l);
}
input.border {
border: var(--border-grey-2);
}
input.border:active {
border: var(--border-blue);
}
input.border:focus {
border: var(--border-blue);
}
input.outline {
border: var(--border-light-2);
background: var(--background);
}
input.outline:active {
border: var(--border-blue);
}
input.outline:focus {
border: var(--border-blue);
}
input:hover {
border: var(--grey-4) 2px solid;
}
input::placeholder {
color: var(--grey-6);
}
input:focus {
border: var(--border-blue);
}
input:disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.error {
margin-top: 10px;
font-size: var(--font-size-xs);
font-family: var(--font-sans);
line-height: 1.17;
color: var(--red);
}
</style>
on:change={onChange}
on:click
on:input />
</Field>

View file

@ -1,62 +0,0 @@
<script>
import { View } from "svench";
import Input from "./Input.svelte";
import Button from "../Button/Button.svelte";
</script>
<View name="default">
<Input placeholder="Enter your name" label="Name" />
</View>
<View name="presentation">
<Input presentation placeholder="Enter your name" label="Name" />
</View>
<View name="outline">
<Input outline placeholder="Enter your name" label="Name" />
</View>
<View name="disabled">
<Input disabled placeholder="Enter your name" label="Name" />
</View>
<View name="disabled with value">
<Input value="Some text" disabled placeholder="Enter your name" label="Name" />
</View>
<View name="thin">
<Input thin placeholder="Enter your name" label="Name" />
</View>
<View name="extraThin">
<Input extraThin placeholder="Enter your name" label="Name" />
</View>
<View name="large">
<Input large placeholder="Enter your name" label="Name" />
</View>
<View name="border">
<Input border presentation placeholder="Enter your name" label="Name" />
</View>
<View name="number">
<Input type="number" placeholder="Enter your age" label="Age" />
</View>
<View name="with edit buttons">
<Input
thin
edit
placeholder="Enter your name"
label="Name"
on:save={console.log} />
</View>
<View name="with error message">
<Input
placeholder="Enter your name"
label="Name"
error="This is an error message!"
on:save={console.log} />
</View>

View file

@ -1,324 +1,35 @@
<script>
import Portal from "svelte-portal"
import { afterUpdate } from "svelte"
import { createEventDispatcher } from "svelte"
import { fly } from "svelte/transition"
import Label from "../Styleguide/Label.svelte"
const xPath =
"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside"
const dispatch = createEventDispatcher()
import Multiselect from "./Core/Multiselect.svelte"
import Field from "./Field.svelte"
export let value = []
export let label = undefined
export let align = "left"
export let secondary = false
export let outline = false
export let label = null
export let disabled = false
export let placeholder = undefined
export let extraThin = false
export let readonly = false
export let labelPosition = "above"
export let error = null
export let placeholder = null
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
let options = []
let optionsVisible = false
let slot
let anchor
$: lookupMap = mapValues(value)
$: selectedOptions = options.filter(option => lookupMap[option.value])
afterUpdate(() => {
// Update available options
const domOptions = Array.from(slot.querySelectorAll("option"))
options = domOptions.map(option => ({
value: option.value,
name: option.textContent,
}))
})
function mapValues(value) {
let map = {}
if (value) {
value.forEach(option => {
map[option] = true
})
}
return map
}
function add(val) {
value = [...value, val]
dispatch("change", value)
}
function remove(val) {
value = value.filter(option => option !== val)
dispatch("change", value)
}
function showOptions(show) {
optionsVisible = show
}
function handleClick() {
showOptions(!optionsVisible)
}
function handleOptionMousedown(e) {
const value = e.target.dataset.value
if (value == null) {
return
}
if (lookupMap[value]) {
remove(value)
} else {
add(value)
}
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
{#if label}
<Label extraSmall grey>{label}</Label>
{/if}
<div class="multiselect" bind:this={anchor}>
<div class="tokens-wrapper">
<div
class="tokens"
class:outline
class:disabled
class:secondary
class:extraThin
class:optionsVisible
on:click|self={handleClick}
class:empty={!value || !value.length}>
{#each selectedOptions as option}
<div
class="token"
class:extraThin
data-id={option.value}
on:click|self={handleClick}>
<span>{option.name}</span>
<div
class="token-remove"
title="Remove {option.name}"
on:click={() => remove(option.value)}>
<svg
class="icon-clear"
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24">
<path d={xPath} />
</svg>
</div>
</div>
{/each}
{#if !value || !value.length}
{#if placeholder && placeholder.length}
<div class:disabled class="placeholder">{placeholder}</div>
{:else}
<div class="placeholder">&nbsp;</div>
{/if}
{/if}
</div>
</div>
<select bind:this={slot} type="multiple" class="hidden">
<slot />
</select>
{#if optionsVisible}
<Portal>
<ul
class="options"
use:positionDropdown={{ anchor, align }}
use:clickOutside={() => showOptions(false)}
transition:fly={{ duration: 200, y: 5 }}
on:mousedown|preventDefault={handleOptionMousedown}>
{#each options as option}
<li
class:selected={lookupMap[option.value]}
data-value={option.value}>
{option.name}
</li>
{/each}
{#if !options.length}
<li class="no-results">No results</li>
{/if}
</ul>
</Portal>
{/if}
</div>
<style>
.multiselect {
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
font-family: var(--font-sans);
min-width: 0;
}
.multiselect:hover {
border-bottom-color: hsl(0, 0%, 50%);
}
.tokens-wrapper {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
flex: 0 1 auto;
}
.tokens {
align-items: center;
display: flex;
flex-wrap: wrap;
position: relative;
width: 0;
flex: 1 1 auto;
background-color: var(--background);
border-radius: var(--border-radius-m);
padding: 0 var(--spacing-m) calc(var(--spacing-m) - var(--spacing-xs))
calc(var(--spacing-m) / 2);
border: var(--border-transparent);
}
.tokens.disabled {
background-color: var(--grey-4);
pointer-events: none;
}
.tokens.outline {
border: var(--border-dark);
}
.tokens.secondary {
background-color: var(--grey-2);
}
.tokens.extraThin {
padding: 0 var(--spacing-m) calc(var(--spacing-s) - var(--spacing-xs))
calc(var(--spacing-m) / 2);
}
.tokens:hover {
cursor: pointer;
}
.tokens.optionsVisible {
border: var(--border-blue);
}
.tokens.empty {
padding: var(--spacing-m);
font-size: var(--font-size-xs);
user-select: none;
}
.tokens.empty.extraThin {
padding: var(--spacing-s) var(--spacing-m);
}
.tokens::after {
width: 100%;
left: 0;
}
.token {
font-size: var(--font-size-xs);
background-color: var(--ink);
color: var(--background);
border-radius: var(--border-radius-l);
display: flex;
flex-direction: row;
align-items: center;
margin: calc(var(--spacing-m) - var(--spacing-xs)) 0 0
calc(var(--spacing-m) / 2);
max-height: 1.3rem;
padding: var(--spacing-xs) var(--spacing-s);
transition: background-color 0.3s;
white-space: nowrap;
overflow: hidden;
}
.token.extraThin {
margin: calc(var(--spacing-s) - var(--spacing-xs)) 0 0
calc(var(--spacing-m) / 2);
}
.token span {
pointer-events: none;
user-select: none;
flex: 1 1 auto;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.token .token-remove {
align-items: center;
background-color: var(--grey-7);
border-radius: 50%;
display: flex;
justify-content: center;
height: 1rem;
width: 1rem;
margin: calc(-1 * var(--spacing-xs)) 0 calc(-1 * var(--spacing-xs))
var(--spacing-xs);
flex: 0 0 auto;
}
.token path {
fill: var(--background);
}
.token .token-remove:hover {
background-color: var(--grey-6);
cursor: pointer;
}
.placeholder {
pointer-events: none;
color: var(--ink);
}
.placeholder.disabled {
color: var(--grey-6);
}
.icon-clear path {
fill: white;
}
.options {
left: 0;
list-style: none;
margin-block-end: 0;
margin-block-start: 0;
overflow-y: auto;
padding-inline-start: 0;
position: absolute;
border: var(--border-dark);
border-radius: var(--border-radius-m);
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
margin: var(--spacing-xs) 0;
padding: var(--spacing-s) 0;
background-color: var(--background);
max-height: 200px;
}
li {
cursor: pointer;
padding: var(--spacing-s) var(--spacing-m);
font-size: var(--font-size-xs);
color: var(--ink);
}
li.selected {
background-color: var(--blue);
color: white;
}
li:not(.selected):hover {
background-color: var(--grey-1);
}
li.no-results:hover {
background-color: white;
cursor: initial;
}
.hidden {
height: 0;
overflow: hidden;
visibility: hidden;
padding: 0;
margin: 0;
border: 0;
outline: 0;
}
</style>
<Field {label} {labelPosition} {disabled} {error}>
<Multiselect
{error}
{disabled}
{value}
{options}
{placeholder}
{getOptionLabel}
{getOptionValue}
on:change={onChange}
on:click />
</Field>

View file

@ -1,63 +0,0 @@
<script>
import { View } from "svench";
import Multiselect from "./Multiselect.svelte";
const options = ["Red", "Blue", "Yellow", "Green", "Pink", "Very long color name to show text wrapping"];
</script>
<View name="default">
<Multiselect name="Test" label="Colours" placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="right aligned">
<div class="max-width">
<Multiselect align="right" name="Test" label="Colours" placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</div>
</View>
<View name="secondary">
<Multiselect name="Test" label="Colours" secondary placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="outline">
<Multiselect name="Test" label="Colours" outline placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="disabled">
<Multiselect name="Test" label="Colours" disabled placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<View name="extraThin">
<Multiselect name="Test" label="Colours" extraThin placeholder="Choose some colours">
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Multiselect>
</View>
<style>
.max-width {
align-self: flex-end;
max-width: 150px;
}
</style>

View file

@ -1,140 +0,0 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let value
export let group
export let name
export let disabled = false
function handleChange() {
if (disabled) return
group = value
dispatch("change", group)
}
</script>
<div class="container">
<input
{disabled}
on:change={handleChange}
{value}
bind:group
type="radio"
{name}
class="checkbox"
id={value} />
<div class="checkbox-container" on:click={handleChange}>
<div class:disabled class="check-div" class:checked={group === value}>
<div class="tick_mark" />
</div>
</div>
<slot />
</div>
<style>
.container {
display: flex;
gap: var(--spacing-s);
}
.checkbox-container {
position: relative;
z-index: 0;
}
.checkbox {
display: none;
}
.check-div {
position: relative;
width: 20px;
height: 20px;
background-color: var(--grey-2);
cursor: pointer;
transition: 0.2s ease transform, 0.2s ease background-color,
0.2s ease box-shadow;
overflow: hidden;
border-radius: 4px;
}
.check-div:before {
content: "";
position: absolute;
top: 50%;
right: 0;
left: 0;
width: 12px;
height: 12px;
margin: 0 auto;
background-color: var(--background);
transform: translateY(-50%);
transition: 0.2s ease width, 0.2s ease height;
border-radius: 2px;
}
.check-div:active {
transform: translateY(-50%) scale(0.9);
}
.tick_mark {
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 4px;
margin: 0 auto;
transform: rotateZ(-40deg);
}
.tick_mark:before,
.tick_mark:after {
content: "";
position: absolute;
background-color: var(--ink);
border-radius: 2px;
opacity: 0;
transition: 0.2s ease transform, 0.2s ease opacity;
}
.tick_mark:before {
left: 0;
bottom: 0;
width: 2px;
height: 6px;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.23);
transform: translateY(-68px);
}
.tick_mark:after {
left: 0;
bottom: 0;
width: 12px;
height: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.23);
transform: translateX(78px);
}
.check-div.disabled:active {
transform: none;
}
.checked {
background-color: var(--grey-2);
}
.checked.disabled {
background-color: var(--grey-5);
}
.checked:before {
width: 0;
height: 0;
}
.checked .tick_mark:before,
.checked .tick_mark:after {
transform: translate(0);
opacity: 1;
}
</style>

View file

@ -1,64 +0,0 @@
<script>
import { View } from "svench";
import Radio from "./Radio.svelte";
let selected = 'Cookies and cream'
let selected2 = 'Mint choc chip'
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
## Multiple checkboxes
Use an array and an each block to use the radio button.
```svelte
<script>
let selected = 'Cookies and cream'
let selected2 = 'Cookies and cream'
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
{#each menu as flavour}
<Radio name="Ice Cream Flavour" value={flavour} bind:group={selected} label={flavour} showLabel/>
{/each}
```
<View name="Multiple radio buttons">
<div class="container">
{#each menu as flavour}
<Radio name="Ice Cream Flavour" value={flavour} bind:group={selected}>
<label for={flavour}>{flavour}</label>
</Radio>
{/each}
</div>
</View>
<View name="Disabled Radio inputs">
<div class="container">
{#each menu as flavour}
<Radio disabled name="Ice Cream Flavour" value={flavour} bind:group={selected2}>
<label for={flavour}>{flavour}</label>
</Radio>
{/each}
</div>
</View>
<style>
label {
display: grid;
place-items: center;
}
.container {
display: grid;
grid-gap: 10px;
}
</style>

View file

@ -0,0 +1,37 @@
<script>
import Field from "./Field.svelte"
import RadioGroup from "./Core/RadioGroup.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let disabled = false
export let labelPosition = "above"
export let error = null
export let options = []
export let getOptionLabel = option => extractProperty(option, "label")
export let getOptionValue = option => extractProperty(option, "value")
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
const extractProperty = (value, property) => {
if (value && typeof value === "object") {
return value[property]
}
return value
}
</script>
<Field {label} {labelPosition} {disabled} {error}>
<RadioGroup
{error}
{disabled}
{value}
{options}
{getOptionLabel}
{getOptionValue}
on:change={onChange} />
</Field>

View file

@ -1,59 +0,0 @@
<script>
import * as Quill from "quill"
import * as MarkdownIt from "markdown-it"
import TurndownService from "turndown"
import { onMount } from "svelte"
import "quill/dist/quill.snow.css"
const convertMarkdown = new MarkdownIt()
convertMarkdown.set({
html: true,
})
const turndownService = new TurndownService()
export let value = ""
export let options = null
export let width = 400
let quill
let container
let defaultOptions = {
modules: {
toolbar: [
[{ header: [1, 2, 3, false] }],
["bold", "italic", "underline", "strike"],
],
},
placeholder: "Type something...",
theme: "snow", // or 'bubble'
}
let mergedOptions = { ...defaultOptions, ...options }
const updateContent = () => {
value = turndownService.turndown(quill.container.firstChild.innerHTML)
}
onMount(() => {
quill = new Quill(container, mergedOptions)
if (value)
quill.clipboard.dangerouslyPasteHTML(convertMarkdown.render(value + "\n"))
quill.on("text-change", updateContent)
return () => {
quill.off("text-change", updateContent)
}
})
</script>
<svelte:head>
{#if mergedOptions.theme !== 'snow'}
<link
rel="stylesheet"
href="//cdn.quilljs.com/1.3.6/quill.{mergedOptions.theme}.css" />
{/if}
</svelte:head>
<div style="width: {width}px">
<div bind:this={container} />
</div>

View file

@ -1,40 +0,0 @@
<script>
import { View } from "svench";
import RichText from "./RichText.svelte";
const options = { placeholder: "this is not the default value!" };
let value;
</script>
### Rich Text Component
This component uses the QuillJS library to add Rich Text editing functionality.
It exposes a <code>content</code> variable that you can bind to in order to get Markdown out of the component.
As well as the content you can also pass in an option object that looks like so:
```js
let options = {
modules: {
toolbar: [
[{ header: [1, 2, 3, false] }],
['bold', 'italic', 'underline', 'strike']
]
},
placeholder: 'Type something...',
theme: 'snow'
}
```
<View name="default">
<RichText bind:value />
</View>
<View name="passing in Markdown">
<RichText value="# This is an h1 heading!" />
</View>
<View name="passing in custom options">
<RichText {options} />
</View>

View file

@ -0,0 +1,27 @@
<script>
import Field from "./Field.svelte"
import Search from "./Core/Search.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let labelPosition = "above"
export let placeholder = null
export let disabled = false
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<Field {label} {labelPosition} {disabled}>
<Search
{disabled}
{value}
{placeholder}
on:change={onChange}
on:click
on:input />
</Field>

View file

@ -1,95 +1,42 @@
<script>
import Icon from "../Icons/Icon.svelte"
import Label from "../Styleguide/Label.svelte"
import Field from "./Field.svelte"
import Select from "./Core/Select.svelte"
import { createEventDispatcher } from "svelte"
export let value = ""
export let name = undefined
export let value = null
export let label = undefined
export let thin = false
export let extraThin = false
export let secondary = false
export let outline = false
export let disabled = false
export let readonly = false
export let labelPosition = "above"
export let error = null
export let placeholder = "Choose an option"
export let options = []
export let getOptionLabel = option => extractProperty(option, "label")
export let getOptionValue = option => extractProperty(option, "value")
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
const extractProperty = (value, property) => {
if (value && typeof value === "object") {
return value[property]
}
return value
}
</script>
<div>
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
<div class="relative">
<select
{name}
class:thin
class:extraThin
class:secondary
class:outline
{disabled}
on:change
bind:value>
<slot />
</select>
<div class="pointer">
<Icon name="arrowdown" />
</div>
</div>
</div>
<style>
select {
font-family: var(--font-sans);
display: block !important;
width: 100% !important;
border-radius: var(--border-radius-s);
border: none;
text-align: left;
color: var(--ink);
font-size: var(--font-size-s);
padding: var(--spacing-m) 2rem var(--spacing-m) var(--spacing-m) !important;
appearance: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
align-items: center;
white-space: pre;
outline: none;
border: var(--border-transparent);
background-color: var(--background);
}
select.thin {
padding: var(--spacing-m);
font-size: var(--font-size-xs);
}
select.extraThin {
padding: var(--spacing-s) 2rem var(--spacing-s) var(--spacing-m) !important;
font-size: var(--font-size-xs);
}
select.secondary {
background: var(--grey-2);
}
select.outline {
border: var(--border-light-2);
}
select:focus {
border: var(--border-blue);
}
select:disabled {
background: var(--grey-4);
color: var(--grey-6);
}
.relative {
position: relative !important;
display: block;
}
.pointer {
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
position: absolute !important;
pointer-events: none !important;
padding-left: 0.5rem !important;
align-items: center !important;
display: flex !important;
color: var(--ink);
}
</style>
<Field {label} {labelPosition} {disabled} {error}>
<Select
{error}
{disabled}
{readonly}
{value}
{options}
{placeholder}
{getOptionLabel}
{getOptionValue}
on:change={onChange}
on:click />
</Field>

View file

@ -1,62 +0,0 @@
<script>
import { View } from "svench";
import Select from "./Select.svelte";
import Spacer from "../Spacer/Spacer.svelte"
const options = ["Chocolate", "Vanilla", "Strawberry Cheesecake"];
</script>
<View name="default">
<Select name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="secondary">
<Select secondary name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="outline">
<Select outline name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="disabled">
<Select disabled name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="thin">
<Select thin name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>
<View name="extraThin">
<Select extraThin name="Test" label="Flavour">
<option value="">Choose an option</option>
{#each options as option}
<option value={option}>{option}</option>
{/each}
</Select>
</View>

View file

@ -1,88 +0,0 @@
<script>
import Label from "../Styleguide/Label.svelte"
export let label
export let min = 0
export let max = 100
export let step = 1
export let value
export let showValue = false
export let showRange = false
</script>
<div>
{#if label}
<Label extraSmall grey>
{label}
{#if showValue && value != null}({value}){/if}
</Label>
{/if}
<div class="container">
{#if showRange && min != null}<span>{min}</span>{/if}
<input type="range" bind:value {min} {max} {step} />
{#if showRange && max != null}<span>{max}</span>{/if}
</div>
</div>
<style>
.container {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-s);
}
span {
flex: 0 0 auto;
color: var(--grey-5);
font-family: var(--font-sans);
font-size: var(--font-size-xs);
font-weight: 400;
}
input[type="range"] {
width: 100%;
margin: 0;
background-color: transparent;
-webkit-appearance: none;
flex: 1 1 auto;
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-webkit-slider-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
-webkit-appearance: none;
border: none;
margin-top: 2px;
}
input[type="range"]::-moz-range-track {
background: var(--grey-4);
border-radius: 9px;
width: 100%;
height: 18px;
cursor: pointer;
padding: 0 2px;
}
input[type="range"]::-moz-range-thumb {
width: 14px;
height: 14px;
background: white;
border-radius: 100%;
cursor: pointer;
border: none;
}
</style>

View file

@ -1,26 +0,0 @@
<script>
import { View } from "svench";
import Slider from "./Slider.svelte";
</script>
<View name="default">
<Slider label=Quantity value="50" />
</View>
<View name="show value">
<Slider label="Quantity" value="50" showValue />
</View>
<View name="show range">
<Slider label="Quantity" value="25" showValue showRange min="0" max="100" />
</View>
<View name="custom min and max">
<Slider label="Quantity" value="350" showValue showRange min="50" max="500" />
</View>
<View name="custom step">
<Slider label="Quantity" value="25" step="25" showValue showRange min="0" max="100" />
</View>

View file

@ -1,132 +1,29 @@
<script>
import Field from "./Field.svelte"
import TextArea from "./Core/TextArea.svelte"
import { createEventDispatcher } from "svelte"
import Button from "../Button/Button.svelte"
import Label from "../Styleguide/Label.svelte"
import text_area_resize from "../Actions/autoresize_textarea.js"
export let value = null
export let label = null
export let labelPosition = "above"
export let placeholder = null
export let disabled = false
export let error = null
export let getCaretPosition = null
const dispatch = createEventDispatcher()
export let name = false
export let label = false
export let thin = false
export let extraThin = false
export let edit = false
export let disabled = false
export let placeholder
export let validator = () => {}
export let value = ""
export const getCaretPosition = () => {
return { start: textarea.selectionStart, end: textarea.selectionEnd }
}
let textarea
// This section handles the edit mode and dispatching of things to the parent when saved
let editMode = false
const save = () => {
editMode = false
dispatch("save", value)
}
const enableEdit = () => {
editMode = true
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<div class="container">
{#if label || edit}
<div class="label-container">
{#if label}
<Label extraSmall grey forAttr={name}>{label}</Label>
{/if}
{#if edit}
<div class="controls">
<Button small secondary disabled={editMode} on:click={enableEdit}>
Edit
</Button>
<Button small blue disabled={!editMode} on:click={save}>Save</Button>
</div>
{/if}
</div>
{/if}
<textarea
class:thin
class:extraThin
bind:value
bind:this={textarea}
on:change
disabled={disabled || (edit && !editMode)}
<Field {label} {labelPosition} {disabled} {error}>
<TextArea
bind:getCaretPosition
{error}
{disabled}
{value}
{placeholder}
{name}
use:text_area_resize />
</div>
<style>
.container {
min-width: 0;
display: flex;
flex-direction: column;
}
.label-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
margin-bottom: var(--spacing-s);
}
.label-container :global(label) {
margin-bottom: 0;
}
.controls {
align-items: center;
display: grid;
grid-template-columns: auto auto;
grid-gap: 12px;
margin-left: auto;
padding-left: 12px;
}
.controls :global(button) {
min-width: 100px;
font-size: var(--font-size-s);
border-radius: var(--rounded-small);
}
textarea {
min-width: 0;
color: var(--ink);
font-size: var(--font-size-s);
font-family: var(--font-sans);
border: none;
border-radius: var(--border-radius-s);
background-color: var(--grey-2);
padding: var(--spacing-m);
margin: 0;
border: var(--border-transparent);
outline: none;
}
textarea::placeholder {
color: var(--grey-6);
}
textarea.thin {
font-size: var(--font-size-xs);
}
textarea.extraThin {
font-size: var(--font-size-xs);
padding: var(--spacing-s) var(--spacing-m);
}
textarea:focus {
border: var(--border-blue);
}
textarea:disabled {
background: var(--grey-4);
}
textarea:disabled {
background: var(--grey-4);
}
textarea:disabled::placeholder {
color: var(--grey-6);
}
</style>
on:change={onChange} />
</Field>

View file

@ -1,30 +0,0 @@
<script>
import { View } from "svench";
import TextArea from "./TextArea.svelte";
import Button from "../Button/Button.svelte";
</script>
<View name="default">
<TextArea placeholder="Enter your email text" label="Email Body" />
</View>
<View name="disabled">
<TextArea disabled placeholder="Enter your email text" label="Email Body" />
</View>
<View name="no label">
<TextArea placeholder="Enter your email text" />
</View>
<View name="thin">
<TextArea thin placeholder="Enter your email text" label="Email Body" />
</View>
<View name="extraThin">
<TextArea extraThin placeholder="Enter your email text" label="Email Body" />
</View>
<View name="with buttons">
<TextArea edit placeholder="Enter your email text" label="Email Body" />
</View>

View file

@ -1,110 +1,22 @@
<script>
export let name = undefined
export let text = ""
export let checked = false
import Field from "./Field.svelte"
import Switch from "./Core/Switch.svelte"
import { createEventDispatcher } from "svelte"
export let value = null
export let label = null
export let labelPosition = "above"
export let text = null
export let disabled = false
export let screenreader = true
export let thin = false
export let error = null
const dispatch = createEventDispatcher()
const onChange = e => {
value = e.detail
dispatch("change", e.detail)
}
</script>
<label for={name} class="container">
<div class="toggle">
<input
id={name}
{name}
type="checkbox"
class:screenreader
{disabled}
bind:checked
on:change />
<div class="track">
<div class="thumb" />
</div>
</div>
{#if text}<span class="text" class:thin>{text}</span>{/if}
</label>
<style>
.container {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.container:disabled {
background-color: var(--grey-2);
cursor: not-allowed;
}
.toggle {
position: relative;
display: inline-block;
align-self: center;
cursor: pointer;
-webkit-user-select: none;
background: transparent;
}
.track {
width: 32px;
height: 18px;
background-color: var(--grey-4);
border-radius: 9px;
transition-delay: 0.12s;
transition-duration: 0.2s;
transition-property: background;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
position: relative;
}
.thumb {
cursor: pointer;
position: absolute;
transition-duration: 0.28s;
transition-property: all;
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
top: 2px;
left: 2px;
width: 14px;
height: 14px;
background-color: white;
border-radius: var(--border-radius-xl);
}
input[type="checkbox"]:checked ~ .track .thumb {
transform: translateX(14px);
}
input[type="checkbox"]:checked ~ .track {
background-color: var(--blue);
}
input[type="checkbox"]:disabled ~ .track {
background-color: var(--grey-4);
cursor: not-allowed;
}
input[type="checkbox"]:disabled ~ .track .thumb {
background-color: var(--grey-2);
cursor: not-allowed;
}
.screenreader {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.text {
font-size: var(--font-size-s);
font-family: var(--font-sans);
cursor: pointer;
user-select: none;
color: var(--ink);
}
.text.thin {
font-size: var(--font-size-xs);
}
</style>
<Field {label} {labelPosition} {disabled} {error}>
<Switch {error} {disabled} {text} {value} on:change={onChange} />
</Field>

View file

@ -1,21 +0,0 @@
<script>
import { View } from "svench";
import Toggle from "./Toggle.svelte";
</script>
<View name="default">
<Toggle />
</View>
<View name="checked with text">
<Toggle text="Display on mobile?" />
</View>
<View name="thin">
<Toggle text="Display on mobile?" thin />
</View>
<View name="disabled">
<Toggle disabled={true} />
</View>

View file

@ -1,19 +1,30 @@
<script context="module">
export const directions = ["n", "ne", "e", "se", "s", "sw", "w", "nw"]
export const directions = ["n", "ne", "e", "se", "s", "sw", "w", "nw"]
</script>
<script>
export let direction = "n"
export let name = "Add"
export let hidden = true
export let s = false
export let m = false;
export let l = false;
export let xl = false
export let direction = "n"
export let name = "Add"
export let hidden = false
export let s = false
export let m = false
export let l = false
export let xl = false
$: rotation = directions.indexOf(direction) * 45
$: rotation = directions.indexOf(direction) * 45
$: useDefault = ![s, m, l, xl].includes(true)
</script>
<svg on:click class:spectrum-Icon--sizeS={s} class:spectrum-Icon--sizeM={m} class:spectrum-Icon--sizeL={l} class:spectrum-Icon--sizeXL={xl} class="spectrum-Icon" focusable="false" aria-hidden={hidden} aria-label="{name}" style={`transform: rotate(${rotation}deg)`}>
<use xlink:href="#spectrum-icon-18-{name}" />
</svg>
<svg
on:click
class:spectrum-Icon--sizeS={s}
class:spectrum-Icon--sizeM={m || useDefault}
class:spectrum-Icon--sizeL={l}
class:spectrum-Icon--sizeXL={xl}
class="spectrum-Icon"
focusable="false"
aria-hidden={hidden}
aria-label={name}
style={`transform: rotate(${rotation}deg)`}>
<use xlink:href="#spectrum-icon-18-{name}" />
</svg>

View file

@ -1,33 +0,0 @@
<script>
import Input from "../Form/Input.svelte"
export let categories = [
{
name: "Customers List - Data Row",
items: [
{ name: "Name", id: "chjaHICHc82h2" },
{ name: "Created", id: "chjaHICgr56Hc82h2" },
{ name: "Status", id: "chjaHICHc8646462h2" },
],
},
{
name: "Home Screen Components",
items: [{ name: "Title", id: "chjaHICHc82h2" }],
},
]
</script>
<div class="container">
<Input thin placeholder="Search" />
{#each categories as { name, items }}
<div class="title">{name}</div>
<ul>
{#each items as { name, id }}
<li>{name}</li>
{/each}
</ul>
{/each}
</div>
<style>
</style>

View file

@ -1,8 +0,0 @@
<script>
import Search from "./Search.svelte";
import { View } from "svench";
</script>
<View name="Name">
<Search />
</View>

View file

@ -5,8 +5,9 @@
import { fade, fly } from "svelte/transition"
import Portal from "svelte-portal"
import Context from "../context"
const dispatch = createEventDispatcher()
import clickOutside from "../Actions/click_outside"
const dispatch = createEventDispatcher()
let visible = false
$: dispatch(visible ? "show" : "hide")
@ -31,8 +32,8 @@
}
async function focusFirstInput(node) {
const inputs = node.querySelectorAll('input')
if (inputs) {
const inputs = node.querySelectorAll("input")
if (inputs?.length) {
await tick()
inputs[0].focus()
}
@ -45,12 +46,58 @@
{#if visible}
<Portal target=".modal-container">
<div use:focusFirstInput class="spectrum-Underlay is-open" transition:fade={{ duration: 200 }} on:click|self={hide}>
<div class="spectrum-Modal-wrapper">
<div class="spectrum-Modal is-open" transition:fly={{ y: 30, duration: 200 }}>
<slot />
<div
class="spectrum-Underlay is-open"
transition:fade={{ duration: 200 }}
on:mousedown|self={hide}>
<div class="modal-wrapper" on:mousedown|self={hide}>
<div class="modal-inner-wrapper" on:mousedown|self={hide}>
<div
use:focusFirstInput
class="spectrum-Modal is-open"
transition:fly={{ y: 30, duration: 200 }}>
<slot />
</div>
</div>
</div>
</div>
</Portal>
{/if}
{/if}
<style>
.spectrum-Underlay {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
z-index: 999;
overflow: auto;
overflow-x: hidden;
}
.modal-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: row;
-moz-box-pack: center;
justify-content: center;
align-items: flex-start;
max-height: 100%;
}
.modal-inner-wrapper {
flex: 1 1 auto;
display: flex;
flex-direction: row;
-moz-box-pack: center;
justify-content: center;
align-items: flex-start;
width: 0;
}
.spectrum-Modal {
overflow: visible;
max-height: none;
margin: 40px 0;
transform: none;
}
</style>

View file

@ -1,4 +1,5 @@
<script>
import "@spectrum-css/dialog/dist/index-vars.css"
import { getContext } from "svelte"
import Button from "../Button/Button.svelte"
import Heading from "../Typography/Heading.svelte"
@ -29,36 +30,38 @@
}
</script>
<div class="spectrum-Dialog spectrum-Dialog--{size}" role="dialog" tabindex="-1" aria-modal="true">
<div
class="spectrum-Dialog spectrum-Dialog--{size}"
role="dialog"
tabindex="-1"
aria-modal="true">
<div class="spectrum-Dialog-grid">
<Heading m h2>{title}</Heading>
<h1 class="spectrum-Dialog-heading spectrum-Dialog-heading--noHeader">
{title}
</h1>
<Divider m />
<!-- TODO: Remove content-grid class once Layout components are in bbui -->
<section class="spectrum-Dialog-content content-grid">
<slot />
</section>
{#if showCancelButton || showConfirmButton}
<div class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter">
<!-- <footer class="footer-content">
<slot name="footer" />
</footer> -->
<div class="spectrum-ButtonGroup-item">
<slot name="footer" />
{#if showCancelButton}
<Button secondary on:click={hide}>{cancelText}</Button>
{/if}
{#if showConfirmButton}
<Button
<div
class="spectrum-ButtonGroup spectrum-Dialog-buttonGroup spectrum-Dialog-buttonGroup--noFooter">
<slot name="footer" />
{#if showCancelButton}
<Button group secondary on:click={hide}>{cancelText}</Button>
{/if}
{#if showConfirmButton}
<Button
group
cta
primary
{...$$restProps}
disabled={confirmDisabled}
on:click={confirm}>
{confirmText}
</Button>
{/if}
</div>
{...$$restProps}
disabled={confirmDisabled}
on:click={confirm}>
{confirmText}
</Button>
{/if}
</div>
{/if}
{#if showCloseIcon}
@ -69,7 +72,6 @@
</div>
</div>
<style>
.content-grid {
display: grid;
@ -78,9 +80,14 @@
color: var(--ink);
}
h1 {
font-weight: normal;
.spectrum-Dialog-content {
overflow: visible;
}
.spectrum-Dialog-buttonGroup {
gap: var(--spectrum-global-dimension-static-size-200);
}
.close-icon {
position: absolute;
top: 15px;
@ -95,27 +102,4 @@
.close-icon :global(svg) {
margin-right: 0;
}
footer {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: var(--spacing-m);
}
.footer-content {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: var(--spacing-m);
}
</style>

View file

@ -30,7 +30,6 @@
<Portal>
<div
tabindex="0"
class:open
use:positionDropdown={{ anchor, align }}
use:clickOutside={hide}
on:keydown={handleEscape}
@ -39,30 +38,3 @@
</div>
</Portal>
{/if}
<style>
.menu-container {
position: fixed;
margin-top: var(--spacing-xs);
padding: var(--spacing-xl);
outline: none;
box-sizing: border-box;
opacity: 0;
min-width: 400px;
z-index: 2;
color: var(--ink);
height: fit-content !important;
border: var(--border-dark);
border-radius: var(--border-radius-m);
transform: scale(0);
transition: opacity 0.13s linear, transform 0.12s cubic-bezier(0, 0, 0.2, 1);
overflow-y: auto;
background-color: var(--background);
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.15);
}
.open {
transform: scale(1);
opacity: 1;
}
</style>

View file

@ -1,69 +1,14 @@
<script>
export let forAttr = "",
extraSmall = false,
small = false,
medium = false,
large = false,
extraLarge = false,
white = false,
grey = false,
black = false
import "@spectrum-css/fieldlabel/dist/index-vars.css"
</script>
<label
class="bb-label"
class:extraSmall
class:small
class:medium
class:large
class:extraLarge
class:white
class:grey
class:black
for={forAttr}>
class={`spectrum-FieldLabel spectrum-FieldLabel--sizeM spectrum-Form-itemLabel`}>
<slot />
</label>
<style>
.bb-label {
font-family: var(--font-sans);
font-weight: 500;
text-rendering: var(--text-render);
color: var(--ink);
font-size: var(--font-size-s);
margin-bottom: var(--spacing-s);
display: block;
}
.extraSmall {
font-size: var(--font-size-xs);
}
.small {
font-size: var(--font-size-s);
}
.medium {
font-size: var(--font-size-m);
}
.large {
font-size: var(--font-size-l);
}
.extraLarge {
font-size: var(--font-size-xl);
}
.white {
color: white;
}
.grey {
color: var(--grey-6);
}
.black {
color: var(--ink);
label {
white-space: nowrap;
}
</style>

View file

@ -1,6 +1,6 @@
<script>
import "@spectrum-css/checkbox/dist/index-vars.css"
import "@spectrum-css/actionbutton/dist/index-vars.css"
import Checkbox from "../Form/Checkbox.svelte"
import ActionButton from "../ActionButton/ActionButton.svelte"
export let selected
export let onToggleSelection
@ -10,39 +10,8 @@
</script>
{#if allowSelectRows}
<label
class="spectrum-Checkbox spectrum-Checkbox--sizeM spectrum-Checkbox--emphasized">
<input
type="checkbox"
class="spectrum-Checkbox-input"
id="checkbox-1"
bind:checked={selected} />
<span class="spectrum-Checkbox-box">
<svg
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Checkbox-checkmark"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Checkmark100" />
</svg>
<svg
class="spectrum-Icon spectrum-UIIcon-Dash100 spectrum-Checkbox-partialCheckmark"
focusable="false"
aria-hidden="true">
<use xlink:href="#spectrum-css-icon-Dash100" />
</svg>
</span>
</label>
<Checkbox value={selected} />
{/if}
{#if allowEditRows}
<button
class="spectrum-ActionButton spectrum-ActionButton--sizeS"
on:click={onEdit}>
<span class="spectrum-ActionButton-label">Edit</span>
</button>
<ActionButton size="s" on:click={onEdit}>Edit</ActionButton>
{/if}
<style>
label {
margin-right: 5px;
}
</style>

View file

@ -8,7 +8,7 @@
export let schema = {}
export let showAutoColumns = false
export let rowCount = 0
export let quiet = true
export let quiet = false
export let loading = false
export let allowSelectRows = true
export let allowEditRows = true
@ -70,10 +70,10 @@
}
const getContentStyle = (visibleRows, rowCount) => {
if (!rowCount) {
if (!rowCount || !visibleRows) {
return ""
}
return `height: ${headerHeight - 1 + visibleRows * (rowHeight + 1)}px;`
return `height: ${headerHeight + visibleRows * (rowHeight + 1)}px;`
}
const sortRows = (rows, sortColumn, sortOrder) => {
@ -266,7 +266,9 @@
</td>
{/if}
{#each fields as field}
<td class="spectrum-Table-cell">
<td
class="spectrum-Table-cell"
class:spectrum-Table-cell--divider={!!schema[field].divider}>
<div class="spectrum-Table-cell-content">
<CellRenderer
{customRenderers}
@ -301,7 +303,7 @@
<style>
.wrapper {
background-color: var(--spectrum-alias-background-color-primary);
background-color: var(--spectrum-alias-background-color-default);
overflow: hidden;
position: relative;
z-index: 1;
@ -311,26 +313,23 @@
height: 100%;
position: relative;
overflow: auto;
border: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
scrollbar-width: thin;
scrollbar-color: var(--spectrum-global-color-gray-400)
var(--spectrum-alias-background-color-primary);
var(--spectrum-alias-background-color-default);
}
.container::-webkit-scrollbar {
width: 16px;
height: 16px;
width: 10px;
height: 10px;
}
.container::-webkit-scrollbar-track {
background: var(--spectrum-alias-background-color-primary);
background: var(--spectrum-alias-background-color-default);
}
.container::-webkit-scrollbar-thumb {
background-color: var(--spectrum-global-color-gray-400);
border-radius: 20px;
border: 4px solid var(--spectrum-alias-background-color-primary);
border-radius: 4px;
}
.container::-webkit-scrollbar-corner {
background: var(--spectrum-alias-background-color-primary);
background: var(--spectrum-alias-background-color-default);
}
.container.quiet {
border: none !important;
@ -359,18 +358,15 @@
transition: opacity 0.2s ease;
}
.container,
th {
border-bottom: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
}
th {
vertical-align: middle;
height: var(--header-height);
position: sticky;
top: 0;
z-index: 2;
background-color: var(--spectrum-alias-background-color-primary);
background-color: var(--spectrum-alias-background-color-default);
border-bottom: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
}
.spectrum-Table-headCell-content {
white-space: nowrap;
@ -421,19 +417,20 @@
padding-top: 0;
padding-bottom: 0;
border-bottom: none !important;
border-left: none !important;
border-right: none !important;
border-top: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
border-radius: 0 !important;
}
tr:first-child td {
border-top: none !important;
}
.container:not(.quiet) td.spectrum-Table-cell--divider {
width: 1px;
border-right: 1px solid
tr:last-child td {
border-bottom: 1px solid
var(--spectrum-table-border-color, var(--spectrum-alias-border-color-mid)) !important;
}
td.spectrum-Table-cell--divider {
width: 1px;
}
.spectrum-Table-cell-content {
height: var(--row-height);
white-space: nowrap;

View file

@ -1,78 +1,28 @@
<script>
import "@spectrum-css/typography/dist/index-vars.css"
import "@spectrum-css/typography/dist/index-vars.css"
// Level
export let h1 = false;
export let h2 = false;
export let h3 = false;
export let h4 = false;
// Sizes
export let xxxl = false;
export let xxl = false;
export let xl = false;
export let l = false;
export let m = false;
export let s = false;
export let xs = false;
export let xxs = false;
export let serif = false;
// Sizes
export let xxxl = false
export let xxl = false
export let xl = false
export let l = false
export let m = false
export let s = false
export let xs = false
export let xxs = false
$: useDefault = ![xxxl, xxl, xl, l, m, s, xs, xxs].includes(true)
</script>
{#if h1}
<h1 class="spectrum-Heading"
class:spectrum-Heading--serif={serif}
class:spectrum-Heading--sizeXXXL={xxxl}
class:spectrum-Heading--sizeXXL={xxl}
class:spectrum-Heading--sizeXL={xl}
class:spectrum-Heading--sizeL={l}
class:spectrum-Heading--sizeM={m}
class:spectrum-Heading--sizeS={s}
class:spectrum-Heading--sizeXS={xs}
class:spectrum-Heading--sizeXXS={xxs}>
<slot />
</h1>
{:else if h2}
<h2 class="spectrum-Heading"
class:spectrum-Heading--serif={serif}
class:spectrum-Heading--sizeXXXL={xxxl}
class:spectrum-Heading--sizeXXL={xxl}
class:spectrum-Heading--sizeXL={xl}
class:spectrum-Heading--sizeL={l}
class:spectrum-Heading--sizeM={m}
class:spectrum-Heading--sizeS={s}
class:spectrum-Heading--sizeXS={xs}
class:spectrum-Heading--sizeXXS={xxs}>
<slot />
</h2>
{:else if h3}
<h3 class="spectrum-Heading"
class:spectrum-Heading--serif={serif}
class:spectrum-Heading--sizeXXXL={xxxl}
class:spectrum-Heading--sizeXXL={xxl}
class:spectrum-Heading--sizeXL={xl}
class:spectrum-Heading--sizeL={l}
class:spectrum-Heading--sizeM={m}
class:spectrum-Heading--sizeS={s}
class:spectrum-Heading--sizeXS={xs}
class:spectrum-Heading--sizeXXS={xxs}>
<slot />
</h3>
{:else if h4}
<h4 class="spectrum-Heading"
class:spectrum-Heading--serif={serif}
class:spectrum-Heading--sizeXXXL={xxxl}
class:spectrum-Heading--sizeXXL={xxl}
class:spectrum-Heading--sizeXL={xl}
class:spectrum-Heading--sizeL={l}
class:spectrum-Heading--sizeM={m}
class:spectrum-Heading--sizeS={s}
class:spectrum-Heading--sizeXS={xs}
class:spectrum-Heading--sizeXXS={xxs}>
<slot />
</h4>
{:else}
SPECIFY HEADING SIZE!
{/if}
<h1
class="spectrum-Heading"
class:spectrum-Heading--sizeXXXL={xxxl}
class:spectrum-Heading--sizeXXL={xxl}
class:spectrum-Heading--sizeXL={xl}
class:spectrum-Heading--sizeL={l}
class:spectrum-Heading--sizeM={m || useDefault}
class:spectrum-Heading--sizeS={s}
class:spectrum-Heading--sizeXS={xs}
class:spectrum-Heading--sizeXXS={xxs}>
<slot />
</h1>

View file

@ -1,11 +1,13 @@
import "./bbui.css"
// Spectrum icons
import "@spectrum-css/icon/dist/index-vars.css"
// Components
export { default as Input } from "./Form/Input.svelte"
export { default as TextArea } from "./Form/TextArea.svelte"
export { default as RichText } from "./Form/RichText.svelte"
export { default as Select } from "./Form/Select.svelte"
export { default as DataList } from "./Form/DataList.svelte"
export { default as Combobox } from "./Form/Combobox.svelte"
export { default as Dropzone } from "./Dropzone/Dropzone.svelte"
export { default as Drawer } from "./Drawer/Drawer.svelte"
export { default as ActionButton } from "./ActionButton/ActionButton.svelte"
@ -14,7 +16,7 @@ export { default as ActionMenu } from "./ActionMenu/ActionMenu.svelte"
export { default as Button } from "./Button/Button.svelte"
export { default as Icon, directions } from "./Icon/Icon.svelte"
export { default as Toggle } from "./Form/Toggle.svelte"
export { default as Radio } from "./Form/Radio.svelte"
export { default as RadioGroup } from "./Form/RadioGroup.svelte"
export { default as Checkbox } from "./Form/Checkbox.svelte"
export { default as Home } from "./Links/Home.svelte"
export { default as DetailSummary } from "./List/Items/DetailSummary.svelte"
@ -34,9 +36,8 @@ export { default as Modal } from "./Modal/Modal.svelte"
export { default as ModalContent } from "./Modal/ModalContent.svelte"
export { default as NotificationDisplay } from "./Notification/NotificationDisplay.svelte"
export { default as Spacer } from "./Spacer/Spacer.svelte"
export { default as DatePicker } from "./DatePicker/DatePicker.svelte"
export { default as DatePicker } from "./Form/DatePicker.svelte"
export { default as Multiselect } from "./Form/Multiselect.svelte"
export { default as Slider } from "./Form/Slider.svelte"
export { default as Context } from "./context"
export { default as Table } from "./Table/Table.svelte"
export { default as Tabs } from "./Tabs/Tabs.svelte"
@ -44,6 +45,7 @@ export { default as Tab } from "./Tabs/Tab.svelte"
export { default as TreeView } from "./TreeView/Tree.svelte"
export { default as TreeItem } from "./TreeView/Item.svelte"
export { default as Divider } from "./Divider/Divider.svelte"
export { default as Search } from "./Form/Search.svelte"
// Typography
export { default as Body } from "./Typography/Body.svelte"
@ -51,6 +53,8 @@ export { default as Heading } from "./Typography/Heading.svelte"
export { default as Detail } from "./Typography/Detail.svelte"
export { default as Code } from "./Typography/Code.svelte"
// Core form components to be used elsewhere (standard components)
export * from "./Form/Core"
// Actions
export { default as autoResizeTextArea } from "./Actions/autoresize_textarea"

View file

@ -1,8 +1,7 @@
import "@spectrum-css/icon/dist/index-vars.css"
import SpectrumUIIcons from "@spectrum-css/icon/dist/spectrum-css-icons.svg"
import SpectrumWorkflowIcons from "@adobe/spectrum-css-workflow-icons/dist/spectrum-icons.svg"
export const loadSpectrumIcons = () => {
export default () => {
loadIconSet("Spectrum UI Icons", SpectrumUIIcons)
loadIconSet("Spectrum Workflow Icons", SpectrumWorkflowIcons)
}

View file

@ -1,8 +1,7 @@
import "@spectrum-css/icon/dist/index-vars.css"
import SpectrumUIIcons from "@spectrum-css/icon/dist/spectrum-css-icons.svg?raw"
import SpectrumWorkflowIcons from "@adobe/spectrum-css-workflow-icons/dist/spectrum-icons.svg?raw"
export const loadSpectrumIcons = () => {
export default () => {
loadIconSet("Spectrum UI Icons", SpectrumUIIcons)
loadIconSet("Spectrum Workflow Icons", SpectrumWorkflowIcons)
}

View file

@ -1,14 +0,0 @@
export default function buildStyle(styles) {
const convertCamel = str => {
return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)
}
let str = ""
for (let s in styles) {
if (styles[s]) {
let key = convertCamel(s)
str += `${key}: ${styles[s]}; `
}
}
return str
}

View file

@ -0,0 +1,8 @@
export const generateID = () => {
const rand = Math.random()
.toString(32)
.substring(2)
// Starts with a letter so that its a valid DOM ID
return `A${rand}`
}

View file

@ -1,22 +0,0 @@
import svelte from "@sveltejs/vite-plugin-svelte"
export default ({ mode }) => {
const isProduction = mode === "production"
return {
build: {
lib: {
entry: "src/index.js",
name: "bbui",
formats: ["es"],
},
minify: isProduction,
},
plugins: [svelte()],
resolve: {
dedupe: ["svelte", "svelte/internal"],
},
rollupOptions: {
external: ["svelte", "svelte/internal"],
},
}
}

File diff suppressed because it is too large Load diff

View file

@ -64,17 +64,13 @@
}
},
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.0",
"@budibase/bbui": "^1.58.13",
"@budibase/client": "^0.8.9",
"@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.8.9",
"@budibase/svelte-ag-grid": "^1.0.4",
"@sentry/browser": "5.19.1",
"@spectrum-css/icon": "^3.0.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
"@svelteschool/svelte-forms": "0.7.0",
"codemirror": "^5.59.0",
"downloadjs": "1.4.7",
"lodash": "4.17.13",

View file

@ -87,7 +87,7 @@ const createScreen = table => {
dataProvider: `{{ literal ${makePropSafe(provider._json._id)} }}`,
theme: "spectrum--lightest",
showAutoColumns: false,
quiet: false,
quiet: true,
size: "spectrum--medium",
rowCount: 8,
})

View file

@ -5,19 +5,8 @@ export const getThemeStore = () => {
const initialValue = {
darkMode: true,
}
const store = localStorageStore("bb-theme", initialValue)
// Resets the custom theme to the default dark theme.
// The reset option is only available when dark theme is on, which is why it
// sets dark mode to true here
store.reset = () => {
store.set({
...initialValue,
darkMode: true,
})
}
// Update theme when store changes
store.subscribe(theme => {
themeElement.classList.toggle("spectrum--darkest", theme.darkMode)

View file

@ -25,6 +25,7 @@
<ModalContent
title="Create Automation"
confirmText="Create"
size="L"
onConfirm={createAutomation}
disabled={!valid}>
<Input bind:value={name} label="Name" />

View file

@ -11,7 +11,7 @@
{#if error}
<div class="errors">{error}</div>
{/if}
<Table title={''} schema={query.schema} {data} {loading} />
<Table schema={query.schema} {data} {loading} rowCount={5} />
<style>
.errors {

View file

@ -5,7 +5,7 @@
Label,
DatePicker,
Toggle,
RichText,
TextArea,
} from "@budibase/bbui"
import Dropzone from "components/common/Dropzone.svelte"
import { capitalise } from "../../../helpers"
@ -21,12 +21,11 @@
</script>
{#if type === 'options'}
<Select thin secondary {label} data-cy="{meta.name}-select" bind:value>
<option value="">Choose an option</option>
{#each meta.constraints.inclusion as opt}
<option value={opt}>{opt}</option>
{/each}
</Select>
<Select
{label}
data-cy="{meta.name}-select"
bind:value
options={meta.constraints.inclusion} />
{:else if type === 'datetime'}
<DatePicker {label} bind:value />
{:else if type === 'attachment'}
@ -41,13 +40,9 @@
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
</div>
{:else if type === 'longform'}
<div>
<Label extraSmall grey>{label}</Label>
<RichText bind:value />
</div>
<TextArea {label} bind:value />
{:else}
<Input
thin
{label}
data-cy="{meta.name}-input"
{type}

View file

@ -10,7 +10,6 @@
import CreateEditRow from "./modals/CreateEditRow.svelte"
import CreateEditUser from "./modals/CreateEditUser.svelte"
import CreateEditColumn from "./modals/CreateEditColumn.svelte"
import "@budibase/svelte-ag-grid/dist/index.css"
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
import RoleCell from "./cells/RoleCell.svelte"
@ -22,6 +21,7 @@
export let loading = false
export let theme = "alpine"
export let hideAutocolumns
export let rowCount
let selectedRows = []
let editableColumn
@ -91,7 +91,9 @@
<div>
<div class="table-title">
<h1>{title}</h1>
{#if title}
<h1>{title}</h1>
{/if}
{#if loading}
<div transition:fade>
<Spinner size="10" />
@ -111,6 +113,7 @@
{schema}
{loading}
{customRenderers}
{rowCount}
bind:selectedRows
allowSelectRows={allowEditing}
allowEditRows={allowEditing}

View file

@ -1,171 +0,0 @@
<script>
import { onMount, onDestroy } from "svelte"
import { Modal, ModalContent } from "@budibase/bbui"
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
import { FIELDS } from "constants/backend"
const SORT_ICON_MAP = {
asc: "ri-arrow-down-fill",
desc: "ri-arrow-up-fill",
}
export let field
export let displayName
export let column
export let enableSorting = true
export let showColumnMenu
export let progressSort
export let editable
let menuButton
let sortDirection = ""
let modal
let hovered
let filterActive
function toggleMenu() {
showColumnMenu(menuButton)
}
function onSort(event) {
progressSort(event.shiftKey)
}
function showModal() {
modal.show()
}
function setSort() {
sortDirection = column.getSort()
}
function setFilterActive(e) {
filterActive = e.column.filterActive
}
onMount(() => {
column.addEventListener("sortChanged", setSort)
column.addEventListener("filterActiveChanged", setFilterActive)
})
onDestroy(() => {
column.removeEventListener("sortChanged", setSort)
column.removeEventListener("filterActiveChanged", setFilterActive)
})
$: type = FIELDS[field?.type?.toUpperCase()]?.name
</script>
<header
on:click={onSort}
data-cy="table-header"
on:mouseover={() => (hovered = true)}
on:mouseleave={() => (hovered = false)}>
<div class="column-header">
<div class="column-header-text">
<div class="column-header-name">
{displayName}
{#if field.autocolumn}<i class="auto ri-magic-fill" />{/if}
</div>
{#if type}
<div class="column-header-type">{type}</div>
{/if}
</div>
<i class={`${SORT_ICON_MAP[sortDirection]} icon`} />
</div>
<Modal bind:this={modal}>
<ModalContent
size="large"
showCancelButton={false}
showConfirmButton={false}
title={`Edit Column: ${field.name}`}>
<CreateEditColumn onClosed={modal.hide} {field} />
</ModalContent>
</Modal>
<section class:show={hovered || filterActive}>
{#if editable && hovered}
<span on:click|stopPropagation={showModal}>
<i class="ri-pencil-line icon" />
</span>
{/if}
<span on:click|stopPropagation={toggleMenu} bind:this={menuButton}>
<i class="ri-filter-line icon" class:active={filterActive} />
</span>
</section>
</header>
<style>
header {
font-family: Inter;
font-weight: 600;
display: flex;
justify-content: space-between;
height: 100%;
width: 100%;
align-items: center;
color: var(--ink);
}
section {
opacity: 0;
transition: 0.3s all;
}
section.show {
opacity: 1;
}
.column-header {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-s);
}
.column-header-text {
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-xs);
}
.column-header-name {
white-space: normal !important;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.column-header-type {
font-size: var(--font-size-xs);
color: var(--grey-6);
}
.icon {
transition: 0.2s all;
font-size: var(--font-size-m);
font-weight: 500;
}
.auto {
font-size: 9px;
transition: none;
position: relative;
margin-left: 2px;
top: -3px;
color: var(--grey-6);
}
.icon:hover {
color: var(--blue);
}
.icon.active,
.icon:hover {
color: var(--blue);
}
</style>

View file

@ -1,35 +0,0 @@
import TableHeader from "./TableHeader.svelte"
export default class TableHeaderWrapper {
constructor() {}
init(params) {
this.agParams = params
this.container = document.createElement("div")
this.container.style.height = "100%"
this.container.style.width = "100%"
this.headerComponent = new TableHeader({
target: this.container,
props: params,
})
this.gui = this.container
}
// can get called more than once, you should return the HTML element
getGui() {
return this.gui
}
// gets called when a new Column Definition has been set for this header
refresh(params) {
this.agParams = params
this.headerComponent = new TableHeader({
target: this.container,
props: params,
})
}
// optional method, gets called once, when component is destroyed
destroy() {}
}

View file

@ -1,27 +0,0 @@
<script>
import Spinner from "components/common/Spinner.svelte"
import { fade } from "svelte/transition"
import Logo from "/assets/bb-logo.svg"
</script>
<div class="ag-overlay-loading-center loading-container">
<div transition:fade class="loading-overlay">
<img height="30" width="30" src={Logo} alt="Budibase icon" />
<span> Loading Your Data </span>
<Spinner size="12" />
</div>
</div>
<style>
.loading-overlay {
display: flex;
align-items: center;
font-family: var(--font-sans);
font-weight: 500;
color: var(--ink);
}
.loading-overlay > * {
margin-right: var(--spacing-m);
}
</style>

View file

@ -1,17 +0,0 @@
import LoadingOverlay from "./LoadingOverlay.svelte"
export default class LoadingOverlayWrapper {
init(params) {
this.gui = document.createElement("div")
new LoadingOverlay({
target: this.gui,
props: {
params,
},
})
}
getGui() {
return this.gui
}
}

View file

@ -1,10 +1,5 @@
<script>
import {
Button,
Icon,
Modal,
ModalContent,
} from "@budibase/bbui"
import { Button, Icon, Modal, ModalContent } from "@budibase/bbui"
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
let modal
@ -14,11 +9,5 @@
Create New Column
</Button>
<Modal bind:this={modal}>
<ModalContent
size="large"
showCancelButton={false}
showConfirmButton={false}
title={'Create Column'}>
<CreateEditColumn onClosed={modal.hide} />
</ModalContent>
<CreateEditColumn />
</Modal>

View file

@ -5,11 +5,13 @@
Label,
Select,
Toggle,
Radio,
RadioGroup,
DatePicker,
ModalContent,
Context,
} from "@budibase/bbui"
import { cloneDeep } from "lodash/fp"
import { tables } from "stores/backend"
import { TableNames, UNEDITABLE_USER_FIELDS } from "constants"
import {
FIELDS,
@ -19,15 +21,15 @@
import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils"
import { notifications } from "@budibase/bbui"
import ValuesList from "components/common/ValuesList.svelte"
import DatePicker from "components/common/DatePicker.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { truncate } from "lodash"
import { getContext } from "svelte"
const AUTO_COL = "auto"
const LINK_TYPE = FIELDS.LINK.type
let fieldDefinitions = cloneDeep(FIELDS)
const { hide } = getContext(Context.Modal)
export let onClosed
export let field = {
type: "string",
constraints: fieldDefinitions.STRING.constraints,
@ -54,8 +56,11 @@
$tables.selected?._id === TableNames.USERS &&
UNEDITABLE_USER_FIELDS.includes(field.name)
$: invalid =
!field.name ||
(field.type === LINK_TYPE && !field.tableId) ||
Object.keys($tables.draft.schema).some(key => key === field.name)
Object.keys($tables.draft?.schema ?? {}).some(
key => key !== originalName && key === field.name
)
// used to select what different options can be displayed for column type
$: canBeSearched =
@ -77,7 +82,6 @@
primaryDisplay,
indexes,
})
onClosed()
}
function deleteColumn() {
@ -86,37 +90,37 @@
} else {
tables.deleteField(field)
notifications.success(`Column ${field.name} deleted.`)
onClosed()
hide()
}
}
function handleTypeChange(event) {
const definition = fieldDefinitions[event.target.value.toUpperCase()]
if (!definition) {
return
}
// remove any extra fields that may not be related to this type
delete field.autocolumn
delete field.subtype
delete field.tableId
delete field.relationshipType
// add in defaults and initial definition
field.type = definition.type
field.constraints = definition.constraints
// default relationships many to many
// Add in defaults and initial definition
const definition = fieldDefinitions[event.detail?.toUpperCase()]
if (definition?.constraints) {
field.constraints = definition.constraints
}
// Default relationships many to many
if (field.type === LINK_TYPE) {
field.relationshipType = RelationshipTypes.MANY_TO_MANY
}
}
function onChangeRequired(e) {
const req = e.target.checked
const req = e.detail
field.constraints.presence = req ? { allowEmpty: false } : false
required = req
}
function onChangePrimaryDisplay(e) {
const isPrimary = e.target.checked
const isPrimary = e.detail
// primary display is always required
if (isPrimary) {
field.constraints.presence = { allowEmpty: false }
@ -124,11 +128,11 @@
}
function onChangePrimaryIndex(e) {
indexes = e.target.checked ? [field.name] : []
indexes = e.detail ? [field.name] : []
}
function onChangeSecondaryIndex(e) {
if (e.target.checked) {
if (e.detail) {
indexes[1] = field.name
} else {
indexes = indexes.slice(0, 1)
@ -172,58 +176,60 @@
}
</script>
<div class="actions" class:hidden={deletion}>
<Input label="Name" thin bind:value={field.name} disabled={uneditable} />
<ModalContent
title={originalName ? 'Edit Column' : 'Create Column'}
confirmText="Save Column"
onConfirm={saveColumn}
disabled={invalid}>
<Input label="Name" bind:value={field.name} disabled={uneditable} />
<Select
disabled={originalName}
secondary
thin
label="Type"
bind:value={field.type}
on:change={handleTypeChange}
bind:value={field.type}>
{#each Object.values(fieldDefinitions) as field}
<option value={field.type}>{field.name}</option>
{/each}
<option value={AUTO_COL}>Auto Column</option>
</Select>
options={[...Object.values(fieldDefinitions), { name: 'Auto Column', type: AUTO_COL }]}
getOptionLabel={field => field.name}
getOptionValue={field => field.type} />
{#if canBeRequired}
<Toggle
checked={required}
on:change={onChangeRequired}
disabled={primaryDisplay}
thin
text="Required" />
{#if canBeRequired || canBeDisplay}
<div>
{#if canBeRequired}
<Toggle
value={required}
on:change={onChangeRequired}
disabled={primaryDisplay}
thin
text="Required" />
{/if}
{#if canBeDisplay}
<Toggle
bind:value={primaryDisplay}
on:change={onChangePrimaryDisplay}
thin
text="Use as table display column" />
{/if}
</div>
{/if}
{#if canBeDisplay}
<Toggle
bind:checked={primaryDisplay}
on:change={onChangePrimaryDisplay}
thin
text="Use as table display column" />
<Label grey small>Search Indexes</Label>
{/if}
{#if canBeSearched}
<Toggle
checked={indexes[0] === field.name}
disabled={indexes[1] === field.name}
on:change={onChangePrimaryIndex}
thin
text="Primary" />
<Toggle
checked={indexes[1] === field.name}
disabled={!indexes[0] || indexes[0] === field.name}
on:change={onChangeSecondaryIndex}
thin
text="Secondary" />
<div>
<Label grey small>Search Indexes</Label>
<Toggle
value={indexes[0] === field.name}
disabled={indexes[1] === field.name}
on:change={onChangePrimaryIndex}
text="Primary" />
<Toggle
value={indexes[1] === field.name}
disabled={!indexes[0] || indexes[0] === field.name}
on:change={onChangeSecondaryIndex}
text="Secondary" />
</div>
{/if}
{#if field.type === 'string'}
<Input
thin
type="number"
label="Max Length"
bind:value={field.constraints.length.maximum} />
@ -238,64 +244,46 @@
<DatePicker label="Latest" bind:value={field.constraints.datetime.latest} />
{:else if field.type === 'number'}
<Input
thin
type="number"
label="Min Value"
bind:value={field.constraints.numericality.greaterThanOrEqualTo} />
<Input
thin
type="number"
label="Max Value"
bind:value={field.constraints.numericality.lessThanOrEqualTo} />
{:else if field.type === 'link'}
<Select label="Table" thin secondary bind:value={field.tableId}>
<option value="">Choose an option</option>
{#each tableOptions as table}
<option value={table._id}>{table.name}</option>
{/each}
</Select>
<Select
label="Table"
bind:value={field.tableId}
options={tableOptions}
getOptionLabel={table => table.name}
getOptionValue={table => table._id} />
{#if relationshipOptions && relationshipOptions.length > 0}
<div>
<Label grey extraSmall>Define the relationship</Label>
<div class="radio-buttons">
{#each relationshipOptions as { value, name }}
<Radio
disabled={originalName}
name="Relationship type"
{value}
bind:group={field.relationshipType}>
<div class="radio-button-labels">
<label for={value}>{name.split('→')[0]}</label>
<label class="rel-type-center" for={value}>→</label>
<label for={value}>{name.split('→')[1]}</label>
</div>
</Radio>
{/each}
</div>
</div>
<RadioGroup
disabled={originalName}
label="Define the relationship"
bind:value={field.relationshipType}
options={relationshipOptions}
getOptionLabel={option => option.name}
getOptionValue={option => option.value} />
{/if}
<Input
label={`Column Name in Other Table`}
thin
bind:value={field.fieldName} />
<Input label={`Column name in other table`} bind:value={field.fieldName} />
{:else if field.type === AUTO_COL}
<Select label="Auto Column Type" thin secondary bind:value={field.subtype}>
<option value="">Choose a subtype</option>
{#each Object.entries(getAutoColumnInformation()) as [subtype, info]}
<option value={subtype}>{info.name}</option>
{/each}
</Select>
<Select
label="Auto Column Type"
value={field.subtype}
on:change={e => (field.subtype = e.detail)}
options={Object.entries(getAutoColumnInformation())}
getOptionLabel={option => option[1].name}
getOptionValue={option => option[0]} />
{/if}
<footer class="create-column-options">
<div slot="footer">
{#if !uneditable && originalName != null}
<Button warning size="S" text on:click={confirmDelete}>Delete Column</Button>
<Button warning text on:click={confirmDelete}>Delete</Button>
{/if}
<Button on:click={onClosed}>Cancel</Button>
<Button cta on:click={saveColumn} bind:disabled={invalid}>
Save Column
</Button>
</footer>
</div>
</div>
</ModalContent>
<ConfirmDialog
bind:this={confirmDeleteDialog}
body={`Are you sure you wish to delete this column? Your data will be deleted and this action cannot be undone.`}
@ -303,44 +291,3 @@
onOk={deleteColumn}
onCancel={hideDeleteDialog}
title="Confirm Deletion" />
<style>
.radio-buttons {
gap: var(--spacing-m);
font-size: var(--font-size-xs);
}
.radio-buttons :global(> *) {
margin-top: var(--spacing-s);
width: 100%;
}
.actions {
display: grid;
grid-gap: var(--spacing-xl);
}
footer {
display: flex;
justify-content: flex-end;
gap: var(--spacing-m);
}
:global(.create-column-options button:first-child) {
margin-right: auto;
}
.rel-type-center {
font-weight: 500;
color: var(--grey-6);
margin-right: 4px;
margin-left: 4px;
padding: 1px 3px 1px 3px;
background: var(--grey-3);
border-radius: 2px;
}
.radio-button-labels {
margin-top: 2px;
}
</style>

View file

@ -17,6 +17,7 @@
: $tables.selected
$: tableSchema = getUserSchema(table)
$: customSchemaKeys = getCustomSchemaKeys(tableSchema)
$: if (!row.status) row.status = "active"
const getUserSchema = table => {
let schema = table?.schema ?? {}
@ -86,20 +87,18 @@
<!-- Defer rendering this select until roles load, otherwise the initial
selection is always undefined -->
<Select
thin
secondary
label="Role"
data-cy="roleId-select"
bind:value={row.roleId}>
<option value="">Choose an option</option>
{#each $roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
<RowFieldControl
meta={{ name: 'status', type: 'options', constraints: { inclusion: ['active', 'inactive'] } }}
bind:value={row.roleId}
options={$roles}
getOptionLabel={role => role.name}
getOptionValue={role => role._id} />
<Select
label="Status"
bind:value={row.status}
defaultValue={'active'} />
options={[{ label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' }]}
getOptionLabel={status => status.label}
getOptionValue={status => status.value} />
{#each customSchemaKeys as [key, meta]}
{#if !meta.autocolumn}
<RowFieldControl {meta} bind:value={row[key]} {creating} />

View file

@ -1,8 +1,6 @@
<script>
import { Button, Select } from "@budibase/bbui"
import { Button, Select, Label, notifications } from "@budibase/bbui"
import { tables, views } from "stores/backend"
import { notifications } from "@budibase/bbui"
import analytics from "analytics"
const CALCULATIONS = [
@ -45,33 +43,29 @@
<div class="actions">
<h5>Calculate</h5>
<div class="input-group-row">
<p>The</p>
<Select secondary thin bind:value={view.calculation}>
<option value={''}>Choose an option</option>
{#each CALCULATIONS as calculation}
<option value={calculation.key}>{calculation.name}</option>
{/each}
</Select>
<Label>The</Label>
<Select
bind:value={view.calculation}
options={CALCULATIONS}
getOptionLabel={x => x.name}
getOptionValue={x => x.key} />
{#if view.calculation}
<p>of</p>
<Select secondary thin bind:value={view.field}>
<option value={''}>You must choose an option</option>
{#each fields as field}
<option value={field}>{field}</option>
{/each}
</Select>
<Label>Of</Label>
<Select bind:value={view.field} options={fields} />
{/if}
</div>
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button primary on:click={saveView} disabled={!view.field}>Save</Button>
<Button cta on:click={saveView} disabled={!view.field}>Save</Button>
</div>
</div>
<style>
.actions {
width: 200px;
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {
@ -91,9 +85,4 @@
gap: var(--spacing-s);
align-items: center;
}
p {
margin: 0;
font-size: var(--font-size-xs);
}
</style>

View file

@ -35,7 +35,7 @@
<Input label="View Name" thin bind:value={name} />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button primary on:click={saveView}>Save View</Button>
<Button cta on:click={saveView}>Save View</Button>
</div>
</div>
@ -48,6 +48,7 @@
.actions {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
.footer {

View file

@ -30,14 +30,15 @@
<div class="popover">
<h5>Export Data</h5>
<Select label="Format" secondary thin bind:value={exportFormat}>
{#each FORMATS as format}
<option value={format.key}>{format.name}</option>
{/each}
</Select>
<Select
label="Format"
bind:value={exportFormat}
options={FORMATS}
getOptionLabel={x => x.name}
getOptionValue={x => x.key} />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button primary on:click={exportView}>Export</Button>
<Button cta on:click={exportView}>Export</Button>
</div>
</div>
@ -45,6 +46,7 @@
.popover {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {

View file

@ -105,6 +105,9 @@
filter.value = ""
}
}
const getOptionLabel = x => x.name
const getOptionValue = x => x.key
</script>
<div class="actions">
@ -115,49 +118,37 @@
{#if idx === 0}
<p>Where</p>
{:else}
<Select secondary thin bind:value={filter.conjunction}>
<option value="">Choose an option</option>
{#each CONJUNCTIONS as conjunction}
<option value={conjunction.key}>{conjunction.name}</option>
{/each}
</Select>
<Select
bind:value={filter.conjunction}
options={CONJUNCTIONS}
{getOptionLabel}
{getOptionValue} />
{/if}
<Select
secondary
thin
bind:value={filter.key}
on:change={fieldChanged(filter)}>
<option value="">Choose an option</option>
{#each fields as field}
<option value={field}>{field}</option>
{/each}
</Select>
<Select secondary thin bind:value={filter.condition}>
<option value="">Choose an option</option>
{#each CONDITIONS as condition}
<option value={condition.key}>{condition.name}</option>
{/each}
</Select>
on:change={fieldChanged(filter)}
options={fields} />
<Select
bind:value={filter.condition}
options={CONDITIONS}
{getOptionLabel}
{getOptionValue} />
{#if filter.key && isMultipleChoice(filter.key)}
<Select secondary thin bind:value={filter.value}>
<option value="">Choose an option</option>
{#each fieldOptions(filter.key) as option}
<option value={option}>{option.toString()}</option>
{/each}
</Select>
<Select
bind:value={filter.value}
options={fieldOptions(filter.key)}
getOptionLabel={x => x.toString()} />
{:else if filter.key && isDate(filter.key)}
<DatePicker
bind:value={filter.value}
placeholder={filter.key || fields[0]} />
{:else if filter.key && isNumber(filter.key)}
<Input
thin
bind:value={filter.value}
placeholder={filter.key || fields[0]}
type="number" />
{:else}
<Input
thin
placeholder={filter.key || fields[0]}
bind:value={filter.value} />
{/if}
@ -171,7 +162,7 @@
</div>
<div class="buttons">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button primary on:click={saveView}>Save</Button>
<Button cta on:click={saveView}>Save</Button>
</div>
</div>
</div>
@ -180,6 +171,7 @@
.actions {
display: grid;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {

View file

@ -22,26 +22,20 @@
</script>
<div class="actions">
<h5>Group</h5>
<div class="input-group-row">
<p>By</p>
<Select secondary thin bind:value={view.groupBy}>
<option value="">Choose an option</option>
{#each fields as field}
<option value={field}>{field}</option>
{/each}
</Select>
</div>
<h5>Group By</h5>
<Select bind:value={view.groupBy} options={fields} />
<div class="footer">
<Button secondary on:click={onClosed}>Cancel</Button>
<Button primary on:click={saveView}>Save</Button>
<Button cta on:click={saveView}>Save</Button>
</div>
</div>
<style>
.actions {
display: grid;
width: 200px;
grid-gap: var(--spacing-xl);
padding: var(--spacing-xl);
}
h5 {

View file

@ -2,6 +2,7 @@
import { roles, permissions as permissionsStore } from "stores/backend"
import { notifications } from "@budibase/bbui"
import { Button, Label, Input, Select, Spacer } from "@budibase/bbui"
import { capitalise } from "../../../../helpers"
export let resourceId
export let permissions
@ -34,16 +35,13 @@
<Label extraSmall grey>Level</Label>
<Label extraSmall grey>Role</Label>
{#each Object.keys(permissions) as level}
<Input secondary thin value={level} disabled={true} />
<Input value={capitalise(level)} disabled />
<Select
secondary
thin
value={permissions[level]}
on:change={e => changePermission(level, e.target.value)}>
{#each $roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
on:change={e => changePermission(level, e.detail)}
options={$roles}
getOptionLabel={x => x.name}
getOptionValue={x => x._id} />
{/each}
</div>
@ -58,6 +56,7 @@
.popover {
display: grid;
width: 400px;
padding: var(--spacing-xl);
}
h5 {

View file

@ -1,6 +1,7 @@
<script>
import { Label, Input, TextArea, Spacer } from "@budibase/bbui"
import { Label, Input, Spacer } from "@budibase/bbui"
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
import { capitalise } from "../../../../helpers"
export let integration
export let schema
@ -11,16 +12,15 @@
<form>
{#each Object.keys(schema) as configKey}
{#if schema[configKey].type === 'object'}
<Label small>{configKey}</Label>
<Label>{capitalise(configKey)}</Label>
<Spacer small />
<KeyValueBuilder
defaults={schema[configKey].default}
bind:object={integration[configKey]} />
{:else}
<div class="form-row">
<Label small>{configKey}</Label>
<Label>{capitalise(configKey)}</Label>
<Input
outline
type={schema[configKey].type}
on:change
bind:value={integration[configKey]} />

View file

@ -47,11 +47,10 @@
disabled={error || !name}>
<Input
data-cy="datasource-name-input"
thin
label="Datasource Name"
on:input={checkValid}
bind:value={name}
{error} />
<Label grey extraSmall>Source</Label>
<Label>Source</Label>
<TableIntegrationMenu bind:integration />
</ModalContent>

View file

@ -91,9 +91,24 @@
}
const handleTypeChange = column => evt => {
schema[column].type = evt.target.value
schema[column].type = evt.detail
validateCSV()
}
const typeOptions = [
{
label: "Text",
value: "string",
},
{
label: "Number",
value: "number",
},
{
label: "Date",
value: "datetime",
},
]
</script>
<div class="dropzone">
@ -102,20 +117,18 @@
{#if files[0]}{files[0].name}{:else}Upload{/if}
</label>
</div>
<div class="schema-fields">
{#if fields.length}
{#if fields.length}
<div class="schema-fields">
{#each fields as columnName}
<div class="field">
<span>{columnName}</span>
<Select
secondary
thin
bind:value={schema[columnName].type}
on:change={handleTypeChange(columnName)}>
<option value={'string'}>Text</option>
<option value={'number'}>Number</option>
<option value={'datetime'}>Date</option>
</Select>
on:change={handleTypeChange(columnName)}
options={typeOptions}
placeholder={null}
getOptionLabel={option => option.label}
getOptionValue={option => option.value} />
<span class="field-status" class:error={!schema[columnName].success}>
{schema[columnName].success ? 'Success' : 'Failure'}
</span>
@ -124,16 +137,15 @@
on:click={() => omitColumn(columnName)} />
</div>
{/each}
{/if}
</div>
</div>
{/if}
{#if fields.length}
<div class="display-column">
<Label extraSmall grey>Display Column</Label>
<Select thin secondary bind:value={primaryDisplay}>
{#each fields as field}
<option value={field}>{field}</option>
{/each}
</Select>
<Select
label="Display Column"
bind:value={primaryDisplay}
options={fields} />
</div>
{/if}
@ -165,6 +177,10 @@
display: none;
}
.schema-fields {
margin-top: var(--spacing-xl);
}
label {
font-family: var(--font-sans);
cursor: pointer;
@ -201,7 +217,7 @@
.field {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: 2fr 2fr 1fr auto;
margin-top: var(--spacing-m);
align-items: center;
grid-gap: var(--spacing-m);

View file

@ -3,7 +3,7 @@
import { store } from "builderStore"
import { tables } from "stores/backend"
import { notifications } from "@budibase/bbui"
import { Input, Label, ModalContent, Toggle } from "@budibase/bbui"
import { Input, Label, ModalContent, Toggle, Divider } from "@budibase/bbui"
import TableDataImport from "../TableDataImport.svelte"
import analytics from "analytics"
import screenTemplates from "builderStore/store/screenTemplates"
@ -105,27 +105,20 @@
<Label extraSmall grey>Auto Columns</Label>
<div class="toggles">
<div class="toggle-1">
<Toggle
text="Created by"
bind:checked={autoColumns.createdBy.enabled} />
<Toggle
text="Created at"
bind:checked={autoColumns.createdAt.enabled} />
<Toggle text="Auto ID" bind:checked={autoColumns.autoID.enabled} />
<Toggle text="Created by" bind:value={autoColumns.createdBy.enabled} />
<Toggle text="Created at" bind:value={autoColumns.createdAt.enabled} />
<Toggle text="Auto ID" bind:value={autoColumns.autoID.enabled} />
</div>
<div class="toggle-2">
<Toggle
text="Updated by"
bind:checked={autoColumns.updatedBy.enabled} />
<Toggle
text="Updated at"
bind:checked={autoColumns.updatedAt.enabled} />
<Toggle text="Updated by" bind:value={autoColumns.updatedBy.enabled} />
<Toggle text="Updated at" bind:value={autoColumns.updatedAt.enabled} />
</div>
</div>
<Divider />
</div>
<Toggle
text="Generate screens in the design section"
bind:checked={createAutoscreens} />
text="Generate screens in Design section"
bind:value={createAutoscreens} />
<div>
<Label grey extraSmall>Create Table from CSV (Optional)</Label>
<TableDataImport bind:dataImport />
@ -134,8 +127,7 @@
<style>
.autocolumns {
padding-bottom: 10px;
border-bottom: 3px solid var(--grey-1);
margin-bottom: -10px;
}
.toggles {

View file

@ -1,114 +0,0 @@
<script>
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let checked = false
function handleChange() {
checked = !checked
dispatch("change", checked)
}
</script>
<input type="checkbox" class="checkbox" id="_checkbox" />
<div class="checkbox-container" on:click={handleChange}>
<div class="check-div" class:checked>
<div class="tick_mark" />
</div>
</div>
<style>
.checkbox-container {
position: relative;
z-index: 0;
}
.checkbox {
display: none;
}
.check-div {
position: relative;
width: 20px;
height: 20px;
background-color: var(--grey-2);
cursor: pointer;
transition: 0.2s ease transform, 0.2s ease background-color,
0.2s ease box-shadow;
overflow: hidden;
border-radius: 4px;
}
.check-div:before {
content: "";
position: absolute;
top: 50%;
right: 0;
left: 0;
width: 12px;
height: 12px;
margin: 0 auto;
background-color: var(--background);
transform: translateY(-50%);
transition: 0.2s ease width, 0.2s ease height;
border-radius: 2px;
}
.check-div:active {
transform: translateY(-50%) scale(0.9);
}
.tick_mark {
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 4px;
margin: 0 auto;
transform: rotateZ(-40deg);
}
.tick_mark:before,
.tick_mark:after {
content: "";
position: absolute;
background-color: var(--ink);
border-radius: 2px;
opacity: 0;
transition: 0.2s ease transform, 0.2s ease opacity;
}
.tick_mark:before {
left: 0;
bottom: 0;
width: 2px;
height: 6px;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.23);
transform: translateY(-68px);
}
.tick_mark:after {
left: 0;
bottom: 0;
width: 12px;
height: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.23);
transform: translateX(78px);
}
.checked {
background-color: var(--grey-2);
}
.checked:before {
width: 0;
height: 0;
}
.checked .tick_mark:before,
.checked .tick_mark:after {
transform: translate(0);
opacity: 1;
}
</style>

View file

@ -1,13 +0,0 @@
<script>
import { DatePicker } from "@budibase/bbui"
export let label
export let value
function onChange(event) {
const [selectedDates] = event.detail
value = selectedDates[0]
}
</script>
<DatePicker {label} on:change={onChange} {value} />

View file

@ -32,12 +32,11 @@
<div class="control">
<Input
{thin}
value={readableValue}
on:change={event => onChange(event.target.value)}
on:change={event => onChange(event.detail)}
{placeholder} />
<div class="icon" on:click={bindingDrawer.show}>
<Icon name="lightning" />
<Icon s name="FlashOn" />
</div>
</div>
<Drawer bind:this={bindingDrawer} {title}>
@ -66,23 +65,30 @@
}
.icon {
right: 2px;
top: 2px;
bottom: 2px;
right: 1px;
top: 1px;
bottom: 1px;
position: absolute;
justify-content: center;
align-items: center;
display: flex;
flex-direction: row;
box-sizing: border-box;
padding-left: 7px;
border-left: 1px solid var(--grey-4);
background-color: var(--grey-2);
border-top-right-radius: var(--border-radius-m);
border-bottom-right-radius: var(--border-radius-m);
color: var(--grey-7);
font-size: 14px;
border-left: 1px solid var(--spectrum-alias-border-color);
border-top-right-radius: var(--spectrum-alias-border-radius-regular);
border-bottom-right-radius: var(--spectrum-alias-border-radius-regular);
width: 31px;
color: var(--spectrum-alias-text-color);
background-color: var(--spectrum-global-color-gray-75);
transition: background-color
var(--spectrum-global-animation-duration-100, 130ms),
box-shadow var(--spectrum-global-animation-duration-100, 130ms),
border-color var(--spectrum-global-animation-duration-100, 130ms);
}
.icon:hover {
color: var(--ink);
cursor: pointer;
color: var(--spectrum-alias-text-color-hover);
background-color: var(--spectrum-global-color-gray-50);
border-color: var(--spectrum-alias-border-color-hover);
}
</style>

View file

@ -42,27 +42,18 @@
{:else}
{#if schema.relationshipType === 'one-to-many'}
<Select
thin
secondary
on:change={e => (linkedIds = e.target.value ? [e.target.value] : [])}
name={label}
{label}>
<option value="">Choose an option</option>
{#each rows as row}
<option selected={row._id === linkedIds[0]} value={row._id}>
{getPrettyName(row)}
</option>
{/each}
</Select>
value={linkedIds?.[0]}
options={rows}
getOptionLabel={getPrettyName}
getOptionValue={row => row._id}
on:change={e => (linkedIds = e.detail ? [e.detail] : [])}
{label} />
{:else}
<Multiselect
secondary
bind:value={linkedIds}
{label}
placeholder="Choose some options">
{#each rows as row}
<option value={row._id}>{getPrettyName(row)}</option>
{/each}
</Multiselect>
options={rows}
getOptionLabel={getPrettyName}
getOptionValue={row => row._id} />
{/if}
{/if}

View file

@ -62,7 +62,6 @@
background-color: var(--grey-3);
}
.nav-item:hover .actions {
display: flex;
visibility: visible;
}

View file

@ -8,12 +8,11 @@
screenSearchString,
} from "builderStore"
import { roles } from "stores/backend"
import { FrontendTypes } from "constants"
import ComponentNavigationTree from "components/design/NavigationPanel/ComponentNavigationTree/index.svelte"
import Layout from "components/design/NavigationPanel/Layout.svelte"
import NewScreenModal from "components/design/NavigationPanel/NewScreenModal.svelte"
import NewLayoutModal from "components/design/NavigationPanel/NewLayoutModal.svelte"
import { Modal, Switcher, Select, Input, Tabs, Tab } from "@budibase/bbui"
import { Modal, Select, Search, Tabs, Tab } from "@budibase/bbui"
const tabs = [
{
@ -29,13 +28,13 @@
let modal
$: selected = tabs.find(t => t.key === $params.assetType).title
const navigate = ({detail}) => {
const navigate = ({ detail }) => {
const { key } = tabs.find(t => t.title === detail)
$goto(`../${key}`)
}
const updateAccessRole = event => {
const role = event.target.value
const role = event.detail
// Select a valid screen with this new role - otherwise we'll not be
// able to change role at all because ComponentNavigationTree will kick us
@ -70,53 +69,41 @@
<div class="tab-content-padding">
<div class="role-select">
<Select
extraThin
secondary
on:change={updateAccessRole}
value={$selectedAccessRole}
label="Filter by Access">
{#each $roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
<div class="search-screens">
<Input
extraThin
placeholder="Enter a route to search"
label="Search Screens"
bind:value={$screenSearchString} />
<i
class="ri-close-line"
on:click={() => ($screenSearchString = null)} />
</div>
</div>
<div class="nav-items-container">
<ComponentNavigationTree />
</div>
<Modal bind:this={modal}>
<NewScreenModal />
</Modal>
label="Filter by Access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
options={$roles} />
<Search
placeholder="Enter a route to search"
label="Search Screens"
bind:value={$screenSearchString} />
</div>
<div class="nav-items-container">
<ComponentNavigationTree />
</div>
<Modal bind:this={modal}>
<NewScreenModal />
</Modal>
</div>
</Tab>
<Tab title="Layouts">
<div class="tab-content-padding">
<i
on:click={modal.show}
data-cy="new-layout"
class="ri-add-circle-fill" />
{#each $store.layouts as layout, idx (layout._id)}
<Layout {layout} border={idx > 0} />
{/each}
<Modal bind:this={modal}>
<NewLayoutModal />
</Modal>
on:click={modal.show}
data-cy="new-layout"
class="ri-add-circle-fill" />
{#each $store.layouts as layout, idx (layout._id)}
<Layout {layout} border={idx > 0} />
{/each}
<Modal bind:this={modal}>
<NewLayoutModal />
</Modal>
</div>
</Tab>
</Tabs>
<i
on:click={modal.show}
data-cy="new-screen"
class="ri-add-circle-fill" />
<i on:click={modal.show} data-cy="new-screen" class="ri-add-circle-fill" />
</div>
<style>

View file

@ -82,8 +82,8 @@
}
const routeChanged = event => {
if (!event.target.value.startsWith("/")) {
route = "/" + event.target.value
if (!event.detail.startsWith("/")) {
route = "/" + event.detail
}
}
</script>
@ -92,24 +92,22 @@
<Select
label="Choose a Template"
bind:value={templateIndex}
secondary
on:change={ev => templateChanged(ev.target.value)}>
{#if templates}
{#each templates as template, index}
<option value={index}>{template.name}</option>
{/each}
{/if}
</Select>
on:change={ev => templateChanged(ev.detail)}
options={templates}
placeholder={null}
getOptionLabel={x => x.name}
getOptionValue={(x, idx) => idx} />
<Input label="Name" bind:value={name} />
<Input
label="Url"
error={routeError}
bind:value={route}
on:change={routeChanged} />
<Select label="Access" bind:value={roleId} secondary>
{#each $roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
<Toggle text="Create link in navigation bar" bind:checked={createLink} />
<Select
label="Access"
bind:value={roleId}
options={$roles}
getOptionLabel={x => x.name}
getOptionValue={x => x._id} />
<Toggle text="Create link in navigation bar" bind:value={createLink} />
</ModalContent>

View file

@ -1,6 +1,6 @@
<script>
import groupBy from "lodash/fp/groupBy"
import { Input, TextArea, Heading, Spacer, Label } from "@budibase/bbui"
import { Search, TextArea, Heading, Spacer, Label } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { isValid } from "@budibase/string-templates"
import {
@ -60,7 +60,7 @@
<div class="drawer-contents">
<div class="container" data-cy="binding-dropdown-modal">
<div class="list">
<Input extraThin placeholder="Search" bind:value={search} />
<Search placeholder="Search" bind:value={search} />
<Spacer medium />
{#if context}
<Heading xs h3>Columns</Heading>
@ -109,10 +109,8 @@
<div class="text">
<TextArea
bind:getCaretPosition
thin
bind:value
placeholder="Add text, or click the objects on the left to add them to
the textbox." />
placeholder="Add text, or click the objects on the left to add them to the textbox." />
{#if !valid}
<p class="syntax-error">
Current Handlebars syntax is invalid, please check the guide
@ -145,7 +143,7 @@
font-family: var(--font-sans);
}
.text :global(textarea) {
min-height: 100px;
min-height: 200px;
}
.text :global(p) {
margin: 0;

View file

@ -63,7 +63,7 @@
<div class="custom-styles">
<TextArea
value={componentInstance._styles.custom}
on:change={event => onCustomStyleChanged(event.target.value)}
on:change={event => onCustomStyleChanged(event.detail)}
placeholder="Enter some CSS..." />
</div>
</DetailSummary>
@ -79,15 +79,11 @@
<div class="transitions">
<Select
value={componentInstance._transition}
on:change={event => onUpdateTransition(event.target.value)}
on:change={event => onUpdateTransition(event.detail)}
name="transition"
label="Transition"
secondary
thin>
{#each transitions as transition}
<option value={transition}>{capitalize(transition)}</option>
{/each}
</Select>
options={transitions}
getOptionLabel={capitalize} />
</div>
{/if}
</div>
@ -105,6 +101,7 @@
position: relative;
display: flex;
min-height: 0;
flex: 1 1 auto;
}
.property-groups {

View file

@ -2,4 +2,4 @@
import FormFieldSelect from "./FormFieldSelect.svelte"
</script>
<FormFieldSelect {...$$props} type="attachment" />
<FormFieldSelect {...$$props} on:change type="attachment" />

View file

@ -2,4 +2,4 @@
import FormFieldSelect from "./FormFieldSelect.svelte"
</script>
<FormFieldSelect {...$$props} type="boolean" />
<FormFieldSelect {...$$props} on:change type="boolean" />

View file

@ -1,7 +0,0 @@
<script>
import Checkbox from "components/common/Checkbox.svelte"
export let value
</script>
<Checkbox checked={value} on:change />

Some files were not shown because too many files have changed in this diff Show more