@feedvalue/core 0.1.11 → 0.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.
- package/dist/index.cjs +170 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.js +169 -15
- package/dist/index.js.map +1 -1
- package/dist/umd/index.min.js +3 -3
- package/dist/umd/index.min.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @feedvalue/core - Context Capture
|
|
3
|
+
*
|
|
4
|
+
* Captures DOM context from a trigger element for enhanced feedback metadata.
|
|
5
|
+
* This allows understanding WHERE on a page the user reacted, not just which page.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Context capture configuration
|
|
9
|
+
*/
|
|
10
|
+
interface ContextCaptureConfig {
|
|
11
|
+
/** Enable automatic context capture (default: true) */
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/** Maximum parent traversal depth (default: 5) */
|
|
14
|
+
maxDepth: number;
|
|
15
|
+
/** Maximum heading text length (default: 100) */
|
|
16
|
+
maxHeadingLength: number;
|
|
17
|
+
/** Data attribute whitelist (default: ['data-section', 'data-feature', 'data-component']) */
|
|
18
|
+
dataAttributeWhitelist: string[];
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_CONTEXT_CAPTURE_CONFIG: ContextCaptureConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Captured context data from DOM traversal
|
|
23
|
+
*/
|
|
24
|
+
interface CapturedContext {
|
|
25
|
+
/** ID of nearest parent element with an id attribute */
|
|
26
|
+
sectionId?: string;
|
|
27
|
+
/** Tag name of the section element (e.g., 'section', 'article', 'div') */
|
|
28
|
+
sectionTag?: string;
|
|
29
|
+
/** Text content of nearest heading (h1-h6) */
|
|
30
|
+
nearestHeading?: string;
|
|
31
|
+
/** Level of the nearest heading (1-6) */
|
|
32
|
+
headingLevel?: number;
|
|
33
|
+
/** Captured data-* attributes from element and parents */
|
|
34
|
+
dataAttributes?: Record<string, string>;
|
|
35
|
+
/** CSS selector path to the trigger element (for debugging) */
|
|
36
|
+
cssSelector?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Capture DOM context from a trigger element
|
|
40
|
+
*
|
|
41
|
+
* @param triggerElement - The element that triggered the reaction (e.g., the button)
|
|
42
|
+
* @param config - Configuration for context capture
|
|
43
|
+
* @returns Captured context object or null if disabled/unavailable
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Capture context from a button click
|
|
48
|
+
* const context = captureContext(event.currentTarget, config);
|
|
49
|
+
* // Returns: { sectionId: 'installation', nearestHeading: 'Installation', ... }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function captureContext(triggerElement: Element | null, config?: ContextCaptureConfig): CapturedContext | null;
|
|
53
|
+
|
|
1
54
|
/**
|
|
2
55
|
* @feedvalue/core - Type Definitions
|
|
3
56
|
*
|
|
4
57
|
* Core types for the FeedValue SDK. These types are shared across
|
|
5
58
|
* all framework packages (React, Vue) and the vanilla API.
|
|
6
59
|
*/
|
|
60
|
+
|
|
7
61
|
/**
|
|
8
62
|
* Widget position on screen
|
|
9
63
|
*/
|
|
@@ -43,6 +97,8 @@ interface WidgetStyling {
|
|
|
43
97
|
backgroundColor: string;
|
|
44
98
|
textColor: string;
|
|
45
99
|
buttonTextColor: string;
|
|
100
|
+
borderColor: string;
|
|
101
|
+
borderWidth: string;
|
|
46
102
|
borderRadius: string;
|
|
47
103
|
customCSS?: string | undefined;
|
|
48
104
|
}
|
|
@@ -91,6 +147,14 @@ interface FeedValueOptions {
|
|
|
91
147
|
* @default false
|
|
92
148
|
*/
|
|
93
149
|
headless?: boolean | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Context capture configuration for reaction widgets.
|
|
152
|
+
* When enabled, captures DOM context (section ID, nearest heading, data attributes)
|
|
153
|
+
* from the trigger element when react() is called.
|
|
154
|
+
*
|
|
155
|
+
* @default { enabled: true, maxDepth: 5, maxHeadingLength: 100, dataAttributeWhitelist: ['data-section', 'data-feature', 'data-component', 'data-fv-section', 'data-fv-feature'] }
|
|
156
|
+
*/
|
|
157
|
+
contextCapture?: Partial<ContextCaptureConfig> | undefined;
|
|
94
158
|
}
|
|
95
159
|
/**
|
|
96
160
|
* Runtime configuration (can be changed after init)
|
|
@@ -425,10 +489,11 @@ interface FeedValueInstance {
|
|
|
425
489
|
/**
|
|
426
490
|
* Submit a reaction.
|
|
427
491
|
* @param value - Selected reaction option value
|
|
428
|
-
* @param options - Optional follow-up text
|
|
492
|
+
* @param options - Optional follow-up text and trigger element for context capture
|
|
429
493
|
*/
|
|
430
494
|
react(value: string, options?: {
|
|
431
495
|
followUp?: string;
|
|
496
|
+
triggerElement?: Element | null;
|
|
432
497
|
}): Promise<void>;
|
|
433
498
|
/**
|
|
434
499
|
* Check if widget is a reaction type
|
|
@@ -450,6 +515,8 @@ interface FeedValueInstance {
|
|
|
450
515
|
setConfig(config: Partial<FeedValueConfig>): void;
|
|
451
516
|
/** Get current configuration */
|
|
452
517
|
getConfig(): FeedValueConfig;
|
|
518
|
+
/** Get widget configuration (from API) */
|
|
519
|
+
getWidgetConfig(): WidgetConfig | null;
|
|
453
520
|
/** Subscribe to state changes */
|
|
454
521
|
subscribe(callback: () => void): () => void;
|
|
455
522
|
/** Get current state snapshot */
|
|
@@ -493,6 +560,7 @@ declare class FeedValue implements FeedValueInstance {
|
|
|
493
560
|
private readonly apiClient;
|
|
494
561
|
private readonly emitter;
|
|
495
562
|
private readonly headless;
|
|
563
|
+
private readonly contextCaptureConfig;
|
|
496
564
|
private config;
|
|
497
565
|
private widgetConfig;
|
|
498
566
|
private state;
|
|
@@ -567,10 +635,11 @@ declare class FeedValue implements FeedValueInstance {
|
|
|
567
635
|
/**
|
|
568
636
|
* Submit a reaction.
|
|
569
637
|
* @param value - Selected reaction option value
|
|
570
|
-
* @param options - Optional follow-up text
|
|
638
|
+
* @param options - Optional follow-up text and trigger element for context capture
|
|
571
639
|
*/
|
|
572
640
|
react(value: string, options?: {
|
|
573
641
|
followUp?: string;
|
|
642
|
+
triggerElement?: Element | null;
|
|
574
643
|
}): Promise<void>;
|
|
575
644
|
/**
|
|
576
645
|
* Check if widget is a reaction type
|
|
@@ -848,4 +917,4 @@ declare function generateFingerprint(): string;
|
|
|
848
917
|
*/
|
|
849
918
|
declare function clearFingerprint(): void;
|
|
850
919
|
|
|
851
|
-
export { ApiClient, type ButtonSize, type ConfigResponse, type CustomField, type CustomFieldType, DEFAULT_API_BASE_URL, type EmojiSentiment, type EventHandler, FeedValue, type FeedValueConfig, type FeedValueEvents, type FeedValueInstance, type FeedValueOptions, type FeedValueState, type FeedbackData, type FeedbackMetadata, type FeedbackResponse, type FollowUpTrigger, NEGATIVE_OPTIONS_MAP, type ReactionBorderRadius, type ReactionBorderWidth, type ReactionConfig, type ReactionData, type ReactionMetadata, type ReactionOption, type ReactionResponse, type ReactionState, type ReactionStyling, type ReactionTemplate, type SubmissionUserData, type TriggerIconType, TypedEventEmitter, type UserData, type UserTraits, type WidgetConfig, type WidgetPosition, type WidgetStyling, type WidgetTheme, type WidgetType, type WidgetUIConfig, clearFingerprint, generateFingerprint };
|
|
920
|
+
export { ApiClient, type ButtonSize, type CapturedContext, type ConfigResponse, type ContextCaptureConfig, type CustomField, type CustomFieldType, DEFAULT_API_BASE_URL, DEFAULT_CONTEXT_CAPTURE_CONFIG, type EmojiSentiment, type EventHandler, FeedValue, type FeedValueConfig, type FeedValueEvents, type FeedValueInstance, type FeedValueOptions, type FeedValueState, type FeedbackData, type FeedbackMetadata, type FeedbackResponse, type FollowUpTrigger, NEGATIVE_OPTIONS_MAP, type ReactionBorderRadius, type ReactionBorderWidth, type ReactionConfig, type ReactionData, type ReactionMetadata, type ReactionOption, type ReactionResponse, type ReactionState, type ReactionStyling, type ReactionTemplate, type SubmissionUserData, type TriggerIconType, TypedEventEmitter, type UserData, type UserTraits, type WidgetConfig, type WidgetPosition, type WidgetStyling, type WidgetTheme, type WidgetType, type WidgetUIConfig, captureContext, clearFingerprint, generateFingerprint };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @feedvalue/core - Context Capture
|
|
3
|
+
*
|
|
4
|
+
* Captures DOM context from a trigger element for enhanced feedback metadata.
|
|
5
|
+
* This allows understanding WHERE on a page the user reacted, not just which page.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Context capture configuration
|
|
9
|
+
*/
|
|
10
|
+
interface ContextCaptureConfig {
|
|
11
|
+
/** Enable automatic context capture (default: true) */
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
/** Maximum parent traversal depth (default: 5) */
|
|
14
|
+
maxDepth: number;
|
|
15
|
+
/** Maximum heading text length (default: 100) */
|
|
16
|
+
maxHeadingLength: number;
|
|
17
|
+
/** Data attribute whitelist (default: ['data-section', 'data-feature', 'data-component']) */
|
|
18
|
+
dataAttributeWhitelist: string[];
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_CONTEXT_CAPTURE_CONFIG: ContextCaptureConfig;
|
|
21
|
+
/**
|
|
22
|
+
* Captured context data from DOM traversal
|
|
23
|
+
*/
|
|
24
|
+
interface CapturedContext {
|
|
25
|
+
/** ID of nearest parent element with an id attribute */
|
|
26
|
+
sectionId?: string;
|
|
27
|
+
/** Tag name of the section element (e.g., 'section', 'article', 'div') */
|
|
28
|
+
sectionTag?: string;
|
|
29
|
+
/** Text content of nearest heading (h1-h6) */
|
|
30
|
+
nearestHeading?: string;
|
|
31
|
+
/** Level of the nearest heading (1-6) */
|
|
32
|
+
headingLevel?: number;
|
|
33
|
+
/** Captured data-* attributes from element and parents */
|
|
34
|
+
dataAttributes?: Record<string, string>;
|
|
35
|
+
/** CSS selector path to the trigger element (for debugging) */
|
|
36
|
+
cssSelector?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Capture DOM context from a trigger element
|
|
40
|
+
*
|
|
41
|
+
* @param triggerElement - The element that triggered the reaction (e.g., the button)
|
|
42
|
+
* @param config - Configuration for context capture
|
|
43
|
+
* @returns Captured context object or null if disabled/unavailable
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Capture context from a button click
|
|
48
|
+
* const context = captureContext(event.currentTarget, config);
|
|
49
|
+
* // Returns: { sectionId: 'installation', nearestHeading: 'Installation', ... }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function captureContext(triggerElement: Element | null, config?: ContextCaptureConfig): CapturedContext | null;
|
|
53
|
+
|
|
1
54
|
/**
|
|
2
55
|
* @feedvalue/core - Type Definitions
|
|
3
56
|
*
|
|
4
57
|
* Core types for the FeedValue SDK. These types are shared across
|
|
5
58
|
* all framework packages (React, Vue) and the vanilla API.
|
|
6
59
|
*/
|
|
60
|
+
|
|
7
61
|
/**
|
|
8
62
|
* Widget position on screen
|
|
9
63
|
*/
|
|
@@ -43,6 +97,8 @@ interface WidgetStyling {
|
|
|
43
97
|
backgroundColor: string;
|
|
44
98
|
textColor: string;
|
|
45
99
|
buttonTextColor: string;
|
|
100
|
+
borderColor: string;
|
|
101
|
+
borderWidth: string;
|
|
46
102
|
borderRadius: string;
|
|
47
103
|
customCSS?: string | undefined;
|
|
48
104
|
}
|
|
@@ -91,6 +147,14 @@ interface FeedValueOptions {
|
|
|
91
147
|
* @default false
|
|
92
148
|
*/
|
|
93
149
|
headless?: boolean | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Context capture configuration for reaction widgets.
|
|
152
|
+
* When enabled, captures DOM context (section ID, nearest heading, data attributes)
|
|
153
|
+
* from the trigger element when react() is called.
|
|
154
|
+
*
|
|
155
|
+
* @default { enabled: true, maxDepth: 5, maxHeadingLength: 100, dataAttributeWhitelist: ['data-section', 'data-feature', 'data-component', 'data-fv-section', 'data-fv-feature'] }
|
|
156
|
+
*/
|
|
157
|
+
contextCapture?: Partial<ContextCaptureConfig> | undefined;
|
|
94
158
|
}
|
|
95
159
|
/**
|
|
96
160
|
* Runtime configuration (can be changed after init)
|
|
@@ -425,10 +489,11 @@ interface FeedValueInstance {
|
|
|
425
489
|
/**
|
|
426
490
|
* Submit a reaction.
|
|
427
491
|
* @param value - Selected reaction option value
|
|
428
|
-
* @param options - Optional follow-up text
|
|
492
|
+
* @param options - Optional follow-up text and trigger element for context capture
|
|
429
493
|
*/
|
|
430
494
|
react(value: string, options?: {
|
|
431
495
|
followUp?: string;
|
|
496
|
+
triggerElement?: Element | null;
|
|
432
497
|
}): Promise<void>;
|
|
433
498
|
/**
|
|
434
499
|
* Check if widget is a reaction type
|
|
@@ -450,6 +515,8 @@ interface FeedValueInstance {
|
|
|
450
515
|
setConfig(config: Partial<FeedValueConfig>): void;
|
|
451
516
|
/** Get current configuration */
|
|
452
517
|
getConfig(): FeedValueConfig;
|
|
518
|
+
/** Get widget configuration (from API) */
|
|
519
|
+
getWidgetConfig(): WidgetConfig | null;
|
|
453
520
|
/** Subscribe to state changes */
|
|
454
521
|
subscribe(callback: () => void): () => void;
|
|
455
522
|
/** Get current state snapshot */
|
|
@@ -493,6 +560,7 @@ declare class FeedValue implements FeedValueInstance {
|
|
|
493
560
|
private readonly apiClient;
|
|
494
561
|
private readonly emitter;
|
|
495
562
|
private readonly headless;
|
|
563
|
+
private readonly contextCaptureConfig;
|
|
496
564
|
private config;
|
|
497
565
|
private widgetConfig;
|
|
498
566
|
private state;
|
|
@@ -567,10 +635,11 @@ declare class FeedValue implements FeedValueInstance {
|
|
|
567
635
|
/**
|
|
568
636
|
* Submit a reaction.
|
|
569
637
|
* @param value - Selected reaction option value
|
|
570
|
-
* @param options - Optional follow-up text
|
|
638
|
+
* @param options - Optional follow-up text and trigger element for context capture
|
|
571
639
|
*/
|
|
572
640
|
react(value: string, options?: {
|
|
573
641
|
followUp?: string;
|
|
642
|
+
triggerElement?: Element | null;
|
|
574
643
|
}): Promise<void>;
|
|
575
644
|
/**
|
|
576
645
|
* Check if widget is a reaction type
|
|
@@ -848,4 +917,4 @@ declare function generateFingerprint(): string;
|
|
|
848
917
|
*/
|
|
849
918
|
declare function clearFingerprint(): void;
|
|
850
919
|
|
|
851
|
-
export { ApiClient, type ButtonSize, type ConfigResponse, type CustomField, type CustomFieldType, DEFAULT_API_BASE_URL, type EmojiSentiment, type EventHandler, FeedValue, type FeedValueConfig, type FeedValueEvents, type FeedValueInstance, type FeedValueOptions, type FeedValueState, type FeedbackData, type FeedbackMetadata, type FeedbackResponse, type FollowUpTrigger, NEGATIVE_OPTIONS_MAP, type ReactionBorderRadius, type ReactionBorderWidth, type ReactionConfig, type ReactionData, type ReactionMetadata, type ReactionOption, type ReactionResponse, type ReactionState, type ReactionStyling, type ReactionTemplate, type SubmissionUserData, type TriggerIconType, TypedEventEmitter, type UserData, type UserTraits, type WidgetConfig, type WidgetPosition, type WidgetStyling, type WidgetTheme, type WidgetType, type WidgetUIConfig, clearFingerprint, generateFingerprint };
|
|
920
|
+
export { ApiClient, type ButtonSize, type CapturedContext, type ConfigResponse, type ContextCaptureConfig, type CustomField, type CustomFieldType, DEFAULT_API_BASE_URL, DEFAULT_CONTEXT_CAPTURE_CONFIG, type EmojiSentiment, type EventHandler, FeedValue, type FeedValueConfig, type FeedValueEvents, type FeedValueInstance, type FeedValueOptions, type FeedValueState, type FeedbackData, type FeedbackMetadata, type FeedbackResponse, type FollowUpTrigger, NEGATIVE_OPTIONS_MAP, type ReactionBorderRadius, type ReactionBorderWidth, type ReactionConfig, type ReactionData, type ReactionMetadata, type ReactionOption, type ReactionResponse, type ReactionState, type ReactionStyling, type ReactionTemplate, type SubmissionUserData, type TriggerIconType, TypedEventEmitter, type UserData, type UserTraits, type WidgetConfig, type WidgetPosition, type WidgetStyling, type WidgetTheme, type WidgetType, type WidgetUIConfig, captureContext, clearFingerprint, generateFingerprint };
|
package/dist/index.js
CHANGED
|
@@ -453,6 +453,123 @@ function clearFingerprint() {
|
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
// src/context-capture.ts
|
|
457
|
+
var DEFAULT_CONTEXT_CAPTURE_CONFIG = {
|
|
458
|
+
enabled: true,
|
|
459
|
+
maxDepth: 5,
|
|
460
|
+
maxHeadingLength: 100,
|
|
461
|
+
dataAttributeWhitelist: [
|
|
462
|
+
"data-section",
|
|
463
|
+
"data-feature",
|
|
464
|
+
"data-component",
|
|
465
|
+
"data-fv-section",
|
|
466
|
+
"data-fv-feature"
|
|
467
|
+
]
|
|
468
|
+
};
|
|
469
|
+
function findNearestWithId(element, maxDepth) {
|
|
470
|
+
let current = element;
|
|
471
|
+
let depth = 0;
|
|
472
|
+
while (current && depth < maxDepth) {
|
|
473
|
+
if (current.id) {
|
|
474
|
+
return current;
|
|
475
|
+
}
|
|
476
|
+
current = current.parentElement;
|
|
477
|
+
depth++;
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
function findNearestHeading(element, section, maxDepth) {
|
|
482
|
+
if (section) {
|
|
483
|
+
const heading = section.querySelector("h1, h2, h3, h4, h5, h6");
|
|
484
|
+
if (heading) return heading;
|
|
485
|
+
}
|
|
486
|
+
let current = element;
|
|
487
|
+
let depth = 0;
|
|
488
|
+
while (current && depth < maxDepth) {
|
|
489
|
+
let sibling = current.previousElementSibling;
|
|
490
|
+
while (sibling) {
|
|
491
|
+
if (/^H[1-6]$/.test(sibling.tagName)) {
|
|
492
|
+
return sibling;
|
|
493
|
+
}
|
|
494
|
+
sibling = sibling.previousElementSibling;
|
|
495
|
+
}
|
|
496
|
+
current = current.parentElement;
|
|
497
|
+
depth++;
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
function captureDataAttributes(element, whitelist, maxDepth) {
|
|
502
|
+
const result = {};
|
|
503
|
+
let current = element;
|
|
504
|
+
let depth = 0;
|
|
505
|
+
while (current && depth < maxDepth) {
|
|
506
|
+
for (const attr of Array.from(current.attributes)) {
|
|
507
|
+
if (whitelist.includes(attr.name) && !result[attr.name]) {
|
|
508
|
+
result[attr.name] = attr.value;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
current = current.parentElement;
|
|
512
|
+
depth++;
|
|
513
|
+
}
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
function generateSelector(element, maxDepth) {
|
|
517
|
+
const parts = [];
|
|
518
|
+
let current = element;
|
|
519
|
+
let depth = 0;
|
|
520
|
+
while (current && depth < maxDepth && current !== document.body) {
|
|
521
|
+
let selector = current.tagName.toLowerCase();
|
|
522
|
+
if (current.id) {
|
|
523
|
+
selector = `#${current.id}`;
|
|
524
|
+
parts.unshift(selector);
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
if (current.className && typeof current.className === "string") {
|
|
528
|
+
const classes = current.className.split(" ").filter((c) => c.trim()).slice(0, 2);
|
|
529
|
+
if (classes.length) {
|
|
530
|
+
selector += "." + classes.join(".");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
parts.unshift(selector);
|
|
534
|
+
current = current.parentElement;
|
|
535
|
+
depth++;
|
|
536
|
+
}
|
|
537
|
+
return parts.join(" > ");
|
|
538
|
+
}
|
|
539
|
+
function truncate(str, maxLength) {
|
|
540
|
+
if (str.length <= maxLength) return str;
|
|
541
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
542
|
+
}
|
|
543
|
+
function captureContext(triggerElement, config = DEFAULT_CONTEXT_CAPTURE_CONFIG) {
|
|
544
|
+
if (!config.enabled || !triggerElement || typeof document === "undefined") {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
const context = {};
|
|
548
|
+
const sectionWithId = findNearestWithId(triggerElement, config.maxDepth);
|
|
549
|
+
if (sectionWithId) {
|
|
550
|
+
context.sectionId = sectionWithId.id;
|
|
551
|
+
context.sectionTag = sectionWithId.tagName.toLowerCase();
|
|
552
|
+
}
|
|
553
|
+
const heading = findNearestHeading(triggerElement, sectionWithId, config.maxDepth);
|
|
554
|
+
if (heading) {
|
|
555
|
+
context.nearestHeading = truncate(heading.textContent?.trim() || "", config.maxHeadingLength);
|
|
556
|
+
const level = heading.tagName[1];
|
|
557
|
+
if (level) {
|
|
558
|
+
context.headingLevel = parseInt(level, 10);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const dataAttrs = captureDataAttributes(
|
|
562
|
+
triggerElement,
|
|
563
|
+
config.dataAttributeWhitelist,
|
|
564
|
+
config.maxDepth
|
|
565
|
+
);
|
|
566
|
+
if (Object.keys(dataAttrs).length > 0) {
|
|
567
|
+
context.dataAttributes = dataAttrs;
|
|
568
|
+
}
|
|
569
|
+
context.cssSelector = generateSelector(triggerElement, 3);
|
|
570
|
+
return Object.keys(context).length > 0 ? context : null;
|
|
571
|
+
}
|
|
572
|
+
|
|
456
573
|
// src/feedvalue.ts
|
|
457
574
|
var SUCCESS_AUTO_CLOSE_DELAY_MS = 3e3;
|
|
458
575
|
var VALID_SENTIMENTS = ["angry", "disappointed", "satisfied", "excited"];
|
|
@@ -475,6 +592,7 @@ var _FeedValue = class _FeedValue {
|
|
|
475
592
|
__publicField(this, "apiClient");
|
|
476
593
|
__publicField(this, "emitter");
|
|
477
594
|
__publicField(this, "headless");
|
|
595
|
+
__publicField(this, "contextCaptureConfig");
|
|
478
596
|
__publicField(this, "config");
|
|
479
597
|
__publicField(this, "widgetConfig", null);
|
|
480
598
|
// State
|
|
@@ -504,6 +622,10 @@ var _FeedValue = class _FeedValue {
|
|
|
504
622
|
this.widgetId = options.widgetId;
|
|
505
623
|
this.headless = options.headless ?? false;
|
|
506
624
|
this.config = { ...DEFAULT_CONFIG, ...options.config };
|
|
625
|
+
this.contextCaptureConfig = {
|
|
626
|
+
...DEFAULT_CONTEXT_CAPTURE_CONFIG,
|
|
627
|
+
...options.contextCapture
|
|
628
|
+
};
|
|
507
629
|
this.apiClient = new ApiClient(
|
|
508
630
|
options.apiBaseUrl ?? DEFAULT_API_BASE_URL,
|
|
509
631
|
this.config.debug
|
|
@@ -567,12 +689,28 @@ var _FeedValue = class _FeedValue {
|
|
|
567
689
|
thankYouMessage: configResponse.config.thankYouMessage ?? "Thank you for your feedback!",
|
|
568
690
|
showBranding: configResponse.config.showBranding ?? true,
|
|
569
691
|
customFields: configResponse.config.customFields,
|
|
570
|
-
// Reaction config (for reaction widgets) -
|
|
692
|
+
// Reaction config (for reaction widgets) - pass through all fields
|
|
571
693
|
...configResponse.config.template && { template: configResponse.config.template },
|
|
572
694
|
...configResponse.config.options && { options: configResponse.config.options },
|
|
573
695
|
followUpLabel: configResponse.config.followUpLabel ?? "Tell us more (optional)",
|
|
574
|
-
submitText: configResponse.config.submitText ?? "Send"
|
|
696
|
+
submitText: configResponse.config.submitText ?? "Send",
|
|
697
|
+
// Reaction widget display options (support both camelCase and snake_case from API)
|
|
698
|
+
...(configResponse.config.showLabels !== void 0 || configResponse.config.show_labels !== void 0) && {
|
|
699
|
+
showLabels: configResponse.config.showLabels ?? configResponse.config.show_labels
|
|
700
|
+
},
|
|
701
|
+
...(configResponse.config.buttonSize || configResponse.config.button_size) && {
|
|
702
|
+
buttonSize: configResponse.config.buttonSize ?? configResponse.config.button_size
|
|
703
|
+
},
|
|
704
|
+
...(configResponse.config.followUpTrigger || configResponse.config.follow_up_trigger) && {
|
|
705
|
+
followUpTrigger: configResponse.config.followUpTrigger ?? configResponse.config.follow_up_trigger
|
|
706
|
+
}
|
|
575
707
|
};
|
|
708
|
+
this.log("Built baseConfig:", {
|
|
709
|
+
buttonSize: baseConfig.buttonSize,
|
|
710
|
+
showLabels: baseConfig.showLabels,
|
|
711
|
+
followUpTrigger: baseConfig.followUpTrigger,
|
|
712
|
+
template: baseConfig.template
|
|
713
|
+
});
|
|
576
714
|
this.widgetConfig = {
|
|
577
715
|
widgetId: configResponse.widget_id,
|
|
578
716
|
widgetKey: configResponse.widget_key,
|
|
@@ -580,12 +718,16 @@ var _FeedValue = class _FeedValue {
|
|
|
580
718
|
type: configResponse.type ?? "feedback",
|
|
581
719
|
config: baseConfig,
|
|
582
720
|
styling: {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
721
|
+
// Pass through all styling properties from API
|
|
722
|
+
...configResponse.styling,
|
|
723
|
+
// Apply defaults for required fields (support both camelCase and snake_case from API)
|
|
724
|
+
primaryColor: configResponse.styling.primaryColor ?? configResponse.styling.primary_color ?? "#3b82f6",
|
|
725
|
+
backgroundColor: configResponse.styling.backgroundColor ?? configResponse.styling.background_color ?? "#ffffff",
|
|
726
|
+
textColor: configResponse.styling.textColor ?? configResponse.styling.text_color ?? "#1f2937",
|
|
727
|
+
buttonTextColor: configResponse.styling.buttonTextColor ?? configResponse.styling.button_text_color ?? "#ffffff",
|
|
728
|
+
borderColor: configResponse.styling.borderColor ?? configResponse.styling.border_color ?? "#e5e7eb",
|
|
729
|
+
borderWidth: configResponse.styling.borderWidth ?? configResponse.styling.border_width ?? "1",
|
|
730
|
+
borderRadius: configResponse.styling.borderRadius ?? configResponse.styling.border_radius ?? "8px"
|
|
589
731
|
}
|
|
590
732
|
};
|
|
591
733
|
if (!this.headless && typeof window !== "undefined" && typeof document !== "undefined") {
|
|
@@ -819,12 +961,12 @@ var _FeedValue = class _FeedValue {
|
|
|
819
961
|
getTemplateOptions(template) {
|
|
820
962
|
const templates = {
|
|
821
963
|
thumbs: [
|
|
822
|
-
{ label: "Helpful", value: "helpful", icon: "
|
|
823
|
-
{ label: "Not Helpful", value: "not_helpful", icon: "
|
|
964
|
+
{ label: "Helpful", value: "helpful", icon: "\u{1F44D}", showFollowUp: false },
|
|
965
|
+
{ label: "Not Helpful", value: "not_helpful", icon: "\u{1F44E}", showFollowUp: true }
|
|
824
966
|
],
|
|
825
967
|
helpful: [
|
|
826
|
-
{ label: "Yes", value: "yes", icon: "
|
|
827
|
-
{ label: "No", value: "no", icon: "
|
|
968
|
+
{ label: "Yes", value: "yes", icon: "\u2713", showFollowUp: false },
|
|
969
|
+
{ label: "No", value: "no", icon: "\u2717", showFollowUp: true }
|
|
828
970
|
],
|
|
829
971
|
emoji: [
|
|
830
972
|
{ label: "Angry", value: "angry", icon: "\u{1F620}", showFollowUp: true },
|
|
@@ -846,7 +988,7 @@ var _FeedValue = class _FeedValue {
|
|
|
846
988
|
/**
|
|
847
989
|
* Submit a reaction.
|
|
848
990
|
* @param value - Selected reaction option value
|
|
849
|
-
* @param options - Optional follow-up text
|
|
991
|
+
* @param options - Optional follow-up text and trigger element for context capture
|
|
850
992
|
*/
|
|
851
993
|
async react(value, options) {
|
|
852
994
|
if (!this.state.isReady) {
|
|
@@ -864,13 +1006,25 @@ var _FeedValue = class _FeedValue {
|
|
|
864
1006
|
const validValues = reactionOptions.map((opt) => opt.value).join(", ");
|
|
865
1007
|
throw new Error(`Invalid reaction value. Must be one of: ${validValues}`);
|
|
866
1008
|
}
|
|
1009
|
+
let capturedContext = null;
|
|
1010
|
+
if (options?.triggerElement) {
|
|
1011
|
+
capturedContext = captureContext(options.triggerElement, this.contextCaptureConfig);
|
|
1012
|
+
this.log("Captured context", capturedContext);
|
|
1013
|
+
}
|
|
867
1014
|
this.emitter.emit("react", { value, hasFollowUp: selectedOption.showFollowUp });
|
|
868
1015
|
this.updateState({ isSubmitting: true });
|
|
869
1016
|
try {
|
|
870
1017
|
const reactionData = {
|
|
871
1018
|
value,
|
|
872
1019
|
metadata: {
|
|
873
|
-
page_url: typeof window !== "undefined" ? window.location.href : ""
|
|
1020
|
+
page_url: typeof window !== "undefined" ? window.location.href : "",
|
|
1021
|
+
// Spread captured context into metadata
|
|
1022
|
+
...capturedContext?.sectionId && { section_id: capturedContext.sectionId },
|
|
1023
|
+
...capturedContext?.sectionTag && { section_tag: capturedContext.sectionTag },
|
|
1024
|
+
...capturedContext?.nearestHeading && { nearest_heading: capturedContext.nearestHeading },
|
|
1025
|
+
...capturedContext?.headingLevel && { heading_level: capturedContext.headingLevel },
|
|
1026
|
+
...capturedContext?.dataAttributes && { data_attributes: capturedContext.dataAttributes },
|
|
1027
|
+
...capturedContext?.cssSelector && { css_selector: capturedContext.cssSelector }
|
|
874
1028
|
},
|
|
875
1029
|
...options?.followUp && { followUp: options.followUp }
|
|
876
1030
|
};
|
|
@@ -1490,6 +1644,6 @@ var NEGATIVE_OPTIONS_MAP = {
|
|
|
1490
1644
|
rating: ["1", "2"]
|
|
1491
1645
|
};
|
|
1492
1646
|
|
|
1493
|
-
export { ApiClient, DEFAULT_API_BASE_URL, FeedValue, NEGATIVE_OPTIONS_MAP, TypedEventEmitter, clearFingerprint, generateFingerprint };
|
|
1647
|
+
export { ApiClient, DEFAULT_API_BASE_URL, DEFAULT_CONTEXT_CAPTURE_CONFIG, FeedValue, NEGATIVE_OPTIONS_MAP, TypedEventEmitter, captureContext, clearFingerprint, generateFingerprint };
|
|
1494
1648
|
//# sourceMappingURL=index.js.map
|
|
1495
1649
|
//# sourceMappingURL=index.js.map
|