mirror of
https://github.com/umami-software/umami.git
synced 2026-02-08 14:47:14 +01:00
Refactored tracker. Added handling for buttons.
This commit is contained in:
parent
2197551e58
commit
28d2787880
9 changed files with 152 additions and 209 deletions
|
|
@ -1,7 +1,16 @@
|
|||
.test {
|
||||
.container {
|
||||
display: grid;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
border: 1px solid var(--base400);
|
||||
border-radius: 5px;
|
||||
padding: 0 20px 20px 20px;
|
||||
display: grid;
|
||||
gap: 40px;
|
||||
grid-template-columns: repeat(3, minmax(300px, 1fr));
|
||||
box-shadow: 0 0 0 10px var(--base100);
|
||||
}
|
||||
|
||||
.header {
|
||||
|
|
@ -9,3 +18,15 @@
|
|||
font-weight: 700;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wrapped {
|
||||
border: 1px solid var(--blue900);
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,15 +82,15 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
|||
<WebsiteSelect websiteId={website?.id} onSelect={handleChange} />
|
||||
</PageHeader>
|
||||
{website && (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<Script
|
||||
async
|
||||
data-website-id={websiteId}
|
||||
src={`${process.env.basePath}/script.js`}
|
||||
data-cache="true"
|
||||
/>
|
||||
<div className={styles.test}>
|
||||
<div>
|
||||
<div className={styles.actions}>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.header}>Page links</div>
|
||||
<div>
|
||||
<Link href={`/console/${websiteId}/page/1/?q=abc`}>page one</Link>
|
||||
|
|
@ -114,12 +114,11 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.header}>Click events</div>
|
||||
<Button id="send-event-button" data-umami-event="button-click" variant="primary">
|
||||
Send event
|
||||
</Button>
|
||||
<p />
|
||||
<Button
|
||||
id="send-event-data-button"
|
||||
data-umami-event="button-click"
|
||||
|
|
@ -129,23 +128,39 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
|
|||
>
|
||||
Send event with data
|
||||
</Button>
|
||||
<Button
|
||||
id="button-with-div-button"
|
||||
data-umami-event="button-click"
|
||||
data-umami-event-name="bob"
|
||||
data-umami-event-id="123"
|
||||
variant="primary"
|
||||
>
|
||||
<div className={styles.wrapped}>Button with div</div>
|
||||
</Button>
|
||||
<div data-umami-event="div-click" className={styles.wrapped}>
|
||||
DIV with attribute
|
||||
</div>
|
||||
<div data-umami-event="div-click-one" className={styles.wrapped}>
|
||||
<div data-umami-event="div-click-two" className={styles.wrapped}>
|
||||
<div data-umami-event="div-click-three" className={styles.wrapped}>
|
||||
Nested DIV
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.group}>
|
||||
<div className={styles.header}>Javascript events</div>
|
||||
<Button id="manual-button" variant="primary" onClick={handleClick}>
|
||||
Run script
|
||||
</Button>
|
||||
<p />
|
||||
<Button id="manual-button" variant="primary" onClick={handleIdentifyClick}>
|
||||
Run identify
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<WebsiteChart websiteId={website.id} />
|
||||
<EventsChart websiteId={website.id} />
|
||||
</div>
|
||||
</>
|
||||
<WebsiteChart websiteId={website.id} />
|
||||
<EventsChart websiteId={website.id} />
|
||||
</div>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export function useDateRange(websiteId?: string) {
|
|||
};
|
||||
|
||||
return [dateRange, saveDateRange] as [
|
||||
{ startDate: Date; endDate: Date; modified?: number; value?: string },
|
||||
{ startDate: Date; endDate: Date; modified?: number; value?: string; unit?: string },
|
||||
(value: string | DateRange) => void,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,10 +77,10 @@ export function EventsChart({ websiteId, className, token }: EventsChartProps) {
|
|||
className={className}
|
||||
datasets={datasets}
|
||||
unit={unit}
|
||||
loading={isLoading}
|
||||
stacked
|
||||
stacked={true}
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
renderTooltipPopup={renderStatusTooltipPopup(unit, locale)}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export function Legend({ chart }) {
|
|||
const { locale } = useLocale();
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const handleClick = index => {
|
||||
const handleClick = (index: string | number) => {
|
||||
const meta = chart.getDatasetMeta(index);
|
||||
|
||||
meta.hidden = meta.hidden === null ? !chart.data.datasets[index].hidden : null;
|
||||
|
|
|
|||
|
|
@ -31,16 +31,6 @@
|
|||
|
||||
/* Helper functions */
|
||||
|
||||
const hook = (_this, method, callback) => {
|
||||
const orig = _this[method];
|
||||
|
||||
return (...args) => {
|
||||
callback.apply(null, args);
|
||||
|
||||
return orig.apply(_this, args);
|
||||
};
|
||||
};
|
||||
|
||||
const getPath = url => {
|
||||
try {
|
||||
return new URL(url).pathname;
|
||||
|
|
@ -59,10 +49,7 @@
|
|||
referrer: currentRef,
|
||||
});
|
||||
|
||||
/* Tracking functions */
|
||||
const trackingDisabled = () =>
|
||||
(localStorage && localStorage.getItem('umami.disabled')) ||
|
||||
(domain && !domains.includes(hostname));
|
||||
/* Event handlers */
|
||||
|
||||
const handlePush = (state, title, url) => {
|
||||
if (!url) return;
|
||||
|
|
@ -75,77 +62,25 @@
|
|||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
const trackElement = el => {
|
||||
const attr = el.getAttribute.bind(el);
|
||||
const eventName = attr(eventNameAttribute);
|
||||
const handlePathChanges = () => {
|
||||
const hook = (_this, method, callback) => {
|
||||
const orig = _this[method];
|
||||
|
||||
if (eventName) {
|
||||
const eventData = {};
|
||||
return (...args) => {
|
||||
callback.apply(null, args);
|
||||
|
||||
el.getAttributeNames().forEach(name => {
|
||||
const match = name.match(eventRegex);
|
||||
|
||||
if (match) {
|
||||
eventData[match[1]] = attr(name);
|
||||
}
|
||||
});
|
||||
|
||||
return track(eventName, eventData);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const callback = e => {
|
||||
const findATagParent = (rootElem, maxSearchDepth) => {
|
||||
let currentElement = rootElem;
|
||||
for (let i = 0; i < maxSearchDepth; i++) {
|
||||
if (currentElement.tagName === 'A') {
|
||||
return currentElement;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
if (!currentElement) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return orig.apply(_this, args);
|
||||
};
|
||||
|
||||
const el = e.target;
|
||||
const anchor = el.tagName === 'A' ? el : findATagParent(el, 10);
|
||||
|
||||
if (anchor) {
|
||||
const { href, target } = anchor;
|
||||
const external =
|
||||
target === '_blank' ||
|
||||
e.ctrlKey ||
|
||||
e.shiftKey ||
|
||||
e.metaKey ||
|
||||
(e.button && e.button === 1);
|
||||
const eventName = anchor.getAttribute(eventNameAttribute);
|
||||
|
||||
if (eventName && href) {
|
||||
if (!external) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return trackElement(anchor).then(() => {
|
||||
if (!external) location.href = href;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
trackElement(el);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', callback, true);
|
||||
history.pushState = hook(history, 'pushState', handlePush);
|
||||
history.replaceState = hook(history, 'replaceState', handlePush);
|
||||
};
|
||||
|
||||
const observeTitle = () => {
|
||||
const callback = ([entry]) => {
|
||||
const handleTitleChanges = () => {
|
||||
const observer = new MutationObserver(([entry]) => {
|
||||
title = entry && entry.target ? entry.target.text : undefined;
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(callback);
|
||||
});
|
||||
|
||||
const node = document.querySelector('head > title');
|
||||
|
||||
|
|
@ -158,6 +93,86 @@
|
|||
}
|
||||
};
|
||||
|
||||
const handleClicks = () => {
|
||||
document.addEventListener(
|
||||
'click',
|
||||
async e => {
|
||||
const isSpecialTag = tagName => ['BUTTON', 'A'].includes(tagName);
|
||||
|
||||
const trackElement = async el => {
|
||||
const attr = el.getAttribute.bind(el);
|
||||
const eventName = attr(eventNameAttribute);
|
||||
|
||||
if (eventName) {
|
||||
const eventData = {};
|
||||
|
||||
el.getAttributeNames().forEach(name => {
|
||||
const match = name.match(eventRegex);
|
||||
|
||||
if (match) {
|
||||
eventData[match[1]] = attr(name);
|
||||
}
|
||||
});
|
||||
|
||||
return track(eventName, eventData);
|
||||
}
|
||||
};
|
||||
|
||||
const findParentTag = (rootElem, maxSearchDepth) => {
|
||||
let currentElement = rootElem;
|
||||
for (let i = 0; i < maxSearchDepth; i++) {
|
||||
if (isSpecialTag(currentElement.tagName)) {
|
||||
return currentElement;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
if (!currentElement) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const el = e.target;
|
||||
const parentElement = isSpecialTag(el.tagName) ? el : findParentTag(el, 10);
|
||||
|
||||
if (parentElement) {
|
||||
const { href, target } = parentElement;
|
||||
const eventName = parentElement.getAttribute(eventNameAttribute);
|
||||
|
||||
if (eventName) {
|
||||
if (parentElement.tagName === 'A') {
|
||||
const external =
|
||||
target === '_blank' ||
|
||||
e.ctrlKey ||
|
||||
e.shiftKey ||
|
||||
e.metaKey ||
|
||||
(e.button && e.button === 1);
|
||||
|
||||
if (eventName && href) {
|
||||
if (!external) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return trackElement(parentElement).then(() => {
|
||||
if (!external) location.href = href;
|
||||
});
|
||||
}
|
||||
} else if (parentElement.tagName === 'BUTTON') {
|
||||
return trackElement(parentElement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return trackElement(el);
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
/* Tracking functions */
|
||||
|
||||
const trackingDisabled = () =>
|
||||
(localStorage && localStorage.getItem('umami.disabled')) ||
|
||||
(domain && !domains.includes(hostname));
|
||||
|
||||
const send = async (payload, type = 'event') => {
|
||||
if (trackingDisabled()) return;
|
||||
const headers = {
|
||||
|
|
@ -173,6 +188,7 @@
|
|||
headers,
|
||||
});
|
||||
const text = await res.text();
|
||||
|
||||
return (cache = text);
|
||||
} catch {
|
||||
/* empty */
|
||||
|
|
@ -212,10 +228,9 @@
|
|||
let initialized;
|
||||
|
||||
if (autoTrack && !trackingDisabled()) {
|
||||
history.pushState = hook(history, 'pushState', handlePush);
|
||||
history.replaceState = hook(history, 'replaceState', handlePush);
|
||||
handleClick();
|
||||
observeTitle();
|
||||
handlePathChanges();
|
||||
handleTitleChanges();
|
||||
handleClicks();
|
||||
|
||||
const init = () => {
|
||||
if (document.readyState === 'complete' && !initialized) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue