@planningcenter/sweetest-alert 0.1.0 → 0.3.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/docs/demo.tsx DELETED
@@ -1,306 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- import { createRoot } from "react-dom/client"
4
- import { useEffect, useRef } from "react"
5
- import { SweetestAlert } from "../src/index"
6
- import Prism from "prismjs"
7
- import "prismjs/themes/prism-tomorrow.css"
8
- import "prismjs/components/prism-jsx"
9
- import "prismjs/components/prism-tsx"
10
- import "prismjs/components/prism-bash"
11
-
12
- import "../node_modules/@planningcenter/tapestry/dist/index.css"
13
- import "../src/sweetest_alert.css"
14
- import "./demo.css"
15
-
16
- function CodeBlock({ code, language = "jsx" }: { code: string; language?: string }) {
17
- const codeRef = useRef<HTMLElement>(null)
18
-
19
- useEffect(() => {
20
- if (codeRef.current) {
21
- Prism.highlightElement(codeRef.current)
22
- }
23
- }, [code, language])
24
-
25
- return (
26
- <div className="codeblock">
27
- <pre>
28
- <code ref={codeRef} className={`language-${language}`}>
29
- {code}
30
- </code>
31
- </pre>
32
- </div>
33
- )
34
- }
35
-
36
- function Demo() {
37
- const showInfoAlert = () => {
38
- SweetestAlert({
39
- type: "info",
40
- title: "Information",
41
- content: "This is an informational alert to let you know something.",
42
- hideCancel: true,
43
- })
44
- }
45
-
46
- const showSuccessAlert = () => {
47
- SweetestAlert({
48
- type: "success",
49
- title: "Success!",
50
- content: "Your operation completed successfully.",
51
- hideCancel: true,
52
- })
53
- }
54
-
55
- const showWarningAlert = () => {
56
- SweetestAlert({
57
- type: "warning",
58
- title: "Warning",
59
- content: "Are you sure you want to proceed with this action?",
60
- confirm: () => console.log("User confirmed warning"),
61
- cancel: () => console.log("User cancelled warning"),
62
- })
63
- }
64
-
65
- const showErrorAlert = () => {
66
- SweetestAlert({
67
- type: "error",
68
- title: "Error Occurred",
69
- content: "Something went wrong. Please try again.",
70
- hideCancel: true,
71
- })
72
- }
73
-
74
- const showDangerAlert = () => {
75
- SweetestAlert({
76
- type: "danger",
77
- title: "Delete Item",
78
- content: "Are you sure you want to delete this item? This action cannot be undone.",
79
- confirmButton: "Delete",
80
- confirm: () => console.log("User confirmed deletion"),
81
- cancel: () => console.log("User cancelled deletion"),
82
- })
83
- }
84
-
85
- const showCustomContentAlert = () => {
86
- SweetestAlert({
87
- type: "info",
88
- title: "Custom Content",
89
- content: (
90
- <>
91
- <p>You can pass custom React components as content.</p>
92
- <ul>
93
- <li>Item one</li>
94
- <li>Item two</li>
95
- <li>Item three</li>
96
- </ul>
97
- <p>This allows for rich, formatted content in your alerts.</p>
98
- </>
99
- ),
100
- confirm: () => console.log("Custom content confirmed"),
101
- cancel: () => console.log("Custom content cancelled"),
102
- })
103
- }
104
-
105
- return (
106
- <>
107
- <div className="container">
108
- <h1>Sweetest Alert Demo</h1>
109
- <h2>Click the buttons below to test different alert types</h2>
110
-
111
- <div className="button-grid">
112
- <button className="demo-button info" onClick={showInfoAlert}>
113
- <h3>Info Alert</h3>
114
- Simple notification
115
- </button>
116
-
117
- <button className="demo-button success" onClick={showSuccessAlert}>
118
- <h3>Success Alert</h3>
119
- Success message
120
- </button>
121
-
122
- <button className="demo-button warning" onClick={showWarningAlert}>
123
- <h3>Warning Alert</h3>
124
- With cancel option
125
- </button>
126
-
127
- <button className="demo-button error" onClick={showErrorAlert}>
128
- <h3>Error Alert</h3>
129
- Error notification
130
- </button>
131
-
132
- <button className="demo-button danger" onClick={showDangerAlert}>
133
- <h3>Danger Alert</h3>
134
- Destructive action
135
- </button>
136
-
137
- <button className="demo-button custom" onClick={showCustomContentAlert}>
138
- <h3>Custom Content</h3>
139
- Rich content example
140
- </button>
141
- </div>
142
-
143
- <div className="tip">
144
- <strong>Tip:</strong> Open your browser console to see the callback logs when you confirm or cancel alerts.
145
- </div>
146
- </div>
147
-
148
- <div className="container">
149
- <h1>Documentation</h1>
150
-
151
- <h2>Installation</h2>
152
- <CodeBlock code="yarn add @planningcenter/sweetest-alert" language="bash" />
153
-
154
- <h3>Peer Dependencies</h3>
155
- <p>This package requires the following peer dependencies:</p>
156
- <ul>
157
- <li>react ^18.3.1</li>
158
- <li>react-dom ^18.3.1</li>
159
- <li>@planningcenter/tapestry ^2.3.0</li>
160
- <li>@planningcenter/icons ^15.25.0</li>
161
- </ul>
162
-
163
- <h2>Usage</h2>
164
-
165
- <h3>Basic Example</h3>
166
- <CodeBlock code={`import { SweetestAlert } from '@planningcenter/sweetest-alert'
167
-
168
- // Simple notification
169
- SweetestAlert({
170
- title: "Warning!",
171
- content: "Watch out, he's coming to get you!",
172
- })`} />
173
-
174
- <h3>Confirmation Dialog</h3>
175
- <CodeBlock code={`SweetestAlert({
176
- type: "danger",
177
- title: "Delete Item",
178
- content: "Are you sure you want to delete this item?",
179
- confirm: () => console.log("Confirmed"),
180
- cancel: () => console.log("Cancelled")
181
- })`} />
182
-
183
- <h3>Alert Types</h3>
184
- <p>The component supports five visual types:</p>
185
- <CodeBlock code={`// Info alert
186
- SweetestAlert({
187
- type: "info",
188
- title: "Information",
189
- content: "This is an informational message."
190
- })
191
-
192
- // Success alert
193
- SweetestAlert({
194
- type: "success",
195
- title: "Success!",
196
- content: "Operation completed successfully."
197
- })
198
-
199
- // Warning alert (default)
200
- SweetestAlert({
201
- type: "warning",
202
- title: "Warning",
203
- content: "Please proceed with caution."
204
- })
205
-
206
- // Error alert
207
- SweetestAlert({
208
- type: "error",
209
- title: "Error",
210
- content: "Something went wrong."
211
- })
212
-
213
- // Danger alert (for destructive actions)
214
- SweetestAlert({
215
- type: "danger",
216
- title: "Delete Account",
217
- content: "This action cannot be undone.",
218
- confirmButton: "Delete"
219
- })`} />
220
-
221
- <h3>Custom Content</h3>
222
- <p>You can pass React components as content for rich formatting:</p>
223
- <CodeBlock code={`SweetestAlert({
224
- title: "Custom Content",
225
- content: (
226
- <>
227
- <p>You can include any React elements:</p>
228
- <ul>
229
- <li>Lists</li>
230
- <li>Links</li>
231
- <li>Formatted text</li>
232
- </ul>
233
- </>
234
- )
235
- })`} />
236
-
237
- <h2>API</h2>
238
- <table>
239
- <thead>
240
- <tr>
241
- <th>Parameter</th>
242
- <th>Type</th>
243
- <th>Required</th>
244
- <th>Default</th>
245
- <th>Description</th>
246
- </tr>
247
- </thead>
248
- <tbody>
249
- <tr>
250
- <td><code>title</code></td>
251
- <td><code>string</code></td>
252
- <td>Yes</td>
253
- <td>-</td>
254
- <td>The main heading text displayed at the top of the modal</td>
255
- </tr>
256
- <tr>
257
- <td><code>content</code></td>
258
- <td><code>string | React.ReactNode</code></td>
259
- <td>Yes</td>
260
- <td>-</td>
261
- <td>The body content. Accepts plain text or React components</td>
262
- </tr>
263
- <tr>
264
- <td><code>type</code></td>
265
- <td><code>&quot;info&quot; | &quot;success&quot; | &quot;error&quot; | &quot;danger&quot; | &quot;warning&quot;</code></td>
266
- <td>No</td>
267
- <td>&quot;warning&quot;</td>
268
- <td>The visual type of alert, affects icon and styling</td>
269
- </tr>
270
- <tr>
271
- <td><code>confirm</code></td>
272
- <td><code>() =&gt; void</code></td>
273
- <td>No</td>
274
- <td>-</td>
275
- <td>Callback executed when confirm button is clicked</td>
276
- </tr>
277
- <tr>
278
- <td><code>cancel</code></td>
279
- <td><code>() =&gt; void</code></td>
280
- <td>No</td>
281
- <td>-</td>
282
- <td>Callback executed when cancel button is clicked</td>
283
- </tr>
284
- <tr>
285
- <td><code>confirmButton</code></td>
286
- <td><code>string</code></td>
287
- <td>No</td>
288
- <td>&quot;Okay&quot;</td>
289
- <td>Custom text for the confirm button</td>
290
- </tr>
291
- <tr>
292
- <td><code>hideCancel</code></td>
293
- <td><code>boolean</code></td>
294
- <td>No</td>
295
- <td>false</td>
296
- <td>When true, hides the cancel button for simple notifications</td>
297
- </tr>
298
- </tbody>
299
- </table>
300
- </div>
301
- </>
302
- )
303
- }
304
-
305
- const root = createRoot(document.getElementById("root")!)
306
- root.render(<Demo />)
package/docs/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍬</text></svg>" />
7
- <title>Sweetest Alert Demo</title>
8
- </head>
9
- <body>
10
- <div id="root"></div>
11
- <script type="module" src="./demo.tsx"></script>
12
- </body>
13
- </html>
package/eslint.config.js DELETED
@@ -1,56 +0,0 @@
1
- import js from '@eslint/js';
2
- import tseslint from 'typescript-eslint';
3
- import react from 'eslint-plugin-react';
4
- import reactHooks from 'eslint-plugin-react-hooks';
5
-
6
- export default tseslint.config(
7
- js.configs.recommended,
8
- ...tseslint.configs.recommended,
9
- {
10
- files: ['**/*.{js,jsx,ts,tsx}'],
11
- plugins: {
12
- react,
13
- 'react-hooks': reactHooks,
14
- },
15
- languageOptions: {
16
- parserOptions: {
17
- ecmaFeatures: {
18
- jsx: true,
19
- },
20
- },
21
- },
22
- settings: {
23
- react: {
24
- version: 'detect',
25
- },
26
- },
27
- rules: {
28
- ...react.configs.recommended.rules,
29
- ...react.configs['jsx-runtime'].rules,
30
- ...reactHooks.configs.recommended.rules,
31
- 'eqeqeq': ['error', 'always'],
32
- 'no-console': 'warn',
33
- 'no-underscore-dangle': 'error',
34
- 'no-unused-vars': 'off',
35
- '@typescript-eslint/no-unused-vars': ['error', {
36
- 'argsIgnorePattern': '^_',
37
- 'varsIgnorePattern': '^_',
38
- }],
39
- 'react/no-deprecated': 'error',
40
- 'semi': ['error', 'never'],
41
- },
42
- },
43
- {
44
- files: ['tests/**/*.{js,jsx,ts,tsx}'],
45
- languageOptions: {
46
- globals: {
47
- document: 'readonly',
48
- window: 'readonly',
49
- Event: 'readonly',
50
- },
51
- },
52
- },
53
- {
54
- ignores: ['dist/', 'node_modules/', '*.config.ts', '*.config.js'],
55
- }
56
- );
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export { SweetestAlert } from "./sweetest_alert.tsx"
@@ -1,130 +0,0 @@
1
- import React from "react"
2
- import { createRoot } from "react-dom/client"
3
- import { Button } from "@planningcenter/tapestry"
4
- import {
5
- checkCircle,
6
- xCircle,
7
- infoCircle,
8
- exclamationTriangle,
9
- } from "@planningcenter/icons/paths/general"
10
-
11
- // A modal dialog component that replaces SweetAlert2 with a custom implementation.
12
- // Creates and manages its own DOM dialog element with React rendering.
13
- //
14
- // Basic Usage
15
- //
16
- // SweetestAlert({
17
- // title: "Delete Item",
18
- // content: "Are you sure you want to delete this item?",
19
- // confirm: () => console.log("Confirmed"),
20
- // cancel: () => console.log("Cancelled")
21
- // });
22
-
23
- type AlertTypes = "info" | "success" | "error" | "danger" | "warning"
24
-
25
- export function SweetestAlert({
26
- // Optional: Callback function executed when cancel button is clicked
27
- cancel,
28
-
29
- // Optional: Callback function executed when confirm button is clicked
30
- confirm,
31
-
32
- // Optional: Text for the confirm button. Defaults to "Okay".
33
- confirmButton = "Okay",
34
-
35
- // Required: The body content explaining the alert.
36
- // Accepts a React fragment or a string of text.
37
- content,
38
-
39
- // Optional: When true, hides the cancel button for simple notifications.
40
- // Defaults to "false".
41
- hideCancel = false,
42
-
43
- // Required: The main heading text displayed at the top of the modal
44
- title,
45
-
46
- // Optional: The visual type of alert, affects icon and styling.
47
- // Values can be "info", "success", "error", "danger", or "warning" (default).
48
- type = "warning",
49
- }: {
50
- cancel?: () => void
51
- confirm?: () => void
52
- confirmButton?: string
53
- content: string | React.ReactNode
54
- hideCancel?: boolean
55
- title: string
56
- type?: AlertTypes
57
- }) {
58
- const dialog = document.createElement("dialog")
59
- dialog.className = "pco-sweetest-alert"
60
- document.body.appendChild(dialog)
61
- const root = createRoot(dialog)
62
-
63
- const cleanup = () => {
64
- root.unmount()
65
- document.body.removeChild(dialog)
66
- }
67
-
68
- const handleConfirm = () => {
69
- cleanup()
70
- confirm?.()
71
- }
72
-
73
- const handleCancel = () => {
74
- cleanup()
75
- cancel?.()
76
- }
77
-
78
- const iconPath = () => {
79
- switch (type) {
80
- case "success":
81
- return checkCircle
82
- case "error":
83
- return xCircle
84
- case "info":
85
- return infoCircle
86
- case "danger":
87
- case "warning":
88
- return exclamationTriangle
89
- }
90
- }
91
-
92
- root.render(
93
- <>
94
- <svg
95
- className={`pco-sweetest-alert__icon pco-sweetest-alert--${type}`}
96
- width="48"
97
- height="48"
98
- viewBox="0 0 16 16"
99
- aria-hidden="true"
100
- >
101
- <path d={iconPath()} />
102
- </svg>
103
-
104
- <h2 className="pco-sweetest-alert__title">{title}</h2>
105
-
106
- <div className="pco-sweetest-alert__body">
107
- {typeof content === "string" ? <p>{content}</p> : content}
108
- </div>
109
-
110
- <div className="pco-sweetest-alert__actions">
111
- {!hideCancel && <Button label="Cancel" kind="ghost" onClick={handleCancel} />}
112
- <Button
113
- label={confirmButton}
114
- kind={type === "danger" ? "delete" : "primary"}
115
- onClick={handleConfirm}
116
- />
117
- </div>
118
- </>
119
- )
120
-
121
- dialog.showModal()
122
-
123
- dialog.addEventListener("close", handleCancel)
124
-
125
- return {
126
- show: () => dialog.showModal(),
127
- close: () => dialog.close(),
128
- cleanup,
129
- }
130
- }