@rokku-x/react-hook-dialog 1.0.4 → 1.1.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 CHANGED
@@ -96,7 +96,57 @@ useHookDialog(defaultConfig?: UseHookDialogConfig)
96
96
 
97
97
  | Return Value | Type | Description |
98
98
  |---|---|---|
99
- | `requestDialog` | `(config: ConfirmConfig) => Promise<ValidValue>` | Function to open a dialog and get user response |
99
+ | `requestDialog` | `(config: ConfirmConfig) => RequestDialogReturnType<ValidValue>` | Function to open a dialog and get user response. `RequestDialogReturnType<T>` is defined as `Promise<T> & { id: string; context: DialogInstanceContext }` which means the native Promise resolves with `T` while the *returned* value exposes `id` and `context` for programmatic control. |
100
+
101
+ ```typescript
102
+ // Convenience alias shown in the library
103
+ type RequestDialogReturnType<T> = Promise<T> & { id: string; context: DialogInstanceContext };
104
+ ```
105
+
106
+ ### Dialog Context & Force Functions ✅
107
+
108
+ The Promise returned from `requestDialog(...)` is augmented with:
109
+
110
+ - `id: string` — unique identifier for the dialog instance
111
+ - `context: DialogInstanceContext` — helper methods to programmatically control the dialog instance
112
+
113
+ You can access the context either directly from the returned Promise (`promise.context`) or via the second value returned from the hook (`getContext(id)`).
114
+
115
+ Dialog context methods:
116
+
117
+ - `forceCancel(forceReject?: boolean)` — close the dialog as a cancellation. If `forceReject` is `true` the promise will be rejected even if the dialog `rejectOnCancel` config is `false`. Otherwise the usual `rejectOnCancel` behavior applies.
118
+ - `forceAction(action: ModalAction)` — programmatically trigger a specific action (resolve/reject according to the action and dialog config).
119
+ - `forceDefault()` — triggers the dialog's default action (first action marked with `isFocused`). Throws if no default action is defined.
120
+
121
+ Example usage:
122
+
123
+ ```tsx
124
+ const [requestDialog, getContext] = useHookDialog();
125
+
126
+ // open a dialog and get the augmented promise
127
+ const p = requestDialog({
128
+ title: 'Confirm',
129
+ content: 'Proceed?',
130
+ actions: [[
131
+ { title: 'Cancel', isCancel: true },
132
+ { title: 'OK', value: true, isFocused: true }
133
+ ]]
134
+ });
135
+
136
+ console.log('dialog id:', p.id);
137
+
138
+ // force the default action (same as clicking the focused action)
139
+ p.context.forceDefault();
140
+
141
+ // or cancel programmatically (forces reject by default)
142
+ p.context.forceCancel();
143
+
144
+ // you can also use the helper returned from the hook
145
+ const ctx = getContext(p.id);
146
+ ctx.forceAction({ title: 'OK', value: true });
147
+ ```
148
+
149
+ > Note: `forceDefault()` will throw if no action is marked with `isFocused`. Use `forceAction(...)` to explicitly specify which action to run.
100
150
 
101
151
  #### Default Config Options
102
152
 
package/dist/README.md CHANGED
@@ -96,7 +96,57 @@ useHookDialog(defaultConfig?: UseHookDialogConfig)
96
96
 
97
97
  | Return Value | Type | Description |
98
98
  |---|---|---|
