add common components

This commit is contained in:
Ludvík Prokopec 2022-12-20 19:35:25 +01:00
parent d86a69c9a7
commit f0ca5baf15
40 changed files with 276 additions and 189 deletions

View File

@ -1,10 +0,0 @@
import FileDrop from './components/FileDrop.svelte'
import Input from './components/Input.svelte'
import Sortable from './components/Sortable.svelte'
import BlockQuote from './components/BlockQuote.svelte'
import MarkdownEditor from './components/MarkdownEditor.svelte'
import MarkdownRenderer from './components/MarkdownRenderer.svelte'
import { Radio, Checkbox, Fileupload as FileUpload, Range, Select, Textarea, Toggle, Hr as HorizontalRule, Button, ButtonGroup } from 'flowbite-svelte'
export { FileDrop, Input, Radio, Checkbox, FileUpload, Range, Select, Textarea, Toggle, Sortable, BlockQuote, HorizontalRule, MarkdownEditor, MarkdownRenderer, Button, ButtonGroup }

View File

@ -1,9 +0,0 @@
<script>
import { Blockquote, P } from 'flowbite-svelte'
</script>
<Blockquote border bg class="p-4 my-4">
<P height="relaxed">
<slot />
</P>
</Blockquote>

View File

@ -1,13 +0,0 @@
<script lang="ts">
import { Dropzone } from 'flowbite-svelte'
export let rules = []
</script>
<Dropzone on:blur on:change on:click on:focus on:mouseenter on:mouseleave on:mouseover>
<svg aria-hidden="true" class="mb-3 w-10 h-10 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p>
<p class="text-xs text-gray-500 dark:text-gray-400">{rules.join(', ')}</p>
</Dropzone>

View File

