diff --git a/src/renderer/components/badge/badge.scss b/src/renderer/components/badge/badge.scss index 23f8e99ff0..46bb34f391 100644 --- a/src/renderer/components/badge/badge.scss +++ b/src/renderer/components/badge/badge.scss @@ -21,10 +21,25 @@ .Badge { display: inline-block; - white-space: nowrap; + background: $colorVague; + color: $textColorSecondary; + border-radius: $radius; + padding: .2em .4em; max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; + + &.isExpandable { + cursor: pointer; + + &:hover { + background-color: $mainBackground; + } + } + + &:not(.isExpanded) { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } &:not(.flat) { background: $colorVague; diff --git a/src/renderer/components/badge/badge.tsx b/src/renderer/components/badge/badge.tsx index 14959b04d1..6ddd6bc37d 100644 --- a/src/renderer/components/badge/badge.tsx +++ b/src/renderer/components/badge/badge.tsx @@ -22,6 +22,8 @@ import "./badge.scss"; import React from "react"; +import { computed, makeObservable, observable } from "mobx"; +import { observer } from "mobx-react"; import { cssNames } from "../../utils/cssNames"; import { TooltipDecoratorProps, withTooltip } from "../tooltip"; @@ -29,19 +31,76 @@ export interface BadgeProps extends React.HTMLAttributes, TooltipDecoratorP small?: boolean; flat?: boolean; label?: React.ReactNode; + isExpanded?: boolean; // always force state to this value } @withTooltip +@observer export class Badge extends React.Component { - render() { - const { className, label, small, flat, children, ...elemProps } = this.props; - const clickable = Boolean(this.props.onClick); + @observable _isExpanded = false; + @observable hasHighlightedText = false; + @observable ref = React.createRef(); - return <> - + constructor(props: BadgeProps) { + super(props); + makeObservable(this); + } + + componentWillUnmount() { + document.removeEventListener("selectionchange", this.onSelectionChange); + } + + @computed get isExpanded() { + return this.props.isExpanded ?? this._isExpanded; + } + + @computed get isExpandable() { + if (!this.ref.current) { + return false; + } + + const { flat } = this.props; + const { scrollWidth, clientWidth, clientHeight, scrollHeight } = this.ref.current; + + return !flat && (clientWidth < scrollWidth || clientHeight < scrollHeight); + } + + onSelectionChange = () => { + this.hasHighlightedText ||= document.getSelection().toString().length > 0; + }; + + onMouseDown = () => { + this.onSelectionChange(); // initial "event" fire on mouse down (for clearing old selections) + document.addEventListener("selectionchange", this.onSelectionChange); + }; + + onMouseUp = () => { + document.removeEventListener("selectionchange", this.onSelectionChange); + + if (!this.hasHighlightedText) { + this._isExpanded = !this._isExpanded; + } + + this.hasHighlightedText = false; + }; + + render() { + const { isExpandable, isExpanded } = this; + const { className, label, small, flat, children, isExpanded: _, ...elemProps } = this.props; + const clickable = Boolean(this.props.onClick); + const classNames = cssNames("Badge", className, { + small, + flat, + isExpandable, + isExpanded, + clickable, + }); + + return ( +
{label} {children} - - ; +
+ ); } } diff --git a/src/renderer/components/icon/icon.scss b/src/renderer/components/icon/icon.scss index b10eb3708b..02501b3716 100644 --- a/src/renderer/components/icon/icon.scss +++ b/src/renderer/components/icon/icon.scss @@ -153,4 +153,4 @@ @extend .active; } } -} \ No newline at end of file +}