99
- | `requestDialog` | `(config: ConfirmConfig) => Promise<ValidValue>` | Function to open a dialog and get user response |
99
+ | `requestDialog` | `(config: ConfirmConfig) => RequestDialogReturnType<ValidValue>` | Function to open a dialog and get user response. `RequestDialogReturnType<T>` is defined as `Promise<T> & { id: string; context: DialogInstanceContext }` which means the native Promise resolves with `T` while the *returned* value exposes `id` and `context` for programmatic control. |
100
+
101
+ ```typescript
102
+ // Convenience alias shown in the library
103
+ type RequestDialogReturnType<T> = Promise<T> & { id: string; context: DialogInstanceContext };
104
+ ```
105
+
106
+ ### Dialog Context & Force Functions ✅
107
+
108
+ The Promise returned from `requestDialog(...)` is augmented with:
109
+
110
+ - `id: string` — unique identifier for the dialog instance
111
+ - `context: DialogInstanceContext` — helper methods to programmatically control the dialog instance
112
+
113
+ You can access the context either directly from the returned Promise (`promise.context`) or via the second value returned from the hook (`getContext(id)`).
114
+
115
+ Dialog context methods:
116
+
117
+ - `forceCancel(forceReject?: boolean)` — close the dialog as a cancellation. If `forceReject` is `true` the promise will be rejected even if the dialog `rejectOnCancel` config is `false`. Otherwise the usual `rejectOnCancel` behavior applies.
118
+ - `forceAction(action: ModalAction)` — programmatically trigger a specific action (resolve/reject according to the action and dialog config).
119
+ - `forceDefault()` — triggers the dialog's default action (first action marked with `isFocused`). Throws if no default action is defined.
120
+
121
+ Example usage:
122
+
123
+ ```tsx
124
+ const [requestDialog, getContext] = useHookDialog();
125
+
126
+ // open a dialog and get the augmented promise
127
+ const p = requestDialog({
128
+ title: 'Confirm',
129
+ content: 'Proceed?',
130
+ actions: [[
131
+ { title: 'Cancel', isCancel: true },
132
+ { title: 'OK', value: true, isFocused: true }
133
+ ]]
134
+ });
135
+
136
+ console.log('dialog id:', p.id);
137
+
138
+ // force the default action (same as clicking the focused action)
139
+ p.context.forceDefault();
140
+
141
+ // or cancel programmatically (forces reject by default)
142
+ p.context.forceCancel();
143
+
144
+ // you can also use the helper returned from the hook
145
+ const ctx = getContext(p.id);
146
+ ctx.forceAction({ title: 'OK', value: true });
147
+ ```
148
+
149
+ > Note: `forceDefault()` will throw if no action is marked with `isFocused`. Use `forceAction(...)` to explicitly specify which action to run.
100
150
 
101
151
  #### Default Config Options
