0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-24 09:14:46 +01:00

chore: replace tooltip (#20160)

This commit is contained in:
David Newell 2024-02-22 12:43:51 +00:00 committed by GitHub
parent 2e0a14a2a6
commit 6cd02e980a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
83 changed files with 749 additions and 658 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -31,10 +31,7 @@ export function SidePanelPaneHeader({ children, title }: SidePanelPaneHeaderProp
</h3>
) : null}
{children}
<Tooltip
placement={modalMode ? 'top' : 'bottomRight'}
title={modalMode ? 'Close' : 'Close this side panel'}
>
<Tooltip placement={modalMode ? 'top' : 'bottom-end'} title={modalMode ? 'Close' : 'Close this side panel'}>
<LemonButton size="small" sideIcon={<IconX />} onClick={() => closeSidePanel()} />
</Tooltip>
</header>

View File

@ -40,7 +40,7 @@ const NOTICES: {
You can now find them in <b>Data Management</b>
</>
),
placement: 'rightBottom',
placement: 'bottom-end',
flagSuffix: 'annotations-2023-10-30',
},
{
@ -52,7 +52,7 @@ const NOTICES: {
You can now find them in <b>People</b>
</>
),
placement: 'rightTop',
placement: 'top-end',
flagSuffix: 'cohorts-2023-10-30',
},
]

View File

