@rokku-x/react-hook-dialog 0.0.2 → 1.0.2
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/LICENSE +21 -0
- package/README.md +63 -3
- package/dist/LICENSE +21 -0
- package/dist/README.md +704 -0
- package/dist/components/ModalWindow.d.ts +1 -0
- package/dist/constants/theme.d.ts +5 -0
- package/dist/hooks/useHookDialog.d.ts +7 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +130 -81
- package/dist/store/dialogStore.d.ts +6 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/utils/utils.d.ts +24 -1
- package/package.json +9 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rokku-x
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# react-hook-dialog
|
|
2
2
|
|
|
3
|
+
[](https://github.com/rokku-x/react-hook-dialog/actions/workflows/ci.yml)
|
|
4
|
+
|
|
3
5
|
A powerful and flexible React dialog hook library for confirmation dialogs, alerts, and modals. Built on top of `@rokku-x/react-hook-modal` with a focus on dialog-specific features like action buttons, variants, and customizable styling.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
@@ -131,6 +133,7 @@ Configuration for individual dialog calls.
|
|
|
131
133
|
| `classNames` | `DialogClassNames` | Custom CSS classes for elements |
|
|
132
134
|
| `styles` | `DialogStyles` | Custom inline styles for elements |
|
|
133
135
|
| `variantStyles` | `DialogVariantStyles` | Custom variant button styles |
|
|
136
|
+
| `isReturnSubmit` | `boolean` | When `true` and `content` is a `<form>`, clicking a submit action returns the serialized form values as the dialog result |
|
|
134
137
|
|
|
135
138
|
### ModalAction
|
|
136
139
|
|
|
@@ -140,11 +143,14 @@ Individual action button configuration.
|
|
|
140
143
|
|---|---|---|---|
|
|
141
144
|
| `title` | `React.ReactNode` | ✓ | Button label (string or React element) |
|
|
142
145
|
| `value` | `ValidValue` | | Value to return when clicked |
|
|
143
|
-
| `isCancel` | `boolean` | | Treat as cancel button (respects rejectOnCancel) |
|
|
146
|
+
| `isCancel` | `boolean` | | Treat as cancel button (respects `rejectOnCancel`) |
|
|
144
147
|
| `isOnLeft` | `boolean` | | Position button on left side of row |
|
|
148
|
+
| `isFocused` | `boolean` | | Request initial focus when the dialog opens (highest focus priority) |
|
|
149
|
+
| `isSubmit` | `boolean` | | Render as `type="submit"` and trigger form submit if `content` is a `<form>` |
|
|
150
|
+
| `noActionReturn` | `boolean` | | Run `onClick` but do _not_ perform default dialog action (`handleAction`) — useful for custom flows |
|
|
145
151
|
| `variant` | `ModalVariant` | | Visual style variant |
|
|
146
|
-
| `className` | `string` | | Additional CSS class |
|
|
147
|
-
| `style` | `React.CSSProperties` | | Custom inline styles |
|
|
152
|
+
| `className` | `string` | | Additional CSS class applied to the button |
|
|
153
|
+
| `style` | `React.CSSProperties` | | Custom inline styles applied to the button (highest style priority) |
|
|
148
154
|
| `onClick` | `((event, action) => void) \| (() => void)` | | Click handler called before default handling |
|
|
149
155
|
|
|
150
156
|
### ModalVariant
|
|
@@ -165,6 +171,23 @@ type ModalVariant = 'primary' | 'secondary' | 'danger' | 'success' | 'warning' |
|
|
|
165
171
|
| `info` | Sky (#0ea5e9) | White |
|
|
166
172
|
| `neutral` | Gray (#6b7280) | White |
|
|
167
173
|
|
|
174
|
+
### Action Flags — Priority & Behavior 🔀
|
|
175
|
+
|
|
176
|
+
- **Focus priority**: `isFocused` (per-action) > close button (if shown) > first action button > dialog container
|
|
177
|
+
- **Click ordering**: `action.onClick` is executed first. Then:
|
|
178
|
+
1. If `isSubmit` is true and `isReturnSubmit` is also enabled (and `content` is a `<form>`), the form is serialized and its values are returned as the dialog result immediately (no native submit is triggered).
|
|
179
|
+
2. Else if `isSubmit` is true, the form's `requestSubmit()` is invoked (useful for native validation flows).
|
|
180
|
+
3. Else if `noActionReturn` is true, the default dialog action is skipped (use to perform custom flows and close the dialog manually).
|
|
181
|
+
4. Otherwise `handleAction` is called and the dialog resolves/rejects using the action's `value`.
|
|
182
|
+
- **isCancel**: marks a cancel action — dialog resolves or rejects according to `rejectOnCancel` and `defaultCancelValue` settings.
|
|
183
|
+
- **Placement**: `isOnLeft` positions the action on the left side; other actions render on the right.
|
|
184
|
+
- **Style & class precedence** (lowest → highest):
|
|
185
|
+
1. built-in `baseVariantStyles` (library defaults)
|
|
186
|
+
2. `ConfirmConfig.variantStyles` (per-call variant overrides)
|
|
187
|
+
3. `ConfirmConfig.styles.actionButton` (per-call default action button styles)
|
|
188
|
+
4. `ModalAction.style` (per-action inline style — highest precedence)
|
|
189
|
+
5. `className` values are appended so per-action `className` and `ConfirmConfig.classNames.actionButton` both apply (per-action classes appear last).
|
|
190
|
+
|
|
168
191
|
### DialogClassNames
|
|
169
192
|
|
|
170
193
|
Customize CSS classes for all elements:
|
|
@@ -564,6 +587,43 @@ const handleFormSubmit = async (formData: any) => {
|
|
|
564
587
|
};
|
|
565
588
|
```
|
|
566
589
|
|
|
590
|
+
### Example 15: Form Dialog Returning Values (isReturnSubmit)
|
|
591
|
+
|
|
592
|
+
```tsx
|
|
593
|
+
const [requestDialog] = useHookDialog();
|
|
594
|
+
|
|
595
|
+
async function openProfileDialog() {
|
|
596
|
+
const result = await requestDialog({
|
|
597
|
+
title: 'Edit Profile',
|
|
598
|
+
content: (
|
|
599
|
+
<form>
|
|
600
|
+
<label>
|
|
601
|
+
Name
|
|
602
|
+
<input name="name" defaultValue="Alice" />
|
|
603
|
+
</label>
|
|
604
|
+
<label>
|
|
605
|
+
Email
|
|
606
|
+
<input name="email" defaultValue="alice@example.com" />
|
|
607
|
+
</label>
|
|
608
|
+
</form>
|
|
609
|
+
),
|
|
610
|
+
actions: [[
|
|
611
|
+
{ title: 'Cancel', isCancel: true },
|
|
612
|
+
// `isSubmit` triggers native submit; when `isReturnSubmit` is enabled on the dialog config, the dialog returns the form values object
|
|
613
|
+
{ title: 'Save', isSubmit: true }
|
|
614
|
+
]],
|
|
615
|
+
isReturnSubmit: true
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
if (result && typeof result === 'object') {
|
|
619
|
+
// `result` is the object built from the submitted form
|
|
620
|
+
console.log('Form values:', result);
|
|
621
|
+
// e.g. result.name, result.email
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
> Note: `isReturnSubmit` overrides `noActionReturn` and returns the serialized form values as the action `value`. `isSubmit` still triggers `requestSubmit()` to allow native validation flows.
|
|
567
627
|
### Example 14: Boolean Result with Custom Values
|
|
568
628
|
|
|
569
629
|
```tsx
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rokku-x
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
# react-hook-dialog
|
|
2
|
+
|
|
3
|
+
[](https://github.com/rokku-x/react-hook-dialog/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
A powerful and flexible React dialog hook library for confirmation dialogs, alerts, and modals. Built on top of `@rokku-x/react-hook-modal` with a focus on dialog-specific features like action buttons, variants, and customizable styling.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎯 **Hook-based API** - Simple and intuitive `useHookDialog()` hook
|
|
10
|
+
- 🎨 **Rich Variants** - 7 button variants (primary, secondary, danger, success, warning, info, neutral)
|
|
11
|
+
- 🧩 **Modular Components** - Composed from reusable Backdrop and DialogWindow components
|
|
12
|
+
- 📝 **Dialog Actions** - Flexible action button system with left/right positioning
|
|
13
|
+
- 💅 **Full Customization** - Injectable className and styles at every level
|
|
14
|
+
- ⌨️ **Rich Configuration** - Default configs with per-call overrides
|
|
15
|
+
- 🎁 **Zero Dependencies** - Only requires React, Zustand, and @rokku-x/react-hook-modal
|
|
16
|
+
- 📱 **TypeScript Support** - Full type safety out of the box
|
|
17
|
+
- ♿ **Backdrop Control** - Configurable backdrop click behavior
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @rokku-x/react-hook-dialog
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
or with yarn:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn add @rokku-x/react-hook-dialog
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### 1. Setup BaseModalRenderer
|
|
34
|
+
|
|
35
|
+
First, add the `BaseModalRenderer` at the root of your application (from `@rokku-x/react-hook-modal`):
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { BaseModalRenderer } from '@rokku-x/react-hook-modal';
|
|
39
|
+
|
|
40
|
+
function App() {
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
<YourComponents />
|
|
44
|
+
<BaseModalRenderer />
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Use useHookDialog Hook
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { useHookDialog } from '@rokku-x/react-hook-dialog';
|
|
54
|
+
|
|
55
|
+
function MyComponent() {
|
|
56
|
+
const [requestDialog] = useHookDialog();
|
|
57
|
+
|
|
58
|
+
const handleConfirm = async () => {
|
|
59
|
+
const result = await requestDialog({
|
|
60
|
+
title: 'Confirm Action',
|
|
61
|
+
content: 'Are you sure you want to proceed?',
|
|
62
|
+
actions: [[
|
|
63
|
+
{ title: 'Cancel', isCancel: true },
|
|
64
|
+
{ title: 'Confirm', variant: 'primary' }
|
|
65
|
+
]]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
console.log('User chose:', result);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return <button onClick={handleConfirm}>Open Dialog</button>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Bundle Size
|
|
76
|
+
|
|
77
|
+
- ESM: ~4.06 kB gzipped (13.48 kB raw)
|
|
78
|
+
- CJS: ~3.48 kB gzipped (9.21 kB raw)
|
|
79
|
+
|
|
80
|
+
Measured with Vite build for v0.0.1.
|
|
81
|
+
|
|
82
|
+
## API Reference
|
|
83
|
+
|
|
84
|
+
### useHookDialog
|
|
85
|
+
|
|
86
|
+
Main hook for displaying confirmation dialogs and alerts.
|
|
87
|
+
|
|
88
|
+
#### Parameters
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
useHookDialog(defaultConfig?: UseHookDialogConfig)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Parameter | Type | Description |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `defaultConfig` | `UseHookDialogConfig` | Optional default configuration applied to all dialogs |
|
|
97
|
+
|
|
98
|
+
#### Returns
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
[requestDialog]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Return Value | Type | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `requestDialog` | `(config: ConfirmConfig) => Promise<ValidValue>` | Function to open a dialog and get user response |
|
|
107
|
+
|
|
108
|
+
#### Default Config Options
|
|
109
|
+
|
|
110
|
+
| Property | Type | Default | Description |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `backdropCancel` | `boolean` | `false` | Allow closing via backdrop click |
|
|
113
|
+
| `rejectOnCancel` | `boolean` | `true` | Reject promise on cancel instead of resolving |
|
|
114
|
+
| `defaultCancelValue` | `ValidValue` | `undefined` | Value to return/reject on cancel |
|
|
115
|
+
| `showCloseButton` | `boolean` | `false` | Show X close button |
|
|
116
|
+
| `classNames` | `DialogClassNames` | `undefined` | Custom CSS classes |
|
|
117
|
+
| `styles` | `DialogStyles` | `undefined` | Custom inline styles |
|
|
118
|
+
| `variantStyles` | `DialogVariantStyles` | `undefined` | Custom variant button styles |
|
|
119
|
+
|
|
120
|
+
### ConfirmConfig
|
|
121
|
+
|
|
122
|
+
Configuration for individual dialog calls.
|
|
123
|
+
|
|
124
|
+
| Property | Type | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `title` | `React.ReactNode` | Dialog title (string or React element) |
|
|
127
|
+
| `content` | `React.ReactNode` | Dialog content (string or React element) |
|
|
128
|
+
| `actions` | `ModalAction[][]` | Array of action button rows |
|
|
129
|
+
| `backdropCancel` | `boolean` | Allow closing via backdrop click |
|
|
130
|
+
| `rejectOnCancel` | `boolean` | Reject promise on cancel |
|
|
131
|
+
| `defaultCancelValue` | `ValidValue` | Value to return/reject on cancel |
|
|
132
|
+
| `showCloseButton` | `boolean` | Show X close button |
|
|
133
|
+
| `classNames` | `DialogClassNames` | Custom CSS classes for elements |
|
|
134
|
+
| `styles` | `DialogStyles` | Custom inline styles for elements |
|
|
135
|
+
| `variantStyles` | `DialogVariantStyles` | Custom variant button styles |
|
|
136
|
+
| `isReturnSubmit` | `boolean` | When `true` and `content` is a `<form>`, clicking a submit action returns the serialized form values as the dialog result |
|
|
137
|
+
|
|
138
|
+
### ModalAction
|
|
139
|
+
|
|
140
|
+
Individual action button configuration.
|
|
141
|
+
|
|
142
|
+
| Property | Type | Required | Description |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| `title` | `React.ReactNode` | ✓ | Button label (string or React element) |
|
|
145
|
+
| `value` | `ValidValue` | | Value to return when clicked |
|
|
146
|
+
| `isCancel` | `boolean` | | Treat as cancel button (respects `rejectOnCancel`) |
|
|
147
|
+
| `isOnLeft` | `boolean` | | Position button on left side of row |
|
|
148
|
+
| `isFocused` | `boolean` | | Request initial focus when the dialog opens (highest focus priority) |
|
|
149
|
+
| `isSubmit` | `boolean` | | Render as `type="submit"` and trigger form submit if `content` is a `<form>` |
|
|
150
|
+
| `noActionReturn` | `boolean` | | Run `onClick` but do _not_ perform default dialog action (`handleAction`) — useful for custom flows |
|
|
151
|
+
| `variant` | `ModalVariant` | | Visual style variant |
|
|
152
|
+
| `className` | `string` | | Additional CSS class applied to the button |
|
|
153
|
+
| `style` | `React.CSSProperties` | | Custom inline styles applied to the button (highest style priority) |
|
|
154
|
+
| `onClick` | `((event, action) => void) \| (() => void)` | | Click handler called before default handling |
|
|
155
|
+
|
|
156
|
+
### ModalVariant
|
|
157
|
+
|
|
158
|
+
Available button variants:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
type ModalVariant = 'primary' | 'secondary' | 'danger' | 'success' | 'warning' | 'info' | 'neutral';
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
| Variant | Color | Text Color |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| `primary` | Blue (#2563eb) | White |
|
|
167
|
+
| `secondary` | Gray (#e5e7eb) | Black |
|
|
168
|
+
| `danger` | Red (#dc2626) | White |
|
|
169
|
+
| `success` | Green (#16a34a) | White |
|
|
170
|
+
| `warning` | Amber (#f59e0b) | Black |
|
|
171
|
+
| `info` | Sky (#0ea5e9) | White |
|
|
172
|
+
| `neutral` | Gray (#6b7280) | White |
|
|
173
|
+
|
|
174
|
+
### Action Flags — Priority & Behavior 🔀
|
|
175
|
+
|
|
176
|
+
- **Focus priority**: `isFocused` (per-action) > close button (if shown) > first action button > dialog container
|
|
177
|
+
- **Click ordering**: `action.onClick` is executed first. Then:
|
|
178
|
+
1. If `isSubmit` is true and `isReturnSubmit` is also enabled (and `content` is a `<form>`), the form is serialized and its values are returned as the dialog result immediately (no native submit is triggered).
|
|
179
|
+
2. Else if `isSubmit` is true, the form's `requestSubmit()` is invoked (useful for native validation flows).
|
|
180
|
+
3. Else if `noActionReturn` is true, the default dialog action is skipped (use to perform custom flows and close the dialog manually).
|
|
181
|
+
4. Otherwise `handleAction` is called and the dialog resolves/rejects using the action's `value`.
|
|
182
|
+
- **isCancel**: marks a cancel action — dialog resolves or rejects according to `rejectOnCancel` and `defaultCancelValue` settings.
|
|
183
|
+
- **Placement**: `isOnLeft` positions the action on the left side; other actions render on the right.
|
|
184
|
+
- **Style & class precedence** (lowest → highest):
|
|
185
|
+
1. built-in `baseVariantStyles` (library defaults)
|
|
186
|
+
2. `ConfirmConfig.variantStyles` (per-call variant overrides)
|
|
187
|
+
3. `ConfirmConfig.styles.actionButton` (per-call default action button styles)
|
|
188
|
+
4. `ModalAction.style` (per-action inline style — highest precedence)
|
|
189
|
+
5. `className` values are appended so per-action `className` and `ConfirmConfig.classNames.actionButton` both apply (per-action classes appear last).
|
|
190
|
+
|
|
191
|
+
### DialogClassNames
|
|
192
|
+
|
|
193
|
+
Customize CSS classes for all elements:
|
|
194
|
+
|
|
195
|
+
| Property | Type | Description |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `backdrop` | `string` | Backdrop overlay |
|
|
198
|
+
| `dialog` | `string` | Dialog container |
|
|
199
|
+
| `closeButton` | `string` | Close button |
|
|
200
|
+
| `title` | `string` | Title element |
|
|
201
|
+
| `content` | `string` | Content container |
|
|
202
|
+
| `actions` | `string` | Actions container |
|
|
203
|
+
| `actionsRow` | `string` | Individual action row |
|
|
204
|
+
| `actionButton` | `string` | Action button |
|
|
205
|
+
|
|
206
|
+
### DialogStyles
|
|
207
|
+
|
|
208
|
+
Customize inline styles for all elements:
|
|
209
|
+
|
|
210
|
+
| Property | Type | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `backdrop` | `React.CSSProperties` | Backdrop overlay styles |
|
|
213
|
+
| `dialog` | `React.CSSProperties` | Dialog container styles |
|
|
214
|
+
| `closeButton` | `React.CSSProperties` | Close button styles |
|
|
215
|
+
| `title` | `React.CSSProperties` | Title element styles |
|
|
216
|
+
| `content` | `React.CSSProperties` | Content container styles |
|
|
217
|
+
| `actions` | `React.CSSProperties` | Actions container styles |
|
|
218
|
+
| `actionsRow` | `React.CSSProperties` | Action row styles |
|
|
219
|
+
| `actionButton` | `React.CSSProperties` | Action button styles |
|
|
220
|
+
|
|
221
|
+
## Components
|
|
222
|
+
|
|
223
|
+
### Backdrop
|
|
224
|
+
|
|
225
|
+
Overlay component that wraps dialog windows.
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
<Backdrop
|
|
229
|
+
onClick={() => handleClose()}
|
|
230
|
+
className="custom-backdrop"
|
|
231
|
+
style={{ backdropFilter: 'blur(5px)' }}
|
|
232
|
+
>
|
|
233
|
+
{children}
|
|
234
|
+
</Backdrop>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### DialogWindow
|
|
238
|
+
|
|
239
|
+
Main dialog container component.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
<DialogWindow
|
|
243
|
+
className="custom-dialog"
|
|
244
|
+
style={{ backgroundColor: '#f5f5f5' }}
|
|
245
|
+
>
|
|
246
|
+
{children}
|
|
247
|
+
</DialogWindow>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Examples
|
|
251
|
+
|
|
252
|
+
### Example 1: Basic Confirmation Dialog
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { useHookDialog } from '@rokku-x/react-hook-dialog';
|
|
256
|
+
|
|
257
|
+
function DeleteConfirm() {
|
|
258
|
+
const [requestDialog] = useHookDialog();
|
|
259
|
+
|
|
260
|
+
const handleDelete = async () => {
|
|
261
|
+
const result = await requestDialog({
|
|
262
|
+
title: 'Delete Item?',
|
|
263
|
+
content: 'This action cannot be undone.',
|
|
264
|
+
actions: [[
|
|
265
|
+
{ title: 'Cancel', isCancel: true, variant: 'secondary' },
|
|
266
|
+
{ title: 'Delete', variant: 'danger', value: true }
|
|
267
|
+
]]
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (result === true) {
|
|
271
|
+
console.log('Item deleted!');
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
return <button onClick={handleDelete}>Delete</button>;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Example 2: Multiple Action Rows
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
const [requestDialog] = useHookDialog();
|
|
283
|
+
|
|
284
|
+
await requestDialog({
|
|
285
|
+
title: 'Choose Action',
|
|
286
|
+
content: 'What would you like to do?',
|
|
287
|
+
actions: [
|
|
288
|
+
[{ title: 'Back', isOnLeft: true, variant: 'secondary' }],
|
|
289
|
+
[
|
|
290
|
+
{ title: 'Cancel', isCancel: true },
|
|
291
|
+
{ title: 'Save', variant: 'primary' }
|
|
292
|
+
]
|
|
293
|
+
]
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Example 3: Custom Styling
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
const [requestDialog] = useHookDialog({
|
|
301
|
+
styles: {
|
|
302
|
+
dialog: {
|
|
303
|
+
borderRadius: '20px',
|
|
304
|
+
backgroundColor: '#f9fafb'
|
|
305
|
+
},
|
|
306
|
+
actionButton: {
|
|
307
|
+
fontWeight: 'bold'
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
classNames: {
|
|
311
|
+
dialog: 'my-custom-dialog'
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await requestDialog({
|
|
316
|
+
title: 'Styled Dialog',
|
|
317
|
+
content: 'This dialog has custom styles'
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Example 4: Custom Button Variants
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
const [requestDialog] = useHookDialog({
|
|
325
|
+
variantStyles: {
|
|
326
|
+
primary: {
|
|
327
|
+
backgroundColor: '#7c3aed', // Purple
|
|
328
|
+
color: '#fff'
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
await requestDialog({
|
|
334
|
+
title: 'Custom Colors',
|
|
335
|
+
actions: [[
|
|
336
|
+
{ title: 'Confirm', variant: 'primary' }
|
|
337
|
+
]]
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Example 5: Button Click Handlers
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
await requestDialog({
|
|
345
|
+
title: 'Action Dialog',
|
|
346
|
+
actions: [[
|
|
347
|
+
{
|
|
348
|
+
title: 'Log to Console',
|
|
349
|
+
onClick: (e) => console.log('Button clicked!'),
|
|
350
|
+
variant: 'info'
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
title: 'Proceed',
|
|
354
|
+
variant: 'primary'
|
|
355
|
+
}
|
|
356
|
+
]]
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Example 6: Rich Content
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
await requestDialog({
|
|
364
|
+
title: <span style={{ color: 'blue' }}>Custom <strong>Title</strong></span>,
|
|
365
|
+
content: (
|
|
366
|
+
<div>
|
|
367
|
+
<p>This dialog has rich content:</p>
|
|
368
|
+
<ul>
|
|
369
|
+
<li>Item 1</li>
|
|
370
|
+
<li>Item 2</li>
|
|
371
|
+
</ul>
|
|
372
|
+
</div>
|
|
373
|
+
),
|
|
374
|
+
actions: [[
|
|
375
|
+
{ title: 'OK', variant: 'primary' }
|
|
376
|
+
]]
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Example 7: Default Configuration
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
const [requestDialog] = useHookDialog({
|
|
384
|
+
showCloseButton: false,
|
|
385
|
+
backdropCancel: false,
|
|
386
|
+
styles: {
|
|
387
|
+
dialog: { maxWidth: '500px' }
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// All subsequent dialogs will use these defaults
|
|
392
|
+
await requestDialog({
|
|
393
|
+
title: 'Will use defaults',
|
|
394
|
+
content: 'No close button, backdrop click disabled'
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Example 8: Alert Dialog
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
async function showAlert(message: string) {
|
|
402
|
+
const [requestDialog] = useHookDialog();
|
|
403
|
+
|
|
404
|
+
await requestDialog({
|
|
405
|
+
title: 'Alert',
|
|
406
|
+
content: message,
|
|
407
|
+
actions: [[
|
|
408
|
+
{ title: 'OK', variant: 'primary' }
|
|
409
|
+
]]
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
showAlert('Operation completed successfully!');
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Example 9: Multiple Choice with Different Values
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
const [requestDialog] = useHookDialog();
|
|
420
|
+
|
|
421
|
+
const handleSaveOptions = async () => {
|
|
422
|
+
const result = await requestDialog({
|
|
423
|
+
title: 'Save Options',
|
|
424
|
+
content: 'How would you like to save?',
|
|
425
|
+
actions: [[
|
|
426
|
+
{ title: 'Cancel', isCancel: true },
|
|
427
|
+
{ title: 'Save Draft', variant: 'secondary', value: 'draft' },
|
|
428
|
+
{ title: 'Publish', variant: 'primary', value: 'publish' }
|
|
429
|
+
]]
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
if (result === 'draft') {
|
|
433
|
+
console.log('Saving as draft...');
|
|
434
|
+
} else if (result === 'publish') {
|
|
435
|
+
console.log('Publishing...');
|
|
436
|
+
} else {
|
|
437
|
+
console.log('Cancelled');
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Example 10: Numeric Rating Dialog
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
const [requestDialog] = useHookDialog();
|
|
446
|
+
|
|
447
|
+
const handleRating = async () => {
|
|
448
|
+
const rating = await requestDialog({
|
|
449
|
+
title: 'Rate Your Experience',
|
|
450
|
+
content: 'How would you rate our service?',
|
|
451
|
+
actions: [
|
|
452
|
+
[
|
|
453
|
+
{ title: '1 Star', variant: 'danger', value: 1 },
|
|
454
|
+
{ title: '2 Stars', variant: 'warning', value: 2 },
|
|
455
|
+
{ title: '3 Stars', variant: 'neutral', value: 3 }
|
|
456
|
+
],
|
|
457
|
+
[
|
|
458
|
+
{ title: '4 Stars', variant: 'info', value: 4 },
|
|
459
|
+
{ title: '5 Stars', variant: 'success', value: 5 }
|
|
460
|
+
]
|
|
461
|
+
],
|
|
462
|
+
showCloseButton: false,
|
|
463
|
+
backdropCancel: false
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
console.log(`User rated: ${rating} stars`);
|
|
467
|
+
// Send rating to API
|
|
468
|
+
};
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Example 11: Conditional Actions Based on Result
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
const [requestDialog] = useHookDialog();
|
|
475
|
+
|
|
476
|
+
const handleFileOperation = async () => {
|
|
477
|
+
const action = await requestDialog({
|
|
478
|
+
title: 'File Actions',
|
|
479
|
+
content: 'What would you like to do with this file?',
|
|
480
|
+
actions: [[
|
|
481
|
+
{ title: 'Download', variant: 'info', value: 'download' },
|
|
482
|
+
{ title: 'Share', variant: 'primary', value: 'share' },
|
|
483
|
+
{ title: 'Delete', variant: 'danger', value: 'delete', isOnLeft: true }
|
|
484
|
+
]]
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
switch (action) {
|
|
488
|
+
case 'download':
|
|
489
|
+
// Download file logic
|
|
490
|
+
window.location.href = '/api/download/file.pdf';
|
|
491
|
+
break;
|
|
492
|
+
case 'share':
|
|
493
|
+
// Open share dialog
|
|
494
|
+
await requestDialog({
|
|
495
|
+
title: 'Share File',
|
|
496
|
+
content: 'File link copied to clipboard!',
|
|
497
|
+
actions: [[{ title: 'OK', variant: 'primary' }]]
|
|
498
|
+
});
|
|
499
|
+
break;
|
|
500
|
+
case 'delete':
|
|
501
|
+
// Confirm deletion
|
|
502
|
+
const confirm = await requestDialog({
|
|
503
|
+
title: 'Confirm Delete',
|
|
504
|
+
content: 'Are you sure? This cannot be undone.',
|
|
505
|
+
actions: [[
|
|
506
|
+
{ title: 'Cancel', isCancel: true },
|
|
507
|
+
{ title: 'Delete', variant: 'danger', value: true }
|
|
508
|
+
]]
|
|
509
|
+
});
|
|
510
|
+
if (confirm) {
|
|
511
|
+
console.log('File deleted');
|
|
512
|
+
}
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Example 12: Handle Cancel vs Reject
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
const [requestDialog] = useHookDialog({
|
|
522
|
+
rejectOnCancel: true // Reject promise on cancel
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
const handleWithErrorHandling = async () => {
|
|
526
|
+
try {
|
|
527
|
+
const result = await requestDialog({
|
|
528
|
+
title: 'Important Action',
|
|
529
|
+
content: 'This requires your confirmation.',
|
|
530
|
+
actions: [[
|
|
531
|
+
{ title: 'Cancel', isCancel: true },
|
|
532
|
+
{ title: 'Proceed', variant: 'primary', value: 'proceed' }
|
|
533
|
+
]]
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (result === 'proceed') {
|
|
537
|
+
console.log('User proceeded');
|
|
538
|
+
// Perform action
|
|
539
|
+
}
|
|
540
|
+
} catch (error) {
|
|
541
|
+
console.log('User cancelled or closed dialog');
|
|
542
|
+
// Handle cancellation
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Example 13: Form Submission with Validation
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
const [requestDialog] = useHookDialog();
|
|
551
|
+
|
|
552
|
+
const handleFormSubmit = async (formData: any) => {
|
|
553
|
+
const action = await requestDialog({
|
|
554
|
+
title: 'Review Changes',
|
|
555
|
+
content: (
|
|
556
|
+
<div>
|
|
557
|
+
<p>You are about to submit the following changes:</p>
|
|
558
|
+
<ul>
|
|
559
|
+
<li>Name: {formData.name}</li>
|
|
560
|
+
<li>Email: {formData.email}</li>
|
|
561
|
+
</ul>
|
|
562
|
+
</div>
|
|
563
|
+
),
|
|
564
|
+
actions: [[
|
|
565
|
+
{ title: 'Edit', variant: 'secondary', value: 'edit' },
|
|
566
|
+
{ title: 'Cancel', isCancel: true },
|
|
567
|
+
{ title: 'Submit', variant: 'success', value: 'submit' }
|
|
568
|
+
]]
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
if (action === 'submit') {
|
|
572
|
+
// Submit form
|
|
573
|
+
const response = await fetch('/api/submit', {
|
|
574
|
+
method: 'POST',
|
|
575
|
+
body: JSON.stringify(formData)
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
await requestDialog({
|
|
579
|
+
title: 'Success',
|
|
580
|
+
content: 'Your changes have been saved!',
|
|
581
|
+
actions: [[{ title: 'OK', variant: 'success' }]]
|
|
582
|
+
});
|
|
583
|
+
} else if (action === 'edit') {
|
|
584
|
+
// Return to form
|
|
585
|
+
console.log('User wants to edit');
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Example 15: Form Dialog Returning Values (isReturnSubmit)
|
|
591
|
+
|
|
592
|
+
```tsx
|
|
593
|
+
const [requestDialog] = useHookDialog();
|
|
594
|
+
|
|
595
|
+
async function openProfileDialog() {
|
|
596
|
+
const result = await requestDialog({
|
|
597
|
+
title: 'Edit Profile',
|
|
598
|
+
content: (
|
|
599
|
+
<form>
|
|
600
|
+
<label>
|
|
601
|
+
Name
|
|
602
|
+
<input name="name" defaultValue="Alice" />
|
|
603
|
+
</label>
|
|
604
|
+
<label>
|
|
605
|
+
Email
|
|
606
|
+
<input name="email" defaultValue="alice@example.com" />
|
|
607
|
+
</label>
|
|
608
|
+
</form>
|
|
609
|
+
),
|
|
610
|
+
actions: [[
|
|
611
|
+
{ title: 'Cancel', isCancel: true },
|
|
612
|
+
// `isSubmit` triggers native submit; when `isReturnSubmit` is enabled on the dialog config, the dialog returns the form values object
|
|
613
|
+
{ title: 'Save', isSubmit: true }
|
|
614
|
+
]],
|
|
615
|
+
isReturnSubmit: true
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
if (result && typeof result === 'object') {
|
|
619
|
+
// `result` is the object built from the submitted form
|
|
620
|
+
console.log('Form values:', result);
|
|
621
|
+
// e.g. result.name, result.email
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
> Note: `isReturnSubmit` overrides `noActionReturn` and returns the serialized form values as the action `value`. `isSubmit` still triggers `requestSubmit()` to allow native validation flows.
|
|
627
|
+
### Example 14: Boolean Result with Custom Values
|
|
628
|
+
|
|
629
|
+
```tsx
|
|
630
|
+
const [requestDialog] = useHookDialog();
|
|
631
|
+
|
|
632
|
+
const handleLogout = async () => {
|
|
633
|
+
const shouldLogout = await requestDialog({
|
|
634
|
+
title: 'Confirm Logout',
|
|
635
|
+
content: 'Are you sure you want to log out?',
|
|
636
|
+
actions: [[
|
|
637
|
+
{ title: 'Stay Logged In', variant: 'secondary', value: false },
|
|
638
|
+
{ title: 'Log Out', variant: 'danger', value: true }
|
|
639
|
+
]]
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
if (shouldLogout) {
|
|
643
|
+
// Perform logout
|
|
644
|
+
sessionStorage.clear();
|
|
645
|
+
window.location.href = '/login';
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## Best Practices
|
|
651
|
+
|
|
652
|
+
1. **Mount `BaseModalRenderer` at root level** - Required for modals to render
|
|
653
|
+
2. **Use default configs for consistency** - Set common styles/behaviors once
|
|
654
|
+
3. **Provide meaningful button labels** - Users should know what each button does
|
|
655
|
+
4. **Use appropriate variants** - Use `danger` for destructive actions, `success` for confirmations
|
|
656
|
+
5. **Keep content concise** - Dialogs should be focused and brief
|
|
657
|
+
6. **Handle both resolve and reject** - Account for cancellation scenarios
|
|
658
|
+
7. **Use `isOnLeft` for secondary actions** - Helps with visual hierarchy
|
|
659
|
+
8. **Customize responsibly** - Maintain accessibility and usability standards
|
|
660
|
+
|
|
661
|
+
## Types
|
|
662
|
+
|
|
663
|
+
### ValidValue
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
type ValidValue = string | number | boolean | undefined;
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
The type of value returned from dialog actions.
|
|
670
|
+
|
|
671
|
+
### DialogVariantStyles
|
|
672
|
+
|
|
673
|
+
```typescript
|
|
674
|
+
type DialogVariantStyles = Partial<Record<ModalVariant, React.CSSProperties>>;
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Custom styles for each variant type.
|
|
678
|
+
|
|
679
|
+
## Accessibility
|
|
680
|
+
|
|
681
|
+
- Backdrop close can be enabled with `backdropCancel: true`
|
|
682
|
+
- Close button can be shown with `showCloseButton: true`
|
|
683
|
+
- All buttons are keyboard accessible
|
|
684
|
+
- ARIA labels provided for interactive elements
|
|
685
|
+
- Supports custom ARIA attributes via className injection
|
|
686
|
+
|
|
687
|
+
## Troubleshooting
|
|
688
|
+
|
|
689
|
+
### Dialog not appearing
|
|
690
|
+
- Ensure `BaseModalRenderer` is mounted at root level
|
|
691
|
+
- Check that `useHookDialog()` is called within the component tree
|
|
692
|
+
|
|
693
|
+
### Styles not applying
|
|
694
|
+
- Verify className/style props are passed to `ConfirmConfig`
|
|
695
|
+
- Check CSS specificity - inline styles take precedence
|
|
696
|
+
- Use browser dev tools to inspect applied styles
|
|
697
|
+
|
|
698
|
+
### Promise never resolves
|
|
699
|
+
- Ensure action buttons have appropriate `value` or are configured as cancel buttons
|
|
700
|
+
- Check that action click handlers don't prevent default behavior
|
|
701
|
+
|
|
702
|
+
## License
|
|
703
|
+
|
|
704
|
+
MIT
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FormDataObject, ConfirmConfig, UseHookDialogConfig, ValidValue } from '../types';
|
|
1
2
|
/**
|
|
2
3
|
* Hook for displaying confirmation dialogs and alerts.
|
|
3
4
|
*
|
|
@@ -33,4 +34,9 @@
|
|
|
33
34
|
* });
|
|
34
35
|
* ```
|
|
35
36
|
*/
|
|
36
|
-
export default function useHookDialog(defaultConfig?: UseHookDialogConfig):
|
|
37
|
+
export default function useHookDialog<T = ValidValue>(defaultConfig?: UseHookDialogConfig): {
|
|
38
|
+
<U = FormDataObject>(config: ConfirmConfig & {
|
|
39
|
+
isReturnSubmit: true;
|
|
40
|
+
}): Promise<U>;
|
|
41
|
+
<U = T>(config: ConfirmConfig): Promise<U>;
|
|
42
|
+
}[];
|
package/dist/index.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("@rokku-x/react-hook-modal"),p=require("react"),d=require("react/jsx-runtime"),$=require("zustand"),D={primary:{backgroundColor:"#2563eb",color:"#fff"},secondary:{backgroundColor:"#e5e7eb",color:"#111"},danger:{backgroundColor:"#dc2626",color:"#fff"},success:{backgroundColor:"#16a34a",color:"#fff"},warning:{backgroundColor:"#f59e0b",color:"#111"},info:{backgroundColor:"#0ea5e9",color:"#fff"},neutral:{backgroundColor:"#6b7280",color:"#fff"}};function w(t){const u={};return t.forEach((n,s)=>{if(s in u){const a=u[s];Array.isArray(a)?a.push(n):u[s]=[a,n]}else u[s]=n}),u}const A=t=>p.isValidElement(t)&&(t.type==="form"||typeof t.type=="string"&&t.type.toLowerCase()==="form"),q=j.ModalWindow;function O({modalWindowId:t,handleAction:u,handleClose:n,config:s}){const{actions:a=[],title:g,backdropCancel:R,showCloseButton:N,classNames:f={},styles:o={},variantStyles:r={}}=s;let{content:l}=s;const y=(a.length?a:[[{title:"OK",variant:"primary"}]]).filter(c=>c&&c.length),h={...D,...r},x=()=>R&&n(t),M=p.useRef(null),B=p.useRef(null),C=p.useRef(null);return p.useEffect(()=>{const c=M.current;if(!c)return;const k=document.activeElement;if(B.current)B.current.focus();else{const i=c.querySelector("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])");i?i.focus():(c.setAttribute("tabindex","-1"),c.focus())}const S=i=>{if(i.key==="Escape")i.stopPropagation(),n(t);else if(i.key==="Tab"){const b=Array.from(c.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])")).filter(Boolean);if(b.length===0){i.preventDefault();return}const e=b[0],m=b[b.length-1];i.shiftKey?document.activeElement===e&&(i.preventDefault(),m.focus()):document.activeElement===m&&(i.preventDefault(),e.focus())}};return c.addEventListener("keydown",S),()=>{c.removeEventListener("keydown",S),k&&k.focus&&k.focus()}},[n,t]),A(l)&&(l=p.cloneElement(l,{ref:C})),d.jsx(j.ModalBackdrop,{onClick:x,className:f.backdrop||"",style:o.backdrop,children:d.jsxs(q,{ref:M,role:"dialog","aria-modal":"true","aria-labelledby":g?`${t}-title`:void 0,className:f.dialog||"",style:o.dialog,children:[N&&d.jsx("button",{type:"button",className:`hook-dialog-close-button ${f.closeButton||""}`,"aria-label":"Close",onClick:()=>n(t),style:{position:"absolute",top:0,right:0,transform:"translate(75%, -75%)",width:32,height:32,background:"none",border:"none",fontSize:21,cursor:"pointer",lineHeight:1,color:"#555",...o.closeButton},children:"×"}),g&&d.jsx("h3",{id:`${t}-title`,className:`hook-dialog-title ${f.title||""}`,style:{margin:"0 0 15px",fontSize:20,...o.title},children:g}),l&&d.jsx("div",{className:`hook-dialog-content ${f.content||""}`,style:{marginBottom:15,color:"#555",...o.content},children:l}),d.jsx("div",{className:`hook-dialog-actions ${f.actions||""}`,style:{display:"flex",flexDirection:"column",gap:10,paddingTop:15,...o.actions},children:y.map((c,k)=>{const S=c.filter(e=>e.isOnLeft),i=c.filter(e=>!e.isOnLeft),b=(e,m)=>{const E=h[e.variant||"secondary"]||h.secondary;return d.jsx("button",{ref:v=>{e.isFocused&&v&&(B.current=v)},"data-action-focused":e.isFocused?"true":void 0,className:`hook-dialog-action-button hook-dialog-action-${e.variant||"secondary"} ${f.actionButton||""} ${e.className||""}`,onClick:v=>{if(e.onClick?.(v,e),e.isSubmit&&s.isReturnSubmit&&C.current)return u(t,e,w(new FormData(C.current)));if(e.isSubmit&&C.current?.requestSubmit(),e.noActionReturn)return v.stopPropagation();u(t,e)},style:{border:"none",borderRadius:15,padding:"10px 18px",fontSize:14,fontWeight:800,cursor:"pointer",...E,...o.actionButton,...e.style||{}},children:e.title},`${e.title}-${m}`)};return d.jsxs("div",{className:`hook-dialog-actions-row ${f.actionsRow||""}`,style:{display:"flex",gap:8,justifyContent:"space-between",...o.actionsRow},children:[d.jsx("div",{className:"hook-dialog-actions-left",style:{display:"flex",gap:8},children:S.map((e,m)=>b(e,m))}),d.jsx("div",{className:"hook-dialog-actions-right",style:{display:"flex",gap:8},children:i.map((e,m)=>b(e,m))})]},k)})})]})})}const F=$.create((t,u)=>({instances:[],addInstance:n=>t(s=>({instances:[...s.instances,n]})),removeInstance:n=>t(s=>({instances:s.instances.filter(a=>a.id!==n)})),getInstance:n=>u().instances.find(s=>s.id===n)}));function L(t){const{instances:u,addInstance:n,removeInstance:s,getInstance:a}=F(),g=j.useBaseModal(),R=p.useCallback(o=>{const r=a(o);r&&(g.popModal(o),r.config.rejectOnCancel!==!1?r.reject(r.config.defaultCancelValue):r.resolve(r.config.defaultCancelValue),s(o))},[a,s,g]),N=p.useCallback((o,r)=>{const l=a(o);l&&(g.popModal(o),r.isCancel&&l.config.rejectOnCancel!==!1?l.reject(r.value):l.resolve(r.value),s(o))},[a,s,g]);function f(o){return new Promise((r,l)=>{const y=Math.random().toString(36).substring(2,6),h={...t,...o,classNames:{...t?.classNames,...o.classNames},styles:{...t?.styles,...o.styles},variantStyles:{...t?.variantStyles,...o.variantStyles}},x={id:y,config:h,resolve:r,reject:l};n(x),x.id=g.pushModal(y,p.createElement(O,{config:h,modalWindowId:y,handleClose:R,handleAction:N}))})}return[f]}Object.defineProperty(exports,"BaseModalRenderer",{enumerable:!0,get:()=>j.BaseModalRenderer});exports.useHookDialog=L;
|
package/dist/index.esm.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { ModalBackdrop as
|
|
3
|
-
import { BaseModalRenderer as
|
|
4
|
-
import R, { useCallback as
|
|
5
|
-
import { jsx as
|
|
6
|
-
import { create as
|
|
7
|
-
const
|
|
2
|
+
import { ModalBackdrop as A, ModalWindow as j, useBaseModal as F } from "@rokku-x/react-hook-modal";
|
|
3
|
+
import { BaseModalRenderer as X } from "@rokku-x/react-hook-modal";
|
|
4
|
+
import R, { useRef as E, useEffect as O, useCallback as M } from "react";
|
|
5
|
+
import { jsx as p, jsxs as w } from "react/jsx-runtime";
|
|
6
|
+
import { create as L } from "zustand";
|
|
7
|
+
const V = {
|
|
8
8
|
primary: { backgroundColor: "#2563eb", color: "#fff" },
|
|
9
9
|
secondary: { backgroundColor: "#e5e7eb", color: "#111" },
|
|
10
10
|
danger: { backgroundColor: "#dc2626", color: "#fff" },
|
|
@@ -13,29 +13,70 @@ const O = {
|
|
|
13
13
|
info: { backgroundColor: "#0ea5e9", color: "#fff" },
|
|
14
14
|
neutral: { backgroundColor: "#6b7280", color: "#fff" }
|
|
15
15
|
};
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
return
|
|
19
|
-
|
|
16
|
+
function q(t) {
|
|
17
|
+
const u = {};
|
|
18
|
+
return t.forEach((s, n) => {
|
|
19
|
+
if (n in u) {
|
|
20
|
+
const a = u[n];
|
|
21
|
+
Array.isArray(a) ? a.push(s) : u[n] = [a, s];
|
|
22
|
+
} else
|
|
23
|
+
u[n] = s;
|
|
24
|
+
}), u;
|
|
25
|
+
}
|
|
26
|
+
const z = (t) => R.isValidElement(t) && (t.type === "form" || typeof t.type == "string" && t.type.toLowerCase() === "form"), I = j;
|
|
27
|
+
function K({ modalWindowId: t, handleAction: u, handleClose: s, config: n }) {
|
|
28
|
+
const { actions: a = [], title: d, backdropCancel: S, showCloseButton: N, classNames: f = {}, styles: o = {}, variantStyles: r = {} } = n;
|
|
29
|
+
let { content: l } = n;
|
|
30
|
+
const y = (a.length ? a : [[{ title: "OK", variant: "primary" }]]).filter((i) => i && i.length), b = { ...V, ...r }, k = () => S && s(t), $ = E(null), B = E(null), x = E(null);
|
|
31
|
+
return O(() => {
|
|
32
|
+
const i = $.current;
|
|
33
|
+
if (!i) return;
|
|
34
|
+
const h = document.activeElement;
|
|
35
|
+
if (B.current)
|
|
36
|
+
B.current.focus();
|
|
37
|
+
else {
|
|
38
|
+
const c = i.querySelector("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])");
|
|
39
|
+
c ? c.focus() : (i.setAttribute("tabindex", "-1"), i.focus());
|
|
40
|
+
}
|
|
41
|
+
const C = (c) => {
|
|
42
|
+
if (c.key === "Escape")
|
|
43
|
+
c.stopPropagation(), s(t);
|
|
44
|
+
else if (c.key === "Tab") {
|
|
45
|
+
const g = Array.from(i.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])")).filter(Boolean);
|
|
46
|
+
if (g.length === 0) {
|
|
47
|
+
c.preventDefault();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const e = g[0], m = g[g.length - 1];
|
|
51
|
+
c.shiftKey ? document.activeElement === e && (c.preventDefault(), m.focus()) : document.activeElement === m && (c.preventDefault(), e.focus());
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return i.addEventListener("keydown", C), () => {
|
|
55
|
+
i.removeEventListener("keydown", C), h && h.focus && h.focus();
|
|
56
|
+
};
|
|
57
|
+
}, [s, t]), z(l) && (l = R.cloneElement(l, { ref: x })), /* @__PURE__ */ p(
|
|
58
|
+
A,
|
|
20
59
|
{
|
|
21
|
-
onClick:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
children: /* @__PURE__ */ C(
|
|
27
|
-
j,
|
|
60
|
+
onClick: k,
|
|
61
|
+
className: f.backdrop || "",
|
|
62
|
+
style: o.backdrop,
|
|
63
|
+
children: /* @__PURE__ */ w(
|
|
64
|
+
I,
|
|
28
65
|
{
|
|
29
|
-
|
|
30
|
-
|
|
66
|
+
ref: $,
|
|
67
|
+
role: "dialog",
|
|
68
|
+
"aria-modal": "true",
|
|
69
|
+
"aria-labelledby": d ? `${t}-title` : void 0,
|
|
70
|
+
className: f.dialog || "",
|
|
71
|
+
style: o.dialog,
|
|
31
72
|
children: [
|
|
32
|
-
|
|
73
|
+
N && /* @__PURE__ */ p(
|
|
33
74
|
"button",
|
|
34
75
|
{
|
|
35
76
|
type: "button",
|
|
36
|
-
className: `hook-dialog-close-button ${
|
|
77
|
+
className: `hook-dialog-close-button ${f.closeButton || ""}`,
|
|
37
78
|
"aria-label": "Close",
|
|
38
|
-
onClick: () => s(
|
|
79
|
+
onClick: () => s(t),
|
|
39
80
|
style: {
|
|
40
81
|
position: "absolute",
|
|
41
82
|
top: 0,
|
|
@@ -49,23 +90,28 @@ function V({ modalWindowId: a, handleAction: m, handleClose: s, config: n }) {
|
|
|
49
90
|
cursor: "pointer",
|
|
50
91
|
lineHeight: 1,
|
|
51
92
|
color: "#555",
|
|
52
|
-
...
|
|
93
|
+
...o.closeButton
|
|
53
94
|
},
|
|
54
95
|
children: "×"
|
|
55
96
|
}
|
|
56
97
|
),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
return /* @__PURE__ */
|
|
98
|
+
d && /* @__PURE__ */ p("h3", { id: `${t}-title`, className: `hook-dialog-title ${f.title || ""}`, style: { margin: "0 0 15px", fontSize: 20, ...o.title }, children: d }),
|
|
99
|
+
l && /* @__PURE__ */ p("div", { className: `hook-dialog-content ${f.content || ""}`, style: { marginBottom: 15, color: "#555", ...o.content }, children: l }),
|
|
100
|
+
/* @__PURE__ */ p("div", { className: `hook-dialog-actions ${f.actions || ""}`, style: { display: "flex", flexDirection: "column", gap: 10, paddingTop: 15, ...o.actions }, children: y.map((i, h) => {
|
|
101
|
+
const C = i.filter((e) => e.isOnLeft), c = i.filter((e) => !e.isOnLeft), g = (e, m) => {
|
|
102
|
+
const D = b[e.variant || "secondary"] || b.secondary;
|
|
103
|
+
return /* @__PURE__ */ p(
|
|
63
104
|
"button",
|
|
64
105
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
106
|
+
ref: (v) => {
|
|
107
|
+
e.isFocused && v && (B.current = v);
|
|
108
|
+
},
|
|
109
|
+
"data-action-focused": e.isFocused ? "true" : void 0,
|
|
110
|
+
className: `hook-dialog-action-button hook-dialog-action-${e.variant || "secondary"} ${f.actionButton || ""} ${e.className || ""}`,
|
|
111
|
+
onClick: (v) => {
|
|
112
|
+
if (e.onClick?.(v, e), e.isSubmit && n.isReturnSubmit && x.current) return u(t, e, q(new FormData(x.current)));
|
|
113
|
+
if (e.isSubmit && x.current?.requestSubmit(), e.noActionReturn) return v.stopPropagation();
|
|
114
|
+
u(t, e);
|
|
69
115
|
},
|
|
70
116
|
style: {
|
|
71
117
|
border: "none",
|
|
@@ -74,19 +120,19 @@ function V({ modalWindowId: a, handleAction: m, handleClose: s, config: n }) {
|
|
|
74
120
|
fontSize: 14,
|
|
75
121
|
fontWeight: 800,
|
|
76
122
|
cursor: "pointer",
|
|
77
|
-
...
|
|
78
|
-
...
|
|
79
|
-
...
|
|
123
|
+
...D,
|
|
124
|
+
...o.actionButton,
|
|
125
|
+
...e.style || {}
|
|
80
126
|
},
|
|
81
|
-
children:
|
|
127
|
+
children: e.title
|
|
82
128
|
},
|
|
83
|
-
`${
|
|
129
|
+
`${e.title}-${m}`
|
|
84
130
|
);
|
|
85
131
|
};
|
|
86
|
-
return /* @__PURE__ */
|
|
87
|
-
/* @__PURE__ */
|
|
88
|
-
/* @__PURE__ */
|
|
89
|
-
] },
|
|
132
|
+
return /* @__PURE__ */ w("div", { className: `hook-dialog-actions-row ${f.actionsRow || ""}`, style: { display: "flex", gap: 8, justifyContent: "space-between", ...o.actionsRow }, children: [
|
|
133
|
+
/* @__PURE__ */ p("div", { className: "hook-dialog-actions-left", style: { display: "flex", gap: 8 }, children: C.map((e, m) => g(e, m)) }),
|
|
134
|
+
/* @__PURE__ */ p("div", { className: "hook-dialog-actions-right", style: { display: "flex", gap: 8 }, children: c.map((e, m) => g(e, m)) })
|
|
135
|
+
] }, h);
|
|
90
136
|
}) })
|
|
91
137
|
]
|
|
92
138
|
}
|
|
@@ -94,50 +140,53 @@ function V({ modalWindowId: a, handleAction: m, handleClose: s, config: n }) {
|
|
|
94
140
|
}
|
|
95
141
|
);
|
|
96
142
|
}
|
|
97
|
-
const
|
|
143
|
+
const P = L((t, u) => ({
|
|
98
144
|
instances: [],
|
|
99
|
-
addInstance: (s) =>
|
|
145
|
+
addInstance: (s) => t((n) => ({
|
|
100
146
|
instances: [...n.instances, s]
|
|
101
147
|
})),
|
|
102
|
-
removeInstance: (s) =>
|
|
103
|
-
instances: n.instances.filter((
|
|
148
|
+
removeInstance: (s) => t((n) => ({
|
|
149
|
+
instances: n.instances.filter((a) => a.id !== s)
|
|
104
150
|
})),
|
|
105
|
-
getInstance: (s) =>
|
|
151
|
+
getInstance: (s) => u().instances.find((n) => n.id === s)
|
|
106
152
|
}));
|
|
107
|
-
function
|
|
108
|
-
const { instances:
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
}, [
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
}, [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
153
|
+
function Q(t) {
|
|
154
|
+
const { instances: u, addInstance: s, removeInstance: n, getInstance: a } = P(), d = F(), S = M((o) => {
|
|
155
|
+
const r = a(o);
|
|
156
|
+
r && (d.popModal(o), r.config.rejectOnCancel !== !1 ? r.reject(r.config.defaultCancelValue) : r.resolve(r.config.defaultCancelValue), n(o));
|
|
157
|
+
}, [a, n, d]), N = M((o, r) => {
|
|
158
|
+
const l = a(o);
|
|
159
|
+
l && (d.popModal(o), r.isCancel && l.config.rejectOnCancel !== !1 ? l.reject(r.value) : l.resolve(r.value), n(o));
|
|
160
|
+
}, [a, n, d]);
|
|
161
|
+
function f(o) {
|
|
162
|
+
return new Promise((r, l) => {
|
|
163
|
+
const y = Math.random().toString(36).substring(2, 6), b = {
|
|
164
|
+
...t,
|
|
165
|
+
...o,
|
|
166
|
+
classNames: {
|
|
167
|
+
...t?.classNames,
|
|
168
|
+
...o.classNames
|
|
169
|
+
},
|
|
170
|
+
styles: {
|
|
171
|
+
...t?.styles,
|
|
172
|
+
...o.styles
|
|
173
|
+
},
|
|
174
|
+
variantStyles: {
|
|
175
|
+
...t?.variantStyles,
|
|
176
|
+
...o.variantStyles
|
|
177
|
+
}
|
|
178
|
+
}, k = {
|
|
179
|
+
id: y,
|
|
180
|
+
config: b,
|
|
181
|
+
resolve: r,
|
|
182
|
+
reject: l
|
|
183
|
+
};
|
|
184
|
+
s(k), k.id = d.pushModal(y, R.createElement(K, { config: b, modalWindowId: y, handleClose: S, handleAction: N }));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return [f];
|
|
139
188
|
}
|
|
140
189
|
export {
|
|
141
|
-
|
|
142
|
-
|
|
190
|
+
X as BaseModalRenderer,
|
|
191
|
+
Q as useHookDialog
|
|
143
192
|
};
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { ConfirmInstance, ValidValue } from '../types';
|
|
1
2
|
/**
|
|
2
3
|
* Zustand store interface for managing dialog instances.
|
|
3
4
|
* Maintains a centralized list of active dialog instances.
|
|
4
5
|
* @internal
|
|
5
6
|
*/
|
|
6
|
-
interface DialogStore {
|
|
7
|
+
interface DialogStore<T = ValidValue> {
|
|
7
8
|
/** Array of currently active dialog instances */
|
|
8
|
-
instances: ConfirmInstance[];
|
|
9
|
+
instances: ConfirmInstance<T>[];
|
|
9
10
|
/** Add a new dialog instance to the store */
|
|
10
|
-
addInstance: (instance: ConfirmInstance) => void;
|
|
11
|
+
addInstance: (instance: ConfirmInstance<T>) => void;
|
|
11
12
|
/** Remove a dialog instance from the store by ID */
|
|
12
13
|
removeInstance: (id: string) => void;
|
|
13
14
|
/** Get a dialog instance by ID */
|
|
14
|
-
getInstance: (id: string) => ConfirmInstance | undefined;
|
|
15
|
+
getInstance: (id: string) => ConfirmInstance<T> | undefined;
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* Zustand store for managing dialog instances.
|
|
@@ -23,5 +24,5 @@ interface DialogStore {
|
|
|
23
24
|
* const { addInstance, removeInstance, getInstance } = useDialogStore();
|
|
24
25
|
* ```
|
|
25
26
|
*/
|
|
26
|
-
export declare const useDialogStore: import('zustand').UseBoundStore<import('zustand').StoreApi<DialogStore
|
|
27
|
+
export declare const useDialogStore: import('zustand').UseBoundStore<import('zustand').StoreApi<DialogStore<any>>>;
|
|
27
28
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { }
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Value stored in FormData entries (string or File)
|
|
4
|
+
*/
|
|
5
|
+
export type FormDataValue = FormDataEntryValue;
|
|
6
|
+
/**
|
|
7
|
+
* Resulting object from converting FormData to a plain object.
|
|
8
|
+
* Repeated keys become arrays of values.
|
|
9
|
+
*/
|
|
10
|
+
export type FormDataObject = Record<string, FormDataValue | FormDataValue[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Convert a FormData instance into a plain object.
|
|
13
|
+
* Repeated keys become arrays of values.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const data = new FormData();
|
|
17
|
+
* data.append('name', 'Alice');
|
|
18
|
+
* data.append('tags', 'a');
|
|
19
|
+
* data.append('tags', 'b');
|
|
20
|
+
* const obj = FormDataToObject(data);
|
|
21
|
+
* // { name: 'Alice', tags: ['a','b'] }
|
|
22
|
+
*/
|
|
23
|
+
export declare function FormDataToObject<T extends FormDataObject = FormDataObject>(formData: FormData): T;
|
|
24
|
+
export declare const IsForm: (content: React.ReactNode) => boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokku-x/react-hook-dialog",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"author": "rokku-x",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"main": "dist/index.cjs.js",
|
|
10
10
|
"module": "dist/index.esm.js",
|
|
11
11
|
"devDependencies": {
|
|
12
|
+
"@changesets/cli": "^2.29.8",
|
|
12
13
|
"@testing-library/jest-dom": "^6.9.1",
|
|
13
14
|
"@testing-library/react": "^14.3.1",
|
|
14
15
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -20,7 +21,8 @@
|
|
|
20
21
|
"typescript": "^5.9.3",
|
|
21
22
|
"vite": "^7.3.1",
|
|
22
23
|
"vite-plugin-dts": "^4.5.4",
|
|
23
|
-
"vitest": "^1.6.1"
|
|
24
|
+
"vitest": "^1.6.1",
|
|
25
|
+
"copyfiles": "^2.4.1"
|
|
24
26
|
},
|
|
25
27
|
"peerDependencies": {
|
|
26
28
|
"@rokku-x/react-hook-modal": "^0.7.8",
|
|
@@ -69,12 +71,14 @@
|
|
|
69
71
|
"provenance": true
|
|
70
72
|
},
|
|
71
73
|
"scripts": {
|
|
72
|
-
"build": "vite build",
|
|
73
|
-
"prepare": "npm run build",
|
|
74
|
+
"build": "vite build && copyfiles -u 1 \"src/types/**/*.d.ts\" dist/ && copyfiles README.md LICENSE dist/",
|
|
74
75
|
"publish:first": "npm publish --access public --provenance false",
|
|
75
76
|
"dev": "vite",
|
|
76
77
|
"preview": "vite preview",
|
|
77
|
-
"test": "vitest"
|
|
78
|
+
"test": "vitest",
|
|
79
|
+
"changeset": "changeset",
|
|
80
|
+
"version:changeset": "changeset version",
|
|
81
|
+
"publish:changeset": "changeset publish --access public"
|
|
78
82
|
},
|
|
79
83
|
"sideEffects": false,
|
|
80
84
|
"types": "dist/index.d.ts",
|