diff --git a/src/components/Floater/Arrow.tsx b/src/components/Floater/Arrow.tsx index f2df769..ae357ae 100644 --- a/src/components/Floater/Arrow.tsx +++ b/src/components/Floater/Arrow.tsx @@ -8,37 +8,54 @@ interface Props { styles: Styles; } +/** + * FloaterArrow component renders a customizable arrow for tooltips/popovers + * @param props Component properties + * @returns React component + */ export default function FloaterArrow(props: Props) { const { arrowRef, placement, styles } = props; - const { - arrow: { color, display, length, position, spread }, + arrow: { color, display, length, position, rounded, spread }, } = styles; + + const [direction] = placement.split('-'); + const isVertical = direction === 'top' || direction === 'bottom'; + const arrowStyles: React.CSSProperties = { display, position }; - let points; - let x = spread; - let y = length; - - if (placement.startsWith('top')) { - points = `0,0 ${x / 2},${y} ${x},0`; - } else if (placement.startsWith('bottom')) { - points = `${x},${y} ${x / 2},0 0,${y}`; - } else if (placement.startsWith('left')) { - y = spread; - x = length; - points = `0,0 ${x},${y / 2} 0,${y}`; - } else if (placement.startsWith('right')) { - y = spread; - x = length; - points = `${x},${y} ${x},0 0,${y / 2}`; + const baseRadius = isVertical ? spread / 8 : length / 8; + const r = rounded ? baseRadius : 0; + + const webkitMask = isVertical + ? `linear-gradient(0deg,#0000 calc(${r}px/sqrt(2)),#000 0), + radial-gradient(${r}px at 50% calc(100% - ${r}px*sqrt(2)),#000 98%,#0000 101%)` + : `linear-gradient(-90deg,#0000 calc(${r}px/sqrt(2)),#000 0), + radial-gradient(${r}px at calc(100% - ${r}px*sqrt(2)) 50%,#000 98%,#0000 101%)`; + + const clipPath = isVertical ? 'polygon(50% 100%,100% 0,0 0)' : 'polygon(100% 50%,0 100%,0 0)'; + + let scales: string | undefined; + + if (direction === 'bottom') { + scales = '1 -1'; + } else if (direction === 'right') { + scales = '-1 1'; } return ( - - - - - + ); } diff --git a/src/modules/styles.ts b/src/modules/styles.ts index 645ca51..2080888 100644 --- a/src/modules/styles.ts +++ b/src/modules/styles.ts @@ -18,6 +18,7 @@ export default function getStyles(styles?: PartialDeep): Styles { length: 16, position: 'absolute', spread: 32, + rounded: false, }, close: { backgroundColor: 'transparent', diff --git a/src/types/common.ts b/src/types/common.ts index d1352f5..0d3f8a8 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -7,27 +7,19 @@ import { } from 'react'; import { PartialDeep, RequireExactlyOne, ValueOf } from 'type-fest'; -import { PopperInstance, PopperModifiers, PopperPlacement } from './popper'; - import { STATUS } from '../literals'; +import { PopperInstance, PopperModifiers, PopperPlacement } from './popper'; + export type Action = 'open' | 'close'; export type CloseFunction = MouseEventHandler; +export type FloaterComponent = FunctionComponent | ReactElement; export type Placement = PopperPlacement | 'center'; -export type SelectorOrElement = string | null | HTMLElement; -export type Statuses = ValueOf; - -export interface CustomComponentProps { - closeFn: CloseFunction; -} +export type Props = RequireExactlyOne; -export interface LogOptions { - data: any; - debug?: boolean; - title: string; -} +export type SelectorOrElement = string | null | HTMLElement; -export type FloaterComponent = FunctionComponent | ReactElement; +export type Statuses = ValueOf; export interface BaseProps { /** @@ -121,7 +113,15 @@ export interface BaseProps { }; } -export type Props = RequireExactlyOne; +export interface CustomComponentProps { + closeFn: CloseFunction; +} + +export interface LogOptions { + data: any; + debug?: boolean; + title: string; +} export interface State { currentPlacement: Placement; @@ -133,6 +133,7 @@ export interface State { export interface Styles { arrow: CSSProperties & { length: number; + rounded: boolean; spread: number; }; close: CSSProperties; diff --git a/test/__snapshots__/index.spec.tsx.snap b/test/__snapshots__/index.spec.tsx.snap index 2221a8b..d7025ed 100644 --- a/test/__snapshots__/index.spec.tsx.snap +++ b/test/__snapshots__/index.spec.tsx.snap @@ -24,20 +24,8 @@ exports[`ReactFloater > with \`component\` as element > should show the floater - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 16px; width: 32px; clip-path: polygon(50% 100%,100% 0,0 0); scale: 1 -1; left: 0px; transform: translate(8px, 0px); top: 0px;" + /> `; @@ -66,20 +54,8 @@ exports[`ReactFloater > with \`component\` as function > should show the floater - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 16px; width: 32px; clip-path: polygon(50% 100%,100% 0,0 0); scale: 1 -1; left: 0px; transform: translate(8px, 0px); top: 0px;" + /> `; @@ -132,20 +108,8 @@ exports[`ReactFloater > with \`placement\` left > should show the floater with c - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 32px; width: 16px; clip-path: polygon(100% 50%,0 100%,0 0); top: 0px; transform: translate(0px, 8px); right: 0px;" + /> `; @@ -173,20 +137,8 @@ exports[`ReactFloater > with \`placement\` right > should show the floater with - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 32px; width: 16px; clip-path: polygon(100% 50%,0 100%,0 0); scale: -1 1; top: 0px; transform: translate(0px, 8px); left: 0px;" + /> `; @@ -214,20 +166,8 @@ exports[`ReactFloater > with \`placement\` top > should show the floater with cl - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 16px; width: 32px; clip-path: polygon(50% 100%,100% 0,0 0); left: 0px; transform: translate(8px, 0px); bottom: 0px;" + /> `; @@ -288,20 +228,8 @@ exports[`ReactFloater > with \`title\`, \`footer\` and \`closeBtn\` > should ren - - - - + style="display: inline-flex; position: absolute; background-color: rgb(255, 255, 255); height: 16px; width: 32px; clip-path: polygon(50% 100%,100% 0,0 0); scale: 1 -1; left: 0px; transform: translate(8px, 0px); top: 0px;" + /> `;