Skip to content

Commit 47c51b8

Browse files
committed
feat: modal component and page
1 parent 8a09cd2 commit 47c51b8

23 files changed

+669
-12
lines changed

.changeset/orange-foxes-compete.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte-5-ui-lib': patch
3+
---
4+
5+
feat: modal component and page

src/generatedFileList.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const fileList = [
6060
"src/lib/kbd/Kbd.svelte",
6161
"src/lib/list-group/Listgroup.svelte",
6262
"src/lib/list-group/ListgroupItem.svelte",
63+
"src/lib/modal/Modal.svelte",
6364
"src/lib/nav/NavBrand.svelte",
6465
"src/lib/nav/NavLi.svelte",
6566
"src/lib/nav/NavUl.svelte",

src/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from './gallery';
1919
export * from './indicator';
2020
export * from './kbd';
2121
export * from './list-group';
22+
export * from './modal';
2223
export * from './nav';
2324
export * from './pagination';
2425
export * from './popover';

src/lib/modal/Modal.svelte

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<script lang="ts">
2+
import type { ParamsType } from '$lib/types';
3+
import CloseButton from '$lib/utils/CloseButton.svelte';
4+
import { type ModalProps as Props, modal } from '.';
5+
import { fade } from 'svelte/transition';
6+
import { sineIn } from 'svelte/easing';
7+
8+
let { children, header, footer, title, modalStatus, dismissable = true, closeModal, divClass, contentClass, closeBtnClass, h3Class, headerClass, bodyClass, footerClass, outsideClose = true, size = 'md', backdrop = true, backdropClass, position = 'center', class: className, params = { duration: 100, easing: sineIn }, transition = fade, rounded = true, shadow = true, ...restProps }: Props = $props();
9+
10+
const { base, div, content, backdrop: backdropCls, header: headerCls, footer: footerCls, body, closeBtn, h3 } = $derived(
11+
modal({
12+
position,
13+
size,
14+
backdrop,
15+
rounded
16+
})
17+
);
18+
19+
</script>
20+
21+
{#if modalStatus}
22+
{#if backdrop && outsideClose}
23+
<div role="presentation" class={backdropCls({ class: backdropClass })} onclick={closeModal}></div>
24+
{:else if backdrop && !outsideClose}
25+
<div role="presentation" class={backdropCls({ class: backdropClass })}></div>
26+
{:else if !backdrop && outsideClose}
27+
<div role="presentation" class="fixed start-0 top-0 z-50 h-full w-full" onclick={closeModal}></div>
28+
{:else if !backdrop && !outsideClose}
29+
<div role="presentation" class="fixed start-0 top-0 z-50 h-full w-full"></div>
30+
{/if}
31+
<div {...restProps} class={base({ className })} transition:transition={params as ParamsType} tabindex="-1">
32+
<div class={div({ class: divClass })}>
33+
<div class={content({ class: contentClass })}>
34+
{#if title || header}
35+
<div class={headerCls({ class: headerClass })}>
36+
{#if title}
37+
<h3 class={h3({ class: h3Class })}>
38+
{title}
39+
</h3>
40+
{:else if header}
41+
{@render header()}
42+
{/if}
43+
{#if dismissable}
44+
<CloseButton onclick={closeModal} class={closeBtn({ class: closeBtnClass })}/>
45+
{/if}
46+
</div>
47+
{/if}
48+
<div class={body({ class: bodyClass })}>
49+
{#if dismissable && !title && !header}
50+
<CloseButton onclick={closeModal} class={closeBtn({ class: closeBtnClass })}/>
51+
{/if}
52+
{@render children()}
53+
</div>
54+
{#if footer}
55+
<div class={footerCls({ class: footerClass })}>
56+
{@render footer()}
57+
</div>
58+
{/if}
59+
</div>
60+
</div>
61+
</div>
62+
{/if}
63+
64+
<!--
65+
@component
66+
[Go to docs](https://svelte-5-ui-lib.codewithshin.com/)
67+
## Props
68+
@prop children
69+
@prop drawerStatus
70+
@prop toggleDrawer
71+
@prop closeDrawer
72+
@prop outsideClose = true
73+
@prop position
74+
@prop width
75+
@prop backdrop = true
76+
@prop backdropClass
77+
@prop placement = 'left'
78+
@prop class: divClass
79+
@prop transitionParams
80+
@prop transitionType = 'fly'
81+
@prop ...restProps
82+
-->

src/lib/modal/index.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Modal from './Modal.svelte';
2+
import type { Snippet } from 'svelte';
3+
import type { HTMLAttributes } from 'svelte/elements';
4+
import { type VariantProps } from 'tailwind-variants';
5+
import type { TransitionFunc, ParamsType } from '../types';
6+
import { modal } from './theme';
7+
8+
type WidthType = VariantProps<typeof modal>['size'];
9+
// type PlacementType = VariantProps<typeof modal>['placement'];
10+
type PosisionType = VariantProps<typeof modal>['position'];
11+
interface ModalProps extends HTMLAttributes<HTMLDivElement> {
12+
children: Snippet;
13+
header?: Snippet,
14+
footer?: Snippet,
15+
title?: string,
16+
modalStatus: boolean;
17+
dismissable?: boolean;
18+
closeModal?: () => void;
19+
closeBtnClass?: string;
20+
h3Class?: string;
21+
divClass?: string;
22+
headerClass?: string;
23+
contentClass?: string;
24+
bodyClass?: string;
25+
footerClass?: string;
26+
outsideClose?: boolean;
27+
position?: PosisionType;
28+
size?: WidthType;
29+
backdrop?: boolean;
30+
backdropClass?: string;
31+
rounded?: boolean;
32+
shadow?: boolean;
33+
// placement?: PlacementType;
34+
class?: string;
35+
params?: ParamsType;
36+
transition?: TransitionFunc;
37+
}
38+
39+
export { Modal, modal, type ModalProps };
40+

src/lib/modal/theme.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { tv } from 'tailwind-variants';
2+
3+
export const modal = tv({
4+
slots: {
5+
base: 'fixed top-0 start-0 end-0 h-modal md:inset-0 md:h-full z-50 w-full p-4 flex pointer-events-none',
6+
div: 'flex relative w-full max-h-full',
7+
content: 'w-full divide-y text-gray-800 dark:text-gray-300 border-gray-300 dark:border-gray-800 divide-gray-300 dark:divide-gray-800 bg-white dark:bg-gray-800 pointer-events-auto',
8+
backdrop: 'fixed inset-0 z-50 bg-gray-900 bg-opacity-50 dark:bg-opacity-80 pointer-events-auto',
9+
header: 'flex justify-between items-center p-4 md:p-5 rounded-t-lg',
10+
footer: 'flex items-center p-4 md:p-5 space-x-3 rtl:space-x-reverse rounded-b-lg',
11+
body: 'p-4 md:p-5 space-y-4 flex-1 overflow-y-auto overscroll-contain',
12+
closeBtn: 'absolute top-3 end-2.5',
13+
h3:'text-xl font-semibold text-gray-900 dark:text-white p-0'
14+
},
15+
variants: {
16+
// position: {
17+
// fixed: { base: 'fixed' },
18+
// absolute: { base: 'absolute' }
19+
// },
20+
position: {
21+
'top-left': { base: 'justify-start items-start'},
22+
'top-center': { base: 'justify-center items-start'},
23+
'top-right': { base: 'justify-end items-start'},
24+
'center-left': { base: 'justify-start items-center'},
25+
'center': { base: 'justify-center items-center'},
26+
'center-right': { base: 'justify-end items-center'},
27+
'bottom-left': { base: 'justify-start items-end'},
28+
'bottom-center': { base: 'justify-center items-end'},
29+
'bottom-right': { base: 'justify-end items-end'},
30+
'default': { base: 'justify-center items-center'}
31+
},
32+
size: {
33+
xs: { div: 'max-w-md' },
34+
sm: { div: 'max-w-lg' },
35+
md: { div: 'max-w-2xl' },
36+
lg: { div: 'max-w-4xl' },
37+
xl: { div: 'max-w-7xl' },
38+
},
39+
backdrop: {
40+
true: { backdrop: 'bg-gray-900 bg-opacity-75' }
41+
},
42+
rounded: {
43+
true: { content: 'rounded' },
44+
},
45+
shadow: {
46+
true: { content: 'shadow-md' }
47+
}
48+
},
49+
})

src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,5 @@ export interface TransitionParamTypes {
103103
export type ParamsType = FadeParams | BlurParams | FlyParams | SlideParams | ScaleParams;
104104

105105
export type TransitionFunc = (node: HTMLElement, params: ParamsType) => TransitionConfig;
106+
107+
export type ModalPlacementType = 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';

src/lib/utils/CloseButton.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { type CloseButtonProps as Props, closeButtonVariants } from './index';
33
4-
let { color = 'primary', onclick, name = 'Close', ariaLabel, size = 'md', href, class: className, ...restProps }: Props = $props();
4+
let { color = 'gray', onclick, name = 'Close', ariaLabel, size = 'md', href, class: className, ...restProps }: Props = $props();
55
66
const { base, svg } = $derived(closeButtonVariants({ color, size }));
77
</script>

0 commit comments

Comments
 (0)