@ -1,45 +0,0 @@
<script lang="ts">
import { Input, Label, Helper } from 'flowbite-svelte'
import id from './idGenerator'
const elementId = id('input-')
export let label = ''
export let value = ''
export let error: string = null
export let type:
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'reset'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week'
| 'search' = 'text'
</script>
<div class="mb-6">
<Label for={elementId} color={error ? 'red' : null} class="mb-2">{label}</Label>
<Input bind:value {type} color={error ? 'red' : null} id={elementId} {...$$props}>
<slot />
</Input>
{#if error}
<Helper class="mt-2" color="red">
<span class="font-medium">Error!</span>
{error}
</Helper>
{/if}
</div>

View File

@ -1,5 +0,0 @@
<div class="h-full w-full top-0 left-0 flex justify-center items-center">
<div class="animate-spin inline-block w-6 h-6 border-[3px] border-current border-t-transparent text-current rounded-full" role="status" aria-label="loading">
<span class="sr-only">Loading...</span>
</div>
</div>

View File

@ -1,15 +0,0 @@
<script>
import 'bytemd/dist/index.css'
import { Editor } from 'bytemd'
import gfm from '@bytemd/plugin-gfm'
const plugins = [gfm()]
export let value = ''
</script>
<div>
<template>
<Editor {value} {plugins} on:change={(e) => (value = e.detail.value)} />
</template>
</div>

View File

@ -1,15 +0,0 @@
<script>
import 'bytemd/dist/index.css'
import { Viewer } from 'bytemd'
import gfm from '@bytemd/plugin-gfm'
const plugins = [gfm()]
export let value = ''
</script>
<div>
<template>
<Viewer {value} {plugins} />
</template>
</div>

View File

@ -1,6 +0,0 @@
const gen = (function* () {
let index = -1
while (true) yield index++
})()
export default (pre: string = '') => `${pre}${gen.next().value}`

View File

@ -0,0 +1,15 @@
import Button from './Common/Button.svelte';
import Input from './Common/Input.svelte';
import InputWrappper from './Common/InputWrappper.svelte';
import Loading from './Common/Loading.svelte';
import SOffline from './Common/SOffline.svelte';
import Link from './Common/Link.svelte';
import Sortable from './Common/Sortable.svelte';
import Radio from './Common/Radio.svelte';
import Checkbox from './Common/Checkbox.svelte';
import Switch from './Common/Switch.svelte';
import Select from './Common/Select.svelte';
import { Body, Html, PwaInstaller } from './Common/Meta';
export { Button, Input, InputWrappper, Loading, SOffline, Body, Html, PwaInstaller, Link, Sortable, Switch, Radio, Checkbox, Select };

View File

@ -0,0 +1,15 @@
<script lang="ts">
export let href: string = null
let className = ''
export { className as class }
</script>
{#if href !== null}
<a {href} class={className}>
<slot />
</a>
{:else}
<button class={className} on:click>
<slot />
</button>
{/if}

View File

@ -0,0 +1,13 @@
<script>
export let checked = false
let className = ''
export { className as class }
</script>
<input
type="checkbox"
class="{className} shrink-0 mt-0.5 border-gray-200 rounded text-blue-600 pointer-events-none focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
{checked}
{...$$restProps}
/>

View File

@ -0,0 +1,41 @@
<script lang="ts">
export let type:
| 'color'
| 'date'
| 'datetime-local'
| 'email'
| 'file'
| 'hidden'
| 'image'
| 'month'
| 'number'
| 'password'
| 'reset'
| 'submit'
| 'tel'
| 'text'
| 'time'
| 'url'
| 'week'
| 'search'
| 'textarea' = 'text'
export let value: any = null
let className = ''
export { className as class }
export let focus = false
const setType = (node: HTMLInputElement) => {
node.type = type
}
const setFocus = (node: HTMLInputElement | HTMLTextAreaElement) => {
focus && node.focus()
}
</script>
{#if type === 'textarea'}
<textarea use:setFocus bind:value class={className} {...$$restProps} />
{:else}
<input use:setType use:setFocus bind:value class={className} {...$$restProps} />
{/if}

View File

@ -0,0 +1,9 @@
<script lang="ts">
import generateElementId from '$lib/utils/elementIdGenerator'
let className = ''
export { className as class }
</script>
<div class={className}>
<slot id={generateElementId()} />
</div>

View File

@ -0,0 +1,8 @@
<script lang="ts">
let className = 'text-blue-600'
export { className as class }
</script>
<div class="{className} animate-spin inline-block w-6 h-6 border-[3px] border-current border-t-transparent rounded-full" role="status" aria-label="loading">
<span class="sr-only">Loading...</span>
</div>

View File

@ -0,0 +1,5 @@
import Body from './meta/Body.svelte';
import Html from './meta/Html.svelte';
import PwaInstaller from './meta/PwaInstaller.svelte';
export { Body, Html, PwaInstaller }

View File

@ -0,0 +1,5 @@
<input
type="radio"
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 pointer-events-none focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
{...$$restProps}
/>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
$: isOnline = navigator.onLine || false
const updateOnlineStatus = () => {
isOnline = navigator.onLine
dispatch('detectedCondition', { online: isOnline })
}
</script>
<svelte:window on:online={updateOnlineStatus} on:offline={updateOnlineStatus} />
{#if isOnline}
<slot name="online" />
{:else}
<slot name="offline" />
{/if}

View File

@ -0,0 +1,8 @@
<select
on:change
on:select
class="py-3 px-4 pr-9 block w-full border-gray-200 rounded-md text-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400"
{...$$restProps}
>
<slot />
</select>

View File

@ -0,0 +1,13 @@
<script>
export let checked = false
let className = ''
export { className as class }
</script>
<input
type="checkbox"
class="{className} first-letter:relative w-[3.25rem] h-7 bg-gray-100 checked:bg-none checked:bg-blue-600 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 ring-1 ring-transparent focus:border-blue-600 focus:ring-blue-600 ring-offset-white focus:outline-none appearance-none dark:bg-gray-700 dark:checked:bg-blue-600 dark:focus:ring-offset-gray-800 before:inline-block before:w-6 before:h-6 before:bg-white checked:before:bg-blue-200 before:translate-x-0 checked:before:translate-x-full before:shadow before:rounded-full before:transform before:ring-0 before:transition before:ease-in-out before:duration-200 dark:before:bg-gray-400 dark:checked:before:bg-blue-200"
{checked}
{...$$restProps}
/>

View File

@ -0,0 +1,16 @@
<script>
import { onMount } from 'svelte'
let className = null
export { className as class }
export let style = null
let bodyElement
onMount(() => {
bodyElement = document.body
})
$: className !== null && bodyElement && (bodyElement.className = className)
$: style !== null && bodyElement && (bodyElement.style.cssText = style)
</script>

View File

@ -0,0 +1,20 @@
<script>
import { onMount } from 'svelte'
export let lang = null
let className = null
export { className as class }
export let style = null
export let xmlns = null
let htmlElement
onMount(() => {
htmlElement = document.documentElement
})
$: className !== null && htmlElement && (htmlElement.className = className)
$: lang !== null && htmlElement && (htmlElement.lang = lang)
$: style !== null && htmlElement && (htmlElement.style.cssText = style)
$: xmlns !== null && htmlElement && (htmlElement.xmlns = xmlns)
</script>

View File

@ -0,0 +1,22 @@
<script>
import { installPrompt } from '../../stores/pwaInstall'
const install = async () => {
if ($installPrompt !== null) {
$installPrompt.prompt()
const { outcome } = await $installPrompt.userChoice
if (outcome === 'accepted') $installPrompt = null
}
}
</script>
<svelte:window
on:beforeinstallprompt|preventDefault={(e) => {
$installPrompt = e
}}
/>
{#if $installPrompt}
<slot {install} />
{/if}

View File

@ -1,15 +0,0 @@
<div>
<slot />
</div>
<style lang="scss">
div {
background-color: rgba(0, 0, 0, 0.1);
border-radius: 0.25rem;
color: rgba(0, 0, 0, 0.7);
box-shadow: #d1d5db 0px -4px 0px inset, rgba(0, 0, 0, 0.4) 0px 1px 1px;
padding: 0.25rem 0.5rem;
}
</style>

View File

@ -0,0 +1,6 @@
import Stack from './Layout/Stack.svelte';
import Row from './Layout/Row.svelte';
import Container from './Layout/Container.svelte';
import AspectRatio from './Layout/AspectRatio.svelte';
export { Stack, Row, Container, AspectRatio };

View File

@ -0,0 +1,13 @@
<script lang="ts">
export let ratio: string
</script>
<div style="--ratio: {ratio};">
<slot />
</div>
<style lang="scss">
div {
aspect-ratio: var(--ratio);
}
</style>

View File

@ -0,0 +1,3 @@
<div class="container">
<slot />
</div>

View File

@ -0,0 +1,3 @@
<div class="flex flex-wrap">
<slot />
</div>

View File

@ -0,0 +1,8 @@
<script lang="ts">
let className = ''
export { className as class }
</script>
<div class="{className} flex flex-col justify-start">
<slot />
</div>

View File

@ -1,5 +0,0 @@
<div class="h-full w-full top-0 left-0 flex justify-center items-center">
<div class="animate-spin inline-block w-6 h-6 border-[3px] border-current border-t-transparent text-current rounded-full" role="status" aria-label="loading">
<span class="sr-only">Loading...</span>
</div>
</div>

View File

@ -1,14 +0,0 @@
<script lang="ts">
export let columns = 3
export let gap = 4
</script>
<div class="masonry-grid columns-{columns} gap-x-{gap}">
<slot />
</div>
<style lang="scss">
:global(div.masonry-grid *) {
break-inside: avoid;
}
</style>

View File

@ -1,9 +0,0 @@
<div class="flex">
<aside class="sidebar__sidebar w-[30%]">
<slot name="aside" />
</aside>
<main class="overflow-auto flex-1">
<slot />
</main>
</div>

View File

@ -1,23 +0,0 @@
<script lang="ts">
export let space = '3rem'
</script>
<div class="layout-stack" style="--space: {space};">
<slot />
</div>
<style lang="scss">
:global(.layout-stack) {
display: flex;
flex-direction: column;
justify-content: flex-start;
& > * {
margin-block: 0;
}
& > * + * {
margin-block-start: var(--space, 1.5rem);
}
}
</style>

View File

@ -0,0 +1,17 @@
export default (node) => {
const handleClick = event => {
if (node && !node.contains(event.target) && !event.defaultPrevented) {
node.dispatchEvent(
new CustomEvent('outclick', node)
)
}
}
document.addEventListener('click', handleClick, true)
return {
destroy() {
document.removeEventListener('click', handleClick, true)
}
}
}

View File

@ -0,0 +1,6 @@
const generator = (function* () {
let index = -1
while (true) yield index++
})()
export default (pre: string = 'input') => `${pre.split(' ').join('-')}-${generator.next().value}`

9
src/lib/utils/share.ts Normal file
View File

@ -0,0 +1,9 @@
export default async (data) => {
try {
await navigator.share(data)
} catch (e) {
navigator.clipboard.writeText(data.url || window.location.href)
}
return true
}

View File

@ -1,5 +1,5 @@
<script lang="ts">
import Link from '$lib/components/Link.svelte'
import { Link } from '$lib/components/Common'
import { _ } from 'svelte-i18n'
</script>

View File

@ -17,9 +17,6 @@
"$src/*": [
"./src/*"
],
"$cms/*": [
"./cms/*"
],
"$routes/*": [
"./src/routes/*"
],

View File

@ -10,7 +10,6 @@ export default defineConfig({
'$lib': path.resolve(__dirname, 'src', 'lib'),
'$root': path.resolve(__dirname),
'$src': path.resolve(__dirname, 'src'),
'$cms': path.resolve(__dirname, 'src', 'cms'),
'$routes': path.resolve(__dirname, 'src', 'routes')
}
},