Skip to content

Commit

Permalink
feat: form related components
Browse files Browse the repository at this point in the history
  • Loading branch information
paring-chan committed Mar 20, 2024
1 parent 2c5f027 commit 9598059
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 2 deletions.
64 changes: 64 additions & 0 deletions src/lib/components/form/Checkbox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script lang="ts">
/**
* The checkboxes state.
*/
export let checked: boolean = false
</script>

<label class="checkbox-label">
<span class="checkbox-container">
<input
type="checkbox"
style="position: absolute; opacity: 0; pointer-events: none;"
bind:checked
{...$$restProps}
on:change
/>
<span class="checkbox">
<svg
class="checkbox-icon"
xmlns="http://www.w3.org/2000/svg"
width="8"
height="7"
viewBox="0 0 8 7"
fill="none"
>
<path d="M0.5 3L3 6L7.5 1" stroke="white" stroke-linecap="round" />
</svg>
</span>
</span>
</label>

<style lang="scss">
.checkbox-container {
position: relative;
width: 14px;
height: 14px;
}
.checkbox {
position: relative;
display: inline-block;
width: 14px;
height: 14px;
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: 3px;
}
.checkbox-icon {
position: absolute;
top: 50%;
left: 50%;
opacity: 0;
transition: opacity ease 0.1s;
transform: translate(-50%, -50%);
}
input[type='checkbox']:focus + .checkbox {
border-color: white;
}
input[type='checkbox']:checked + .checkbox > .checkbox-icon {
opacity: 1;
}
</style>
44 changes: 44 additions & 0 deletions src/lib/components/form/FormField.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import type { TranslationKey } from '$lib/index.js'
import Translation from '$lib/utils/Translation.svelte'
import FormHint from './FormHint.svelte'
export let label: TranslationKey | null = null
export let error: TranslationKey | null = null
</script>

<div class="form-field">
<label class="form-field-content">
{#if label}
<p class="label">
<Translation key={label} />
</p>
{/if}
<slot />
</label>
{#if error}
<FormHint>
<Translation key={error} />
</FormHint>
{/if}
</div>

<style lang="scss">
.label {
font-size: 16px;
font-weight: 500;
line-height: 16px;
}
.form-field {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-field-content {
display: flex;
flex-direction: column;
gap: 2px;
}
</style>
25 changes: 25 additions & 0 deletions src/lib/components/form/FormHint.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="hint">
<slot />
</div>

<style lang="scss">
.hint {
display: flex;
color: rgba(var(--color-red), 1);
font-weight: 500;
font-size: 14px;
line-height: 14px;
align-items: center;
gap: 6px;
&::before {
content: '';
width: 8px;
height: 8px;
background-color: currentColor;
border-radius: 4px;
}
}
</style>
45 changes: 45 additions & 0 deletions src/lib/components/form/HorizontalFormField.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts">
import type { TranslationKey } from '$lib/index.js'
import Translation from '$lib/utils/Translation.svelte'
import FormHint from './FormHint.svelte'
export let label: TranslationKey | null = null
export let error: TranslationKey | null = null
</script>

<div class="form-field">
<label class="form-field-content">
{#if label}
<p class="label">
<Translation key={label} />
</p>
{/if}
<slot />
</label>
{#if error}
<FormHint>
<Translation key={error} />
</FormHint>
{/if}
</div>

<style lang="scss">
.label {
font-size: 16px;
font-weight: 500;
line-height: 16px;
flex-grow: 1;
}
.form-field {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-field-content {
display: flex;
gap: 16px;
align-items: center;
}
</style>
43 changes: 43 additions & 0 deletions src/lib/components/form/InputControl.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import { getGlobalContext, translate, type TranslationKey } from '$lib/index.js'
export let placeholder: TranslationKey | null = null
const ctx = getGlobalContext()
$: translatedPlaceholder = placeholder ? translate(ctx.language, placeholder, {}, false) : null
$: console.log($translatedPlaceholder)
</script>

<div class="input-control">
<input
{...$$restProps}
placeholder={translatedPlaceholder ? $translatedPlaceholder : null}
class="input"
/>
</div>

<style lang="scss">
.input-control {
height: 32px;
display: flex;
border-bottom: 1px solid rgba(255, 255, 255, 0.4);
}
.input {
flex-grow: 1;
background: transparent;
&:focus {
outline: none;
}
&::placeholder {
font-weight: 300;
font-size: 16px;
line-height: 16px;
}
}
</style>
21 changes: 21 additions & 0 deletions src/lib/components/form/LabeledCheckbox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import { Checkbox, type TranslationKey } from '$lib/index.js'
export let label: TranslationKey
</script>

<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="label">
<Checkbox />
<span>{label}</span>
</label>

<style lang="scss">
.label {
display: flex;
gap: 8px;
align-items: center;
font-size: 14px;
line-height: 14px;
}
</style>
7 changes: 7 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,10 @@ export { default as NavAccessibilitySettings } from './components/nav/NavAccessi
export { default as Footer } from './components/footer/Footer.svelte'
export { default as FooterLink } from './components/footer/FooterLink.svelte'
export { default as FooterSection } from './components/footer/FooterSection.svelte'

// Form
export { default as FormField } from './components/form/FormField.svelte'
export { default as HorizontalFormField } from './components/form/HorizontalFormField.svelte'
export { default as InputControl } from './components/form/InputControl.svelte'
export { default as Checkbox } from './components/form/Checkbox.svelte'
export { default as LabeledCheckbox } from './components/form/LabeledCheckbox.svelte'
9 changes: 7 additions & 2 deletions src/lib/utils/translation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/ban-types */

import { writable, type Readable, derived } from 'svelte/store'

import langs from '../localization/langs.json'
Expand All @@ -17,8 +19,11 @@ const langSections = ['common', 'footer', 'nav'] as const

export type LangSection = (typeof langSections)[number] | ADOFAIGG_UI.ExtraLangSections[number]
export type LangData = Record<string, FluentBundle>
// eslint-disable-next-line @typescript-eslint/ban-types
export type StringTranslationKey = `${LangSection}:` | (`${LangSection}:${string & {}}` & {})
export type StringTranslationKey =
| `${LangSection}:`
| (`${LangSection}:${string & {}}` & {})
| (`${string}:${string}` & {})

export type ArrayTranslationKey = [LangSection, string]
export type TranslationKey = StringTranslationKey | ArrayTranslationKey

Expand Down
18 changes: 18 additions & 0 deletions src/stories/form/Checkbox.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts" context="module">
import { Checkbox } from '$lib/index.js'
import { Story, Template } from '@storybook/addon-svelte-csf'
import type { Meta } from '@storybook/svelte'
export const meta: Meta = {
component: Checkbox,
title: 'Components/Form/Controls/Checkbox'
}
</script>

<Template let:args>
<Checkbox {...args} />
</Template>

<Story name="Default" args={{}} />

<Story name="Checked" args={{ checked: true }} />
22 changes: 22 additions & 0 deletions src/stories/form/FormField.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts" context="module">
import { FormField, InputControl } from '$lib/index.js'
import { Story, Template } from '@storybook/addon-svelte-csf'
import type { Meta } from '@storybook/svelte'
export const meta: Meta = {
component: FormField,
title: 'Components/Form/Form Field'
}
</script>

<Template let:args>
<FormField {...args}>
<InputControl placeholder="custom:Wow sans" />
</FormField>
</Template>

<Story name="Default" args={{}} />

<Story name="Labeled" args={{ label: 'custom:Label' }} />

<Story name="With Error" args={{ label: 'custom:Label', error: 'custom:Wow this is error' }} />
22 changes: 22 additions & 0 deletions src/stories/form/HorizontalFormField.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts" context="module">
import { HorizontalFormField, InputControl } from '$lib/index.js'
import { Story, Template } from '@storybook/addon-svelte-csf'
import type { Meta } from '@storybook/svelte'
export const meta: Meta = {
component: HorizontalFormField,
title: 'Components/Form/Horizontal Form Field'
}
</script>

<Template let:args>
<HorizontalFormField {...args}>
<InputControl placeholder="custom:Wow sans" />
</HorizontalFormField>
</Template>

<Story name="Default" args={{}} />

<Story name="Labeled" args={{ label: 'custom:Label' }} />

<Story name="With Error" args={{ label: 'custom:Label', error: 'custom:Wow this is error' }} />
18 changes: 18 additions & 0 deletions src/stories/form/InputControl.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts" context="module">
import { InputControl } from '$lib/index.js'
import { Story, Template } from '@storybook/addon-svelte-csf'
import type { Meta } from '@storybook/svelte'
export const meta: Meta = {
component: InputControl,
title: 'Components/Form/Controls/Input Control'
}
</script>

<Template let:args>
<InputControl {...args} />
</Template>

<Story name="Default" args={{}} />

<Story name="With Placeholder" args={{ placeholder: 'custom:Wow Placeholder' }} />
18 changes: 18 additions & 0 deletions src/stories/form/LabeledCheckbox.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script lang="ts" context="module">
import { LabeledCheckbox } from '$lib/index.js'
import { Story, Template } from '@storybook/addon-svelte-csf'
import type { Meta } from '@storybook/svelte'
export const meta: Meta = {
component: LabeledCheckbox,
title: 'Components/Form/Controls/Labeled Checkbox'
}
</script>

<Template let:args>
<LabeledCheckbox label="custom:wow this is label" {...args} />
</Template>

<Story name="Default" args={{}} />

<Story name="Checked" args={{ checked: true }} />

0 comments on commit 9598059

Please sign in to comment.