@gravity-ai/react 0.1.0
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 +166 -0
- package/dist/index.d.mts +160 -0
- package/dist/index.d.ts +160 -0
- package/dist/index.js +303 -0
- package/dist/index.mjs +274 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @gravity-ai/react
|
|
2
|
+
|
|
3
|
+
React components for rendering Gravity AI advertisements with automatic impression and click tracking.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @gravity-ai/react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> **Note:** This package has a peer dependency on React 17+.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Client } from '@gravity-ai/api';
|
|
17
|
+
import { AdBanner } from '@gravity-ai/react';
|
|
18
|
+
import { useEffect, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
const client = new Client('your-api-key');
|
|
21
|
+
|
|
22
|
+
function ChatMessage({ messages }) {
|
|
23
|
+
const [ad, setAd] = useState(null);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
client.getAd({ messages }).then(setAd);
|
|
27
|
+
}, [messages]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
{/* Your chat content */}
|
|
32
|
+
<AdBanner ad={ad} theme="dark" size="medium" />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Components
|
|
39
|
+
|
|
40
|
+
### `<AdBanner />`
|
|
41
|
+
|
|
42
|
+
A fully-styled, customizable ad banner component.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { AdBanner } from '@gravity-ai/react';
|
|
46
|
+
|
|
47
|
+
<AdBanner
|
|
48
|
+
ad={adResponse}
|
|
49
|
+
theme="dark" // 'light' | 'dark' | 'minimal' | 'branded'
|
|
50
|
+
size="medium" // 'small' | 'medium' | 'large' | 'responsive'
|
|
51
|
+
showLabel={true} // Show "Sponsored" label
|
|
52
|
+
labelText="Ad" // Custom label text
|
|
53
|
+
openInNewTab={true} // Open click URL in new tab
|
|
54
|
+
onImpression={() => console.log('Impression tracked')}
|
|
55
|
+
onClickTracked={() => console.log('Click tracked')}
|
|
56
|
+
/>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Props
|
|
60
|
+
|
|
61
|
+
| Prop | Type | Default | Description |
|
|
62
|
+
|------|------|---------|-------------|
|
|
63
|
+
| `ad` | `AdResponse \| null` | required | The ad response from Gravity API |
|
|
64
|
+
| `theme` | `'light' \| 'dark' \| 'minimal' \| 'branded'` | `'light'` | Visual theme preset |
|
|
65
|
+
| `size` | `'small' \| 'medium' \| 'large' \| 'responsive'` | `'medium'` | Size preset |
|
|
66
|
+
| `className` | `string` | - | Custom class name for container |
|
|
67
|
+
| `style` | `CSSProperties` | - | Custom inline styles |
|
|
68
|
+
| `showLabel` | `boolean` | `true` | Show "Sponsored" label |
|
|
69
|
+
| `labelText` | `string` | `'Sponsored'` | Custom label text |
|
|
70
|
+
| `fallback` | `ReactNode` | `null` | Content when ad is null |
|
|
71
|
+
| `openInNewTab` | `boolean` | `true` | Open link in new tab |
|
|
72
|
+
| `backgroundColor` | `string` | - | Custom background (overrides theme) |
|
|
73
|
+
| `textColor` | `string` | - | Custom text color (overrides theme) |
|
|
74
|
+
| `accentColor` | `string` | - | Custom accent color |
|
|
75
|
+
| `borderRadius` | `number \| string` | - | Custom border radius |
|
|
76
|
+
| `disableImpressionTracking` | `boolean` | `false` | Disable auto impression tracking |
|
|
77
|
+
| `onClick` | `() => void` | - | Custom click handler |
|
|
78
|
+
| `onImpression` | `() => void` | - | Callback when impression fires |
|
|
79
|
+
| `onClickTracked` | `() => void` | - | Callback when click is tracked |
|
|
80
|
+
|
|
81
|
+
### `<AdText />`
|
|
82
|
+
|
|
83
|
+
A minimal text-only component for full styling control.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { AdText } from '@gravity-ai/react';
|
|
87
|
+
|
|
88
|
+
<AdText
|
|
89
|
+
ad={adResponse}
|
|
90
|
+
className="my-custom-class"
|
|
91
|
+
style={{ color: 'blue' }}
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Props
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Description |
|
|
98
|
+
|------|------|---------|-------------|
|
|
99
|
+
| `ad` | `AdResponse \| null` | required | The ad response from Gravity API |
|
|
100
|
+
| `className` | `string` | - | Custom class name |
|
|
101
|
+
| `style` | `CSSProperties` | - | Custom inline styles |
|
|
102
|
+
| `fallback` | `ReactNode` | `null` | Content when ad is null |
|
|
103
|
+
| `openInNewTab` | `boolean` | `true` | Open link in new tab |
|
|
104
|
+
| `disableImpressionTracking` | `boolean` | `false` | Disable auto tracking |
|
|
105
|
+
| `onClick` | `() => void` | - | Custom click handler |
|
|
106
|
+
| `onImpression` | `() => void` | - | Callback on impression |
|
|
107
|
+
| `onClickTracked` | `() => void` | - | Callback on click |
|
|
108
|
+
|
|
109
|
+
## Hooks
|
|
110
|
+
|
|
111
|
+
### `useAdTracking`
|
|
112
|
+
|
|
113
|
+
For building custom ad components with automatic tracking.
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { useAdTracking } from '@gravity-ai/react';
|
|
117
|
+
|
|
118
|
+
function CustomAdComponent({ ad }) {
|
|
119
|
+
const { handleClick } = useAdTracking({
|
|
120
|
+
ad,
|
|
121
|
+
onImpression: () => console.log('Viewed'),
|
|
122
|
+
onClickTracked: () => console.log('Clicked'),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<a href={ad.clickUrl} onClick={handleClick}>
|
|
127
|
+
{ad.adText}
|
|
128
|
+
</a>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Theming Examples
|
|
134
|
+
|
|
135
|
+
### Dark Theme
|
|
136
|
+
```tsx
|
|
137
|
+
<AdBanner ad={ad} theme="dark" />
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Custom Colors
|
|
141
|
+
```tsx
|
|
142
|
+
<AdBanner
|
|
143
|
+
ad={ad}
|
|
144
|
+
backgroundColor="#1e1b4b"
|
|
145
|
+
textColor="#e0e7ff"
|
|
146
|
+
accentColor="#818cf8"
|
|
147
|
+
/>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Minimal (Inherit Parent Styles)
|
|
151
|
+
```tsx
|
|
152
|
+
<AdBanner ad={ad} theme="minimal" showLabel={false} />
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## TypeScript
|
|
156
|
+
|
|
157
|
+
Full TypeScript support with exported types:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import type { AdResponse, AdBannerProps, AdTheme, AdSize } from '@gravity-ai/react';
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
|
166
|
+
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ad response from the Gravity API
|
|
6
|
+
* This mirrors the type from @gravity-ai/api for convenience
|
|
7
|
+
*/
|
|
8
|
+
interface AdResponse {
|
|
9
|
+
adText: string;
|
|
10
|
+
impUrl?: string;
|
|
11
|
+
clickUrl?: string;
|
|
12
|
+
payout?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Visual theme presets for the ad banner
|
|
16
|
+
*/
|
|
17
|
+
type AdTheme = 'light' | 'dark' | 'minimal' | 'branded';
|
|
18
|
+
/**
|
|
19
|
+
* Banner size presets
|
|
20
|
+
*/
|
|
21
|
+
type AdSize = 'small' | 'medium' | 'large' | 'responsive';
|
|
22
|
+
/**
|
|
23
|
+
* Props for the AdBanner component
|
|
24
|
+
*/
|
|
25
|
+
interface AdBannerProps {
|
|
26
|
+
/** The ad response from Gravity API */
|
|
27
|
+
ad: AdResponse | null;
|
|
28
|
+
/** Visual theme preset */
|
|
29
|
+
theme?: AdTheme;
|
|
30
|
+
/** Size preset */
|
|
31
|
+
size?: AdSize;
|
|
32
|
+
/** Custom class name for the container */
|
|
33
|
+
className?: string;
|
|
34
|
+
/** Custom inline styles for the container */
|
|
35
|
+
style?: CSSProperties;
|
|
36
|
+
/** Custom styles for the ad text */
|
|
37
|
+
textStyle?: CSSProperties;
|
|
38
|
+
/** Custom class name for the ad text */
|
|
39
|
+
textClassName?: string;
|
|
40
|
+
/** Whether to show the "Sponsored" label */
|
|
41
|
+
showLabel?: boolean;
|
|
42
|
+
/** Custom label text (default: "Sponsored") */
|
|
43
|
+
labelText?: string;
|
|
44
|
+
/** Custom styles for the label */
|
|
45
|
+
labelStyle?: CSSProperties;
|
|
46
|
+
/** Custom click handler (called in addition to tracking) */
|
|
47
|
+
onClick?: () => void;
|
|
48
|
+
/** Callback when impression is tracked */
|
|
49
|
+
onImpression?: () => void;
|
|
50
|
+
/** Callback when click is tracked */
|
|
51
|
+
onClickTracked?: () => void;
|
|
52
|
+
/** Custom content to render when ad is null */
|
|
53
|
+
fallback?: ReactNode;
|
|
54
|
+
/** Whether to disable automatic impression tracking */
|
|
55
|
+
disableImpressionTracking?: boolean;
|
|
56
|
+
/** Whether to open link in new tab (default: true) */
|
|
57
|
+
openInNewTab?: boolean;
|
|
58
|
+
/** Custom border radius */
|
|
59
|
+
borderRadius?: number | string;
|
|
60
|
+
/** Custom background color (overrides theme) */
|
|
61
|
+
backgroundColor?: string;
|
|
62
|
+
/** Custom text color (overrides theme) */
|
|
63
|
+
textColor?: string;
|
|
64
|
+
/** Custom accent/brand color */
|
|
65
|
+
accentColor?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Props for the AdText component (minimal text-only rendering)
|
|
69
|
+
*/
|
|
70
|
+
interface AdTextProps {
|
|
71
|
+
/** The ad response from Gravity API */
|
|
72
|
+
ad: AdResponse | null;
|
|
73
|
+
/** Custom class name */
|
|
74
|
+
className?: string;
|
|
75
|
+
/** Custom inline styles */
|
|
76
|
+
style?: CSSProperties;
|
|
77
|
+
/** Custom click handler */
|
|
78
|
+
onClick?: () => void;
|
|
79
|
+
/** Callback when impression is tracked */
|
|
80
|
+
onImpression?: () => void;
|
|
81
|
+
/** Callback when click is tracked */
|
|
82
|
+
onClickTracked?: () => void;
|
|
83
|
+
/** Content to render when ad is null */
|
|
84
|
+
fallback?: ReactNode;
|
|
85
|
+
/** Whether to disable automatic impression tracking */
|
|
86
|
+
disableImpressionTracking?: boolean;
|
|
87
|
+
/** Whether to open link in new tab (default: true) */
|
|
88
|
+
openInNewTab?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* AdBanner - A customizable component for rendering Gravity AI advertisements
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```tsx
|
|
96
|
+
* import { AdBanner } from '@gravity-ai/react';
|
|
97
|
+
*
|
|
98
|
+
* function MyComponent() {
|
|
99
|
+
* const [ad, setAd] = useState(null);
|
|
100
|
+
*
|
|
101
|
+
* useEffect(() => {
|
|
102
|
+
* client.getAd({ messages }).then(setAd);
|
|
103
|
+
* }, []);
|
|
104
|
+
*
|
|
105
|
+
* return (
|
|
106
|
+
* <AdBanner
|
|
107
|
+
* ad={ad}
|
|
108
|
+
* theme="dark"
|
|
109
|
+
* size="medium"
|
|
110
|
+
* showLabel
|
|
111
|
+
* />
|
|
112
|
+
* );
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
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;
|
|
117
|
+
declare namespace AdBanner {
|
|
118
|
+
var displayName: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* AdText - A minimal text-only component for rendering Gravity AI advertisements
|
|
123
|
+
*
|
|
124
|
+
* Use this when you want full control over styling and just need the ad text
|
|
125
|
+
* with automatic tracking.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```tsx
|
|
129
|
+
* import { AdText } from '@gravity-ai/react';
|
|
130
|
+
*
|
|
131
|
+
* function MyComponent() {
|
|
132
|
+
* return (
|
|
133
|
+
* <AdText
|
|
134
|
+
* ad={ad}
|
|
135
|
+
* className="my-custom-ad-style"
|
|
136
|
+
* />
|
|
137
|
+
* );
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
declare function AdText({ ad, className, style, onClick, onImpression, onClickTracked, fallback, disableImpressionTracking, openInNewTab, }: AdTextProps): react_jsx_runtime.JSX.Element;
|
|
142
|
+
declare namespace AdText {
|
|
143
|
+
var displayName: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface UseAdTrackingOptions {
|
|
147
|
+
ad: AdResponse | null;
|
|
148
|
+
disableImpressionTracking?: boolean;
|
|
149
|
+
onImpression?: () => void;
|
|
150
|
+
onClickTracked?: () => void;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Hook to handle ad impression and click tracking
|
|
154
|
+
*/
|
|
155
|
+
declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): {
|
|
156
|
+
handleClick: () => void;
|
|
157
|
+
impressionTracked: boolean;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export { AdBanner, type AdBannerProps, type AdResponse, type AdSize, AdText, type AdTextProps, type AdTheme, useAdTracking };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ad response from the Gravity API
|
|
6
|
+
* This mirrors the type from @gravity-ai/api for convenience
|
|
7
|
+
*/
|
|
8
|
+
interface AdResponse {
|
|
9
|
+
adText: string;
|
|
10
|
+
impUrl?: string;
|
|
11
|
+
clickUrl?: string;
|
|
12
|
+
payout?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Visual theme presets for the ad banner
|
|
16
|
+
*/
|
|
17
|
+
type AdTheme = 'light' | 'dark' | 'minimal' | 'branded';
|
|
18
|
+
/**
|
|
19
|
+
* Banner size presets
|
|
20
|
+
*/
|
|
21
|
+
type AdSize = 'small' | 'medium' | 'large' | 'responsive';
|
|
22
|
+
/**
|
|
23
|
+
* Props for the AdBanner component
|
|
24
|
+
*/
|
|
25
|
+
interface AdBannerProps {
|
|
26
|
+
/** The ad response from Gravity API */
|
|
27
|
+
ad: AdResponse | null;
|
|
28
|
+
/** Visual theme preset */
|
|
29
|
+
theme?: AdTheme;
|
|
30
|
+
/** Size preset */
|
|
31
|
+
size?: AdSize;
|
|
32
|
+
/** Custom class name for the container */
|
|
33
|
+
className?: string;
|
|
34
|
+
/** Custom inline styles for the container */
|
|
35
|
+
style?: CSSProperties;
|
|
36
|
+
/** Custom styles for the ad text */
|
|
37
|
+
textStyle?: CSSProperties;
|
|
38
|
+
/** Custom class name for the ad text */
|
|
39
|
+
textClassName?: string;
|
|
40
|
+
/** Whether to show the "Sponsored" label */
|
|
41
|
+
showLabel?: boolean;
|
|
42
|
+
/** Custom label text (default: "Sponsored") */
|
|
43
|
+
labelText?: string;
|
|
44
|
+
/** Custom styles for the label */
|
|
45
|
+
labelStyle?: CSSProperties;
|
|
46
|
+
/** Custom click handler (called in addition to tracking) */
|
|
47
|
+
onClick?: () => void;
|
|
48
|
+
/** Callback when impression is tracked */
|
|
49
|
+
onImpression?: () => void;
|
|
50
|
+
/** Callback when click is tracked */
|
|
51
|
+
onClickTracked?: () => void;
|
|
52
|
+
/** Custom content to render when ad is null */
|
|
53
|
+
fallback?: ReactNode;
|
|
54
|
+
/** Whether to disable automatic impression tracking */
|
|
55
|
+
disableImpressionTracking?: boolean;
|
|
56
|
+
/** Whether to open link in new tab (default: true) */
|
|
57
|
+
openInNewTab?: boolean;
|
|
58
|
+
/** Custom border radius */
|
|
59
|
+
borderRadius?: number | string;
|
|
60
|
+
/** Custom background color (overrides theme) */
|
|
61
|
+
backgroundColor?: string;
|
|
62
|
+
/** Custom text color (overrides theme) */
|
|
63
|
+
textColor?: string;
|
|
64
|
+
/** Custom accent/brand color */
|
|
65
|
+
accentColor?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Props for the AdText component (minimal text-only rendering)
|
|
69
|
+
*/
|
|
70
|
+
interface AdTextProps {
|
|
71
|
+
/** The ad response from Gravity API */
|
|
72
|
+
ad: AdResponse | null;
|
|
73
|
+
/** Custom class name */
|
|
74
|
+
className?: string;
|
|
75
|
+
/** Custom inline styles */
|
|
76
|
+
style?: CSSProperties;
|
|
77
|
+
/** Custom click handler */
|
|
78
|
+
onClick?: () => void;
|
|
79
|
+
/** Callback when impression is tracked */
|
|
80
|
+
onImpression?: () => void;
|
|
81
|
+
/** Callback when click is tracked */
|
|
82
|
+
onClickTracked?: () => void;
|
|
83
|
+
/** Content to render when ad is null */
|
|
84
|
+
fallback?: ReactNode;
|
|
85
|
+
/** Whether to disable automatic impression tracking */
|
|
86
|
+
disableImpressionTracking?: boolean;
|
|
87
|
+
/** Whether to open link in new tab (default: true) */
|
|
88
|
+
openInNewTab?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* AdBanner - A customizable component for rendering Gravity AI advertisements
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```tsx
|
|
96
|
+
* import { AdBanner } from '@gravity-ai/react';
|
|
97
|
+
*
|
|
98
|
+
* function MyComponent() {
|
|
99
|
+
* const [ad, setAd] = useState(null);
|
|
100
|
+
*
|
|
101
|
+
* useEffect(() => {
|
|
102
|
+
* client.getAd({ messages }).then(setAd);
|
|
103
|
+
* }, []);
|
|
104
|
+
*
|
|
105
|
+
* return (
|
|
106
|
+
* <AdBanner
|
|
107
|
+
* ad={ad}
|
|
108
|
+
* theme="dark"
|
|
109
|
+
* size="medium"
|
|
110
|
+
* showLabel
|
|
111
|
+
* />
|
|
112
|
+
* );
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
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;
|
|
117
|
+
declare namespace AdBanner {
|
|
118
|
+
var displayName: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* AdText - A minimal text-only component for rendering Gravity AI advertisements
|
|
123
|
+
*
|
|
124
|
+
* Use this when you want full control over styling and just need the ad text
|
|
125
|
+
* with automatic tracking.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```tsx
|
|
129
|
+
* import { AdText } from '@gravity-ai/react';
|
|
130
|
+
*
|
|
131
|
+
* function MyComponent() {
|
|
132
|
+
* return (
|
|
133
|
+
* <AdText
|
|
134
|
+
* ad={ad}
|
|
135
|
+
* className="my-custom-ad-style"
|
|
136
|
+
* />
|
|
137
|
+
* );
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
declare function AdText({ ad, className, style, onClick, onImpression, onClickTracked, fallback, disableImpressionTracking, openInNewTab, }: AdTextProps): react_jsx_runtime.JSX.Element;
|
|
142
|
+
declare namespace AdText {
|
|
143
|
+
var displayName: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface UseAdTrackingOptions {
|
|
147
|
+
ad: AdResponse | null;
|
|
148
|
+
disableImpressionTracking?: boolean;
|
|
149
|
+
onImpression?: () => void;
|
|
150
|
+
onClickTracked?: () => void;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Hook to handle ad impression and click tracking
|
|
154
|
+
*/
|
|
155
|
+
declare function useAdTracking({ ad, disableImpressionTracking, onImpression, onClickTracked, }: UseAdTrackingOptions): {
|
|
156
|
+
handleClick: () => void;
|
|
157
|
+
impressionTracked: boolean;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export { AdBanner, type AdBannerProps, type AdResponse, type AdSize, AdText, type AdTextProps, type AdTheme, useAdTracking };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AdBanner: () => AdBanner,
|
|
24
|
+
AdText: () => AdText,
|
|
25
|
+
useAdTracking: () => useAdTracking
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/components/AdBanner.tsx
|
|
30
|
+
var import_react2 = require("react");
|
|
31
|
+
|
|
32
|
+
// src/hooks/useAdTracking.ts
|
|
33
|
+
var import_react = require("react");
|
|
34
|
+
function useAdTracking({
|
|
35
|
+
ad,
|
|
36
|
+
disableImpressionTracking = false,
|
|
37
|
+
onImpression,
|
|
38
|
+
onClickTracked
|
|
39
|
+
}) {
|
|
40
|
+
const impressionTracked = (0, import_react.useRef)(false);
|
|
41
|
+
(0, import_react.useEffect)(() => {
|
|
42
|
+
if (!ad || !ad.impUrl || disableImpressionTracking || impressionTracked.current) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const trackImpression = async () => {
|
|
46
|
+
try {
|
|
47
|
+
const img = new Image();
|
|
48
|
+
img.src = ad.impUrl;
|
|
49
|
+
impressionTracked.current = true;
|
|
50
|
+
onImpression?.();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("[Gravity] Failed to track impression:", error);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
trackImpression();
|
|
56
|
+
}, [ad, disableImpressionTracking, onImpression]);
|
|
57
|
+
(0, import_react.useEffect)(() => {
|
|
58
|
+
impressionTracked.current = false;
|
|
59
|
+
}, [ad?.impUrl]);
|
|
60
|
+
const handleClick = (0, import_react.useCallback)(() => {
|
|
61
|
+
if (!ad?.clickUrl) return;
|
|
62
|
+
onClickTracked?.();
|
|
63
|
+
}, [ad?.clickUrl, onClickTracked]);
|
|
64
|
+
return {
|
|
65
|
+
handleClick,
|
|
66
|
+
impressionTracked: impressionTracked.current
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/styles.ts
|
|
71
|
+
var baseContainerStyle = {
|
|
72
|
+
display: "block",
|
|
73
|
+
textDecoration: "none",
|
|
74
|
+
cursor: "pointer",
|
|
75
|
+
transition: "all 0.2s ease",
|
|
76
|
+
boxSizing: "border-box",
|
|
77
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
|
78
|
+
};
|
|
79
|
+
var themeStyles = {
|
|
80
|
+
light: {
|
|
81
|
+
backgroundColor: "#ffffff",
|
|
82
|
+
color: "#1a1a1a",
|
|
83
|
+
border: "1px solid #e5e5e5",
|
|
84
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.08)"
|
|
85
|
+
},
|
|
86
|
+
dark: {
|
|
87
|
+
backgroundColor: "#1a1a1a",
|
|
88
|
+
color: "#f5f5f5",
|
|
89
|
+
border: "1px solid #333333",
|
|
90
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.3)"
|
|
91
|
+
},
|
|
92
|
+
minimal: {
|
|
93
|
+
backgroundColor: "transparent",
|
|
94
|
+
color: "inherit",
|
|
95
|
+
border: "none",
|
|
96
|
+
boxShadow: "none"
|
|
97
|
+
},
|
|
98
|
+
branded: {
|
|
99
|
+
backgroundColor: "#6366f1",
|
|
100
|
+
color: "#ffffff",
|
|
101
|
+
border: "none",
|
|
102
|
+
boxShadow: "0 2px 8px rgba(99, 102, 241, 0.3)"
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var sizeStyles = {
|
|
106
|
+
small: {
|
|
107
|
+
padding: "8px 12px",
|
|
108
|
+
fontSize: "13px",
|
|
109
|
+
lineHeight: "1.4",
|
|
110
|
+
borderRadius: "6px"
|
|
111
|
+
},
|
|
112
|
+
medium: {
|
|
113
|
+
padding: "12px 16px",
|
|
114
|
+
fontSize: "14px",
|
|
115
|
+
lineHeight: "1.5",
|
|
116
|
+
borderRadius: "8px"
|
|
117
|
+
},
|
|
118
|
+
large: {
|
|
119
|
+
padding: "16px 20px",
|
|
120
|
+
fontSize: "16px",
|
|
121
|
+
lineHeight: "1.6",
|
|
122
|
+
borderRadius: "10px"
|
|
123
|
+
},
|
|
124
|
+
responsive: {
|
|
125
|
+
padding: "clamp(8px, 2vw, 16px) clamp(12px, 3vw, 20px)",
|
|
126
|
+
fontSize: "clamp(13px, 1.5vw, 16px)",
|
|
127
|
+
lineHeight: "1.5",
|
|
128
|
+
borderRadius: "clamp(6px, 1vw, 10px)"
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var baseLabelStyle = {
|
|
132
|
+
fontSize: "10px",
|
|
133
|
+
fontWeight: 600,
|
|
134
|
+
textTransform: "uppercase",
|
|
135
|
+
letterSpacing: "0.5px",
|
|
136
|
+
opacity: 0.7,
|
|
137
|
+
marginBottom: "4px",
|
|
138
|
+
display: "block"
|
|
139
|
+
};
|
|
140
|
+
function getAdBannerStyles(theme, size, customStyles) {
|
|
141
|
+
const combined = {
|
|
142
|
+
...baseContainerStyle,
|
|
143
|
+
...themeStyles[theme],
|
|
144
|
+
...sizeStyles[size]
|
|
145
|
+
};
|
|
146
|
+
if (customStyles?.backgroundColor) {
|
|
147
|
+
combined.backgroundColor = customStyles.backgroundColor;
|
|
148
|
+
}
|
|
149
|
+
if (customStyles?.textColor) {
|
|
150
|
+
combined.color = customStyles.textColor;
|
|
151
|
+
}
|
|
152
|
+
if (customStyles?.borderRadius !== void 0) {
|
|
153
|
+
combined.borderRadius = customStyles.borderRadius;
|
|
154
|
+
}
|
|
155
|
+
if (customStyles?.style) {
|
|
156
|
+
Object.assign(combined, customStyles.style);
|
|
157
|
+
}
|
|
158
|
+
return combined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/components/AdBanner.tsx
|
|
162
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
163
|
+
function AdBanner({
|
|
164
|
+
ad,
|
|
165
|
+
theme = "light",
|
|
166
|
+
size = "medium",
|
|
167
|
+
className,
|
|
168
|
+
style,
|
|
169
|
+
textStyle,
|
|
170
|
+
textClassName,
|
|
171
|
+
showLabel = true,
|
|
172
|
+
labelText = "Sponsored",
|
|
173
|
+
labelStyle,
|
|
174
|
+
onClick,
|
|
175
|
+
onImpression,
|
|
176
|
+
onClickTracked,
|
|
177
|
+
fallback = null,
|
|
178
|
+
disableImpressionTracking = false,
|
|
179
|
+
openInNewTab = true,
|
|
180
|
+
borderRadius,
|
|
181
|
+
backgroundColor,
|
|
182
|
+
textColor,
|
|
183
|
+
accentColor
|
|
184
|
+
}) {
|
|
185
|
+
const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
|
|
186
|
+
const { handleClick } = useAdTracking({
|
|
187
|
+
ad,
|
|
188
|
+
disableImpressionTracking,
|
|
189
|
+
onImpression,
|
|
190
|
+
onClickTracked
|
|
191
|
+
});
|
|
192
|
+
if (!ad) {
|
|
193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: fallback });
|
|
194
|
+
}
|
|
195
|
+
const containerStyles = getAdBannerStyles(theme, size, {
|
|
196
|
+
backgroundColor,
|
|
197
|
+
textColor,
|
|
198
|
+
borderRadius,
|
|
199
|
+
style
|
|
200
|
+
});
|
|
201
|
+
if (isHovered && theme !== "minimal") {
|
|
202
|
+
containerStyles.transform = "translateY(-1px)";
|
|
203
|
+
containerStyles.boxShadow = theme === "dark" ? "0 4px 12px rgba(0, 0, 0, 0.4)" : theme === "branded" ? "0 4px 16px rgba(99, 102, 241, 0.4)" : "0 4px 12px rgba(0, 0, 0, 0.12)";
|
|
204
|
+
}
|
|
205
|
+
const labelStyles = {
|
|
206
|
+
...baseLabelStyle,
|
|
207
|
+
color: accentColor || (theme === "branded" ? "rgba(255,255,255,0.8)" : void 0),
|
|
208
|
+
...labelStyle
|
|
209
|
+
};
|
|
210
|
+
const textStyles = {
|
|
211
|
+
margin: 0,
|
|
212
|
+
...textStyle
|
|
213
|
+
};
|
|
214
|
+
const handleClickInternal = (e) => {
|
|
215
|
+
handleClick();
|
|
216
|
+
onClick?.();
|
|
217
|
+
if (!ad.clickUrl) {
|
|
218
|
+
e.preventDefault();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const linkProps = ad.clickUrl ? {
|
|
222
|
+
href: ad.clickUrl,
|
|
223
|
+
target: openInNewTab ? "_blank" : void 0,
|
|
224
|
+
rel: openInNewTab ? "noopener noreferrer sponsored" : "sponsored"
|
|
225
|
+
} : {};
|
|
226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
227
|
+
"a",
|
|
228
|
+
{
|
|
229
|
+
...linkProps,
|
|
230
|
+
className,
|
|
231
|
+
style: containerStyles,
|
|
232
|
+
onClick: handleClickInternal,
|
|
233
|
+
onMouseEnter: () => setIsHovered(true),
|
|
234
|
+
onMouseLeave: () => setIsHovered(false),
|
|
235
|
+
"data-gravity-ad": true,
|
|
236
|
+
children: [
|
|
237
|
+
showLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: labelStyles, children: labelText }),
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: textClassName, style: textStyles, children: ad.adText })
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
AdBanner.displayName = "GravityAdBanner";
|
|
244
|
+
|
|
245
|
+
// src/components/AdText.tsx
|
|
246
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
247
|
+
function AdText({
|
|
248
|
+
ad,
|
|
249
|
+
className,
|
|
250
|
+
style,
|
|
251
|
+
onClick,
|
|
252
|
+
onImpression,
|
|
253
|
+
onClickTracked,
|
|
254
|
+
fallback = null,
|
|
255
|
+
disableImpressionTracking = false,
|
|
256
|
+
openInNewTab = true
|
|
257
|
+
}) {
|
|
258
|
+
const { handleClick } = useAdTracking({
|
|
259
|
+
ad,
|
|
260
|
+
disableImpressionTracking,
|
|
261
|
+
onImpression,
|
|
262
|
+
onClickTracked
|
|
263
|
+
});
|
|
264
|
+
if (!ad) {
|
|
265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
|
|
266
|
+
}
|
|
267
|
+
const handleClickInternal = (e) => {
|
|
268
|
+
handleClick();
|
|
269
|
+
onClick?.();
|
|
270
|
+
if (!ad.clickUrl) {
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const baseStyle = {
|
|
275
|
+
textDecoration: "none",
|
|
276
|
+
color: "inherit",
|
|
277
|
+
cursor: ad.clickUrl ? "pointer" : "default",
|
|
278
|
+
...style
|
|
279
|
+
};
|
|
280
|
+
if (ad.clickUrl) {
|
|
281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
282
|
+
"a",
|
|
283
|
+
{
|
|
284
|
+
href: ad.clickUrl,
|
|
285
|
+
target: openInNewTab ? "_blank" : void 0,
|
|
286
|
+
rel: openInNewTab ? "noopener noreferrer sponsored" : "sponsored",
|
|
287
|
+
className,
|
|
288
|
+
style: baseStyle,
|
|
289
|
+
onClick: handleClickInternal,
|
|
290
|
+
"data-gravity-ad": true,
|
|
291
|
+
children: ad.adText
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className, style: baseStyle, "data-gravity-ad": true, children: ad.adText });
|
|
296
|
+
}
|
|
297
|
+
AdText.displayName = "GravityAdText";
|
|
298
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
299
|
+
0 && (module.exports = {
|
|
300
|
+
AdBanner,
|
|
301
|
+
AdText,
|
|
302
|
+
useAdTracking
|
|
303
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// src/components/AdBanner.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
// src/hooks/useAdTracking.ts
|
|
5
|
+
import { useEffect, useRef, useCallback } from "react";
|
|
6
|
+
function useAdTracking({
|
|
7
|
+
ad,
|
|
8
|
+
disableImpressionTracking = false,
|
|
9
|
+
onImpression,
|
|
10
|
+
onClickTracked
|
|
11
|
+
}) {
|
|
12
|
+
const impressionTracked = useRef(false);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!ad || !ad.impUrl || disableImpressionTracking || impressionTracked.current) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const trackImpression = async () => {
|
|
18
|
+
try {
|
|
19
|
+
const img = new Image();
|
|
20
|
+
img.src = ad.impUrl;
|
|
21
|
+
impressionTracked.current = true;
|
|
22
|
+
onImpression?.();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("[Gravity] Failed to track impression:", error);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
trackImpression();
|
|
28
|
+
}, [ad, disableImpressionTracking, onImpression]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
impressionTracked.current = false;
|
|
31
|
+
}, [ad?.impUrl]);
|
|
32
|
+
const handleClick = useCallback(() => {
|
|
33
|
+
if (!ad?.clickUrl) return;
|
|
34
|
+
onClickTracked?.();
|
|
35
|
+
}, [ad?.clickUrl, onClickTracked]);
|
|
36
|
+
return {
|
|
37
|
+
handleClick,
|
|
38
|
+
impressionTracked: impressionTracked.current
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/styles.ts
|
|
43
|
+
var baseContainerStyle = {
|
|
44
|
+
display: "block",
|
|
45
|
+
textDecoration: "none",
|
|
46
|
+
cursor: "pointer",
|
|
47
|
+
transition: "all 0.2s ease",
|
|
48
|
+
boxSizing: "border-box",
|
|
49
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
|
50
|
+
};
|
|
51
|
+
var themeStyles = {
|
|
52
|
+
light: {
|
|
53
|
+
backgroundColor: "#ffffff",
|
|
54
|
+
color: "#1a1a1a",
|
|
55
|
+
border: "1px solid #e5e5e5",
|
|
56
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.08)"
|
|
57
|
+
},
|
|
58
|
+
dark: {
|
|
59
|
+
backgroundColor: "#1a1a1a",
|
|
60
|
+
color: "#f5f5f5",
|
|
61
|
+
border: "1px solid #333333",
|
|
62
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.3)"
|
|
63
|
+
},
|
|
64
|
+
minimal: {
|
|
65
|
+
backgroundColor: "transparent",
|
|
66
|
+
color: "inherit",
|
|
67
|
+
border: "none",
|
|
68
|
+
boxShadow: "none"
|
|
69
|
+
},
|
|
70
|
+
branded: {
|
|
71
|
+
backgroundColor: "#6366f1",
|
|
72
|
+
color: "#ffffff",
|
|
73
|
+
border: "none",
|
|
74
|
+
boxShadow: "0 2px 8px rgba(99, 102, 241, 0.3)"
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var sizeStyles = {
|
|
78
|
+
small: {
|
|
79
|
+
padding: "8px 12px",
|
|
80
|
+
fontSize: "13px",
|
|
81
|
+
lineHeight: "1.4",
|
|
82
|
+
borderRadius: "6px"
|
|
83
|
+
},
|
|
84
|
+
medium: {
|
|
85
|
+
padding: "12px 16px",
|
|
86
|
+
fontSize: "14px",
|
|
87
|
+
lineHeight: "1.5",
|
|
88
|
+
borderRadius: "8px"
|
|
89
|
+
},
|
|
90
|
+
large: {
|
|
91
|
+
padding: "16px 20px",
|
|
92
|
+
fontSize: "16px",
|
|
93
|
+
lineHeight: "1.6",
|
|
94
|
+
borderRadius: "10px"
|
|
95
|
+
},
|
|
96
|
+
responsive: {
|
|
97
|
+
padding: "clamp(8px, 2vw, 16px) clamp(12px, 3vw, 20px)",
|
|
98
|
+
fontSize: "clamp(13px, 1.5vw, 16px)",
|
|
99
|
+
lineHeight: "1.5",
|
|
100
|
+
borderRadius: "clamp(6px, 1vw, 10px)"
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var baseLabelStyle = {
|
|
104
|
+
fontSize: "10px",
|
|
105
|
+
fontWeight: 600,
|
|
106
|
+
textTransform: "uppercase",
|
|
107
|
+
letterSpacing: "0.5px",
|
|
108
|
+
opacity: 0.7,
|
|
109
|
+
marginBottom: "4px",
|
|
110
|
+
display: "block"
|
|
111
|
+
};
|
|
112
|
+
function getAdBannerStyles(theme, size, customStyles) {
|
|
113
|
+
const combined = {
|
|
114
|
+
...baseContainerStyle,
|
|
115
|
+
...themeStyles[theme],
|
|
116
|
+
...sizeStyles[size]
|
|
117
|
+
};
|
|
118
|
+
if (customStyles?.backgroundColor) {
|
|
119
|
+
combined.backgroundColor = customStyles.backgroundColor;
|
|
120
|
+
}
|
|
121
|
+
if (customStyles?.textColor) {
|
|
122
|
+
combined.color = customStyles.textColor;
|
|
123
|
+
}
|
|
124
|
+
if (customStyles?.borderRadius !== void 0) {
|
|
125
|
+
combined.borderRadius = customStyles.borderRadius;
|
|
126
|
+
}
|
|
127
|
+
if (customStyles?.style) {
|
|
128
|
+
Object.assign(combined, customStyles.style);
|
|
129
|
+
}
|
|
130
|
+
return combined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/components/AdBanner.tsx
|
|
134
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
135
|
+
function AdBanner({
|
|
136
|
+
ad,
|
|
137
|
+
theme = "light",
|
|
138
|
+
size = "medium",
|
|
139
|
+
className,
|
|
140
|
+
style,
|
|
141
|
+
textStyle,
|
|
142
|
+
textClassName,
|
|
143
|
+
showLabel = true,
|
|
144
|
+
labelText = "Sponsored",
|
|
145
|
+
labelStyle,
|
|
146
|
+
onClick,
|
|
147
|
+
onImpression,
|
|
148
|
+
onClickTracked,
|
|
149
|
+
fallback = null,
|
|
150
|
+
disableImpressionTracking = false,
|
|
151
|
+
openInNewTab = true,
|
|
152
|
+
borderRadius,
|
|
153
|
+
backgroundColor,
|
|
154
|
+
textColor,
|
|
155
|
+
accentColor
|
|
156
|
+
}) {
|
|
157
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
158
|
+
const { handleClick } = useAdTracking({
|
|
159
|
+
ad,
|
|
160
|
+
disableImpressionTracking,
|
|
161
|
+
onImpression,
|
|
162
|
+
onClickTracked
|
|
163
|
+
});
|
|
164
|
+
if (!ad) {
|
|
165
|
+
return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
166
|
+
}
|
|
167
|
+
const containerStyles = getAdBannerStyles(theme, size, {
|
|
168
|
+
backgroundColor,
|
|
169
|
+
textColor,
|
|
170
|
+
borderRadius,
|
|
171
|
+
style
|
|
172
|
+
});
|
|
173
|
+
if (isHovered && theme !== "minimal") {
|
|
174
|
+
containerStyles.transform = "translateY(-1px)";
|
|
175
|
+
containerStyles.boxShadow = theme === "dark" ? "0 4px 12px rgba(0, 0, 0, 0.4)" : theme === "branded" ? "0 4px 16px rgba(99, 102, 241, 0.4)" : "0 4px 12px rgba(0, 0, 0, 0.12)";
|
|
176
|
+
}
|
|
177
|
+
const labelStyles = {
|
|
178
|
+
...baseLabelStyle,
|
|
179
|
+
color: accentColor || (theme === "branded" ? "rgba(255,255,255,0.8)" : void 0),
|
|
180
|
+
...labelStyle
|
|
181
|
+
};
|
|
182
|
+
const textStyles = {
|
|
183
|
+
margin: 0,
|
|
184
|
+
...textStyle
|
|
185
|
+
};
|
|
186
|
+
const handleClickInternal = (e) => {
|
|
187
|
+
handleClick();
|
|
188
|
+
onClick?.();
|
|
189
|
+
if (!ad.clickUrl) {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const linkProps = ad.clickUrl ? {
|
|
194
|
+
href: ad.clickUrl,
|
|
195
|
+
target: openInNewTab ? "_blank" : void 0,
|
|
196
|
+
rel: openInNewTab ? "noopener noreferrer sponsored" : "sponsored"
|
|
197
|
+
} : {};
|
|
198
|
+
return /* @__PURE__ */ jsxs(
|
|
199
|
+
"a",
|
|
200
|
+
{
|
|
201
|
+
...linkProps,
|
|
202
|
+
className,
|
|
203
|
+
style: containerStyles,
|
|
204
|
+
onClick: handleClickInternal,
|
|
205
|
+
onMouseEnter: () => setIsHovered(true),
|
|
206
|
+
onMouseLeave: () => setIsHovered(false),
|
|
207
|
+
"data-gravity-ad": true,
|
|
208
|
+
children: [
|
|
209
|
+
showLabel && /* @__PURE__ */ jsx("span", { style: labelStyles, children: labelText }),
|
|
210
|
+
/* @__PURE__ */ jsx("p", { className: textClassName, style: textStyles, children: ad.adText })
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
AdBanner.displayName = "GravityAdBanner";
|
|
216
|
+
|
|
217
|
+
// src/components/AdText.tsx
|
|
218
|
+
import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime";
|
|
219
|
+
function AdText({
|
|
220
|
+
ad,
|
|
221
|
+
className,
|
|
222
|
+
style,
|
|
223
|
+
onClick,
|
|
224
|
+
onImpression,
|
|
225
|
+
onClickTracked,
|
|
226
|
+
fallback = null,
|
|
227
|
+
disableImpressionTracking = false,
|
|
228
|
+
openInNewTab = true
|
|
229
|
+
}) {
|
|
230
|
+
const { handleClick } = useAdTracking({
|
|
231
|
+
ad,
|
|
232
|
+
disableImpressionTracking,
|
|
233
|
+
onImpression,
|
|
234
|
+
onClickTracked
|
|
235
|
+
});
|
|
236
|
+
if (!ad) {
|
|
237
|
+
return /* @__PURE__ */ jsx2(Fragment2, { children: fallback });
|
|
238
|
+
}
|
|
239
|
+
const handleClickInternal = (e) => {
|
|
240
|
+
handleClick();
|
|
241
|
+
onClick?.();
|
|
242
|
+
if (!ad.clickUrl) {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const baseStyle = {
|
|
247
|
+
textDecoration: "none",
|
|
248
|
+
color: "inherit",
|
|
249
|
+
cursor: ad.clickUrl ? "pointer" : "default",
|
|
250
|
+
...style
|
|
251
|
+
};
|
|
252
|
+
if (ad.clickUrl) {
|
|
253
|
+
return /* @__PURE__ */ jsx2(
|
|
254
|
+
"a",
|
|
255
|
+
{
|
|
256
|
+
href: ad.clickUrl,
|
|
257
|
+
target: openInNewTab ? "_blank" : void 0,
|
|
258
|
+
rel: openInNewTab ? "noopener noreferrer sponsored" : "sponsored",
|
|
259
|
+
className,
|
|
260
|
+
style: baseStyle,
|
|
261
|
+
onClick: handleClickInternal,
|
|
262
|
+
"data-gravity-ad": true,
|
|
263
|
+
children: ad.adText
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return /* @__PURE__ */ jsx2("span", { className, style: baseStyle, "data-gravity-ad": true, children: ad.adText });
|
|
268
|
+
}
|
|
269
|
+
AdText.displayName = "GravityAdText";
|
|
270
|
+
export {
|
|
271
|
+
AdBanner,
|
|
272
|
+
AdText,
|
|
273
|
+
useAdTracking
|
|
274
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gravity-ai/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React components for rendering Gravity AI advertisements",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"import": "./dist/index.mjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --external react",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts --external react",
|
|
22
|
+
"lint": "tsc --noEmit",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
26
|
+
"version:patch": "npm version patch",
|
|
27
|
+
"version:minor": "npm version minor",
|
|
28
|
+
"version:major": "npm version major",
|
|
29
|
+
"publish:patch": "npm run version:patch && npm publish",
|
|
30
|
+
"publish:minor": "npm run version:minor && npm publish",
|
|
31
|
+
"publish:major": "npm run version:major && npm publish"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"gravity",
|
|
35
|
+
"advertising",
|
|
36
|
+
"react",
|
|
37
|
+
"ads",
|
|
38
|
+
"typescript",
|
|
39
|
+
"contextual-advertising",
|
|
40
|
+
"ai-ads",
|
|
41
|
+
"ad-component"
|
|
42
|
+
],
|
|
43
|
+
"author": "Gravity Team",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"homepage": "https://github.com/Try-Gravity/gravity-js#readme",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/Try-Gravity/gravity-js.git",
|
|
49
|
+
"directory": "packages/react"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/Try-Gravity/gravity-js/issues"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"react": ">=17.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
65
|
+
"@testing-library/react": "^14.2.1",
|
|
66
|
+
"@types/react": "^18.2.48",
|
|
67
|
+
"jsdom": "^24.0.0",
|
|
68
|
+
"react": "^18.2.0",
|
|
69
|
+
"react-dom": "^18.2.0",
|
|
70
|
+
"tsup": "^8.0.1",
|
|
71
|
+
"typescript": "^5.3.3",
|
|
72
|
+
"vitest": "^1.2.1"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|