merge spectrum-bbui
This commit is contained in:
commit
a84b1ed316
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]()
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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} />
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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} />
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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"> </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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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>
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -1,8 +0,0 @@
|
|||
<script>
|
||||
import Search from "./Search.svelte";
|
||||
import { View } from "svench";
|
||||
</script>
|
||||
|
||||
<View name="Name">
|
||||
<Search />
|
||||
</View>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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}`
|
||||
}
|
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<ModalContent
|
||||
title="Create Automation"
|
||||
confirmText="Create"
|
||||
size="L"
|
||||
onConfirm={createAutomation}
|
||||
disabled={!valid}>
|
||||
<Input bind:value={name} label="Name" />
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
|
@ -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() {}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
|
@ -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} />
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
background-color: var(--grey-3);
|
||||
}
|
||||
.nav-item:hover .actions {
|
||||
display: flex;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
import FormFieldSelect from "./FormFieldSelect.svelte"
|
||||
</script>
|
||||
|
||||
<FormFieldSelect {...$$props} type="attachment" />
|
||||
<FormFieldSelect {...$$props} on:change type="attachment" />
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
import FormFieldSelect from "./FormFieldSelect.svelte"
|
||||
</script>
|
||||
|
||||
<FormFieldSelect {...$$props} type="boolean" />
|
||||
<FormFieldSelect {...$$props} on:change type="boolean" />
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue