@deepcitation/deepcitation-js 1.1.13 → 1.1.14
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { type ReactNode } from "react";
|
|
2
2
|
import { type CitationStatus } from "../types/citation.js";
|
|
3
3
|
import type { Verification } from "../types/verification.js";
|
|
4
|
-
import type { BaseCitationProps, CitationEventHandlers, CitationRenderProps, CitationVariant } from "./types.js";
|
|
4
|
+
import type { BaseCitationProps, CitationBehaviorConfig, CitationEventHandlers, CitationRenderProps, CitationVariant } from "./types.js";
|
|
5
5
|
import "./styles.css";
|
|
6
6
|
export type { CitationVariant } from "./types.js";
|
|
7
7
|
/**
|
|
@@ -74,6 +74,29 @@ export type { CitationVariant } from "./types.js";
|
|
|
74
74
|
* popoverPosition="hidden"
|
|
75
75
|
* />
|
|
76
76
|
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example Customize click behavior - disable image expand
|
|
79
|
+
* ```tsx
|
|
80
|
+
* <CitationComponent
|
|
81
|
+
* citation={citation}
|
|
82
|
+
* verification={verificationResult}
|
|
83
|
+
* behaviorConfig={{ disableImageExpand: true }}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example Custom click handler with default behavior
|
|
88
|
+
* ```tsx
|
|
89
|
+
* <CitationComponent
|
|
90
|
+
* citation={citation}
|
|
91
|
+
* verification={verificationResult}
|
|
92
|
+
* behaviorConfig={{
|
|
93
|
+
* onClick: (context, event) => {
|
|
94
|
+
* // Log analytics, then let default behavior proceed
|
|
95
|
+
* analytics.track('citation_clicked', { key: context.citationKey });
|
|
96
|
+
* }
|
|
97
|
+
* }}
|
|
98
|
+
* />
|
|
99
|
+
* ```
|
|
77
100
|
*/
|
|
78
101
|
export interface CitationComponentProps extends BaseCitationProps {
|
|
79
102
|
/**
|
|
@@ -97,8 +120,22 @@ export interface CitationComponentProps extends BaseCitationProps {
|
|
|
97
120
|
displayBrackets?: boolean;
|
|
98
121
|
/**
|
|
99
122
|
* Event handlers for citation interactions.
|
|
123
|
+
* These are always called regardless of behaviorConfig settings.
|
|
100
124
|
*/
|
|
101
125
|
eventHandlers?: CitationEventHandlers;
|
|
126
|
+
/**
|
|
127
|
+
* Configuration for customizing default click/hover behaviors.
|
|
128
|
+
* Use this to disable or extend the built-in behaviors.
|
|
129
|
+
*
|
|
130
|
+
* Default behaviors:
|
|
131
|
+
* - Hover: Shows zoom-in cursor when popover is pinned and has image
|
|
132
|
+
* - Click 1: Pins the popover open (stays visible without hover)
|
|
133
|
+
* - Click 2: Opens full-size image overlay (if image available)
|
|
134
|
+
* - Click 3: Closes image and unpins popover
|
|
135
|
+
*
|
|
136
|
+
* @see CitationBehaviorConfig for all options
|
|
137
|
+
*/
|
|
138
|
+
behaviorConfig?: CitationBehaviorConfig;
|
|
102
139
|
/**
|
|
103
140
|
* Enable mobile touch handlers.
|
|
104
141
|
* @default false
|
|
@@ -198,7 +198,7 @@ const DefaultPopoverContent = ({ citation, verification, status, onImageClick, }
|
|
|
198
198
|
* This means partial matches have blue text (because they were found) but
|
|
199
199
|
* an orange indicator (because they didn't match exactly).
|
|
200
200
|
*/
|
|
201
|
-
export const CitationComponent = forwardRef(({ citation, children, className, displayKeySpan = true, displayBrackets = true, fallbackDisplay, verification, variant = "brackets", eventHandlers, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
|
|
201
|
+
export const CitationComponent = forwardRef(({ citation, children, className, displayKeySpan = true, displayBrackets = true, fallbackDisplay, verification, variant = "brackets", eventHandlers, behaviorConfig, isMobile = false, renderIndicator, renderContent, popoverPosition = "top", renderPopoverContent, }, ref) => {
|
|
202
202
|
const containerRef = useRef(null);
|
|
203
203
|
const wrapperRef = useRef(null);
|
|
204
204
|
const [expandedImageSrc, setExpandedImageSrc] = useState(null);
|
|
@@ -247,9 +247,63 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
|
|
|
247
247
|
}, [isTooltipExpanded]);
|
|
248
248
|
const citationKey = useMemo(() => generateCitationKey(citation), [citation]);
|
|
249
249
|
const citationInstanceId = useMemo(() => generateCitationInstanceId(citationKey), [citationKey]);
|
|
250
|
+
// Create behavior context for custom handlers
|
|
251
|
+
const getBehaviorContext = useCallback(() => ({
|
|
252
|
+
citation,
|
|
253
|
+
citationKey,
|
|
254
|
+
verification: verification ?? null,
|
|
255
|
+
isTooltipExpanded,
|
|
256
|
+
isImageExpanded: !!expandedImageSrc,
|
|
257
|
+
hasImage: !!verification?.verificationImageBase64,
|
|
258
|
+
}), [citation, citationKey, verification, isTooltipExpanded, expandedImageSrc]);
|
|
259
|
+
// Apply behavior actions from custom handler
|
|
260
|
+
const applyBehaviorActions = useCallback((actions) => {
|
|
261
|
+
if (actions.setTooltipExpanded !== undefined) {
|
|
262
|
+
setIsTooltipExpanded(actions.setTooltipExpanded);
|
|
263
|
+
}
|
|
264
|
+
if (actions.setImageExpanded !== undefined) {
|
|
265
|
+
if (typeof actions.setImageExpanded === "string") {
|
|
266
|
+
setExpandedImageSrc(actions.setImageExpanded);
|
|
267
|
+
}
|
|
268
|
+
else if (actions.setImageExpanded === true && verification?.verificationImageBase64) {
|
|
269
|
+
setExpandedImageSrc(verification.verificationImageBase64);
|
|
270
|
+
}
|
|
271
|
+
else if (actions.setImageExpanded === false) {
|
|
272
|
+
setExpandedImageSrc(null);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (actions.setPhrasesExpanded !== undefined) {
|
|
276
|
+
setIsPhrasesExpanded(actions.setPhrasesExpanded);
|
|
277
|
+
}
|
|
278
|
+
}, [verification?.verificationImageBase64]);
|
|
250
279
|
const handleToggleTooltip = useCallback((e) => {
|
|
251
280
|
e.preventDefault();
|
|
252
281
|
e.stopPropagation();
|
|
282
|
+
const context = getBehaviorContext();
|
|
283
|
+
// Call custom onClick handler first (if provided)
|
|
284
|
+
if (behaviorConfig?.onClick) {
|
|
285
|
+
const result = behaviorConfig.onClick(context, e);
|
|
286
|
+
// If custom handler returns actions, apply them and skip default behavior
|
|
287
|
+
if (result && typeof result === "object") {
|
|
288
|
+
applyBehaviorActions(result);
|
|
289
|
+
// Always call eventHandlers.onClick regardless of custom behavior
|
|
290
|
+
eventHandlers?.onClick?.(citation, citationKey, e);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// If custom handler returns false, skip default behavior entirely
|
|
294
|
+
if (result === false) {
|
|
295
|
+
// Always call eventHandlers.onClick regardless of custom behavior
|
|
296
|
+
eventHandlers?.onClick?.(citation, citationKey, e);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
// Otherwise (undefined/void), proceed with default behavior
|
|
300
|
+
}
|
|
301
|
+
// Check if click behavior is completely disabled
|
|
302
|
+
if (behaviorConfig?.disableClickBehavior) {
|
|
303
|
+
eventHandlers?.onClick?.(citation, citationKey, e);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Default click behavior
|
|
253
307
|
// If we have a verification image
|
|
254
308
|
if (verification?.verificationImageBase64) {
|
|
255
309
|
if (expandedImageSrc) {
|
|
@@ -258,27 +312,36 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
|
|
|
258
312
|
setIsTooltipExpanded(false);
|
|
259
313
|
}
|
|
260
314
|
else if (isTooltipExpanded) {
|
|
261
|
-
// Already pinned - second click expands image
|
|
262
|
-
|
|
315
|
+
// Already pinned - second click expands image (unless disabled)
|
|
316
|
+
if (!behaviorConfig?.disableImageExpand) {
|
|
317
|
+
setExpandedImageSrc(verification.verificationImageBase64);
|
|
318
|
+
}
|
|
263
319
|
}
|
|
264
320
|
else {
|
|
265
|
-
// First click - just pin the popover open
|
|
266
|
-
|
|
321
|
+
// First click - just pin the popover open (unless disabled)
|
|
322
|
+
if (!behaviorConfig?.disablePopoverPin) {
|
|
323
|
+
setIsTooltipExpanded(true);
|
|
324
|
+
}
|
|
267
325
|
}
|
|
268
326
|
}
|
|
269
327
|
else {
|
|
270
328
|
// No image - toggle phrases expansion for miss/partial tooltips
|
|
271
|
-
|
|
272
|
-
|
|
329
|
+
if (!behaviorConfig?.disablePopoverPin) {
|
|
330
|
+
setIsTooltipExpanded((prev) => !prev);
|
|
331
|
+
setIsPhrasesExpanded((prev) => !prev);
|
|
332
|
+
}
|
|
273
333
|
}
|
|
274
334
|
eventHandlers?.onClick?.(citation, citationKey, e);
|
|
275
335
|
}, [
|
|
276
336
|
eventHandlers,
|
|
337
|
+
behaviorConfig,
|
|
277
338
|
citation,
|
|
278
339
|
citationKey,
|
|
279
340
|
verification?.verificationImageBase64,
|
|
280
341
|
expandedImageSrc,
|
|
281
342
|
isTooltipExpanded,
|
|
343
|
+
getBehaviorContext,
|
|
344
|
+
applyBehaviorActions,
|
|
282
345
|
]);
|
|
283
346
|
const status = getCitationStatus(verification ?? null);
|
|
284
347
|
// const { isVerified, isPending } = status;
|
|
@@ -297,11 +360,19 @@ export const CitationComponent = forwardRef(({ citation, children, className, di
|
|
|
297
360
|
const foundStatusClass = useMemo(() => getFoundStatusClass(status), [status]);
|
|
298
361
|
// Event handlers
|
|
299
362
|
const handleMouseEnter = useCallback(() => {
|
|
363
|
+
// Call custom onHover.onEnter handler (if provided)
|
|
364
|
+
if (behaviorConfig?.onHover?.onEnter) {
|
|
365
|
+
behaviorConfig.onHover.onEnter(getBehaviorContext());
|
|
366
|
+
}
|
|
300
367
|
eventHandlers?.onMouseEnter?.(citation, citationKey);
|
|
301
|
-
}, [eventHandlers, citation, citationKey]);
|
|
368
|
+
}, [eventHandlers, behaviorConfig, citation, citationKey, getBehaviorContext]);
|
|
302
369
|
const handleMouseLeave = useCallback(() => {
|
|
370
|
+
// Call custom onHover.onLeave handler (if provided)
|
|
371
|
+
if (behaviorConfig?.onHover?.onLeave) {
|
|
372
|
+
behaviorConfig.onHover.onLeave(getBehaviorContext());
|
|
373
|
+
}
|
|
303
374
|
eventHandlers?.onMouseLeave?.(citation, citationKey);
|
|
304
|
-
}, [eventHandlers, citation, citationKey]);
|
|
375
|
+
}, [eventHandlers, behaviorConfig, citation, citationKey, getBehaviorContext]);
|
|
305
376
|
const handleTouchEnd = useCallback((e) => {
|
|
306
377
|
if (isMobile) {
|
|
307
378
|
e.preventDefault();
|
package/lib/react/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @packageDocumentation
|
|
12
12
|
*/
|
|
13
|
-
export type { CitationContentProps, CitationRenderProps, CitationTooltipProps, CitationStyles, CitationStateClasses, CitationCursorClasses, CitationEventHandlers, CitationVariant as CitationVariantType, UrlFetchStatus, UrlCitationMeta, UrlCitationProps, } from "./types.js";
|
|
13
|
+
export type { CitationContentProps, CitationRenderProps, CitationTooltipProps, CitationStyles, CitationStateClasses, CitationCursorClasses, CitationEventHandlers, CitationVariant as CitationVariantType, UrlFetchStatus, UrlCitationMeta, UrlCitationProps, CitationBehaviorConfig, CitationBehaviorContext, CitationBehaviorActions, CitationClickBehavior, CitationHoverBehavior, } from "./types.js";
|
|
14
14
|
export { extractDomain, isBlockedStatus, isErrorStatus, isVerifiedStatus, } from "./UrlCitationComponent.js";
|
|
15
15
|
export { generateCitationKey, generateCitationInstanceId, getCitationDisplayText, getCitationKeySpanText, classNames, CITATION_X_PADDING, CITATION_Y_PADDING, } from "./utils.js";
|
|
16
16
|
export { CitationComponent, MemoizedCitationComponent, type CitationVariant, type CitationComponentProps, } from "./CitationComponent.js";
|
package/lib/react/types.d.ts
CHANGED
|
@@ -184,6 +184,147 @@ export interface CitationEventHandlers {
|
|
|
184
184
|
/** Called on touch end (mobile) */
|
|
185
185
|
onTouchEnd?: (citation: Citation, citationKey: string, event: React.TouchEvent) => void;
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Context provided to behavior handlers for making decisions.
|
|
189
|
+
*/
|
|
190
|
+
export interface CitationBehaviorContext {
|
|
191
|
+
/** The citation data */
|
|
192
|
+
citation: Citation;
|
|
193
|
+
/** Unique key for this citation */
|
|
194
|
+
citationKey: string;
|
|
195
|
+
/** Verification result if available */
|
|
196
|
+
verification: Verification | null;
|
|
197
|
+
/** Whether the popover is currently pinned open */
|
|
198
|
+
isTooltipExpanded: boolean;
|
|
199
|
+
/** Whether the full-size image overlay is currently open */
|
|
200
|
+
isImageExpanded: boolean;
|
|
201
|
+
/** Whether a verification image is available */
|
|
202
|
+
hasImage: boolean;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Actions that can be performed by the citation component.
|
|
206
|
+
* These are returned by behavior handlers to control component state.
|
|
207
|
+
*/
|
|
208
|
+
export interface CitationBehaviorActions {
|
|
209
|
+
/** Pin or unpin the popover (keeps it visible without hover) */
|
|
210
|
+
setTooltipExpanded?: boolean;
|
|
211
|
+
/** Open or close the full-size image overlay */
|
|
212
|
+
setImageExpanded?: boolean | string;
|
|
213
|
+
/** Expand or collapse the search phrases list (for miss/partial states) */
|
|
214
|
+
setPhrasesExpanded?: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Configuration for click behavior.
|
|
218
|
+
* Return actions to perform, or `false` to prevent default behavior.
|
|
219
|
+
*/
|
|
220
|
+
export type CitationClickBehavior = (context: CitationBehaviorContext, event: React.MouseEvent | React.TouchEvent) => CitationBehaviorActions | false | void;
|
|
221
|
+
/**
|
|
222
|
+
* Configuration for hover behavior.
|
|
223
|
+
*/
|
|
224
|
+
export interface CitationHoverBehavior {
|
|
225
|
+
/** Called when mouse enters the citation */
|
|
226
|
+
onEnter?: (context: CitationBehaviorContext) => void;
|
|
227
|
+
/** Called when mouse leaves the citation */
|
|
228
|
+
onLeave?: (context: CitationBehaviorContext) => void;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Configuration for customizing default citation behaviors.
|
|
232
|
+
*
|
|
233
|
+
* @example Use defaults (no config needed)
|
|
234
|
+
* ```tsx
|
|
235
|
+
* <CitationComponent citation={citation} verification={verification} />
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* @example Disable click-to-expand image (popover still pins on click)
|
|
239
|
+
* ```tsx
|
|
240
|
+
* <CitationComponent
|
|
241
|
+
* citation={citation}
|
|
242
|
+
* verification={verification}
|
|
243
|
+
* behaviorConfig={{ disableImageExpand: true }}
|
|
244
|
+
* />
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @example Disable all click behavior
|
|
248
|
+
* ```tsx
|
|
249
|
+
* <CitationComponent
|
|
250
|
+
* citation={citation}
|
|
251
|
+
* verification={verification}
|
|
252
|
+
* behaviorConfig={{ disableClickBehavior: true }}
|
|
253
|
+
* />
|
|
254
|
+
* ```
|
|
255
|
+
*
|
|
256
|
+
* @example Custom click behavior (extend defaults)
|
|
257
|
+
* ```tsx
|
|
258
|
+
* <CitationComponent
|
|
259
|
+
* citation={citation}
|
|
260
|
+
* verification={verification}
|
|
261
|
+
* behaviorConfig={{
|
|
262
|
+
* onClick: (context, event) => {
|
|
263
|
+
* // Log analytics
|
|
264
|
+
* analytics.track('citation_clicked', { key: context.citationKey });
|
|
265
|
+
* // Return nothing to use default behavior
|
|
266
|
+
* }
|
|
267
|
+
* }}
|
|
268
|
+
* />
|
|
269
|
+
* ```
|
|
270
|
+
*
|
|
271
|
+
* @example Override click behavior completely
|
|
272
|
+
* ```tsx
|
|
273
|
+
* <CitationComponent
|
|
274
|
+
* citation={citation}
|
|
275
|
+
* verification={verification}
|
|
276
|
+
* behaviorConfig={{
|
|
277
|
+
* onClick: (context, event) => {
|
|
278
|
+
* // Custom behavior: always open image immediately
|
|
279
|
+
* if (context.hasImage) {
|
|
280
|
+
* return { setImageExpanded: true };
|
|
281
|
+
* }
|
|
282
|
+
* return false; // Prevent default behavior
|
|
283
|
+
* }
|
|
284
|
+
* }}
|
|
285
|
+
* />
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export interface CitationBehaviorConfig {
|
|
289
|
+
/**
|
|
290
|
+
* Disable the default click behavior entirely.
|
|
291
|
+
* When true, clicks won't pin popovers or expand images.
|
|
292
|
+
* Your eventHandlers.onClick will still be called.
|
|
293
|
+
* @default false
|
|
294
|
+
*/
|
|
295
|
+
disableClickBehavior?: boolean;
|
|
296
|
+
/**
|
|
297
|
+
* Disable the click-to-expand image behavior.
|
|
298
|
+
* First click still pins the popover, but second click won't expand the image.
|
|
299
|
+
* @default false
|
|
300
|
+
*/
|
|
301
|
+
disableImageExpand?: boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Disable the popover pinning behavior.
|
|
304
|
+
* Clicks won't pin the popover open; it will only show on hover.
|
|
305
|
+
* @default false
|
|
306
|
+
*/
|
|
307
|
+
disablePopoverPin?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* Custom click behavior handler.
|
|
310
|
+
*
|
|
311
|
+
* - Return `CitationBehaviorActions` to override default behavior with specific actions
|
|
312
|
+
* - Return `false` to prevent default behavior entirely
|
|
313
|
+
* - Return `void`/`undefined` to let default behavior proceed
|
|
314
|
+
*
|
|
315
|
+
* This is called BEFORE the default behavior, allowing you to:
|
|
316
|
+
* 1. Add side effects (analytics, etc.) and let defaults proceed (return void)
|
|
317
|
+
* 2. Completely override behavior (return actions or false)
|
|
318
|
+
*
|
|
319
|
+
* Note: eventHandlers.onClick is always called regardless of this handler's return value.
|
|
320
|
+
*/
|
|
321
|
+
onClick?: CitationClickBehavior;
|
|
322
|
+
/**
|
|
323
|
+
* Custom hover behavior handlers.
|
|
324
|
+
* These are called in addition to (not instead of) eventHandlers.onMouseEnter/Leave.
|
|
325
|
+
*/
|
|
326
|
+
onHover?: CitationHoverBehavior;
|
|
327
|
+
}
|
|
187
328
|
/**
|
|
188
329
|
* Props for the tooltip wrapper component
|
|
189
330
|
*/
|