You've already forked AstralRinth
forked from didirus/AstralRinth
Add Code component
This commit is contained in:
7
docs/routes/components/Code.md
Normal file
7
docs/routes/components/Code.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
```svelte example raised
|
||||||
|
<script lang="ts">
|
||||||
|
import { Code } from 'omorphia'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Code text="AaBbCcDd" />
|
||||||
|
```
|
||||||
@@ -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}
|
||||||
|
|||||||
45
src/components/Code.svelte
Normal file
45
src/components/Code.svelte
Normal 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>
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user