UNPKG

@heymantle/react

Version:

Mantle's React component library

1 lines 27.7 kB
{"version":3,"file":"index.mjs","sources":["../src/utils/constants.ts","../src/components/core/MantleProvider/MantleProvider.tsx","../src/utils/features.ts","../src/utils/money.ts","../src/utils/plans.ts","../src/utils/views.ts"],"sourcesContent":["export const PlanAvailability = {\n Public: \"public\",\n CustomerTag: \"customerTag\",\n ShopifyPlan: \"shopifyPlan\",\n Customer: \"customer\",\n Hidden: \"hidden\",\n} as const;\n\nexport const Labels = {\n AmountPerInterval: \"{{ amount }} per {{ interval }}\",\n Back: \"Back\",\n Cancel: \"Cancel\",\n CancelConfirmation: \"Are you sure you want to cancel your subscription?\",\n CancelPlan: \"Cancel plan\",\n ChangePlan: \"Change plan\",\n CurrentPlan: \"Current plan\",\n CustomPlans: \"Custom plans\",\n CustomPlansDescription: \"Plans tailored to your specific needs\",\n DiscountAmount: \"{{ amount }} discount\",\n DiscountAmountExpired: \"{{ amount }} discount expired\",\n FreeTrialLength: \"{{ trialDays }}-day free trial\",\n Features: \"Features\",\n Month: \"month\",\n MonthShort: \"mo\",\n Monthly: \"Monthly\",\n NextBillingDate: \"Next billing date\",\n NotSubscribed: \"You're not subscribed to a plan yet.\",\n Year: \"year\",\n YearShort: \"yr\",\n Yearly: \"Yearly\",\n MostPopular: \"Most popular\",\n Per: \"/\",\n Plans: \"Plans\",\n Price: \"Price\",\n SelectPlan: \"Select plan\",\n SubscribeSuccessTitle: \"Subscription successful\",\n SubscribeSuccessBody: \"Thanks for subscribing to our app!\",\n Subscription: \"Subscription\",\n SubscriptionCancelled: \"Subscription cancelled\",\n UsageCharges: \"Usage charges\",\n\n // Common\n loading: \"Loading...\",\n error: \"An error occurred\",\n retry: \"Retry\",\n cancel: \"Cancel\",\n continue: \"Continue\",\n \n // Subscription\n subscribe: \"Subscribe\",\n subscribeNow: \"Subscribe Now\",\n upgradeNow: \"Upgrade Now\",\n manageSubscription: \"Manage Subscription\",\n cancelSubscription: \"Cancel Subscription\",\n \n // Payment\n addPaymentMethod: \"Add Payment Method\",\n updatePaymentMethod: \"Update Payment Method\",\n \n // Features\n featureNotAvailable: \"This feature is not available on your current plan\",\n upgradeRequired: \"Upgrade Required\",\n limitReached: \"Limit Reached\",\n} as const;\n","import { MantleClient } from \"@heymantle/client\";\nimport React, { createContext, useContext, useState, useEffect } from \"react\";\nimport { Labels } from \"../../../utils/constants\";\nimport type {\n Feature,\n Customer,\n Subscription,\n Plan,\n UsageEvent,\n SetupIntent,\n HostedSession,\n RequirePaymentMethodOptions,\n} from \"@heymantle/client\";\n\n/** The main context interface that encapsulates functionality exposed by MantleProvider */\nexport interface TMantleContext {\n /** The MantleClient instance */\n client: MantleClient;\n /** The current customer */\n customer: Customer | null;\n /** The current subscription */\n subscription: Subscription | null;\n /** The available plans */\n plans: Plan[];\n /** Whether the current customer is loading */\n loading: boolean;\n /** Internationalization labels */\n i18n: typeof Labels;\n /** Refetch the current customer */\n refetch: () => Promise<void>;\n /** Send a new usage event to Mantle */\n sendUsageEvent: SendUsageEventCallback;\n /** Get a usage report for a usage metric */\n getUsageReport: GetUsageReportCallback;\n /** Subscribe to a new plan */\n subscribe: SubscribeCallback;\n /** Cancel the current subscription */\n cancelSubscription: CancelSubscriptionCallback;\n /** Start the process of adding a new payment method */\n addPaymentMethod: AddPaymentMethodCallback;\n /** Check if a feature is enabled */\n isFeatureEnabled: FeatureEnabledCallback;\n /** Get the limit for a feature */\n limitForFeature: FeatureLimitCallback;\n /** Create a hosted session */\n createHostedSession: HostedSessionCallback;\n}\n\n/** Callback to send a new usage event to Mantle */\nexport type SendUsageEventCallback = (usageEvent: UsageEvent) => Promise<void>;\n\n/** Callback to get a usage report for a usage metric */\nexport type GetUsageReportCallback = (params: {\n /** The ID of the usage metric to get a report for */\n usageId: string;\n /** The period to get the usage report for */\n period: string;\n}) => Promise<any>;\n\n/** Common subscription parameters without the plan selection */\nexport type BaseSubscribeParams = {\n /** The ID of the discount to apply */\n discountId?: string;\n /** The billing provider to use */\n billingProvider?: string;\n /** The URL to return to after subscribing */\n returnUrl?: string;\n /** Whether to use the saved payment method for the customer. Defaults to true. */\n useSavedPaymentMethod?: boolean;\n /** The number of trial days to offer */\n trialDays?: number;\n /** (Stripe only) Whether to use Stripe checkout for the subscription. Not applicable for Shopify subscriptions as they are always hosted */\n hosted?: boolean;\n /** (Stripe only) The collection method to use for the subscription */\n collectionMethod?: string;\n /** (Stripe only) The number of days until the subscription is due */\n daysUntilDue?: number;\n /** (Stripe only) The payment method types to use for the subscription */\n paymentMethodTypes?: string[];\n /** (Stripe only) When to require a payment method for the subscription */\n requirePaymentMethod?: RequirePaymentMethodOptions;\n /** (Stripe only) Whether to automatically calculate tax for the subscription. Defaults to false. */\n automaticTax?: boolean;\n /** (Stripe checkout only) Tell the Stripe Checkout Session to require a billing address */\n requireBillingAddress?: boolean;\n /** (Stripe checkout only) Prefill the Stripe customer's email address */\n email?: string;\n /** (Stripe checkout only) Key-value pairs of metadata to attach to the subscription */\n metadata?: Record<string, string>;\n};\n\n/** Parameters for subscribing to a single plan */\nexport type SinglePlanSubscribe = BaseSubscribeParams & {\n /** The ID of the plan to subscribe to */\n planId: string;\n /** Not allowed when using planId */\n planIds?: never;\n};\n\n/** Parameters for subscribing to multiple plans */\nexport type MultiPlanSubscribe = BaseSubscribeParams & {\n /** Not allowed when using planIds */\n planId?: never;\n /** The IDs of the plans to subscribe to */\n planIds: string[];\n};\n\n/** Callback to subscribe to a new plan or plans */\nexport type SubscribeCallback = (params: SinglePlanSubscribe | MultiPlanSubscribe) => Promise<Subscription>;\n\n/** Callback to cancel the current subscription */\nexport type CancelSubscriptionCallback = (params?: {\n /** The reason for canceling the subscription */\n cancelReason?: string;\n}) => Promise<Subscription>;\n\n/** Callback to start the process of adding a new payment method */\nexport type AddPaymentMethodCallback = (params: {\n /** The URL to return to after connecting a new PaymentMethod */\n returnUrl: string;\n}) => Promise<SetupIntent>;\n\n/** Callback to check if a feature is enabled */\nexport type FeatureEnabledCallback = (params: {\n /** The key of the feature to evaluate */\n featureKey: string;\n /** The count to evaluate against the feature limit if there is one */\n count?: number;\n}) => boolean;\n\n/** Callback to get the limit for a feature */\nexport type FeatureLimitCallback = (params: {\n /** The key of the feature to evaluate */\n featureKey: string;\n}) => number;\n\n/** Callback to create a hosted session */\nexport type HostedSessionCallback = (params: {\n /** The type of hosted session to create */\n type: string;\n /** The configuration for the hosted session */\n config: Record<string, any>;\n}) => Promise<HostedSession>;\n\n/** Props for the MantleProvider component */\nexport interface MantleProviderProps {\n /** The Mantle App ID provided by Mantle */\n appId: string;\n /** The Mantle Customer API Token returned by the identify endpoint */\n customerApiToken: string;\n /** The Mantle API URL to use */\n apiUrl?: string;\n /** The children to render */\n children: React.ReactNode;\n /** The i18n object to use for labels */\n i18n?: typeof Labels;\n /** Whether to wait for the customer to be fetched */\n waitForCustomer?: boolean;\n /** The component to render while waiting for the customer to be fetched */\n loadingComponent?: React.ReactNode;\n}\n\n/** React Context for providing Mantle functionality throughout the app */\nconst MantleContext = createContext<TMantleContext | undefined>(undefined);\n\n/**\n * Evaluates whether a feature is enabled based on its type and value\n * @param feature - The feature to evaluate\n * @param count - The count to evaluate against if the feature is a limit type\n * @returns Whether the feature is considered enabled\n */\nconst evaluateFeature = ({ feature, count = 0 }: { feature: Feature; count?: number }): boolean => {\n if (feature?.type === \"boolean\") {\n return feature.value;\n } else if (feature?.type === \"limit\") {\n return count < feature.value || feature.value === -1;\n }\n return false;\n};\n\n/**\n * MantleProvider uses the React Context API to provide a MantleClient instance and\n * the current customer to its children, which can be accessed using the useMantle hook.\n * \n * @example\n * ```tsx\n * function App() {\n * return (\n * <MantleProvider\n * appId=\"your-app-id\"\n * customerApiToken=\"customer-token\"\n * >\n * <YourApp />\n * </MantleProvider>\n * );\n * }\n * ```\n */\nexport const MantleProvider: React.FC<MantleProviderProps> = ({\n appId,\n customerApiToken,\n apiUrl = \"https://appapi.heymantle.com/v1\",\n children,\n i18n = Labels,\n waitForCustomer = false,\n loadingComponent = null,\n}) => {\n const mantleClient = new MantleClient({ appId, customerApiToken, apiUrl });\n const [customer, setCustomer] = useState<Customer | null>(null);\n const [loading, setLoading] = useState(true);\n\n /**\n * Fetches the current customer from Mantle's API\n * Updates the customer state and handles loading states\n */\n const fetchCustomer = async () => {\n try {\n setLoading(true);\n const customer = await mantleClient.getCustomer();\n setCustomer(customer);\n } catch (error) {\n console.error(\"[MantleProvider] Error fetching customer: \", error);\n } finally {\n setLoading(false);\n }\n };\n\n /**\n * Sends a usage event to Mantle\n * @param usageEvent - The usage event to send\n */\n const sendUsageEvent: SendUsageEventCallback = async (usageEvent) => {\n await mantleClient.sendUsageEvent(usageEvent);\n };\n\n /**\n * Gets a usage report for a specific metric and period\n * @param params.usageId - The ID of the usage metric\n * @param params.period - The period to get the report for\n * @returns The usage report data\n */\n const getUsageReport: GetUsageReportCallback = async ({ usageId, period }) => {\n return await mantleClient.getUsageMetricReport({ id: usageId, period });\n };\n\n /**\n * Subscribes to one or more plans\n * @param params - Either SinglePlanSubscribe or MultiPlanSubscribe parameters\n * @returns The created subscription\n */\n const subscribe: SubscribeCallback = async (params) => {\n return await mantleClient.subscribe(params);\n };\n\n /**\n * Cancels the current subscription\n * @param params.cancelReason - Optional reason for cancellation\n * @returns The cancelled subscription\n */\n const cancelSubscription: CancelSubscriptionCallback = async ({ cancelReason } = {}) => {\n return await mantleClient.cancelSubscription({\n ...(cancelReason && { cancelReason }),\n });\n };\n\n /**\n * Initiates the process of adding a new payment method\n * @param params.returnUrl - The URL to return to after adding the payment method\n * @returns A SetupIntent for completing the payment method addition\n * @throws Error if returnUrl is not provided\n */\n const addPaymentMethod: AddPaymentMethodCallback = async ({ returnUrl }) => {\n if (!returnUrl) {\n throw new Error(\"returnUrl is required\");\n }\n return await mantleClient.addPaymentMethod({ returnUrl });\n };\n\n /**\n * Creates a hosted session for various checkout flows\n * @param params.type - The type of hosted session to create\n * @param params.config - Configuration options for the hosted session\n * @returns The created hosted session\n * @throws Error if type is not provided\n */\n const createHostedSession: HostedSessionCallback = async ({ type, config }) => {\n if (!type) {\n throw new Error(\"type is required\");\n }\n const searchParams = new URL(document.location.toString()).searchParams;\n const locale = searchParams.get(\"locale\");\n return await mantleClient.createHostedSession({\n type,\n config: {\n ...(locale ? { locale } : {}),\n ...(config || {}),\n },\n });\n };\n\n // Fetch customer when the token changes\n useEffect(() => {\n if (customerApiToken) {\n fetchCustomer();\n }\n }, [customerApiToken]);\n\n const plans = customer?.plans || [];\n const subscription = customer?.subscription || null;\n\n if (waitForCustomer && loading) {\n return loadingComponent || null;\n }\n\n return (\n <MantleContext.Provider\n value={{\n client: mantleClient,\n customer,\n subscription,\n plans,\n loading,\n i18n: { ...Labels, ...i18n },\n sendUsageEvent,\n getUsageReport,\n subscribe,\n cancelSubscription,\n addPaymentMethod,\n createHostedSession,\n isFeatureEnabled: ({ featureKey, count = 0 }) => {\n if (customer?.features[featureKey]) {\n return evaluateFeature({\n feature: customer.features[featureKey],\n count,\n });\n }\n return false;\n },\n limitForFeature: ({ featureKey }) => {\n if (\n customer?.features[featureKey] &&\n customer.features[featureKey].type === \"limit\"\n ) {\n return customer.features[featureKey].value;\n }\n return -1;\n },\n refetch: async () => {\n await fetchCustomer();\n },\n }}\n >\n {children}\n </MantleContext.Provider>\n );\n};\n\n/**\n * React hook to access the Mantle context\n * Must be used within a MantleProvider component\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { customer, subscription } = useMantle();\n * return <div>Hello {customer?.name}</div>;\n * }\n * ```\n * \n * @returns The Mantle context containing all Mantle functionality\n * @throws Error if used outside of a MantleProvider\n */\nexport const useMantle = (): TMantleContext => {\n const context = useContext(MantleContext);\n \n if (context === undefined) {\n throw new Error(\"useMantle must be used within a MantleProvider\");\n }\n\n return context;\n}; ","import type { Feature } from '@heymantle/client';\n\n/**\n * Determines if a feature is enabled based on its type and value\n * @param feature - The feature to check\n * @returns true if the feature is enabled\n */\nexport const featureEnabled = (feature: Feature): boolean => {\n return (\n (feature.type === \"boolean\" && feature.value === true) ||\n (feature.type === \"limit\" && feature.value !== 0)\n );\n};\n\n/**\n * Sorts features by enabled status (enabled first) and then by name\n * @param a - First feature to compare\n * @param b - Second feature to compare\n * @returns Sort order (-1, 0, or 1)\n */\nexport const featureSort = (a: Feature, b: Feature): number =>\n (Number(featureEnabled(b)) - Number(featureEnabled(a))) || a.name.localeCompare(b.name); ","/**\n * Creates a number formatter for the specified currency\n * @param currencyCode - The currency code to format for\n * @returns An Intl.NumberFormat instance configured for the currency\n */\nconst moneyFormatter = (currencyCode: string = \"USD\"): Intl.NumberFormat =>\n new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currencyCode,\n notation: \"standard\",\n });\n\n/**\n * Formats the given amount of money based on the specified currency code\n * @param amount - The amount of money to format\n * @param currencyCode - The currency code to use for formatting\n * @param removeCents - Whether to remove the cents from the formatted result\n * @returns The formatted money string\n * \n * @example\n * ```ts\n * money(1234.56) // \"$1,234.56\"\n * money(1234.56, \"USD\", true) // \"$1,234\"\n * money(1234.56, \"EUR\") // \"€1,234.56\"\n * ```\n */\nexport const money = (\n amount: number,\n currencyCode: string = \"USD\",\n removeCents: boolean = true\n): string => {\n let result = moneyFormatter(currencyCode).format(amount);\n if (removeCents) {\n result = result.replace(/\\.00$/, '');\n }\n return result;\n}; ","import type { Discount, Plan } from \"@heymantle/client\";\nimport { Labels } from \"./constants\";\n\nexport enum PlanInterval {\n Annual = \"ANNUAL\",\n Every30Days = \"EVERY_30_DAYS\",\n}\n\ntype IntervalLabelType = \"year\" | \"month\";\ntype IntervalLabelShortType = \"yr\" | \"mo\";\n\n/**\n * Generate a long label for the given interval\n * @param interval - The interval to generate a label for\n * @returns The long label for the interval\n */\nexport const intervalLabelLong = (interval: PlanInterval = PlanInterval.Every30Days): IntervalLabelType => {\n switch (interval) {\n case PlanInterval.Annual:\n return \"year\";\n case PlanInterval.Every30Days:\n default:\n return \"month\";\n }\n};\n\n/**\n * Generate a short label for the given interval\n * @param interval - The interval to generate a label for\n * @returns The short label for the interval\n */\nexport const intervalLabelShort = (interval: PlanInterval = PlanInterval.Every30Days): IntervalLabelShortType => {\n switch (interval) {\n case PlanInterval.Annual:\n return \"yr\";\n case PlanInterval.Every30Days:\n default:\n return \"mo\";\n }\n};\n\ninterface IntervalLabelParams {\n interval?: PlanInterval;\n useShortFormPlanIntervals?: boolean;\n}\n\n/**\n * Generate a label for the given interval and format\n * @param params.interval - The interval to generate a label for\n * @param params.useShortFormPlanIntervals - Whether to use short form plan intervals\n * @returns The label for the interval\n */\nexport const intervalLabel = ({\n interval = PlanInterval.Every30Days,\n useShortFormPlanIntervals = true,\n}: IntervalLabelParams): IntervalLabelType | IntervalLabelShortType => {\n return useShortFormPlanIntervals ? intervalLabelShort(interval) : intervalLabelLong(interval);\n};\n\ninterface PlanParams {\n plan: Plan;\n customFieldKey?: string;\n}\n\n/**\n * Check if the plan is recommended by using custom fields\n * @param params.plan - The Mantle plan to check\n * @param params.customFieldKey - The key to check for the recommended status\n * @returns Whether the plan is recommended\n */\nexport const isRecommendedPlan = ({ \n plan, \n customFieldKey = \"recommended\" \n}: PlanParams): boolean => {\n return !!plan.customFields?.[customFieldKey];\n};\n\n/**\n * Get the custom button label for the plan, or the default label\n * @param params.plan - The Mantle plan to check\n * @param params.customFieldKey - The key to check for the button label\n * @returns The custom button label or the default label\n */\nexport const customButtonLabel = ({ \n plan, \n customFieldKey = \"buttonLabel\" \n}: PlanParams): string => {\n return plan.customFields?.[customFieldKey] || Labels.subscribe;\n};\n\n/**\n * Get the best discount for the plan\n * @param params.plan - The Mantle plan to check for a discount\n * @returns The highest discount for the plan, or undefined if none found\n */\nexport const highestDiscount = ({ plan }: { plan: Plan }): Discount | undefined => {\n return plan.discounts?.length > 0\n ? plan.discounts.reduce((prev, current) =>\n prev.discountedAmount < current.discountedAmount ? prev : current\n )\n : undefined;\n}; ","interface ColumnSpans {\n xs: number;\n sm: number;\n md: number;\n lg: number;\n xl: number;\n}\n\n/**\n * Get the column span for the grid layout\n * @param count - The number of columns\n * @returns The column span for each screen size\n * \n * @example\n * ```ts\n * columnSpan(4) // { xs: 6, sm: 6, md: 2, lg: 3, xl: 3 }\n * columnSpan(3) // { xs: 6, sm: 6, md: 2, lg: 4, xl: 4 }\n * ```\n */\nexport const columnSpan = (count: number = 4): ColumnSpans => {\n if (count % 4 === 0) return { xs: 6, sm: 6, md: 2, lg: 3, xl: 3 };\n if (count % 3 === 0) return { xs: 6, sm: 6, md: 2, lg: 4, xl: 4 };\n if (count % 2 === 0) return { xs: 6, sm: 6, md: 3, lg: 6, xl: 6 };\n if (count === 1) return { xs: 6, sm: 6, md: 6, lg: 12, xl: 12 };\n return { xs: 6, sm: 6, md: 2, lg: 4, xl: 4 };\n};\n\n/**\n * Get the column count for the grid layout\n * @param size - The number of items\n * @returns The number of columns\n * \n * @example\n * ```ts\n * columnCount(4) // 4\n * columnCount(3) // 3\n * columnCount(2) // 2\n * ```\n */\nexport const columnCount = (size: number = 4): number => {\n if (size % 4 === 0) return 4;\n if (size % 3 === 0) return 3;\n if (size % 2 === 0) return 2;\n if (size === 1) return 1;\n return 4;\n}; "],"names":["PlanAvailability","Labels","MantleContext","createContext","evaluateFeature","feature","count","MantleProvider","appId","customerApiToken","apiUrl","children","i18n","waitForCustomer","loadingComponent","mantleClient","MantleClient","customer","setCustomer","useState","loading","setLoading","fetchCustomer","error","sendUsageEvent","usageEvent","getUsageReport","usageId","period","subscribe","params","cancelSubscription","cancelReason","addPaymentMethod","returnUrl","createHostedSession","type","config","locale","useEffect","plans","subscription","jsx","featureKey","useMantle","context","useContext","featureEnabled","featureSort","a","b","moneyFormatter","currencyCode","money","amount","removeCents","result","PlanInterval","intervalLabelLong","interval","intervalLabelShort","intervalLabel","useShortFormPlanIntervals","isRecommendedPlan","plan","customFieldKey","_a","customButtonLabel","highestDiscount","prev","current","columnSpan","columnCount","size"],"mappings":"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"}