102
152
 
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("react"),a=require("@rokku-x/react-hook-modal"),s=require("../components/ModalWindow.cjs.js"),t=require("../store/dialogStore.cjs.js");exports.default=function(o){const{instances:n,addInstance:l,removeInstance:c,getInstance:r}=t.useDialogStore(),i=a.useBaseModal(),u=e.useCallback(e=>{const a=r(e);a&&(i.popModal(e),!1!==a.config.rejectOnCancel?a.reject(a.config.defaultCancelValue):a.resolve(a.config.defaultCancelValue),c(e))},[r,c,i]),d=e.useCallback((e,a)=>{const s=r(e);s&&(i.popModal(e),a.isCancel&&!1!==s.config.rejectOnCancel?s.reject(a.value):s.resolve(a.value),c(e))},[r,c,i]);return[function(a){return new Promise((t,n)=>{const c=Math.random().toString(36).substring(2,6),r={...o,...a,classNames:{...o?.classNames,...a.classNames},styles:{...o?.styles,...a.styles},variantStyles:{...o?.variantStyles,...a.variantStyles}},f={id:c,config:r,resolve:t,reject:n};l(f),f.id=i.pushModal(c,e.createElement(s.default,{config:r,modalWindowId:c,handleClose:u,handleAction:d}))})}]};
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("@rokku-x/react-hook-modal"),s=require("../store/dialogStore.cjs.js");exports.default=function(o){const{addInstance:t,handleAction:a,handleClose:n,getContext:l}=s.useDialogStore();return e.useBaseModal(),[function(e){const s=Math.random().toString(36).substring(2,6);(e.actions?.flat().filter(e=>e.isFocused)||[]).length>1&&console.warn(`useHookDialog: Multiple actions are marked as isFocused in dialog "${s}". Only one action should be focused.`);const a=e.actions?.some(e=>e.some(e=>e.isCancel)),n=e.backdropCancel??o?.backdropCancel??!a,r={...o,...e,classNames:{...o?.classNames,...e.classNames},styles:{...o?.styles,...e.styles},variantStyles:{...o?.variantStyles,...e.variantStyles},backdropCancel:n},c=new Promise((e,o)=>{t({id:s,config:r,resolve:e,reject:o})});return Object.defineProperties(c,{context:{get:()=>l(s),enumerable:!0,configurable:!1},id:{get:()=>s,enumerable:!0,configurable:!1}}),c},l]};
@@ -1,4 +1,4 @@
1
- import { FormDataObject, ConfirmConfig, UseHookDialogConfig, ValidValue } from '../types';
1
+ import { UseHookDialogConfig, ValidValue, UseHookDialogReturnType } from '../types';
2
2
  /**
3
3
  * Hook for displaying confirmation dialogs and alerts.
4
4
  *
@@ -34,9 +34,4 @@ import { FormDataObject, ConfirmConfig, UseHookDialogConfig, ValidValue } from '
34
34
  * });
35
35
  * ```
36
36
  */
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
- }[];
37
+ export default function useHookDialog<T = ValidValue>(defaultConfig?: UseHookDialogConfig): UseHookDialogReturnType<T>;
@@ -1 +1 @@
1
- import e,{useCallback as o}from"react";import{useBaseModal as t}from"@rokku-x/react-hook-modal";import n from"../components/ModalWindow.esm.js";import{useDialogStore as a}from"../store/dialogStore.esm.js";function s(s){const{instances:l,addInstance:r,removeInstance:c,getInstance:i}=a(),d=t(),m=o(e=>{const o=i(e);o&&(d.popModal(e),!1!==o.config.rejectOnCancel?o.reject(o.config.defaultCancelValue):o.resolve(o.config.defaultCancelValue),c(e))},[i,c,d]),f=o((e,o)=>{const t=i(e);t&&(d.popModal(e),o.isCancel&&!1!==t.config.rejectOnCancel?t.reject(o.value):t.resolve(o.value),c(e))},[i,c,d]);return[function(o){return new Promise((t,a)=>{const l=Math.random().toString(36).substring(2,6),c={...s,...o,classNames:{...s?.classNames,...o.classNames},styles:{...s?.styles,...o.styles},variantStyles:{...s?.variantStyles,...o.variantStyles}},i={id:l,config:c,resolve:t,reject:a};r(i),i.id=d.pushModal(l,e.createElement(n,{config:c,modalWindowId:l,handleClose:m,handleAction:f}))})}]}export{s as default};
1
+ import{useBaseModal as e}from"@rokku-x/react-hook-modal";import{useDialogStore as o}from"../store/dialogStore.esm.js";function s(s){const{addInstance:t,handleAction:a,handleClose:n,getContext:r}=o();return e(),[function(e){const o=Math.random().toString(36).substring(2,6);(e.actions?.flat().filter(e=>e.isFocused)||[]).length>1&&console.warn(`useHookDialog: Multiple actions are marked as isFocused in dialog "${o}". Only one action should be focused.`);const a=e.actions?.some(e=>e.some(e=>e.isCancel)),n=e.backdropCancel??s?.backdropCancel??!a,l={...s,...e,classNames:{...s?.classNames,...e.classNames},styles:{...s?.styles,...e.styles},variantStyles:{...s?.variantStyles,...e.variantStyles},backdropCancel:n},c=new Promise((e,s)=>{t({id:o,config:l,resolve:e,reject:s})});return Object.defineProperties(c,{context:{get:()=>r(o),enumerable:!0,configurable:!1},id:{get:()=>o,enumerable:!0,configurable:!1}}),c},r]}export{s as default};
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("zustand").create((e,n)=>({instances:[],addInstance:n=>e(e=>({instances:[...e.instances,n]})),removeInstance:n=>e(e=>({instances:e.instances.filter(e=>e.id!==n)})),getInstance:e=>n().instances.find(n=>n.id===e)}));exports.useDialogStore=e;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../components/ModalWindow.cjs.js"),n=require("@rokku-x/react-hook-modal"),o=require("react"),t=require("zustand"),a=n.useBaseModal(),c=t.create((n,t)=>({instances:[],addInstance:c=>{n(e=>({instances:[...e.instances,c]})),c.id=a.pushModal(c.id,o.createElement(e.default,{config:c.config,modalWindowId:c.id,handleClose:t().handleClose,handleAction:t().handleAction}))},removeInstance:e=>n(n=>({instances:n.instances.filter(n=>n.id!==e)})),handleClose:(e,n=!1)=>{const o=t().getInstance(e);o&&(a.popModal(e),!1!==o.config.rejectOnCancel||n?o.reject(o.config.defaultCancelValue):o.resolve(o.config.defaultCancelValue),t().removeInstance(e))},handleAction:(e,n)=>{const o=t().getInstance(e);o&&(a.popModal(e),n.isCancel&&!1!==o.config.rejectOnCancel?o.reject(n.value):o.resolve(n.value),t().removeInstance(e))},getContext:e=>{const n=t().getInstance(e);if(!n)throw new Error(`Dialog instance with id "${e}" not found.`);return{id:n.id,config:n.config,forceCancel:(n=!0)=>t().handleClose(e,n),forceAction:n=>{t().handleAction(e,n)},forceDefault:()=>{const o=n.config.actions?.flat().find(e=>e.isFocused);if(!o)throw new Error(`No default action (isFocused) defined for dialog instance with id "${e}".`);t().handleAction(e,o)}}},getInstance:e=>t().instances.find(n=>n.id===e)}));exports.useDialogStore=c;
@@ -1,4 +1,4 @@
1
- import { ConfirmInstance, ValidValue } from '../types';
1
+ import { ConfirmInstance, ModalAction, ValidValue } from '../types';
2
2
  /**
3
3
  * Zustand store interface for managing dialog instances.
4
4
  * Maintains a centralized list of active dialog instances.
@@ -11,8 +11,18 @@ interface DialogStore<T = ValidValue> {
11
11
  addInstance: (instance: ConfirmInstance<T>) => void;
12
12
  /** Remove a dialog instance from the store by ID */
