Merge pull request #1068 from datopian/analytics
[core][m]: Add analytics component to the core packages
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -48297,7 +48297,7 @@
|
|||||||
},
|
},
|
||||||
"packages/components": {
|
"packages/components": {
|
||||||
"name": "@portaljs/components",
|
"name": "@portaljs/components",
|
||||||
"version": "0.4.0",
|
"version": "0.5.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@githubocto/flat-ui": "^0.14.1",
|
"@githubocto/flat-ui": "^0.14.1",
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@portaljs/core",
|
"name": "@portaljs/core",
|
||||||
"version": "1.0.8",
|
"version": "1.0.9",
|
||||||
"description": "Core Portal.JS components, configs and utils.",
|
"description": "Core Portal.JS components, configs and utils.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
36
packages/core/src/ui/analytics/GoogleAnalytics.tsx
Normal file
36
packages/core/src/ui/analytics/GoogleAnalytics.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import Script from 'next/script.js'
|
||||||
|
|
||||||
|
export interface GoogleAnalyticsProps {
|
||||||
|
googleAnalyticsId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GA = ({ googleAnalyticsId }: GoogleAnalyticsProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Script
|
||||||
|
strategy="afterInteractive"
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Script strategy="afterInteractive" id="ga-script">
|
||||||
|
{`
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '${googleAnalyticsId}');
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
|
||||||
|
export const logEvent = (action, category, label, value) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
window.gtag?.('event', action, {
|
||||||
|
event_category: category,
|
||||||
|
event_label: label,
|
||||||
|
value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
41
packages/core/src/ui/analytics/Plausible.tsx
Normal file
41
packages/core/src/ui/analytics/Plausible.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import Script from 'next/script.js'
|
||||||
|
|
||||||
|
export interface PlausibleProps {
|
||||||
|
plausibleDataDomain: string
|
||||||
|
dataApi?: string
|
||||||
|
src?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plausible analytics component.
|
||||||
|
* To proxy the requests through your own domain, you can use the dataApi and src attribute.
|
||||||
|
* See [Plausible docs](https://plausible.io/docs/proxy/guides/nextjs#step-2-adjust-your-deployed-script)
|
||||||
|
* for more information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const Plausible = ({
|
||||||
|
plausibleDataDomain,
|
||||||
|
dataApi = undefined,
|
||||||
|
src = 'https://plausible.io/js/plausible.js',
|
||||||
|
}: PlausibleProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Script
|
||||||
|
strategy="lazyOnload"
|
||||||
|
data-domain={plausibleDataDomain}
|
||||||
|
data-api={dataApi}
|
||||||
|
src={src}
|
||||||
|
/>
|
||||||
|
<Script strategy="lazyOnload" id="plausible-script">
|
||||||
|
{`
|
||||||
|
window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://plausible.io/docs/custom-event-goals
|
||||||
|
export const logEvent = (eventName, ...rest) => {
|
||||||
|
return window.plausible?.(eventName, ...rest)
|
||||||
|
}
|
||||||
25
packages/core/src/ui/analytics/Posthog.tsx
Normal file
25
packages/core/src/ui/analytics/Posthog.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import Script from 'next/script.js'
|
||||||
|
|
||||||
|
export interface PosthogProps {
|
||||||
|
posthogProjectApiKey: string
|
||||||
|
apiHost?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posthog analytics component.
|
||||||
|
* See [Posthog docs](https://posthog.com/docs/libraries/js#option-1-add-javascript-snippet-to-your-html-badgerecommendedbadge) for more information.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const Posthog = ({
|
||||||
|
posthogProjectApiKey,
|
||||||
|
apiHost = 'https://app.posthog.com',
|
||||||
|
}: PosthogProps) => {
|
||||||
|
return (
|
||||||
|
<Script strategy="lazyOnload" id="posthog-script">
|
||||||
|
{`
|
||||||
|
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
||||||
|
posthog.init('${posthogProjectApiKey}',{api_host:'${apiHost}'})
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
)
|
||||||
|
}
|
||||||
29
packages/core/src/ui/analytics/SimpleAnalytics.tsx
Normal file
29
packages/core/src/ui/analytics/SimpleAnalytics.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import Script from 'next/script.js'
|
||||||
|
|
||||||
|
export interface SimpleAnalyticsProps {
|
||||||
|
src?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SimpleAnalytics = ({
|
||||||
|
src = 'https://scripts.simpleanalyticscdn.com/latest.js',
|
||||||
|
}: SimpleAnalyticsProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Script strategy="lazyOnload" id="sa-script">
|
||||||
|
{`
|
||||||
|
window.sa_event=window.sa_event||function(){var a=[].slice.call(arguments);window.sa_event.q?window.sa_event.q.push(a):window.sa_event.q=[a]};
|
||||||
|
`}
|
||||||
|
</Script>
|
||||||
|
<Script strategy="lazyOnload" src={src} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.simpleanalytics.com/events
|
||||||
|
export const logEvent = (eventName, callback) => {
|
||||||
|
if (callback) {
|
||||||
|
return window.sa_event?.(eventName, callback)
|
||||||
|
} else {
|
||||||
|
return window.sa_event?.(eventName)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/core/src/ui/analytics/Umami.tsx
Normal file
20
packages/core/src/ui/analytics/Umami.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import Script from 'next/script.js'
|
||||||
|
|
||||||
|
export interface UmamiProps {
|
||||||
|
umamiWebsiteId: string
|
||||||
|
src?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Umami = ({
|
||||||
|
umamiWebsiteId,
|
||||||
|
src = 'https://analytics.umami.is/script.js',
|
||||||
|
}: UmamiProps) => {
|
||||||
|
return (
|
||||||
|
<Script
|
||||||
|
async
|
||||||
|
defer
|
||||||
|
data-website-id={umamiWebsiteId}
|
||||||
|
src={src} // Replace with your umami instance
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
82
packages/core/src/ui/analytics/index.tsx
Normal file
82
packages/core/src/ui/analytics/index.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { GA, GoogleAnalyticsProps } from "./GoogleAnalytics";
|
||||||
|
import { Plausible, PlausibleProps } from "./Plausible";
|
||||||
|
import { SimpleAnalytics, SimpleAnalyticsProps } from "./SimpleAnalytics";
|
||||||
|
import { Umami, UmamiProps } from "./Umami";
|
||||||
|
import { Posthog, PosthogProps } from "./Posthog";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
gtag?: (...args: any[]) => void;
|
||||||
|
plausible?: (...args: any[]) => void;
|
||||||
|
sa_event?: (...args: any[]) => void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnalyticsConfig {
|
||||||
|
googleAnalytics?: GoogleAnalyticsProps;
|
||||||
|
plausibleAnalytics?: PlausibleProps;
|
||||||
|
umamiAnalytics?: UmamiProps;
|
||||||
|
posthogAnalytics?: PosthogProps;
|
||||||
|
simpleAnalytics?: SimpleAnalyticsProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* const analytics: AnalyticsConfig = {
|
||||||
|
* plausibleDataDomain: '', // e.g. tailwind-nextjs-starter-blog.vercel.app
|
||||||
|
* simpleAnalytics: false, // true or false
|
||||||
|
* umamiWebsiteId: '', // e.g. 123e4567-e89b-12d3-a456-426614174000
|
||||||
|
* posthogProjectApiKey: '', // e.g. AhnJK8392ndPOav87as450xd
|
||||||
|
* googleAnalyticsId: '', // e.g. UA-000000-2 or G-XXXXXXX
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export interface AnalyticsProps {
|
||||||
|
analyticsConfig: AnalyticsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isProduction = true || process.env["NODE_ENV"] === "production";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports Plausible, Simple Analytics, Umami, Posthog or Google Analytics.
|
||||||
|
* All components default to the hosted service, but can be configured to use a self-hosted
|
||||||
|
* or proxied version of the script by providing the `src` / `apiHost` props.
|
||||||
|
*
|
||||||
|
* Note: If you want to use an analytics provider you have to add it to the
|
||||||
|
* content security policy in the `next.config.js` file.
|
||||||
|
* @param {AnalyticsProps} { analytics }
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
export const Analytics = ({ analyticsConfig }: AnalyticsProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isProduction && analyticsConfig.plausibleAnalytics && (
|
||||||
|
<Plausible {...analyticsConfig.plausibleAnalytics} />
|
||||||
|
)}
|
||||||
|
{isProduction && analyticsConfig.simpleAnalytics && (
|
||||||
|
<SimpleAnalytics {...analyticsConfig.simpleAnalytics} />
|
||||||
|
)}
|
||||||
|
{isProduction && analyticsConfig.posthogAnalytics && (
|
||||||
|
<Posthog {...analyticsConfig.posthogAnalytics} />
|
||||||
|
)}
|
||||||
|
{isProduction && analyticsConfig.umamiAnalytics && (
|
||||||
|
<Umami {...analyticsConfig.umamiAnalytics} />
|
||||||
|
)}
|
||||||
|
{isProduction && analyticsConfig.googleAnalytics && (
|
||||||
|
<GA {...analyticsConfig.googleAnalytics} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { GA, Plausible, SimpleAnalytics, Umami, Posthog };
|
||||||
|
|
||||||
|
export type {
|
||||||
|
GoogleAnalyticsProps,
|
||||||
|
PlausibleProps,
|
||||||
|
UmamiProps,
|
||||||
|
PosthogProps,
|
||||||
|
SimpleAnalyticsProps,
|
||||||
|
};
|
||||||
@@ -21,3 +21,4 @@ export { SiteToc, NavItem, NavGroup } from "./SiteToc";
|
|||||||
export { Comments, CommentsConfig } from "./Comments";
|
export { Comments, CommentsConfig } from "./Comments";
|
||||||
export { AuthorConfig } from "./types";
|
export { AuthorConfig } from "./types";
|
||||||
export { Hero } from "./Hero";
|
export { Hero } from "./Hero";
|
||||||
|
export { Analytics, AnalyticsConfig } from "./analytics";
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export const pageview = ({
|
|||||||
analyticsID: string;
|
analyticsID: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (typeof window.gtag !== undefined) {
|
if (typeof window.gtag !== undefined) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
window.gtag("config", analyticsID, {
|
window.gtag("config", analyticsID, {
|
||||||
page_path: url,
|
page_path: url,
|
||||||
});
|
});
|
||||||
@@ -16,6 +18,8 @@ export const pageview = ({
|
|||||||
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
|
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
|
||||||
export const event = ({ action, category, label, value }) => {
|
export const event = ({ action, category, label, value }) => {
|
||||||
if (typeof window.gtag !== undefined) {
|
if (typeof window.gtag !== undefined) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
window.gtag("event", action, {
|
window.gtag("event", action, {
|
||||||
event_category: category,
|
event_category: category,
|
||||||
event_label: label,
|
event_label: label,
|
||||||
|
|||||||
Reference in New Issue
Block a user