Build out components (#45)

* move loader to Feedback

* fill out button props to match what is available on daisy

* links

* Alert component and InfoIcon

* Loading component

* Progress component

* lol wtf

* Tooltip component

* Skeleton component

* Divider component

* fix errors

* made this component early so i just fixed up some of the props
This commit is contained in:
Baobeld 2025-01-04 22:14:19 -05:00 committed by GitHub
parent bff55596c6
commit 54d4eaa058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 443 additions and 43 deletions

View file

@ -8,9 +8,11 @@
title: 'Actions/Button',
component: Button,
args: {
onClick: fn(),
onclick: fn(),
},
argTypes: {
active: { control: 'boolean' },
animation: { control: 'boolean' },
block: { control: 'boolean' },
color: {
control: 'select',
@ -20,28 +22,25 @@
'secondary',
'accent',
'ghost',
'link',
'info',
'success',
'warning',
'error',
],
},
disabled: { control: 'boolean' },
full: { control: 'boolean' },
glass: { control: 'boolean' },
outline: {
control: 'boolean',
shape: {
control: 'select',
options: ['circle', 'square'],
},
responsive: { control: 'boolean' },
size: {
control: 'select',
options: ['Default', 'xs', 'sm', 'lg'],
options: ['xs', 'sm', '-', 'lg'],
defaultValue: 'Default',
},
type: {
control: 'select',
options: ['button', 'reset', 'submit'],
},
variant: { control: 'select', options: ['link', 'outline'] },
wide: { control: 'boolean' },
},
});

View file

@ -1,61 +1,68 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import type { Snippet } from 'svelte';
import type { HTMLButtonAttributes } from 'svelte/elements';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
interface Props {
type Props = {
active?: boolean;
animation?: boolean;
block?: boolean;
children: Snippet;
color?: DaisyColor;
full?: boolean;
glass?: boolean;
outline?: boolean;
onClick?: () => void;
responsive?: boolean;
shape?: 'circle' | 'square';
size?: DaisySize;
type?: HTMLButtonAttributes['type'];
variant?: 'link' | 'outline';
wide?: boolean;
}
} & SvelteHTMLElements['button'];
let {
active = false,
animation = true,
block = false,
children,
class: className,
color,
disabled,
full = false,
glass = false,
outline = false,
onClick,
responsive = false,
shape,
size,
type = 'button',
wide = false,
variant,
...props
}: Props = $props();
</script>
<button
{type}
onclick={onClick}
class:btn-outline={outline}
{...props}
{disabled}
class={twMerge('btn', className)}
class:btn-active={active}
class:no-animation={!animation}
class:btn-block={block}
class:btn-wide={wide}
class:w-full={full}
class:glass
class:btn-xs={size === 'xs'}
class:btn-sm={size === 'sm'}
class:btn-lg={size === 'lg'}
class:btn-neutral={color === 'neutral'}
class:btn-primary={color === 'primary'}
class:btn-secondary={color === 'secondary'}
class:btn-accent={color === 'accent'}
class:btn-ghost={color === 'ghost'}
class:btn-link={color === 'link'}
class:btn-info={color === 'info'}
class:btn-success={color === 'success'}
class:btn-warning={color === 'warning'}
class:btn-error={color === 'error'}
class={`btn ${responsive && 'btn-xs sm:btn-sm md:btn-md lg:btn-lg'}`}
class:btn-disabled={disabled}
class:w-full={full}
class:glass
class:btn-circle={shape === 'circle'}
class:btn-square={shape === 'square'}
class:btn-xs={size === 'xs'}
class:btn-sm={size === 'sm'}
class:btn-lg={size === 'lg'}
class:btn-link={variant === 'link'}
class:btn-outline={variant === 'outline'}
class:btn-wide={wide}
>
{@render children()}
{@render children?.()}
</button>
<style></style>

View file

@ -75,7 +75,6 @@
class:input-secondary={color === 'secondary'}
class:input-accent={color === 'accent'}
class:input-ghost={color === 'ghost'}
class:input-link={color === 'link'}
class:input-info={color === 'info'}
class:input-success={color === 'success'}
class:input-warning={color === 'warning'}

View file

