mirror of https://github.com/Budibase/budibase.git
11 changed files with 90 additions and 254 deletions
@ -0,0 +1,48 @@ |
|||||
|
<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 let updateOnChange = true |
||||
|
|
||||
|
const dispatch = createEventDispatcher() |
||||
|
|
||||
|
const onChange = event => { |
||||
|
dispatch("change", event.target.value) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div |
||||
|
class="spectrum-Textfield spectrum-Textfield--multiline" |
||||
|
class:is-invalid={!!error} |
||||
|
class:is-disabled={disabled}> |
||||
|
{#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 |
||||
|
placeholder={placeholder || ''} |
||||
|
class="spectrum-Textfield-input" |
||||
|
{disabled} |
||||
|
{id} |
||||
|
on:blur={onChange} |
||||
|
on:change={updateOnChange ? onChange : null}>{value || ''}</textarea> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.spectrum-Textfield { |
||||
|
width: 100%; |
||||
|
} |
||||
|
textarea { |
||||
|
resize: vertical; |
||||
|
min-height: 80px !important; |
||||
|
} |
||||
|
</style> |
||||
@ -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,132 +1,29 @@ |
|||||
<script> |
<script> |
||||
|
import Field from "./Field.svelte" |
||||
|
import TextArea from "./Core/TextArea.svelte" |
||||
import { createEventDispatcher } from "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 value = null |
||||
|
export let label = null |
||||
export let name = false |
export let labelPosition = "above" |
||||
export let label = false |
export let placeholder = null |
||||
export let thin = false |
|
||||
export let extraThin = false |
|
||||
export let edit = false |
|
||||
export let disabled = false |
export let disabled = false |
||||
export let placeholder |
export let error = null |
||||
export let validator = () => {} |
export let updateOnChange = true |
||||
export let value = "" |
|
||||
export const getCaretPosition = () => { |
|
||||
return { start: textarea.selectionStart, end: textarea.selectionEnd } |
|
||||
} |
|
||||
|
|
||||
let textarea |
|
||||
|
|
||||
// This section handles the edit mode and dispatching of things to the parent when saved |
|
||||
let editMode = false |
|
||||
|
|
||||
const save = () => { |
|
||||
editMode = false |
|
||||
dispatch("save", value) |
|
||||
} |
|
||||
|
|
||||
const enableEdit = () => { |
const dispatch = createEventDispatcher() |
||||
editMode = true |
const onChange = e => { |
||||
|
dispatch("change", e.detail) |
||||
|
value = e.detail |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<div class="container"> |
<Field {label} {labelPosition} {disabled} {error}> |
||||
{#if label || edit} |
<TextArea |
||||
<div class="label-container"> |
{error} |
||||
{#if label} |
{disabled} |
||||
<Label extraSmall grey forAttr={name}>{label}</Label> |
{value} |
||||
{/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)} |
|
||||
{placeholder} |
{placeholder} |
||||
{name} |
{updateOnChange} |
||||
use:text_area_resize /> |
on:change={onChange} /> |
||||
</div> |
</Field> |
||||
|
|
||||
<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> |
|
||||
|
|||||
Loading…
Reference in new issue