@mikael240820/modal-toast-tailwind 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # modal-toast-tailwind
2
+
3
+ React component for create modal (when `children` is provided) or toast (when `open` is `true`)
4
+ IMPORTANT: Must use **Tailwind CSS** in your **project**
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ npm i @mikael240820/modal-toast-tailwind
10
+ ```
11
+
12
+ ### Tailwind CSS
13
+
14
+ In your main CSS file, add the following:
15
+
16
+ ```css
17
+ @source "../node_modules/@mikael240820/modal-toast-tailwind/dist/**/*.{js,jsx,mjs}";
18
+ ```
19
+
20
+ ## Exemples
21
+
22
+ ### Modal
23
+
24
+ ```jsx
25
+ import { ModalToast as Modal } from "@mikael240820/modal-toast-tailwind";
26
+
27
+ export default function App() {
28
+ return (
29
+ <Modal title="My modal" triggerLabel="Open">
30
+ <div>Modal content</div>
31
+ </Modal>
32
+ );
33
+ }
34
+ ```
35
+
36
+ ### Toast
37
+
38
+ ```jsx
39
+ import { useState } from "react";
40
+ import { ModalToast as Toast } from "@mikael240820/modal-toast-tailwind";
41
+
42
+ export default function App() {
43
+ const [open, setOpen] = useState(false);
44
+
45
+ return (
46
+ <>
47
+ <button onClick={() => setOpen(true)}>Show toast</button>
48
+
49
+ <Toast
50
+ title="Saved"
51
+ description="Your changes have been success."
52
+ open={open}
53
+ onClose={() => setOpen(false)}
54
+ duration={5000}
55
+ />
56
+ </>
57
+ );
58
+ }
59
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,150 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.js
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ ModalToast: () => ModalToast
23
+ });
24
+ module.exports = __toCommonJS(index_exports);
25
+
26
+ // src/ModalToast.jsx
27
+ var import_react = require("react");
28
+ var import_jsx_runtime = require("react/jsx-runtime");
29
+ function ModalToast({
30
+ title,
31
+ description,
32
+ children,
33
+ triggerLabel = "Open",
34
+ open = false,
35
+ onClose,
36
+ duration = 5e3
37
+ }) {
38
+ const isModal = Boolean(children);
39
+ const [modalOpen, setModalOpen] = (0, import_react.useState)(false);
40
+ const titleId = (0, import_react.useId)();
41
+ const descriptionId = (0, import_react.useId)();
42
+ const onCloseRef = (0, import_react.useRef)(onClose);
43
+ (0, import_react.useEffect)(() => {
44
+ onCloseRef.current = onClose;
45
+ }, [onClose]);
46
+ (0, import_react.useEffect)(() => {
47
+ if (isModal || !open) {
48
+ return;
49
+ }
50
+ const timer = setTimeout(() => {
51
+ var _a;
52
+ (_a = onCloseRef.current) == null ? void 0 : _a.call(onCloseRef);
53
+ }, duration);
54
+ return () => clearTimeout(timer);
55
+ }, [duration, isModal, open]);
56
+ (0, import_react.useEffect)(() => {
57
+ if (!isModal || !modalOpen) {
58
+ return;
59
+ }
60
+ const handleKeyDown = (event) => {
61
+ if (event.key === "Escape") {
62
+ setModalOpen(false);
63
+ }
64
+ };
65
+ window.addEventListener("keydown", handleKeyDown);
66
+ return () => window.removeEventListener("keydown", handleKeyDown);
67
+ }, [isModal, modalOpen]);
68
+ if (isModal) {
69
+ const triggerText = triggerLabel || title || "Open";
70
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "inline-flex", children: [
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ className: "inline-flex items-center rounded-md border border-leaf-200 bg-white px-3 py-2 text-sm font-semibold text-leaf-900 hover:bg-leaf-100",
76
+ onClick: () => setModalOpen(true),
77
+ children: triggerText
78
+ }
79
+ ),
80
+ modalOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
81
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ className: "absolute inset-0 bg-leaf-900/20 backdrop-blur-sm",
86
+ onClick: () => setModalOpen(false),
87
+ "aria-label": "Close modal"
88
+ }
89
+ ),
90
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
91
+ "div",
92
+ {
93
+ role: "dialog",
94
+ "aria-modal": "true",
95
+ "aria-labelledby": title ? titleId : void 0,
96
+ "aria-describedby": description ? descriptionId : void 0,
97
+ className: "relative w-full max-w-lg rounded-lg border border-leaf-200 bg-white p-5 shadow-xl",
98
+ children: [
99
+ title || description ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-4", children: [
100
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { id: titleId, className: "text-lg font-semibold text-leaf-900", children: title }) : null,
101
+ description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { id: descriptionId, className: "mt-1 text-sm text-leaf-700", children: description }) : null
102
+ ] }) : null,
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm text-leaf-700", children }),
104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-5 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
105
+ "button",
106
+ {
107
+ type: "button",
108
+ className: "inline-flex items-center rounded-md bg-leaf-700 px-4 py-2 text-sm font-semibold text-white",
109
+ onClick: () => setModalOpen(false),
110
+ children: "Close"
111
+ }
112
+ ) })
113
+ ]
114
+ }
115
+ )
116
+ ] }) : null
117
+ ] });
118
+ }
119
+ if (!open) {
120
+ return;
121
+ }
122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fixed bottom-6 right-6 z-50 w-[calc(100%-3rem)] max-w-sm", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
+ "div",
124
+ {
125
+ role: "status",
126
+ "aria-live": "polite",
127
+ className: "rounded-lg border border-leaf-200 bg-white p-4 shadow-lg",
128
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
130
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm font-semibold text-leaf-900", children: title }) : null,
131
+ description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-1 text-sm text-leaf-700", children: description }) : null
132
+ ] }),
133
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
134
+ "button",
135
+ {
136
+ type: "button",
137
+ className: "rounded-md border border-leaf-200 px-2 py-1 text-xs font-semibold text-leaf-700 hover:bg-leaf-100",
138
+ onClick: onClose,
139
+ "aria-label": "Close notification",
140
+ children: "Close"
141
+ }
142
+ )
143
+ ] })
144
+ }
145
+ ) });
146
+ }
147
+ // Annotate the CommonJS export names for ESM import in node:
148
+ 0 && (module.exports = {
149
+ ModalToast
150
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,124 @@
1
+ // src/ModalToast.jsx
2
+ import { useEffect, useId, useRef, useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ function ModalToast({
5
+ title,
6
+ description,
7
+ children,
8
+ triggerLabel = "Open",
9
+ open = false,
10
+ onClose,
11
+ duration = 5e3
12
+ }) {
13
+ const isModal = Boolean(children);
14
+ const [modalOpen, setModalOpen] = useState(false);
15
+ const titleId = useId();
16
+ const descriptionId = useId();
17
+ const onCloseRef = useRef(onClose);
18
+ useEffect(() => {
19
+ onCloseRef.current = onClose;
20
+ }, [onClose]);
21
+ useEffect(() => {
22
+ if (isModal || !open) {
23
+ return;
24
+ }
25
+ const timer = setTimeout(() => {
26
+ var _a;
27
+ (_a = onCloseRef.current) == null ? void 0 : _a.call(onCloseRef);
28
+ }, duration);
29
+ return () => clearTimeout(timer);
30
+ }, [duration, isModal, open]);
31
+ useEffect(() => {
32
+ if (!isModal || !modalOpen) {
33
+ return;
34
+ }
35
+ const handleKeyDown = (event) => {
36
+ if (event.key === "Escape") {
37
+ setModalOpen(false);
38
+ }
39
+ };
40
+ window.addEventListener("keydown", handleKeyDown);
41
+ return () => window.removeEventListener("keydown", handleKeyDown);
42
+ }, [isModal, modalOpen]);
43
+ if (isModal) {
44
+ const triggerText = triggerLabel || title || "Open";
45
+ return /* @__PURE__ */ jsxs("div", { className: "inline-flex", children: [
46
+ /* @__PURE__ */ jsx(
47
+ "button",
48
+ {
49
+ type: "button",
50
+ className: "inline-flex items-center rounded-md border border-leaf-200 bg-white px-3 py-2 text-sm font-semibold text-leaf-900 hover:bg-leaf-100",
51
+ onClick: () => setModalOpen(true),
52
+ children: triggerText
53
+ }
54
+ ),
55
+ modalOpen ? /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
56
+ /* @__PURE__ */ jsx(
57
+ "button",
58
+ {
59
+ type: "button",
60
+ className: "absolute inset-0 bg-leaf-900/20 backdrop-blur-sm",
61
+ onClick: () => setModalOpen(false),
62
+ "aria-label": "Close modal"
63
+ }
64
+ ),
65
+ /* @__PURE__ */ jsxs(
66
+ "div",
67
+ {
68
+ role: "dialog",
69
+ "aria-modal": "true",
70
+ "aria-labelledby": title ? titleId : void 0,
71
+ "aria-describedby": description ? descriptionId : void 0,
72
+ className: "relative w-full max-w-lg rounded-lg border border-leaf-200 bg-white p-5 shadow-xl",
73
+ children: [
74
+ title || description ? /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
75
+ title ? /* @__PURE__ */ jsx("h3", { id: titleId, className: "text-lg font-semibold text-leaf-900", children: title }) : null,
76
+ description ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-1 text-sm text-leaf-700", children: description }) : null
77
+ ] }) : null,
78
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-leaf-700", children }),
79
+ /* @__PURE__ */ jsx("div", { className: "mt-5 flex justify-end", children: /* @__PURE__ */ jsx(
80
+ "button",
81
+ {
82
+ type: "button",
83
+ className: "inline-flex items-center rounded-md bg-leaf-700 px-4 py-2 text-sm font-semibold text-white",
84
+ onClick: () => setModalOpen(false),
85
+ children: "Close"
86
+ }
87
+ ) })
88
+ ]
89
+ }
90
+ )
91
+ ] }) : null
92
+ ] });
93
+ }
94
+ if (!open) {
95
+ return;
96
+ }
97
+ return /* @__PURE__ */ jsx("div", { className: "fixed bottom-6 right-6 z-50 w-[calc(100%-3rem)] max-w-sm", children: /* @__PURE__ */ jsx(
98
+ "div",
99
+ {
100
+ role: "status",
101
+ "aria-live": "polite",
102
+ className: "rounded-lg border border-leaf-200 bg-white p-4 shadow-lg",
103
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
104
+ /* @__PURE__ */ jsxs("div", { children: [
105
+ title ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-leaf-900", children: title }) : null,
106
+ description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-leaf-700", children: description }) : null
107
+ ] }),
108
+ /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ className: "rounded-md border border-leaf-200 px-2 py-1 text-xs font-semibold text-leaf-700 hover:bg-leaf-100",
113
+ onClick: onClose,
114
+ "aria-label": "Close notification",
115
+ children: "Close"
116
+ }
117
+ )
118
+ ] })
119
+ }
120
+ ) });
121
+ }
122
+ export {
123
+ ModalToast
124
+ };
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@mikael240820/modal-toast-tailwind",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.cjs",
5
+ "module": "dist/index.js",
6
+ "files": [
7
+ "dist",
8
+ "README.md"
9
+ ],
10
+ "peerDependencies": {
11
+ "react": ">=19.2",
12
+ "react-dom": ">=19.2"
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "devDependencies": {
19
+ "tsup": "^8.5.1",
20
+ "typescript": "^5.9.3"
21
+ }
22
+ }