@feedvalue/react 0.1.9 → 0.1.10

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.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode } from 'react';
2
- import { FeedValue, FeedbackData, UserTraits, FeedValueConfig } from '@feedvalue/core';
3
- export { FeedValueConfig, FeedValueEvents, FeedValueState, FeedbackData, FeedbackMetadata, UserData, UserTraits } from '@feedvalue/core';
2
+ import { FeedValue, FeedbackData, UserTraits, FeedValueConfig, ReactionState, ButtonSize, FollowUpTrigger, ReactionOption } from '@feedvalue/core';
3
+ export { FeedValueConfig, FeedValueEvents, FeedValueState, FeedbackData, FeedbackMetadata, ReactionConfig, ReactionData, ReactionOption, ReactionState, UserData, UserTraits, WidgetType } from '@feedvalue/core';
4
4
 
5
5
  /**
6
6
  * @feedvalue/react - Provider
@@ -133,6 +133,95 @@ declare function useFeedValue(): FeedValueContextValue;
133
133
  */
134
134
  declare function useFeedValueOptional(): FeedValueContextValue | null;
135
135
 
136
+ /**
137
+ * Return type for useReaction hook
138
+ */
139
+ interface UseReactionReturn extends ReactionState {
140
+ /** Submit a reaction */
141
+ react: (value: string, followUp?: string) => Promise<void>;
142
+ /** Set which option is showing follow-up input */
143
+ setShowFollowUp: (value: string | null) => void;
144
+ /** Clear the submitted state to allow re-submission */
145
+ clearSubmitted: () => void;
146
+ /** Check if widget is a reaction type */
147
+ isReactionWidget: boolean;
148
+ /** Widget configuration is ready */
149
+ isReady: boolean;
150
+ /** Whether to show text labels next to icons */
151
+ showLabels: boolean;
152
+ /** Button size */
153
+ buttonSize: ButtonSize;
154
+ /** When to show follow-up input */
155
+ followUpTrigger: FollowUpTrigger;
156
+ /** Check if an option should show follow-up based on followUpTrigger */
157
+ shouldShowFollowUp: (optionValue: string) => boolean;
158
+ }
159
+ /**
160
+ * Hook for reaction widgets
161
+ *
162
+ * Provides reaction options, submission handling, and follow-up state management.
163
+ *
164
+ * @example
165
+ * ```tsx
166
+ * 'use client';
167
+ * import { useReaction } from '@feedvalue/react';
168
+ *
169
+ * function ReactionButtons() {
170
+ * const {
171
+ * options,
172
+ * react,
173
+ * isSubmitting,
174
+ * submitted,
175
+ * error,
176
+ * showFollowUp,
177
+ * setShowFollowUp,
178
+ * isReady,
179
+ * } = useReaction();
180
+ *
181
+ * if (!isReady || !options) return null;
182
+ *
183
+ * if (submitted) {
184
+ * return <div>Thanks for your feedback!</div>;
185
+ * }
186
+ *
187
+ * return (
188
+ * <div>
189
+ * {options.map((option) => (
190
+ * <button
191
+ * key={option.value}
192
+ * onClick={() => {
193
+ * if (option.showFollowUp) {
194
+ * setShowFollowUp(option.value);
195
+ * } else {
196
+ * react(option.value);
197
+ * }
198
+ * }}
199
+ * disabled={isSubmitting}
200
+ * >
201
+ * {option.icon} {option.label}
202
+ * </button>
203
+ * ))}
204
+ *
205
+ * {showFollowUp && (
206
+ * <form onSubmit={(e) => {
207
+ * e.preventDefault();
208
+ * const form = e.target as HTMLFormElement;
209
+ * const input = form.elements.namedItem('followUp') as HTMLInputElement;
210
+ * react(showFollowUp, input.value);
211
+ * }}>
212
+ * <input name="followUp" placeholder="Tell us more..." />
213
+ * <button type="submit">Send</button>
214
+ * </form>
215
+ * )}
216
+ *
217
+ * {error && <div>Error: {error.message}</div>}
218
+ * </div>
219
+ * );
220
+ * }
221
+ * ```
222
+ */
223
+ declare function useReaction(): UseReactionReturn;
224
+
136
225
  /**
137
226
  * @feedvalue/react - Components
138
227
  *
@@ -169,5 +258,73 @@ interface FeedValueWidgetProps extends Omit<FeedValueProviderProps, 'children'>
169
258
  * ```
170
259
  */
171
260
  declare function FeedValueWidget(props: FeedValueWidgetProps): React.ReactElement;
261
+ /**
262
+ * Props for ReactionButtons component
263
+ */
264
+ interface ReactionButtonsProps {
265
+ /** Custom CSS class for the container */
266
+ className?: string;
267
+ /** Custom CSS class for buttons */
268
+ buttonClassName?: string;
269
+ /** Custom CSS class for the follow-up form */
270
+ formClassName?: string;
271
+ /** Custom CSS class for the thank you message */
272
+ thankYouClassName?: string;
273
+ /** Callback when a reaction is submitted */
274
+ onReact?: (value: string, followUp?: string) => void;
275
+ /** Callback when an error occurs */
276
+ onError?: (error: Error) => void;
277
+ /** Custom render function for buttons (for full control) */
278
+ renderButton?: (option: ReactionOption, onClick: () => void, isDisabled: boolean) => React.ReactNode;
279
+ /** Custom render function for thank you message */
280
+ renderThankYou?: (value: string) => React.ReactNode;
281
+ /** Whether to show follow-up inline (default) or in a modal */
282
+ followUpMode?: 'inline' | 'none';
283
+ /** Hide after submission (default: false) */
284
+ hideAfterSubmit?: boolean;
285
+ }
286
+ /**
287
+ * ReactionButtons component
288
+ *
289
+ * Renders reaction buttons (thumbs up/down, helpful, emoji, etc.) for inline feedback.
290
+ * Must be used within a FeedValueProvider with a reaction-type widget.
291
+ *
292
+ * @example
293
+ * ```tsx
294
+ * 'use client';
295
+ * import { FeedValueProvider, ReactionButtons } from '@feedvalue/react';
296
+ *
297
+ * function ArticleFooter() {
298
+ * return (
299
+ * <FeedValueProvider widgetId="your-reaction-widget-id" headless>
300
+ * <div>
301
+ * <h3>Was this helpful?</h3>
302
+ * <ReactionButtons
303
+ * onReact={(value) => console.log('Reacted:', value)}
304
+ * />
305
+ * </div>
306
+ * </FeedValueProvider>
307
+ * );
308
+ * }
309
+ * ```
310
+ *
311
+ * @example
312
+ * ```tsx
313
+ * // With custom button rendering
314
+ * <ReactionButtons
315
+ * renderButton={(option, onClick, isDisabled) => (
316
+ * <button
317
+ * key={option.value}
318
+ * onClick={onClick}
319
+ * disabled={isDisabled}
320
+ * className="my-custom-button"
321
+ * >
322
+ * {option.icon} {option.label}
323
+ * </button>
324
+ * )}
325
+ * />
326
+ * ```
327
+ */
328
+ declare function ReactionButtons(props: ReactionButtonsProps): React.ReactElement;
172
329
 
173
- export { type FeedValueContextValue, FeedValueProvider, type FeedValueProviderProps, FeedValueWidget, type FeedValueWidgetProps, useFeedValue, useFeedValueOptional };
330
+ export { type FeedValueContextValue, FeedValueProvider, type FeedValueProviderProps, FeedValueWidget, type FeedValueWidgetProps, ReactionButtons, type ReactionButtonsProps, type UseReactionReturn, useFeedValue, useFeedValueOptional, useReaction };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode } from 'react';
2
- import { FeedValue, FeedbackData, UserTraits, FeedValueConfig } from '@feedvalue/core';
3
- export { FeedValueConfig, FeedValueEvents, FeedValueState, FeedbackData, FeedbackMetadata, UserData, UserTraits } from '@feedvalue/core';
2
+ import { FeedValue, FeedbackData, UserTraits, FeedValueConfig, ReactionState, ButtonSize, FollowUpTrigger, ReactionOption } from '@feedvalue/core';
3
+ export { FeedValueConfig, FeedValueEvents, FeedValueState, FeedbackData, FeedbackMetadata, ReactionConfig, ReactionData, ReactionOption, ReactionState, UserData, UserTraits, WidgetType } from '@feedvalue/core';
4
4
 