@ -0,0 +1,25 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Alert from './Alert.svelte';
import type { ComponentProps } from 'svelte';
import { InfoIcon } from '../Icons';
const { Story } = defineMeta({
title: 'Feedback/Alert',
component: Alert,
argTypes: {
status: { control: 'select', options: ['info', 'success', 'warning', 'error'] },
},
});
</script>
{#snippet template(props: ComponentProps<typeof Alert>)}
<Alert {...props}>
{#snippet icon()}
<InfoIcon class="h-6 w-6 shrink-0 stroke-info" />
{/snippet}
<span>Hello world!</span>
</Alert>
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,24 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import type { Snippet } from 'svelte';
type Props = {
children?: Snippet;
icon?: Snippet;
status?: Extract<DaisyColor, 'info' | 'success' | 'warning' | 'error'>;
};
let { children, icon, status: color }: Props = $props();
</script>
<div
role="alert"
class="alert"
class:alert-info={color === 'info'}
class:alert-success={color === 'success'}
class:alert-warning={color === 'warning'}
class:alert-error={color === 'error'}
>
{@render icon?.()}
{@render children?.()}
</div>

View file

@ -0,0 +1,40 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Loading from './Loading.svelte';
const { Story } = defineMeta({
title: 'Feedback/Loading',
component: Loading,
argTypes: {
color: {
control: 'select',
options: [
'neutral',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
size: { control: 'select', options: ['xs', 'sm', 'md', 'lg'] },
variant: {
control: 'select',
options: ['spinner', 'dots', 'ring', 'ball', 'bars', 'infinity'],
},
},
});
</script>
{#snippet template(props: ComponentProps<typeof Loading>)}
<Loading {...props} />
{/snippet}
<Story
name="Default"
args={{ color: 'neutral', size: 'md', variant: 'spinner' }}
children={template}
/>

View file

@ -0,0 +1,31 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
type Props = {
color?: Exclude<DaisyColor, 'ghost'>;
size?: DaisySize | 'md';
variant?: 'spinner' | 'dots' | 'ring' | 'ball' | 'bars' | 'infinity';
};
let { color, size = 'md', variant = 'spinner' }: Props = $props();
</script>
<span
class="loading"
class:text-primary={color === 'primary'}
class:text-secondary={color === 'secondary'}
class:text-accent={color === 'accent'}
class:text-info={color === 'info'}
class:text-success={color === 'success'}
class:text-warning={color === 'warning'}
class:text-error={color === 'error'}
class:loading-xs={size === 'xs'}
class:loading-sm={size === 'sm'}
class:loading-md={size === 'md'}
class:loading-lg={size === 'lg'}
class:loading-spinner={variant === 'spinner'}
class:loading-dots={variant === 'dots'}
class:loading-ring={variant === 'ring'}
class:loading-ball={variant === 'ball'}
class:loading-bars={variant === 'bars'}
class:loading-infinity={variant === 'infinity'}
></span>

View file

@ -0,0 +1,24 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Progress from './Progress.svelte';
const { Story } = defineMeta({
title: 'Feedback/Progress',
component: Progress,
argTypes: {
color: {
control: 'select',
options: ['primary', 'secondary', 'accent', 'info', 'success', 'warning', 'error'],
},
value: { control: 'number' },
max: { control: 'number' },
},
});
</script>
{#snippet template(props: ComponentProps<typeof Progress>)}
<Progress {...props} />
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,24 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
color?: Exclude<DaisyColor, 'neutral' | 'ghost'>;
} & SvelteHTMLElements['progress'];
let { children, class: className, color, ...props }: Props = $props();
</script>
<progress
class={twMerge(className, 'progress')}
class:progress-primary={color === 'primary'}
class:progress-secondary={color === 'secondary'}
class:progress-accent={color === 'accent'}
class:progress-info={color === 'info'}
class:progress-success={color === 'success'}
class:progress-warning={color === 'warning'}
class:progress-error={color === 'error'}
{...props}
>
{@render children?.()}
</progress>

View file

@ -0,0 +1,18 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Skeleton from './Skeleton.svelte';
import type { ComponentProps } from 'svelte';
const { Story } = defineMeta({
title: 'Feedback/Skeleton',
component: Skeleton,
});
</script>
{#snippet template(props: ComponentProps<typeof Skeleton>)}
<Skeleton {...props} />
{/snippet}
<Story name="Default" args={{ class: 'h-32 w-32' }} children={template} />
<Story name="Circle" args={{ class: 'h-32 w-32 rounded-full' }} children={template} />

View file

@ -0,0 +1,12 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
children?: Snippet;
} & SvelteHTMLElements['div'];
let { children, class: className, ...props }: Props = $props();
</script>
<div {...props} class={twMerge('skeleton', className)}>{@render children?.()}</div>

View file

@ -0,0 +1,27 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Tooltip from './Tooltip.svelte';
import { Button } from '../Actions';
import type { ComponentProps } from 'svelte';
const { Story } = defineMeta({
title: 'Feedback/Tooltip',
component: Tooltip,
argTypes: {
color: {
control: 'select',
options: ['primary', 'secondary', 'accent', 'info', 'success', 'warning', 'error'],
},
open: { control: 'boolean' },
position: { control: 'select', options: ['top', 'bottom', 'left', 'right'] },
},
});
</script>
{#snippet template(props: ComponentProps<typeof Tooltip>)}
<Tooltip {...props}>
<Button color="primary">Button</Button>
</Tooltip>
{/snippet}
<Story name="Default" args={{ tip: "It's a button" }} children={template} />

View file

@ -0,0 +1,33 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
color?: Exclude<DaisyColor, 'neutral' | 'ghost'>;
open?: boolean;
position?: 'top' | 'bottom' | 'left' | 'right';
tip?: string;
} & SvelteHTMLElements['div'];
let { children, class: className, color, open, position, tip, ...props }: Props = $props();
</script>
<div
class={twMerge('tooltip', className)}
class:tooltip-primary={color === 'primary'}
class:tooltip-secondary={color === 'secondary'}
class:tooltip-accent={color === 'accent'}
class:tooltip-info={color === 'info'}
class:tooltip-success={color === 'success'}
class:tooltip-warning={color === 'warning'}
class:tooltip-error={color === 'error'}
class:tooltip-open={open}
class:tooltip-top={position === 'top'}
class:tooltip-bottom={position === 'bottom'}
class:tooltip-left={position === 'left'}
class:tooltip-right={position === 'right'}
data-tip={tip}
{...props}
>
{@render children?.()}
</div>

View file

@ -0,0 +1,6 @@
export { default as Alert } from './Alert.svelte';
export { default as Loader } from './Loader.svelte';
export { default as Loading } from './Loading.svelte';
export { default as Progress } from './Progress.svelte';
export { default as Skeleton } from './Skeleton.svelte';
export { default as Tooltip } from './Tooltip.svelte';

View file

@ -0,0 +1,8 @@
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
let { class: className, ...props }: SvelteHTMLElements['i'] = $props();
</script>
<i {...props} class={twMerge(className, 'fi fi-sr-info')}></i>

View file

@ -0,0 +1 @@
export { default as InfoIcon } from './InfoIcon.svelte';

View file

@ -0,0 +1,41 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Divider from './Divider.svelte';
import type { ComponentProps } from 'svelte';
const { Story } = defineMeta({
title: 'Layout/Divider',
component: Divider,
argTypes: {
color: {
control: 'select',
options: [
'neutral',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
direction: { control: 'select', options: ['horizontal', 'vertical'] },
variant: { control: 'select', options: ['start', 'end'] },
},
});
</script>
{#snippet template(props: ComponentProps<typeof Divider>)}
<div
class="flex"
class:flex-row={props.direction === 'horizontal'}
class:flex-col={props.direction === 'vertical'}
>
<span>Side A</span>
<Divider {...props} />
<span>Side B</span>
</div>
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,31 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
color?: Exclude<DaisyColor, 'ghost'>;
direction?: 'horizontal' | 'vertical';
variant?: 'start' | 'end';
} & SvelteHTMLElements['div'];
let { children, class: className, color, direction, variant, ...props }: Props = $props();
</script>
<div
class={twMerge('divider', className)}
class:divider-neutral={color === 'neutral'}
class:divider-primary={color === 'primary'}
class:divider-secondary={color === 'secondary'}
class:divider-accent={color === 'accent'}
class:divider-info={color === 'info'}
class:divider-success={color === 'success'}
class:divider-warning={color === 'warning'}
class:divider-error={color === 'error'}
class:divider-horizontal={direction === 'horizontal'}
class:divider-vertical={direction === 'vertical'}
class:divider-start={variant === 'start'}
class:divider-end={variant === 'end'}
{...props}
>
{@render children?.()}
</div>

View file

View file

@ -0,0 +1,31 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Link from './Link.svelte';
const { Story } = defineMeta({
title: 'Navigation/Link',
component: Link,
argTypes: {
color: {
control: 'select',
options: [
'primary',
'secondary',
'accent',
'neutral',
'info',
'success',
'warning',
'error',
],
},
},
});
</script>
{#snippet template(props: ComponentProps<typeof Link>)}
<Link {...props}>Hello world!</Link>
{/snippet}
<Story name="Default" children={template} />

View file

@ -0,0 +1,23 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = { color?: DaisyColor; hover?: boolean } & SvelteHTMLElements['a'];
let { children, class: className, color, hover, ...props }: Props = $props();
</script>
<a
class={twMerge(className, 'link')}
class:link-primary={color === 'primary'}
class:link-secondary={color === 'secondary'}
class:link-accent={color === 'accent'}
class:link-neutral={color === 'neutral'}
class:link-info={color === 'info'}
class:link-success={color === 'success'}
class:link-warning={color === 'warning'}
class:link-error={color === 'error'}
class:link-hover={hover}
{...props}>{@render children?.()}</a
>

View file

@ -1,4 +1,5 @@
import Navbar from './Navbar';
import Tabs from './Tabs';
export { default as Link } from './Link.svelte';
export { Navbar, Tabs };

View file

@ -1,3 +0,0 @@
import Loader from './Loader.svelte';
export default Loader;

View file

@ -1,11 +1,9 @@
export type DaisyColor =
| 'default'
| 'neutral'
| 'primary'
| 'secondary'
| 'accent'
| 'ghost'
| 'link'
| 'info'
| 'success'
| 'warning'

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import Loader from '$lib/components/common/Loader';
import { Loader } from '$lib/components/Feedback';
import { fade } from 'svelte/transition';
$effect(() => {

View file

@ -55,7 +55,7 @@
/>
</div>
<div class="card-actions justify-center px-8 pb-4">
<Button outline type="submit" full>{messages.sms_button_submit()}</Button>
<Button type="submit" variant="outline" full>{messages.sms_button_submit()}</Button>
</div>
</form>
</div>