Modal
This commit is contained in:
35
dist/sigpro-ui.cjs
vendored
35
dist/sigpro-ui.cjs
vendored
@@ -1074,28 +1074,33 @@ var MenuModule = /*#__PURE__*/Object.freeze({
|
|||||||
/** MODAL */
|
/** MODAL */
|
||||||
const Modal = (props, children) => {
|
const Modal = (props, children) => {
|
||||||
const { title, buttons, open, ...rest } = props;
|
const { title, buttons, open, ...rest } = props;
|
||||||
const close = () => open(false);
|
|
||||||
|
const close = (e) => {
|
||||||
|
if (e && e.preventDefault) e.preventDefault();
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
return sigpro.$if(open, () =>
|
return sigpro.$if(open, () =>
|
||||||
sigpro.$html("dialog", { ...rest, class: "modal modal-open" }, [
|
sigpro.$html("dialog", {
|
||||||
|
...rest,
|
||||||
|
class: "modal modal-open",
|
||||||
|
oncancel: close
|
||||||
|
}, [
|
||||||
sigpro.$html("div", { class: "modal-box" }, [
|
sigpro.$html("div", { class: "modal-box" }, [
|
||||||
title ? sigpro.$html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
title ? sigpro.$html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
||||||
typeof children === "function" ? children() : children,
|
sigpro.$html("div", { class: "py-2" }, [
|
||||||
|
typeof children === "function" ? children() : children
|
||||||
|
]),
|
||||||
sigpro.$html("div", { class: "modal-action flex gap-2" }, [
|
sigpro.$html("div", { class: "modal-action flex gap-2" }, [
|
||||||
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
||||||
Button({ onclick: close }, tt("close")()),
|
Button({ type: "button", onclick: close }, tt("close")()),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
sigpro.$html(
|
sigpro.$html("div", {
|
||||||
"form",
|
class: "modal-backdrop bg-black/20",
|
||||||
{
|
onclick: close
|
||||||
method: "dialog",
|
})
|
||||||
class: "modal-backdrop",
|
])
|
||||||
onclick: (e) => (e.preventDefault(), close()),
|
|
||||||
},
|
|
||||||
[sigpro.$html("button", {}, "close")],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1257,7 +1262,7 @@ var StackModule = /*#__PURE__*/Object.freeze({
|
|||||||
/** STAT */
|
/** STAT */
|
||||||
const Stat = (props) =>
|
const Stat = (props) =>
|
||||||
sigpro.$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
sigpro.$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
||||||
props.icon && sigpro.$html("img", { src: props.icon }),
|
props.icon && sigpro.$html("div", { class: "stat-figure text-secondary" }, props.icon),
|
||||||
props.label && sigpro.$html("div", { class: "stat-title" }, props.label),
|
props.label && sigpro.$html("div", { class: "stat-title" }, props.label),
|
||||||
sigpro.$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
sigpro.$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
||||||
props.desc && sigpro.$html("div", { class: "stat-desc" }, props.desc),
|
props.desc && sigpro.$html("div", { class: "stat-desc" }, props.desc),
|
||||||
|
|||||||
35
dist/sigpro-ui.esm.js
vendored
35
dist/sigpro-ui.esm.js
vendored
@@ -1070,28 +1070,33 @@ var MenuModule = /*#__PURE__*/Object.freeze({
|
|||||||
/** MODAL */
|
/** MODAL */
|
||||||
const Modal = (props, children) => {
|
const Modal = (props, children) => {
|
||||||
const { title, buttons, open, ...rest } = props;
|
const { title, buttons, open, ...rest } = props;
|
||||||
const close = () => open(false);
|
|
||||||
|
const close = (e) => {
|
||||||
|
if (e && e.preventDefault) e.preventDefault();
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
return $if(open, () =>
|
return $if(open, () =>
|
||||||
$html("dialog", { ...rest, class: "modal modal-open" }, [
|
$html("dialog", {
|
||||||
|
...rest,
|
||||||
|
class: "modal modal-open",
|
||||||
|
oncancel: close
|
||||||
|
}, [
|
||||||
$html("div", { class: "modal-box" }, [
|
$html("div", { class: "modal-box" }, [
|
||||||
title ? $html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
title ? $html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
||||||
typeof children === "function" ? children() : children,
|
$html("div", { class: "py-2" }, [
|
||||||
|
typeof children === "function" ? children() : children
|
||||||
|
]),
|
||||||
$html("div", { class: "modal-action flex gap-2" }, [
|
$html("div", { class: "modal-action flex gap-2" }, [
|
||||||
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
||||||
Button({ onclick: close }, tt("close")()),
|
Button({ type: "button", onclick: close }, tt("close")()),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
$html(
|
$html("div", {
|
||||||
"form",
|
class: "modal-backdrop bg-black/20",
|
||||||
{
|
onclick: close
|
||||||
method: "dialog",
|
})
|
||||||
class: "modal-backdrop",
|
])
|
||||||
onclick: (e) => (e.preventDefault(), close()),
|
|
||||||
},
|
|
||||||
[$html("button", {}, "close")],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1253,7 +1258,7 @@ var StackModule = /*#__PURE__*/Object.freeze({
|
|||||||
/** STAT */
|
/** STAT */
|
||||||
const Stat = (props) =>
|
const Stat = (props) =>
|
||||||
$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
||||||
props.icon && $html("img", { src: props.icon }),
|
props.icon && $html("div", { class: "stat-figure text-secondary" }, props.icon),
|
||||||
props.label && $html("div", { class: "stat-title" }, props.label),
|
props.label && $html("div", { class: "stat-title" }, props.label),
|
||||||
$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
||||||
props.desc && $html("div", { class: "stat-desc" }, props.desc),
|
props.desc && $html("div", { class: "stat-desc" }, props.desc),
|
||||||
|
|||||||
35
dist/sigpro-ui.umd.js
vendored
35
dist/sigpro-ui.umd.js
vendored
@@ -1071,28 +1071,33 @@ var SigProUI = (function (exports, sigpro) {
|
|||||||
/** MODAL */
|
/** MODAL */
|
||||||
const Modal = (props, children) => {
|
const Modal = (props, children) => {
|
||||||
const { title, buttons, open, ...rest } = props;
|
const { title, buttons, open, ...rest } = props;
|
||||||
const close = () => open(false);
|
|
||||||
|
const close = (e) => {
|
||||||
|
if (e && e.preventDefault) e.preventDefault();
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
return sigpro.$if(open, () =>
|
return sigpro.$if(open, () =>
|
||||||
sigpro.$html("dialog", { ...rest, class: "modal modal-open" }, [
|
sigpro.$html("dialog", {
|
||||||
|
...rest,
|
||||||
|
class: "modal modal-open",
|
||||||
|
oncancel: close
|
||||||
|
}, [
|
||||||
sigpro.$html("div", { class: "modal-box" }, [
|
sigpro.$html("div", { class: "modal-box" }, [
|
||||||
title ? sigpro.$html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
title ? sigpro.$html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
||||||
typeof children === "function" ? children() : children,
|
sigpro.$html("div", { class: "py-2" }, [
|
||||||
|
typeof children === "function" ? children() : children
|
||||||
|
]),
|
||||||
sigpro.$html("div", { class: "modal-action flex gap-2" }, [
|
sigpro.$html("div", { class: "modal-action flex gap-2" }, [
|
||||||
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
||||||
Button({ onclick: close }, tt("close")()),
|
Button({ type: "button", onclick: close }, tt("close")()),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
sigpro.$html(
|
sigpro.$html("div", {
|
||||||
"form",
|
class: "modal-backdrop bg-black/20",
|
||||||
{
|
onclick: close
|
||||||
method: "dialog",
|
})
|
||||||
class: "modal-backdrop",
|
])
|
||||||
onclick: (e) => (e.preventDefault(), close()),
|
|
||||||
},
|
|
||||||
[sigpro.$html("button", {}, "close")],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1254,7 +1259,7 @@ var SigProUI = (function (exports, sigpro) {
|
|||||||
/** STAT */
|
/** STAT */
|
||||||
const Stat = (props) =>
|
const Stat = (props) =>
|
||||||
sigpro.$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
sigpro.$html("div", { ...props, class: joinClass("stat", props.class) }, [
|
||||||
props.icon && sigpro.$html("img", { src: props.icon }),
|
props.icon && sigpro.$html("div", { class: "stat-figure text-secondary" }, props.icon),
|
||||||
props.label && sigpro.$html("div", { class: "stat-title" }, props.label),
|
props.label && sigpro.$html("div", { class: "stat-title" }, props.label),
|
||||||
sigpro.$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
sigpro.$html("div", { class: "stat-value" }, () => val(props.value) ?? props.value),
|
||||||
props.desc && sigpro.$html("div", { class: "stat-desc" }, props.desc),
|
props.desc && sigpro.$html("div", { class: "stat-desc" }, props.desc),
|
||||||
|
|||||||
2
dist/sigpro-ui.umd.min.js
vendored
2
dist/sigpro-ui.umd.min.js
vendored
File diff suppressed because one or more lines are too long
667
docs/components/modal.md
Normal file
667
docs/components/modal.md
Normal file
@@ -0,0 +1,667 @@
|
|||||||
|
# Modal
|
||||||
|
|
||||||
|
Modal dialog component for displaying content in an overlay with customizable actions and backdrop.
|
||||||
|
|
||||||
|
## Tag
|
||||||
|
|
||||||
|
`Modal`
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
||||||
|
| `open` | `boolean \| Signal<boolean>` | `false` | Modal visibility state |
|
||||||
|
| `title` | `string \| VNode \| Signal` | `-` | Modal title text |
|
||||||
|
| `buttons` | `Array<VNode> \| VNode` | `-` | Action buttons to display (auto-closes on click) |
|
||||||
|
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||||
|
| `children` | `string \| VNode` | `-` | Modal content |
|
||||||
|
|
||||||
|
## Live Examples
|
||||||
|
|
||||||
|
### Basic Modal
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const BasicDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Basic Modal',
|
||||||
|
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(BasicDemo, '#demo-basic');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modal with Actions
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const ActionsDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const confirmed = $(false);
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
confirmed(true);
|
||||||
|
isOpen(false);
|
||||||
|
Toast('Action confirmed!', 'alert-success', 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Confirm Action'),
|
||||||
|
() => confirmed() ? Alert({
|
||||||
|
type: 'success',
|
||||||
|
message: 'You confirmed the action!'
|
||||||
|
}) : null,
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Confirm Action',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: handleConfirm
|
||||||
|
}, 'Confirm')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, 'Are you sure you want to perform this action? This cannot be undone.')
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(ActionsDemo, '#demo-actions');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Modal
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-form" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const FormModal = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const name = $('');
|
||||||
|
const email = $('');
|
||||||
|
const submitted = $(false);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (name() && email()) {
|
||||||
|
submitted(true);
|
||||||
|
isOpen(false);
|
||||||
|
Toast(`Welcome ${name()}!`, 'alert-success', 2000);
|
||||||
|
setTimeout(() => submitted(false), 3000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Sign Up'),
|
||||||
|
() => submitted() ? Alert({
|
||||||
|
type: 'success',
|
||||||
|
message: 'Account created successfully!'
|
||||||
|
}) : null,
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Create Account',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: handleSubmit
|
||||||
|
}, 'Sign Up')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'flex flex-col gap-4 py-2' }, [
|
||||||
|
Input({
|
||||||
|
label: 'Full Name',
|
||||||
|
value: name,
|
||||||
|
placeholder: 'Enter your name',
|
||||||
|
oninput: (e) => name(e.target.value)
|
||||||
|
}),
|
||||||
|
Input({
|
||||||
|
label: 'Email',
|
||||||
|
type: 'email',
|
||||||
|
value: email,
|
||||||
|
placeholder: 'Enter your email',
|
||||||
|
oninput: (e) => email(e.target.value)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(FormModal, '#demo-form');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Confirmation Modal
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-confirm" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const ConfirmDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const items = $([
|
||||||
|
{ id: 1, name: 'Document A' },
|
||||||
|
{ id: 2, name: 'Document B' },
|
||||||
|
{ id: 3, name: 'Document C' }
|
||||||
|
]);
|
||||||
|
const pendingDelete = $(null);
|
||||||
|
|
||||||
|
const confirmDelete = (item) => {
|
||||||
|
pendingDelete(item);
|
||||||
|
isOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
items(items().filter(i => i.id !== pendingDelete().id));
|
||||||
|
isOpen(false);
|
||||||
|
Toast(`Deleted: ${pendingDelete().name}`, 'alert-info', 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4' }, [
|
||||||
|
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
||||||
|
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
||||||
|
Span({}, item.name),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-xs btn-error',
|
||||||
|
onclick: () => confirmDelete(item)
|
||||||
|
}, 'Delete')
|
||||||
|
])
|
||||||
|
)),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Delete Confirmation',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-error',
|
||||||
|
onclick: handleDelete
|
||||||
|
}, 'Delete')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, () => `Are you sure you want to delete "${pendingDelete()?.name}"? This action cannot be undone.`)
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(ConfirmDemo, '#demo-confirm');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Large Content Modal
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-large" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const LargeDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Large Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Terms and Conditions',
|
||||||
|
buttons: Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'I Agree')
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4 max-h-96 overflow-y-auto' }, [
|
||||||
|
Div({ class: 'space-y-4' }, [
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '1. Introduction'),
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '2. User Obligations'),
|
||||||
|
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '3. Privacy Policy'),
|
||||||
|
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '4. Termination'),
|
||||||
|
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores.'
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(LargeDemo, '#demo-large');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Modals
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-multiple" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const MultipleDemo = () => {
|
||||||
|
const modal1 = $(false);
|
||||||
|
const modal2 = $(false);
|
||||||
|
const modal3 = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
||||||
|
Button({ class: 'btn', onclick: () => modal1(true) }, 'Info Modal'),
|
||||||
|
Button({ class: 'btn', onclick: () => modal2(true) }, 'Success Modal'),
|
||||||
|
Button({ class: 'btn', onclick: () => modal3(true) }, 'Warning Modal'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal1,
|
||||||
|
title: 'Information',
|
||||||
|
buttons: Button({ onclick: () => modal1(false) }, 'OK')
|
||||||
|
}, 'This is an informational message.'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal2,
|
||||||
|
title: 'Success!',
|
||||||
|
buttons: Button({ onclick: () => modal2(false) }, 'Great!')
|
||||||
|
}, 'Your operation was completed successfully.'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal3,
|
||||||
|
title: 'Warning',
|
||||||
|
buttons: Button({ onclick: () => modal3(false) }, 'Understood')
|
||||||
|
}, 'Please review your input before proceeding.')
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(MultipleDemo, '#demo-multiple');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Styled Modal
|
||||||
|
|
||||||
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||||
|
<div id="demo-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const CustomDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Custom Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
class: 'bg-gradient-to-r from-primary to-secondary text-primary-content'
|
||||||
|
}, [
|
||||||
|
Div({ class: 'text-center py-8' }, [
|
||||||
|
Div({ class: 'text-6xl mb-4' }, '🎉'),
|
||||||
|
Div({ class: 'text-2xl font-bold mb-2' }, 'Congratulations!'),
|
||||||
|
Div({ class: 'mb-6' }, 'You\'ve successfully completed the tutorial.'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost bg-white/20 hover:bg-white/30',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Get Started')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(CustomDemo, '#demo-custom');
|
||||||
|
```
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const initModalExamples = () => {
|
||||||
|
|
||||||
|
// 1. Basic Modal
|
||||||
|
const basicTarget = document.querySelector('#demo-basic');
|
||||||
|
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||||
|
const BasicDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Basic Modal',
|
||||||
|
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(BasicDemo, basicTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Modal with Actions
|
||||||
|
const actionsTarget = document.querySelector('#demo-actions');
|
||||||
|
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
||||||
|
const ActionsDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const confirmed = $(false);
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
confirmed(true);
|
||||||
|
isOpen(false);
|
||||||
|
Toast('Action confirmed!', 'alert-success', 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Confirm Action'),
|
||||||
|
() => confirmed() ? Alert({
|
||||||
|
type: 'success',
|
||||||
|
message: 'You confirmed the action!'
|
||||||
|
}) : null,
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Confirm Action',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: handleConfirm
|
||||||
|
}, 'Confirm')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, 'Are you sure you want to perform this action? This cannot be undone.')
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(ActionsDemo, actionsTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Form Modal
|
||||||
|
const formTarget = document.querySelector('#demo-form');
|
||||||
|
if (formTarget && !formTarget.hasChildNodes()) {
|
||||||
|
const FormModal = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const name = $('');
|
||||||
|
const email = $('');
|
||||||
|
const submitted = $(false);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (name() && email()) {
|
||||||
|
submitted(true);
|
||||||
|
isOpen(false);
|
||||||
|
Toast(`Welcome ${name()}!`, 'alert-success', 2000);
|
||||||
|
setTimeout(() => submitted(false), 3000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Sign Up'),
|
||||||
|
() => submitted() ? Alert({
|
||||||
|
type: 'success',
|
||||||
|
message: 'Account created successfully!'
|
||||||
|
}) : null,
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Create Account',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: handleSubmit
|
||||||
|
}, 'Sign Up')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'flex flex-col gap-4 py-2' }, [
|
||||||
|
Input({
|
||||||
|
label: 'Full Name',
|
||||||
|
value: name,
|
||||||
|
placeholder: 'Enter your name',
|
||||||
|
oninput: (e) => name(e.target.value)
|
||||||
|
}),
|
||||||
|
Input({
|
||||||
|
label: 'Email',
|
||||||
|
type: 'email',
|
||||||
|
value: email,
|
||||||
|
placeholder: 'Enter your email',
|
||||||
|
oninput: (e) => email(e.target.value)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(FormModal, formTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Confirmation Modal
|
||||||
|
const confirmTarget = document.querySelector('#demo-confirm');
|
||||||
|
if (confirmTarget && !confirmTarget.hasChildNodes()) {
|
||||||
|
const ConfirmDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
const items = $([
|
||||||
|
{ id: 1, name: 'Document A' },
|
||||||
|
{ id: 2, name: 'Document B' },
|
||||||
|
{ id: 3, name: 'Document C' }
|
||||||
|
]);
|
||||||
|
const pendingDelete = $(null);
|
||||||
|
|
||||||
|
const confirmDelete = (item) => {
|
||||||
|
pendingDelete(item);
|
||||||
|
isOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
items(items().filter(i => i.id !== pendingDelete().id));
|
||||||
|
isOpen(false);
|
||||||
|
Toast(`Deleted: ${pendingDelete().name}`, 'alert-info', 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-col gap-4' }, [
|
||||||
|
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
||||||
|
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
||||||
|
Span({}, item.name),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-xs btn-error',
|
||||||
|
onclick: () => confirmDelete(item)
|
||||||
|
}, 'Delete')
|
||||||
|
])
|
||||||
|
)),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Delete Confirmation',
|
||||||
|
buttons: [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Cancel'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-error',
|
||||||
|
onclick: handleDelete
|
||||||
|
}, 'Delete')
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4' }, () => `Are you sure you want to delete "${pendingDelete()?.name}"? This action cannot be undone.`)
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(ConfirmDemo, confirmTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Large Content Modal
|
||||||
|
const largeTarget = document.querySelector('#demo-large');
|
||||||
|
if (largeTarget && !largeTarget.hasChildNodes()) {
|
||||||
|
const LargeDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Large Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
title: 'Terms and Conditions',
|
||||||
|
buttons: Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'I Agree')
|
||||||
|
}, [
|
||||||
|
Div({ class: 'py-4 max-h-96 overflow-y-auto' }, [
|
||||||
|
Div({ class: 'space-y-4' }, [
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '1. Introduction'),
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '2. User Obligations'),
|
||||||
|
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '3. Privacy Policy'),
|
||||||
|
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.'
|
||||||
|
]),
|
||||||
|
Div({ class: 'text-sm' }, [
|
||||||
|
Div({ class: 'font-bold mb-2' }, '4. Termination'),
|
||||||
|
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores.'
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(LargeDemo, largeTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Multiple Modals
|
||||||
|
const multipleTarget = document.querySelector('#demo-multiple');
|
||||||
|
if (multipleTarget && !multipleTarget.hasChildNodes()) {
|
||||||
|
const MultipleDemo = () => {
|
||||||
|
const modal1 = $(false);
|
||||||
|
const modal2 = $(false);
|
||||||
|
const modal3 = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
||||||
|
Button({ class: 'btn', onclick: () => modal1(true) }, 'Info Modal'),
|
||||||
|
Button({ class: 'btn', onclick: () => modal2(true) }, 'Success Modal'),
|
||||||
|
Button({ class: 'btn', onclick: () => modal3(true) }, 'Warning Modal'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal1,
|
||||||
|
title: 'Information',
|
||||||
|
buttons: Button({ onclick: () => modal1(false) }, 'OK')
|
||||||
|
}, 'This is an informational message.'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal2,
|
||||||
|
title: 'Success!',
|
||||||
|
buttons: Button({ onclick: () => modal2(false) }, 'Great!')
|
||||||
|
}, 'Your operation was completed successfully.'),
|
||||||
|
|
||||||
|
Modal({
|
||||||
|
open: modal3,
|
||||||
|
title: 'Warning',
|
||||||
|
buttons: Button({ onclick: () => modal3(false) }, 'Understood')
|
||||||
|
}, 'Please review your input before proceeding.')
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(MultipleDemo, multipleTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Custom Styled Modal
|
||||||
|
const customTarget = document.querySelector('#demo-custom');
|
||||||
|
if (customTarget && !customTarget.hasChildNodes()) {
|
||||||
|
const CustomDemo = () => {
|
||||||
|
const isOpen = $(false);
|
||||||
|
|
||||||
|
return Div({ class: 'flex justify-center' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-primary',
|
||||||
|
onclick: () => isOpen(true)
|
||||||
|
}, 'Open Custom Modal'),
|
||||||
|
Modal({
|
||||||
|
open: isOpen,
|
||||||
|
class: 'bg-gradient-to-r from-primary to-secondary text-primary-content'
|
||||||
|
}, [
|
||||||
|
Div({ class: 'text-center py-8' }, [
|
||||||
|
Div({ class: 'text-6xl mb-4' }, '🎉'),
|
||||||
|
Div({ class: 'text-2xl font-bold mb-2' }, 'Congratulations!'),
|
||||||
|
Div({ class: 'mb-6' }, 'You\'ve successfully completed the tutorial.'),
|
||||||
|
Button({
|
||||||
|
class: 'btn btn-ghost bg-white/20 hover:bg-white/30',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}, 'Get Started')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
$mount(CustomDemo, customTarget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
initModalExamples();
|
||||||
|
|
||||||
|
if (window.$docsify) {
|
||||||
|
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||||
|
hook.doneEach(initModalExamples);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -5,27 +5,32 @@ import { Button } from "./Button.js";
|
|||||||
/** MODAL */
|
/** MODAL */
|
||||||
export const Modal = (props, children) => {
|
export const Modal = (props, children) => {
|
||||||
const { title, buttons, open, ...rest } = props;
|
const { title, buttons, open, ...rest } = props;
|
||||||
const close = () => open(false);
|
|
||||||
|
const close = (e) => {
|
||||||
|
if (e && e.preventDefault) e.preventDefault();
|
||||||
|
open(false);
|
||||||
|
};
|
||||||
|
|
||||||
return $if(open, () =>
|
return $if(open, () =>
|
||||||
$html("dialog", { ...rest, class: "modal modal-open" }, [
|
$html("dialog", {
|
||||||
|
...rest,
|
||||||
|
class: "modal modal-open",
|
||||||
|
oncancel: close
|
||||||
|
}, [
|
||||||
$html("div", { class: "modal-box" }, [
|
$html("div", { class: "modal-box" }, [
|
||||||
title ? $html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
title ? $html("h3", { class: "text-lg font-bold mb-4" }, title) : null,
|
||||||
typeof children === "function" ? children() : children,
|
$html("div", { class: "py-2" }, [
|
||||||
|
typeof children === "function" ? children() : children
|
||||||
|
]),
|
||||||
$html("div", { class: "modal-action flex gap-2" }, [
|
$html("div", { class: "modal-action flex gap-2" }, [
|
||||||
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
||||||
Button({ onclick: close }, tt("close")()),
|
Button({ type: "button", onclick: close }, tt("close")()),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
$html(
|
$html("div", {
|
||||||
"form",
|
class: "modal-backdrop bg-black/20",
|
||||||
{
|
onclick: close
|
||||||
method: "dialog",
|
})
|
||||||
class: "modal-backdrop",
|
])
|
||||||
onclick: (e) => (e.preventDefault(), close()),
|
|
||||||
},
|
|
||||||
[$html("button", {}, "close")],
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user