@getuserfeedback/react 0.1.2 → 0.2.1
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 +3 -4
- package/dist/index.d.ts +25 -41
- package/dist/index.js +2 -2
- package/dist/index.js.map +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ By default, flows are displayed in a floating container and [themed](https://get
|
|
|
69
69
|
For example, if you're using shadcn/ui dialogs throughout your product, our widgets can be seamlessly rendered into them and appear just like any other part of your app.
|
|
70
70
|
|
|
71
71
|
```tsx
|
|
72
|
-
import {
|
|
72
|
+
import { useFlow } from "@getuserfeedback/react";
|
|
73
73
|
import { Button, Dialog, DialogContent } from "ui";
|
|
74
74
|
|
|
75
75
|
export function FeedbackDialog() {
|
|
@@ -80,7 +80,7 @@ export function FeedbackDialog() {
|
|
|
80
80
|
containerRef,
|
|
81
81
|
width,
|
|
82
82
|
height
|
|
83
|
-
} =
|
|
83
|
+
} = useFlow({ flowId: "YOUR_FLOW_ID", container: "custom" });
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<>
|
|
@@ -106,8 +106,7 @@ export function FeedbackDialog() {
|
|
|
106
106
|
|
|
107
107
|
- `GetUserFeedbackProvider`
|
|
108
108
|
- `useGetUserFeedback()`
|
|
109
|
-
- `useFlow({ flowId, prefetchOnMount
|
|
110
|
-
- `useFlowContainer({ flowId, prefetchOnMount? })`
|
|
109
|
+
- `useFlow({ flowId, prefetchOnMount?, hideCloseButton?, container?: "default" | "custom" })`
|
|
111
110
|
- `useDefaultFlowContainer()`
|
|
112
111
|
|
|
113
112
|
## `GetUserFeedbackProvider`
|
package/dist/index.d.ts
CHANGED
|
@@ -9,11 +9,19 @@ interface UseFlowOptions {
|
|
|
9
9
|
flowId: string;
|
|
10
10
|
/** Prefetch the flow resources on mount so it opens faster. Default `false`. */
|
|
11
11
|
prefetchOnMount?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Where the flow mounts: `"default"` (floating container) or `"custom"` (your container via containerRef).
|
|
14
|
+
* Default `"default"`.
|
|
15
|
+
*/
|
|
16
|
+
container?: "default" | "custom";
|
|
17
|
+
/** When true, the view does not show a close button. */
|
|
18
|
+
hideCloseButton?: boolean;
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
|
-
* Return value for useFlow()
|
|
21
|
+
* Return value for useFlow() when container is `"default"`: state and callbacks for the floating flow.
|
|
15
22
|
*/
|
|
16
23
|
interface UseFlowReturn {
|
|
24
|
+
readonly container: "default";
|
|
17
25
|
/** True when the flow is visible. */
|
|
18
26
|
isOpen: boolean;
|
|
19
27
|
/** True when the flow is loading. */
|
|
@@ -30,9 +38,10 @@ interface UseFlowReturn {
|
|
|
30
38
|
height?: number;
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
|
-
* Return value for
|
|
41
|
+
* Return value for useFlow() when container is `"custom"`: state and callbacks for host-managed container flows.
|
|
34
42
|
*/
|
|
35
43
|
interface UseFlowContainerReturn {
|
|
44
|
+
readonly container: "custom";
|
|
36
45
|
/** True when the flow is visible. */
|
|
37
46
|
isOpen: boolean;
|
|
38
47
|
/** True when the flow is loading. */
|
|
@@ -78,8 +87,8 @@ interface GetUserFeedbackProviderProps {
|
|
|
78
87
|
}
|
|
79
88
|
/**
|
|
80
89
|
* Provider for getuserfeedback React hooks.
|
|
81
|
-
* Wrap your app (or a subtree) so `useFlow`, `
|
|
82
|
-
* `
|
|
90
|
+
* Wrap your app (or a subtree) so `useFlow`, `useDefaultFlowContainer`, and
|
|
91
|
+
* `useGetUserFeedback` can access a client
|
|
83
92
|
* created from `clientOptions`.
|
|
84
93
|
*
|
|
85
94
|
* If your app uses `useDefaultFlowContainer()`, all flows in this subtree
|
|
@@ -122,47 +131,22 @@ interface GetUserFeedbackProviderProps {
|
|
|
122
131
|
declare function GetUserFeedbackProvider({ children, clientOptions, }: GetUserFeedbackProviderProps): ReactElement;
|
|
123
132
|
/**
|
|
124
133
|
* @name useFlow
|
|
125
|
-
* @description Imperatively display a flow.
|
|
126
|
-
* @example
|
|
134
|
+
* @description Imperatively display a flow in the default floating container or your own container.
|
|
135
|
+
* @example Default (floating) container
|
|
127
136
|
* ```tsx
|
|
128
|
-
*
|
|
129
|
-
* const { open, isLoading } = useFlow({ flowId: "sur_123" });
|
|
130
|
-
* return <Button onClick={() => open()} disabled={isLoading}>Feedback</Button>;
|
|
131
|
-
* }
|
|
137
|
+
* const { open, isLoading } = useFlow({ flowId: "sur_123" });
|
|
132
138
|
* ```
|
|
133
|
-
|
|
134
|
-
declare function useFlow(flow: UseFlowOptions): UseFlowReturn;
|
|
135
|
-
/**
|
|
136
|
-
* @name useFlowContainer
|
|
137
|
-
* @description Imperatively display a flow in your own container element.
|
|
138
|
-
* @example In a custom dialog
|
|
139
|
+
* @example Custom container
|
|
139
140
|
* ```tsx
|
|
140
|
-
*
|
|
141
|
-
* const { containerRef, shouldRenderContainer, width, height, setOpen, isLoading } =
|
|
142
|
-
* useFlowContainer({ flowId: "sur_123" });
|
|
143
|
-
*
|
|
144
|
-
* return (
|
|
145
|
-
* <>
|
|
146
|
-
* <Button onClick={() => setOpen(true)} loading={isLoading}>
|
|
147
|
-
* Feedback
|
|
148
|
-
* </Button>
|
|
149
|
-
* <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>
|
|
150
|
-
* <DialogContent ref={containerRef} style={{ width, height }} />
|
|
151
|
-
* </Dialog>
|
|
152
|
-
* </>
|
|
153
|
-
* );
|
|
154
|
-
* }
|
|
155
|
-
*
|
|
156
|
-
* function App() {
|
|
157
|
-
* return (
|
|
158
|
-
* <GetUserFeedbackProvider clientOptions={{ apiKey: "YOUR_API_KEY" }}>
|
|
159
|
-
* <FlowContainerDialog />
|
|
160
|
-
* </GetUserFeedbackProvider>
|
|
161
|
-
* );
|
|
162
|
-
* }
|
|
141
|
+
* const { containerRef, shouldRenderContainer, setOpen } = useFlow({ flowId: "sur_123", container: "custom" });
|
|
163
142
|
* ```
|
|
164
143
|
*/
|
|
165
|
-
declare function
|
|
144
|
+
declare function useFlow(options: UseFlowOptions & {
|
|
145
|
+
container?: "default";
|
|
146
|
+
}): UseFlowReturn;
|
|
147
|
+
declare function useFlow(options: UseFlowOptions & {
|
|
148
|
+
container: "custom";
|
|
149
|
+
}): UseFlowContainerReturn;
|
|
166
150
|
/**
|
|
167
151
|
* @name useDefaultFlowContainer
|
|
168
152
|
* @description Register and control a default flow container for all flows in this provider subtree.
|
|
@@ -212,5 +196,5 @@ declare function useGetUserFeedback(): Client;
|
|
|
212
196
|
|
|
213
197
|
declare const REACT_SDK_VERSION: string;
|
|
214
198
|
|
|
215
|
-
export { GetUserFeedbackProvider, REACT_SDK_VERSION, useDefaultFlowContainer, useFlow,
|
|
199
|
+
export { GetUserFeedbackProvider, REACT_SDK_VERSION, useDefaultFlowContainer, useFlow, useGetUserFeedback };
|
|
216
200
|
export type { GetUserFeedbackProviderProps, UseDefaultFlowContainerReturn, UseFlowContainerReturn, UseFlowOptions, UseFlowReturn };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{createClient as
|
|
2
|
+
import{createClient as Q}from"@getuserfeedback/sdk";import{createContext as W,useCallback as I,useContext as z,useEffect as T,useMemo as h,useRef as B,useState as F}from"react";import{jsx as V}from"react/jsx-runtime";var X={isOpen:!1,isLoading:!1,shouldRenderContainer:!1,width:void 0,height:void 0},q=W(null);function Y({children:G,clientOptions:_}){let R=Q(_),U=B(null),v=B(0),[P,p]=F(()=>({...R.getFlowState()})),A=I((E)=>{if(U.current=E,v.current===0)return;R.setDefaultContainerPolicy({kind:"hostContainer",host:E,sharing:"perFlowRun"})},[R]),b=I(()=>{if(v.current+=1,v.current===1)R.setDefaultContainerPolicy({kind:"hostContainer",host:U.current,sharing:"perFlowRun"});return()=>{if(v.current=Math.max(0,v.current-1),v.current===0)R.setDefaultContainerPolicy({kind:"floating"})}},[R]);T(()=>R.subscribeFlowState((E)=>{p({isOpen:E.isOpen,isLoading:E.isLoading,width:E.width,height:E.height})},{emitInitial:!0}),[R]);let N=h(()=>{if(!P.isOpen&&!P.isLoading)return X;return{isOpen:P.isOpen,isLoading:P.isLoading,shouldRenderContainer:!0,width:P.width,height:P.height}},[P]),O=h(()=>({client:R,apiKey:_.apiKey,setDefaultFlowContainer:A,registerDefaultFlowContainerConsumer:b,defaultFlowContainerState:N}),[R,_.apiKey,N,b,A]);return V(q.Provider,{value:O,children:G})}var Z=(G)=>{let _=z(q);if(!_)throw Error("useFlow must be used within a GetUserFeedbackProvider");let{client:R}=_,{flowId:U,prefetchOnMount:v=!1,hideCloseButton:P}=G.flow,p=h(()=>R.flow(U),[R,U]),A=G.mode==="container",[b,N]=F(()=>p.getFlowState()),O=h(()=>({containerRequirement:A?"hostOnly":void 0,hideCloseButton:P}),[A,P]),E=I(()=>{return p.open(O)},[p,O]),y=I(()=>p.close(),[p]),J=I((K)=>K?E():y(),[y,E]),L=I((K)=>{if(!A)return;p.setContainer(K)},[p,A]),M=A&&(b.isLoading||b.isOpen);return T(()=>p.subscribeFlowState((K)=>N({isOpen:K.isOpen,isLoading:K.isLoading,width:K.width,height:K.height}),{emitInitial:!1}),[p]),T(()=>{if(!v)return;p.prefetch()},[p,v]),{isOpen:b.isOpen,isLoading:b.isLoading,width:b.width,height:b.height,shouldRenderContainer:M,setOpen:J,open:E,close:y,prerender:()=>p.prerender({hideCloseButton:P}),containerRef:L}};function $(G){let R=(G.container??"default")==="custom"?"container":"default",U=Z({flow:{flowId:G.flowId,prefetchOnMount:G.prefetchOnMount,hideCloseButton:G.hideCloseButton},mode:R});if(R==="container")return{container:"custom",isOpen:U.isOpen,isLoading:U.isLoading,width:U.width,height:U.height,shouldRenderContainer:U.shouldRenderContainer,setOpen:U.setOpen,prerender:U.prerender,containerRef:U.containerRef};return{container:"default",isOpen:U.isOpen,isLoading:U.isLoading,width:U.width,height:U.height,open:U.open,close:U.close,prerender:U.prerender}}function j(){let G=z(q);if(!G)throw Error("useDefaultFlowContainer must be used within a GetUserFeedbackProvider");let{client:_,defaultFlowContainerState:R,registerDefaultFlowContainerConsumer:U,setDefaultFlowContainer:v}=G;T(()=>U(),[U]);let P=I((b)=>{v(b)},[v]),p=I(()=>_.close(),[_]),A=I((b)=>{if(b)return Promise.resolve();return p()},[p]);return{isOpen:R.isOpen,isLoading:R.isLoading,shouldRenderContainer:R.shouldRenderContainer,width:R.width,height:R.height,setOpen:A,containerRef:P}}function D(){let G=z(q);if(!G)throw Error("useGetUserFeedback must be used within a GetUserFeedbackProvider");return G.client}var H="0.2.1".trim(),m=H.length>0?H:"0.0.0-local";export{D as useGetUserFeedback,$ as useFlow,j as useDefaultFlowContainer,m as REACT_SDK_VERSION,Y as GetUserFeedbackProvider};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=903A5C6C86A3DECB64756E2164756E21
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client.tsx", "../src/version.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"\"use client\";\n\nimport {\n\ttype Client,\n\ttype ClientOptions,\n\tcreateClient,\n\ttype FlowState,\n} from \"@getuserfeedback/sdk\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\n\n/**\n * Options for useFlow().\n */\nexport interface UseFlowOptions {\n\t/** The flow/survey ID to control. */\n\tflowId: string;\n\t/** Prefetch the flow resources on mount so it opens faster. Default `false`. */\n\tprefetchOnMount?: boolean;\n}\n\n/**\n * Return value for useFlow(): state and callbacks to show the default floating flow.\n */\nexport interface UseFlowReturn {\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True when the flow is loading. */\n\tisLoading: boolean;\n\t/** Open the flow. */\n\topen: () => Promise<void>;\n\t/** Close the flow. */\n\tclose: () => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useFlowContainer(): state and callbacks for host-managed container flows.\n */\nexport interface UseFlowContainerReturn {\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True when the flow is loading. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the custom container element. */\n\tshouldRenderContainer: boolean;\n\t/** Open the flow when `true`, close when `false`. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Ref callback: attach this to the container element where the flow should render. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useDefaultFlowContainer(): container ref and aggregate flow state\n * for all flows in the same provider subtree (including targeting-engine opens).\n */\nexport interface UseDefaultFlowContainerReturn {\n\t/** True when any flow is visible. */\n\tisOpen: boolean;\n\t/** True when any flow is loading. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the default flow container. */\n\tshouldRenderContainer: boolean;\n\t/** Active flow width in pixels (when known). */\n\twidth?: number;\n\t/** Active flow height in pixels (when known). */\n\theight?: number;\n\t/** Close currently displayed flows when `false`; opening is flow-driven. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Ref callback for the default flow container element. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n}\n\ntype InstanceFlowRenderState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\ntype DefaultFlowContainerState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\tshouldRenderContainer: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\nconst DEFAULT_FLOW_CONTAINER_STATE: DefaultFlowContainerState = {\n\tisOpen: false,\n\tisLoading: false,\n\tshouldRenderContainer: false,\n\twidth: undefined,\n\theight: undefined,\n};\n\ntype GetUserFeedbackContextValue = {\n\tclient: Client;\n\tapiKey: string;\n\tsetDefaultFlowContainer: (element: HTMLElement | null) => void;\n\tregisterDefaultFlowContainerConsumer: () => () => void;\n\tdefaultFlowContainerState: DefaultFlowContainerState;\n};\n\nconst GetUserFeedbackContext =\n\tcreateContext<GetUserFeedbackContextValue | null>(null);\n\nexport interface GetUserFeedbackProviderProps {\n\t/** Application subtree that can use getuserfeedback hooks. */\n\tchildren: ReactNode;\n\t/** Core SDK client options used to create/reuse the underlying client. */\n\tclientOptions: ClientOptions;\n}\n\n/**\n * Provider for getuserfeedback React hooks.\n * Wrap your app (or a subtree) so `useFlow`, `useFlowContainer`,\n * `useDefaultFlowContainer`, and `useGetUserFeedback` can access a client\n * created from `clientOptions`.\n *\n * If your app uses `useDefaultFlowContainer()`, all flows in this subtree\n * (including targeting-engine opens) target that container.\n * If the container ref is not mounted yet, loader keeps flows parked until it is.\n *\n * @example Basic usage\n * ```tsx\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <Routes />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n *\n * @example Display *any* flow in your own dialog (a shadcn/ui dialog in this example):\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n *\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer /> // Flows will be triggered by the targeting engine and displayed in your own dialog\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function GetUserFeedbackProvider({\n\tchildren,\n\tclientOptions,\n}: GetUserFeedbackProviderProps): ReactElement {\n\tconst client = createClient(clientOptions);\n\tconst defaultFlowContainerRef = useRef<HTMLElement | null>(null);\n\tconst defaultFlowContainerConsumerCountRef = useRef(0);\n\tconst [instanceFlowState, setInstanceFlowState] =\n\t\tuseState<InstanceFlowRenderState>(() => ({\n\t\t\t...client.getFlowState(),\n\t\t}));\n\n\tconst setDefaultFlowContainer = useCallback(\n\t\t(element: HTMLElement | null) => {\n\t\t\tdefaultFlowContainerRef.current = element;\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: element,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t},\n\t\t[client],\n\t);\n\n\tconst registerDefaultFlowContainerConsumer = useCallback(() => {\n\t\tdefaultFlowContainerConsumerCountRef.current += 1;\n\t\tif (defaultFlowContainerConsumerCountRef.current === 1) {\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: defaultFlowContainerRef.current,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t}\n\t\treturn () => {\n\t\t\tdefaultFlowContainerConsumerCountRef.current = Math.max(\n\t\t\t\t0,\n\t\t\t\tdefaultFlowContainerConsumerCountRef.current - 1,\n\t\t\t);\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\tclient.setDefaultContainerPolicy({ kind: \"floating\" });\n\t\t\t}\n\t\t};\n\t}, [client]);\n\n\tuseEffect(\n\t\t() =>\n\t\t\tclient.subscribeFlowState(\n\t\t\t\t(state) => {\n\t\t\t\t\tsetInstanceFlowState({\n\t\t\t\t\t\tisOpen: state.isOpen,\n\t\t\t\t\t\tisLoading: state.isLoading,\n\t\t\t\t\t\twidth: state.width,\n\t\t\t\t\t\theight: state.height,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\temitInitial: true,\n\t\t\t\t},\n\t\t\t),\n\t\t[client],\n\t);\n\n\tconst defaultFlowContainerState = useMemo<DefaultFlowContainerState>(() => {\n\t\tif (!instanceFlowState.isOpen && !instanceFlowState.isLoading) {\n\t\t\treturn DEFAULT_FLOW_CONTAINER_STATE;\n\t\t}\n\t\treturn {\n\t\t\tisOpen: instanceFlowState.isOpen,\n\t\t\tisLoading: instanceFlowState.isLoading,\n\t\t\tshouldRenderContainer: true,\n\t\t\twidth: instanceFlowState.width,\n\t\t\theight: instanceFlowState.height,\n\t\t};\n\t}, [instanceFlowState]);\n\n\tconst contextValue = useMemo(\n\t\t() => ({\n\t\t\tclient,\n\t\t\tapiKey: clientOptions.apiKey,\n\t\t\tsetDefaultFlowContainer,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tdefaultFlowContainerState,\n\t\t}),\n\t\t[\n\t\t\tclient,\n\t\t\tclientOptions.apiKey,\n\t\t\tdefaultFlowContainerState,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tsetDefaultFlowContainer,\n\t\t],\n\t);\n\n\treturn (\n\t\t<GetUserFeedbackContext.Provider value={contextValue}>\n\t\t\t{children}\n\t\t</GetUserFeedbackContext.Provider>\n\t);\n}\n\ntype UseFlowControllerOptions = {\n\tflow: UseFlowOptions;\n\tmode: \"default\" | \"container\";\n};\n\ntype UseFlowControllerReturn = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n\tshouldRenderContainer: boolean;\n\tsetOpen: (next: boolean) => Promise<void>;\n\topen: () => Promise<void>;\n\tclose: () => Promise<void>;\n\tprerender: () => Promise<void>;\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n};\n\nconst useFlowController = (\n\toptions: UseFlowControllerOptions,\n): UseFlowControllerReturn => {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useFlow must be used within a GetUserFeedbackProvider\");\n\t}\n\tconst { client } = ctx;\n\tconst { flowId, prefetchOnMount = false } = options.flow;\n\tconst flowRun = useMemo(() => client.flow(flowId), [client, flowId]);\n\tconst requiresContainer = options.mode === \"container\";\n\n\t/** Flow state from loader events; open/loading + dimensions. */\n\tconst [flowState, setFlowState] = useState<FlowState>(() =>\n\t\tflowRun.getFlowState(),\n\t);\n\n\tconst open = useCallback((): Promise<void> => {\n\t\treturn flowRun.open(\n\t\t\trequiresContainer ? { containerRequirement: \"hostOnly\" } : undefined,\n\t\t);\n\t}, [flowRun, requiresContainer]);\n\n\tconst close = useCallback((): Promise<void> => flowRun.close(), [flowRun]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => (next ? open() : close()),\n\t\t[close, open],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tif (!requiresContainer) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tflowRun.setContainer(element);\n\t\t},\n\t\t[flowRun, requiresContainer],\n\t);\n\n\tconst shouldRenderContainer =\n\t\trequiresContainer && (flowState.isLoading || flowState.isOpen);\n\n\tuseEffect(\n\t\t() =>\n\t\t\tflowRun.subscribeFlowState(\n\t\t\t\t(state) =>\n\t\t\t\t\tsetFlowState({\n\t\t\t\t\t\tisOpen: state.isOpen,\n\t\t\t\t\t\tisLoading: state.isLoading,\n\t\t\t\t\t\twidth: state.width,\n\t\t\t\t\t\theight: state.height,\n\t\t\t\t\t}),\n\t\t\t\t{\n\t\t\t\t\temitInitial: false,\n\t\t\t\t},\n\t\t\t),\n\t\t[flowRun],\n\t);\n\n\tuseEffect(() => {\n\t\tif (!prefetchOnMount) {\n\t\t\treturn;\n\t\t}\n\t\tflowRun.prefetch();\n\t}, [flowRun, prefetchOnMount]);\n\n\treturn {\n\t\tisOpen: flowState.isOpen,\n\t\tisLoading: flowState.isLoading,\n\t\twidth: flowState.width,\n\t\theight: flowState.height,\n\t\tshouldRenderContainer,\n\t\tsetOpen,\n\t\topen,\n\t\tclose,\n\t\tprerender: () => flowRun.prerender(),\n\t\tcontainerRef,\n\t};\n};\n\n/**\n * @name useFlow\n * @description Imperatively display a flow.\n * @example Basic usage\n * ```tsx\n * function FeedbackButton() {\n * const { open, isLoading } = useFlow({ flowId: \"sur_123\" });\n * return <Button onClick={() => open()} disabled={isLoading}>Feedback</Button>;\n * }\n * ```\n */\nexport function useFlow(flow: UseFlowOptions): UseFlowReturn {\n\tconst controller = useFlowController({ flow, mode: \"default\" });\n\treturn {\n\t\tisOpen: controller.isOpen,\n\t\tisLoading: controller.isLoading,\n\t\twidth: controller.width,\n\t\theight: controller.height,\n\t\topen: controller.open,\n\t\tclose: controller.close,\n\t\tprerender: controller.prerender,\n\t};\n}\n\n/**\n * @name useFlowContainer\n * @description Imperatively display a flow in your own container element.\n * @example In a custom dialog\n * ```tsx\n * function FlowContainerDialog() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen, isLoading } =\n * useFlowContainer({ flowId: \"sur_123\" });\n *\n * return (\n * <>\n * <Button onClick={() => setOpen(true)} loading={isLoading}>\n * Feedback\n * </Button>\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * </>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <FlowContainerDialog />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function useFlowContainer(flow: UseFlowOptions): UseFlowContainerReturn {\n\tconst controller = useFlowController({ flow, mode: \"container\" });\n\treturn {\n\t\tisOpen: controller.isOpen,\n\t\tisLoading: controller.isLoading,\n\t\twidth: controller.width,\n\t\theight: controller.height,\n\t\tshouldRenderContainer: controller.shouldRenderContainer,\n\t\tsetOpen: controller.setOpen,\n\t\tprerender: controller.prerender,\n\t\tcontainerRef: controller.containerRef,\n\t};\n}\n\n/**\n * @name useDefaultFlowContainer\n * @description Register and control a default flow container for all flows in this provider subtree.\n * @example Display any flow in your own dialog\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function useDefaultFlowContainer(): UseDefaultFlowContainerReturn {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useDefaultFlowContainer must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\n\tconst {\n\t\tclient,\n\t\tdefaultFlowContainerState,\n\t\tregisterDefaultFlowContainerConsumer,\n\t\tsetDefaultFlowContainer,\n\t} = ctx;\n\n\tuseEffect(\n\t\t() => registerDefaultFlowContainerConsumer(),\n\t\t[registerDefaultFlowContainerConsumer],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tsetDefaultFlowContainer(element);\n\t\t},\n\t\t[setDefaultFlowContainer],\n\t);\n\n\tconst close = useCallback((): Promise<void> => client.close(), [client]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => {\n\t\t\tif (next) {\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t\treturn close();\n\t\t},\n\t\t[close],\n\t);\n\n\treturn {\n\t\tisOpen: defaultFlowContainerState.isOpen,\n\t\tisLoading: defaultFlowContainerState.isLoading,\n\t\tshouldRenderContainer: defaultFlowContainerState.shouldRenderContainer,\n\t\twidth: defaultFlowContainerState.width,\n\t\theight: defaultFlowContainerState.height,\n\t\tsetOpen,\n\t\tcontainerRef,\n\t};\n}\n\n/**\n * Hook for accessing the getuserfeedback client instance.\n *\n * @example Basic usage\n * ```tsx\n * const client = useGetUserFeedback();\n *\n * // on login\n * client.identify('user-123', { email: 'user@example.com' });\n *\n * // on logout\n * client.reset();\n * ```\n *\n * @example Override color scheme\n * ```tsx\n * const client = useGetUserFeedback();\n * await client.configure({ colorScheme: \"dark\" });\n * ```\n */\nexport function useGetUserFeedback(): Client {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useGetUserFeedback must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\treturn ctx.client;\n}\n",
|
|
5
|
+
"\"use client\";\n\nimport {\n\ttype Client,\n\ttype ClientOptions,\n\tcreateClient,\n\ttype FlowState,\n} from \"@getuserfeedback/sdk\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\n\n/**\n * Options for useFlow().\n */\nexport interface UseFlowOptions {\n\t/** The flow/survey ID to control. */\n\tflowId: string;\n\t/** Prefetch the flow resources on mount so it opens faster. Default `false`. */\n\tprefetchOnMount?: boolean;\n\t/**\n\t * Where the flow mounts: `\"default\"` (floating container) or `\"custom\"` (your container via containerRef).\n\t * Default `\"default\"`.\n\t */\n\tcontainer?: \"default\" | \"custom\";\n\t/** When true, the view does not show a close button. */\n\thideCloseButton?: boolean;\n}\n\n/**\n * Return value for useFlow() when container is `\"default\"`: state and callbacks for the floating flow.\n */\nexport interface UseFlowReturn {\n\treadonly container: \"default\";\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True when the flow is loading. */\n\tisLoading: boolean;\n\t/** Open the flow. */\n\topen: () => Promise<void>;\n\t/** Close the flow. */\n\tclose: () => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useFlow() when container is `\"custom\"`: state and callbacks for host-managed container flows.\n */\nexport interface UseFlowContainerReturn {\n\treadonly container: \"custom\";\n\t/** True when the flow is visible. */\n\tisOpen: boolean;\n\t/** True when the flow is loading. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the custom container element. */\n\tshouldRenderContainer: boolean;\n\t/** Open the flow when `true`, close when `false`. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Prerendering warms up the flow for instant display later. */\n\tprerender: () => Promise<void>;\n\t/** Ref callback: attach this to the container element where the flow should render. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n\t/** Flow width in pixels (when known). */\n\twidth?: number;\n\t/** Flow height in pixels (when known). */\n\theight?: number;\n}\n\n/**\n * Return value for useDefaultFlowContainer(): container ref and aggregate flow state\n * for all flows in the same provider subtree (including targeting-engine opens).\n */\nexport interface UseDefaultFlowContainerReturn {\n\t/** True when any flow is visible. */\n\tisOpen: boolean;\n\t/** True when any flow is loading. */\n\tisLoading: boolean;\n\t/** True when your host UI should render the default flow container. */\n\tshouldRenderContainer: boolean;\n\t/** Active flow width in pixels (when known). */\n\twidth?: number;\n\t/** Active flow height in pixels (when known). */\n\theight?: number;\n\t/** Close currently displayed flows when `false`; opening is flow-driven. */\n\tsetOpen: (next: boolean) => Promise<void>;\n\t/** Ref callback for the default flow container element. */\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n}\n\ntype InstanceFlowRenderState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\ntype DefaultFlowContainerState = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\tshouldRenderContainer: boolean;\n\twidth?: number;\n\theight?: number;\n};\n\nconst DEFAULT_FLOW_CONTAINER_STATE: DefaultFlowContainerState = {\n\tisOpen: false,\n\tisLoading: false,\n\tshouldRenderContainer: false,\n\twidth: undefined,\n\theight: undefined,\n};\n\ntype GetUserFeedbackContextValue = {\n\tclient: Client;\n\tapiKey: string;\n\tsetDefaultFlowContainer: (element: HTMLElement | null) => void;\n\tregisterDefaultFlowContainerConsumer: () => () => void;\n\tdefaultFlowContainerState: DefaultFlowContainerState;\n};\n\nconst GetUserFeedbackContext =\n\tcreateContext<GetUserFeedbackContextValue | null>(null);\n\nexport interface GetUserFeedbackProviderProps {\n\t/** Application subtree that can use getuserfeedback hooks. */\n\tchildren: ReactNode;\n\t/** Core SDK client options used to create/reuse the underlying client. */\n\tclientOptions: ClientOptions;\n}\n\n/**\n * Provider for getuserfeedback React hooks.\n * Wrap your app (or a subtree) so `useFlow`, `useDefaultFlowContainer`, and\n * `useGetUserFeedback` can access a client\n * created from `clientOptions`.\n *\n * If your app uses `useDefaultFlowContainer()`, all flows in this subtree\n * (including targeting-engine opens) target that container.\n * If the container ref is not mounted yet, loader keeps flows parked until it is.\n *\n * @example Basic usage\n * ```tsx\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <Routes />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n *\n * @example Display *any* flow in your own dialog (a shadcn/ui dialog in this example):\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n *\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer /> // Flows will be triggered by the targeting engine and displayed in your own dialog\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function GetUserFeedbackProvider({\n\tchildren,\n\tclientOptions,\n}: GetUserFeedbackProviderProps): ReactElement {\n\tconst client = createClient(clientOptions);\n\tconst defaultFlowContainerRef = useRef<HTMLElement | null>(null);\n\tconst defaultFlowContainerConsumerCountRef = useRef(0);\n\tconst [instanceFlowState, setInstanceFlowState] =\n\t\tuseState<InstanceFlowRenderState>(() => ({\n\t\t\t...client.getFlowState(),\n\t\t}));\n\n\tconst setDefaultFlowContainer = useCallback(\n\t\t(element: HTMLElement | null) => {\n\t\t\tdefaultFlowContainerRef.current = element;\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: element,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t},\n\t\t[client],\n\t);\n\n\tconst registerDefaultFlowContainerConsumer = useCallback(() => {\n\t\tdefaultFlowContainerConsumerCountRef.current += 1;\n\t\tif (defaultFlowContainerConsumerCountRef.current === 1) {\n\t\t\tclient.setDefaultContainerPolicy({\n\t\t\t\tkind: \"hostContainer\",\n\t\t\t\thost: defaultFlowContainerRef.current,\n\t\t\t\tsharing: \"perFlowRun\",\n\t\t\t});\n\t\t}\n\t\treturn () => {\n\t\t\tdefaultFlowContainerConsumerCountRef.current = Math.max(\n\t\t\t\t0,\n\t\t\t\tdefaultFlowContainerConsumerCountRef.current - 1,\n\t\t\t);\n\t\t\tif (defaultFlowContainerConsumerCountRef.current === 0) {\n\t\t\t\tclient.setDefaultContainerPolicy({ kind: \"floating\" });\n\t\t\t}\n\t\t};\n\t}, [client]);\n\n\tuseEffect(\n\t\t() =>\n\t\t\tclient.subscribeFlowState(\n\t\t\t\t(state) => {\n\t\t\t\t\tsetInstanceFlowState({\n\t\t\t\t\t\tisOpen: state.isOpen,\n\t\t\t\t\t\tisLoading: state.isLoading,\n\t\t\t\t\t\twidth: state.width,\n\t\t\t\t\t\theight: state.height,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\temitInitial: true,\n\t\t\t\t},\n\t\t\t),\n\t\t[client],\n\t);\n\n\tconst defaultFlowContainerState = useMemo<DefaultFlowContainerState>(() => {\n\t\tif (!instanceFlowState.isOpen && !instanceFlowState.isLoading) {\n\t\t\treturn DEFAULT_FLOW_CONTAINER_STATE;\n\t\t}\n\t\treturn {\n\t\t\tisOpen: instanceFlowState.isOpen,\n\t\t\tisLoading: instanceFlowState.isLoading,\n\t\t\tshouldRenderContainer: true,\n\t\t\twidth: instanceFlowState.width,\n\t\t\theight: instanceFlowState.height,\n\t\t};\n\t}, [instanceFlowState]);\n\n\tconst contextValue = useMemo(\n\t\t() => ({\n\t\t\tclient,\n\t\t\tapiKey: clientOptions.apiKey,\n\t\t\tsetDefaultFlowContainer,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tdefaultFlowContainerState,\n\t\t}),\n\t\t[\n\t\t\tclient,\n\t\t\tclientOptions.apiKey,\n\t\t\tdefaultFlowContainerState,\n\t\t\tregisterDefaultFlowContainerConsumer,\n\t\t\tsetDefaultFlowContainer,\n\t\t],\n\t);\n\n\treturn (\n\t\t<GetUserFeedbackContext.Provider value={contextValue}>\n\t\t\t{children}\n\t\t</GetUserFeedbackContext.Provider>\n\t);\n}\n\ntype UseFlowControllerOptions = {\n\tflow: UseFlowOptions;\n\tmode: \"default\" | \"container\";\n};\n\ntype UseFlowControllerReturn = {\n\tisOpen: boolean;\n\tisLoading: boolean;\n\twidth?: number;\n\theight?: number;\n\tshouldRenderContainer: boolean;\n\tsetOpen: (next: boolean) => Promise<void>;\n\topen: () => Promise<void>;\n\tclose: () => Promise<void>;\n\tprerender: () => Promise<void>;\n\tcontainerRef: (element: HTMLDivElement | null) => void;\n};\n\nconst useFlowController = (\n\toptions: UseFlowControllerOptions,\n): UseFlowControllerReturn => {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\"useFlow must be used within a GetUserFeedbackProvider\");\n\t}\n\tconst { client } = ctx;\n\tconst { flowId, prefetchOnMount = false, hideCloseButton } = options.flow;\n\tconst flowRun = useMemo(() => client.flow(flowId), [client, flowId]);\n\tconst requiresContainer = options.mode === \"container\";\n\n\t/** Flow state from loader events; open/loading + dimensions. */\n\tconst [flowState, setFlowState] = useState<FlowState>(() =>\n\t\tflowRun.getFlowState(),\n\t);\n\n\tconst openOptions = useMemo(\n\t\t() => ({\n\t\t\tcontainerRequirement: requiresContainer\n\t\t\t\t? (\"hostOnly\" as const)\n\t\t\t\t: undefined,\n\t\t\thideCloseButton,\n\t\t}),\n\t\t[requiresContainer, hideCloseButton],\n\t);\n\n\tconst open = useCallback((): Promise<void> => {\n\t\treturn flowRun.open(openOptions);\n\t}, [flowRun, openOptions]);\n\n\tconst close = useCallback((): Promise<void> => flowRun.close(), [flowRun]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => (next ? open() : close()),\n\t\t[close, open],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tif (!requiresContainer) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tflowRun.setContainer(element);\n\t\t},\n\t\t[flowRun, requiresContainer],\n\t);\n\n\tconst shouldRenderContainer =\n\t\trequiresContainer && (flowState.isLoading || flowState.isOpen);\n\n\tuseEffect(\n\t\t() =>\n\t\t\tflowRun.subscribeFlowState(\n\t\t\t\t(state) =>\n\t\t\t\t\tsetFlowState({\n\t\t\t\t\t\tisOpen: state.isOpen,\n\t\t\t\t\t\tisLoading: state.isLoading,\n\t\t\t\t\t\twidth: state.width,\n\t\t\t\t\t\theight: state.height,\n\t\t\t\t\t}),\n\t\t\t\t{\n\t\t\t\t\temitInitial: false,\n\t\t\t\t},\n\t\t\t),\n\t\t[flowRun],\n\t);\n\n\tuseEffect(() => {\n\t\tif (!prefetchOnMount) {\n\t\t\treturn;\n\t\t}\n\t\tflowRun.prefetch();\n\t}, [flowRun, prefetchOnMount]);\n\n\treturn {\n\t\tisOpen: flowState.isOpen,\n\t\tisLoading: flowState.isLoading,\n\t\twidth: flowState.width,\n\t\theight: flowState.height,\n\t\tshouldRenderContainer,\n\t\tsetOpen,\n\t\topen,\n\t\tclose,\n\t\tprerender: () => flowRun.prerender({ hideCloseButton }),\n\t\tcontainerRef,\n\t};\n};\n\n/**\n * @name useFlow\n * @description Imperatively display a flow in the default floating container or your own container.\n * @example Default (floating) container\n * ```tsx\n * const { open, isLoading } = useFlow({ flowId: \"sur_123\" });\n * ```\n * @example Custom container\n * ```tsx\n * const { containerRef, shouldRenderContainer, setOpen } = useFlow({ flowId: \"sur_123\", container: \"custom\" });\n * ```\n */\nexport function useFlow(\n\toptions: UseFlowOptions & { container?: \"default\" },\n): UseFlowReturn;\nexport function useFlow(\n\toptions: UseFlowOptions & { container: \"custom\" },\n): UseFlowContainerReturn;\nexport function useFlow(\n\toptions: UseFlowOptions,\n): UseFlowReturn | UseFlowContainerReturn {\n\tconst container = options.container ?? \"default\";\n\tconst mode = container === \"custom\" ? \"container\" : \"default\";\n\tconst controller = useFlowController({\n\t\tflow: {\n\t\t\tflowId: options.flowId,\n\t\t\tprefetchOnMount: options.prefetchOnMount,\n\t\t\thideCloseButton: options.hideCloseButton,\n\t\t},\n\t\tmode,\n\t});\n\n\tif (mode === \"container\") {\n\t\treturn {\n\t\t\tcontainer: \"custom\",\n\t\t\tisOpen: controller.isOpen,\n\t\t\tisLoading: controller.isLoading,\n\t\t\twidth: controller.width,\n\t\t\theight: controller.height,\n\t\t\tshouldRenderContainer: controller.shouldRenderContainer,\n\t\t\tsetOpen: controller.setOpen,\n\t\t\tprerender: controller.prerender,\n\t\t\tcontainerRef: controller.containerRef,\n\t\t};\n\t}\n\n\treturn {\n\t\tcontainer: \"default\",\n\t\tisOpen: controller.isOpen,\n\t\tisLoading: controller.isLoading,\n\t\twidth: controller.width,\n\t\theight: controller.height,\n\t\topen: controller.open,\n\t\tclose: controller.close,\n\t\tprerender: controller.prerender,\n\t};\n}\n\n/**\n * @name useDefaultFlowContainer\n * @description Register and control a default flow container for all flows in this provider subtree.\n * @example Display any flow in your own dialog\n * ```tsx\n * function DefaultFlowContainer() {\n * const { containerRef, shouldRenderContainer, width, height, setOpen } = useDefaultFlowContainer();\n * return (\n * <Dialog open={shouldRenderContainer} onOpenChange={setOpen}>\n * <DialogContent ref={containerRef} style={{ width, height }} />\n * </Dialog>\n * );\n * }\n *\n * function App() {\n * return (\n * <GetUserFeedbackProvider clientOptions={{ apiKey: \"YOUR_API_KEY\" }}>\n * <AppRoutes />\n * <DefaultFlowContainer />\n * </GetUserFeedbackProvider>\n * );\n * }\n * ```\n */\nexport function useDefaultFlowContainer(): UseDefaultFlowContainerReturn {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useDefaultFlowContainer must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\n\tconst {\n\t\tclient,\n\t\tdefaultFlowContainerState,\n\t\tregisterDefaultFlowContainerConsumer,\n\t\tsetDefaultFlowContainer,\n\t} = ctx;\n\n\tuseEffect(\n\t\t() => registerDefaultFlowContainerConsumer(),\n\t\t[registerDefaultFlowContainerConsumer],\n\t);\n\n\tconst containerRef = useCallback(\n\t\t(element: HTMLDivElement | null): void => {\n\t\t\tsetDefaultFlowContainer(element);\n\t\t},\n\t\t[setDefaultFlowContainer],\n\t);\n\n\tconst close = useCallback((): Promise<void> => client.close(), [client]);\n\n\tconst setOpen = useCallback(\n\t\t(next: boolean): Promise<void> => {\n\t\t\tif (next) {\n\t\t\t\treturn Promise.resolve();\n\t\t\t}\n\t\t\treturn close();\n\t\t},\n\t\t[close],\n\t);\n\n\treturn {\n\t\tisOpen: defaultFlowContainerState.isOpen,\n\t\tisLoading: defaultFlowContainerState.isLoading,\n\t\tshouldRenderContainer: defaultFlowContainerState.shouldRenderContainer,\n\t\twidth: defaultFlowContainerState.width,\n\t\theight: defaultFlowContainerState.height,\n\t\tsetOpen,\n\t\tcontainerRef,\n\t};\n}\n\n/**\n * Hook for accessing the getuserfeedback client instance.\n *\n * @example Basic usage\n * ```tsx\n * const client = useGetUserFeedback();\n *\n * // on login\n * client.identify('user-123', { email: 'user@example.com' });\n *\n * // on logout\n * client.reset();\n * ```\n *\n * @example Override color scheme\n * ```tsx\n * const client = useGetUserFeedback();\n * await client.configure({ colorScheme: \"dark\" });\n * ```\n */\nexport function useGetUserFeedback(): Client {\n\tconst ctx = useContext(GetUserFeedbackContext);\n\tif (!ctx) {\n\t\tthrow new Error(\n\t\t\t\"useGetUserFeedback must be used within a GetUserFeedbackProvider\",\n\t\t);\n\t}\n\treturn ctx.client;\n}\n",
|
|
6
6
|
"declare const __GX_REACT_SDK_VERSION__: string | undefined;\n\nconst reactSdkVersion =\n\ttypeof __GX_REACT_SDK_VERSION__ === \"string\"\n\t\t? __GX_REACT_SDK_VERSION__.trim()\n\t\t: \"\";\n\n// Build scripts inject __GX_REACT_SDK_VERSION__ for published artifacts.\n// Source-linked workspace usage may not inject defines, so keep a safe fallback.\nexport const REACT_SDK_VERSION =\n\treactSdkVersion.length > 0 ? reactSdkVersion : \"0.0.0-local\";\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";AAEA,uBAGC,6BAID,wBACC,iBACA,gBACA,eACA,aACA,YACA,cACA,
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "903A5C6C86A3DECB64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "getuserfeedback React SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"prepack": "node scripts/prepack.cjs",
|
|
29
29
|
"postpack": "node scripts/postpack.cjs",
|
|
30
|
-
"pack:verify": "node scripts/verify-
|
|
31
|
-
"publish:dry-run": "node scripts/publish.cjs --dry-run",
|
|
32
|
-
"publish:npm": "node scripts/publish.cjs",
|
|
30
|
+
"pack:verify": "node ../../scripts/pack-and-verify.cjs --expect-dependency @getuserfeedback/sdk:../sdk/package.json",
|
|
31
|
+
"publish:dry-run": "node ../../scripts/publish-package.cjs . --expect-dependency @getuserfeedback/sdk:../sdk/package.json -- --dry-run",
|
|
32
|
+
"publish:npm": "node ../../scripts/publish-package.cjs . --expect-dependency @getuserfeedback/sdk:../sdk/package.json",
|
|
33
33
|
"build": "bun x rimraf dist tsconfig.tsbuildinfo && bun run scripts/build.ts && bun x rollup -c rollup.dts.config.mjs",
|
|
34
34
|
"typecheck": "tsc -b tsconfig.json",
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@getuserfeedback/sdk": "^0.1.
|
|
38
|
+
"@getuserfeedback/sdk": "^0.1.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": ">=18.0.0"
|