Add Code component

This commit is contained in:
venashial
2022-07-09 14:51:04 -07:00
parent 035fa3be3f
commit 48bc18017e
9 changed files with 111 additions and 23 deletions

View File

@@ -0,0 +1,7 @@
```svelte example raised
<script lang="ts">
import { Code } from 'omorphia'
</script>
<Code text="AaBbCcDd" />
```

View File

@@ -52,9 +52,16 @@
if (!disabled) dispatch('click') if (!disabled) dispatch('click')
} }
function dispatchFiles(event: Event) { // Handle `change` event on file input
function handleChangeFiles(event: Event) {
if (!disabled) dispatch('files', (event.target as HTMLInputElement).files || new FileList()) if (!disabled) dispatch('files', (event.target as HTMLInputElement).files || new FileList())
} }
// Handle `drop` event on file input
function handleDropFiles(event: DragEvent) {
event.preventDefault()
if (!disabled) dispatch('files', event.dataTransfer.files || new FileList())
}
</script> </script>
{#if as === 'a'} {#if as === 'a'}
@@ -64,8 +71,13 @@
{:else if as === 'input'} {:else if as === 'input'}
<input class={className} {value} {disabled} {title} on:click={dispatchClick} /> <input class={className} {value} {disabled} {title} on:click={dispatchClick} />
{:else if as === 'file'} {:else if as === 'file'}
<label class={className} {disabled} {title}> <label
<input type="file" on:change={dispatchFiles} /> class={className}
{disabled}
{title}
on:drop={handleDropFiles}
on:dragover={(event) => event.preventDefault()}>
<input type="file" on:change={handleChangeFiles} />
<slot /> <slot />
</label> </label>
{:else} {:else}

View File

@@ -0,0 +1,45 @@
<script lang="ts">
import IconClipboardCopy from 'virtual:icons/heroicons-outline/clipboard-copy'
import IconCheck from 'virtual:icons/heroicons-outline/check'
export let text: string
let copied = false
</script>
<button
class="code"
class:copied
title="Copy code to clipboard"
on:click={async () => {
await navigator.clipboard.writeText(text)
copied = true
}}>
{text}
{#if copied}
<IconCheck />
{:else}
<IconClipboardCopy />
{/if}
</button>
<style lang="postcss">
.code {
display: flex;
gap: 0.5rem;
font-family: var(--mono-font);
padding: 0.25rem 0.5rem;
background-color: var(--color-code-bg);
width: min-content;
border-radius: var(--rounded-sm);
user-select: text;
&.copied {
cursor: default;
}
&:hover:not(.copied) {
filter: brightness(0.9);
}
}
</style>

View File

@@ -13,12 +13,12 @@
} }
export let multiple = false export let multiple = false
export let accept: string export let accept: string = '*'
/** Prevents width from expanding due to large file names or images */ /** Prevents width from expanding due to large file names or images */
export let constrained = false export let constrained = false
export let files: (File | RemoteFile)[] = [] export let files: (File | RemoteFile)[] = []
export let file: File | RemoteFile | undefined export let file: File | RemoteFile | undefined = undefined
$: if (files) file = files[0] || undefined $: if (files) file = files[0] || undefined
let inputElement: HTMLInputElement let inputElement: HTMLInputElement
@@ -28,7 +28,7 @@
// Check for duplicate files that aren't remote // Check for duplicate files that aren't remote
if ( if (
!files !files
.filter((it) => it instanceof File) .filter((it) => !('remote' in file))
.map((file) => file.name) .map((file) => file.name)
.includes(file.name) .includes(file.name)
) { ) {
@@ -67,7 +67,7 @@
{#each files as file (file.name)} {#each files as file (file.name)}
<div class="file"> <div class="file">
<div class="file__tab"> <div class="file__tab">
{#if file instanceof File && file.type.startsWith('image/')} {#if !('remote' in file) && file.type.startsWith('image/')}
<IconPhotograph /> <IconPhotograph />
{:else} {:else}
<IconFile /> <IconFile />
@@ -79,7 +79,7 @@
files = files.filter((it) => it.name !== file.name) files = files.filter((it) => it.name !== file.name)
}}><IconTrash /> Remove</Button> }}><IconTrash /> Remove</Button>
</div> </div>
{#if file instanceof File && file.type.startsWith('image/')} {#if !('remote' in file) && file.type.startsWith('image/')}
<div class="file__preview"> <div class="file__preview">
<img <img
class="file__preview__image" class="file__preview__image"

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { browser, prerendering } from '$app/env' import { browser, prerendering } from '$app/env'
import { page } from '$app/stores' import { page } from '$app/stores'
import { onMount } from 'svelte' import { onDestroy, onMount } from 'svelte'
import { debounce } from 'throttle-debounce' import { debounce } from 'throttle-debounce'
interface Link { interface Link {
@@ -53,18 +53,24 @@
$: if (activeIndex > -1 && browser && linkElements.length > 0) startAnimation() $: if (activeIndex > -1 && browser && linkElements.length > 0) startAnimation()
function startAnimation() { function startAnimation() {
indicator.direction = activeIndex < oldIndex ? 'left' : 'right' // Avoids error that `linkElements[activeIndex]` is null
if (linkElements[activeIndex]) {
indicator.direction = activeIndex < oldIndex ? 'left' : 'right'
indicator.left = linkElements[activeIndex].offsetLeft indicator.left = linkElements[activeIndex].offsetLeft
indicator.right = indicator.right =
linkElements[activeIndex].parentElement.offsetWidth - linkElements[activeIndex].parentElement.offsetWidth -
linkElements[activeIndex].offsetLeft - linkElements[activeIndex].offsetLeft -
linkElements[activeIndex].offsetWidth linkElements[activeIndex].offsetWidth
indicator.top = linkElements[activeIndex].offsetTop + linkElements[activeIndex].offsetHeight - 2 indicator.top =
linkElements[activeIndex].offsetTop + linkElements[activeIndex].offsetHeight - 2
}
oldIndex = activeIndex oldIndex = activeIndex
} }
const debounced = debounce(100, startAnimation)
let useAnimation = false let useAnimation = false
onMount(() => { onMount(() => {
@@ -72,7 +78,11 @@
useAnimation = true useAnimation = true
}, 300) }, 300)
window.addEventListener('resize', debounce(100, startAnimation)) window.addEventListener('resize', debounced)
})
onDestroy(() => {
if (browser) window.removeEventListener('resize', debounced)
}) })
</script> </script>

