@gravity-ai/react 1.1.4 → 1.1.6
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 +149 -5
- package/dist/index.d.mts +102 -115
- package/dist/index.d.ts +102 -115
- package/dist/index.js +1217 -156
- package/dist/index.mjs +1216 -155
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @gravity-ai/react
|
|
2
2
|
|
|
3
|
-
React components for rendering Gravity
|
|
3
|
+
React components for rendering Gravity ads with automatic impression and click tracking.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,13 +8,157 @@ React components for rendering Gravity AI advertisements with automatic impressi
|
|
|
8
8
|
npm install @gravity-ai/react
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
>
|
|
11
|
+
> Peer dependency: React 17+
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
```tsx
|
|
16
|
+
import { GravityAd } from '@gravity-ai/react';
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
function ChatResponse({ ads }) {
|
|
19
|
+
return ads.map((ad, i) => <GravityAd key={i} ad={ad} />);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
That's it. The component renders a styled ad card using inline styles and automatically fires the impression pixel when the ad scrolls into view.
|
|
24
|
+
|
|
25
|
+
## API Response Shape
|
|
26
|
+
|
|
27
|
+
The components expect the objects returned by the Gravity API:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
interface AdResponse {
|
|
31
|
+
adText: string;
|
|
32
|
+
title?: string;
|
|
33
|
+
cta?: string;
|
|
34
|
+
brandName?: string;
|
|
35
|
+
url?: string;
|
|
36
|
+
favicon?: string;
|
|
37
|
+
impUrl?: string; // impression tracking pixel
|
|
38
|
+
clickUrl?: string; // click-through URL
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Components
|
|
43
|
+
|
|
44
|
+
### `<GravityAd />`
|
|
45
|
+
|
|
46
|
+
Full-featured ad card with favicon, brand name, title, body text, and CTA button. All styles are inline — no injected stylesheets, no CSS class names, no CSS-in-JS. What you see in the JSX is what renders.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<GravityAd
|
|
50
|
+
ad={ad}
|
|
51
|
+
variant="card" // "card" | "inline" | "minimal"
|
|
52
|
+
showLabel={true} // show "Sponsored" label
|
|
53
|
+
labelText="Sponsored"
|
|
54
|
+
openInNewTab={true}
|
|
55
|
+
onImpression={() => console.log('seen')}
|
|
56
|
+
onClickTracked={() => console.log('clicked')}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Variants:**
|
|
61
|
+
|
|
62
|
+
| Variant | Description |
|
|
63
|
+
|---------|-------------|
|
|
64
|
+
| `card` | Default. Vertical card with border, shadow, and padding. |
|
|
65
|
+
| `inline` | Horizontal layout for sidebars or toolbars. |
|
|
66
|
+
| `minimal` | No border, shadow, or background. Blends with host content. |
|
|
67
|
+
|
|
68
|
+
### Styling with `style` and `slotProps`
|
|
69
|
+
|
|
70
|
+
Override the outer container with the `style` prop:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<GravityAd ad={ad} style={{ maxWidth: 400, borderRadius: 16 }} />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Override any inner element with `slotProps`:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<GravityAd
|
|
80
|
+
ad={ad}
|
|
81
|
+
slotProps={{
|
|
82
|
+
cta: { style: { background: '#E11D48', borderRadius: 999 } },
|
|
83
|
+
label: { style: { display: 'none' } },
|
|
84
|
+
title: { style: { fontSize: 16 } },
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Each `slotProps` key maps to a DOM element:
|
|
90
|
+
|
|
91
|
+
| Key | Element | What it controls |
|
|
92
|
+
|-----|---------|------------------|
|
|
93
|
+
| `container` | Outer `<a>` | Same as top-level `style` prop |
|
|
94
|
+
| `inner` | Padding wrapper `<div>` | Layout, padding, gap |
|
|
95
|
+
| `header` | Header row `<div>` | Favicon + brand + label row |
|
|
96
|
+
| `favicon` | Favicon `<img>` | Size, border-radius |
|
|
97
|
+
| `brand` | Brand name `<span>` | Font, color |
|
|
98
|
+
| `label` | "Sponsored" `<span>` | Pill styling, visibility |
|
|
99
|
+
| `body` | Body wrapper `<div>` | Title + text layout |
|
|
100
|
+
| `title` | Title `<p>` | Font, color |
|
|
101
|
+
| `text` | Ad text `<p>` | Font, color |
|
|
102
|
+
| `cta` | CTA button `<span>` | Background, border-radius, padding |
|
|
103
|
+
|
|
104
|
+
Each slot accepts `{ style?: CSSProperties; className?: string }`.
|
|
105
|
+
|
|
106
|
+
For the full styling guide with DOM structure and common recipes, see **[STYLING.md](./STYLING.md)**.
|
|
107
|
+
|
|
108
|
+
### `<AdText />`
|
|
109
|
+
|
|
110
|
+
Unstyled text-only renderer. Use when you want full control over presentation.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<AdText
|
|
114
|
+
ad={ad}
|
|
115
|
+
className="my-custom-style"
|
|
116
|
+
style={{ fontSize: 14 }}
|
|
117
|
+
/>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Renders `ad.adText` as a link (if `clickUrl` exists) or a span. No built-in styles beyond `text-decoration: none; color: inherit`.
|
|
121
|
+
|
|
122
|
+
## Impression Tracking
|
|
123
|
+
|
|
124
|
+
Both components use `IntersectionObserver` to fire the impression pixel **only when the ad is actually visible** to the user (50% of the element in the viewport). This is a significant improvement over fire-on-mount — publishers won't report impressions for ads rendered below the fold that were never seen.
|
|
125
|
+
|
|
126
|
+
The impression fires exactly once per ad. If the ad object changes (different `impUrl`), the tracking resets for the new ad.
|
|
127
|
+
|
|
128
|
+
To disable automatic tracking:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<GravityAd ad={ad} disableImpressionTracking />
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Hooks
|
|
135
|
+
|
|
136
|
+
### `useAdTracking`
|
|
137
|
+
|
|
138
|
+
Low-level hook for building fully custom ad components:
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { useAdTracking } from '@gravity-ai/react';
|
|
142
|
+
|
|
143
|
+
function CustomAd({ ad }) {
|
|
144
|
+
const { containerRef, handleClick } = useAdTracking({
|
|
145
|
+
ad,
|
|
146
|
+
onImpression: () => console.log('impression fired'),
|
|
147
|
+
onClickTracked: () => console.log('click tracked'),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<a ref={containerRef} href={ad.clickUrl} onClick={handleClick}>
|
|
152
|
+
{ad.adText}
|
|
153
|
+
</a>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The hook returns:
|
|
159
|
+
- `containerRef` — attach to the DOM element for IntersectionObserver-based impression tracking
|
|
160
|
+
- `handleClick` — call on click to fire the click tracking callback
|
|
161
|
+
- `impressionFired` — boolean indicating whether the impression has already been fired
|
|
18
162
|
|
|
19
163
|
## License
|
|
20
164
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,154 +1,142 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { CSSProperties, ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Ad response from the Gravity API
|
|
6
|
-
* This mirrors the type from @gravity-ai/api for convenience
|
|
7
|
-
*/
|
|
8
4
|
interface AdResponse {
|
|
9
|
-
/** The advertisement copy text */
|
|
10
5
|
adText: string;
|
|
11
|
-
/** Ad title */
|
|
12
6
|
title?: string;
|
|
13
|
-
/** Call-to-action text (e.g., 'Learn More', 'Shop Now') */
|
|
14
7
|
cta?: string;
|
|
15
|
-
/** Brand/advertiser name */
|
|
16
8
|
brandName?: string;
|
|
17
|
-
/** Landing page URL */
|
|
18
9
|
url?: string;
|
|
19
|
-
/** Favicon URL */
|
|
20
10
|
favicon?: string;
|
|
21
|
-
/** Impression tracking URL - fire this when ad is displayed */
|
|
22
11
|
impUrl?: string;
|
|
23
|
-
/** Click-through tracking URL - use this as href for ad clicks */
|
|
24
12
|
clickUrl?: string;
|
|
25
13
|
}
|
|
14
|
+
type GravityAdVariant = 'card' | 'inline' | 'minimal' | 'bubble' | 'contextual' | 'native' | 'footnote' | 'quote' | 'suggestion' | 'accent' | 'side-panel' | 'labeled' | 'spotlight' | 'embed' | 'split-action' | 'pill' | 'banner' | 'divider' | 'toolbar' | 'tooltip' | 'notification' | 'hyperlink' | 'text-link';
|
|
26
15
|
/**
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
type AdSize = 'small' | 'medium' | 'large' | 'responsive';
|
|
34
|
-
/**
|
|
35
|
-
* Props for the AdBanner component
|
|
16
|
+
* Style + className overrides for individual elements inside `<GravityAd />`.
|
|
17
|
+
*
|
|
18
|
+
* Each key maps 1:1 to a DOM element in the component's JSX tree.
|
|
19
|
+
* Pass `style` to merge onto the element's inline styles, or `className`
|
|
20
|
+
* to append a CSS class.
|
|
36
21
|
*/
|
|
37
|
-
interface
|
|
38
|
-
/**
|
|
22
|
+
interface GravityAdSlotProps {
|
|
23
|
+
/** Outer `<a>` wrapper (same as the top-level `style`/`className` props) */
|
|
24
|
+
container?: {
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
className?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Inner padding/layout wrapper */
|
|
29
|
+
inner?: {
|
|
30
|
+
style?: CSSProperties;
|
|
31
|
+
className?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Header row (favicon + brand + label) */
|
|
34
|
+
header?: {
|
|
35
|
+
style?: CSSProperties;
|
|
36
|
+
className?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Favicon `<img>` */
|
|
39
|
+
favicon?: {
|
|
40
|
+
style?: CSSProperties;
|
|
41
|
+
className?: string;
|
|
42
|
+
};
|
|
43
|
+
/** Brand name `<span>` */
|
|
44
|
+
brand?: {
|
|
45
|
+
style?: CSSProperties;
|
|
46
|
+
className?: string;
|
|
47
|
+
};
|
|
48
|
+
/** "Sponsored" label `<span>` */
|
|
49
|
+
label?: {
|
|
50
|
+
style?: CSSProperties;
|
|
51
|
+
className?: string;
|
|
52
|
+
};
|
|
53
|
+
/** Body wrapper (title + text) */
|
|
54
|
+
body?: {
|
|
55
|
+
style?: CSSProperties;
|
|
56
|
+
className?: string;
|
|
57
|
+
};
|
|
58
|
+
/** Title `<p>` */
|
|
59
|
+
title?: {
|
|
60
|
+
style?: CSSProperties;
|
|
61
|
+
className?: string;
|
|
62
|
+
};
|
|
63
|
+
/** Ad text `<p>` */
|
|
64
|
+
text?: {
|
|
65
|
+
style?: CSSProperties;
|
|
66
|
+
className?: string;
|
|
67
|
+
};
|
|
68
|
+
/** CTA button `<span>` */
|
|
69
|
+
cta?: {
|
|
70
|
+
style?: CSSProperties;
|
|
71
|
+
className?: string;
|
|
72
|
+
};
|
|
73
|
+
/** Icon wrapper (larger background container around favicon) */
|
|
74
|
+
iconWrapper?: {
|
|
75
|
+
style?: CSSProperties;
|
|
76
|
+
className?: string;
|
|
77
|
+
};
|
|
78
|
+
/** Colored accent bar (top of `accent` variant) */
|
|
79
|
+
accentBar?: {
|
|
80
|
+
style?: CSSProperties;
|
|
81
|
+
className?: string;
|
|
82
|
+
};
|
|
83
|
+
/** Secondary CTA (`split-action` variant) */
|
|
84
|
+
secondaryCta?: {
|
|
85
|
+
style?: CSSProperties;
|
|
86
|
+
className?: string;
|
|
87
|
+
};
|
|
88
|
+
/** Footer area */
|
|
89
|
+
footer?: {
|
|
90
|
+
style?: CSSProperties;
|
|
91
|
+
className?: string;
|
|
92
|
+
};
|
|
93
|
+
/** Tooltip arrow element */
|
|
94
|
+
arrow?: {
|
|
95
|
+
style?: CSSProperties;
|
|
96
|
+
className?: string;
|
|
97
|
+
};
|
|
98
|
+
/** Context header text (`contextual` variant) */
|
|
99
|
+
contextHeader?: {
|
|
100
|
+
style?: CSSProperties;
|
|
101
|
+
className?: string;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
interface GravityAdProps {
|
|
39
105
|
ad: AdResponse | null;
|
|
40
|
-
|
|
41
|
-
theme?: AdTheme;
|
|
42
|
-
/** Size preset */
|
|
43
|
-
size?: AdSize;
|
|
44
|
-
/** Custom class name for the container */
|
|
106
|
+
variant?: GravityAdVariant;
|
|
45
107
|
className?: string;
|
|
46
|
-
/** Custom inline styles for the container */
|
|
47
108
|
style?: CSSProperties;
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
/** Custom class name for the ad text */
|
|
51
|
-
textClassName?: string;
|
|
52
|
-
/** Whether to show the "Sponsored" label */
|
|
109
|
+
/** Targeted style overrides for inner elements. */
|
|
110
|
+
slotProps?: GravityAdSlotProps;
|
|
53
111
|
showLabel?: boolean;
|
|
54
|
-
/** Custom label text (default: "Sponsored") */
|
|
55
112
|
labelText?: string;
|
|
56
|
-
/** Custom styles for the label */
|
|
57
|
-
labelStyle?: CSSProperties;
|
|
58
|
-
/** Custom click handler (called in addition to tracking) */
|
|
59
113
|
onClick?: () => void;
|
|
60
|
-
/** Callback when impression is tracked */
|
|
61
114
|
onImpression?: () => void;
|
|
62
|
-
/** Callback when click is tracked */
|
|
63
115
|
onClickTracked?: () => void;
|
|
64
|
-
/** Custom content to render when ad is null */
|
|
65
116
|
fallback?: ReactNode;
|
|
66
|
-
/** Whether to disable automatic impression tracking */
|
|
67
117
|
disableImpressionTracking?: boolean;
|
|
68
|
-
/** Whether to open link in new tab (default: true) */
|
|
69
118
|
openInNewTab?: boolean;
|
|
70
|
-
/** Custom border radius */
|
|
71
|
-
borderRadius?: number | string;
|
|
72
|
-
/** Custom background color (overrides theme) */
|
|
73
|
-
backgroundColor?: string;
|
|
74
|
-
/** Custom text color (overrides theme) */
|
|
75
|
-
textColor?: string;
|
|
76
|
-
/** Custom accent/brand color */
|
|
77
|
-
accentColor?: string;
|
|
78
119
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Props for the AdText component (minimal text-only rendering)
|
|
81
|
-
*/
|
|
82
120
|
interface AdTextProps {
|
|
83
|
-
/** The ad response from Gravity API */
|
|
84
121
|
ad: AdResponse | null;
|
|
85
|
-
/** Custom class name */
|
|
86
122
|
className?: string;
|
|
87
|
-
/** Custom inline styles */
|
|
88
123
|
style?: CSSProperties;
|
|
89
|
-
/** Custom click handler */
|
|
90
124
|
onClick?: () => void;
|
|
91
|
-
/** Callback when impression is tracked */
|
|
92
125
|
onImpression?: () => void;
|
|
93
|
-
/** Callback when click is tracked */
|
|
94
126
|
onClickTracked?: () => void;
|
|
95
|
-
/** Content to render when ad is null */
|
|
96
127
|
fallback?: ReactNode;
|
|
97
|
-
/** Whether to disable automatic impression tracking */
|
|
98
128
|
disableImpressionTracking?: boolean;
|
|
99
|
-
/** Whether to open link in new tab (default: true) */
|
|
100
129
|
openInNewTab?: boolean;
|
|
101
130
|
}
|
|
102
131
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```tsx
|
|
108
|
-
* import { AdBanner } from '@gravity-ai/react';
|
|
109
|
-
*
|
|
110
|
-
* function MyComponent() {
|
|
111
|
-
* const [ad, setAd] = useState(null);
|
|
112
|
-
*
|
|
113
|
-
* useEffect(() => {
|
|
114
|
-
* client.getAd({ messages, sessionId, placements }).then(res => setAd(res?.[0] || null));
|
|
115
|
-
* }, [messages]);
|
|
116
|
-
*
|
|
117
|
-
* return (
|
|
118
|
-
* <AdBanner
|
|
119
|
-
* ad={ad}
|
|
120
|
-
* theme="dark"
|
|
121
|
-
* size="medium"
|
|
122
|
-
* showLabel
|
|
123
|
-
* />
|
|
124
|
-
* );
|
|
125
|
-
* }
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
declare function AdBanner({ ad, theme, size, className, style, textStyle, textClassName, showLabel, labelText, labelStyle, onClick, onImpression, onClickTracked, fallback, disableImpressionTracking, openInNewTab, borderRadius, backgroundColor, textColor, accentColor, }: AdBannerProps): react_jsx_runtime.JSX.Element;
|
|
129
|
-
declare namespace AdBanner {
|
|
132
|
+
declare function GravityAd({ ad, variant, className, style, slotProps, showLabel, labelText, onClick, onImpression, onClickTracked, fallback, disableImpressionTracking, openInNewTab, }: GravityAdProps): react_jsx_runtime.JSX.Element;
|
|
133
|
+
declare namespace GravityAd {
|
|
130
134
|
var displayName: string;
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* Use this when you want full control over styling and just need the ad text
|
|
137
|
-
* with automatic tracking.
|
|
138
|
-
*
|
|
139
|
-
* @example
|
|
140
|
-
* ```tsx
|
|
141
|
-
* import { AdText } from '@gravity-ai/react';
|
|
142
|
-
*
|
|
143
|
-
* function MyComponent() {
|
|
144
|
-
* return (
|
|
145
|
-
* <AdText
|
|
146
|
-
* ad={ad}
|
|
147
|
-
* className="my-custom-ad-style"
|
|
148
|
-
* />
|
|
149
|
-
* );
|
|
150
|
-
* }
|
|
151
|
-
* ```
|
|
138
|
+
* Unstyled text-only ad renderer with automatic tracking.
|
|
139
|
+
* Use when you want full control over presentation.
|
|
152
140
|
*/
|
|
153
141
|
declare function AdText({ ad, className, style, onClick, onImpression, onClickTracked, fallback, disableImpressionTracking, openInNewTab, }: AdTextProps): react_jsx_runtime.JSX.Element;
|
|
154
142
|
declare namespace AdText {
|
|
@@ -161,12 +149,11 @@ interface UseAdTrackingOptions {
|
|
|
161
149
|
onImpression?: () => void;
|
|
162
150
|
onClickTracked?: () => void;
|
|
163
151
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
*/
|
|
167
|
-
declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): {
|
|
152
|
+
interface UseAdTrackingReturn {
|
|
153
|
+
containerRef: React.RefObject<HTMLElement | null>;
|
|
168
154
|
handleClick: () => void;
|
|
169
|
-
|
|
170
|
-
}
|
|
155
|
+
impressionFired: boolean;
|
|
156
|
+
}
|
|
157
|
+
declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): UseAdTrackingReturn;
|
|
171
158
|
|
|
172
|
-
export {
|
|
159
|
+
export { type AdResponse, AdText, type AdTextProps, GravityAd, type GravityAdProps, type GravityAdSlotProps, type GravityAdVariant, useAdTracking };
|