@windrun-huaiin/third-ui 31.3.7 → 31.4.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/dist/main/credit/credit-nav-button.d.ts +3 -2
- package/dist/main/credit/credit-nav-button.js +7 -9
- package/dist/main/credit/credit-nav-button.mjs +5 -7
- package/dist/main/credit/credit-overview-nav-client.d.ts +7 -2
- package/dist/main/credit/credit-overview-nav-client.js +2 -2
- package/dist/main/credit/credit-overview-nav-client.mjs +2 -2
- package/dist/main/credit/index.d.ts +1 -1
- package/dist/main/credit/types.d.ts +1 -0
- package/dist/main/money-price/index.d.ts +2 -0
- package/dist/main/money-price/index.js +4 -0
- package/dist/main/money-price/index.mjs +1 -0
- package/dist/main/money-price/money-price-interactive.d.ts +1 -1
- package/dist/main/money-price/money-price-interactive.js +7 -5
- package/dist/main/money-price/money-price-interactive.mjs +7 -5
- package/dist/main/money-price/money-price-types.d.ts +2 -0
- package/dist/main/money-price/money-price.d.ts +1 -1
- package/dist/main/money-price/money-price.js +2 -2
- package/dist/main/money-price/money-price.mjs +2 -2
- package/dist/main/money-price/pricing-modal.d.ts +33 -0
- package/dist/main/money-price/pricing-modal.js +109 -0
- package/dist/main/money-price/pricing-modal.mjs +105 -0
- package/package.json +3 -3
- package/src/main/credit/credit-nav-button.tsx +15 -56
- package/src/main/credit/credit-overview-nav-client.tsx +8 -1
- package/src/main/credit/index.ts +1 -0
- package/src/main/credit/types.ts +1 -0
- package/src/main/money-price/index.ts +10 -0
- package/src/main/money-price/money-price-interactive.tsx +41 -31
- package/src/main/money-price/money-price-types.ts +2 -0
- package/src/main/money-price/money-price.tsx +2 -0
- package/src/main/money-price/pricing-modal.tsx +283 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { XIcon } from '@windrun-huaiin/base-ui/icons';
|
|
4
|
+
import { themeMainBgColor } from '@windrun-huaiin/base-ui/lib';
|
|
5
|
+
import {
|
|
6
|
+
AlertDialog,
|
|
7
|
+
AlertDialogContent,
|
|
8
|
+
AlertDialogHeader,
|
|
9
|
+
AlertDialogTitle,
|
|
10
|
+
} from '@windrun-huaiin/base-ui/ui';
|
|
11
|
+
import { cn } from '@windrun-huaiin/lib/utils';
|
|
12
|
+
import {
|
|
13
|
+
createContext,
|
|
14
|
+
useCallback,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useMemo,
|
|
18
|
+
useRef,
|
|
19
|
+
useState,
|
|
20
|
+
type ReactNode,
|
|
21
|
+
} from 'react';
|
|
22
|
+
import { dialogThemedOverlayClass } from '../alert-dialog/dialog-styles';
|
|
23
|
+
import { MoneyPriceInteractive } from './money-price-interactive';
|
|
24
|
+
import type {
|
|
25
|
+
InitUserContext,
|
|
26
|
+
MoneyPriceConfig,
|
|
27
|
+
MoneyPriceData,
|
|
28
|
+
} from './money-price-types';
|
|
29
|
+
|
|
30
|
+
export interface OpenPricingModalOptions {
|
|
31
|
+
billingType?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PricingModalProps {
|
|
35
|
+
open: boolean;
|
|
36
|
+
onOpenChange: (open: boolean) => void;
|
|
37
|
+
data: MoneyPriceData;
|
|
38
|
+
config: MoneyPriceConfig;
|
|
39
|
+
billingType?: string;
|
|
40
|
+
checkoutApiEndpoint?: string;
|
|
41
|
+
customerPortalApiEndpoint?: string;
|
|
42
|
+
enableClerkModal?: boolean;
|
|
43
|
+
enabledBillingTypes?: string[];
|
|
44
|
+
mobileBillingSwitchBehavior?: 'sticky' | 'static';
|
|
45
|
+
enableSubscriptionUpgrade?: boolean;
|
|
46
|
+
initUserContext?: InitUserContext;
|
|
47
|
+
isInitLoading?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface PricingModalProviderProps
|
|
51
|
+
extends Omit<PricingModalProps, 'open' | 'onOpenChange' | 'billingType'> {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
pricingContextEndpoint?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface PricingModalContextValue {
|
|
57
|
+
openPricingModal: (options?: OpenPricingModalOptions) => void;
|
|
58
|
+
closePricingModal: () => void;
|
|
59
|
+
isPricingModalOpen: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const emptyInitUserContext: InitUserContext = {
|
|
63
|
+
fingerprintId: null,
|
|
64
|
+
xUser: null,
|
|
65
|
+
xCredit: null,
|
|
66
|
+
xSubscription: null,
|
|
67
|
+
isClerkAuthenticated: false,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const PricingModalContext = createContext<PricingModalContextValue | null>(null);
|
|
71
|
+
|
|
72
|
+
function normalizeEnabledBillingTypes(
|
|
73
|
+
data: MoneyPriceData,
|
|
74
|
+
enabledBillingTypes: string[] | undefined,
|
|
75
|
+
) {
|
|
76
|
+
const availableKeys = data.billingSwitch.options.map((option) => option.key);
|
|
77
|
+
const configuredKeys = enabledBillingTypes?.length ? enabledBillingTypes : availableKeys;
|
|
78
|
+
|
|
79
|
+
return Array.from(
|
|
80
|
+
new Set(
|
|
81
|
+
configuredKeys
|
|
82
|
+
.map((billingType) => billingType.trim())
|
|
83
|
+
.filter((billingType) => availableKeys.includes(billingType)),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resolveBillingType({
|
|
89
|
+
data,
|
|
90
|
+
enabledBillingTypes,
|
|
91
|
+
requestedBillingType,
|
|
92
|
+
}: {
|
|
93
|
+
data: MoneyPriceData;
|
|
94
|
+
enabledBillingTypes?: string[];
|
|
95
|
+
requestedBillingType?: string;
|
|
96
|
+
}) {
|
|
97
|
+
const enabledKeys = normalizeEnabledBillingTypes(data, enabledBillingTypes);
|
|
98
|
+
|
|
99
|
+
if (requestedBillingType && enabledKeys.includes(requestedBillingType)) {
|
|
100
|
+
return requestedBillingType;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (enabledKeys.includes(data.billingSwitch.defaultKey)) {
|
|
104
|
+
return data.billingSwitch.defaultKey;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return enabledKeys[0] ?? data.billingSwitch.options[0]?.key;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function PricingModal({
|
|
111
|
+
open,
|
|
112
|
+
onOpenChange,
|
|
113
|
+
data,
|
|
114
|
+
config,
|
|
115
|
+
billingType,
|
|
116
|
+
checkoutApiEndpoint,
|
|
117
|
+
customerPortalApiEndpoint,
|
|
118
|
+
enableClerkModal = false,
|
|
119
|
+
enabledBillingTypes,
|
|
120
|
+
mobileBillingSwitchBehavior,
|
|
121
|
+
enableSubscriptionUpgrade = true,
|
|
122
|
+
initUserContext,
|
|
123
|
+
isInitLoading = false,
|
|
124
|
+
}: PricingModalProps) {
|
|
125
|
+
const resolvedBillingType = useMemo(
|
|
126
|
+
() =>
|
|
127
|
+
resolveBillingType({
|
|
128
|
+
data,
|
|
129
|
+
enabledBillingTypes,
|
|
130
|
+
requestedBillingType: billingType,
|
|
131
|
+
}),
|
|
132
|
+
[billingType, data, enabledBillingTypes],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<AlertDialog open={open} onOpenChange={onOpenChange}>
|
|
137
|
+
{open ? (
|
|
138
|
+
<AlertDialogContent
|
|
139
|
+
className={cn(
|
|
140
|
+
'mt-5 sm:mt-6 md:mt-10 lg:mt-15 w-[95vw] max-w-[1200px] overflow-hidden border border-slate-200 p-0 shadow-[0_32px_90px_rgba(15,23,42,0.25)] ring-1 ring-black/5 dark:border-white/12 dark:shadow-[0_40px_120px_rgba(0,0,0,0.6)] dark:ring-white/10',
|
|
141
|
+
themeMainBgColor,
|
|
142
|
+
)}
|
|
143
|
+
overlayClassName={dialogThemedOverlayClass}
|
|
144
|
+
>
|
|
145
|
+
<AlertDialogHeader className="flex flex-row items-center justify-between border-b border-slate-200 px-6 pt-4 pb-1 dark:border-slate-800">
|
|
146
|
+
<AlertDialogTitle asChild>
|
|
147
|
+
<div className="flex flex-wrap items-baseline gap-3 text-slate-900 dark:text-white">
|
|
148
|
+
<span className="text-2xl font-semibold leading-tight">
|
|
149
|
+
{data.title}
|
|
150
|
+
</span>
|
|
151
|
+
{data.subtitle ? (
|
|
152
|
+
<span className="text-sm font-medium text-slate-500 dark:text-slate-300">
|
|
153
|
+
{data.subtitle}
|
|
154
|
+
</span>
|
|
155
|
+
) : null}
|
|
156
|
+
</div>
|
|
157
|
+
</AlertDialogTitle>
|
|
158
|
+
<button
|
|
159
|
+
type="button"
|
|
160
|
+
className="rounded-full p-2 text-gray-400 transition hover:bg-gray-400 hover:text-gray-400 dark:text-white/80 dark:hover:bg-white/80 dark:hover:text-white/80"
|
|
161
|
+
onClick={() => onOpenChange(false)}
|
|
162
|
+
>
|
|
163
|
+
<XIcon className="h-6 w-6" />
|
|
164
|
+
</button>
|
|
165
|
+
</AlertDialogHeader>
|
|
166
|
+
<div className="max-h-[60vh] overflow-y-auto px-4 pt-2 pb-6 sm:max-h-[80vh]">
|
|
167
|
+
<div className="mx-auto w-full">
|
|
168
|
+
<MoneyPriceInteractive
|
|
169
|
+
key={resolvedBillingType ?? 'default'}
|
|
170
|
+
data={data}
|
|
171
|
+
config={config}
|
|
172
|
+
checkoutApiEndpoint={checkoutApiEndpoint}
|
|
173
|
+
customerPortalApiEndpoint={customerPortalApiEndpoint}
|
|
174
|
+
enableClerkModal={enableClerkModal}
|
|
175
|
+
enabledBillingTypes={enabledBillingTypes}
|
|
176
|
+
mobileBillingSwitchBehavior={mobileBillingSwitchBehavior}
|
|
177
|
+
enableSubscriptionUpgrade={enableSubscriptionUpgrade}
|
|
178
|
+
initialBillingType={resolvedBillingType}
|
|
179
|
+
disableAutoDetectBilling={resolvedBillingType === 'onetime'}
|
|
180
|
+
initUserContext={initUserContext}
|
|
181
|
+
isInitLoading={isInitLoading}
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</AlertDialogContent>
|
|
186
|
+
) : null}
|
|
187
|
+
</AlertDialog>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function PricingModalProvider({
|
|
192
|
+
children,
|
|
193
|
+
pricingContextEndpoint,
|
|
194
|
+
initUserContext: initUserContextProp,
|
|
195
|
+
isInitLoading: isInitLoadingProp = false,
|
|
196
|
+
...modalProps
|
|
197
|
+
}: PricingModalProviderProps) {
|
|
198
|
+
const [open, setOpen] = useState(false);
|
|
199
|
+
const [requestedBillingType, setRequestedBillingType] = useState<string | undefined>();
|
|
200
|
+
const [loadedInitUserContext, setLoadedInitUserContext] = useState<InitUserContext>(
|
|
201
|
+
initUserContextProp ?? emptyInitUserContext,
|
|
202
|
+
);
|
|
203
|
+
const [isContextLoading, setIsContextLoading] = useState(false);
|
|
204
|
+
const loadedContextRef = useRef(!!initUserContextProp);
|
|
205
|
+
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (initUserContextProp) {
|
|
208
|
+
loadedContextRef.current = true;
|
|
209
|
+
setLoadedInitUserContext(initUserContextProp);
|
|
210
|
+
}
|
|
211
|
+
}, [initUserContextProp]);
|
|
212
|
+
|
|
213
|
+
const loadPricingContext = useCallback(async () => {
|
|
214
|
+
if (!pricingContextEndpoint || loadedContextRef.current) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
setIsContextLoading(true);
|
|
219
|
+
try {
|
|
220
|
+
const response = await fetch(pricingContextEndpoint, {
|
|
221
|
+
credentials: 'same-origin',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (!response.ok) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const nextContext = (await response.json()) as InitUserContext;
|
|
229
|
+
loadedContextRef.current = true;
|
|
230
|
+
setLoadedInitUserContext(nextContext);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.warn('[PricingModalProvider] Failed to load pricing context', error);
|
|
233
|
+
} finally {
|
|
234
|
+
setIsContextLoading(false);
|
|
235
|
+
}
|
|
236
|
+
}, [pricingContextEndpoint]);
|
|
237
|
+
|
|
238
|
+
const openPricingModal = useCallback(
|
|
239
|
+
(options?: OpenPricingModalOptions) => {
|
|
240
|
+
setRequestedBillingType(options?.billingType);
|
|
241
|
+
setOpen(true);
|
|
242
|
+
void loadPricingContext();
|
|
243
|
+
},
|
|
244
|
+
[loadPricingContext],
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const closePricingModal = useCallback(() => {
|
|
248
|
+
setOpen(false);
|
|
249
|
+
}, []);
|
|
250
|
+
|
|
251
|
+
const contextValue = useMemo(
|
|
252
|
+
() => ({
|
|
253
|
+
openPricingModal,
|
|
254
|
+
closePricingModal,
|
|
255
|
+
isPricingModalOpen: open,
|
|
256
|
+
}),
|
|
257
|
+
[closePricingModal, open, openPricingModal],
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<PricingModalContext.Provider value={contextValue}>
|
|
262
|
+
{children}
|
|
263
|
+
<PricingModal
|
|
264
|
+
{...modalProps}
|
|
265
|
+
open={open}
|
|
266
|
+
onOpenChange={setOpen}
|
|
267
|
+
billingType={requestedBillingType}
|
|
268
|
+
initUserContext={initUserContextProp ?? loadedInitUserContext}
|
|
269
|
+
isInitLoading={isInitLoadingProp || isContextLoading}
|
|
270
|
+
/>
|
|
271
|
+
</PricingModalContext.Provider>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function usePricingModal() {
|
|
276
|
+
const context = useContext(PricingModalContext);
|
|
277
|
+
|
|
278
|
+
if (!context) {
|
|
279
|
+
throw new Error('usePricingModal must be used within PricingModalProvider.');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return context;
|
|
283
|
+
}
|