View File

@@ -2,8 +2,9 @@
import IconChevronDown from 'virtual:icons/lucide/chevron-down' import IconChevronDown from 'virtual:icons/lucide/chevron-down'
import IconCheck from 'virtual:icons/heroicons-outline/check' import IconCheck from 'virtual:icons/heroicons-outline/check'
import { clickOutside } from 'svelte-use-click-outside' import { clickOutside } from 'svelte-use-click-outside'
import { onMount, tick } from 'svelte' import { onDestroy, onMount, tick } from 'svelte'
import { debounce } from 'throttle-debounce' import { debounce } from 'throttle-debounce'
import { browser } from '$app/env'
interface Option { interface Option {
label: string label: string
@@ -129,12 +130,20 @@
} }
} }
const debounced = debounce(100, checkDirection)
onMount(() => { onMount(() => {
checkDirection() checkDirection()
const debounced = debounce(100, checkDirection)
window.addEventListener('resize', debounced) window.addEventListener('resize', debounced)
window.addEventListener('scroll', debounced) document.body.addEventListener('scroll', debounced)
})
onDestroy(() => {
if (browser) {
window.removeEventListener('resize', debounced)
document.body.removeEventListener('scroll', debounced)
}
}) })
</script> </script>

View File

@@ -10,13 +10,14 @@
export let id: string = undefined export let id: string = undefined
export let fill = false export let fill = false
export let raised = false export let raised = false
export let autofocus = false
</script> </script>
<div class={classCombine(['text-input', raised && 'text-input--raised'])} class:fill> <div class={classCombine(['text-input', raised && 'text-input--raised'])} class:fill>
{#if multiline} {#if multiline}
<textarea {id} {placeholder} bind:value /> <textarea {id} {placeholder} {autofocus} bind:value />
{:else} {:else}
<input type="text" {id} {placeholder} bind:value class:has-icon={icon} /> <input type="text" {id} {placeholder} {autofocus} bind:value class:has-icon={icon} />
{#if icon} {#if icon}
<svelte:component this={icon} /> <svelte:component this={icon} />
{/if} {/if}

View File

@@ -12,6 +12,8 @@ export { default as CheckboxVirtualList } from './components/CheckboxVirtualList
export { default as Chips } from './components/Chips.svelte' export { default as Chips } from './components/Chips.svelte'
export { default as Code } from './components/Code.svelte'
export { default as Field } from './components/Field.svelte' export { default as Field } from './components/Field.svelte'
export { default as FileUpload } from './components/FileUpload.svelte' export { default as FileUpload } from './components/FileUpload.svelte'

View File

@@ -16,8 +16,10 @@
align-items: center; align-items: center;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
gap: 1rem; gap: 1rem;
/* Prevents shifting in height when button added */
min-height: 3.5rem;
:global(.icon) { > .icon {
/* Uses `px` to make icons slightly larger */ /* Uses `px` to make icons slightly larger */
min-width: 18px; min-width: 18px;
height: 18px; height: 18px;