add package

This commit is contained in:
Benjamin Palko 2025-03-07 13:58:04 -05:00
parent 000eeaaa31
commit 6eb5998526
55 changed files with 1538 additions and 0 deletions

View file

@ -0,0 +1,53 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { fn } from '@storybook/test';
import type { ComponentProps } from 'svelte';
import Button from './Button.svelte';
const { Story } = defineMeta({
title: 'Actions/Button',
component: Button,
args: {
onclick: fn(),
},
argTypes: {
active: { control: 'boolean' },
animation: { control: 'boolean' },
block: { control: 'boolean' },
color: {
control: 'select',
options: [
'neutral',
'primary',
'secondary',
'accent',
'ghost',
'info',
'success',
'warning',
'error',
],
},
disabled: { control: 'boolean' },
full: { control: 'boolean' },
glass: { control: 'boolean' },
shape: {
control: 'select',
options: ['circle', 'square'],
},
size: {
control: 'select',
options: ['xs', 'sm', '-', 'lg'],
defaultValue: 'Default',
},
variant: { control: 'select', options: ['link', 'outline'] },
wide: { control: 'boolean' },
},
});
</script>
{#snippet template({ children: _, ...props }: Partial<ComponentProps<typeof Button>>)}
<Button {...props}>Button</Button>
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,67 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
active?: boolean;
animation?: boolean;
block?: boolean;
color?: DaisyColor;
full?: boolean;
glass?: boolean;
shape?: 'circle' | 'square';
size?: DaisySize;
variant?: 'link' | 'outline';
wide?: boolean;
} & SvelteHTMLElements['button'];
let {
active = false,
animation = true,
block = false,
children,
class: className,
color,
full = false,
glass = false,
shape,
size,
wide = false,
variant,
...props
}: Props = $props();
</script>
<button
{...props}
class={twMerge('btn', clsx(className))}
class:btn-active={active}
class:no-animation={!animation}
class:btn-block={block}
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-info={color === 'info'}
class:btn-success={color === 'success'}
class:btn-warning={color === 'warning'}
class:btn-error={color === 'error'}
class:btn-disabled={props.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?.()}
</button>
<style></style>

View file

