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:
parent
2e0a14a2a6
commit
6cd02e980a
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -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>
|
||||
|
@ -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',
|
||||
},
|
||||
]
|
||||
|
@ -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" />
|
||||
|
@ -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" />
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
)
|
||||
})
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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 })
|
||||
|
@ -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',
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
)
|
||||
})
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -341,7 +341,6 @@ export function LemonTable<T extends Record<string, any>>({
|
||||
: null
|
||||
}
|
||||
/>
|
||||
{/* this non-breaking space lets antd's tooltip work*/}{' '}
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -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{' '}
|
||||
|
@ -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])
|
||||
|
||||
|
@ -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
|
||||
|
4
frontend/src/lib/lemon-ui/Tooltip/Tooltip.scss
Normal file
4
frontend/src/lib/lemon-ui/Tooltip/Tooltip.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.Tooltip {
|
||||
z-index: var(--z-tooltip);
|
||||
box-shadow: var(--modal-shadow-elevation);
|
||||
}
|
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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]}
|
||||
|
@ -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>
|
||||
|
@ -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) && (
|
||||
|
@ -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"
|
||||
/>
|
||||
)}
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
</>
|
||||
) : (
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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}`}>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
|
@ -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"
|
||||
/>
|
||||
)
|
||||
|
@ -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"
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -119,7 +119,7 @@ export default function FeatureFlagSchedule(): JSX.Element {
|
||||
>
|
||||
<LemonTag type={type}>
|
||||
<b className="uppercase">{text}</b>
|
||||
</LemonTag>{' '}
|
||||
</LemonTag>
|
||||
</Tooltip>
|
||||
)
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
),
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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)}
|
||||
|
@ -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 />,
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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" />
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
|
@ -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 }} />
|
||||
|
@ -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>
|
||||
|
||||
|
@ -69,7 +69,6 @@ export function AppView({
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
|
||||
{orderedIndex ? (
|
||||
<>
|
||||
Apps that react to incoming events run in order. This app runs in position{' '}
|
||||
|
@ -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({
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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} />
|
||||
|
@ -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 ? (
|
||||
<>
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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">
|
||||
|
@ -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'
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ export default function SurveyEdit(): JSX.Element {
|
||||
displayThankYouMessage: false,
|
||||
})
|
||||
}}
|
||||
tooltipPlacement="topRight"
|
||||
tooltipPlacement="top-end"
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
|
@ -76,7 +76,7 @@ export function SurveyEditQuestionHeader({
|
||||
survey.questions.filter((_, i) => i !== index)
|
||||
)
|
||||
}}
|
||||
tooltipPlacement="topRight"
|
||||
tooltipPlacement="top-end"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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==}
|
||||
|
Loading…
Reference in New Issue
Block a user