5
5
  /**
6
6
  * @feedvalue/react - Provider
@@ -133,6 +133,95 @@ declare function useFeedValue(): FeedValueContextValue;
133
133
  */
134
134
  declare function useFeedValueOptional(): FeedValueContextValue | null;
135
135
 
136
+ /**
137
+ * Return type for useReaction hook
138
+ */
139
+ interface UseReactionReturn extends ReactionState {
140
+ /** Submit a reaction */
141
+ react: (value: string, followUp?: string) => Promise<void>;
142
+ /** Set which option is showing follow-up input */
143
+ setShowFollowUp: (value: string | null) => void;
144
+ /** Clear the submitted state to allow re-submission */
145
+ clearSubmitted: () => void;
146
+ /** Check if widget is a reaction type */
147
+ isReactionWidget: boolean;
148
+ /** Widget configuration is ready */
149
+ isReady: boolean;
150
+ /** Whether to show text labels next to icons */
151
+ showLabels: boolean;
152
+ /** Button size */
153
+ buttonSize: ButtonSize;
154
+ /** When to show follow-up input */
155
+ followUpTrigger: FollowUpTrigger;
156
+ /** Check if an option should show follow-up based on followUpTrigger */
157
+ shouldShowFollowUp: (optionValue: string) => boolean;
158
+ }
159
+ /**
160
+ * Hook for reaction widgets
161
+ *
162
+ * Provides reaction options, submission handling, and follow-up state management.
163
+ *
164
+ * @example
165
+ * ```tsx
166
+ * 'use client';
167
+ * import { useReaction } from '@feedvalue/react';
168
+ *
169
+ * function ReactionButtons() {
170
+ * const {
171
+ * options,
172
+ * react,
173
+ * isSubmitting,
174
+ * submitted,
175
+ * error,
176
+ * showFollowUp,
177
+ * setShowFollowUp,
178
+ * isReady,
179
+ * } = useReaction();
180
+ *
181
+ * if (!isReady || !options) return null;
182
+ *
183
+ * if (submitted) {
184
+ * return <div>Thanks for your feedback!</div>;
185
+ * }
186
+ *
187
+ * return (
188
+ * <div>
189
+ * {options.map((option) => (
190
+ * <button
191
+ * key={option.value}
192
+ * onClick={() => {
193
+ * if (option.showFollowUp) {
194
+ * setShowFollowUp(option.value);
195
+ * } else {
196
+ * react(option.value);
197
+ * }
198
+ * }}
199
+ * disabled={isSubmitting}
200
+ * >
201
+ * {option.icon} {option.label}
202
+ * </button>
203
+ * ))}
204
+ *
205
+ * {showFollowUp && (
206
+ * <form onSubmit={(e) => {
207
+ * e.preventDefault();
208
+ * const form = e.target as HTMLFormElement;
209
+ * const input = form.elements.namedItem('followUp') as HTMLInputElement;
210
+ * react(showFollowUp, input.value);
211
+ * }}>
212
+ * <input name="followUp" placeholder="Tell us more..." />
213
+ * <button type="submit">Send</button>
214
+ * </form>
215
+ * )}
216
+ *
217
+ * {error && <div>Error: {error.message}</div>}
218
+ * </div>
219
+ * );
220
+ * }
221
+ * ```
222
+ */
223
+ declare function useReaction(): UseReactionReturn;
224
+
136
225
  /**
137
226
  * @feedvalue/react - Components
138
227
  *
@@ -169,5 +258,73 @@ interface FeedValueWidgetProps extends Omit<FeedValueProviderProps, 'children'>
169
258
  * ```
170
259
  */
