forked from tsai/budibase
162 changed files with 2486 additions and 9523 deletions
@ -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,140 +1,22 @@ |
|||
<script> |
|||
import Field from "./Field.svelte" |
|||
import Checkbox from "./Core/Checkbox.svelte" |
|||
import { createEventDispatcher } from "svelte" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
|
|||
export let checked = false |
|||
export let value |
|||
export let name |
|||
export let disabled |
|||
export let value = null |
|||
export let label = null |
|||
export let labelPosition = "above" |
|||
export let text = null |
|||
export let disabled = false |
|||
export let error = null |
|||
|
|||
function handleChange() { |
|||
if (disabled) return |
|||
checked = !checked |
|||
dispatch("change", checked) |
|||
const dispatch = createEventDispatcher() |
|||
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 = () => {} |
|||
|
|||
// This section handles the edit mode and dispatching of things to the parent when saved |
|||
let editMode = false |
|||
export let readonly = false |
|||
export let error = null |
|||
|
|||
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 |
|||
|
|||
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 |
|||
} |
|||
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 |
|||
|
|||
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 |
|||
</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> |
|||
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") |
|||
|
|||
<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); |
|||
const dispatch = createEventDispatcher() |
|||
const onChange = e => { |
|||
value = e.detail |
|||
dispatch("change", e.detail) |
|||
} |
|||
select:focus { |
|||
border: var(--border-blue); |
|||
} |
|||
select:disabled { |
|||
background: var(--grey-4); |
|||
color: var(--grey-6); |
|||
} |
|||
|
|||
.relative { |
|||
position: relative !important; |
|||
display: block; |
|||
const extractProperty = (value, property) => { |
|||
if (value && typeof value === "object") { |
|||
return value[property] |
|||
} |
|||
return value |
|||
} |
|||
</script> |
|||
|
|||
.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" |
|||
|
|||
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 value = null |
|||
export let label = null |
|||
export let labelPosition = "above" |
|||
export let placeholder = null |
|||
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) |
|||
} |
|||
export let error = null |
|||
export let getCaretPosition = null |
|||
|
|||
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} |
|||
<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 |
|||
</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; |
|||
} |
|||
export let error = null |
|||
|
|||
.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; |
|||
const dispatch = createEventDispatcher() |
|||
const onChange = e => { |
|||
value = e.detail |
|||
dispatch("change", e.detail) |
|||
} |
|||
</script> |
|||
|
|||
.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> |
|||
@ -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,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,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
@ -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,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} /> |
|||
@ -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 changed in this diff
Loading…
Reference in new issue