From 26d48f7649b31ec3bf651bddf0495ccf2492c597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Fri, 15 Sep 2023 11:59:03 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20[extension]=20add=20columns=20to=20?= =?UTF-8?q?the=20event=20list=20(#2372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ ditch mantine Table for a proper CSS grid * ✨ add columns to the event list * 👌🐛 replace the Grid with a Table This fixes wrapping issues and reduce complexity (the CSS grid syntax is a bit confusing). The UI isn't quite how I like it, I have ideas on how to improve it but it'll wait. * ♻️ refactor columns operations * 👌✨ add a button to remove columns * ✏️🐛 the monorepo TS configuration prevents us from using iterators --- .../src/panel/components/panel.tsx | 8 +- .../components/tabs/eventsTab/columnDrag.tsx | 188 +++++++++++++ .../components/tabs/eventsTab/columnUtils.ts | 40 +++ .../panel/components/tabs/eventsTab/drag.ts | 121 +++++++++ .../components/tabs/eventsTab/eventRow.tsx | 165 ++++++++--- .../components/tabs/eventsTab/eventsList.tsx | 256 +++++++++++++++++- .../components/tabs/eventsTab/eventsTab.tsx | 36 ++- .../tabs/eventsTab/eventsTabSide.tsx | 8 +- .../tabs/eventsTab/eventsTabTop.tsx | 6 +- .../panel/components/tabs/eventsTab/index.ts | 1 + .../panel/hooks/useEvents/facetRegistry.ts | 10 +- developer-extension/src/panel/uiUtils.ts | 12 +- 12 files changed, 783 insertions(+), 68 deletions(-) create mode 100644 developer-extension/src/panel/components/tabs/eventsTab/columnDrag.tsx create mode 100644 developer-extension/src/panel/components/tabs/eventsTab/columnUtils.ts create mode 100644 developer-extension/src/panel/components/tabs/eventsTab/drag.ts diff --git a/developer-extension/src/panel/components/panel.tsx b/developer-extension/src/panel/components/panel.tsx index ecd5dbdfec..3029db17bd 100644 --- a/developer-extension/src/panel/components/panel.tsx +++ b/developer-extension/src/panel/components/panel.tsx @@ -10,7 +10,7 @@ import { useSettings } from '../hooks/useSettings' import { DEFAULT_PANEL_TAB, PanelTabs } from '../../common/constants' import { SettingsTab } from './tabs/settingsTab' import { InfosTab } from './tabs/infosTab' -import { EventsTab } from './tabs/eventsTab' +import { EventsTab, DEFAULT_COLUMNS } from './tabs/eventsTab' import { ReplayTab } from './tabs/replayTab' export function Panel() { @@ -21,6 +21,8 @@ export function Panel() { const { events, filters, setFilters, clear, facetRegistry } = useEvents(settings) + const [columns, setColumns] = useState(DEFAULT_COLUMNS) + const [activeTab, setActiveTab] = useState(DEFAULT_PANEL_TAB) function updateActiveTab(activeTab: string | null) { setActiveTab(activeTab) @@ -60,7 +62,9 @@ export function Panel() { events={events} facetRegistry={facetRegistry} filters={filters} - onFiltered={setFilters} + onFiltersChange={setFilters} + columns={columns} + onColumnsChange={setColumns} clear={clear} /> diff --git a/developer-extension/src/panel/components/tabs/eventsTab/columnDrag.tsx b/developer-extension/src/panel/components/tabs/eventsTab/columnDrag.tsx new file mode 100644 index 0000000000..354b1d1c45 --- /dev/null +++ b/developer-extension/src/panel/components/tabs/eventsTab/columnDrag.tsx @@ -0,0 +1,188 @@ +import type { RefObject } from 'react' +import React, { useState, useEffect } from 'react' +import { Box, Text } from '@mantine/core' +import { BORDER_RADIUS } from '../../../uiUtils' +import type { Coordinates } from './drag' +import { initDrag } from './drag' +import type { EventListColumn } from './columnUtils' +import { moveColumn, removeColumn, getColumnTitle } from './columnUtils' + +/** Horizontal padding used by the Mantine Table in pixels */ +const HORIZONTAL_PADDING = 10 + +/** Vertical padding used by the Mantine Table in pixels */ +const VERTICAL_PADDING = 7 + +/** Number of pixel to determine if the cursor is close enough of a position to trigger an action */ +const ACTION_DISTANCE_THRESHOLD = 20 + +export function ColumnDrag({ + headerRowRef, + columns, + onColumnsChange, +}: { + headerRowRef: RefObject + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void +}) { + const [drag, setDrag] = useState(null) + + useEffect(() => { + if (columns.length > 1) { + const { stop } = initColumnDrag(headerRowRef.current!, setDrag, columns, onColumnsChange) + return stop + } + }, [columns]) + + return drag && +} + +function DragGhost({ drag }: { drag: DragState }) { + return ( + + {getColumnTitle(drag.column)} + + ) +} + +function getClosestCell(target: HTMLElement) { + if (target.closest('button, .mantine-Popover-dropdown')) { + return null + } + return target.closest('[data-header-cell]') +} + +interface DragState { + targetRect: DOMRect + startPosition: Coordinates + position: Coordinates + action?: DragAction + moved: boolean + insertPlaces: Place[] + column: EventListColumn +} + +interface Place { + index: number + xPosition: number +} + +type DragAction = { type: 'delete' } | { type: 'insert'; place: Place } + +function initColumnDrag( + target: HTMLElement, + onColumnDragStateChanges: (state: DragState | null) => void, + columns: EventListColumn[], + onColumnsChange: (columns: EventListColumn[]) => void +) { + let state: DragState | null = null + + return initDrag({ + target, + + onStart({ target, position }) { + const targetCell = getClosestCell(target) + if (!targetCell) { + return false + } + const siblings = Array.from(targetCell.parentElement!.querySelectorAll(':scope > [data-header-cell]')) + const columnIndex = siblings.indexOf(targetCell) + + state = { + targetRect: targetCell.getBoundingClientRect(), + insertPlaces: siblings.flatMap((sibling, index) => { + if (sibling === targetCell) { + return [] + } + return { + xPosition: sibling.getBoundingClientRect()[index < columnIndex ? 'left' : 'right'], + index, + } + }), + startPosition: position, + position, + moved: false, + action: undefined, + column: columns[columnIndex], + } + onColumnDragStateChanges(state) + }, + + onMove({ position }) { + if (!state) { + return + } + let action: DragAction | undefined + if (Math.abs(state.startPosition.y - position.y) > ACTION_DISTANCE_THRESHOLD) { + action = { type: 'delete' } + } else { + const insertPlace = state.insertPlaces.find( + ({ xPosition }) => Math.abs(position.x - xPosition) < ACTION_DISTANCE_THRESHOLD + ) + if (insertPlace) { + action = { type: 'insert', place: insertPlace } + } + } + + state = { ...state, action, position, moved: true } + onColumnDragStateChanges(state) + }, + + onDrop() { + if (!state) { + return + } + + if (state.action) { + switch (state.action.type) { + case 'delete': + onColumnsChange(removeColumn(columns, state.column)) + break + case 'insert': + onColumnsChange(moveColumn(columns, state.column, state.action.place.index)) + break + } + } + + state = null + onColumnDragStateChanges(state) + }, + + onAbort() { + state = null + onColumnDragStateChanges(state) + }, + }) +} diff --git a/developer-extension/src/panel/components/tabs/eventsTab/columnUtils.ts b/developer-extension/src/panel/components/tabs/eventsTab/columnUtils.ts new file mode 100644 index 0000000000..3e5ead6a4d --- /dev/null +++ b/developer-extension/src/panel/components/tabs/eventsTab/columnUtils.ts @@ -0,0 +1,40 @@ +export type EventListColumn = + | { type: 'date' } + | { type: 'description' } + | { type: 'type' } + | { type: 'field'; path: string } + +export const DEFAULT_COLUMNS: EventListColumn[] = [{ type: 'date' }, { type: 'type' }, { type: 'description' }] + +export function includesColumn(existingColumns: EventListColumn[], newColumn: EventListColumn) { + return existingColumns.some((column) => { + if (column.type === 'field' && newColumn.type === 'field') { + return column.path === newColumn.path + } + return column.type === newColumn.type + }) +} + +export function addColumn(columns: EventListColumn[], columnToAdd: EventListColumn) { + return columns.concat(columnToAdd) +} + +export function removeColumn(columns: EventListColumn[], columnToRemove: EventListColumn) { + return columns.filter((column) => columnToRemove !== column) +} + +export function moveColumn(columns: EventListColumn[], columnToMove: EventListColumn, index: number) { + const newColumns = removeColumn(columns, columnToMove) + newColumns.splice(index, 0, columnToMove) + return newColumns +} + +export function getColumnTitle(column: EventListColumn) { + return column.type === 'date' + ? 'Date' + : column.type === 'description' + ? 'Description' + : column.type === 'type' + ? 'Type' + : column.path +} diff --git a/developer-extension/src/panel/components/tabs/eventsTab/drag.ts b/developer-extension/src/panel/components/tabs/eventsTab/drag.ts new file mode 100644 index 0000000000..5d2e0dff0e --- /dev/null +++ b/developer-extension/src/panel/components/tabs/eventsTab/drag.ts @@ -0,0 +1,121 @@ +export interface Coordinates { + x: number + y: number +} + +/** + * This is a framework agnostic drag implementation that works as a state machine: + * + * ``` + * (init) + * | + * [onStart] + * | \______________ + * | \ + * + * | ____ | + * | / \ (end) + * [onMove] ) + * | \ \____/ + * | \______________ + * | \ + * | + * | + * | | + * [onDrop] [onAbort] + * | _________________/ + * |/ + * (end) + * ``` + */ +export function initDrag({ + target, + onStart, + onMove, + onAbort, + onDrop, +}: { + target: HTMLElement + onStart: (event: { target: HTMLElement; position: Coordinates }) => boolean | void + onMove: (event: { position: Coordinates }) => void + onDrop: () => void + onAbort: () => void +}) { + type DragState = + | { isDragging: false } + | { + isDragging: true + removeListeners: () => void + } + + let state: DragState = { + isDragging: false, + } + + target.addEventListener('pointerdown', onPointerDown, { capture: true }) + + return { + stop: () => { + endDrag(true) + target.removeEventListener('pointerdown', onPointerDown, { capture: true }) + }, + } + + function onPointerDown(event: PointerEvent) { + if ( + state.isDragging || + event.buttons !== 1 || + onStart({ target: event.target as HTMLElement, position: { x: event.clientX, y: event.clientY } }) === false + ) { + return + } + + event.preventDefault() + + state = { + isDragging: true, + removeListeners: () => { + removeEventListener('pointerup', onPointerUp, { capture: true }) + removeEventListener('pointermove', onPointerMove, { capture: true }) + }, + } + + addEventListener('pointerup', onPointerUp, { capture: true }) + addEventListener('pointermove', onPointerMove, { capture: true }) + } + + function onPointerUp(_event: PointerEvent) { + endDrag(false) + } + + function onPointerMove(event: PointerEvent) { + if (!state.isDragging) { + return + } + + if (event.buttons !== 1) { + // The user might have released the click outside of the window + endDrag(true) + return + } + + onMove({ + position: { + x: event.clientX, + y: event.clientY, + }, + }) + } + + function endDrag(abort: boolean) { + if (state.isDragging) { + if (abort) { + onAbort() + } else { + onDrop() + } + state.removeListeners() + state = { isDragging: false } + } + } +} diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx index abad030d80..cd2e3fb261 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventRow.tsx @@ -1,5 +1,6 @@ -import { Badge, Box } from '@mantine/core' -import type { ReactNode } from 'react' +import type { BoxProps } from '@mantine/core' +import { Badge, Box, Menu } from '@mantine/core' +import type { ComponentPropsWithoutRef, ReactNode } from 'react' import React, { useRef, useState } from 'react' import type { TelemetryEvent } from '../../../../../../packages/core/src/domain/telemetry' import type { LogsEvent } from '../../../../../../packages/logs/src/logsEvent.types' @@ -15,6 +16,9 @@ import { isTelemetryEvent, isLogEvent, isRumEvent } from '../../../sdkEvent' import { formatDate, formatDuration } from '../../../formatNumber' import { defaultFormatValue, Json } from '../../json' import { LazyCollapse } from '../../lazyCollapse' +import type { FacetRegistry } from '../../../hooks/useEvents' +import type { EventListColumn } from './columnUtils' +import { addColumn, includesColumn } from './columnUtils' const RUM_EVENT_TYPE_COLOR = { action: 'violet', @@ -45,46 +49,129 @@ const RESOURCE_TYPE_LABELS: Record = { other: 'Other', } -export function EventRow({ event }: { event: SdkEvent }) { - const [isCollapsed, setIsCollapsed] = useState(true) - const jsonRef = useRef(null) +export const EventRow = React.memo( + ({ + event, + columns, + facetRegistry, + onColumnsChange, + }: { + event: SdkEvent + columns: EventListColumn[] + facetRegistry: FacetRegistry + onColumnsChange: (newColumn: EventListColumn[]) => void + }) => { + const [isCollapsed, setIsCollapsed] = useState(true) + const jsonRef = useRef(null) + function getMenuItemsForPath(path: string) { + const newColumn: EventListColumn = { type: 'field', path } + if (!path || includesColumn(columns, newColumn)) { + return null + } + return ( + <> + { + onColumnsChange(addColumn(columns, newColumn)) + }} + > + Add column + + + ) + } + + return ( + + {columns.map((column, index): React.ReactElement => { + const isLast = index === columns.length - 1 + switch (column.type) { + case 'date': + return ( + + {formatDate(event.date)} + + ) + case 'description': + return ( + { + if (jsonRef.current?.contains(event.target as Node)) { + // Ignore clicks on the collapsible area + return + } + setIsCollapsed((previous) => !previous) + }} + > + + + + + + ) + case 'type': + return ( + + {isRumEvent(event) || isTelemetryEvent(event) ? ( + + {event.type} + + ) : ( + + {event.origin as string} {event.status as string} + + )} + + ) + case 'field': { + const value = facetRegistry.getFieldValueForEvent(event, column.path) + return ( + + {value !== undefined && ( + getMenuItemsForPath(path ? `${column.path}.${path}` : column.path)} + formatValue={(path, value) => formatValue(path ? `${column.path}.${path}` : column.path, value)} + /> + )} + + ) + } + } + })} + + ) + } +) + +function Cell({ isLast, ...props }: BoxProps & ComponentPropsWithoutRef<'div'> & { isLast: boolean }) { return ( { - if (jsonRef.current?.contains(event.target as Node)) { - // Ignore clicks on the collapsible area - return - } - setIsCollapsed((previous) => !previous) - }} - sx={{ cursor: 'pointer' }} - > - {formatDate(event.date)} - - {isRumEvent(event) || isTelemetryEvent(event) ? ( - - {event.type} - - ) : ( - - {event.origin as string} {event.status as string} - - )} - - - - - - - - + {...props} + component="td" + colSpan={isLast ? 2 : 1} + sx={[ + { + verticalAlign: 'top', + }, + ...(Array.isArray(props.sx) ? props.sx : [props.sx]), + ]} + /> ) } diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventsList.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventsList.tsx index 4248801cd3..07afce80da 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventsList.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventsList.tsx @@ -1,19 +1,253 @@ -import { Table } from '@mantine/core' -import React from 'react' -import type { EventFilters } from '../../../hooks/useEvents' +import { Popover, Box, Text, Button, Flex, Autocomplete, Table, useMantineTheme, CloseButton } from '@mantine/core' +import type { ForwardedRef, ReactNode } from 'react' +import React, { useMemo, useRef, useState, forwardRef } from 'react' +import type { EventFilters, FacetRegistry } from '../../../hooks/useEvents' import type { SdkEvent } from '../../../sdkEvent' import { isRumViewEvent } from '../../../sdkEvent' +import { BORDER_RADIUS, separatorBorder } from '../../../uiUtils' +import type { EventListColumn } from './columnUtils' +import { removeColumn, getColumnTitle, DEFAULT_COLUMNS, includesColumn } from './columnUtils' import { EventRow } from './eventRow' +import { ColumnDrag } from './columnDrag' -export function EventsList({ events, filters }: { events: SdkEvent[]; filters: EventFilters }) { +export function EventsList({ + events, + filters, + facetRegistry, + columns, + onColumnsChange, +}: { + events: SdkEvent[] + filters: EventFilters + facetRegistry: FacetRegistry + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void +}) { + const headerRowRef = useRef(null) + const theme = useMantineTheme() + + return ( + + + + + {columns.map((column) => ( + + ))} + + + + + + + + {events.map((event) => ( + + ))} + +
+ + +
+ ) +} + +function ColumnHeader({ + columns, + column, + onColumnsChange, +}: { + columns: EventListColumn[] + column: EventListColumn + onColumnsChange: (columns: EventListColumn[]) => void +}) { return ( - - - {events.map((event) => ( - - ))} - -
+ + + + {getColumnTitle(column)} + onColumnsChange(removeColumn(columns, column))} /> + + + + ) +} + +function AddColumnPopover({ + columns, + onColumnsChange, + facetRegistry, +}: { + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void + facetRegistry: FacetRegistry +}) { + return ( + + + + + ({ background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white })} + > + + {DEFAULT_COLUMNS.map((column) => ( + + ))} + + + + + ) +} + +function AddDefaultColumnButton({ + column, + columns, + onColumnsChange, +}: { + column: EventListColumn + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void +}) { + if (includesColumn(columns, column)) { + return null + } + return ( + + {getColumnTitle(column)} + + + ) +} + +function AddFieldColumn({ + columns, + onColumnsChange, + facetRegistry, +}: { + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void + facetRegistry: FacetRegistry +}) { + const [input, setInput] = useState('') + + function addFieldColumn(path: string) { + const newColumn: EventListColumn = { path, type: 'field' } + if (!includesColumn(columns, newColumn)) { + onColumnsChange(columns.concat(newColumn)) + } + } + + const allPaths = useMemo( + () => + Array.from(facetRegistry.getAllFieldPaths()).sort((a, b) => { + // Sort private fields last + if (a.startsWith('_dd') !== b.startsWith('_dd')) { + if (a.startsWith('_dd')) { + return 1 + } + if (b.startsWith('_dd')) { + return -1 + } + } + return a < b ? -1 : 1 + }), + [] + ) + + const suggestions = allPaths.filter((path) => path.includes(input)) + + return ( + + { + event.preventDefault() + addFieldColumn(input) + }} + sx={{ display: 'contents' }} + > + ) => { + const inputIndex = value.indexOf(input) + let renderedValue: ReactNode + if (inputIndex < 0) { + renderedValue = value + } else { + renderedValue = ( + <> + {value.slice(0, inputIndex)} + + {value.slice(inputIndex, inputIndex + input.length)} + + {value.slice(inputIndex + input.length)} + + ) + } + + return ( + + {renderedValue} + + ) + })} + onItemSubmit={({ value }) => addFieldColumn(value)} + /> + + + ) } diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventsTab.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventsTab.tsx index 80820e96f7..600b455768 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventsTab.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventsTab.tsx @@ -1,26 +1,52 @@ import React from 'react' +import { Center, Text } from '@mantine/core' import type { EventFilters, FacetRegistry } from '../../../hooks/useEvents' import { TabBase } from '../../tabBase' import type { SdkEvent } from '../../../sdkEvent' import { EventsTabTop } from './eventsTabTop' import { EventsList } from './eventsList' import { EventsTabSide } from './eventsTabSide' +import type { EventListColumn } from './columnUtils' interface EventsTabProps { events: SdkEvent[] facetRegistry: FacetRegistry | undefined filters: EventFilters - onFiltered: (filters: EventFilters) => void + onFiltersChange: (filters: EventFilters) => void + columns: EventListColumn[] + onColumnsChange: (columns: EventListColumn[]) => void clear: () => void } -export function EventsTab({ events, facetRegistry, filters, onFiltered, clear }: EventsTabProps) { +export function EventsTab({ + events, + facetRegistry, + filters, + onFiltersChange, + columns, + onColumnsChange, + clear, +}: EventsTabProps) { return ( } - leftSide={} + top={} + leftSide={} > - + {events.length === 0 || !facetRegistry ? ( +
+ + No events + +
+ ) : ( + + )}
) } diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventsTabSide.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventsTabSide.tsx index 09aea3150e..7270173426 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventsTabSide.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventsTabSide.tsx @@ -6,18 +6,18 @@ import { FacetList } from './facetList' export function EventsTabSide({ facetRegistry, filters, - onFiltered, + onFiltersChange, }: { facetRegistry?: FacetRegistry filters: EventFilters - onFiltered: (filters: EventFilters) => void + onFiltersChange: (filters: EventFilters) => void }) { return ( onFiltered({ ...filters, outdatedVersions: !e.target.checked })} + onChange={(e) => onFiltersChange({ ...filters, outdatedVersions: !e.target.checked })} mb="sm" /> @@ -26,7 +26,7 @@ export function EventsTabSide({ facetRegistry={facetRegistry} excludedFacetValues={filters.excludedFacetValues} onExcludedFacetValuesChange={(newExcludedFacetValues) => - onFiltered({ ...filters, excludedFacetValues: newExcludedFacetValues }) + onFiltersChange({ ...filters, excludedFacetValues: newExcludedFacetValues }) } /> )} diff --git a/developer-extension/src/panel/components/tabs/eventsTab/eventsTabTop.tsx b/developer-extension/src/panel/components/tabs/eventsTab/eventsTabTop.tsx index 578f33ef29..2fa58ed6c9 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/eventsTabTop.tsx +++ b/developer-extension/src/panel/components/tabs/eventsTab/eventsTabTop.tsx @@ -5,11 +5,11 @@ import type { EventFilters } from '../../../hooks/useEvents' export function EventsTabTop({ filters, - onFiltered, + onFiltersChange, clear, }: { filters: EventFilters - onFiltered: (filters: EventFilters) => void + onFiltersChange: (filters: EventFilters) => void clear: () => void }) { return ( @@ -18,7 +18,7 @@ export function EventsTabTop({ placeholder="Filter your events, syntax: 'type:view application.id:40d8ca4b'" value={filters.query} style={{ flexGrow: 1 }} - onChange={(event) => onFiltered({ ...filters, query: event.currentTarget.value })} + onChange={(event) => onFiltersChange({ ...filters, query: event.currentTarget.value })} className="dd-privacy-mask" /> diff --git a/developer-extension/src/panel/components/tabs/eventsTab/index.ts b/developer-extension/src/panel/components/tabs/eventsTab/index.ts index 09950fc976..72bbd35fc9 100644 --- a/developer-extension/src/panel/components/tabs/eventsTab/index.ts +++ b/developer-extension/src/panel/components/tabs/eventsTab/index.ts @@ -1 +1,2 @@ export { EventsTab } from './eventsTab' +export { DEFAULT_COLUMNS } from './columnUtils' diff --git a/developer-extension/src/panel/hooks/useEvents/facetRegistry.ts b/developer-extension/src/panel/hooks/useEvents/facetRegistry.ts index 2887688605..7320cb0893 100644 --- a/developer-extension/src/panel/hooks/useEvents/facetRegistry.ts +++ b/developer-extension/src/panel/hooks/useEvents/facetRegistry.ts @@ -11,6 +11,7 @@ type EventFields = Map export class FacetRegistry { facetValueCounts: FacetValueCounts = new Map() eventFieldsCache: WeakMap = new WeakMap() + allEventFieldPaths: Set = new Set() addEvent(event: SdkEvent) { const fields = getAllFields(event) @@ -18,6 +19,9 @@ export class FacetRegistry { this.eventFieldsCache.set(event, fields) incrementFacetValueCounts(fields, this.facetValueCounts) + fields.forEach((_value, fieldPath) => { + this.allEventFieldPaths.add(fieldPath) + }) } getFieldValueForEvent(event: SdkEvent, fieldPath: FieldPath): FieldMultiValue | undefined { @@ -28,6 +32,10 @@ export class FacetRegistry { return this.facetValueCounts.get(fieldPath) || new Map() } + getAllFieldPaths() { + return this.allEventFieldPaths + } + clear() { this.facetValueCounts.clear() } @@ -130,7 +138,7 @@ export function getAllFields(event: object) { * Add the value to the fields map. If a value is already defined for the given path, store it as * an array to reflect all possible values for that path. */ - function pushField(path: string, value: FieldValue) { + function pushField(path: FieldPath, value: FieldValue) { const previousValue = fields.get(path) if (Array.isArray(previousValue)) { previousValue.push(value) diff --git a/developer-extension/src/panel/uiUtils.ts b/developer-extension/src/panel/uiUtils.ts index 4aa8f0eb58..f963d10307 100644 --- a/developer-extension/src/panel/uiUtils.ts +++ b/developer-extension/src/panel/uiUtils.ts @@ -4,12 +4,18 @@ export const BORDER_RADIUS = 8 // arbitrary export const CHECKBOX_WIDTH = 20 // the Mantine checkbox component happen to be 20px wide export const TABS_LIST_BORDER_WIDTH = 2 // the Mantine tabs list happen to have a 2px border +export function separatorBorder(theme: MantineTheme) { + return `1px solid ${borderColor(theme)}` +} + /** * Returns the same CSS border as the mantine TabsList */ export function tabsListBorder(theme: MantineTheme) { // https://github.com/mantinedev/mantine/blob/cf0f85faec56615ea5fbd7813e83bac60dbaefb7/src/mantine-core/src/Tabs/TabsList/TabsList.styles.ts#L25-L26 - return `${TABS_LIST_BORDER_WIDTH}px solid ${ - theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3] - }` + return `${TABS_LIST_BORDER_WIDTH}px solid ${borderColor(theme)}` +} + +function borderColor(theme: MantineTheme) { + return theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[3] }