Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/components/atoms/BottomBarButton.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import React, { MouseEventHandler, FC } from 'react'
import styled from '../../lib/styled'
import { flexCenter, borderLeft } from '../../lib/styled/styleFunctions'
import Tooltip from './Tooltip'

interface BottomBarButtonProps {
className?: string
onClick?: MouseEventHandler<HTMLButtonElement>
tooltipText?: string
}

const BottomBarButton: FC<BottomBarButtonProps> = ({
className,
onClick,
children,
tooltipText,
}) => {
return (
<Container className={className} onClick={onClick}>
{children}
</Container>
<Tooltip space={10} text={tooltipText}>
<Container className={className} onClick={onClick}>
{children}
</Container>
</Tooltip>
)
}

Expand Down
31 changes: 31 additions & 0 deletions src/components/atoms/GeneralPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import ReactDOM from 'react-dom'

interface GeneralPortalProps {
portalKey?: string
}

class GeneralPortal extends React.PureComponent<GeneralPortalProps> {
private containerEl = document.createElement('div')
constructor(props: GeneralPortalProps) {
super(props)
}

componentDidMount() {
document.body.appendChild(this.containerEl)
}

componentWillUnmount() {
document.body.removeChild(this.containerEl)
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.containerEl,
this.props.portalKey
)
}
}

export default GeneralPortal
18 changes: 10 additions & 8 deletions src/components/atoms/NavigatorButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import styled from '../../lib/styled'
import Icon from './Icon'
import Tooltip from './Tooltip'

const ButtonContainer = styled.button`
width: 24px;
Expand Down Expand Up @@ -45,14 +46,15 @@ const NavigatorButton = ({
spin,
}: NavigatorButtonProps) => {
return (
<ButtonContainer
onClick={onClick}
onContextMenu={onContextMenu}
title={title}
className={active ? 'active' : ''}
>
<Icon path={iconPath} spin={spin} />
</ButtonContainer>
<Tooltip space={10} text={title}>
<ButtonContainer
onClick={onClick}
onContextMenu={onContextMenu}
className={active ? 'active' : ''}
>
<Icon path={iconPath} spin={spin} />
</ButtonContainer>
</Tooltip>
)
}

Expand Down
22 changes: 12 additions & 10 deletions src/components/atoms/ToolbarButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styled from '../../lib/styled'
import Icon from './Icon'
import { flexCenter, textOverflow } from '../../lib/styled/styleFunctions'
import cc from 'classcat'
import Tooltip from './Tooltip'

interface ToolbarButtonProps {
iconPath?: string
Expand All @@ -22,16 +23,17 @@ const ToolbarButton = ({
limitWidth,
}: ToolbarButtonProps) => {
return (
<Container
className={cc([active && 'active', limitWidth && 'limitWidth'])}
title={title == null ? label : title}
onClick={onClick}
>
{iconPath != null && <Icon className='icon' path={iconPath} />}
{label != null && label.length > 0 && (
<div className='label'>{label}</div>
)}
</Container>
<Tooltip text={title == null ? label : title}>
<Container
className={cc([active && 'active', limitWidth && 'limitWidth'])}
onClick={onClick}
>
{iconPath != null && <Icon className='icon' path={iconPath} />}
{label != null && label.length > 0 && (
<div className='label'>{label}</div>
)}
</Container>
</Tooltip>
)
}

Expand Down
22 changes: 12 additions & 10 deletions src/components/atoms/ToolbarIconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import styled from '../../lib/styled'
import Icon from './Icon'
import { flexCenter } from '../../lib/styled/styleFunctions'
import Tooltip from './Tooltip'

const Container = styled.button`
height: 32px;
Expand All @@ -12,7 +13,7 @@ const Container = styled.button`
padding: 0 5px;

background-color: transparent;
${flexCenter}
${flexCenter};

border: none;
border-radius: 3px;
Expand Down Expand Up @@ -49,15 +50,16 @@ const ToolbarIconButton = React.forwardRef(
}: ToolbarButtonProps,
ref
) => (
<Container
onClick={onClick}
onContextMenu={onContextMenu}
className={active ? 'active' : ''}
ref={ref}
title={title}
>
<Icon size={18} path={iconPath} />
</Container>
<Tooltip space={10} text={title}>
<Container
onClick={onClick}
onContextMenu={onContextMenu}
className={active ? 'active' : ''}
ref={ref}
>
<Icon size={18} path={iconPath} />
</Container>
</Tooltip>
)
)

Expand Down
189 changes: 189 additions & 0 deletions src/components/atoms/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import React from 'react'
import GeneralPortal from './GeneralPortal'
import styled from '../../lib/styled/styled'
import { BaseTheme } from '../../lib/styled/BaseTheme'

interface TooltipProps {
text?: string
width?: number
arrowWidth?: number
space?: number
tooltipDelayMs?: number
}

interface TooltipStyle {
width: number | string
left?: number | string
top?: number | string
bottom?: number | string
}

interface TooltipArrowStyle {
width: number | string
left?: number | string
top?: number | string
bottom?: number | string
transform?: string
display?: string
}

interface TooltipState {
visible: boolean
style?: TooltipStyle
arrowStyle?: TooltipArrowStyle
}

interface TooltipBodyProps {
theme: BaseTheme
arrowStyle: TooltipArrowStyle
}

const TooltipBody = styled.div<BaseTheme & TooltipBodyProps>`
position: fixed;
padding: 5px;
background: ${({ theme }) => theme.tooltipBackgroundColor};
color: white;
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.3);
text-align: center;
font-size: 11px;
border-radius: 6px;
z-index: 6000;

