@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @gravity-ai/react
2
2
 
3
- React components for rendering Gravity AI advertisements with automatic impression and click tracking.
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
- > **Note:** This package has a peer dependency on React 17+.
11
+ > Peer dependency: React 17+
12
12
 
13
- ## Documentation
13
+ ## Quick Start
14
14
 
15
- For full documentation, examples, and API reference, visit:
15
+ ```tsx
16
+ import { GravityAd } from '@gravity-ai/react';
16
17
 
17
- **[https://www.trygravity.ai/api](https://www.trygravity.ai/api)**
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
- * Visual theme presets for the ad banner
28
- */
29
- type AdTheme = 'light' | 'dark' | 'minimal' | 'branded';
30
- /**
31
- * Banner size presets
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 AdBannerProps {
38
- /** The ad response from Gravity API */
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
- /** Visual theme preset */
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
- /** Custom styles for the ad text */
49
- textStyle?: CSSProperties;
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
- * AdBanner - A customizable component for rendering Gravity AI advertisements
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
- * AdText - A minimal text-only component for rendering Gravity AI advertisements
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
- * Hook to handle ad impression and click tracking
166
- */
167
- declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): {
152
+ interface UseAdTrackingReturn {
153
+ containerRef: React.RefObject<HTMLElement | null>;
168
154
  handleClick: () => void;
169
- impressionTracked: boolean;
170
- };
155
+ impressionFired: boolean;
156
+ }
157
+ declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): UseAdTrackingReturn;
171
158
 
172
- export { AdBanner, type AdBannerProps, type AdResponse, type AdSize, AdText, type AdTextProps, type AdTheme, useAdTracking };
159
+ export { type AdResponse, AdText, type AdTextProps, GravityAd, type GravityAdProps, type GravityAdSlotProps, type GravityAdVariant, useAdTracking };