@applite/duticotac-react 0.0.1 → 0.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/README.md CHANGED
@@ -11,9 +11,11 @@ Ce package fournit une experience de paiement complete : selection du provider,
11
11
  - [Installation](#installation)
12
12
  - [Prerequis](#prerequis)
13
13
  - [Demarrage rapide](#demarrage-rapide)
14
+ - [Hook useDuticotac](#hook-useduticotac)
14
15
  - [Flux de paiement](#flux-de-paiement)
15
16
  - [Composants](#composants)
16
17
  - [DuticotacPaymentModal](#duticotacpaymentmodal)
18
+ - [ResponsiveDialog](#responsivedialog)
17
19
  - [ProviderSelector](#providerselector)
18
20
  - [PhoneInput](#phoneinput)
19
21
  - [OtpInput](#otpinput)
@@ -48,51 +50,140 @@ Les deux packages sont necessaires : `@applite/duticotac` contient le SDK (logiq
48
50
 
49
51
  ## Demarrage rapide
50
52
 
53
+ La maniere recommandee d'utiliser ce package est le hook **`useDuticotac`**. Il fournit un dialog responsive integre (modal sur desktop, bottom sheet sur mobile) et une API imperative simple :
54
+
51
55
  ```tsx
52
- import { useState } from "react";
53
56
  import { DuticotacSDK } from "@applite/duticotac";
54
- import { DuticotacPaymentModal } from "@applite/duticotac-react";
57
+ import { useDuticotac } from "@applite/duticotac-react";
55
58
 
56
- // Creez l'instance SDK une seule fois (en dehors du composant)
57
- const sdk = new DuticotacSDK({
58
- apiKey: "your_api_key",
59
- appId: "your_app_id", // optionnel
60
- });
59
+ const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
61
60
 
62
61
  function CheckoutPage() {
63
- const [paymentOpen, setPaymentOpen] = useState(false);
62
+ const { pay, Dialog } = useDuticotac({
63
+ sdk,
64
+ getTransactionId: async () => {
65
+ const res = await fetch("/api/create-transaction", { method: "POST" });
66
+ const data = await res.json();
67
+ return data.transactionId;
68
+ },
69
+ });
64
70
 
65
71
  return (
66
72
  <>
67
- <button onClick={() => setPaymentOpen(true)}>
73
+ <button onClick={() => pay({
74
+ amount: 5000,
75
+ onSuccess: (tx) => console.log("Paiement confirme :", tx),
76
+ })}>
68
77
  Acheter — 5 000 FCFA
69
78
  </button>
79
+ {Dialog}
80
+ </>
81
+ );
82
+ }
83
+ ```
70
84
 
71
- <DuticotacPaymentModal
72
- open={paymentOpen}
73
- onClose={() => setPaymentOpen(false)}
74
- sdk={sdk}
75
- amount={5000}
76
- providers={["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"]}
77
- getTransactionId={async () => {
78
- // Appelez votre backend pour generer un ID unique
79
- const res = await fetch("/api/create-transaction", { method: "POST" });
80
- const data = await res.json();
81
- return data.transactionId;
82
- }}
83
- productReference="premium-monthly"
84
- name="Jean Dupont"
85
- email="jean@example.com"
86
- onSuccess={(transaction) => {
87
- console.log("Paiement confirme :", transaction);
88
- // Rafraichir le solde, rediriger, etc.
89
- }}
90
- />
85
+ > Pour un controle plus fin (modal custom, Shadcn Dialog, etc.), utilisez directement le composant [`DuticotacPaymentModal`](#duticotacpaymentmodal) avec sa prop `renderModal`.
86
+
87
+ ---
88
+
89
+ ## Hook `useDuticotac`
90
+
91
+ La maniere la plus simple d'integrer Duticotac. Le hook retourne une fonction `pay()` et un element `Dialog` avec un dialog responsive integre (modal centre sur desktop, bottom sheet sur mobile).
92
+
93
+ ```tsx
94
+ import { DuticotacSDK } from "@applite/duticotac";
95
+ import { useDuticotac } from "@applite/duticotac-react";
96
+
97
+ const sdk = new DuticotacSDK({ apiKey: "your_api_key" });
98
+
99
+ function CheckoutPage() {
100
+ const { pay, Dialog } = useDuticotac({
101
+ sdk,
102
+ providers: ["OM_CI", "MTN_CI", "MOOV_CI", "WAVE_CI"],
103
+ getTransactionId: async () => {
104
+ const res = await fetch("/api/create-transaction", { method: "POST" });
105
+ const data = await res.json();
106
+ return data.transactionId;
107
+ },
108
+ name: "Jean Dupont",
109
+ email: "jean@example.com",
110
+ });
111
+
112
+ return (
113
+ <>
114
+ <button onClick={() => pay({
115
+ amount: 5000,
116
+ productReference: "premium-monthly",
117
+ onSuccess: (tx) => {
118
+ console.log("Paiement confirme :", tx);
119
+ },
120
+ })}>
121
+ Acheter — 5 000 FCFA
122
+ </button>
123
+ {Dialog}
91
124
  </>
92
125
  );
93
126
  }
94
127
  ```
95
128
 
129
+ ### API du hook
130
+
131
+ ```ts
132
+ const { pay, Dialog, isOpen, close } = useDuticotac(config);
133
+ ```
134
+
135
+ **`config` (UseDuticotacConfig)**
136
+
137
+ | Champ | Type | Requis | Description |
138
+ |-------|------|--------|-------------|
139
+ | `sdk` | `DuticotacSDK` | Oui | Instance du SDK |
140
+ | `getTransactionId` | `() => Promise<string>` | Oui | Generateur d'ID transaction |
141
+ | `providers` | `PaymentProvider[]` | Non | Providers disponibles (defaut: OM, MTN, MOOV, WAVE) |
142
+ | `productReference` | `string` | Non | Reference produit par defaut |
143
+ | `name` | `string` | Non | Nom du client par defaut |
144
+ | `email` | `string` | Non | Email du client par defaut |
145
+ | `customerId` | `string` | Non | ID du client |
146
+ | `kolaboReference` | `string` | Non | Reference Kolabo |
147
+ | `app` | `CoreApp` | Non | Application source |
148
+ | `platform` | `PlatformType` | Non | Plateforme |
149
+ | `initialPhone` | `string` | Non | Pre-remplir le telephone |
150
+ | `pollTimeout` | `number` | Non | Timeout polling en ms (defaut: 90000) |
151
+ | `title` | `string` | Non | Titre du dialog (defaut: "Paiement") |
152
+
153
+ **Retour**
154
+
155
+ | Champ | Type | Description |
156
+ |-------|------|-------------|
157
+ | `pay` | `(options: PayOptions) => void` | Ouvre le dialog de paiement |
158
+ | `Dialog` | `ReactNode` | Element JSX a rendre une fois dans l'arbre |
159
+ | `isOpen` | `boolean` | Etat du dialog |
160
+ | `close` | `() => void` | Fermer le dialog programmatiquement |
161
+
162
+ **`PayOptions`** (argument de `pay()`)
163
+
164
+ | Champ | Type | Requis | Description |
165
+ |-------|------|--------|-------------|
166
+ | `amount` | `number` | Oui | Montant en FCFA |
167
+ | `providers` | `PaymentProvider[]` | Non | Override les providers |
168
+ | `productReference` | `string` | Non | Override la reference produit |
169
+ | `name` | `string` | Non | Override le nom |
170
+ | `email` | `string` | Non | Override l'email |
171
+ | `customerId` | `string` | Non | Override l'ID client |
172
+ | `successMessage` | `string` | Non | Message de succes personnalise |
173
+ | `onSuccess` | `(tx: TransactionModel) => void` | Non | Callback de succes |
174
+ | `onClose` | `() => void` | Non | Callback de fermeture |
175
+
176
+ ### Comportement responsive
177
+
178
+ Le dialog s'adapte automatiquement :
179
+
180
+ | Ecran | Comportement |
181
+ |-------|-------------|
182
+ | Desktop (> 640px) | Modal centre, max-width 440px, animation scale |
183
+ | Mobile (<= 640px) | Bottom sheet, glisse depuis le bas, coins arrondis en haut, poignee de drag |
184
+
185
+ Les deux modes incluent : fermeture par backdrop click, touche Escape, verrouillage du scroll, et transitions CSS fluides.
186
+
96
187
  ---
97
188
 
98
189
  ## Flux de paiement
@@ -162,6 +253,43 @@ import { DuticotacPaymentModal } from "@applite/duticotac-react";
162
253
 
163
254
  ---
164
255
 
256
+ ### `ResponsiveDialog`
257
+
258
+ Dialog responsive qui s'adapte automatiquement : modal centre sur desktop, bottom sheet sur mobile. Utilise en interne par `useDuticotac`, mais peut etre utilise independamment.
259
+
260
+ ```tsx
261
+ import { ResponsiveDialog } from "@applite/duticotac-react";
262
+
263
+ <ResponsiveDialog
264
+ open={isOpen}
265
+ onClose={() => setOpen(false)}
266
+ title="Mon dialog"
267
+ >
268
+ <p>Contenu du dialog</p>
269
+ </ResponsiveDialog>
270
+ ```
271
+
272
+ #### Props
273
+
274
+ | Prop | Type | Requis | Description |
275
+ |------|------|--------|-------------|
276
+ | `open` | `boolean` | Oui | Visibilite du dialog |
277
+ | `onClose` | `() => void` | Oui | Callback de fermeture |
278
+ | `title` | `string` | Oui | Titre affiche dans le header |
279
+ | `children` | `ReactNode` | Oui | Contenu du dialog |
280
+ | `className` | `string` | Non | Classes CSS additionnelles |
281
+
282
+ #### Fonctionnalites
283
+
284
+ - **Desktop** (> 640px) : Modal centre, max-width 440px, animation d'echelle
285
+ - **Mobile** (<= 640px) : Bottom sheet, slide-up, coins arrondis en haut, poignee de drag
286
+ - Rendu via `createPortal` dans `document.body`
287
+ - Fermeture : backdrop click, touche Escape
288
+ - Verrouillage du scroll body quand ouvert
289
+ - Transitions CSS fluides (250ms)
290
+
291
+ ---
292
+
165
293
  ### `ProviderSelector`
166
294
 
167
295
  Grille de selection des providers de paiement avec logos et indicateur de selection.
@@ -493,8 +621,13 @@ import type {
493
621
  ### Exports de `@applite/duticotac-react`
494
622
 
495
623
  ```ts
624
+ // Hook (recommande)
625
+ export { useDuticotac } from "@applite/duticotac-react";
626
+ export type { UseDuticotacConfig, UseDuticotacReturn, PayOptions } from "@applite/duticotac-react";
627
+
496
628
  // Composants
497
629
  export { DuticotacPaymentModal } from "@applite/duticotac-react";
630
+ export { ResponsiveDialog } from "@applite/duticotac-react";
498
631
  export { ProviderSelector } from "@applite/duticotac-react";
499
632
  export { PhoneInput } from "@applite/duticotac-react";
500
633
  export { OtpInput } from "@applite/duticotac-react";
@@ -502,6 +635,7 @@ export { TransactionStatus } from "@applite/duticotac-react";
502
635
 
503
636
  // Types des props
504
637
  export type { DuticotacPaymentModalProps } from "@applite/duticotac-react";
638
+ export type { ResponsiveDialogProps } from "@applite/duticotac-react";
505
639
  export type { ProviderSelectorProps } from "@applite/duticotac-react";
506
640
  export type { PhoneInputProps } from "@applite/duticotac-react";
507
641
  export type { OtpInputProps } from "@applite/duticotac-react";
package/dist/index.d.mts CHANGED
@@ -90,6 +90,69 @@ interface TransactionStatusProps {
90
90
  }
91
91
  declare function TransactionStatus({ status, elapsedSeconds, message, errorMessage, paymentUrl, onRetry, onClose, onOpenPaymentUrl, className, }: TransactionStatusProps): react_jsx_runtime.JSX.Element;
92
92
 
93
+ interface ResponsiveDialogProps {
94
+ open: boolean;
95
+ onClose: () => void;
96
+ title: string;
97
+ children: React.ReactNode;
98
+ className?: string;
99
+ }
100
+ declare function ResponsiveDialog({ open, onClose, title, children, className, }: ResponsiveDialogProps): React.ReactPortal | null;
101
+
102
+ interface UseDuticotacConfig {
103
+ /** DuticotacSDK instance. */
104
+ sdk: DuticotacSDK;
105
+ /** Available payment providers. */
106
+ providers?: PaymentProvider[];
107
+ /** Called to get a unique transaction ID before cashout. */
108
+ getTransactionId: () => Promise<string>;
109
+ /** Shared defaults — can be overridden per pay() call. */
110
+ productReference?: string;
111
+ name?: string;
112
+ email?: string;
113
+ customerId?: string;
114
+ kolaboReference?: string;
115
+ app?: CoreApp;
116
+ platform?: PlatformType;
117
+ initialPhone?: string;
118
+ /** Polling timeout in ms (default: 90000). */
119
+ pollTimeout?: number;
120
+ /** Dialog title (default: "Paiement"). */
121
+ title?: string;
122
+ }
123
+ interface PayOptions {
124
+ /** Amount in FCFA. */
125
+ amount: number;
126
+ /** Override providers for this payment. */
127
+ providers?: PaymentProvider[];
128
+ /** Override product reference. */
129
+ productReference?: string;
130
+ /** Override customer details. */
131
+ name?: string;
132
+ email?: string;
133
+ customerId?: string;
134
+ /** Custom success message. */
135
+ successMessage?: string;
136
+ /** Called when payment is confirmed. */
137
+ onSuccess?: (transaction: TransactionModel) => void;
138
+ /** Called when the dialog closes (success or cancel). */
139
+ onClose?: () => void;
140
+ }
141
+ interface UseDuticotacReturn {
142
+ /** Open the payment dialog with the given options. */
143
+ pay: (options: PayOptions) => void;
144
+ /** Whether the dialog is currently open. */
145
+ isOpen: boolean;
146
+ /** Close the dialog programmatically. */
147
+ close: () => void;
148
+ /**
149
+ * The dialog JSX element. Render this once somewhere in your component tree.
150
+ * @example return <>{Dialog}</>
151
+ */
152
+ Dialog: React.ReactNode;
153
+ }
154
+ declare function useDuticotac(config: UseDuticotacConfig): UseDuticotacReturn;
155
+
93
156
  declare function cn(...inputs: ClassValue[]): string;
94
157
 
95
158
  declare function formatCFA(n: number): string;
@@ -99,4 +162,4 @@ declare function validatePhone(phone: string, provider: PaymentProvider): string
99
162
  declare function normalizePhone(phone: string): string;
100
163
  declare function needsOtp(provider: PaymentProvider): boolean;
101
164
 
102
- export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, TransactionStatus, type TransactionStatusProps, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, validatePhone };
165
+ export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, type PayOptions, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, ResponsiveDialog, type ResponsiveDialogProps, TransactionStatus, type TransactionStatusProps, type UseDuticotacConfig, type UseDuticotacReturn, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, useDuticotac, validatePhone };
package/dist/index.d.ts CHANGED
@@ -90,6 +90,69 @@ interface TransactionStatusProps {
90
90
  }
91
91
  declare function TransactionStatus({ status, elapsedSeconds, message, errorMessage, paymentUrl, onRetry, onClose, onOpenPaymentUrl, className, }: TransactionStatusProps): react_jsx_runtime.JSX.Element;
92
92
 
93
+ interface ResponsiveDialogProps {
94
+ open: boolean;
95
+ onClose: () => void;
96
+ title: string;
97
+ children: React.ReactNode;
98
+ className?: string;
99
+ }
100
+ declare function ResponsiveDialog({ open, onClose, title, children, className, }: ResponsiveDialogProps): React.ReactPortal | null;
101
+
102
+ interface UseDuticotacConfig {
103
+ /** DuticotacSDK instance. */
104
+ sdk: DuticotacSDK;
105
+ /** Available payment providers. */
106
+ providers?: PaymentProvider[];
107
+ /** Called to get a unique transaction ID before cashout. */
108
+ getTransactionId: () => Promise<string>;
109
+ /** Shared defaults — can be overridden per pay() call. */
110
+ productReference?: string;
111
+ name?: string;
112
+ email?: string;
113
+ customerId?: string;
114
+ kolaboReference?: string;
115
+ app?: CoreApp;
116
+ platform?: PlatformType;
117
+ initialPhone?: string;
118
+ /** Polling timeout in ms (default: 90000). */
119
+ pollTimeout?: number;
120
+ /** Dialog title (default: "Paiement"). */
121
+ title?: string;
122
+ }
123
+ interface PayOptions {
124
+ /** Amount in FCFA. */
125
+ amount: number;
126
+ /** Override providers for this payment. */
127
+ providers?: PaymentProvider[];
128
+ /** Override product reference. */
129
+ productReference?: string;
130
+ /** Override customer details. */
131
+ name?: string;
132
+ email?: string;
133
+ customerId?: string;
134
+ /** Custom success message. */
135
+ successMessage?: string;
136
+ /** Called when payment is confirmed. */
137
+ onSuccess?: (transaction: TransactionModel) => void;
138
+ /** Called when the dialog closes (success or cancel). */
139
+ onClose?: () => void;
140
+ }
141
+ interface UseDuticotacReturn {
142
+ /** Open the payment dialog with the given options. */
143
+ pay: (options: PayOptions) => void;
144
+ /** Whether the dialog is currently open. */
145
+ isOpen: boolean;
146
+ /** Close the dialog programmatically. */
147
+ close: () => void;
148
+ /**
149
+ * The dialog JSX element. Render this once somewhere in your component tree.
150
+ * @example return <>{Dialog}</>
151
+ */
152
+ Dialog: React.ReactNode;
153
+ }
154
+ declare function useDuticotac(config: UseDuticotacConfig): UseDuticotacReturn;
155
+
93
156
  declare function cn(...inputs: ClassValue[]): string;
94
157
 
95
158
  declare function formatCFA(n: number): string;
@@ -99,4 +162,4 @@ declare function validatePhone(phone: string, provider: PaymentProvider): string
99
162
  declare function normalizePhone(phone: string): string;
100
163
  declare function needsOtp(provider: PaymentProvider): boolean;
101
164
 
102
- export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, TransactionStatus, type TransactionStatusProps, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, validatePhone };
165
+ export { DuticotacPaymentModal, type DuticotacPaymentModalProps, OtpInput, type OtpInputProps, type PayOptions, PhoneInput, type PhoneInputProps, ProviderSelector, type ProviderSelectorProps, ResponsiveDialog, type ResponsiveDialogProps, TransactionStatus, type TransactionStatusProps, type UseDuticotacConfig, type UseDuticotacReturn, cn, formatCFA, getPhoneRegex, needsOtp, normalizePhone, useDuticotac, validatePhone };
package/dist/index.js CHANGED
@@ -35,12 +35,14 @@ __export(index_exports, {
35
35
  OtpInput: () => OtpInput,
36
36
  PhoneInput: () => PhoneInput,
37
37
  ProviderSelector: () => ProviderSelector,
38
+ ResponsiveDialog: () => ResponsiveDialog,
38
39
  TransactionStatus: () => TransactionStatus,
39
40
  cn: () => cn,
40
41
  formatCFA: () => formatCFA,
41
42
  getPhoneRegex: () => getPhoneRegex,
42
43
  needsOtp: () => needsOtp,
43
44
  normalizePhone: () => normalizePhone,
45
+ useDuticotac: () => useDuticotac,
44
46
  validatePhone: () => validatePhone
45
47
  });
46
48
  module.exports = __toCommonJS(index_exports);
@@ -703,18 +705,276 @@ function DuticotacPaymentModal({
703
705
  ] })
704
706
  ] });
705
707
  }
708
+
709
+ // src/components/responsive-dialog.tsx
710
+ var React3 = __toESM(require("react"));
711
+ var import_react_dom = require("react-dom");
712
+ var import_jsx_runtime6 = require("react/jsx-runtime");
713
+ var ANIMATION_MS = 250;
714
+ function ResponsiveDialog({
715
+ open,
716
+ onClose,
717
+ title,
718
+ children,
719
+ className
720
+ }) {
721
+ const [mounted, setMounted] = React3.useState(false);
722
+ const [visible, setVisible] = React3.useState(false);
723
+ const panelRef = React3.useRef(null);
724
+ React3.useEffect(() => {
725
+ if (open) {
726
+ setMounted(true);
727
+ requestAnimationFrame(() => {
728
+ requestAnimationFrame(() => setVisible(true));
729
+ });
730
+ } else if (mounted) {
731
+ setVisible(false);
732
+ const timer = setTimeout(() => setMounted(false), ANIMATION_MS);
733
+ return () => clearTimeout(timer);
734
+ }
735
+ }, [open]);
736
+ React3.useEffect(() => {
737
+ if (!open) return;
738
+ const handler = (e) => {
739
+ if (e.key === "Escape") onClose();
740
+ };
741
+ document.addEventListener("keydown", handler);
742
+ return () => document.removeEventListener("keydown", handler);
743
+ }, [open, onClose]);
744
+ React3.useEffect(() => {
745
+ if (!mounted) return;
746
+ const prev = document.body.style.overflow;
747
+ document.body.style.overflow = "hidden";
748
+ return () => {
749
+ document.body.style.overflow = prev;
750
+ };
751
+ }, [mounted]);
752
+ if (!mounted) return null;
753
+ const dialog = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
755
+ .dtc-dialog-backdrop {
756
+ position: fixed;
757
+ inset: 0;
758
+ z-index: 9998;
759
+ background: rgba(0,0,0,0);
760
+ transition: background ${ANIMATION_MS}ms ease;
761
+ }
762
+ .dtc-dialog-backdrop.dtc-visible {
763
+ background: rgba(0,0,0,0.5);
764
+ }
765
+
766
+ /* \u2500\u2500 Desktop: centered modal \u2500\u2500 */
767
+ .dtc-dialog-panel {
768
+ position: fixed;
769
+ z-index: 9999;
770
+ background: var(--background, #fff);
771
+ border: 1px solid var(--border, #e5e7eb);
772
+ box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);
773
+
774
+ /* Desktop default */
775
+ top: 50%;
776
+ left: 50%;
777
+ transform: translate(-50%, -50%) scale(0.95);
778
+ opacity: 0;
779
+ width: calc(100% - 32px);
780
+ max-width: 440px;
781
+ max-height: calc(100vh - 64px);
782
+ border-radius: 16px;
783
+ overflow-y: auto;
784
+
785
+ transition:
786
+ transform ${ANIMATION_MS}ms cubic-bezier(0.22, 1, 0.36, 1),
787
+ opacity ${ANIMATION_MS}ms ease;
788
+ }
789
+ .dtc-dialog-panel.dtc-visible {
790
+ transform: translate(-50%, -50%) scale(1);
791
+ opacity: 1;
792
+ }
793
+
794
+ /* \u2500\u2500 Mobile: bottom sheet \u2500\u2500 */
795
+ @media (max-width: 640px) {
796
+ .dtc-dialog-panel {
797
+ top: auto;
798
+ left: 0;
799
+ right: 0;
800
+ bottom: 0;
801
+ transform: translateY(100%);
802
+ opacity: 1;
803
+ width: 100%;
804
+ max-width: 100%;
805
+ max-height: 90vh;
806
+ border-radius: 20px 20px 0 0;
807
+ border-bottom: none;
808
+ }
809
+ .dtc-dialog-panel.dtc-visible {
810
+ transform: translateY(0);
811
+ }
812
+ }
813
+ ` }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
815
+ "div",
816
+ {
817
+ className: cn("dtc-dialog-backdrop", visible && "dtc-visible"),
818
+ onClick: onClose,
819
+ "aria-hidden": true
820
+ }
821
+ ),
822
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
823
+ "div",
824
+ {
825
+ ref: panelRef,
826
+ className: cn("dtc-dialog-panel", visible && "dtc-visible", className),
827
+ role: "dialog",
828
+ "aria-modal": "true",
829
+ "aria-label": title,
830
+ children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dtc-mobile-handle", style: {
832
+ display: "none",
833
+ justifyContent: "center",
834
+ paddingTop: 12,
835
+ paddingBottom: 4
836
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
837
+ width: 36,
838
+ height: 4,
839
+ borderRadius: 2,
840
+ background: "var(--border, #d1d5db)"
841
+ } }) }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
843
+ @media (max-width: 640px) {
844
+ .dtc-mobile-handle { display: flex !important; }
845
+ }
846
+ ` }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
848
+ display: "flex",
849
+ alignItems: "center",
850
+ justifyContent: "space-between",
851
+ padding: "16px 20px 0"
852
+ }, children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { style: {
854
+ fontSize: 18,
855
+ fontWeight: 700,
856
+ color: "var(--foreground, #111)",
857
+ margin: 0
858
+ }, children: title }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ onClick: onClose,
864
+ style: {
865
+ background: "none",
866
+ border: "none",
867
+ padding: 4,
868
+ cursor: "pointer",
869
+ borderRadius: 6,
870
+ color: "var(--muted-foreground, #6b7280)",
871
+ display: "flex",
872
+ alignItems: "center",
873
+ justifyContent: "center"
874
+ },
875
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
876
+ "path",
877
+ {
878
+ d: "M6 18L18 6M6 6l12 12",
879
+ stroke: "currentColor",
880
+ strokeWidth: "2",
881
+ strokeLinecap: "round"
882
+ }
883
+ ) })
884
+ }
885
+ )
886
+ ] }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: "16px 20px 20px" }, children })
888
+ ]
889
+ }
890
+ )
891
+ ] });
892
+ if (typeof document === "undefined") return null;
893
+ return (0, import_react_dom.createPortal)(dialog, document.body);
894
+ }
895
+
896
+ // src/hooks/use-duticotac.tsx
897
+ var React4 = __toESM(require("react"));
898
+ var import_jsx_runtime7 = require("react/jsx-runtime");
899
+ function useDuticotac(config) {
900
+ const [open, setOpen] = React4.useState(false);
901
+ const [payOptions, setPayOptions] = React4.useState(null);
902
+ const configRef = React4.useRef(config);
903
+ configRef.current = config;
904
+ const pay = React4.useCallback((options) => {
905
+ setPayOptions(options);
906
+ setOpen(true);
907
+ }, []);
908
+ const close = React4.useCallback(() => {
909
+ setOpen(false);
910
+ }, []);
911
+ const handleClose = React4.useCallback(() => {
912
+ setOpen(false);
913
+ payOptions?.onClose?.();
914
+ }, [payOptions]);
915
+ const handleSuccess = React4.useCallback(
916
+ (tx) => {
917
+ payOptions?.onSuccess?.(tx);
918
+ },
919
+ [payOptions]
920
+ );
921
+ const DialogElement = React4.useMemo(() => {
922
+ if (!payOptions) return null;
923
+ const cfg = configRef.current;
924
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
925
+ DuticotacPaymentModal,
926
+ {
927
+ open,
928
+ onClose: handleClose,
929
+ onSuccess: handleSuccess,
930
+ sdk: cfg.sdk,
931
+ amount: payOptions.amount,
932
+ providers: payOptions.providers ?? cfg.providers,
933
+ getTransactionId: cfg.getTransactionId,
934
+ productReference: payOptions.productReference ?? cfg.productReference,
935
+ initialPhone: cfg.initialPhone,
936
+ name: payOptions.name ?? cfg.name,
937
+ email: payOptions.email ?? cfg.email,
938
+ customerId: payOptions.customerId ?? cfg.customerId,
939
+ kolaboReference: cfg.kolaboReference,
940
+ app: cfg.app,
941
+ platform: cfg.platform,
942
+ pollTimeout: cfg.pollTimeout,
943
+ title: cfg.title,
944
+ successMessage: payOptions.successMessage,
945
+ renderModal: ({ open: isOpen, onClose: modalClose, title: modalTitle, children }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
946
+ ResponsiveDialog,
947
+ {
948
+ open: isOpen,
949
+ onClose: modalClose,
950
+ title: modalTitle,
951
+ children
952
+ }
953
+ )
954
+ }
955
+ );
956
+ }, [open, payOptions, handleClose, handleSuccess]);
957
+ return {
958
+ pay,
959
+ isOpen: open,
960
+ close,
961
+ Dialog: DialogElement
962
+ };
963
+ }
706
964
  // Annotate the CommonJS export names for ESM import in node:
707
965
  0 && (module.exports = {
708
966
  DuticotacPaymentModal,
709
967
  OtpInput,
710
968
  PhoneInput,
711
969
  ProviderSelector,
970
+ ResponsiveDialog,
712
971
  TransactionStatus,
713
972
  cn,
714
973
  formatCFA,
715
974
  getPhoneRegex,
716
975
  needsOtp,
717
976
  normalizePhone,
977
+ useDuticotac,
718
978
  validatePhone
719
979
  });
720
980
  //# sourceMappingURL=index.js.map