fix(player) - tracker (#1264)
* fix(tracker): fix time inputs capturing
* fix(player): clear selection manger on clicks; display frustrations row on xray by default
* fix(player): add option todisable network in iframes
* cherry-pick 383b5dd6405d8f47b9d794c123390e91b4154663 conflicts
* Revert "cherry-pick 383b5dd6405d8f47b9d794c123390e91b4154663 conflicts"
This reverts commit 364b62fda0.
* fix(player): fix selection manager styles and reset
---------
Co-authored-by: nick-delirium <nikita@openreplay.com>
This commit is contained in:
parent
29e9b71cd1
commit
d7d2884113
11 changed files with 72 additions and 23 deletions
|
|
@ -18,6 +18,7 @@ function OverviewPanel({ issuesList }: { issuesList: Record<string, any>[] }) {
|
|||
const [dataLoaded, setDataLoaded] = React.useState(false);
|
||||
const [selectedFeatures, setSelectedFeatures] = React.useState([
|
||||
'PERFORMANCE',
|
||||
'FRUSTRATIONS',
|
||||
'ERRORS',
|
||||
'NETWORK',
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default class Cursor {
|
|||
private tagElement: HTMLDivElement;
|
||||
private coords = { x: 0, y: 0 };
|
||||
private isMoving = false;
|
||||
private onClick: () => void;
|
||||
|
||||
constructor(overlay: HTMLDivElement, isMobile: boolean) {
|
||||
this.cursor = document.createElement('div');
|
||||
|
|
@ -75,13 +76,14 @@ export default class Cursor {
|
|||
click() {
|
||||
const styleList = this.isMobile ? styles.clickedMobile : styles.clicked
|
||||
this.cursor.classList.add(styleList)
|
||||
this.onClick?.()
|
||||
setTimeout(() => {
|
||||
this.cursor.classList.remove(styleList)
|
||||
}, 600)
|
||||
}
|
||||
|
||||
// TODO (to keep on a different playing speed):
|
||||
// transition
|
||||
// setTransitionSpeed()
|
||||
setOnClickHook(callback: () => void) {
|
||||
this.onClick = callback
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ function isIframe(el: Element): el is HTMLIFrameElement {
|
|||
export default class Screen {
|
||||
readonly overlay: HTMLDivElement
|
||||
readonly cursor: Cursor
|
||||
private selectionTargets: { start?: HTMLDivElement, end?: HTMLDivElement } = { start: undefined, end: undefined }
|
||||
|
||||
private readonly iframe: HTMLIFrameElement;
|
||||
private readonly screen: HTMLDivElement;
|
||||
|
|
@ -253,4 +254,27 @@ export default class Screen {
|
|||
setOnUpdate(cb: any) {
|
||||
this.onUpdateHook = cb
|
||||
}
|
||||
|
||||
public createSelection(start: HTMLDivElement, end: HTMLDivElement) {
|
||||
this.selectionTargets = { start, end }
|
||||
|
||||
this.overlay.appendChild(start);
|
||||
this.overlay.appendChild(end);
|
||||
|
||||
setTimeout(() => {
|
||||
start.className = styles.highlightoff
|
||||
end.className = styles.highlightoff
|
||||
}, 750)
|
||||
}
|
||||
|
||||
public clearSelection() {
|
||||
if (this.selectionTargets.start && this.selectionTargets.end) {
|
||||
this.overlay.removeChild(this.selectionTargets.start);
|
||||
this.overlay.removeChild(this.selectionTargets.end);
|
||||
this.selectionTargets.start.remove()
|
||||
this.selectionTargets.end.remove()
|
||||
this.selectionTargets = { start: undefined, end: undefined }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,3 +20,8 @@
|
|||
bottom: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.highlightoff {
|
||||
opacity: 0;
|
||||
transition: all 0.25s cubic-bezier(0, 0, 0.4, 1.0);
|
||||
}
|
||||
|
|
@ -61,6 +61,10 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
this.stringDict = stringDict
|
||||
}
|
||||
|
||||
public clearSelectionManager() {
|
||||
this.selectionManager.clearSelection()
|
||||
}
|
||||
|
||||
append(m: Message): void {
|
||||
if (m.tp === MType.SetNodeScroll) {
|
||||
let scrollManager = this.nodeScrollManagers.get(m.id)
|
||||
|
|
|
|||
|
|
@ -9,15 +9,12 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
|
|||
}
|
||||
|
||||
private selected: [{ id: number, node: Element } | null, { id: number, node: Element } | null] = [null, null];
|
||||
private markers: Element[] = []
|
||||
|
||||
clearSelection() {
|
||||
this.selected[0] && this.screen.overlay.removeChild(this.selected[0].node) && this.selected[0].node.remove();
|
||||
this.selected[1] && this.screen.overlay.removeChild(this.selected[1].node) && this.selected[1].node.remove();
|
||||
this.markers.forEach(marker => marker.remove())
|
||||
public clearSelection = () => {
|
||||
if (this.selected[0] === null && this.selected[1] === null) return;
|
||||
|
||||
this.screen.clearSelection()
|
||||
this.selected = [null, null];
|
||||
this.markers = [];
|
||||
}
|
||||
|
||||
move(t: number) {
|
||||
|
|
@ -25,6 +22,7 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
|
|||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
// in theory: empty selection or selection removed
|
||||
if (msg.selectionStart <= 0) {
|
||||
this.clearSelection()
|
||||
|
|
@ -48,26 +46,22 @@ export default class SelectionManager extends ListWalker<SelectionChange> {
|
|||
|
||||
Object.assign(endPointer.style, {
|
||||
top: endCoords.top + 'px',
|
||||
left: (endCoords.left + (endCoords.width / 2) + 3) + 'px',
|
||||
width: (endCoords.width / 2) + 'px',
|
||||
right: (endCoords.right) + 'px',
|
||||
width: (endCoords.width) + 'px',
|
||||
height: endCoords.height + 'px',
|
||||
borderRight: '2px solid blue',
|
||||
position: 'absolute',
|
||||
boxShadow: '1px 4px 1px -2px blue',
|
||||
});
|
||||
Object.assign(startPointer.style, {
|
||||
top: startCoords.top + 'px',
|
||||
left: (startCoords.left - 3) + 'px',
|
||||
width: (startCoords.width / 2 ) + 'px',
|
||||
left: (startCoords.left) + 'px',
|
||||
width: (startCoords.width) + 'px',
|
||||
height: startCoords.height + 'px',
|
||||
borderLeft: '2px solid blue',
|
||||
position: 'absolute',
|
||||
boxShadow: '1px 4px 1px -2px blue',
|
||||
});
|
||||
|
||||
this.markers.push(startPointer, endPointer);
|
||||
this.screen.overlay.appendChild(startPointer);
|
||||
this.screen.overlay.appendChild(endPointer);
|
||||
this.screen.createSelection(startPointer, endPointer);
|
||||
|
||||
this.selected = [{ id: msg.selectionStart, node: startPointer }, { id: msg.selectionEnd, node: endPointer }];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export default class PagesManager extends ListWalker<DOMManager> {
|
|||
moveReady(t: number): Promise<void> {
|
||||
const requiredPage = this.moveGetLast(t)
|
||||
if (requiredPage != null) {
|
||||
this.currentPage?.clearSelectionManager()
|
||||
this.currentPage = requiredPage
|
||||
this.currentPage.reset() // Otherwise it won't apply create_document
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
# 7.0.1
|
||||
|
||||
- fix time inputs capturing
|
||||
- add option to disable network tracking inside iframes
|
||||
|
||||
# 7.0.0
|
||||
|
||||
- **[breaking]** added gzip compression to large messages
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@openreplay/tracker",
|
||||
"description": "The OpenReplay tracker main package",
|
||||
"version": "7.0.0",
|
||||
"version": "7.0.1",
|
||||
"keywords": [
|
||||
"logging",
|
||||
"replay"
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
"lint-staged": "^13.0.3",
|
||||
"prettier": "^2.7.1",
|
||||
"replace-in-files": "^2.0.3",
|
||||
"rollup": "^2.17.0",
|
||||
"rollup": "^2.59.0",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"semver": "^6.3.0",
|
||||
"ts-jest": "^29.0.3",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,17 @@ import { normSpaces, IN_BROWSER, getLabelAttribute, now } from '../utils.js'
|
|||
import { hasTag } from '../app/guards.js'
|
||||
import { InputChange, SetInputValue, SetInputChecked } from '../app/messages.gen.js'
|
||||
|
||||
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date', 'tel']
|
||||
const INPUT_TYPES = [
|
||||
'text',
|
||||
'password',
|
||||
'email',
|
||||
'search',
|
||||
'number',
|
||||
'range',
|
||||
'date',
|
||||
'tel',
|
||||
'time',
|
||||
]
|
||||
|
||||
// TODO: take into consideration "contenteditable" attribute
|
||||
type TextFieldElement = HTMLInputElement | HTMLTextAreaElement
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export interface Options {
|
|||
failuresOnly: boolean
|
||||
ignoreHeaders: Array<string> | boolean
|
||||
capturePayload: boolean
|
||||
captureInIframes: boolean
|
||||
sanitizer?: Sanitizer
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +90,7 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
|||
ignoreHeaders: ['Cookie', 'Set-Cookie', 'Authorization'],
|
||||
capturePayload: false,
|
||||
sessionTokenHeader: false,
|
||||
captureInIframes: true,
|
||||
},
|
||||
opts,
|
||||
)
|
||||
|
|
@ -312,7 +314,6 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
|||
) {
|
||||
const rdo = getXHRRequestDataObject(this)
|
||||
rdo.body = body
|
||||
|
||||
// @ts-ignore ??? this -> XMLHttpRequest
|
||||
return nativeSend.apply(this, arguments)
|
||||
}
|
||||
|
|
@ -336,5 +337,7 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
|||
|
||||
patchWindow(window)
|
||||
|
||||
app.observer.attachContextCallback(app.safe(patchWindow))
|
||||
if (options.captureInIframes) {
|
||||
app.observer.attachContextCallback(app.safe(patchWindow))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue