-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from ryu-man/feat/feaild-comp
feat: define new component `Field`
- Loading branch information
Showing
12 changed files
with
333 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './sharedContext' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { getContext, hasContext, setContext } from 'svelte'; | ||
import { type Readable, type Writable, derived, get, readonly, writable } from 'svelte/store'; | ||
|
||
export const SVELTE_FUI_SHARED_CONTEXT_KEY = 'svelte-fui-shared-context-key'; | ||
|
||
export type SharedContext<T> = Record<string, Record<string, unknown>> & T; | ||
|
||
export function getSharedContext<T>(key?: 'input' | 'label'): Readable<SharedContext<T>> { | ||
const context$ = getContext(SVELTE_FUI_SHARED_CONTEXT_KEY) as Writable<SharedContext<T>>; | ||
|
||
if (!context$) { | ||
return readonly(writable({} as SharedContext<T>)); | ||
} | ||
|
||
if (key) { | ||
return derived(context$, (val) => val[key] as SharedContext<T>); | ||
} | ||
|
||
return readonly(context$); | ||
} | ||
|
||
export function setSharedContext<T>(context: SharedContext<T>): Writable<SharedContext<T>> { | ||
if (hasContext(SVELTE_FUI_SHARED_CONTEXT_KEY)) { | ||
// extends and overrides the existent context | ||
const existedContext = get(getSharedContext()); | ||
|
||
setContext(SVELTE_FUI_SHARED_CONTEXT_KEY, writable(structuredClone({ ...existedContext, ...context }))); | ||
} | ||
|
||
return setContext(SVELTE_FUI_SHARED_CONTEXT_KEY, writable(structuredClone(context))); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<script lang="ts"> | ||
import { Meta, Story } from '@storybook/addon-svelte-csf'; | ||
import type { ArgTypes } from '@storybook/svelte'; | ||
import { App, Input } from '@svelte-fui/core'; | ||
import { AddCircleFilled } from '@svelte-fui/icons'; | ||
import { webDarkTheme, webLightTheme } from '@svelte-fui/themes'; | ||
import { onMount } from 'svelte'; | ||
import Field from './Field.svelte'; | ||
import ValidationMessage from './ValidationMessage.svelte'; | ||
const defaultValues = { | ||
size: 'md', | ||
orientation: 'vertical', | ||
}; | ||
const argTypes = { | ||
label: { | ||
type: 'string' | ||
}, | ||
size: { | ||
type: 'string', | ||
options: ['sm', 'md', 'lg'], | ||
control: 'select' | ||
}, | ||
orientation: { | ||
type: 'string', | ||
options: ['horizontal', 'vertical'], | ||
control: 'select' | ||
}, | ||
state: { | ||
type: 'string', | ||
options: ['none', 'success', 'warning', 'error'], | ||
control: 'select' | ||
} | ||
} satisfies ArgTypes; | ||
let theme = webLightTheme; | ||
onMount(() => { | ||
function handler(schemeMedia: MediaQueryListEvent) { | ||
theme = schemeMedia.matches ? webLightTheme : webDarkTheme; | ||
} | ||
const schemeMedia = matchMedia('(prefers-color-scheme: light)'); | ||
schemeMedia.addEventListener('change', handler); | ||
theme = schemeMedia.matches ? webLightTheme : webDarkTheme; | ||
return () => { | ||
schemeMedia.removeEventListener('change', handler); | ||
}; | ||
}); | ||
</script> | ||
|
||
<Meta title="Components/Field" component={Field} {argTypes} /> | ||
|
||
<Story id="field" name="Field" args={defaultValues} let:args> | ||
<App {theme}> | ||
<div class="flex h-full w-full flex-col items-center justify-center gap-4"> | ||
<div class="flex w-[90%] flex-col gap-4"> | ||
<Field {...args} label="Example Field" state="error"> | ||
<Input /> | ||
<ValidationMessage>This an error message</ValidationMessage> | ||
</Field> | ||
|
||
<Field {...args} label="Example Field" state="warning"> | ||
<Input /> | ||
<ValidationMessage>This a warning message</ValidationMessage> | ||
</Field> | ||
|
||
<Field {...args} label="Example Field" state="success"> | ||
<Input /> | ||
<ValidationMessage>This a success message</ValidationMessage> | ||
</Field> | ||
|
||
<Field {...args} label="Example Field" state="none"> | ||
<Input /> | ||
<ValidationMessage>This a simple message</ValidationMessage> | ||
</Field> | ||
<Field {...args} label="Example Field" state="none"> | ||
<Input /> | ||
<ValidationMessage icon={AddCircleFilled}>This a simple message</ValidationMessage> | ||
</Field> | ||
</div> | ||
</div> | ||
</App> | ||
</Story> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<script lang="ts"> | ||
import { CheckmarkCircleFilled, ErrorCircleFilled, WarningFilled } from '@svelte-fui/icons'; | ||
import { setSharedContext } from '../context'; | ||
import { classnames } from '../internal'; | ||
import { Label } from '../label'; | ||
import ValidationMessage from './ValidationMessage.svelte'; | ||
import { setFieldContext } from './context'; | ||
import type { State } from './types,'; | ||
export let label: string = ''; | ||
export let orientation: 'horizontal' | 'vertical' = 'vertical'; | ||
export let size: 'sm' | 'md' | 'lg' = 'md'; | ||
export let validationMessage = ''; | ||
export let state: State = validationMessage ? 'error' : 'none'; | ||
const validationMessageIcons = { | ||
error: ErrorCircleFilled, | ||
warning: WarningFilled, | ||
success: CheckmarkCircleFilled, | ||
none: undefined | ||
}; | ||
const sharedContext$ = setSharedContext({ input: { invalid: state === 'error', size }, label: { size } }); | ||
$: sharedContext$.set({ input: { invalid: state === 'error', size }, label: { size } }); | ||
const { icon$, state$ } = setFieldContext({ state, icon: validationMessageIcons[state || 'none'] }); | ||
$: icon$.set(validationMessageIcons[state || 'none']); | ||
$: state$.set(state); | ||
</script> | ||
|
||
<div class={classnames('fui-field', orientation, state, size, { 'no-label': !label })}> | ||
<Label>{label}</Label> | ||
<slot /> | ||
</div> | ||
|
||
<style lang="postcss"> | ||
.fui-field { | ||
display: grid; | ||
&.horizontal { | ||
grid-template-columns: 33% 1fr; | ||
grid-template-rows: auto auto auto 1fr; | ||
&.no-label { | ||
padding-left: 33%; | ||
grid-template-columns: 1fr; | ||
} | ||
} | ||
} | ||
.fui-field :global(.fui-label) { | ||
@apply py-xxs; | ||
} | ||
.fui-field :global(.fui-label.lg) { | ||
@apply py-[1px]; | ||
} | ||
.fui-field.vertical :global(.fui-label) { | ||
@apply mb-xxs; | ||
} | ||
.fui-field.vertical :global(.fui-label.lg) { | ||
@apply pb-xs; | ||
} | ||
.fui-field.horizontal :global(.fui-label) { | ||
@apply mr-m; | ||
grid-row-start: 1; | ||
grid-row-end: -1; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<script lang="ts"> | ||
import type { ComponentType } from 'svelte/internal'; | ||
import { Icon } from '../icon'; | ||
import { classnames } from '../internal'; | ||
import { getFieldContext } from './context'; | ||
const { state$, icon$ } = getFieldContext(); | ||
export let icon: ComponentType | undefined = $icon$; | ||
export let id: string | undefined = undefined; | ||
let klass = ''; | ||
export { klass as class }; | ||
let iconSize = '12px'; | ||
</script> | ||
|
||
<div | ||
{id} | ||
class={classnames('fui-field-validation-message', $state$, { icon: !!icon, 'secondary-text': !!$$slots.default }, klass)} | ||
style:--icon-size={iconSize} | ||
> | ||
{#if icon} | ||
<!-- content here --> | ||
<Icon class={classnames('fui-field-validation-message-icon', $state$)} src={icon} size={iconSize} ariaHidden /> | ||
{/if} | ||
<slot><!-- optional fallback --></slot> | ||
</div> | ||
|
||
<style lang="postcss"> | ||
.fui-field-validation-message { | ||
@apply font-regular text-base-200 flex items-center text-left; | ||
} | ||
.fui-field-validation-message.secondary-text { | ||
@apply mt-xxs text-neutral-foreground-3 caption-1; | ||
&.error { | ||
@apply text-palette-red-foreground-1; | ||
} | ||
&.icon { | ||
/* Add a gutter for the icon, to allow multiple lines of text to line up to the right of the icon. */ | ||
padding-left: calc(var(--icon-size) + theme(spacing.xs)); | ||
} | ||
} | ||
.fui-field-validation-message :global(.fui-field-validation-message-icon) { | ||
@apply inline-block; | ||
font-size: var(--icon-size); | ||
margin-left: calc(-1 * var(--icon-size) - theme(spacing.xs)); | ||
margin-right: theme(spacing.xs); | ||
line-height: 0; | ||
vertical-align: -1px; | ||
} | ||
.fui-field-validation-message :global(.fui-field-validation-message-icon.error) { | ||
@apply !text-palette-red-foreground-1; | ||
} | ||
.fui-field-validation-message :global(.fui-field-validation-message-icon.warning) { | ||
@apply !text-palette-dark-orange-foreground-1; | ||
} | ||
.fui-field-validation-message :global(.fui-field-validation-message-icon.success) { | ||
@apply !text-palette-green-foreground-1; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { getContext, setContext } from 'svelte'; | ||
import type { ComponentType } from 'svelte/internal'; | ||
import { type Writable, writable } from 'svelte/store'; | ||
import type { State } from './types,'; | ||
|
||
export const SVELTE_FUI_FIELD_CONTEXT_KEY = 'svelte-fui-field-context-key'; | ||
|
||
export type FieldContext = { | ||
state$: Writable<State>; | ||
icon$: Writable<ComponentType | undefined>; | ||
}; | ||
|
||
export function getFieldContext(): FieldContext { | ||
return getContext(SVELTE_FUI_FIELD_CONTEXT_KEY); | ||
} | ||
|
||
export function setFieldContext(values: { state: State; icon: ComponentType | undefined }) { | ||
const context: FieldContext = { | ||
state$: writable(values.state), | ||
icon$: writable(values.icon) | ||
}; | ||
|
||
return setContext(SVELTE_FUI_FIELD_CONTEXT_KEY, context); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as Field } from './Field.svelte'; | ||
export { default as ValidationMessage } from './ValidationMessage.svelte' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type State = 'none' | 'error' | 'warning' | 'success' | undefined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
a85069b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
svelte-fui – ./
svelte-fui-ryu-man.vercel.app
svelte-fui.vercel.app
svelte-fui-git-main-ryu-man.vercel.app