@ -0,0 +1,36 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Modal from './Modal.svelte';
import ModalBody from './ModalBody.svelte';
import ModalActions from './ModalActions.svelte';
import Button from '../Button.svelte';
const { Story } = defineMeta({
title: 'Actions/Modal',
component: Modal,
argTypes: {
open: {
control: 'boolean',
defaultValue: false,
},
},
});
let dialog: HTMLDialogElement | undefined = $state(undefined);
</script>
{#snippet template({ children: _, ...props }: Partial<ComponentProps<typeof Modal>>)}
<Button onclick={() => dialog?.showModal()}>Open</Button>
<Modal {...props} backdrop bind:dialog>
<ModalBody>
<h3 class="text-lg font-bold">Hello!</h3>
<p class="py-4">Press ESC key or click the button below to close</p>
<ModalActions>
<Button onclick={() => dialog?.close()}>Close</Button>
</ModalActions>
</ModalBody>
</Modal>
{/snippet}
<Story name="Default" children={template} />

View file

@ -0,0 +1,21 @@
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['dialog'] & {
backdrop?: boolean;
dialog?: HTMLDialogElement;
};
let { backdrop, dialog = $bindable(), children, class: className, ...props }: Props = $props();
</script>
<dialog {...props} class={twMerge('modal', clsx(className))} bind:this={dialog}>
{@render children?.()}
{#if backdrop}
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
{/if}
</dialog>

View file

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

View file

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

View file

@ -0,0 +1,3 @@
export { default as Modal } from './Modal.svelte';
export { default as ModalActions } from './ModalActions.svelte';
export { default as ModalBody } from './ModalBody.svelte';

View file

@ -0,0 +1,2 @@
export { default as Button } from './Button.svelte';
export * from './Modal/';

View file

@ -0,0 +1,63 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import TextInput from './TextInput.svelte';
import { User } from 'lucide-svelte';
const { Story } = defineMeta({
title: 'Data Input/Text Input',
component: TextInput,
argTypes: {
bordered: {
control: 'boolean',
},
color: {
control: 'select',
options: [
'ghost',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
disabled: {
control: 'boolean',
},
error: {
control: 'text',
},
fade: { control: 'boolean' },
start: { control: 'text' },
end: { control: 'text' },
label: { control: 'text' },
placeholder: { control: 'text' },
size: {
control: 'select',
options: ['xs', 'sm', '-', 'lg'],
},
type: {
control: 'select',
options: ['email', 'password', 'search', 'tel', 'text', 'url'],
},
},
});
</script>
<Story name="Text Label" args={{ color: 'primary', name: 'text', start: 'Text' }} />
<Story name="Icon Start">
<TextInput name="text" color="secondary">
{#snippet start()}
<User />
{/snippet}
</TextInput>
</Story>
<Story name="Icon End">
<TextInput name="text" color="secondary">
{#snippet end()}
<User />
{/snippet}
</TextInput>
</Story>

View file

@ -0,0 +1,91 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { HTMLInputTypeAttribute, SvelteHTMLElements } from 'svelte/elements';
import { fade as fadeTransition } from 'svelte/transition';
import { twMerge } from 'tailwind-merge';
type Props = {
bordered?: boolean;
color?: Exclude<DaisyColor, 'neutral'>;
error?: string | Snippet;
fade?: boolean;
start?: string | Snippet;
end?: string | Snippet;
label?: string | Snippet;
size?: DaisySize;
type?: Extract<
HTMLInputTypeAttribute,
'email' | 'password' | 'search' | 'tel' | 'text' | 'url'
>;
} & Omit<SvelteHTMLElements['input'], 'type'>;
let {
bordered = false,
class: className,
color,
error,
fade,
start,
end,
label,
size,
...props
}: Props = $props();
</script>
<label class="form-control w-full" transition:fadeTransition={{ duration: fade ? 200 : 0 }}>
<div class="label">
<span
class="label-text flex items-center gap-2"
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' || error}
>
{#if typeof label === 'string'}
{label}
{:else if label}
{@render label()}
{/if}
</span>
<span class="label-text-alt font-semibold text-error">
{#if typeof error === 'string'}
{error}
{:else if error}
{@render error()}
{/if}
</span>
</div>
<div
class="input flex w-full items-center gap-2"
class:input-bordered={bordered}
class:input-xs={size === 'xs'}
class:input-sm={size === 'sm'}
class:input-lg={size === 'lg'}
class:input-primary={color === 'primary'}
class:input-secondary={color === 'secondary'}
class:input-accent={color === 'accent'}
class:input-ghost={color === 'ghost'}
class:input-info={color === 'info'}
class:input-success={color === 'success'}
class:input-warning={color === 'warning'}
class:input-error={color === 'error' || error}
>
{#if typeof start === 'string'}
{start}
{:else}
{@render start?.()}
{/if}
<input {...props} class={twMerge('grow', clsx(className))} />
{#if typeof end === 'string'}
{end}
{:else}
{@render end?.()}
{/if}
</div>
</label>

View file

@ -0,0 +1,51 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Textarea from './Textarea.svelte';
import type { ComponentProps } from 'svelte';
const { Story } = defineMeta({
title: 'Data Input/Textarea',
component: Textarea,
argTypes: {
bordered: {
control: 'boolean',
},
color: {
control: 'select',
options: [
'default',
'ghost',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
disabled: {
control: 'boolean',
},
error: {
control: 'text',
},
label: {
control: 'text',
},
placeholder: {
control: 'text',
},
size: {
control: 'select',
options: ['xs', 'sm', '-', 'lg'],
},
},
});
</script>
{#snippet template(props: ComponentProps<typeof Textarea>)}
<Textarea {...props} />
{/snippet}
<Story name="Default" args={{ label: 'Label' }} children={template} />

View file

@ -0,0 +1,74 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
bordered?: boolean;
color?: Omit<DaisyColor, 'neutral'>;
error?: string | Snippet;
label?: string | Snippet;
resizable?: boolean | 'yes' | 'no' | 'x' | 'y';
size?: DaisySize | 'md';
} & SvelteHTMLElements['textarea'];
let {
bordered,
class: className,
color,
error,
label,
resizable,
size,
...props
}: Props = $props();
</script>
<label class="form-control w-full">
<div class="label">
<span
class="label-text flex items-center gap-2"
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' || error}
>
{#if typeof label === 'string'}
{label}
{:else if label}
{@render label()}
{/if}
</span>
<span class="label-text-alt font-semibold text-error">
{#if typeof error === 'string'}
{error}
{:else if error}
{@render error()}
{/if}
</span>
</div>
<textarea
{...props}
class={twMerge('textarea', clsx(className))}
class:textarea-bordered={bordered}
class:textarea-xs={size === 'xs'}
class:textarea-sm={size === 'sm'}
class:textarea-lg={size === 'lg'}
class:textarea-ghost={color === 'ghost'}
class:textarea-primary={color === 'primary'}
class:textarea-secondary={color === 'secondary'}
class:textarea-accent={color === 'accent'}
class:textarea-info={color === 'info'}
class:textarea-success={color === 'success'}
class:textarea-warning={color === 'warning'}
class:textarea-error={color === 'error' || error}
class:resize={resizable === true || resizable === 'yes'}
class:resize-x={resizable === 'x'}
class:resize-y={resizable === 'y'}
class:resize-none={resizable === false || resizable === 'no'}
></textarea>
</label>

View file

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

View file

@ -0,0 +1,51 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Avatar from './Avatar.svelte';
const { Story } = defineMeta({
title: 'Data display/Avatar',
component: Avatar,
argTypes: {
img: {
control: 'text',
},
placeholder: {
control: 'text',
defaultValue: 'DP',
},
presence: {
control: 'select',
options: [undefined, 'online', 'offline'],
},
ring: {
control: 'select',
options: [
undefined,
'neutral',
'primary',
'secondary',
'accent',
'info',
'success',
'warning',
'error',
],
},
shape: {
control: 'select',
options: ['square', 'circle'],
},
size: {
control: 'select',
options: ['xs', 'sm', 'md', 'lg'],
},
},
});
</script>
<Story
name="Default"
args={{
img: 'https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp',
}}
/>

View file

@ -0,0 +1,62 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
img?: string;
placeholder: string;
presence?: 'online' | 'offline';
ring?: Exclude<DaisyColor, 'ghost'>;
shape?: 'square' | 'circle';
size?: DaisySize | 'md';
} & Omit<SvelteHTMLElements['div'], 'children'>;
let {
class: className,
img,
placeholder,
presence,
ring,
shape = 'circle',
size = 'md',
...props
}: Props = $props();
</script>
<div {...props} class={twMerge('avatar', presence, clsx(className))} class:placeholder>
<div
class={twMerge(
'rounded-xl',
clsx({
'rounded-xl': shape === 'square',
'rounded-full': shape === 'circle',
'w-12': size === 'xs',
'w-16': size === 'sm',
'w-20': size === 'md',
'w-32': size === 'lg',
'avatar-ring ring ring-offset-2 ring-offset-base-100': !!ring,
})
)}
class:bg-neutral={placeholder}
class:ring-neutral={ring === 'neutral'}
class:ring-primary={ring === 'primary'}
class:ring-secondary={ring === 'secondary'}
class:ring-accent={ring === 'accent'}
class:ring-info={ring === 'info'}
class:ring-success={ring === 'success'}
class:ring-warning={ring === 'warning'}
class:ring-error={ring === 'error'}
>
{#if img}
<img src={img} alt={placeholder} />
{:else}
<span
class:text-3xl={size === 'lg'}
class:text-xl={size === 'md'}
class:text-xs={size === 'xs'}>{placeholder}</span
>
{/if}
</div>
</div>

View file

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

View file

@ -0,0 +1,25 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import { Info } from 'lucide-svelte';
import type { ComponentProps } from 'svelte';
import Alert from './Alert.svelte';
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()}
<Info />
{/snippet}
<span>Hello world!</span>
</Alert>
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,28 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import clsx from 'clsx';
import type { Snippet } from 'svelte';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
children?: Snippet;
icon?: Snippet;
status?: Extract<DaisyColor, 'info' | 'success' | 'warning' | 'error'>;
} & SvelteHTMLElements['div'];
let { children, class: className, icon, status: color, ...props }: Props = $props();
</script>
<div
{...props}
role="alert"
class={twMerge('alert', clsx(className))}
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,11 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Loader from './Loader.svelte';
const { Story } = defineMeta({
title: 'Feedback/Loader',
component: Loader,
});
</script>
<Story name="Default" args={{}} />

View file

@ -0,0 +1,51 @@
<script lang="ts"></script>
<span class="loader"></span>
<style>
.loader {
width: 48px;
height: 48px;
border: 3px dotted #fff;
border-style: solid solid dotted dotted;
border-radius: 50%;
display: inline-block;
position: relative;
box-sizing: border-box;
animation: rotation 2s linear infinite;
}
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
border: 3px dotted #ff3d00;
border-style: solid solid dotted;
width: 24px;
height: 24px;
border-radius: 50%;
animation: rotationBack 1s linear infinite;
transform-origin: center center;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes rotationBack {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-360deg);
}
}
</style>

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,34 @@
<script lang="ts">
import type { DaisyColor, DaisySize } from '$lib/types';
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = {
color?: Exclude<DaisyColor, 'ghost'>;
size?: DaisySize | 'md';
variant?: 'spinner' | 'dots' | 'ring' | 'ball' | 'bars' | 'infinity';
} & Pick<SvelteHTMLElements['span'], 'class'>;
let { class: className, color, size = 'md', variant = 'spinner' }: Props = $props();
</script>
<span
class={twMerge('loading', clsx(className))}
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,25 @@
<script lang="ts">
import clsx from 'clsx';
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
{...props}
class={twMerge('progress', clsx(className))}
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'}
>
{@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,10 @@
<script lang="ts">
import clsx from 'clsx';
import type { SvelteHTMLElements } from 'svelte/elements';
import { twMerge } from 'tailwind-merge';
type Props = SvelteHTMLElements['div'];
let { children, class: className, ...props }: Props = $props();
</script>
<div {...props} class={twMerge('skeleton', clsx(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,34 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import clsx from 'clsx';
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
{...props}
class={twMerge('tooltip', clsx(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}
>
{@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,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,32 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import clsx from 'clsx';
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
{...props}
class={twMerge('divider', clsx(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'}
>
{@render children?.()}
</div>

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,24 @@
<script lang="ts">
import type { DaisyColor } from '$lib/types';
import clsx from 'clsx';
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
{...props}
class={twMerge('link', clsx(className))}
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}>{@render children?.()}</a
>

View file

@ -0,0 +1,27 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Navbar from './Navbar.svelte';
const { Story } = defineMeta({
title: 'Navigation/Navbar',
component: Navbar,
});
</script>
<Story name="Default">
<Navbar>
{#snippet start()}
<button class="btn">Home</button>
{/snippet}
{#snippet center()}
<h2 class="text-xl">Title</h2>
{/snippet}
{#snippet end()}
<div class="avatar placeholder">
<div class="w-8 rounded-full bg-neutral text-neutral-content">
<span class="text-xs">UI</span>
</div>
</div>
{/snippet}
</Navbar>
</Story>

View file

@ -0,0 +1,11 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let { start, center, end }: { start?: Snippet; center?: Snippet; end?: Snippet } = $props();
</script>
<header class="navbar justify-between rounded-box bg-base-200 px-4">
<div class="navbar-start">{@render start?.()}</div>
<div class="navbar-center">{@render center?.()}</div>
<div class="navbar-end">{@render end?.()}</div>
</header>

View file

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

View file

@ -0,0 +1,5 @@
<script lang="ts">
let { active, label, onclick } = $props();
</script>
<input aria-label={label} type="radio" role="tab" class="tab" class:tab-active={active} {onclick} />

View file

@ -0,0 +1,27 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import type { ComponentProps } from 'svelte';
import Tabs from './Tabs.svelte';
const { Story } = defineMeta({
title: 'Navigation/Tabs',
component: Tabs,
tags: ['autodocs'],
argTypes: {
size: {
control: 'select',
options: ['xs', 'sm', 'rg', 'lg'],
},
variant: {
control: 'select',
options: ['none', 'bordered', 'lifted', 'boxed'],
},
},
});
</script>
{#snippet template(args: Partial<ComponentProps<typeof Tabs>>)}
<Tabs tabs={['Tab 1', 'Tab 2']} {...args} />
{/snippet}
<Story name="Default" args={{}} children={template} />

View file

@ -0,0 +1,36 @@
<script lang="ts">
import type { DaisySize } from '$lib/types';
import Tab from './Tab.svelte';
type Props = {
size?: DaisySize;
tabs: string[];
selected?: number;
variant?: 'none' | 'bordered' | 'lifted' | 'boxed';
};
let { size, tabs, selected: value = $bindable(0), variant = 'none' }: Props = $props();
</script>
<div
role="tablist"
class="tabs w-full"
class:tabs-xs={size === 'xs'}
class:tabs-sm={size === 'sm'}
class:tabs-lg={size === 'lg'}
class:tabs-bordered={variant === 'bordered'}
class:tabs-lifted={variant === 'lifted'}
class:tabs-boxed={variant === 'boxed'}
>
{#each tabs as tab, index}
{#key [tab, value]}
<Tab
active={index === value}
label={tab}
onclick={() => {
value = index;
}}
/>
{/key}
{/each}
</div>

View file

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

View file

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

View file

@ -0,0 +1,6 @@
export * from "./Actions/";
export * from "./DataInput/";
export * from "./Datadisplay/";
export * from "./Feedback/";
export * from "./Layout/";
export * from "./Navigation/";

View file

@ -0,0 +1,10 @@
export type DaisyColor =
| "neutral"
| "primary"
| "secondary"
| "accent"
| "ghost"
| "info"
| "success"
| "warning"
| "error";

View file

@ -0,0 +1 @@
export type DaisySize = "xs" | "sm" | "lg";

View file

@ -0,0 +1,2 @@
export * from "./daisy-colors";
export * from "./daisy-sizes";