@ -65,7 +65,9 @@ const DashboardRelationRow = ({
</Link>
{isPrimary && (
<Tooltip title="Primary dashboards are shown on the project home page">
<IconCottage className="text-warning text-base" />
<span className="flex items-center">
<IconCottage className="text-warning text-base" />
</span>
</Tooltip>
)}
<span className="grow" />

View File

@ -105,7 +105,7 @@ export function InsightMeta({
{loading && (
<Tooltip
title="This insight is queued to check for newer results. It will be updated soon."
placement="topRight"
placement="top-end"
>
<span className="text-primary text-sm font-medium">
<Spinner className="mx-1" />

View File

@ -1,5 +1,5 @@
import { hide } from '@floating-ui/react'
import { IconLock } from '@posthog/icons'
import { IconInfo, IconLock } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonDivider } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { ActionPopoverInfo } from 'lib/components/DefinitionPopover/ActionPopoverInfo'
@ -14,7 +14,7 @@ import {
TaxonomicFilterGroup,
TaxonomicFilterGroupType,
} from 'lib/components/TaxonomicFilter/types'
import { IconInfo, IconOpenInNew } from 'lib/lemon-ui/icons'
import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea'
import { Link } from 'lib/lemon-ui/Link'
import { Popover } from 'lib/lemon-ui/Popover'

View File

@ -204,7 +204,9 @@ export function EditableField({
<div className="EditableField__actions">
{markdown && (
<Tooltip title="Markdown formatting support">
<IconMarkdown className="text-muted text-2xl" />
<span className="flex items-center">
<IconMarkdown className="text-muted text-2xl" />
</span>
</Tooltip>
)}
<LemonButton
@ -243,7 +245,7 @@ export function EditableField({
) : (
<Tooltip
title={isDisplayTooltipNeeded ? localTentativeValue : undefined}
placement="bottomLeft"
placement="bottom-start"
delayMs={0}
>
<span className="EditableField__display" ref={displayRef}>
@ -273,10 +275,12 @@ export function EditableField({
</Tooltip>
{!isEditing && notice && (
<Tooltip title={notice.tooltip} placement="right">
{React.cloneElement(notice.icon, {
...notice.icon.props,
className: clsx(notice.icon.props.className, 'EditableField__notice'),
})}
<span className="flex items-center">
{React.cloneElement(notice.icon, {
...notice.icon.props,
className: clsx(notice.icon.props.className, 'EditableField__notice'),
})}
</span>
</Tooltip>
)}
</div>

View File

@ -1,6 +1,7 @@
import { useMountedLogic } from 'kea'
import { LemonButton, LemonButtonProps, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { forwardRef } from 'react'
import { sidePanelExportsLogic } from '~/layout/navigation-3000/sidepanel/panels/exports/sidePanelExportsLogic'
import { sidePanelLogic } from '~/layout/navigation-3000/sidepanel/sidePanelLogic'
@ -20,69 +21,72 @@ export interface ExportButtonProps extends Pick<LemonButtonProps, 'icon' | 'type
items: ExportButtonItem[]
}
export function ExportButton({ items, ...buttonProps }: ExportButtonProps): JSX.Element {
useMountedLogic(sidePanelLogic)
useMountedLogic(sidePanelExportsLogic)
export const ExportButton: React.FunctionComponent<ExportButtonProps & React.RefAttributes<HTMLButtonElement>> =
forwardRef(function ExportButton({ items, ...buttonProps }, ref): JSX.Element {
useMountedLogic(sidePanelLogic)
useMountedLogic(sidePanelExportsLogic)
const { actions } = sidePanelLogic
const { loadExports } = sidePanelExportsLogic.actions
const { actions } = sidePanelLogic
const { loadExports } = sidePanelExportsLogic.actions
const onExportClick = async (triggerExportProps: TriggerExportProps): Promise<void> => {
actions.openSidePanel(SidePanelTab.Exports)
loadExports()
await triggerExport(triggerExportProps)
loadExports()
}
const onExportClick = async (triggerExportProps: TriggerExportProps): Promise<void> => {
actions.openSidePanel(SidePanelTab.Exports)
loadExports()
await triggerExport(triggerExportProps)
loadExports()
}
return (
<LemonButtonWithDropdown
data-attr="export-button"
{...buttonProps}
dropdown={{
actionable: true,
placement: 'right-start',
closeParentPopoverOnClickInside: true,
overlay: (
<>
<h5>File type</h5>
<LemonDivider />
{items.map(({ title, ...triggerExportProps }, i) => {
const exportFormatExtension = triggerExportProps.export_format.split('/').pop()
return (
<LemonButtonWithDropdown
ref={ref}
data-attr="export-button"
{...buttonProps}
dropdown={{
actionable: true,
placement: 'right-start',
closeParentPopoverOnClickInside: true,
overlay: (
<>
<h5>File type</h5>
<LemonDivider />
{items.map(({ title, ...triggerExportProps }, i) => {
const exportFormatExtension = triggerExportProps.export_format.split('/').pop()
let target: string
let exportBody: string = ''
if (triggerExportProps.insight) {
target = `insight-${triggerExportProps.insight}`
} else if (triggerExportProps.dashboard) {
target = `dashboard-${triggerExportProps.dashboard}`
} else if ('path' in (triggerExportProps.export_context || {})) {
target = (triggerExportProps.export_context as OnlineExportContext)?.path || 'unknown'
exportBody =
(triggerExportProps.export_context as OnlineExportContext)?.body || 'unknown'
} else {
target = 'unknown'
}
let target: string
let exportBody: string = ''
if (triggerExportProps.insight) {
target = `insight-${triggerExportProps.insight}`
} else if (triggerExportProps.dashboard) {
target = `dashboard-${triggerExportProps.dashboard}`
} else if ('path' in (triggerExportProps.export_context || {})) {
target =
(triggerExportProps.export_context as OnlineExportContext)?.path || 'unknown'
exportBody =
(triggerExportProps.export_context as OnlineExportContext)?.body || 'unknown'
} else {
target = 'unknown'
}
return (
<LemonButton
key={i}
fullWidth
onClick={() => void onExportClick(triggerExportProps)}
data-attr={`export-button-${exportFormatExtension}`}
data-ph-capture-attribute-export-target={target}
data-ph-capture-attribute-export-body={
exportBody.length ? JSON.stringify(exportBody) : null
}
>
{title ? title : `.${exportFormatExtension}`}
</LemonButton>
)
})}
</>
),
}}
>
Export
</LemonButtonWithDropdown>
)
}
return (
<LemonButton
key={i}
fullWidth
onClick={() => void onExportClick(triggerExportProps)}
data-attr={`export-button-${exportFormatExtension}`}
data-ph-capture-attribute-export-target={target}
data-ph-capture-attribute-export-body={
exportBody.length ? JSON.stringify(exportBody) : null
}
>
{title ? title : `.${exportFormatExtension}`}
</LemonButton>
)
})}
</>
),
}}
>
Export
</LemonButtonWithDropdown>
)
})

View File

@ -1,8 +1,8 @@
import { IconInfo } from '@posthog/icons'
import { LemonDivider } from '@posthog/lemon-ui'
import { Properties } from '@posthog/plugin-scaffold'
import { useActions, useValues } from 'kea'
import { PropertiesTable } from 'lib/components/PropertiesTable'
import { IconInfo } from 'lib/lemon-ui/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { humanList } from 'lib/utils'
import { teamLogic } from 'scenes/teamLogic'

View File

@ -1,4 +1,5 @@
import { IconCohort, IconPerson, IconUnverifiedEvent } from 'lib/lemon-ui/icons'
import { IconPerson } from '@posthog/icons'
import { IconCohort, IconUnverifiedEvent } from 'lib/lemon-ui/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { PropertyFilterType } from '~/types'
@ -9,7 +10,9 @@ export function PropertyFilterIcon({ type }: { type?: PropertyFilterType }): JSX
case 'event':
iconElement = (
<Tooltip title="Event property">
<IconUnverifiedEvent />
<span className="flex items-center">
<IconUnverifiedEvent />
</span>
</Tooltip>
)
break
@ -23,7 +26,9 @@ export function PropertyFilterIcon({ type }: { type?: PropertyFilterType }): JSX
case 'cohort':
iconElement = (
<Tooltip title="Cohort filter">
<IconCohort />
<span className="flex items-center">
<IconCohort />
</span>
</Tooltip>
)
break

View File

@ -70,7 +70,7 @@ export function RestrictedArea({
const restrictionReason = useRestrictedArea({ minimumAccessLevel, scope })
return restrictionReason ? (
<Tooltip title={restrictionReason} placement="topLeft" delayMs={0}>
<Tooltip title={restrictionReason} placement="top-start" delayMs={0}>
<span>
<Component isRestricted={true} restrictionReason={restrictionReason} />
</span>

View File

@ -1,5 +1,6 @@
import './TaxonomicFilter.scss'
import { IconKeyboard } from '@posthog/icons'
import clsx from 'clsx'
import { BindLogic, useActions, useValues } from 'kea'
import {
@ -7,7 +8,6 @@ import {
TaxonomicFilterLogicProps,
TaxonomicFilterProps,
} from 'lib/components/TaxonomicFilter/types'
import { IconKeyboard } from 'lib/lemon-ui/icons'
import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { useEffect, useMemo, useRef } from 'react'

View File

@ -6,7 +6,6 @@ import { Dayjs } from 'lib/dayjs'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { humanFriendlyDetailedTime, pluralize } from 'lib/utils'
import { AlignType } from 'rc-trigger/lib/interface'
export interface TimelinePoint {
timestamp: Dayjs
@ -23,25 +22,6 @@ export interface TimelineSeekbarProps {
className?: string
}
const SEEKBAR_TOOLTIP_PLACEMENTS: Record<string, AlignType> = {
topRight: {
points: ['br', 'tr'],
offset: [7, 0], // To align with badges
overflow: {
adjustX: 0,
adjustY: 0,
},
},
topLeft: {
points: ['bl', 'tl'],
offset: [-7, 0], // To align with badges
overflow: {
adjustX: 0,
adjustY: 0,
},
},
}
export function TimelineSeekbar({
points,
note,
@ -80,8 +60,8 @@ export function TimelineSeekbar({
"This data point's range hasn't been loaded yet"
)
}
placement="topLeft"
builtinPlacements={SEEKBAR_TOOLTIP_PLACEMENTS}
placement="top-start"
offset={0}
delayMs={0}
>
<div className="TimelineSeekbar__line-start" />
@ -101,8 +81,8 @@ export function TimelineSeekbar({
"This data point's range hasn't been loaded yet"
)
}
placement="topRight"
builtinPlacements={SEEKBAR_TOOLTIP_PLACEMENTS}
placement="top-end"
offset={0}
delayMs={0}
>
<div
@ -128,8 +108,8 @@ export function TimelineSeekbar({
{pluralize(count, 'relevant event')} with such properties
</span>
}
placement="topLeft"
builtinPlacements={SEEKBAR_TOOLTIP_PLACEMENTS}
placement="top-start"
arrowOffset={7}
delayMs={0}
>
<div

View File

@ -2,6 +2,7 @@ import './LemonBadge.scss'
import clsx from 'clsx'
import { compactNumber, humanFriendlyNumber } from 'lib/utils'
import { forwardRef } from 'react'
import { CSSTransition } from 'react-transition-group'
interface LemonBadgePropsBase {
@ -27,35 +28,41 @@ export interface LemonBadgeNumberProps extends LemonBadgePropsBase {
}
/** An icon-sized badge. */
export function LemonBadge({
content,
visible = true,
size = 'medium',
position = 'none',
className,
status = 'primary',
active = false,
...spanProps
}: LemonBadgeProps): JSX.Element {
return (
<CSSTransition in={visible} timeout={150} classNames="LemonBadge-" mountOnEnter unmountOnExit>
<span
className={clsx(
'LemonBadge',
!content && 'LemonBadge--dot',
`LemonBadge--${size}`,
`LemonBadge--${status}`,
`LemonBadge--position-${position}`,
active && 'LemonBadge--active',
className
)}
{...spanProps}
>
{content}
</span>
</CSSTransition>
)
}
const LemonBadgeComponent: React.FunctionComponent<LemonBadgeProps & React.RefAttributes<HTMLSpanElement>> = forwardRef(
function LemonBadgeComponent(
{
content,
visible = true,
size = 'medium',
position = 'none',
className,
status = 'primary',
active = false,
...spanProps
},
ref
): JSX.Element {
return (
<CSSTransition in={visible} timeout={150} classNames="LemonBadge-" mountOnEnter unmountOnExit>
<span
ref={ref}
className={clsx(
'LemonBadge',
!content && 'LemonBadge--dot',
`LemonBadge--${size}`,
`LemonBadge--${status}`,
`LemonBadge--position-${position}`,
active && 'LemonBadge--active',
className
)}
{...spanProps}
>
{content}
</span>
</CSSTransition>
)
}
)
/** An icon-sized badge for displaying a count.
*
@ -63,37 +70,34 @@ export function LemonBadge({
* JSX elements are rendered outright to support use cases where the badge is meant to show an icon.
* If `showZero` is set to `true`, the component won't be hidden if the count is 0.
*/
function LemonBadgeNumber({
count,
maxDigits = 1,
showZero = false,
...badgeProps
}: LemonBadgeNumberProps): JSX.Element {
if (maxDigits < 1) {
throw new Error('maxDigits must be at least 1')
}
const LemonBadgeNumber: React.FunctionComponent<LemonBadgeNumberProps & React.RefAttributes<HTMLSpanElement>> =
forwardRef(function LemonBadgeNumber({ count, maxDigits = 1, showZero = false, ...badgeProps }, ref): JSX.Element {
if (maxDigits < 1) {
throw new Error('maxDigits must be at least 1')
}
// NOTE: We use 1 for the text if not showing so the fade out animation looks right
const text =
typeof count === 'object'
? count
: typeof count === 'number' && count !== 0
? count < Math.pow(10, maxDigits)
? compactNumber(count)
: `${'9'.repeat(maxDigits)}+`
: showZero
? '0'
: '1'
const hide = count === undefined || (count == 0 && !showZero)
// NOTE: We use 1 for the text if not showing so the fade out animation looks right
const text =
typeof count === 'object'
? count
: typeof count === 'number' && count !== 0
? count < Math.pow(10, maxDigits)
? compactNumber(count)
: `${'9'.repeat(maxDigits)}+`
: showZero
? '0'
: '1'
const hide = count === undefined || (count == 0 && !showZero)
return (
<LemonBadge
visible={!hide}
title={typeof count === 'number' ? humanFriendlyNumber(count) : undefined}
content={text}
{...badgeProps}
/>
)
}
return (
<LemonBadge
ref={ref}
visible={!hide}
title={typeof count === 'number' ? humanFriendlyNumber(count) : undefined}
content={text}
{...badgeProps}
/>
)
})
LemonBadge.Number = LemonBadgeNumber
export const LemonBadge = Object.assign(LemonBadgeComponent, { Number: LemonBadgeNumber })

View File

@ -68,7 +68,7 @@ export function LemonCheckbox({
}, [checked, indeterminate])
return (
<Tooltip title={disabledReason ? <i>{disabledReason}</i> : null} placement="topLeft">
<Tooltip title={disabledReason ? <i>{disabledReason}</i> : null} placement="top-start">
<span
className={clsx(
'LemonCheckbox',

View File

@ -1,7 +1,7 @@
import './LemonLabel.scss'
import { IconInfo } from '@posthog/icons'
import clsx from 'clsx'
import { IconInfo } from 'lib/lemon-ui/icons'
import { Link, LinkProps } from '../Link'
import { Tooltip } from '../Tooltip'

View File

@ -1,4 +1,5 @@
import clsx from 'clsx'
import { forwardRef } from 'react'
export type LemonProgressProps = {
size?: 'medium' | 'large'
@ -8,33 +9,32 @@ export type LemonProgressProps = {
className?: string
}
export const LemonProgress = ({
size = 'medium',
percent,
strokeColor = 'var(--brand-blue)',
children,
className,
}: LemonProgressProps): JSX.Element => {
const width = isNaN(percent) ? 0 : Math.max(Math.min(percent, 100), 0)
export const LemonProgress: React.FunctionComponent<LemonProgressProps & React.RefAttributes<HTMLDivElement>> =
forwardRef(function LemonProgress(
{ size = 'medium', percent, strokeColor = 'var(--brand-blue)', children, className },
ref
): JSX.Element {
const width = isNaN(percent) ? 0 : Math.max(Math.min(percent, 100), 0)
return (
<div
className={clsx(
'LemonProgress rounded-full w-full inline-block bg-bg-3000',
size === 'large' ? 'h-5' : 'h-1.5',
className
)}
>
<span
return (
<div
ref={ref}
className={clsx(
'LemonProgress__track block h-full rounded-full transition-all',
width > 0 ? (size === 'large' ? 'min-w-5' : 'min-w-1.5') : null
'LemonProgress rounded-full w-full inline-block bg-bg-3000',
size === 'large' ? 'h-5' : 'h-1.5',
className
)}
// eslint-disable-next-line react/forbid-dom-props
style={{ width: `${width}%`, backgroundColor: strokeColor }}
>
{children}
</span>
</div>
)
}
<span
className={clsx(
'LemonProgress__track block h-full rounded-full transition-all',
width > 0 ? (size === 'large' ? 'min-w-5' : 'min-w-1.5') : null
)}
// eslint-disable-next-line react/forbid-dom-props
style={{ width: `${width}%`, backgroundColor: strokeColor }}
>
{children}
</span>
</div>
)
})

View File

@ -50,7 +50,7 @@ export function LemonRadio<T extends React.Key>({
if (option.disabledReason) {
return (
<Tooltip trigger="hover" key={option.value} title={option.disabledReason}>
<Tooltip key={option.value} title={option.disabledReason}>
{content}
</Tooltip>
)

View File

@ -1,6 +1,7 @@
import { IconX } from '@posthog/icons'
import { LemonButton } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { forwardRef } from 'react'
export interface LemonSnackProps {
type?: 'regular' | 'pill'
@ -13,51 +14,46 @@ export interface LemonSnackProps {
'data-attr'?: string
}
export function LemonSnack({
type = 'regular',
children,
wrap,
onClick,
onClose,
title,
className,
}: LemonSnackProps): JSX.Element {
const isRegular = type === 'regular'
const isClickable = !!onClick
const bgColor = isRegular ? 'primary-highlight' : 'primary-alt-highlight'
return (
<span
className={clsx(
'LemonSnack',
!isRegular && 'LemonSnack--pill',
`inline-flex text-primary-alt max-w-full overflow-hidden break-all items-center py-1 bg-${bgColor}`,
!wrap && 'whitespace-nowrap',
isRegular ? 'px-1.5 rounded' : 'px-4 rounded-full h-8',
isClickable && 'cursor-pointer',
className
)}
onClick={onClick}
>
export const LemonSnack: React.FunctionComponent<LemonSnackProps & React.RefAttributes<HTMLSpanElement>> = forwardRef(
function LemonSnack({ type = 'regular', children, wrap, onClick, onClose, title, className }, ref): JSX.Element {
const isRegular = type === 'regular'
const isClickable = !!onClick
const bgColor = isRegular ? 'primary-highlight' : 'primary-alt-highlight'
return (
<span
className="overflow-hidden text-ellipsis"
title={title ?? (typeof children === 'string' ? children : undefined)}
ref={ref}
className={clsx(
'LemonSnack',
!isRegular && 'LemonSnack--pill',
`inline-flex text-primary-alt max-w-full overflow-hidden break-all items-center py-1 bg-${bgColor}`,
!wrap && 'whitespace-nowrap',
isRegular ? 'px-1.5 rounded' : 'px-4 rounded-full h-8',
isClickable && 'cursor-pointer',
className
)}
onClick={onClick}
>
{children}
</span>
{onClose && (
<span className={clsx('LemonSnack__close shrink-0 ml-1', isRegular || '-mr-1')}>
<LemonButton
size="small"
noPadding
icon={<IconX />}
onClick={(event) => {
event.stopPropagation()
onClose()
}}
/>
<span
className="overflow-hidden text-ellipsis"
title={title ?? (typeof children === 'string' ? children : undefined)}
>
{children}
</span>
)}
</span>
)
}
{onClose && (
<span className={clsx('LemonSnack__close shrink-0 ml-1', isRegular || '-mr-1')}>
<LemonButton
size="small"
noPadding
icon={<IconX />}
onClick={(event) => {
event.stopPropagation()
onClose()
}}
/>
</span>
)}
</span>
)
}
)

View File

@ -2,7 +2,7 @@ import './LemonSwitch.scss'
import clsx from 'clsx'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { useMemo, useState } from 'react'
import { forwardRef, useMemo, useState } from 'react'
export interface LemonSwitchProps {
className?: string
@ -25,84 +25,90 @@ export interface LemonSwitchProps {
/** Counter used for collision-less automatic switch IDs. */
let switchCounter = 0
export function LemonSwitch({
className,
id: rawId,
onChange,
checked,
fullWidth,
bordered,
disabled,
disabledReason,
label,
labelClassName,
tooltip,
'data-attr': dataAttr,
'aria-label': ariaLabel,
handleContent,
}: LemonSwitchProps): JSX.Element {
const id = useMemo(() => rawId || `lemon-switch-${switchCounter++}`, [rawId])
const [isActive, setIsActive] = useState(false)
export const LemonSwitch: React.FunctionComponent<LemonSwitchProps & React.RefAttributes<HTMLDivElement>> = forwardRef(
function LemonSwitch(
{
className,
id: rawId,
onChange,
checked,
fullWidth,
bordered,
disabled,
disabledReason,
label,
labelClassName,
tooltip,
'data-attr': dataAttr,
'aria-label': ariaLabel,
handleContent,
},
ref
): JSX.Element {
const id = useMemo(() => rawId || `lemon-switch-${switchCounter++}`, [rawId])
const [isActive, setIsActive] = useState(false)
const conditionalProps = {}
if (ariaLabel) {
conditionalProps['aria-label'] = ariaLabel
}
const conditionalProps = {}
if (ariaLabel) {
conditionalProps['aria-label'] = ariaLabel
}
let tooltipContent: JSX.Element | null = null
if (disabledReason) {
disabled = true // Support `disabledReason` while maintaining compatibility with `disabled`
tooltipContent = <span className="italic">{disabledReason}</span>
} else if (tooltip) {
tooltipContent = <span>{tooltip}</span>
}
let buttonComponent = (
<button
id={id}
className="LemonSwitch__button"
type="button"
role="switch"
onClick={() => {
if (onChange) {
onChange(!checked)
}
}}
onMouseDown={() => setIsActive(true)}
onMouseUp={() => setIsActive(false)}
onMouseOut={() => setIsActive(false)}
data-attr={dataAttr}
disabled={disabled}
{...conditionalProps}
>
<div className="LemonSwitch__slider" />
<div className="LemonSwitch__handle">{handleContent}</div>
</button>
)
if (tooltipContent) {
buttonComponent = (
<Tooltip title={tooltipContent}>
{/* wrap it in a div so that the tooltip works even when disabled */}
<div className="flex items-center">{buttonComponent}</div>
</Tooltip>
let tooltipContent: JSX.Element | null = null
if (disabledReason) {
disabled = true // Support `disabledReason` while maintaining compatibility with `disabled`
tooltipContent = <span className="italic">{disabledReason}</span>
} else if (tooltip) {
tooltipContent = <span>{tooltip}</span>
}
let buttonComponent = (
<button
id={id}
className="LemonSwitch__button"
type="button"
role="switch"
onClick={() => {
if (onChange) {
onChange(!checked)
}
}}
onMouseDown={() => setIsActive(true)}
onMouseUp={() => setIsActive(false)}
onMouseOut={() => setIsActive(false)}
data-attr={dataAttr}
disabled={disabled}
{...conditionalProps}
>
<div className="LemonSwitch__slider" />
<div className="LemonSwitch__handle">{handleContent}</div>
</button>
)
if (tooltipContent) {
buttonComponent = (
<Tooltip title={tooltipContent}>
{/* wrap it in a div so that the tooltip works even when disabled */}
<div className="flex items-center">{buttonComponent}</div>
</Tooltip>
)
}
return (
<div
ref={ref}
className={clsx('LemonSwitch', className, {
'LemonSwitch--checked': checked,
'LemonSwitch--active': isActive,
'LemonSwitch--bordered': bordered,
'LemonSwitch--disabled': disabled,
'LemonSwitch--full-width': fullWidth,
})}
>
{label && (
<label htmlFor={id} className={labelClassName}>
{label}
</label>
)}
{buttonComponent}
</div>
)
}
return (
<div
className={clsx('LemonSwitch', className, {
'LemonSwitch--checked': checked,
'LemonSwitch--active': isActive,
'LemonSwitch--bordered': bordered,
'LemonSwitch--disabled': disabled,
'LemonSwitch--full-width': fullWidth,
})}
>
{label && (
<label htmlFor={id} className={labelClassName}>
{label}
</label>
)}
{buttonComponent}
</div>
)
}
)

View File

@ -341,7 +341,6 @@ export function LemonTable<T extends Record<string, any>>({
: null
}
/>
{/* this non-breaking space lets antd's tooltip work*/}{' '}
</Tooltip>
)}
</div>

View File

@ -1,4 +1,5 @@
import { IconArrowDown, IconArrowUp, IconSort } from 'lib/lemon-ui/icons'
import { forwardRef } from 'react'
/** Sorting state. */
export interface Sorting {
@ -25,11 +26,13 @@ export function getNextSorting(
}
}
export function SortingIndicator({ order }: { order: Sorting['order'] | null }): JSX.Element {
export const SortingIndicator: React.FunctionComponent<
{ order: Sorting['order'] | null } & React.RefAttributes<HTMLDivElement>
> = forwardRef(function SortingIndicator({ order }, ref): JSX.Element {
return (
<div className="flex items-center text-base ml-2 whitespace-nowrap">
<div ref={ref} className="flex items-center text-base ml-2 whitespace-nowrap">
<IconSort />
{order === -1 ? <IconArrowDown /> : order === 1 ? <IconArrowUp /> : null}
</div>
)
}
})

View File

@ -1,7 +1,6 @@
import './LemonTabs.scss'
import clsx from 'clsx'
import { AlignType } from 'rc-trigger/lib/interface'
import { useSliderPositioning } from '../hooks'
import { IconInfo } from '../icons'
@ -37,18 +36,6 @@ interface LemonTabsCSSProperties extends React.CSSProperties {
'--lemon-tabs-slider-offset': `${number}px`
}
/** Custom tooltip placement so that it's is closely aligned with the tabs, instead of being distanced. */
const TAB_TOOLTIP_PLACEMENTS: Record<string, AlignType> = {
top: {
points: ['bc', 'tc'], // Bottom-center of tooltip aligned to top-center of target
offset: [0, 4], // This is the key change - positioning the tooltip lower to align arrow tip and top of tab
overflow: {
adjustX: 0,
adjustY: 0,
},
},
}
export function LemonTabs<T extends string | number>({
activeKey,
onChange,
@ -85,7 +72,7 @@ export function LemonTabs<T extends string | number>({
</>
)
return (
<Tooltip key={tab.key} title={tab.tooltip} builtinPlacements={TAB_TOOLTIP_PLACEMENTS}>
<Tooltip key={tab.key} title={tab.tooltip} placement="top" offset={0}>
<li
className={clsx('LemonTabs__tab', tab.key === activeKey && 'LemonTabs__tab--active')}
onClick={onChange ? () => onChange(tab.key) : undefined}

View File

@ -5,6 +5,7 @@ import clsx from 'clsx'
import { IconEllipsis } from 'lib/lemon-ui/icons'
import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton'
import { LemonButtonDropdown } from 'lib/lemon-ui/LemonButton'
import { forwardRef } from 'react'
export type LemonTagType =
| 'primary'
@ -31,51 +32,46 @@ export interface LemonTagProps extends React.HTMLAttributes<HTMLDivElement> {
popover?: LemonButtonDropdown
}
export function LemonTag({
type = 'default',
children,
className,
size = 'medium',
weight,
icon,
closable,
onClose,
popover,
...props
}: LemonTagProps): JSX.Element {
return (
<div
className={clsx(
'LemonTag',
`LemonTag--size-${size}`,
!!props.onClick && 'cursor-pointer',
`LemonTag--${type}`,
weight && `LemonTag--${weight}`,
className
)}
{...props}
>
{icon && <span className="LemonTag__icon">{icon}</span>}
{children}
{popover?.overlay && (
<LemonButtonWithDropdown
dropdown={popover}
size="small"
className="LemonTag__right-button"
icon={<IconEllipsis />}
onClick={(e) => {
e.stopPropagation()
}}
/>
)}
{closable && (
<LemonButton
icon={<IconX className="h-3.5 w-3.5" />}
onClick={onClose}
size="xsmall"
className="LemonTag__right-button"
/>
)}
</div>
)
}
export const LemonTag: React.FunctionComponent<LemonTagProps & React.RefAttributes<HTMLDivElement>> = forwardRef(
function LemonTag(
{ type = 'default', children, className, size = 'medium', weight, icon, closable, onClose, popover, ...props },
ref
): JSX.Element {
return (
<div
ref={ref}
className={clsx(
'LemonTag',
`LemonTag--size-${size}`,
!!props.onClick && 'cursor-pointer',
`LemonTag--${type}`,
weight && `LemonTag--${weight}`,
className
)}
{...props}
>
{icon && <span className="LemonTag__icon">{icon}</span>}
{children}
{popover?.overlay && (
<LemonButtonWithDropdown
dropdown={popover}
size="small"
className="LemonTag__right-button"
icon={<IconEllipsis />}
onClick={(e) => {
e.stopPropagation()
}}
/>
)}
{closable && (
<LemonButton
icon={<IconX className="h-3.5 w-3.5" />}
onClick={onClose}
size="xsmall"
className="LemonTag__right-button"
/>
)}
</div>
)
}
)

View File

@ -63,7 +63,9 @@ export const LemonTextAreaMarkdown = React.forwardRef<HTMLTextAreaElement, Lemon
) : (
<div className="text-muted inline-flex items-center space-x-1">
<Tooltip title="Enable object storage to add images by dragging and dropping.">
<IconTools className="text-xl mr-1" />
<span>
<IconTools className="text-xl mr-1" />
</span>
</Tooltip>
<span>
Add external images using{' '}

View File

@ -114,8 +114,7 @@ export const Popover = React.forwardRef<HTMLDivElement, PopoverProps>(function P
const {
x,
y,
reference,
refs: { reference: referenceRef, floating: floatingRef },
refs: { reference: referenceRef, floating: floatingRef, setReference },
strategy,
placement: effectivePlacement,
update,
@ -156,7 +155,7 @@ export const Popover = React.forwardRef<HTMLDivElement, PopoverProps>(function P
useLayoutEffect(() => {
if (referenceElement) {
reference(referenceElement)
setReference(referenceElement)
}
}, [referenceElement])

View File

@ -24,7 +24,7 @@ export function ProfileBubbles({ people, tooltip, limit = 6, ...divProps }: Prof
}
return (
<Tooltip title={tooltip} overlayStyle={{ maxWidth: 'none' }}>
<Tooltip title={tooltip}>
<div className={clsx('ProfileBubbles', !!divProps.onClick && 'cursor-pointer')} {...divProps}>
{shownPeople.map(({ email, name, title }, index) => (
<ProfilePicture

View File

@ -0,0 +1,4 @@
.Tooltip {
z-index: var(--z-tooltip);
box-shadow: var(--modal-shadow-elevation);
}

View File

@ -1,61 +1,142 @@
// eslint-disable-next-line no-restricted-imports
import { Tooltip as AntdTooltip } from 'antd'
import { TooltipProps as AntdTooltipProps } from 'antd/lib/tooltip'
import { useFloatingContainerContext } from 'lib/hooks/useFloatingContainerContext'
import React, { useState } from 'react'
import { useDebounce } from 'use-debounce'
import './Tooltip.scss'
const DEFAULT_DELAY_MS = 500
import {
arrow,
autoUpdate,
flip,
FloatingArrow,
FloatingPortal,
offset as offsetFunc,
Placement,
shift,
useDismiss,
useFloating,
useFocus,
useHover,
useInteractions,
useMergeRefs,
useRole,
useTransitionStyles,
} from '@floating-ui/react'
import clsx from 'clsx'
import React, { useRef, useState } from 'react'
export type TooltipProps = AntdTooltipProps & {
export interface TooltipProps {
title: string | React.ReactNode | (() => string)
children: JSX.Element
delayMs?: number
offset?: number
arrowOffset?: number
placement?: Placement
className?: string
visible?: boolean
}
/** Extension of Ant Design's Tooltip that enables a delay.
*
* Caveat: doesn't work with disabled elements due to lack of workaround that Ant Design uses.
* See https://github.com/ant-design/ant-design/blob/master/components/tooltip/index.tsx#L82-L130.
*/
// CAUTION: Any changes here will affect tooltips across the entire app.
export function Tooltip({ children, visible, delayMs = DEFAULT_DELAY_MS, ...props }: TooltipProps): JSX.Element {
const [localVisible, setVisible] = useState(false)
const [debouncedLocalVisible] = useDebounce(visible ?? localVisible, delayMs)
export function Tooltip({
children,
title,
className = '',
placement = 'top',
offset = 8,
arrowOffset,
delayMs = 500,
visible: controlledOpen,
}: TooltipProps): JSX.Element {
const [uncontrolledOpen, setUncontrolledOpen] = useState(false)
const caretRef = useRef(null)
const floatingContainer = useFloatingContainerContext()?.current
const open = controlledOpen ?? uncontrolledOpen
if (!('mouseEnterDelay' in props)) {
// If not preserving default behavior and mouseEnterDelay is not already provided, we use a custom default here
props.mouseEnterDelay = delayMs
}
const { context, refs } = useFloating({
placement,
open,
onOpenChange: setUncontrolledOpen,
whileElementsMounted: autoUpdate,
middleware: [
offsetFunc(offset),
flip({ fallbackAxisSideDirection: 'start' }),
shift(),
arrow({ element: caretRef }),
],
})
const hover = useHover(context, {
move: false,
delay: {
open: delayMs,
close: 0,
},
})
const focus = useFocus(context)
const dismiss = useDismiss(context)
const role = useRole(context, { role: 'tooltip' })
const { getFloatingProps, getReferenceProps } = useInteractions([hover, focus, dismiss, role])
const { styles: transitionStyles } = useTransitionStyles(context, {
duration: {
open: 150,
close: 0,
},
initial: ({ side }) => ({
opacity: 0,
transform: {
top: 'translateY(3px)',
bottom: 'translateY(-3px)',
left: 'translateX(3px)',
right: 'translateX(-3px)',
}[side],
}),
})
const childrenRef = (children as any).ref
const triggerRef = useMergeRefs([refs.setReference, childrenRef])
// If child is not a valid element (string or string + ReactNode, Fragment), antd wraps children in a span.
// See https://github.com/ant-design/ant-design/blob/master/components/tooltip/index.tsx#L226
const child = React.isValidElement(children) ? children : <span>{children}</span>
const derivedVisible = typeof visible === 'undefined' ? localVisible && debouncedLocalVisible : visible
const clonedChild = React.cloneElement(
child,
getReferenceProps({
ref: triggerRef,
...child.props,
})
)
return props.title ? (
<AntdTooltip
{...props}
getPopupContainer={floatingContainer ? () => floatingContainer : undefined}
visible={derivedVisible}
>
{React.cloneElement(child, {
onMouseEnter: () => {
child.props.onMouseEnter?.()
if (typeof visible === 'undefined') {
setVisible(true)
}
},
onMouseLeave: () => {
child.props.onMouseLeave?.()
if (typeof visible === 'undefined') {
setVisible(false)
}
},
})}
</AntdTooltip>
return title ? (
<>
{clonedChild}
{open && (
<FloatingPortal>
<div
ref={refs.setFloating}
className="Tooltip max-w-sm"
// eslint-disable-next-line react/forbid-dom-props
style={{ ...context.floatingStyles }}
{...getFloatingProps()}
>
<div
className={clsx(
'bg-tooltip-bg py-1.5 px-2 break-words rounded text-start text-white',
className
)}
// eslint-disable-next-line react/forbid-dom-props
style={{ ...transitionStyles }}
>
{typeof title === 'function' ? title() : title}
<FloatingArrow
ref={caretRef}
context={context}
width={8}
height={4}
staticOffset={arrowOffset}
fill="var(--tooltip-bg)"
/>
</div>
</div>
</FloatingPortal>
)}
</>
) : (
child
children
)
}

View File

@ -51,7 +51,7 @@ export function AndOrFilterSelect({
),
},
]}
optionTooltipPlacement={topLevelFilter ? 'bottomRight' : 'bottomLeft'}
optionTooltipPlacement={topLevelFilter ? 'bottom-end' : 'bottom-start'}
dropdownMatchSelectWidth={false}
/>
{value === FilterLogicalOperator.Or ? suffix[0] : suffix[1]}

View File

@ -1,4 +1,4 @@
import { IconInfo } from 'lib/lemon-ui/icons'
import { IconInfo } from '@posthog/icons'
import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
@ -49,7 +49,7 @@ export function PersonsSearch({ query, setQuery }: PersonSearchProps): JSX.Eleme
disabled={!setQuery}
onChange={onChange}
/>
<Tooltip title={<>{labels[target].description}</>}>
<Tooltip title={labels[target].description}>
<IconInfo className="text-2xl text-muted-alt shrink-0" />
</Tooltip>
</div>

View File

@ -71,7 +71,9 @@ function DescribeSpan({ node }: { node: TimeToSeeNode }): JSX.Element {
{(isFrustratingSession || isFrustratingInteraction) && (
<Tooltip title="This was frustrating because it took longer than 5 seconds">
<IconSad />
<span>
<IconSad />
</span>
</Tooltip>
)}
{isInteractionNode(node) && (

View File

@ -146,7 +146,7 @@ export function ResourcePermission({
icon={<IconTrash />}
onClick={() => deleteAssociatedRole(role.id)}
tooltip="Remove custom role from feature flag"
tooltipPlacement="bottomLeft"
tooltipPlacement="bottom-start"
size="small"
/>
)}
@ -272,7 +272,7 @@ function RoleRow({ role, deleteRole }: { role: RoleType; deleteRole?: (roleId: R
icon={<IconTrash />}
onClick={() => deleteRole(role.id)}
tooltip="Remove role from permission"
tooltipPlacement="bottomLeft"
tooltipPlacement="bottom-start"
size="small"
/>
)}

View File

@ -1,6 +1,6 @@
import { IconInfo } from '@posthog/icons'
import { useActions, useValues } from 'kea'
import { TZLabel } from 'lib/components/TZLabel'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonSelect } from 'lib/lemon-ui/LemonSelect'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { LemonTable } from 'lib/lemon-ui/LemonTable'

View File

@ -1,8 +1,8 @@
import { IconInfo } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonDivider, LemonInput, LemonSelect } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { Form } from 'kea-forms'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { LemonCalendarSelectInput } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect'
import { LemonField } from 'lib/lemon-ui/LemonField'

View File

@ -213,12 +213,12 @@ export function Billing(): JSX.Element {
)}`
: null
}
placement="bottomLeft"
placement="bottom-start"
>
<strong>
$
{parseInt(billing.discount_amount_usd).toLocaleString()}
</strong>{' '}
</strong>
</Tooltip>
remaining credits applied to your bill.
</p>

View File

@ -93,15 +93,8 @@ export const BillingLimitInput = ({ product }: { product: BillingProductV2Type }
>
${customLimitUsd}
</div>
<Tooltip
title={
<>
Set a billing limit to control your recurring costs. Some features may stop
working if your usage exceeds your billing cap.
</>
}
>
{billing?.billing_period?.interval}ly billing limit
<Tooltip title="Set a billing limit to control your recurring costs. Some features may stop working if your usage exceeds your billing cap.">
<span>{billing?.billing_period?.interval}ly billing limit</span>
</Tooltip>
</>
) : (

View File

@ -472,24 +472,25 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
}amount you have been billed for this ${
billing?.billing_period?.interval
} so far.`}
className="flex flex-col items-center"
>
<div className="font-bold text-3xl leading-7">
$
{(
parseFloat(product.current_amount_usd || '') *
(1 -
(billing?.discount_percent
? billing.discount_percent / 100
: 0))
).toFixed(2) || '0.00'}
<div className="flex flex-col items-center">
<div className="font-bold text-3xl leading-7">
$
{(
parseFloat(product.current_amount_usd || '') *
(1 -
(billing?.discount_percent
? billing.discount_percent / 100
: 0))
).toFixed(2) || '0.00'}
</div>
<span className="text-xs text-muted">
{capitalizeFirstLetter(
billing?.billing_period?.interval || ''
)}
-to-date
</span>
</div>
<span className="text-xs text-muted">
{capitalizeFirstLetter(
billing?.billing_period?.interval || ''
)}
-to-date
</span>
</Tooltip>
{product.tiers && (
<Tooltip
@ -498,19 +499,20 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
? ', discounts on your account,'
: ''
} and the remaining time left in this billing period.`}
className="flex flex-col items-center justify-end"
>
<div className="font-bold text-muted text-lg leading-5">
$
{(
parseFloat(product.projected_amount_usd || '') *
(1 -
(billing?.discount_percent
? billing.discount_percent / 100
: 0))
).toFixed(2) || '0.00'}
<div className="flex flex-col items-center justify-end">
<div className="font-bold text-muted text-lg leading-5">
$
{(
parseFloat(product.projected_amount_usd || '') *
(1 -
(billing?.discount_percent
? billing.discount_percent / 100
: 0))
).toFixed(2) || '0.00'}
</div>
<span className="text-xs text-muted">Projected</span>
</div>
<span className="text-xs text-muted">Projected</span>
</Tooltip>
)}
</div>
@ -520,14 +522,15 @@ export const BillingProduct = ({ product }: { product: BillingProductV2Type }):
<div className="my-8">
<Tooltip
title={`The current amount you will be billed for this ${billing?.billing_period?.interval}.`}
className="flex flex-col items-center"
>
<div className="font-bold text-3xl leading-7">
${product.current_amount_usd}
<div className="flex flex-col items-center">
<div className="font-bold text-3xl leading-7">
${product.current_amount_usd}
</div>
<span className="text-xs text-muted">
per {billing?.billing_period?.interval || 'period'}
</span>
</div>
<span className="text-xs text-muted">
per {billing?.billing_period?.interval || 'period'}
</span>
</Tooltip>
</div>
)}

View File

@ -235,7 +235,9 @@ export const PlanComparison = ({
i == fullyFeaturedPlan?.features?.length - 1 && 'PlanTable__th__last-feature'
)}
>
<Tooltip title={feature.description}>{feature.name}</Tooltip>
<Tooltip title={feature.description}>
<span>{feature.name}</span>
</Tooltip>
</th>
{plans?.map((plan) => (
<td key={`${plan.plan_key}-${feature.key}`}>
@ -255,7 +257,7 @@ export const PlanComparison = ({
<th colSpan={1} className="PlanTable__th__section rounded text-left">
<h3 className="mt-6 mb-2">
<Tooltip title="Organizations with any paid subscription get access to additional features.">
Included platform features:
<span>Included platform features:</span>
</Tooltip>
</h3>
</th>
@ -298,7 +300,9 @@ export const PlanComparison = ({
: ''
)}
>
<Tooltip title={feature.description}>{feature.name}</Tooltip>
<Tooltip title={feature.description}>
<span>{feature.name}</span>
</Tooltip>
</th>
{includedProduct.plans?.map((plan) => (
<React.Fragment key={`${plan.plan_key}-${feature.key}`}>

View File

@ -65,16 +65,14 @@ export function DashboardReloadAction(): JSX.Element {
>
<Tooltip
title="Auto refresh will only work while this tab is open"
placement="topRight"
placement="top-end"
>
<div>
<LemonSwitch
onChange={(checked) => setAutoRefresh(checked, autoRefresh.interval)}
label="Auto refresh"
checked={autoRefresh.enabled}
fullWidth={true}
/>
</div>
<LemonSwitch
onChange={(checked) => setAutoRefresh(checked, autoRefresh.interval)}
label="Auto refresh"
checked={autoRefresh.enabled}
fullWidth={true}
/>
</Tooltip>
<LemonDivider />
<div className="flex flex-col">

View File

@ -101,7 +101,9 @@ export function DashboardsTable({
)}
{isPrimary && (
<Tooltip title="The primary dashboard is shown on the project home page.">
<IconCottage className="ml-1 text-base text-warning" />
<span>
<IconCottage className="ml-1 text-base text-warning" />
</span>
</Tooltip>
)}
</div>

View File

@ -1,10 +1,10 @@
import { IconInfo } from '@posthog/icons'
import { actions, connect, kea, path, reducers, selectors, useActions, useValues } from 'kea'
import { actionToUrl, combineUrl, router, urlToAction } from 'kea-router'
import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog'
import { PageHeader } from 'lib/components/PageHeader'
import { TitleWithIcon } from 'lib/components/TitleWithIcon'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { Tooltip } from 'lib/lemon-ui/Tooltip'

View File

@ -120,7 +120,7 @@ export function ViewLinkDeleteButton({ table, column }: ViewLinkDeleteButtonProp
icon={<IconTrash />}
onClick={() => deleteViewLink(table, column)}
tooltip="Remove view association"
tooltipPlacement="bottomLeft"
tooltipPlacement="bottom-start"
size="small"
/>
)

View File

@ -286,7 +286,7 @@ export function Experiment(): JSX.Element {
!(index === 0 || index === 1) && (
<Tooltip
title="Delete this variant"
placement="bottomLeft"
placement="bottom-start"
>
<LemonButton
size="small"

View File

@ -1,3 +1,4 @@
import { IconInfo } from '@posthog/icons'
import { LemonButton, LemonDivider, LemonInput, LemonModal, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { Field, Form } from 'kea-forms'
@ -5,7 +6,6 @@ import { InsightLabel } from 'lib/components/InsightLabel'
import { PropertyFilterButton } from 'lib/components/PropertyFilters/components/PropertyFilterButton'
import { TZLabel } from 'lib/components/TZLabel'
import { dayjs } from 'lib/dayjs'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonSlider } from 'lib/lemon-ui/LemonSlider'
import { humanFriendlyNumber } from 'lib/utils'
import { groupFilters } from 'scenes/feature-flags/FeatureFlags'

View File

@ -1,3 +1,4 @@
import { IconInfo } from '@posthog/icons'
import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import { forms } from 'kea-forms'
import { loaders } from 'kea-loaders'
@ -5,7 +6,6 @@ import { router, urlToAction } from 'kea-router'
import api from 'lib/api'
import { FunnelLayout } from 'lib/constants'
import { dayjs } from 'lib/dayjs'
import { IconInfo } from 'lib/lemon-ui/icons'
import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { toParams } from 'lib/utils'

View File

@ -1004,7 +1004,7 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element {
? 'Cannot delete variants from a feature flag that is part of an experiment'
: undefined
}
tooltipPlacement="topRight"
tooltipPlacement="top-end"
/>
)}
</div>
@ -1030,7 +1030,7 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element {
? 'Cannot add variants to a feature flag that is part of an experiment. To update variants, create a new experiment.'
: undefined
}
tooltipPlacement="topLeft"
tooltipPlacement="top-start"
center
>
Add variant

View File

@ -119,7 +119,7 @@ export default function FeatureFlagSchedule(): JSX.Element {
>
<LemonTag type={type}>
<b className="uppercase">{text}</b>
</LemonTag>{' '}
</LemonTag>
</Tooltip>
)
},

View File

@ -1,4 +1,4 @@
import { IconInfo } from 'lib/lemon-ui/icons'
import { IconInfo } from '@posthog/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
export function DuplicateStepIndicator(): JSX.Element {

View File

@ -1,6 +1,6 @@
import { IconInfo } from '@posthog/icons'
import { Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { IconInfo } from 'lib/lemon-ui/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { humanFriendlyDuration, percentage } from 'lib/utils'
import React from 'react'

View File

@ -31,7 +31,7 @@ export function InsightsNav(): JSX.Element {
label: (
<Link to={insightTypeUrls[type]} preventClick data-attr={dataAttr}>
<Tooltip placement="top" title={INSIGHT_TYPES_METADATA[type].description}>
{label}
<span>{label}</span>
</Tooltip>
</Link>
),

View File

@ -17,8 +17,7 @@ export function RetentionDatePicker(): JSX.Element {
return (
<Tooltip title="Cohorts up to this end date">
{/* eslint-disable-next-line react/forbid-dom-props */}
<span style={{ maxWidth: 100 }} className="flex inline-flex items-center pl-2">
<span className="flex inline-flex items-center pl-2 max-w-[100px]">
<DatePicker
showTime={period === 'Hour'}
use12Hours

View File

@ -1,8 +1,8 @@
import './BreakdownTagMenu.scss'
import { IconInfo } from '@posthog/icons'
import { LemonButton, LemonDivider, LemonInput, LemonSwitch } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { IconInfo } from 'lib/lemon-ui/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { insightLogic } from 'scenes/insights/insightLogic'
import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic'

View File

@ -132,15 +132,15 @@ export function CorrelationMatrix(): JSX.Element {
)}
</td>
<td>
<div className="percentage">
<Tooltip
title={`False negative (FN) - Percentage of users who ${action} and did not complete the funnel.`}
>
<Tooltip
title={`False negative (FN) - Percentage of users who ${action} and did not complete the funnel.`}
>
<div className="percentage">
{falseNegative
? percentage(falseNegative / (falseNegative + trueNegative))
: '0.00%'}
</Tooltip>
</div>
</div>
</Tooltip>
{falseNegative === 0 ? (
'0 users'
) : (
@ -157,27 +157,27 @@ export function CorrelationMatrix(): JSX.Element {
<tr>
<td className="horizontal-header">No</td>
<td>
<div className="percentage">
<Tooltip
title={`False positive (FP) - Percentage of users who did not ${action} and completed the funnel.`}
>
<Tooltip
title={`False positive (FP) - Percentage of users who did not ${action} and completed the funnel.`}
>
<div className="percentage">
{falsePositive
? percentage(falsePositive / (truePositive + falsePositive))
: '0.00%'}
</Tooltip>
</div>
</div>
</Tooltip>
{pluralize(falsePositive, 'user', undefined, true)}
</td>
<td>
<div className="percentage">
<Tooltip
title={`True negative (TN) - Percentage of users who did not ${action} and did not complete the funnel.`}
>
<Tooltip
title={`True negative (TN) - Percentage of users who did not ${action} and did not complete the funnel.`}
>
<div className="percentage">
{trueNegative
? percentage(trueNegative / (falseNegative + trueNegative))
: '0.00%'}
</Tooltip>
</div>
</div>
</Tooltip>
{pluralize(trueNegative, 'user', undefined, true)}
</td>
</tr>

View File

@ -48,7 +48,7 @@ export function FunnelStepsPicker(): JSX.Element | null {
size="small"
className="mx-1"
dropdownMatchSelectWidth={false}
optionTooltipPlacement="bottomLeft"
optionTooltipPlacement="bottom-start"
disabled={!isFunnelWithEnoughSteps}
options={optionsForRange(fromRange)}
value={funnelsFilter?.funnelFromStep || 0}
@ -61,7 +61,7 @@ export function FunnelStepsPicker(): JSX.Element | null {
size="small"
className="mx-1"
dropdownMatchSelectWidth={false}
optionTooltipPlacement="bottomLeft"
optionTooltipPlacement="bottom-start"
disabled={!isFunnelWithEnoughSteps}
options={optionsForRange(toRange)}
value={funnelsFilter?.funnelToStep || Math.max(numberOfSeries - 1, 1)}

View File

@ -1,10 +1,10 @@
import './index.scss'
import { IconInfo } from '@posthog/icons'
import { LemonBanner, Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { PageHeader } from 'lib/components/PageHeader'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
@ -36,8 +36,10 @@ export function SystemStatus(): JSX.Element {
{
key: 'overview',
label: (
<Tooltip title={<>System overview is cached for 60 seconds</>}>
System overview <IconInfo />
<Tooltip title="System overview is cached for 60 seconds">
<span>
System overview <IconInfo />
</span>
</Tooltip>
),
content: <OverviewTab />,

View File

@ -26,7 +26,9 @@ export const EventIcon = ({ event }: EventIconProps): JSX.Element => {
}
return (
<Tooltip title={`${CORE_FILTER_DEFINITIONS_BY_GROUP.events[event.event]?.label || 'Custom'} event`}>
<Component className="text-2xl text-muted" />
<span>
<Component className="text-2xl text-muted" />
</span>
</Tooltip>
)
}

View File

@ -2,6 +2,7 @@ import './PersonScene.scss'
// eslint-disable-next-line no-restricted-imports
import { DownOutlined } from '@ant-design/icons'
import { IconInfo } from '@posthog/icons'
import { LemonButton, LemonDivider, LemonSelect, LemonTag, Link } from '@posthog/lemon-ui'
import { Dropdown, Menu } from 'antd'
import { useActions, useValues } from 'kea'
@ -12,7 +13,6 @@ import { PageHeader } from 'lib/components/PageHeader'
import { PropertiesTable } from 'lib/components/PropertiesTable'
import { TZLabel } from 'lib/components/TZLabel'
import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner'

View File

@ -1,6 +1,6 @@
import { IconInfo } from '@posthog/icons'
import { LemonInput } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { IconInfo } from 'lib/lemon-ui/icons'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { useEffect, useState } from 'react'
import { useDebouncedCallback } from 'use-debounce'

View File

@ -85,7 +85,7 @@ function PluginConfigurationFields({
<>
{fieldConfig.secret && (
<Tooltip
placement="topLeft"
placement="top-start"
title="This field is write-only. Its value won't be visible after saving."
>
<IconLock className="ml-1.5" />

View File

@ -1,10 +1,9 @@
import { IconCollapse, IconExpand } from '@posthog/icons'
import { IconCollapse, IconExpand, IconInfo } from '@posthog/icons'
import { useActions, useValues } from 'kea'
import { Chart, ChartDataset, ChartItem } from 'lib/Chart'
import { getColorVar } from 'lib/colors'
import { CodeSnippet, Language } from 'lib/components/CodeSnippet'
import { TZLabel } from 'lib/components/TZLabel'
import { IconInfo } from 'lib/lemon-ui/icons'
import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'

View File

@ -140,7 +140,9 @@ export function RenderApp({ plugin, imageSize }: RenderAppProps): JSX.Element {
<PluginImage plugin={plugin} size={imageSize} />
</Link>
) : (
<PluginImage plugin={plugin} size={imageSize} /> // TODO: tooltip doesn't work on this
<span>
<PluginImage plugin={plugin} size={imageSize} />
</span>
)}
</Tooltip>
</div>

View File

@ -46,7 +46,7 @@ function EnabledDisabledSwitch({
const SecretFieldIcon = (): JSX.Element => (
<>
<Tooltip
placement="topLeft"
placement="top-start"
title="This is a secret write-only field. Its value is not available after saving."
>
<IconLock style={{ marginRight: 5 }} />

View File

@ -40,15 +40,17 @@ export function PluginJobConfiguration(props: InterfaceJobsProps): JSX.Element {
<>
<span className="ml-1" onClick={() => playButtonOnClick(jobHasEmptyPayload)}>
<Tooltip title={configureOrRunJobTooltip}>
{jobHasEmptyPayload ? (
<IconPlayCircle
className={runJobAvailable ? 'Plugin__RunJobButton' : 'Plugin__RunJobButton--disabled'}
/>
) : (
<IconGear
className={runJobAvailable ? 'Plugin__RunJobButton' : 'Plugin__RunJobButton--disabled'}
/>
)}
<span>
{jobHasEmptyPayload ? (
<IconPlayCircle
className={runJobAvailable ? 'Plugin__RunJobButton' : 'Plugin__RunJobButton--disabled'}
/>
) : (
<IconGear
className={runJobAvailable ? 'Plugin__RunJobButton' : 'Plugin__RunJobButton--disabled'}
/>
)}
</span>
</Tooltip>
</span>

View File

@ -69,7 +69,6 @@ export function AppView({
<Tooltip
title={
<>
 
{orderedIndex ? (
<>
Apps that react to incoming events run in order. This app runs in position{' '}

View File

@ -81,7 +81,7 @@ function OnboardingNotCompletedButton({
}
export function getProductIcon(iconKey?: string | null, className?: string): JSX.Element {
return Icons[iconKey || 'IconLogomark']({ className })
return Icons[iconKey || 'IconLogomark'].render({ className })
}
export function ProductCard({

View File

@ -68,7 +68,7 @@ interface NewInsightButtonProps {
export interface InsightTypeMetadata {
name: string
description?: string
icon: (props?: any) => JSX.Element
icon: (props?: any) => JSX.Element | null
inMenu: boolean
}

View File

@ -227,7 +227,9 @@ export function PlayerMeta(): JSX.Element {
</>
}
>
<IconWindow value={currentWindowIndex + 1} className="text-muted-alt" />
<span>
<IconWindow value={currentWindowIndex + 1} className="text-muted-alt" />
</span>
</Tooltip>
<URLOrScreen lastUrl={lastUrl} />

View File

@ -44,8 +44,7 @@ export function SeekSkip({ direction }: { direction: 'forward' | 'backward' }):
return (
<Tooltip
placement="top"
overlayInnerStyle={{ minHeight: 'auto' }}
overlay={
title={
<div className="text-center">
{!altKeyHeld ? (
<>

View File

@ -1,16 +1,8 @@
import { IconTerminal, IconX } from '@posthog/icons'
import { IconInfo, IconPause, IconTerminal, IconX } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonInput, LemonSelect, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { FEATURE_FLAGS } from 'lib/constants'
import {
IconBugShield,
IconGauge,
IconInfo,
IconPause,
IconPlayCircle,
IconSchedule,
IconUnverifiedEvent,
} from 'lib/lemon-ui/icons'
import { IconBugShield, IconGauge, IconPlayCircle, IconSchedule, IconUnverifiedEvent } from 'lib/lemon-ui/icons'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'

View File

@ -204,7 +204,7 @@ export function PlayerInspectorListItem({
title="This event occured before the recording started, likely as the page was loading."
placement="left"
>
{colonDelimitedDuration(item.timeInRecording / 1000, fixedUnits)}
<span>{colonDelimitedDuration(item.timeInRecording / 1000, fixedUnits)}</span>
</Tooltip>
) : (
colonDelimitedDuration(item.timeInRecording / 1000, fixedUnits)

View File

@ -305,7 +305,9 @@ const TimingBar = ({
<>
<div className="flex flex-row px-2 py-1">
<div className="w-2/5">
<Tooltip title={perfDescriptions[section]}>{label}</Tooltip>
<Tooltip title={perfDescriptions[section]}>
<span>{label}</span>
</Tooltip>
</div>
<div className="flex-1 grow relative">
<div

View File

@ -1,10 +1,11 @@
import { IconKeyboard, IconPinFilled } from '@posthog/icons'
import clsx from 'clsx'
import { useValues } from 'kea'
import { FlaggedFeature } from 'lib/components/FlaggedFeature'
import { PropertyIcon } from 'lib/components/PropertyIcon'
import { TZLabel } from 'lib/components/TZLabel'
import { FEATURE_FLAGS } from 'lib/constants'
import { IconAutoAwesome, IconAutocapture, IconKeyboard, IconPinFilled, IconSchedule } from 'lib/lemon-ui/icons'
import { IconAutoAwesome, IconAutocapture, IconSchedule } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { Popover } from 'lib/lemon-ui/Popover'
@ -211,7 +212,7 @@ function PinnedIndicator(): JSX.Element | null {
const isTestingSaved = featureFlags[FEATURE_FLAGS.SAVED_NOT_PINNED] === 'test'
const description = isTestingSaved ? 'saved' : 'pinned'
return (
<Tooltip placement="topRight" title={<>This recording is {description} to this list.</>}>
<Tooltip placement="top-end" title={<>This recording is {description} to this list.</>}>
<IconPinFilled className="text-sm text-orange shrink-0" />
</Tooltip>
)

View File

@ -24,25 +24,23 @@ export function SessionRecordingsPlaylistSettings(): JSX.Element {
}
placement="bottom"
>
<span>
<LemonSwitch
aria-label="Autoplay next recording"
checked={!!autoplayDirection}
onChange={toggleAutoplayDirection}
handleContent={
<span
className={clsx(
'transition-all flex items-center',
!autoplayDirection && 'text-border text-sm',
!!autoplayDirection && 'text-white text-xs pl-px',
autoplayDirection === 'newer' && 'rotate-180'
)}
>
{autoplayDirection ? <IconPlay /> : <IconPause />}
</span>
}
/>
</span>
<LemonSwitch
aria-label="Autoplay next recording"
checked={!!autoplayDirection}
onChange={toggleAutoplayDirection}
handleContent={
<span
className={clsx(
'transition-all flex items-center',
!autoplayDirection && 'text-border text-sm',
!!autoplayDirection && 'text-white text-xs pl-px',
autoplayDirection === 'newer' && 'rotate-180'
)}
>
{autoplayDirection ? <IconPlay /> : <IconPause />}
</span>
}
/>
</Tooltip>
</div>
<div className="flex flex-row items-center justify-between space-x-2">

View File

@ -1,10 +1,10 @@
import { IconInfo } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonTable } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini'
import { useRestrictedArea } from 'lib/components/RestrictedArea'
import { TitleWithIcon } from 'lib/components/TitleWithIcon'
import { OrganizationMembershipLevel } from 'lib/constants'
import { IconInfo } from 'lib/lemon-ui/icons'
import { LemonTableColumns } from 'lib/lemon-ui/LemonTable'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { organizationLogic } from 'scenes/organizationLogic'

View File

@ -73,12 +73,12 @@ function VerifiedDomainsTable(): JSX.Element {
{
key: 'is_verified',
title: (
<>
Verification
<div className="flex items-center space-x-1">
<span>Verification</span>
<Tooltip title="Verification (through DNS) is required to use domains for authentication (e.g. SAML or enforce SSO).">
<IconInfo style={{ marginLeft: 4 }} />
<IconInfo />
</Tooltip>
</>
</div>
),
render: function Verified(_, { is_verified, verified_at }) {
return is_verified ? (
@ -101,8 +101,8 @@ function VerifiedDomainsTable(): JSX.Element {
{
key: 'jit_provisioning_enabled',
title: (
<>
Automatic provisioning{' '}
<div className="flex items-center space-x-1">
<span>Automatic provisioning</span>
<Tooltip
title={`Enables just-in-time provisioning. If a user logs in with SSO with an email address on this domain an account will be created in ${
currentOrganization?.name || 'this organization'
@ -110,7 +110,7 @@ function VerifiedDomainsTable(): JSX.Element {
>
<IconInfo />
</Tooltip>
</>
</div>
),
render: function AutomaticProvisioning(_, { jit_provisioning_enabled, id, is_verified }) {
return is_verified ? (
@ -132,12 +132,12 @@ function VerifiedDomainsTable(): JSX.Element {
{
key: 'sso_enforcement',
title: (
<>
Enforce SSO{' '}
<div className="flex items-center space-x-1">
<span>Enforce SSO</span>
<Tooltip title="Require users with email addresses on this domain to always log in using a specific SSO provider.">
<IconInfo />
</Tooltip>
</>
</div>
),
render: function SSOEnforcement(_, { sso_enforcement, is_verified, id, has_saml }, index) {
if (!isSSOEnforcementAvailable) {

View File

@ -1,10 +1,9 @@
// eslint-disable-next-line no-restricted-imports
import { CloseCircleOutlined, CrownFilled, LogoutOutlined } from '@ant-design/icons'
import { IconLock, IconUnlock } from '@posthog/icons'
import { LemonButton, LemonSelect, LemonSelectOption, LemonSwitch, LemonTable } from '@posthog/lemon-ui'
import { IconCrown, IconLock, IconUnlock } from '@posthog/icons'
import { LemonButton, LemonSelect, LemonSelectOption, LemonSnack, LemonSwitch, LemonTable } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { RestrictedArea, RestrictionScope, useRestrictedArea } from 'lib/components/RestrictedArea'
import { OrganizationMembershipLevel, TeamMembershipLevel } from 'lib/constants'
import { IconCancel, IconLogout } from 'lib/lemon-ui/icons'
import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
import { LemonTableColumns } from 'lib/lemon-ui/LemonTable'
import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture'
@ -51,11 +50,13 @@ function LevelComponent(member: FusedTeamMemberType): JSX.Element | null {
? `This user is a member of the project implicitly due to being an organization ${levelName}.`
: getReasonForAccessLevelChangeProhibition(myMembershipLevel, user, member, allowedLevels)
const levelButton = disallowedReason ? (
<div className="border rounded px-3 py-2 flex inline-flex items-center">
{member.level === OrganizationMembershipLevel.Owner && <CrownFilled className="mr-2" />}
{levelName}
</div>
return disallowedReason ? (
<Tooltip title={disallowedReason}>
<LemonSnack className="capitalize">
{member.level === OrganizationMembershipLevel.Owner && <IconCrown className="mr-2" />}
{levelName}
</LemonSnack>
</Tooltip>
) : (
<LemonSelect
dropdownMatchSelectWidth={false}
@ -78,8 +79,6 @@ function LevelComponent(member: FusedTeamMemberType): JSX.Element | null {
value={member.explicit_team_level}
/>
)
return disallowedReason ? <Tooltip title={disallowedReason}>{levelButton}</Tooltip> : levelButton
}
function ActionsComponent(member: FusedTeamMemberType): JSX.Element | null {
@ -117,13 +116,16 @@ function ActionsComponent(member: FusedTeamMemberType): JSX.Element | null {
// Only members without implicit access can leave or be removed
member.organization_level < MINIMUM_IMPLICIT_ACCESS_LEVEL
const isSelf = member.user.uuid === user.uuid
return allowDeletion ? (
<LemonButton status="danger" onClick={handleClick} data-attr="delete-team-membership">
{member.user.uuid !== user.uuid ? (
<CloseCircleOutlined title="Remove from project" />
) : (
<LogoutOutlined title="Leave project" />
)}
<LemonButton
status="danger"
onClick={handleClick}
data-attr="delete-team-membership"
tooltip={isSelf ? 'Leave project' : 'Remove from project'}
>
{isSelf ? <IconLogout /> : <IconCancel />}
</LemonButton>
) : null
}

View File

@ -222,7 +222,7 @@ export default function SurveyEdit(): JSX.Element {
displayThankYouMessage: false,
})
}}
tooltipPlacement="topRight"
tooltipPlacement="top-end"
/>
</div>
),

View File

@ -76,7 +76,7 @@ export function SurveyEditQuestionHeader({
survey.questions.filter((_, i) => i !== index)
)
}}
tooltipPlacement="topRight"
tooltipPlacement="top-end"
/>
)}
</div>

View File

@ -142,6 +142,7 @@ $colors: (
'glass-bg-3000': var(--glass-bg-3000),
'glass-border-3000': var(--border-3000),
'link-3000': var(--link-3000),
'tooltip-bg': var(--tooltip-bg),
'primary-3000-frame-bg': var(--primary-3000-frame-bg),
'primary-3000-button-bg': var(--primary-3000-button-bg),
'primary-3000-button-border': var(--primary-3000-button-border),
@ -248,6 +249,7 @@ $_lifecycle_dormant: $_danger;
--z-command-palette: 1875;
--z-force-modal-above-popovers: 1850;
--z-ant-message: 1070;
--z-tooltip: 1070;
--z-ant-select-dropdown: 1065;
--z-definition-popover: 1064;
--z-popover: 1063;

View File

@ -62,15 +62,13 @@ export const HeatmapToolbarMenu = (): JSX.Element => {
</div>
<Tooltip title="Matching links by their target URL can exclude clicks from the heatmap if the URL is too unique.">
<div>
<LemonSwitch
checked={matchLinksByHref}
label="Match links by their target URL"
onChange={(checked) => setMatchLinksByHref(checked)}
fullWidth={true}
bordered={true}
/>
</div>
<LemonSwitch
checked={matchLinksByHref}
label="Match links by their target URL"
onChange={(checked) => setMatchLinksByHref(checked)}
fullWidth={true}
bordered={true}
/>
</Tooltip>
</div>
</ToolbarMenu.Header>

View File

@ -70,12 +70,12 @@
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@floating-ui/react": "^0.16.0",
"@floating-ui/react": "^0.26.9",
"@lottiefiles/react-lottie-player": "^3.4.7",
"@medv/finder": "^3.1.0",
"@microlink/react-json-view": "^1.21.3",
"@monaco-editor/react": "4.4.6",
"@posthog/icons": "0.5.1",
"@posthog/icons": "0.6.3",
"@posthog/plugin-scaffold": "^1.4.4",
"@react-hook/size": "^2.1.2",
"@rrweb/types": "2.0.0-alpha.11",

View File

@ -29,8 +29,8 @@ dependencies:
specifier: ^3.2.1
version: 3.2.1(react@18.2.0)
'@floating-ui/react':
specifier: ^0.16.0
version: 0.16.0(@types/react@17.0.52)(react-dom@18.2.0)(react@18.2.0)
specifier: ^0.26.9
version: 0.26.9(react-dom@18.2.0)(react@18.2.0)
'@lottiefiles/react-lottie-player':
specifier: ^3.4.7
version: 3.4.7(react@18.2.0)
@ -44,8 +44,8 @@ dependencies:
specifier: 4.4.6
version: 4.4.6(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
'@posthog/icons':
specifier: 0.5.1
version: 0.5.1(react-dom@18.2.0)(react@18.2.0)
specifier: 0.6.3
version: 0.6.3(react-dom@18.2.0)(react@18.2.0)
'@posthog/plugin-scaffold':
specifier: ^1.4.4
version: 1.4.4
@ -4396,22 +4396,26 @@ packages:
resolution: {integrity: sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==}
dependencies:
'@floating-ui/utils': 0.1.1
dev: true
/@floating-ui/core@1.6.0:
resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
dependencies:
'@floating-ui/utils': 0.2.1
dev: false
/@floating-ui/dom@1.5.1:
resolution: {integrity: sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==}
dependencies:
'@floating-ui/core': 1.4.1
'@floating-ui/utils': 0.1.1
dev: true
/@floating-ui/react-dom@1.3.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
/@floating-ui/dom@1.6.1:
resolution: {integrity: sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==}
dependencies:
'@floating-ui/dom': 1.5.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@floating-ui/core': 1.6.0
'@floating-ui/utils': 0.2.1
dev: false
/@floating-ui/react-dom@2.0.1(react-dom@18.2.0)(react@18.2.0):
@ -4425,23 +4429,37 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/@floating-ui/react@0.16.0(@types/react@17.0.52)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-h+69TJSAY2R/k5rw+az56RzzDFc/Tg7EHn/qEgwkIVz56Zg9LlaRMMUvxkcvd+iN3CNFDLtEnDlsXnpshjsRsQ==}
/@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/react-dom': 1.3.0(react-dom@18.2.0)(react@18.2.0)
aria-hidden: 1.2.1(@types/react@17.0.52)(react@18.2.0)
'@floating-ui/dom': 1.6.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@floating-ui/react@0.26.9(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-p86wynZJVEkEq2BBjY/8p2g3biQ6TlgT4o/3KgFKyTWoJLU1GZ8wpctwRqtkEl2tseYA+kw7dBAIDFcednfI5w==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
'@floating-ui/utils': 0.2.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
tabbable: 6.1.1
transitivePeerDependencies:
- '@types/react'
dev: false
/@floating-ui/utils@0.1.1:
resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==}
dev: true
/@floating-ui/utils@0.2.1:
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
dev: false
/@hapi/hoek@9.3.0:
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@ -5089,8 +5107,8 @@ packages:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: false
/@posthog/icons@0.5.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-BWy5nZMTz4Y2J1zDuUm8Mk+A3OgJH3QdQQl5sZaqxWb6WA32CWzIx1W70CETiN0GMEm9UbiMU3JholhMTPIhcQ==}
/@posthog/icons@0.6.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-EQ86OFe9omsU9vUCvISksNN+QPH/VHiE4Z0A8FZApSKbiCtsX2zecPgX3ou765V284ktajkeROsrUI0luj8jRw==}
peerDependencies:
react: '>=16.14.0'
react-dom: '>=16.14.0'
@ -8956,6 +8974,7 @@ packages:
'@types/react': 17.0.52
react: 18.2.0
tslib: 2.4.1
dev: true
/aria-query@5.1.3:
resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}