You've already forked AstralRinth
forked from didirus/AstralRinth
Add component API to built docs + Add Checkbox, CheckboxList, & CheckboxVirtualList
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<script lang="ts">
|
||||
// TODO: sizes
|
||||
// TODO: icon only buttons should have uniform padding
|
||||
|
||||
import { classCombine } from '$lib/utils/classCombine'
|
||||
|
||||
/** The element to be styled as a button */
|
||||
|
||||
66
src/lib/components/Checkbox.svelte
Normal file
66
src/lib/components/Checkbox.svelte
Normal file
@@ -0,0 +1,66 @@
|
||||
<script lang="ts">
|
||||
import uniqueId from 'lodash.uniqueid'
|
||||
import IconCheck from 'virtual:icons/heroicons-outline/check'
|
||||
|
||||
export let checked = false;
|
||||
|
||||
const id = `checkbox-${uniqueId()}`
|
||||
</script>
|
||||
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" bind:checked={checked} on:change id={id} name={id}>
|
||||
<label for={id} class="checkbox__label">
|
||||
<span class="checkbox__label__box">
|
||||
<IconCheck />
|
||||
</span>
|
||||
<slot/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.checkbox {
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
grid-gap: 0.5rem;
|
||||
|
||||
&__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
> :global(.icon) {
|
||||
width: 1.1rem;
|
||||
margin-right: -0.1rem;
|
||||
}
|
||||
|
||||
&__box {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
display: flex;
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--color-button-bg);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
> :global(.icon) {
|
||||
display: none;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
color: var(--color-raised-bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[type=checkbox]:checked + label span {
|
||||
background-color: var(--color-brand);
|
||||
|
||||
:global(.icon) {
|
||||
display: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
47
src/lib/components/CheckboxList.svelte
Normal file
47
src/lib/components/CheckboxList.svelte
Normal file
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import Checkbox from './Checkbox.svelte'
|
||||
import type { SvelteComponent } from 'svelte'
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
/** The element that will be in the `value` array while the option is checked */
|
||||
value: string | number;
|
||||
icon: SvelteComponent;
|
||||
}
|
||||
|
||||
export let value = []
|
||||
export let options: Option[] = []
|
||||
|
||||
const handleChange = (e, key) => {
|
||||
if (e.target.checked) {
|
||||
if (!value) value = []
|
||||
value = [key, ...value]
|
||||
} else {
|
||||
value = value.filter((it) => key !== it)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="checkbox-list">
|
||||
{#each options as option}
|
||||
<Checkbox on:change={(e) => handleChange(e, option.value)}>
|
||||
{#if option.icon}
|
||||
<svelte:component this={option.icon}/>
|
||||
{/if}
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.checkbox-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.wrap {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
src/lib/components/CheckboxVirtualList.svelte
Normal file
56
src/lib/components/CheckboxVirtualList.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import Checkbox from './Checkbox.svelte'
|
||||
import type { SvelteComponent } from 'svelte'
|
||||
import VirtualList from 'svelte-tiny-virtual-list'
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
/** The element that will be in the `value` array while the option is checked */
|
||||
value: string | number;
|
||||
icon: SvelteComponent;
|
||||
}
|
||||
|
||||
/** Height in pixels of list */
|
||||
export let height = 200
|
||||
|
||||
export let value = []
|
||||
export let options: Option[] = []
|
||||
|
||||
const handleChange = (e, key) => {
|
||||
if (e.target.checked) {
|
||||
if (!value) value = []
|
||||
value = [key, ...value]
|
||||
} else {
|
||||
value = value.filter((it) => key !== it)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<VirtualList
|
||||
width="100%"
|
||||
{height}
|
||||
itemCount={options.length}
|
||||
itemSize={30}>
|
||||
<div slot="item" let:index let:style {style} style:padding-bottom={(options.length) - 1 === index ? '2.5rem' : ''}>
|
||||
{@const option = options[index]}
|
||||
<Checkbox checked={value.includes(option.value)} on:change={(e) => handleChange(e, option.value)}>
|
||||
{#if option.icon}
|
||||
<svelte:component this={option.icon}/>
|
||||
{/if}
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</VirtualList>
|
||||
|
||||
<style lang="postcss">
|
||||
.checkbox-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.wrap {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
53
src/lib/components/Input.svelte
Normal file
53
src/lib/components/Input.svelte
Normal file
@@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
// TODO: Move to TextInput.svelte & simplify options for text
|
||||
|
||||
import { SvelteComponent } from 'svelte'
|
||||
|
||||
export let key = ''
|
||||
export let label = ''
|
||||
export let placeholder = ''
|
||||
export let icon: string | SvelteComponent = ''
|
||||
export let type: 'text' | 'textarea'
|
||||
export let options: [
|
||||
{
|
||||
label: string;
|
||||
value: string | number;
|
||||
icon: string;
|
||||
}
|
||||
] = []
|
||||
export let value = undefined
|
||||
|
||||
export let wrap = false
|
||||
</script>
|
||||
|
||||
{#if label}
|
||||
<div class="input__label">
|
||||
{label}
|
||||
</div>
|
||||
{/if}
|
||||
{#if type === 'text'}
|
||||
<input type="text" name={key} class="input-box" {placeholder} bind:value={value}/>
|
||||
{:else if type === 'textarea'}
|
||||
<textarea name={key} class="input-box input-box--fill" {placeholder} bind:value={value}/>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.input-box {
|
||||
border-radius: var(--rounded-sm);
|
||||
box-shadow: var(--shadow-inset-sm);
|
||||
background-color: var(--color-button-bg);
|
||||
border: none;
|
||||
padding: 0.25rem 0.75rem;
|
||||
width: 20rem;
|
||||
max-width: 100%;
|
||||
|
||||
&--fill {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
import IconMoon from 'virtual:icons/heroicons-outline/moon'
|
||||
import IconSun from 'virtual:icons/heroicons-outline/sun'
|
||||
|
||||
let theme = 'light'
|
||||
let theme = 'dark'
|
||||
let background: 'var(--color-raised-bg)' | 'var(--color-bg)' = 'var(--color-bg)'
|
||||
</script>
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
&__code {
|
||||
margin: 0;
|
||||
border-radius: var(--rounded-sm-bottom) !important;
|
||||
background: hsl(220, 13%, 22%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
let api
|
||||
if ($page.url.pathname.includes('components')) {
|
||||
import(`../../../lib/components/${title}.svelte?raw&sveld`).then(output => api = output.default)
|
||||
if (import.meta.env.DEV) {
|
||||
import(`../../../lib/components/${title}.svelte?raw&sveld`).then(output => api = output.default)
|
||||
} else {
|
||||
fetch('/_app/COMPONENT_API.json').then(res => res.json()).then(output => api = output[`${title}.svelte`])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -42,9 +46,9 @@
|
||||
<tbody>
|
||||
{#each api.props as prop}
|
||||
<tr>
|
||||
<td>{prop.name}</td>
|
||||
<td><code>{prop.name}</code></td>
|
||||
<td><code>{prop.type ?? ''}</code></td>
|
||||
<td>{prop.value ?? ''}</td>
|
||||
<td><code>{prop.value ?? ''}</code></td>
|
||||
<td>{prop.constant ? '[Read only] ' : ''}{prop.description?.replace('null', '') || ''}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
@@ -64,7 +68,7 @@
|
||||
<tbody>
|
||||
{#each api.events as event}
|
||||
<tr>
|
||||
<td>{event.name}</td>
|
||||
<td><code>{event.name}</code></td>
|
||||
<td>{!!event.parent}</td>
|
||||
<td>{event.description?.replace('null', '') || ''}</td>
|
||||
</tr>
|
||||
@@ -84,8 +88,8 @@
|
||||
<tbody>
|
||||
{#each api.slots as slot}
|
||||
<tr>
|
||||
<td>{slot.name}</td>
|
||||
<td>{slot.fallback}</td>
|
||||
<td><code>{slot.name}</code></td>
|
||||
<td>{slot.fallback ?? 'None'}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
||||
20
src/routes/components/Checkbox.md
Normal file
20
src/routes/components/Checkbox.md
Normal file
@@ -0,0 +1,20 @@
|
||||
### Text-only Example
|
||||
|
||||
```svelte example
|
||||
<script lang="ts">
|
||||
import Checkbox from "omorphia/components/Checkbox.svelte";
|
||||
</script>
|
||||
|
||||
<Checkbox>Extra components</Checkbox>
|
||||
```
|
||||
|
||||
### Text with Icon Example
|
||||
|
||||
```svelte example
|
||||
<script lang="ts">
|
||||
import Checkbox from "omorphia/components/Checkbox.svelte";
|
||||
import IconCarrot from 'virtual:icons/lucide/carrot'
|
||||
</script>
|
||||
|
||||
<Checkbox><IconCarrot /> Food </Checkbox>
|
||||
```
|
||||
33
src/routes/components/CheckboxList.md
Normal file
33
src/routes/components/CheckboxList.md
Normal file
@@ -0,0 +1,33 @@
|
||||
```svelte example
|
||||
<script lang="ts">
|
||||
import CheckboxList from "omorphia/components/CheckboxList.svelte";
|
||||
import IconSquare from 'virtual:icons/lucide/square'
|
||||
import IconCircle from 'virtual:icons/lucide/circle'
|
||||
import IconTriangle from 'virtual:icons/lucide/triangle'
|
||||
|
||||
let selected = []
|
||||
</script>
|
||||
|
||||
<CheckboxList
|
||||
bind:value={selected}
|
||||
options={[
|
||||
{
|
||||
label: 'Circle',
|
||||
icon: IconCircle,
|
||||
value: 'CIR',
|
||||
},
|
||||
{
|
||||
label: 'Triangle',
|
||||
icon: IconTriangle,
|
||||
value: 'TRI',
|
||||
},
|
||||
{
|
||||
label: 'Square',
|
||||
icon: IconSquare,
|
||||
value: 'SQU',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
Selected: {selected}
|
||||
```
|
||||
23
src/routes/components/CheckboxVirtualList.md
Normal file
23
src/routes/components/CheckboxVirtualList.md
Normal file
@@ -0,0 +1,23 @@
|
||||
```svelte example
|
||||
<script lang="ts">
|
||||
import CheckboxVirtualList from "omorphia/components/CheckboxVirtualList.svelte";
|
||||
import IconStar from 'virtual:icons/heroicons-outline/star'
|
||||
import uniqueId from 'lodash.uniqueid'
|
||||
|
||||
let options = Array(100).fill({})
|
||||
.map(option => ({
|
||||
label: 'Star-' + uniqueId(),
|
||||
icon: IconStar,
|
||||
value: uniqueId(),
|
||||
}))
|
||||
|
||||
let selected = ['2', '6']
|
||||
</script>
|
||||
|
||||
<CheckboxVirtualList
|
||||
bind:value={selected}
|
||||
{options}
|
||||
/>
|
||||
|
||||
Selected: {selected}
|
||||
```
|
||||
@@ -13,8 +13,8 @@
|
||||
label: 'Button'
|
||||
},
|
||||
{
|
||||
href: '/Link',
|
||||
label: 'Link'
|
||||
href: '/Chips',
|
||||
label: 'Chips'
|
||||
},
|
||||
{
|
||||
href: '/NavRow',
|
||||
|
||||
Reference in New Issue
Block a user