diff --git a/front/src/modules/activities/timeline/components/Timeline.tsx b/front/src/modules/activities/timeline/components/Timeline.tsx index 33f353a5e886..3ab4df0a901a 100644 --- a/front/src/modules/activities/timeline/components/Timeline.tsx +++ b/front/src/modules/activities/timeline/components/Timeline.tsx @@ -18,6 +18,8 @@ import { beautifyPastDateRelativeToNow, } from '~/utils/date-utils'; +import { OverflowingTextWithTooltip } from '../../../ui/tooltip/OverflowingTextWithTooltip'; + const StyledMainContainer = styled.div` align-items: flex-start; align-self: stretch; @@ -144,17 +146,15 @@ const StyledCardTitle = styled.div` color: ${({ theme }) => theme.font.color.primary}; font-weight: ${({ theme }) => theme.font.weight.medium}; line-height: ${({ theme }) => theme.text.lineHeight.lg}; + + width: 100%; `; const StyledCardContent = styled.div` - -webkit-box-orient: vertical; - - -webkit-line-clamp: 3; align-self: stretch; color: ${({ theme }) => theme.font.color.secondary}; - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; + + width: 100%; `; const StyledTooltip = styled(Tooltip)` @@ -279,10 +279,18 @@ export function Timeline({ entity }: { entity: CommentableEntity }) { } > - {commentThread.title ? commentThread.title : '(No title)'} + - {body ? body : '(No content)'} + diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx index 130ce1ad840c..03f24b2a1f9f 100644 --- a/front/src/modules/companies/components/CompanyBoardCard.tsx +++ b/front/src/modules/companies/components/CompanyBoardCard.tsx @@ -45,6 +45,7 @@ const StyledBoardCard = styled.div<{ selected: boolean }>` const StyledBoardCardWrapper = styled.div` padding-bottom: ${({ theme }) => theme.spacing(2)}; + width: 100%; `; const StyledBoardCardHeader = styled.div` @@ -64,6 +65,7 @@ const StyledBoardCardHeader = styled.div` width: ${({ theme }) => theme.icon.size.md}px; } `; + const StyledBoardCardBody = styled.div` display: flex; flex-direction: column; diff --git a/front/src/modules/ui/board/components/BoardColumn.tsx b/front/src/modules/ui/board/components/BoardColumn.tsx index 34b5777ebb28..fe71f21c69c1 100644 --- a/front/src/modules/ui/board/components/BoardColumn.tsx +++ b/front/src/modules/ui/board/components/BoardColumn.tsx @@ -12,7 +12,9 @@ export const StyledColumn = styled.div<{ isFirstColumn: boolean }>` isFirstColumn ? 'none' : theme.border.color.light}; display: flex; flex-direction: column; + max-width: 200px; min-width: 200px; + padding: ${({ theme }) => theme.spacing(2)}; `; diff --git a/front/src/modules/ui/chip/components/EntityChip.tsx b/front/src/modules/ui/chip/components/EntityChip.tsx index a447111c5c05..f18f7bc0fc37 100644 --- a/front/src/modules/ui/chip/components/EntityChip.tsx +++ b/front/src/modules/ui/chip/components/EntityChip.tsx @@ -5,6 +5,8 @@ import styled from '@emotion/styled'; import { Avatar, AvatarType } from '@/users/components/Avatar'; +import { OverflowingTextWithTooltip } from '../../tooltip/OverflowingTextWithTooltip'; + export enum ChipVariant { opaque = 'opaque', transparent = 'transparent', @@ -94,7 +96,9 @@ export function EntityChip({ size={14} type={avatarType} /> - {name} + + + ) : ( @@ -105,7 +109,9 @@ export function EntityChip({ size={14} type={avatarType} /> - {name} + + + ); } diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx index b9e54a8c50de..93392a0b5528 100644 --- a/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx +++ b/front/src/modules/ui/dropdown/components/DropdownMenuSelectableItem.tsx @@ -23,6 +23,8 @@ const DropdownMenuSelectableItemContainer = styled(DropdownMenuItem)` display: flex; justify-content: space-between; + + max-width: 150px; `; const StyledLeftContainer = styled.div` @@ -30,6 +32,8 @@ const StyledLeftContainer = styled.div` display: flex; gap: ${({ theme }) => theme.spacing(2)}; + + overflow: hidden; `; const StyledRightIcon = styled.div` diff --git a/front/src/modules/ui/editable-field/variants/components/NumberEditableField.tsx b/front/src/modules/ui/editable-field/variants/components/NumberEditableField.tsx index 2a23b2dfd8cd..70dfed65bb50 100644 --- a/front/src/modules/ui/editable-field/variants/components/NumberEditableField.tsx +++ b/front/src/modules/ui/editable-field/variants/components/NumberEditableField.tsx @@ -72,7 +72,7 @@ export function NumberEditableField({ /> } displayModeContent={internalValue} - isDisplayModeContentEmpty={!(internalValue !== '')} + isDisplayModeContentEmpty={!(internalValue !== '' && internalValue)} /> ); diff --git a/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx b/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx index d180f10ee77b..1c5a8014a6e5 100644 --- a/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx +++ b/front/src/modules/ui/editable-field/variants/components/TextEditableField.tsx @@ -5,6 +5,8 @@ import { FieldContext } from '@/ui/editable-field/states/FieldContext'; import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText'; import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope'; +import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip'; + type OwnProps = { icon?: React.ReactNode; placeholder?: string; @@ -54,7 +56,7 @@ export function TextEditableField({ }} /> } - displayModeContent={internalValue} + displayModeContent={} isDisplayModeContentEmpty={!(internalValue !== '')} /> diff --git a/front/src/modules/ui/layout/show-page/components/ShowPageSummaryCard.tsx b/front/src/modules/ui/layout/show-page/components/ShowPageSummaryCard.tsx index 4e7cad6db63b..f95811c33a16 100644 --- a/front/src/modules/ui/layout/show-page/components/ShowPageSummaryCard.tsx +++ b/front/src/modules/ui/layout/show-page/components/ShowPageSummaryCard.tsx @@ -9,6 +9,8 @@ import { beautifyPastDateRelativeToNow, } from '~/utils/date-utils'; +import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip'; + type OwnProps = { id?: string; logoOrAvatar?: string; @@ -31,6 +33,7 @@ const StyledInfoContainer = styled.div` display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(1)}; + width: 100%; `; const StyledDate = styled.div` @@ -42,6 +45,8 @@ const StyledTitle = styled.div` color: ${({ theme }) => theme.font.color.primary}; font-size: ${({ theme }) => theme.font.size.xl}; font-weight: ${({ theme }) => theme.font.weight.semiBold}; + + max-width: 100%; `; const StyledTooltip = styled(Tooltip)` @@ -50,6 +55,8 @@ const StyledTooltip = styled(Tooltip)` color: ${({ theme }) => theme.font.color.primary}; + font-size: ${({ theme }) => theme.font.size.sm}; + font-weight: ${({ theme }) => theme.font.weight.regular}; padding: ${({ theme }) => theme.spacing(2)}; `; @@ -74,7 +81,9 @@ export function ShowPageSummaryCard({ placeholder={title} /> - {title} + + + Added {beautifiedCreatedAt} ago diff --git a/front/src/modules/ui/layout/top-bar/components/TopBar.tsx b/front/src/modules/ui/layout/top-bar/components/TopBar.tsx index 301f05af1a49..abcf3175788d 100644 --- a/front/src/modules/ui/layout/top-bar/components/TopBar.tsx +++ b/front/src/modules/ui/layout/top-bar/components/TopBar.tsx @@ -6,6 +6,8 @@ import { IconButton } from '@/ui/button/components/IconButton'; import { IconChevronLeft, IconPlus } from '@/ui/icon/index'; import NavCollapseButton from '@/ui/navbar/components/NavCollapseButton'; +import { OverflowingTextWithTooltip } from '../../../tooltip/OverflowingTextWithTooltip'; + export const TOP_BAR_MIN_HEIGHT = 40; const TopBarContainer = styled.div` @@ -14,20 +16,27 @@ const TopBarContainer = styled.div` color: ${({ theme }) => theme.font.color.primary}; display: flex; flex-direction: row; - font-size: 14px; + font-size: ${({ theme }) => theme.font.size.lg}; + justify-content: space-between; min-height: ${TOP_BAR_MIN_HEIGHT}px; padding: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(3)}; `; -const TitleContainer = styled.div` +const StyledLeftContainer = styled.div` + align-items: center; display: flex; - font-family: 'Inter'; - font-size: 14px; - margin-left: 4px; + flex-direction: row; width: 100%; `; +const TitleContainer = styled.div` + display: flex; + font-size: ${({ theme }) => theme.font.size.md}; + margin-left: ${({ theme }) => theme.spacing(1)}; + max-width: 50%; +`; + const BackIconButton = styled(IconButton)` margin-right: ${({ theme }) => theme.spacing(1)}; `; @@ -51,15 +60,19 @@ export function TopBar({ return ( <> - - {hasBackButton && ( - } - onClick={navigateBack} - /> - )} - {icon} - {title} + + + {hasBackButton && ( + } + onClick={navigateBack} + /> + )} + {icon} + + + + {onAddButtonClick && ( } diff --git a/front/src/modules/ui/relation-picker/components/SingleEntitySelectBase.tsx b/front/src/modules/ui/relation-picker/components/SingleEntitySelectBase.tsx index ffac377c45cb..ca51d51707fc 100644 --- a/front/src/modules/ui/relation-picker/components/SingleEntitySelectBase.tsx +++ b/front/src/modules/ui/relation-picker/components/SingleEntitySelectBase.tsx @@ -9,6 +9,7 @@ import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys'; import { Avatar } from '@/users/components/Avatar'; import { isDefined } from '~/utils/isDefined'; +import { OverflowingTextWithTooltip } from '../../tooltip/OverflowingTextWithTooltip'; import { useEntitySelectScroll } from '../hooks/useEntitySelectScroll'; import { EntityForSelect } from '../types/EntityForSelect'; import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope'; @@ -86,7 +87,7 @@ export function SingleEntitySelectBase< size={16} type={entity.avatarType ?? 'rounded'} /> - {entity.name} + )) )} diff --git a/front/src/modules/ui/themes/border.ts b/front/src/modules/ui/themes/border.ts index 97011a7ee604..03d57ff2379f 100644 --- a/front/src/modules/ui/themes/border.ts +++ b/front/src/modules/ui/themes/border.ts @@ -14,6 +14,7 @@ export const borderLight = { strong: grayScale.gray25, medium: grayScale.gray20, light: grayScale.gray15, + invertedSecondary: grayScale.gray50, inverted: grayScale.gray60, }, ...common, @@ -24,6 +25,7 @@ export const borderDark = { strong: grayScale.gray65, medium: grayScale.gray70, light: grayScale.gray75, + invertedSecondary: grayScale.gray40, inverted: grayScale.gray30, }, ...common, diff --git a/front/src/modules/ui/tooltip/AppTooltip.tsx b/front/src/modules/ui/tooltip/AppTooltip.tsx new file mode 100644 index 000000000000..f7d3e40f1c16 --- /dev/null +++ b/front/src/modules/ui/tooltip/AppTooltip.tsx @@ -0,0 +1,18 @@ +import { Tooltip } from 'react-tooltip'; +import styled from '@emotion/styled'; + +export const AppTooltip = styled(Tooltip)` + background-color: ${({ theme }) => theme.background.primary}; + box-shadow: ${({ theme }) => theme.boxShadow.light}; + + color: ${({ theme }) => theme.font.color.primary}; + + font-size: ${({ theme }) => theme.font.size.sm}; + font-weight: ${({ theme }) => theme.font.weight.regular}; + max-width: 40%; + + padding: ${({ theme }) => theme.spacing(2)}; + word-break: break-word; + + z-index: ${({ theme }) => theme.lastLayerZIndex}; +`; diff --git a/front/src/modules/ui/tooltip/OverflowingTextWithTooltip.tsx b/front/src/modules/ui/tooltip/OverflowingTextWithTooltip.tsx new file mode 100644 index 000000000000..3ddec331fd53 --- /dev/null +++ b/front/src/modules/ui/tooltip/OverflowingTextWithTooltip.tsx @@ -0,0 +1,81 @@ +import { useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import styled from '@emotion/styled'; +import { v4 as uuidV4 } from 'uuid'; + +import { AppTooltip } from './AppTooltip'; + +const StyledOverflowingText = styled.div<{ cursorPointer: boolean }>` + cursor: ${({ cursorPointer }) => (cursorPointer ? 'pointer' : 'inherit')}; + font-family: inherit; + font-size: inherit; + + font-weight: inherit; + overflow: hidden; + text-overflow: ellipsis; + + white-space: nowrap; + width: 100%; +`; + +export function OverflowingTextWithTooltip({ + text, +}: { + text: string | null | undefined; +}) { + const textElementId = `title-id-${uuidV4()}`; + + const textRef = useRef(null); + + const [isTitleOverflowing, setIsTitleOverflowing] = useState(false); + + useEffect(() => { + const isOverflowing = + (text?.length ?? 0) > 0 && textRef.current + ? textRef.current?.scrollHeight > textRef.current?.clientHeight || + textRef.current.scrollWidth > textRef.current.clientWidth + : false; + + if (isTitleOverflowing !== isOverflowing) { + setIsTitleOverflowing(isOverflowing); + } + }, [isTitleOverflowing, text]); + + function handleTooltipClick(event: React.MouseEvent) { + event.stopPropagation(); + event.preventDefault(); + } + + function handleTooltipMouseUp(event: React.MouseEvent) { + event.stopPropagation(); + event.preventDefault(); + } + + return ( + <> + + {text} + + {isTitleOverflowing && + createPortal( +
+ +
, + document.body, + )} + + ); +}