Build out components #45

Merged
BenjaminPalko merged 14 commits from build-out-components into master 2025-01-04 22:14:19 -05:00
28 changed files with 443 additions and 43 deletions

View file

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

View file

@ -1,61 +1,68 @@
<script lang="ts"> <script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types'; import type { DaisyColor, DaisySize } from '$lib/types';
piopi commented 2025-01-04 21:28:47 -05:00 (Migrated from github.com)
Review

Any reason or standard you had to change the onClick to onclick?

Any reason or standard you had to change the onClick to onclick?
BenjaminPalko commented 2025-01-04 21:34:02 -05:00 (Migrated from github.com)
Review

Yeah, onclick is the default prop name on elements in svelte, its not the same as with React where it follows JS camel case styling.

Yeah, onclick is the default prop name on elements in svelte, its not the same as with React where it follows JS camel case styling.
piopi commented 2025-01-04 21:34:54 -05:00 (Migrated from github.com)
Review

kk thanks, good to know

kk thanks, good to know
BenjaminPalko commented 2025-01-04 21:35:02 -05:00 (Migrated from github.com)
Review

actually, I think I'm gonig to redo the typing here, there is redundancy

actually, I think I'm gonig to redo the typing here, there is redundancy
import type { Snippet } from 'svelte'; import type { SvelteHTMLElements } from 'svelte/elements';
import type { HTMLButtonAttributes } from 'svelte/elements'; import { twMerge } from 'tailwind-merge';
interface Props { type Props = {
active?: boolean;
animation?: boolean;
block?: boolean; block?: boolean;
children: Snippet;
color?: DaisyColor; color?: DaisyColor;
full?: boolean; full?: boolean;
glass?: boolean; glass?: boolean;
outline?: boolean; shape?: 'circle' | 'square';
onClick?: () => void;
responsive?: boolean;
size?: DaisySize; size?: DaisySize;
type?: HTMLButtonAttributes['type']; variant?: 'link' | 'outline';
wide?: boolean; wide?: boolean;
} } & SvelteHTMLElements['button'];
let { let {
active = false,
animation = true,
block = false, block = false,
children, children,
class: className,
color, color,
disabled,
full = false, full = false,
glass = false, glass = false,
outline = false, shape,
onClick,
responsive = false,
size, size,
type = 'button',
wide = false, wide = false,
variant,
...props
}: Props = $props(); }: Props = $props();
</script> </script>
<button <button
{type} {...props}
onclick={onClick} {disabled}
class:btn-outline={outline} class={twMerge('btn', className)}
class:btn-active={active}
class:no-animation={!animation}
class:btn-block={block} 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-neutral={color === 'neutral'}
class:btn-primary={color === 'primary'} class:btn-primary={color === 'primary'}
class:btn-secondary={color === 'secondary'} class:btn-secondary={color === 'secondary'}
class:btn-accent={color === 'accent'} class:btn-accent={color === 'accent'}
class:btn-ghost={color === 'ghost'} class:btn-ghost={color === 'ghost'}
class:btn-link={color === 'link'}
class:btn-info={color === 'info'} class:btn-info={color === 'info'}
class:btn-success={color === 'success'} class:btn-success={color === 'success'}
class:btn-warning={color === 'warning'} class:btn-warning={color === 'warning'}
class:btn-error={color === 'error'} 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> </button>
<style></style> <style></style>

View file

@ -75,7 +75,6 @@
class:input-secondary={color === 'secondary'} class:input-secondary={color === 'secondary'}
class:input-accent={color === 'accent'} class:input-accent={color === 'accent'}
class:input-ghost={color === 'ghost'} class:input-ghost={color === 'ghost'}
class:input-link={color === 'link'}
class:input-info={color === 'info'} class:input-info={color === 'info'}
class:input-success={color === 'success'} class:input-success={color === 'success'}
class:input-warning={color === 'warning'} 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',
],
piopi commented 2025-01-04 21:30:42 -05:00 (Migrated from github.com)
Review

I see some of them also used in the alert and other place, maybe it could be worth it to put them in an array or enums as UI tokens

I see some of them also used in the alert and other place, maybe it could be worth it to put them in an array or enums as UI tokens
piopi commented 2025-01-04 21:31:06 -05:00 (Migrated from github.com)
Review

Same thing for the sizes tokens and anything you see used in common

Same thing for the sizes tokens and anything you see used in common
piopi commented 2025-01-04 21:33:21 -05:00 (Migrated from github.com)
Review

Just noticed my comment is on the stories file but I meant it to be on the component itself

Just noticed my comment is on the stories file but I meant it to be on the component itself
BenjaminPalko commented 2025-01-04 21:46:12 -05:00 (Migrated from github.com)
Review

It did occur to me, there can be variation between components though, some will have ghost, others might have neutral, or not. So I just haven't made a decision for it yet.

It did occur to me, there can be variation between components though, some will have ghost, others might have neutral, or not. So I just haven't made a decision for it yet.
},
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 Navbar from './Navbar';
import Tabs from './Tabs'; import Tabs from './Tabs';
export { default as Link } from './Link.svelte';
export { Navbar, Tabs }; 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 = export type DaisyColor =
| 'default'
| 'neutral' | 'neutral'
| 'primary' | 'primary'
| 'secondary' | 'secondary'
| 'accent' | 'accent'
| 'ghost' | 'ghost'
| 'link'
| 'info' | 'info'
| 'success' | 'success'
| 'warning' | 'warning'

View file

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

View file

@ -55,7 +55,7 @@
/> />
</div> </div>
<div class="card-actions justify-center px-8 pb-4"> <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> </div>
</form> </form>
</div> </div>