&::after {
display: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.display};
content: '';
position: absolute;
bottom: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.bottom};
top: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.top};
left: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.left};
transform: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.transform};
margin-left: ${({ arrowStyle }: TooltipBodyProps) => -arrowStyle.width}px;
border-width: ${({ arrowStyle }: TooltipBodyProps) => arrowStyle.width}px;
border-style: solid;
border-color: transparent transparent
${({ theme }) => theme.tooltipBackgroundColor} transparent;
}
`

const TooltipContainer = styled.span`
//border-bottom: 1px dashed grey;
`

const DEFAULT_TOOLTIP_DELAY_MS = 500

class Tooltip extends React.Component<TooltipProps, TooltipState> {
private readonly width: number
private readonly arrowWidth: number
private readonly space: number
private readonly tooltipDelayMs: number
private showTooltipTimer: any | null
tooltipContainerRef = React.createRef<HTMLSpanElement>()
tooltipRef = React.createRef<HTMLSpanElement>()
constructor(props: TooltipProps) {
super(props)

this.state = {
visible: false,
arrowStyle: {
width: '5px',
},
}

this.arrowWidth = props.arrowWidth || 6
this.width = props.width || 120
this.space = props.space || 4
this.tooltipDelayMs = props.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS

this.showTooltip = this.showTooltip.bind(this)
this.hideTooltip = this.hideTooltip.bind(this)
}

showTooltip() {
if (!this.tooltipContainerRef.current) return
const style: TooltipStyle = {
width: `${this.width}px`,
}
const arrowStyle: TooltipArrowStyle = {
width: `${this.arrowWidth}`,
}
const dimensions = this.tooltipContainerRef.current.getBoundingClientRect()

style.left = dimensions.left + dimensions.width / 2 - this.width / 2
arrowStyle.left = '50%'

// this.space might better be of width size not height offset like now
const leftMargin = document.body.clientWidth - this.width - this.space
if (style.left > leftMargin) {
// we could decide to not show arrow when not in center
// or use below logic to position it at the center of element which tooltip is shown
// arrowStyle.display = 'none'
let newLeftPosition = Math.max(this.space, style.left)
newLeftPosition = Math.min(newLeftPosition, leftMargin)
arrowStyle.left = `${
Math.abs(dimensions.left - newLeftPosition) + dimensions.width / 2
}px`
} else {
arrowStyle.display = 'block'
}

style.left = Math.max(this.space, style.left)
style.left = Math.min(style.left, leftMargin)

const topOfThePagePercent = 0.9
if (dimensions.top < topOfThePagePercent * window.innerHeight) {
style.top = dimensions.top + dimensions.height + this.space
if (this.state.arrowStyle) {
arrowStyle.bottom = '100%'
arrowStyle.top = 'auto'
}
} else {
style.bottom = window.innerHeight - dimensions.top + this.space
if (this.state.arrowStyle) {
arrowStyle.top = '100%'
arrowStyle.bottom = 'auto'
arrowStyle.transform = 'rotate(180deg)'
}
}

this.setState({
visible: true,
style,
arrowStyle,
})
}

hideTooltip() {
clearTimeout(this.showTooltipTimer)
this.setState({ visible: false })
}

render() {
return (
<TooltipContainer
onMouseOver={() => {
this.showTooltipTimer = setTimeout(
this.showTooltip,
this.tooltipDelayMs
)
}}
onMouseOut={this.hideTooltip}
ref={this.tooltipContainerRef}
>
{this.props.children}

{this.state.visible && this.props.text && (
<GeneralPortal>
<TooltipBody
arrowStyle={this.state.arrowStyle}
style={this.state.style}
>
{this.props.text}
</TooltipBody>
</GeneralPortal>
)}
</TooltipContainer>
)
}
}

export default Tooltip
7 changes: 6 additions & 1 deletion src/components/molecules/EditorIndentationStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { openContextMenu } from '../../lib/electronOnly'
import { MenuItemConstructorOptions } from 'electron'
import { capitalize } from '../../lib/string'
import BottomBarButton from '../atoms/BottomBarButton'
import { useTranslation } from 'react-i18next'

const EditorIndentationStatus = () => {
const { preferences, setPreferences } = usePreferences()
const currentIndentType = preferences['editor.indentType']
const currentIndentSize = preferences['editor.indentSize']
const { t } = useTranslation()

const openEditorIndentationContextMenu = useCallback(() => {
openContextMenu({
Expand Down Expand Up @@ -60,7 +62,10 @@ const EditorIndentationStatus = () => {
}, [currentIndentType, currentIndentSize, setPreferences])

return (
<BottomBarButton onClick={openEditorIndentationContextMenu}>
<BottomBarButton
tooltipText={t('editor.editorIndentStatus')}
onClick={openEditorIndentationContextMenu}
>
{capitalize(currentIndentType)}: {currentIndentSize}
</BottomBarButton>
)
Expand Down
Loading