13
13
  removeInstance: (id: string) => void;
14
+ /** Handle closing a dialog instance */
15
+ handleClose: (id: string, isForceCancel?: boolean) => void;
16
+ /** Handle an action taken on a dialog instance */
17
+ handleAction: (id: string, action: ModalAction) => void;
14
18
  /** Get a dialog instance by ID */
15
19
  getInstance: (id: string) => ConfirmInstance<T> | undefined;
20
+ /** Get the context of a dialog instance by ID */
21
+ getContext: (id: string) => {
22
+ id: string;
23
+ config: any;
24
+ forceCancel: () => void;
25
+ } | undefined;
16
26
  }
17
27
  /**
18
28
  * Zustand store for managing dialog instances.
@@ -1 +1 @@
1
- import{create as n}from"zustand";const s=n((n,s)=>({instances:[],addInstance:s=>n(n=>({instances:[...n.instances,s]})),removeInstance:s=>n(n=>({instances:n.instances.filter(n=>n.id!==s)})),getInstance:n=>s().instances.find(s=>s.id===n)}));export{s as useDialogStore};
1
+ import e from"../components/ModalWindow.esm.js";import{useBaseModal as n}from"@rokku-x/react-hook-modal";import o from"react";import{create as t}from"zustand";const c=n(),a=t((n,t)=>({instances:[],addInstance:a=>{n(e=>({instances:[...e.instances,a]})),a.id=c.pushModal(a.id,o.createElement(e,{config:a.config,modalWindowId:a.id,handleClose:t().handleClose,handleAction:t().handleAction}))},removeInstance:e=>n(n=>({instances:n.instances.filter(n=>n.id!==e)})),handleClose:(e,n=!1)=>{const o=t().getInstance(e);o&&(c.popModal(e),!1!==o.config.rejectOnCancel||n?o.reject(o.config.defaultCancelValue):o.resolve(o.config.defaultCancelValue),t().removeInstance(e))},handleAction:(e,n)=>{const o=t().getInstance(e);o&&(c.popModal(e),n.isCancel&&!1!==o.config.rejectOnCancel?o.reject(n.value):o.resolve(n.value),t().removeInstance(e))},getContext:e=>{const n=t().getInstance(e);if(!n)throw new Error(`Dialog instance with id "${e}" not found.`);return{id:n.id,config:n.config,forceCancel:(n=!0)=>t().handleClose(e,n),forceAction:n=>{t().handleAction(e,n)},forceDefault:()=>{const o=n.config.actions?.flat().find(e=>e.isFocused);if(!o)throw new Error(`No default action (isFocused) defined for dialog instance with id "${e}".`);t().handleAction(e,o)}}},getInstance:e=>t().instances.find(n=>n.id===e)}));export{a as useDialogStore};
@@ -235,4 +235,26 @@ interface HandleActionCallback {
235
235
  * Friendly Form Data object type
236
236
  */
237
237
  export type FormDataObject = Record<string, FormDataEntryValue | FormDataEntryValue[]>;
238
+ /** Function type for requesting a dialog*/
239
+ export interface RequestDialog<U> {
240
+ <U = FormDataObject>(config: ConfirmConfig & {
241
+ isReturnSubmit: true;
242
+ }): RequestDialogReturnType<U>;
243
+ <U = ValidValue>(config: ConfirmConfig): RequestDialogReturnType<U>;
244
+ }
245
+ /** Context information for a dialog instance */
246
+ export type DialogInstanceContext = {
247
+ id: string;
248
+ config: any;
249
+ forceCancel: (forceReject?: boolean) => void;
250
+ forceAction: (action: ModalAction) => void;
251
+ forceDefault: () => void;
252
+ };
253
+ /** Return type of the requestDialog function with added context property */
254
+ export type RequestDialogReturnType<T> = Promise<T> & {
255
+ id: string;
256
+ context: DialogInstanceContext;
257
+ };
258
+ /** Return type of the useHookDialog hook */
259
+ export type UseHookDialogReturnType<T> = [requestDialog: RequestDialog<T>, getContext: (id: string) => DialogInstanceContext];
238
260
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokku-x/react-hook-dialog",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "author": "rokku-x",
5
5
  "repository": {
6
6
  "type": "git",