@kelet-ai/feedback-ui 1.1.4 → 1.3.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/README.md +70 -0
- package/dist/components/vote-feedback.d.ts +2 -1
- package/dist/contexts/kelet.d.ts +20 -0
- package/dist/feedback-ui.es.js +37 -13
- package/dist/feedback-ui.es.js.map +1 -1
- package/dist/feedback-ui.es.min.js +667 -650
- package/dist/feedback-ui.es.min.js.map +1 -1
- package/dist/feedback-ui.umd.js +35 -11
- package/dist/feedback-ui.umd.js.map +1 -1
- package/dist/feedback-ui.umd.min.js +11 -11
- package/dist/feedback-ui.umd.min.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,6 +126,43 @@ function ContentEditor() {
|
|
|
126
126
|
}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### **Pattern 4: Manual Signal Sending**
|
|
130
|
+
|
|
131
|
+
Send signals directly without tying them to component state — useful for custom events, page views, or any interaction not covered by the other patterns:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { KeletProvider, useKeletSignal } from "@kelet-ai/feedback-ui"
|
|
135
|
+
|
|
136
|
+
function CopyButton({ sessionId }: { sessionId: string }) {
|
|
137
|
+
const sendSignal = useKeletSignal()
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<button
|
|
141
|
+
onClick={() => {
|
|
142
|
+
navigator.clipboard.writeText("...")
|
|
143
|
+
sendSignal({
|
|
144
|
+
session_id: sessionId,
|
|
145
|
+
kind: "feedback",
|
|
146
|
+
source: "human",
|
|
147
|
+
score: 1,
|
|
148
|
+
trigger_name: "copy_button_clicked",
|
|
149
|
+
})
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
Copy
|
|
153
|
+
</button>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function App() {
|
|
158
|
+
return (
|
|
159
|
+
<KeletProvider apiKey="..." project="my-project">
|
|
160
|
+
<CopyButton sessionId="response-abc123" />
|
|
161
|
+
</KeletProvider>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
129
166
|
### **Pattern 3: Complex State with Reducer**
|
|
130
167
|
|
|
131
168
|
For advanced state management with automatic trigger tracking, by using a drop-in replacement for useReducer:
|
|
@@ -359,6 +396,39 @@ Context-aware feedback form that appears after voting.
|
|
|
359
396
|
|
|
360
397
|
---
|
|
361
398
|
|
|
399
|
+
## ✉️ Manual Signal Sending
|
|
400
|
+
|
|
401
|
+
### **useKeletSignal Hook**
|
|
402
|
+
|
|
403
|
+
Returns the signal-sending function from the nearest `KeletProvider`. Use this to send signals manually for any event — custom interactions, page views, feature usage — without tying them to component state.
|
|
404
|
+
|
|
405
|
+
Safe to call outside a provider: returns a no-op and logs a warning.
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { useKeletSignal } from "@kelet-ai/feedback-ui"
|
|
409
|
+
|
|
410
|
+
function MyComponent() {
|
|
411
|
+
const sendSignal = useKeletSignal()
|
|
412
|
+
|
|
413
|
+
const handleAction = () => {
|
|
414
|
+
sendSignal({
|
|
415
|
+
session_id: "my-session",
|
|
416
|
+
kind: "feedback",
|
|
417
|
+
source: "human",
|
|
418
|
+
score: 1,
|
|
419
|
+
trigger_name: "user_action",
|
|
420
|
+
metadata: { context: "sidebar" },
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return <button onClick={handleAction}>Do something</button>
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Requires a `KeletProvider` ancestor to send signals to the API.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
362
432
|
## 📊 Automatic Feedback Hooks
|
|
363
433
|
|
|
364
434
|
### **useFeedbackState Hook**
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
1
2
|
import { DownvoteButtonProps, PopoverProps, SubmitButtonProps, TextareaProps, UpvoteButtonProps, VoteFeedbackRootProps } from '../types';
|
|
2
3
|
export declare const VoteFeedback: {
|
|
3
4
|
Root: ({ children, onFeedback, defaultText, session_id: sessionIdProp, metadata, trigger_name: triggerProp, trace_id, }: VoteFeedbackRootProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
5
|
UpvoteButton: ({ asChild, children, onClick, ...props }: UpvoteButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
-
DownvoteButton: ({ asChild, children, onClick, ...props }: DownvoteButtonProps) => string | number | bigint | true | Iterable<
|
|
6
|
+
DownvoteButton: ({ asChild, children, onClick, ...props }: DownvoteButtonProps) => string | number | bigint | true | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element;
|
|
6
7
|
Popover: ({ asChild, children, ...props }: PopoverProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
8
|
Textarea: ({ asChild, value, onChange, ...props }: TextareaProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
9
|
SubmitButton: ({ asChild, children, onClick, ...props }: SubmitButtonProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/contexts/kelet.d.ts
CHANGED
|
@@ -12,6 +12,26 @@ interface KeletProviderProps {
|
|
|
12
12
|
}
|
|
13
13
|
export declare const KeletContext: React.Context<KeletContextValue | null>;
|
|
14
14
|
export declare const useKelet: () => KeletContextValue;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the signal-sending function from the nearest KeletProvider.
|
|
17
|
+
*
|
|
18
|
+
* Use this hook to send signals manually — e.g. tracking custom events,
|
|
19
|
+
* page views, or any interaction not covered by useFeedbackState.
|
|
20
|
+
*
|
|
21
|
+
* Safe to call outside a provider: returns a no-op and logs a warning.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const sendSignal = useKeletSignal()
|
|
25
|
+
*
|
|
26
|
+
* sendSignal({
|
|
27
|
+
* session_id: 'my-session',
|
|
28
|
+
* kind: 'feedback',
|
|
29
|
+
* source: 'human',
|
|
30
|
+
* score: 1,
|
|
31
|
+
* trigger_name: 'copy_button_clicked',
|
|
32
|
+
* })
|
|
33
|
+
*/
|
|
34
|
+
export declare const useKeletSignal: () => ((data: FeedbackData) => Promise<void>);
|
|
15
35
|
export declare const useDefaultFeedbackHandler: () => ((data: FeedbackData) => Promise<void>);
|
|
16
36
|
export declare const KeletProvider: React.FC<React.PropsWithChildren<KeletProviderProps>>;
|
|
17
37
|
export {};
|
package/dist/feedback-ui.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import require$$0$1, { createContext, useEffect, useContext, useCallback, isValidElement, cloneElement,
|
|
1
|
+
import require$$0$1, { createContext, useEffect, useContext, useCallback, isValidElement, cloneElement, useRef, useState, useId } from "react";
|
|
2
2
|
function getDefaultExportFromCjs(x) {
|
|
3
3
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
4
4
|
}
|
|
@@ -443,7 +443,7 @@ const useKelet = () => {
|
|
|
443
443
|
}
|
|
444
444
|
return context;
|
|
445
445
|
};
|
|
446
|
-
const
|
|
446
|
+
const useKeletSignal = () => {
|
|
447
447
|
const context = useContext(KeletContext);
|
|
448
448
|
if (!context) {
|
|
449
449
|
console.warn(
|
|
@@ -455,6 +455,7 @@ const useDefaultFeedbackHandler = () => {
|
|
|
455
455
|
return context.feedback;
|
|
456
456
|
}
|
|
457
457
|
};
|
|
458
|
+
const useDefaultFeedbackHandler = useKeletSignal;
|
|
458
459
|
const KeletProvider = ({ apiKey, project, baseUrl, children }) => {
|
|
459
460
|
useEffect(() => {
|
|
460
461
|
initEventCapture();
|
|
@@ -574,6 +575,11 @@ const VoteFeedbackRoot = ({
|
|
|
574
575
|
setVote(null);
|
|
575
576
|
setTimeout(() => triggerRef.current?.focus(), 0);
|
|
576
577
|
}, [session_id, defaultText]);
|
|
578
|
+
const closePopover = useCallback(() => {
|
|
579
|
+
setShowPopover(false);
|
|
580
|
+
setFeedbackText(defaultText);
|
|
581
|
+
setTimeout(() => triggerRef.current?.focus(), 0);
|
|
582
|
+
}, [defaultText]);
|
|
577
583
|
const handleUpvote = useCallback(async () => {
|
|
578
584
|
setVote("upvote");
|
|
579
585
|
const data = {
|
|
@@ -672,9 +678,7 @@ const VoteFeedbackRoot = ({
|
|
|
672
678
|
const handleKeyDown = useCallback(
|
|
673
679
|
(e) => {
|
|
674
680
|
if (e.key === "Escape") {
|
|
675
|
-
|
|
676
|
-
setFeedbackText(defaultText);
|
|
677
|
-
triggerRef.current?.focus();
|
|
681
|
+
closePopover();
|
|
678
682
|
} else if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
679
683
|
e.preventDefault();
|
|
680
684
|
handleSubmit().then((_) => {
|
|
@@ -697,7 +701,7 @@ const VoteFeedbackRoot = ({
|
|
|
697
701
|
}
|
|
698
702
|
}
|
|
699
703
|
},
|
|
700
|
-
[handleSubmit, showPopover, popoverId,
|
|
704
|
+
[handleSubmit, showPopover, popoverId, closePopover]
|
|
701
705
|
);
|
|
702
706
|
const contextValue = {
|
|
703
707
|
onFeedback: handler,
|
|
@@ -713,6 +717,7 @@ const VoteFeedbackRoot = ({
|
|
|
713
717
|
handleTextareaChange,
|
|
714
718
|
handleSubmit,
|
|
715
719
|
handleKeyDown,
|
|
720
|
+
closePopover,
|
|
716
721
|
textareaRef,
|
|
717
722
|
triggerRef,
|
|
718
723
|
popoverId,
|
|
@@ -802,7 +807,25 @@ const DownvoteButton = ({
|
|
|
802
807
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("button", { ...slotProps, children: typeof children === "function" ? children({ isSelected }) : children });
|
|
803
808
|
};
|
|
804
809
|
const Popover = ({ asChild, children, ...props }) => {
|
|
805
|
-
const {
|
|
810
|
+
const {
|
|
811
|
+
showPopover,
|
|
812
|
+
closePopover,
|
|
813
|
+
handleKeyDown,
|
|
814
|
+
popoverId,
|
|
815
|
+
triggerId,
|
|
816
|
+
triggerRef
|
|
817
|
+
} = useVoteFeedbackContext();
|
|
818
|
+
const containerRef = useRef(null);
|
|
819
|
+
useEffect(() => {
|
|
820
|
+
if (!showPopover) return;
|
|
821
|
+
const handleMouseDown = (event) => {
|
|
822
|
+
if (containerRef.current && !containerRef.current.contains(event.target) && !triggerRef.current?.contains(event.target)) {
|
|
823
|
+
closePopover();
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
827
|
+
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
828
|
+
}, [showPopover, closePopover, triggerRef]);
|
|
806
829
|
if (!showPopover) {
|
|
807
830
|
return null;
|
|
808
831
|
}
|
|
@@ -818,14 +841,14 @@ const Popover = ({ asChild, children, ...props }) => {
|
|
|
818
841
|
};
|
|
819
842
|
if (asChild && isValidElement(children)) {
|
|
820
843
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
821
|
-
cloneElement(
|
|
822
|
-
children,
|
|
823
|
-
|
|
824
|
-
),
|
|
844
|
+
cloneElement(children, {
|
|
845
|
+
...mergeProps(slotProps, children.props),
|
|
846
|
+
ref: containerRef
|
|
847
|
+
}),
|
|
825
848
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { id: `${popoverId}-description`, className: "sr-only", children: "Provide additional feedback for your downvote" })
|
|
826
849
|
] });
|
|
827
850
|
}
|
|
828
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ...slotProps, children: [
|
|
851
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: containerRef, ...slotProps, children: [
|
|
829
852
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { id: `${popoverId}-description`, className: "sr-only", children: "Provide additional feedback for your downvote" }),
|
|
830
853
|
children
|
|
831
854
|
] });
|
|
@@ -2275,6 +2298,7 @@ export {
|
|
|
2275
2298
|
VoteFeedback,
|
|
2276
2299
|
useDefaultFeedbackHandler,
|
|
2277
2300
|
useFeedbackState,
|
|
2278
|
-
useKelet
|
|
2301
|
+
useKelet,
|
|
2302
|
+
useKeletSignal
|
|
2279
2303
|
};
|
|
2280
2304
|
//# sourceMappingURL=feedback-ui.es.js.map
|