171
260
  declare function FeedValueWidget(props: FeedValueWidgetProps): React.ReactElement;
261
+ /**
262
+ * Props for ReactionButtons component
263
+ */
264
+ interface ReactionButtonsProps {
265
+ /** Custom CSS class for the container */
266
+ className?: string;
267
+ /** Custom CSS class for buttons */
268
+ buttonClassName?: string;
269
+ /** Custom CSS class for the follow-up form */
270
+ formClassName?: string;
271
+ /** Custom CSS class for the thank you message */
272
+ thankYouClassName?: string;
273
+ /** Callback when a reaction is submitted */
274
+ onReact?: (value: string, followUp?: string) => void;
275
+ /** Callback when an error occurs */
276
+ onError?: (error: Error) => void;
277
+ /** Custom render function for buttons (for full control) */
278
+ renderButton?: (option: ReactionOption, onClick: () => void, isDisabled: boolean) => React.ReactNode;
279
+ /** Custom render function for thank you message */
280
+ renderThankYou?: (value: string) => React.ReactNode;
281
+ /** Whether to show follow-up inline (default) or in a modal */
282
+ followUpMode?: 'inline' | 'none';
283
+ /** Hide after submission (default: false) */
284
+ hideAfterSubmit?: boolean;
285
+ }
286
+ /**
287
+ * ReactionButtons component
288
+ *
289
+ * Renders reaction buttons (thumbs up/down, helpful, emoji, etc.) for inline feedback.
290
+ * Must be used within a FeedValueProvider with a reaction-type widget.
291
+ *
292
+ * @example
293
+ * ```tsx
294
+ * 'use client';
295
+ * import { FeedValueProvider, ReactionButtons } from '@feedvalue/react';
296
+ *
297
+ * function ArticleFooter() {
298
+ * return (
299
+ * <FeedValueProvider widgetId="your-reaction-widget-id" headless>
300
+ * <div>
301
+ * <h3>Was this helpful?</h3>
302
+ * <ReactionButtons
303
+ * onReact={(value) => console.log('Reacted:', value)}
304
+ * />
305
+ * </div>
306
+ * </FeedValueProvider>
307
+ * );
308
+ * }
309
+ * ```
310
+ *
311
+ * @example
312
+ * ```tsx
313
+ * // With custom button rendering
314
+ * <ReactionButtons
315
+ * renderButton={(option, onClick, isDisabled) => (
316
+ * <button
317
+ * key={option.value}
318
+ * onClick={onClick}
319
+ * disabled={isDisabled}
320
+ * className="my-custom-button"
321
+ * >
322
+ * {option.icon} {option.label}
323
+ * </button>
324
+ * )}
325
+ * />
326
+ * ```
327
+ */
328
+ declare function ReactionButtons(props: ReactionButtonsProps): React.ReactElement;
172
329
 
173
- export { type FeedValueContextValue, FeedValueProvider, type FeedValueProviderProps, FeedValueWidget, type FeedValueWidgetProps, useFeedValue, useFeedValueOptional };
330
+ export { type FeedValueContextValue, FeedValueProvider, type FeedValueProviderProps, FeedValueWidget, type FeedValueWidgetProps, ReactionButtons, type ReactionButtonsProps, type UseReactionReturn, useFeedValue, useFeedValueOptional, useReaction };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { createContext, useRef, useEffect, useSyncExternalStore, useCallback, useMemo, useContext } from 'react';
2
- import { FeedValue } from '@feedvalue/core';
3
- import { jsx } from 'react/jsx-runtime';
1
+ import React2, { createContext, useRef, useEffect, useSyncExternalStore, useCallback, useMemo, useContext, useState } from 'react';
2
+ import { FeedValue, NEGATIVE_OPTIONS_MAP } from '@feedvalue/core';
3
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
4
 
5
5
  var FeedValueContext = createContext(null);
