@cornercue/react 0.1.0-beta.5

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/index.cjs ADDED
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var core = require('@cornercue/core');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/FeedbackButton.tsx
8
+ var sharedWidget = null;
9
+ var widgetRefCount = 0;
10
+ function getSharedWidget(config) {
11
+ if (!sharedWidget) {
12
+ sharedWidget = new core.CornerCueWidget(config);
13
+ }
14
+ widgetRefCount++;
15
+ return sharedWidget;
16
+ }
17
+ function releaseSharedWidget() {
18
+ widgetRefCount--;
19
+ if (widgetRefCount <= 0 && sharedWidget) {
20
+ sharedWidget.destroy();
21
+ sharedWidget = null;
22
+ widgetRefCount = 0;
23
+ }
24
+ }
25
+ function FeedbackButton({
26
+ projectId,
27
+ user,
28
+ metadata,
29
+ defaultType,
30
+ config,
31
+ // events - reserved for future use
32
+ children,
33
+ className
34
+ }) {
35
+ const triggerRef = react.useRef(null);
36
+ const widgetRef = react.useRef(null);
37
+ react.useEffect(() => {
38
+ widgetRef.current = getSharedWidget(config);
39
+ return () => {
40
+ releaseSharedWidget();
41
+ widgetRef.current = null;
42
+ };
43
+ }, [config]);
44
+ const handleClick = react.useCallback(
45
+ (e) => {
46
+ e.preventDefault();
47
+ e.stopPropagation();
48
+ const widget = widgetRef.current;
49
+ const trigger = triggerRef.current;
50
+ if (!widget || !trigger) return;
51
+ if (widget.isWidgetOpen()) {
52
+ widget.close();
53
+ } else {
54
+ widget.open(trigger, {
55
+ projectId,
56
+ user: user ?? null,
57
+ metadata: metadata ?? null,
58
+ defaultType
59
+ });
60
+ }
61
+ },
62
+ [projectId, user, metadata, defaultType]
63
+ );
64
+ if (!react.isValidElement(children)) {
65
+ console.warn("FeedbackButton: children must be a valid React element");
66
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
67
+ }
68
+ const childElement = children;
69
+ return react.cloneElement(childElement, {
70
+ ref: triggerRef,
71
+ onClick: (e) => {
72
+ childElement.props.onClick?.(e);
73
+ handleClick(e);
74
+ },
75
+ className: className ? `${childElement.props.className || ""} ${className}`.trim() : childElement.props.className
76
+ });
77
+ }
78
+
79
+ Object.defineProperty(exports, "CornerCueWidget", {
80
+ enumerable: true,
81
+ get: function () { return core.CornerCueWidget; }
82
+ });
83
+ exports.FeedbackButton = FeedbackButton;
84
+ //# sourceMappingURL=index.cjs.map
85
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/FeedbackButton.tsx"],"names":["CornerCueWidget","useRef","useEffect","useCallback","isValidElement","cloneElement"],"mappings":";;;;;;;AA+DA,IAAI,YAAA,GAAuC,IAAA;AAC3C,IAAI,cAAA,GAAiB,CAAA;AAErB,SAAS,gBAAgB,MAAA,EAAwC;AAC/D,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,YAAA,GAAe,IAAIA,qBAAgB,MAAM,CAAA;AAAA,EAC3C;AACA,EAAA,cAAA,EAAA;AACA,EAAA,OAAO,YAAA;AACT;AAEA,SAAS,mBAAA,GAA4B;AACnC,EAAA,cAAA,EAAA;AACA,EAAA,IAAI,cAAA,IAAkB,KAAK,YAAA,EAAc;AACvC,IAAA,YAAA,CAAa,OAAA,EAAQ;AACrB,IAAA,YAAA,GAAe,IAAA;AACf,IAAA,cAAA,GAAiB,CAAA;AAAA,EACnB;AACF;AAgBO,SAAS,cAAA,CAAe;AAAA,EAC7B,SAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA;AAAA,EAEA,QAAA;AAAA,EACA;AACF,CAAA,EAAsC;AACpC,EAAA,MAAM,UAAA,GAAaC,aAAoB,IAAI,CAAA;AAC3C,EAAA,MAAM,SAAA,GAAYA,aAA+B,IAAI,CAAA;AAGrD,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,gBAAgB,MAAM,CAAA;AAE1C,IAAA,OAAO,MAAM;AACX,MAAA,mBAAA,EAAoB;AACpB,MAAA,SAAA,CAAU,OAAA,GAAU,IAAA;AAAA,IACtB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,MAAM,WAAA,GAAcC,iBAAA;AAAA,IAClB,CAAC,CAAA,KAAkB;AACjB,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAElB,MAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,MAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAE3B,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS;AAEzB,MAAA,IAAI,MAAA,CAAO,cAAa,EAAG;AACzB,QAAA,MAAA,CAAO,KAAA,EAAM;AAAA,MACf,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,OAAA,EAAS;AAAA,UACnB,SAAA;AAAA,UACA,MAAM,IAAA,IAAQ,IAAA;AAAA,UACd,UAAU,QAAA,IAAY,IAAA;AAAA,UACtB;AAAA,SACD,CAAA;AAAA,MACH;AAAA,IACF,CAAA;AAAA,IACA,CAAC,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,WAAW;AAAA,GACzC;AAGA,EAAA,IAAI,CAACC,oBAAA,CAAe,QAAQ,CAAA,EAAG;AAC7B,IAAA,OAAA,CAAQ,KAAK,wDAAwD,CAAA;AACrE,IAAA,6DAAU,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,YAAA,GAAe,QAAA;AAMrB,EAAA,OAAOC,mBAAa,YAAA,EAAc;AAAA,IAChC,GAAA,EAAK,UAAA;AAAA,IACL,OAAA,EAAS,CAAC,CAAA,KAAkB;AAE1B,MAAA,YAAA,CAAa,KAAA,CAAM,UAAU,CAAC,CAAA;AAC9B,MAAA,WAAA,CAAY,CAAC,CAAA;AAAA,IACf,CAAA;AAAA,IACA,SAAA,EAAW,SAAA,GACP,CAAA,EAAG,YAAA,CAAa,KAAA,CAAM,SAAA,IAAa,EAAE,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,GAC1D,aAAa,KAAA,CAAM;AAAA,GACxB,CAAA;AACH","file":"index.cjs","sourcesContent":["import React, {\n useRef,\n useEffect,\n useCallback,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n type MouseEvent,\n} from \"react\";\nimport {\n CornerCueWidget,\n type WidgetConfig,\n type UserInfo,\n type Metadata,\n type WidgetEvents,\n type FeedbackType,\n} from \"@cornercue/core\";\n\nexport interface FeedbackButtonProps {\n /**\n * Project ID for submissions\n */\n projectId: string;\n\n /**\n * User information to attach to feedback\n */\n user?: UserInfo | null;\n\n /**\n * Additional metadata to attach to feedback\n */\n metadata?: Metadata | null;\n\n /**\n * Default feedback type to open directly (skips menu)\n * Options: 'bug' | 'feature' | 'feedback'\n */\n defaultType?: FeedbackType;\n\n /**\n * Widget configuration overrides\n */\n config?: WidgetConfig;\n\n /**\n * Event callbacks\n */\n events?: WidgetEvents;\n\n /**\n * The trigger element (button, link, etc.)\n */\n children: ReactNode;\n\n /**\n * Additional class name for the wrapper\n */\n className?: string;\n}\n\n// Shared widget instance\nlet sharedWidget: CornerCueWidget | null = null;\nlet widgetRefCount = 0;\n\nfunction getSharedWidget(config?: WidgetConfig): CornerCueWidget {\n if (!sharedWidget) {\n sharedWidget = new CornerCueWidget(config);\n }\n widgetRefCount++;\n return sharedWidget;\n}\n\nfunction releaseSharedWidget(): void {\n widgetRefCount--;\n if (widgetRefCount <= 0 && sharedWidget) {\n sharedWidget.destroy();\n sharedWidget = null;\n widgetRefCount = 0;\n }\n}\n\n/**\n * React component that wraps a trigger element and opens the feedback widget on click.\n *\n * @example\n * ```tsx\n * <FeedbackButton\n * projectId=\"your-project-id\"\n * user={{ id: 'user123', email: 'user@example.com' }}\n * metadata={{ plan: 'pro' }}\n * >\n * <button>Give Feedback</button>\n * </FeedbackButton>\n * ```\n */\nexport function FeedbackButton({\n projectId,\n user,\n metadata,\n defaultType,\n config,\n // events - reserved for future use\n children,\n className,\n}: FeedbackButtonProps): ReactElement {\n const triggerRef = useRef<HTMLElement>(null);\n const widgetRef = useRef<CornerCueWidget | null>(null);\n\n // Initialize widget on mount\n useEffect(() => {\n widgetRef.current = getSharedWidget(config);\n\n return () => {\n releaseSharedWidget();\n widgetRef.current = null;\n };\n }, [config]);\n\n // Handle click on trigger\n const handleClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const widget = widgetRef.current;\n const trigger = triggerRef.current;\n\n if (!widget || !trigger) return;\n\n if (widget.isWidgetOpen()) {\n widget.close();\n } else {\n widget.open(trigger, {\n projectId,\n user: user ?? null,\n metadata: metadata ?? null,\n defaultType,\n });\n }\n },\n [projectId, user, metadata, defaultType]\n );\n\n // Clone the child element to add ref and click handler\n if (!isValidElement(children)) {\n console.warn(\"FeedbackButton: children must be a valid React element\");\n return <>{children}</>;\n }\n\n const childElement = children as ReactElement<{\n ref?: React.Ref<HTMLElement>;\n onClick?: (e: MouseEvent) => void;\n className?: string;\n }>;\n\n return cloneElement(childElement, {\n ref: triggerRef,\n onClick: (e: MouseEvent) => {\n // Call original onClick if present\n childElement.props.onClick?.(e);\n handleClick(e);\n },\n className: className\n ? `${childElement.props.className || \"\"} ${className}`.trim()\n : childElement.props.className,\n });\n}\n\n// Re-export types\nexport type {\n WidgetConfig,\n UserInfo,\n Metadata,\n FeedbackType,\n WidgetEvents,\n OpenOptions,\n} from \"@cornercue/core\";\n"]}
@@ -0,0 +1,56 @@
1
+ import { ReactNode, ReactElement } from 'react';
2
+ import { UserInfo, Metadata, FeedbackType, WidgetConfig, WidgetEvents } from '@cornercue/core';
3
+ export { CornerCueWidget, FeedbackType, Metadata, OpenOptions, UserInfo, WidgetConfig, WidgetEvents } from '@cornercue/core';
4
+
5
+ interface FeedbackButtonProps {
6
+ /**
7
+ * Project ID for submissions
8
+ */
9
+ projectId: string;
10
+ /**
11
+ * User information to attach to feedback
12
+ */
13
+ user?: UserInfo | null;
14
+ /**
15
+ * Additional metadata to attach to feedback
16
+ */
17
+ metadata?: Metadata | null;
18
+ /**
19
+ * Default feedback type to open directly (skips menu)
20
+ * Options: 'bug' | 'feature' | 'feedback'
21
+ */
22
+ defaultType?: FeedbackType;
23
+ /**
24
+ * Widget configuration overrides
25
+ */
26
+ config?: WidgetConfig;
27
+ /**
28
+ * Event callbacks
29
+ */
30
+ events?: WidgetEvents;
31
+ /**
32
+ * The trigger element (button, link, etc.)
33
+ */
34
+ children: ReactNode;
35
+ /**
36
+ * Additional class name for the wrapper
37
+ */
38
+ className?: string;
39
+ }
40
+ /**
41
+ * React component that wraps a trigger element and opens the feedback widget on click.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * <FeedbackButton
46
+ * projectId="your-project-id"
47
+ * user={{ id: 'user123', email: 'user@example.com' }}
48
+ * metadata={{ plan: 'pro' }}
49
+ * >
50
+ * <button>Give Feedback</button>
51
+ * </FeedbackButton>
52
+ * ```
53
+ */
54
+ declare function FeedbackButton({ projectId, user, metadata, defaultType, config, children, className, }: FeedbackButtonProps): ReactElement;
55
+
56
+ export { FeedbackButton, type FeedbackButtonProps };
@@ -0,0 +1,56 @@
1
+ import { ReactNode, ReactElement } from 'react';
2
+ import { UserInfo, Metadata, FeedbackType, WidgetConfig, WidgetEvents } from '@cornercue/core';
3
+ export { CornerCueWidget, FeedbackType, Metadata, OpenOptions, UserInfo, WidgetConfig, WidgetEvents } from '@cornercue/core';
4
+
5
+ interface FeedbackButtonProps {
6
+ /**
7
+ * Project ID for submissions
8
+ */
9
+ projectId: string;
10
+ /**
11
+ * User information to attach to feedback
12
+ */
13
+ user?: UserInfo | null;
14
+ /**
15
+ * Additional metadata to attach to feedback
16
+ */
17
+ metadata?: Metadata | null;
18
+ /**
19
+ * Default feedback type to open directly (skips menu)
20
+ * Options: 'bug' | 'feature' | 'feedback'
21
+ */
22
+ defaultType?: FeedbackType;
23
+ /**
24
+ * Widget configuration overrides
25
+ */
26
+ config?: WidgetConfig;
27
+ /**
28
+ * Event callbacks
29
+ */
30
+ events?: WidgetEvents;
31
+ /**
32
+ * The trigger element (button, link, etc.)
33
+ */
34
+ children: ReactNode;
35
+ /**
36
+ * Additional class name for the wrapper
37
+ */
38
+ className?: string;
39
+ }
40
+ /**
41
+ * React component that wraps a trigger element and opens the feedback widget on click.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * <FeedbackButton
46
+ * projectId="your-project-id"
47
+ * user={{ id: 'user123', email: 'user@example.com' }}
48
+ * metadata={{ plan: 'pro' }}
49
+ * >
50
+ * <button>Give Feedback</button>
51
+ * </FeedbackButton>
52
+ * ```
53
+ */
54
+ declare function FeedbackButton({ projectId, user, metadata, defaultType, config, children, className, }: FeedbackButtonProps): ReactElement;
55
+
56
+ export { FeedbackButton, type FeedbackButtonProps };
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ import { useRef, useEffect, useCallback, isValidElement, cloneElement } from 'react';
2
+ import { CornerCueWidget } from '@cornercue/core';
3
+ export { CornerCueWidget } from '@cornercue/core';
4
+ import { jsx, Fragment } from 'react/jsx-runtime';
5
+
6
+ // src/FeedbackButton.tsx
7
+ var sharedWidget = null;
8
+ var widgetRefCount = 0;
9
+ function getSharedWidget(config) {
10
+ if (!sharedWidget) {
11
+ sharedWidget = new CornerCueWidget(config);
12
+ }
13
+ widgetRefCount++;
14
+ return sharedWidget;
15
+ }
16
+ function releaseSharedWidget() {
17
+ widgetRefCount--;
18
+ if (widgetRefCount <= 0 && sharedWidget) {
19
+ sharedWidget.destroy();
20
+ sharedWidget = null;
21
+ widgetRefCount = 0;
22
+ }
23
+ }
24
+ function FeedbackButton({
25
+ projectId,
26
+ user,
27
+ metadata,
28
+ defaultType,
29
+ config,
30
+ // events - reserved for future use
31
+ children,
32
+ className
33
+ }) {
34
+ const triggerRef = useRef(null);
35
+ const widgetRef = useRef(null);
36
+ useEffect(() => {
37
+ widgetRef.current = getSharedWidget(config);
38
+ return () => {
39
+ releaseSharedWidget();
40
+ widgetRef.current = null;
41
+ };
42
+ }, [config]);
43
+ const handleClick = useCallback(
44
+ (e) => {
45
+ e.preventDefault();
46
+ e.stopPropagation();
47
+ const widget = widgetRef.current;
48
+ const trigger = triggerRef.current;
49
+ if (!widget || !trigger) return;
50
+ if (widget.isWidgetOpen()) {
51
+ widget.close();
52
+ } else {
53
+ widget.open(trigger, {
54
+ projectId,
55
+ user: user ?? null,
56
+ metadata: metadata ?? null,
57
+ defaultType
58
+ });
59
+ }
60
+ },
61
+ [projectId, user, metadata, defaultType]
62
+ );
63
+ if (!isValidElement(children)) {
64
+ console.warn("FeedbackButton: children must be a valid React element");
65
+ return /* @__PURE__ */ jsx(Fragment, { children });
66
+ }
67
+ const childElement = children;
68
+ return cloneElement(childElement, {
69
+ ref: triggerRef,
70
+ onClick: (e) => {
71
+ childElement.props.onClick?.(e);
72
+ handleClick(e);
73
+ },
74
+ className: className ? `${childElement.props.className || ""} ${className}`.trim() : childElement.props.className
75
+ });
76
+ }
77
+
78
+ export { FeedbackButton };
79
+ //# sourceMappingURL=index.js.map
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/FeedbackButton.tsx"],"names":[],"mappings":";;;;;;AA+DA,IAAI,YAAA,GAAuC,IAAA;AAC3C,IAAI,cAAA,GAAiB,CAAA;AAErB,SAAS,gBAAgB,MAAA,EAAwC;AAC/D,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,YAAA,GAAe,IAAI,gBAAgB,MAAM,CAAA;AAAA,EAC3C;AACA,EAAA,cAAA,EAAA;AACA,EAAA,OAAO,YAAA;AACT;AAEA,SAAS,mBAAA,GAA4B;AACnC,EAAA,cAAA,EAAA;AACA,EAAA,IAAI,cAAA,IAAkB,KAAK,YAAA,EAAc;AACvC,IAAA,YAAA,CAAa,OAAA,EAAQ;AACrB,IAAA,YAAA,GAAe,IAAA;AACf,IAAA,cAAA,GAAiB,CAAA;AAAA,EACnB;AACF;AAgBO,SAAS,cAAA,CAAe;AAAA,EAC7B,SAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA;AAAA,EAEA,QAAA;AAAA,EACA;AACF,CAAA,EAAsC;AACpC,EAAA,MAAM,UAAA,GAAa,OAAoB,IAAI,CAAA;AAC3C,EAAA,MAAM,SAAA,GAAY,OAA+B,IAAI,CAAA;AAGrD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,gBAAgB,MAAM,CAAA;AAE1C,IAAA,OAAO,MAAM;AACX,MAAA,mBAAA,EAAoB;AACpB,MAAA,SAAA,CAAU,OAAA,GAAU,IAAA;AAAA,IACtB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,CAAA,KAAkB;AACjB,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAElB,MAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,MAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAE3B,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS;AAEzB,MAAA,IAAI,MAAA,CAAO,cAAa,EAAG;AACzB,QAAA,MAAA,CAAO,KAAA,EAAM;AAAA,MACf,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,KAAK,OAAA,EAAS;AAAA,UACnB,SAAA;AAAA,UACA,MAAM,IAAA,IAAQ,IAAA;AAAA,UACd,UAAU,QAAA,IAAY,IAAA;AAAA,UACtB;AAAA,SACD,CAAA;AAAA,MACH;AAAA,IACF,CAAA;AAAA,IACA,CAAC,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,WAAW;AAAA,GACzC;AAGA,EAAA,IAAI,CAAC,cAAA,CAAe,QAAQ,CAAA,EAAG;AAC7B,IAAA,OAAA,CAAQ,KAAK,wDAAwD,CAAA;AACrE,IAAA,uCAAU,QAAA,EAAS,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,YAAA,GAAe,QAAA;AAMrB,EAAA,OAAO,aAAa,YAAA,EAAc;AAAA,IAChC,GAAA,EAAK,UAAA;AAAA,IACL,OAAA,EAAS,CAAC,CAAA,KAAkB;AAE1B,MAAA,YAAA,CAAa,KAAA,CAAM,UAAU,CAAC,CAAA;AAC9B,MAAA,WAAA,CAAY,CAAC,CAAA;AAAA,IACf,CAAA;AAAA,IACA,SAAA,EAAW,SAAA,GACP,CAAA,EAAG,YAAA,CAAa,KAAA,CAAM,SAAA,IAAa,EAAE,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,GAC1D,aAAa,KAAA,CAAM;AAAA,GACxB,CAAA;AACH","file":"index.js","sourcesContent":["import React, {\n useRef,\n useEffect,\n useCallback,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n type MouseEvent,\n} from \"react\";\nimport {\n CornerCueWidget,\n type WidgetConfig,\n type UserInfo,\n type Metadata,\n type WidgetEvents,\n type FeedbackType,\n} from \"@cornercue/core\";\n\nexport interface FeedbackButtonProps {\n /**\n * Project ID for submissions\n */\n projectId: string;\n\n /**\n * User information to attach to feedback\n */\n user?: UserInfo | null;\n\n /**\n * Additional metadata to attach to feedback\n */\n metadata?: Metadata | null;\n\n /**\n * Default feedback type to open directly (skips menu)\n * Options: 'bug' | 'feature' | 'feedback'\n */\n defaultType?: FeedbackType;\n\n /**\n * Widget configuration overrides\n */\n config?: WidgetConfig;\n\n /**\n * Event callbacks\n */\n events?: WidgetEvents;\n\n /**\n * The trigger element (button, link, etc.)\n */\n children: ReactNode;\n\n /**\n * Additional class name for the wrapper\n */\n className?: string;\n}\n\n// Shared widget instance\nlet sharedWidget: CornerCueWidget | null = null;\nlet widgetRefCount = 0;\n\nfunction getSharedWidget(config?: WidgetConfig): CornerCueWidget {\n if (!sharedWidget) {\n sharedWidget = new CornerCueWidget(config);\n }\n widgetRefCount++;\n return sharedWidget;\n}\n\nfunction releaseSharedWidget(): void {\n widgetRefCount--;\n if (widgetRefCount <= 0 && sharedWidget) {\n sharedWidget.destroy();\n sharedWidget = null;\n widgetRefCount = 0;\n }\n}\n\n/**\n * React component that wraps a trigger element and opens the feedback widget on click.\n *\n * @example\n * ```tsx\n * <FeedbackButton\n * projectId=\"your-project-id\"\n * user={{ id: 'user123', email: 'user@example.com' }}\n * metadata={{ plan: 'pro' }}\n * >\n * <button>Give Feedback</button>\n * </FeedbackButton>\n * ```\n */\nexport function FeedbackButton({\n projectId,\n user,\n metadata,\n defaultType,\n config,\n // events - reserved for future use\n children,\n className,\n}: FeedbackButtonProps): ReactElement {\n const triggerRef = useRef<HTMLElement>(null);\n const widgetRef = useRef<CornerCueWidget | null>(null);\n\n // Initialize widget on mount\n useEffect(() => {\n widgetRef.current = getSharedWidget(config);\n\n return () => {\n releaseSharedWidget();\n widgetRef.current = null;\n };\n }, [config]);\n\n // Handle click on trigger\n const handleClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const widget = widgetRef.current;\n const trigger = triggerRef.current;\n\n if (!widget || !trigger) return;\n\n if (widget.isWidgetOpen()) {\n widget.close();\n } else {\n widget.open(trigger, {\n projectId,\n user: user ?? null,\n metadata: metadata ?? null,\n defaultType,\n });\n }\n },\n [projectId, user, metadata, defaultType]\n );\n\n // Clone the child element to add ref and click handler\n if (!isValidElement(children)) {\n console.warn(\"FeedbackButton: children must be a valid React element\");\n return <>{children}</>;\n }\n\n const childElement = children as ReactElement<{\n ref?: React.Ref<HTMLElement>;\n onClick?: (e: MouseEvent) => void;\n className?: string;\n }>;\n\n return cloneElement(childElement, {\n ref: triggerRef,\n onClick: (e: MouseEvent) => {\n // Call original onClick if present\n childElement.props.onClick?.(e);\n handleClick(e);\n },\n className: className\n ? `${childElement.props.className || \"\"} ${className}`.trim()\n : childElement.props.className,\n });\n}\n\n// Re-export types\nexport type {\n WidgetConfig,\n UserInfo,\n Metadata,\n FeedbackType,\n WidgetEvents,\n OpenOptions,\n} from \"@cornercue/core\";\n"]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@cornercue/react",
3
+ "version": "0.1.0-beta.5",
4
+ "description": "CornerCue feedback widget - React component",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "typecheck": "tsc --noEmit"
28
+ },
29
+ "dependencies": {
30
+ "@cornercue/core": "workspace:*"
31
+ },
32
+ "peerDependencies": {
33
+ "react": ">=17.0.0",
34
+ "react-dom": ">=17.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "^18.2.0",
38
+ "@types/react-dom": "^18.2.0",
39
+ "react": "^18.2.0",
40
+ "react-dom": "^18.2.0",
41
+ "tsup": "^8.0.0",
42
+ "typescript": "^5.3.0"
43
+ },
44
+ "keywords": [
45
+ "feedback",
46
+ "widget",
47
+ "cornercue",
48
+ "react",
49
+ "component"
50
+ ],
51
+ "license": "MIT"
52
+ }