6
6
  var getServerSnapshot = () => ({
@@ -120,10 +120,308 @@ function useFeedValue() {
120
120
  function useFeedValueOptional() {
121
121
  return useContext(FeedValueContext);
122
122
  }
123
+ function useReaction() {
124
+ const { instance, isReady } = useFeedValue();
125
+ const [showFollowUp, setShowFollowUp] = useState(null);
126
+ const [submitted, setSubmitted] = useState(null);
127
+ const [isSubmitting, setIsSubmitting] = useState(false);
128
+ const [error, setError] = useState(null);
129
+ const options = useMemo(() => {
130
+ if (!instance || !isReady) return null;
131
+ return instance.getReactionOptions();
132
+ }, [instance, isReady]);
133
+ const reactionConfig = useMemo(() => {
134
+ if (!instance || !isReady) return null;
135
+ return instance._widgetConfig?.config ?? null;
136
+ }, [instance, isReady]);
137
+ const showLabels = reactionConfig?.showLabels ?? true;
138
+ const buttonSize = reactionConfig?.buttonSize ?? "md";
139
+ const followUpTrigger = reactionConfig?.followUpTrigger ?? "negative";
140
+ const template = reactionConfig?.template;
141
+ const isReactionWidget = useMemo(() => {
142
+ return instance?.isReaction() ?? false;
143
+ }, [instance]);
144
+ const shouldShowFollowUp = useCallback(
145
+ (optionValue) => {
146
+ if (followUpTrigger === "none") return false;
147
+ if (followUpTrigger === "all") return true;
148
+ if (template && NEGATIVE_OPTIONS_MAP[template]) {
149
+ return NEGATIVE_OPTIONS_MAP[template].includes(optionValue);
150
+ }
151
+ const option = options?.find((o) => o.value === optionValue);
152
+ return option?.showFollowUp ?? false;
153
+ },
154
+ [followUpTrigger, template, options]
155
+ );
156
+ const react = useCallback(
157
+ async (value, followUp) => {
158
+ if (!instance) {
159
+ throw new Error("FeedValue not initialized");
160
+ }
161
+ setIsSubmitting(true);
162
+ setError(null);
163
+ try {
164
+ await instance.react(value, followUp ? { followUp } : void 0);
165
+ setSubmitted(value);
166
+ setShowFollowUp(null);
167
+ } catch (err) {
168
+ const error2 = err instanceof Error ? err : new Error(String(err));
169
+ setError(error2);
170
+ throw error2;
171
+ } finally {
172
+ setIsSubmitting(false);
173
+ }
174
+ },
175
+ [instance]
176
+ );
177
+ const clearSubmitted = useCallback(() => {
178
+ setSubmitted(null);
179
+ setError(null);
180
+ }, []);
181
+ return {
182
+ options,
183
+ isSubmitting,
184
+ submitted,
185
+ error,
186
+ showFollowUp,
187
+ setShowFollowUp,
188
+ react,
189
+ clearSubmitted,
190
+ isReactionWidget,
191
+ isReady,
192
+ showLabels,
193
+ buttonSize,
194
+ followUpTrigger,
195
+ shouldShowFollowUp
196
+ };
197
+ }
123
198
  function FeedValueWidget(props) {
124
199
  return /* @__PURE__ */ jsx(FeedValueProvider, { ...props, children: null });
125
200
  }
201
+ var sizeStyles = {
202
+ sm: {
203
+ button: { padding: "6px 12px", fontSize: "12px", gap: "4px" },
204
+ icon: { fontSize: "16px" },
205
+ label: { fontSize: "12px" }
206
+ },
207
+ md: {
208
+ button: { padding: "8px 16px", fontSize: "14px", gap: "6px" },
209
+ icon: { fontSize: "18px" },
210
+ label: { fontSize: "14px" }
211
+ },
212
+ lg: {
213
+ button: { padding: "12px 20px", fontSize: "16px", gap: "8px" },
214
+ icon: { fontSize: "24px" },
215
+ label: { fontSize: "16px" }
216
+ }
217
+ };
218
+ var defaultStyles = {
219
+ container: {
220
+ display: "flex",
221
+ alignItems: "center",
222
+ gap: "8px",
223
+ flexWrap: "wrap"
224
+ },
225
+ button: {
226
+ display: "inline-flex",
227
+ alignItems: "center",
228
+ border: "1px solid #e0e0e0",
229
+ borderRadius: "20px",
230
+ background: "#fff",
231
+ cursor: "pointer",
232
+ transition: "all 0.15s ease"
233
+ },
234
+ buttonHover: {
235
+ borderColor: "#6366f1",
236
+ background: "#f5f3ff"
237
+ },
238
+ buttonDisabled: {
239
+ opacity: 0.6,
240
+ cursor: "not-allowed"
241
+ },
242
+ form: {
243
+ display: "flex",
244
+ flexDirection: "column",
245
+ gap: "8px",
246
+ marginTop: "8px",
247
+ width: "100%",
248
+ maxWidth: "400px"
249
+ },
250
+ input: {
251
+ padding: "8px 12px",
252
+ border: "1px solid #e0e0e0",
253
+ borderRadius: "8px",
254
+ fontSize: "14px",
255
+ resize: "none"
256
+ },
257
+ submitButton: {
258
+ padding: "8px 16px",
259
+ border: "none",
260
+ borderRadius: "8px",
261
+ background: "#6366f1",
262
+ color: "#fff",
263
+ cursor: "pointer",
264
+ fontSize: "14px",
265
+ fontWeight: 500,
266
+ alignSelf: "flex-start"
267
+ },
268
+ thankYou: {
269
+ color: "#059669",
270
+ fontSize: "14px",
271
+ fontWeight: 500
272
+ },
273
+ error: {
274
+ color: "#dc2626",
275
+ fontSize: "14px"
276
+ }
277
+ };
278
+ function ReactionButtonsInner({
279
+ className,
280
+ buttonClassName,
281
+ formClassName,
282
+ thankYouClassName,
283
+ onReact,
284
+ onError,
285
+ renderButton,
286
+ renderThankYou,
287
+ followUpMode = "inline",
288
+ hideAfterSubmit = false
289
+ }) {
290
+ const {
291
+ options,
292
+ react,
293
+ isSubmitting,
294
+ submitted,
295
+ error,
296
+ showFollowUp,
297
+ setShowFollowUp,
298
+ isReactionWidget,
299
+ isReady,
300
+ showLabels,
301
+ buttonSize,
302
+ shouldShowFollowUp
303
+ } = useReaction();
304
+ const [followUpText, setFollowUpText] = useState("");
305
+ const [hoveredButton, setHoveredButton] = useState(null);
306
+ const handleClick = useCallback(
307
+ (option) => {
308
+ if (shouldShowFollowUp(option.value) && followUpMode === "inline") {
309
+ setShowFollowUp(option.value);
310
+ } else {
311
+ react(option.value).then(() => onReact?.(option.value)).catch((err) => onError?.(err));
312
+ }
313
+ },
314
+ [react, setShowFollowUp, followUpMode, onReact, onError, shouldShowFollowUp]
315
+ );
316
+ const handleFollowUpSubmit = useCallback(
317
+ (e) => {
318
+ e.preventDefault();
319
+ if (!showFollowUp) return;
320
+ react(showFollowUp, followUpText.trim() || void 0).then(() => {
321
+ onReact?.(showFollowUp, followUpText.trim() || void 0);
322
+ setFollowUpText("");
323
+ }).catch((err) => onError?.(err));
324
+ },
325
+ [react, showFollowUp, followUpText, onReact, onError]
326
+ );
327
+ const handleCancelFollowUp = useCallback(() => {
328
+ setShowFollowUp(null);
329
+ setFollowUpText("");
330
+ }, [setShowFollowUp]);
331
+ if (!isReady || !isReactionWidget || !options) {
332
+ return null;
333
+ }
334
+ if (submitted && hideAfterSubmit) {
335
+ return null;
336
+ }
337
+ if (submitted) {
338
+ if (renderThankYou) {
339
+ return /* @__PURE__ */ jsx(Fragment, { children: renderThankYou(submitted) });
340
+ }
341
+ return /* @__PURE__ */ jsx("div", { className: thankYouClassName, style: thankYouClassName ? void 0 : defaultStyles.thankYou, children: "Thanks for your feedback!" });
342
+ }
343
+ const followUpOption = showFollowUp ? options.find((o) => o.value === showFollowUp) : null;
344
+ return /* @__PURE__ */ jsxs("div", { className, style: className ? void 0 : defaultStyles.container, children: [
345
+ !showFollowUp && options.map((option) => {
346
+ if (renderButton) {
347
+ return /* @__PURE__ */ jsx(React2.Fragment, { children: renderButton(option, () => handleClick(option), isSubmitting) }, option.value);
348
+ }
349
+ const isHovered = hoveredButton === option.value;
350
+ const sizeStyle = sizeStyles[buttonSize] || sizeStyles.md;
351
+ const buttonStyle = {
352
+ ...defaultStyles.button,
353
+ ...sizeStyle.button,
354
+ ...isHovered ? defaultStyles.buttonHover : {},
355
+ ...isSubmitting ? defaultStyles.buttonDisabled : {}
356
+ };
357
+ return /* @__PURE__ */ jsxs(
358
+ "button",
359
+ {
360
+ type: "button",
361
+ className: buttonClassName,
362
+ style: buttonClassName ? void 0 : buttonStyle,
363
+ onClick: () => handleClick(option),
364
+ onMouseEnter: () => setHoveredButton(option.value),
365
+ onMouseLeave: () => setHoveredButton(null),
366
+ disabled: isSubmitting,
367
+ "aria-label": option.label,
368
+ children: [
369
+ /* @__PURE__ */ jsx("span", { role: "img", "aria-hidden": "true", style: sizeStyle.icon, children: option.icon }),
370
+ showLabels && /* @__PURE__ */ jsx("span", { style: sizeStyle.label, children: option.label })
371
+ ]
372
+ },
373
+ option.value
374
+ );
375
+ }),
376
+ showFollowUp && followUpOption && /* @__PURE__ */ jsxs(
377
+ "form",
378
+ {
379
+ className: formClassName,
380
+ style: formClassName ? void 0 : defaultStyles.form,
381
+ onSubmit: handleFollowUpSubmit,
382
+ children: [
383
+ /* @__PURE__ */ jsx(
384
+ "textarea",
385
+ {
386
+ value: followUpText,
387
+ onChange: (e) => setFollowUpText(e.target.value),
388
+ placeholder: followUpOption.followUpPlaceholder ?? "Tell us more (optional)",
389
+ rows: 3,
390
+ style: defaultStyles.input,
391
+ disabled: isSubmitting
392
+ }
393
+ ),
394
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
395
+ /* @__PURE__ */ jsx(
396
+ "button",
397
+ {
398
+ type: "submit",
399
+ style: defaultStyles.submitButton,
400
+ disabled: isSubmitting,
401
+ children: isSubmitting ? "Sending..." : "Send"
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(
405
+ "button",
406
+ {
407
+ type: "button",
408
+ onClick: handleCancelFollowUp,
409
+ style: { ...defaultStyles.button, padding: "8px 16px" },
410
+ disabled: isSubmitting,
411
+ children: "Cancel"
412
+ }
413
+ )
414
+ ] })
415
+ ]
416
+ }
417
+ ),
418
+ error && /* @__PURE__ */ jsx("div", { style: defaultStyles.error, children: error.message })
419
+ ] });
420
+ }
421
+ function ReactionButtons(props) {
422
+ return /* @__PURE__ */ jsx(ReactionButtonsInner, { ...props });
423
+ }
126
424
 
127
- export { FeedValueProvider, FeedValueWidget, useFeedValue, useFeedValueOptional };
425
+ export { FeedValueProvider, FeedValueWidget, ReactionButtons, useFeedValue, useFeedValueOptional, useReaction };
128
426
  //# sourceMappingURL=index.js.map
129
427
  //# sourceMappingURL=index.js.map