Merge pull request #21115 from overleaf/ii-bs5-review-panel

[web] BS5 review panel new

GitOrigin-RevId: c65d17d0053858bd74984ba746a620b89d900606
This commit is contained in:
ilkin-overleaf 2024-10-18 13:14:45 +03:00 committed by Copybot
parent f84b205b4b
commit 8bc374c916
29 changed files with 1020 additions and 158 deletions

View file

@ -11,4 +11,7 @@ export const bsVersionDecorator: Meta = {
options: ['3', '5'],
},
},
args: {
[bootstrapVersionArg]: '3',
},
}

View file

@ -47,7 +47,7 @@ function ProjectListTable() {
selectOrUnselectAllProjects,
} = useProjectListContext()
const { handleSort } = useSort()
const checkAllRef = useRef<HTMLInputElement>()
const checkAllRef = useRef<HTMLInputElement>(null)
const handleAllProjectsCheckboxChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {

View file

@ -9,12 +9,12 @@ import { useThreadsActionsContext } from '../context/threads-context'
import { removeNewCommentRangeEffect } from '@/features/source-editor/extensions/add-comment'
import useSubmittableTextInput from '../hooks/use-submittable-text-input'
import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
import { Button } from 'react-bootstrap'
import { ReviewPanelEntry } from './review-panel-entry'
import { ThreadId } from '../../../../../types/review-panel/review-panel'
import { Decoration } from '@codemirror/view'
import { useModalsContext } from '@/features/ide-react/context/modals-context'
import { debugConsole } from '@/utils/debugging'
import OLButton from '@/features/ui/components/ol/ol-button'
export const ReviewPanelAddComment: FC<{
docId: string
@ -148,23 +148,23 @@ export const ReviewPanelAddComment: FC<{
disabled={submitting}
/>
<div className="review-panel-add-comment-buttons">
<Button
bsSize="sm"
bsStyle={null}
<OLButton
variant="ghost"
size="sm"
className="review-panel-add-comment-cancel-button"
disabled={submitting}
onClick={handleClose}
>
{t('cancel')}
</Button>
<Button
</OLButton>
<OLButton
type="submit"
bsSize="sm"
className="btn-primary"
variant="primary"
size="sm"
disabled={content === '' || submitting}
>
{t('comment')}
</Button>
</OLButton>
</div>
</form>
</ReviewPanelEntry>

View file

@ -8,8 +8,7 @@ import {
import { useTranslation } from 'react-i18next'
import classnames from 'classnames'
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
import { Button } from 'react-bootstrap'
import Tooltip from '@/shared/components/tooltip'
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import MaterialIcon from '@/shared/components/material-icon'
import { formatTimeBasedOnYear } from '@/features/utils/format-date'
import { useChangesUsersContext } from '../context/changes-users-context'
@ -109,29 +108,30 @@ export const ReviewPanelChange = memo<{
</div>
{editable && permissions.write && (
<div className="review-panel-entry-actions">
<Tooltip
<OLTooltip
id="accept-change"
overlayProps={{ placement: 'bottom' }}
description={t('accept_change')}
tooltipProps={{ className: 'review-panel-tooltip' }}
>
<Button onClick={acceptHandler} bsStyle={null}>
<button type="button" className="btn" onClick={acceptHandler}>
<MaterialIcon
type="check"
className="review-panel-entry-actions-icon"
accessibilityLabel={t('accept_change')}
/>
</Button>
</Tooltip>
</button>
</OLTooltip>
<Tooltip
<OLTooltip
id="reject-change"
description={t('reject_change')}
overlayProps={{ placement: 'bottom' }}
tooltipProps={{ className: 'review-panel-tooltip' }}
>
<Button
bsStyle={null}
<button
type="button"
className="btn"
onClick={() =>
aggregate
? rejectChanges(change.id, aggregate.id)
@ -143,8 +143,8 @@ export const ReviewPanelChange = memo<{
accessibilityLabel={t('reject_change')}
type="close"
/>
</Button>
</Tooltip>
</button>
</OLTooltip>
</div>
)}
</div>

View file

@ -1,29 +1,80 @@
import ControlledDropdown from '@/shared/components/controlled-dropdown'
import MaterialIcon from '@/shared/components/material-icon'
import { FC, memo } from 'react'
import { Dropdown, MenuItem } from 'react-bootstrap'
import { FC, memo, forwardRef } from 'react'
import {
Dropdown as BS3Dropdown,
MenuItem as BS3MenuItem,
} from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import {
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
import classnames from 'classnames'
const ReviewPanelCommentDropdownToggleButton = forwardRef<
HTMLButtonElement,
React.ButtonHTMLAttributes<HTMLButtonElement>
>((props, ref) => (
<button {...props} ref={ref} className={classnames(props.className, 'btn')} />
))
ReviewPanelCommentDropdownToggleButton.displayName =
'ReviewPanelCommentDropdownToggleButton'
const ReviewPanelCommentOptions: FC<{
onEdit: () => void
onDelete: () => void
}> = ({ onEdit, onDelete }) => {
id: string
}> = ({ onEdit, onDelete, id }) => {
const { t } = useTranslation()
return (
<ControlledDropdown id="review-panel-comment-options" pullRight>
<Dropdown.Toggle noCaret bsSize="small" bsStyle={null}>
<BootstrapVersionSwitcher
bs3={
<ControlledDropdown id={`review-panel-comment-options-${id}`} pullRight>
<BS3Dropdown.Toggle noCaret bsSize="small" bsStyle={null}>
<MaterialIcon
type="more_vert"
className="review-panel-entry-actions-icon"
accessibilityLabel={t('more_options')}
/>
</Dropdown.Toggle>
<Dropdown.Menu>
<MenuItem onClick={onEdit}>{t('edit')}</MenuItem>
<MenuItem onClick={onDelete}>{t('delete')}</MenuItem>
</Dropdown.Menu>
</BS3Dropdown.Toggle>
<BS3Dropdown.Menu>
<BS3MenuItem onClick={onEdit}>{t('edit')}</BS3MenuItem>
<BS3MenuItem onClick={onDelete}>{t('delete')}</BS3MenuItem>
</BS3Dropdown.Menu>
</ControlledDropdown>
}
bs5={
<Dropdown align="end">
<DropdownToggle
as={ReviewPanelCommentDropdownToggleButton}
id={`review-panel-comment-options-btn-${id}`}
>
<MaterialIcon
type="more_vert"
className="review-panel-entry-actions-icon"
accessibilityLabel={t('more_options')}
/>
</DropdownToggle>
<DropdownMenu flip={false}>
<li role="none">
<DropdownItem as="button" onClick={onEdit}>
{t('edit')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem as="button" onClick={onDelete}>
{t('delete')}
</DropdownItem>
</li>
</DropdownMenu>
</Dropdown>
}
/>
)
}

View file

@ -1,7 +1,12 @@
import AccessibleModal from '@/shared/components/accessible-modal'
import { FC, memo } from 'react'
import { Button, Modal } from 'react-bootstrap'
import OLButton from '@/features/ui/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
import OLModal, {
OLModalBody,
OLModalFooter,
OLModalHeader,
OLModalTitle,
} from '@/features/ui/components/ol/ol-modal'
const ReviewPanelDeleteCommentModal: FC<{
onHide: () => void
@ -12,20 +17,20 @@ const ReviewPanelDeleteCommentModal: FC<{
const { t } = useTranslation()
return (
<AccessibleModal show onHide={onHide}>
<Modal.Header>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>{message}</Modal.Body>
<Modal.Footer>
<Button bsStyle={null} className="btn-secondary" onClick={onHide}>
<OLModal show onHide={onHide}>
<OLModalHeader>
<OLModalTitle>{title}</OLModalTitle>
</OLModalHeader>
<OLModalBody>{message}</OLModalBody>
<OLModalFooter>
<OLButton variant="secondary" onClick={onHide}>
{t('cancel')}
</Button>
<Button bsStyle="danger" onClick={onDelete}>
</OLButton>
<OLButton variant="danger" onClick={onDelete}>
{t('delete')}
</Button>
</Modal.Footer>
</AccessibleModal>
</OLButton>
</OLModalFooter>
</OLModal>
)
}

View file

@ -1,6 +1,6 @@
import { FC, useCallback, useEffect, useRef, useState } from 'react'
import classNames from 'classnames'
import { Button } from 'react-bootstrap'
import OLButton from '@/features/ui/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
export const ExpandableContent: FC<{ className?: string }> = ({
@ -49,22 +49,22 @@ export const ExpandableContent: FC<{ className?: string }> = ({
{children}
</div>
{isExpanded ? (
<Button
bsStyle="link"
<OLButton
variant="link"
className="btn-inline-link"
onClick={handleShowLess}
>
{t('show_less')}
</Button>
</OLButton>
) : (
isOverflowing && (
<Button
bsStyle="link"
<OLButton
variant="link"
className="btn-inline-link"
onClick={handleShowMore}
>
{t('show_more')}
</Button>
</OLButton>
)
)}
</div>

View file

@ -2,7 +2,6 @@ import { FC, memo, useState } from 'react'
import { ReviewPanelResolvedThreadsButton } from './review-panel-resolved-threads-button'
import { ReviewPanelTrackChangesMenu } from './review-panel-track-changes-menu'
import ReviewPanelTrackChangesMenuButton from './review-panel-track-changes-menu-button'
import { Button } from 'react-bootstrap'
import MaterialIcon from '@/shared/components/material-icon'
import { useLayoutContext } from '@/shared/context/layout-context'
import SplitTestBadge from '@/shared/components/split-test-badge'
@ -26,14 +25,13 @@ const ReviewPanelHeader: FC = () => {
/>
</span>
</div>
<Button
bsStyle={null}
className="review-panel-close-button"
<button
type="button"
className="btn review-panel-close-button"
onClick={() => setReviewPanelOpen(false)}
>
<MaterialIcon type="close" />
</Button>
</button>
</div>
<div className="review-panel-tools">
<ReviewPanelResolvedThreadsButton />

View file

@ -5,8 +5,7 @@ import {
} from '../../../../../types/review-panel/review-panel'
import { useTranslation } from 'react-i18next'
import { formatTimeBasedOnYear } from '@/features/utils/format-date'
import Tooltip from '@/shared/components/tooltip'
import { Button } from 'react-bootstrap'
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import MaterialIcon from '@/shared/components/material-icon'
import AutoExpandingTextArea from '@/shared/components/auto-expanding-text-area'
import { buildName } from '../utils/build-name'
@ -90,26 +89,27 @@ export const ReviewPanelMessage: FC<{
<div className="review-panel-entry-actions">
{!isReply && !isThreadResolved && (
<Tooltip
<OLTooltip
id="resolve-thread"
overlayProps={{ placement: 'bottom' }}
description={t('resolve_comment')}
tooltipProps={{ className: 'review-panel-tooltip' }}
>
<Button onClick={onResolve} bsStyle={null}>
<button type="button" className="btn" onClick={onResolve}>
<MaterialIcon
type="check"
className="review-panel-entry-actions-icon"
accessibilityLabel={t('resolve_comment')}
/>
</Button>
</Tooltip>
</button>
</OLTooltip>
)}
{!isThreadResolved && (
<ReviewPanelCommentOptions
onEdit={handleEditOption}
onDelete={showDeleteModal}
id={message.id}
/>
)}
</div>

View file

@ -1,8 +1,9 @@
import { FC, memo } from 'react'
import { Button } from 'react-bootstrap'
import MaterialIcon from '@/shared/components/material-icon'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import OLButton from '@/features/ui/components/ol/ol-button'
import { bsVersion } from '@/features/utils/bootstrap-5'
const MoreCommentsButton: FC<{
onClick: () => void
@ -17,14 +18,18 @@ const MoreCommentsButton: FC<{
upwards: direction === 'upward',
})}
>
<Button
bsSize="small"
className="btn-secondary review-panel-more-comments-button"
<OLButton
variant="secondary"
size="sm"
className={bsVersion({ bs3: 'review-panel-more-comments-button' })}
onClick={onClick}
bs3Props={{
bsSize: 'xsmall',
}}
>
<MaterialIcon type={`arrow_${direction}_alt`} />
{t('more_comments')}
</Button>
</OLButton>
</div>
)
}

View file

@ -11,7 +11,6 @@ import {
} from '../../../../../types/change'
import { canAggregate } from '../utils/can-aggregate'
import { Button } from 'react-bootstrap'
import MaterialIcon from '@/shared/components/material-icon'
import useOverviewFileCollapsed from '../hooks/use-overview-file-collapsed'
import { useThreadsContext } from '../context/threads-context'
@ -61,9 +60,9 @@ export const ReviewPanelOverviewFile: FC<{
return (
<>
<div>
<Button
bsClass="review-panel-overview-file-header"
bsStyle={null}
<button
type="button"
className="review-panel-overview-file-header"
onClick={toggleCollapsed}
>
<MaterialIcon
@ -73,7 +72,7 @@ export const ReviewPanelOverviewFile: FC<{
<div className="review-panel-overview-file-entry-count">
{numEntries}
</div>
</Button>
</button>
{!collapsed && (
<div className="review-panel-overview-file-entries">

View file

@ -2,12 +2,11 @@ import React, { FC, useCallback, useState } from 'react'
import { useTranslation, Trans } from 'react-i18next'
import { ThreadId } from '../../../../../types/review-panel/review-panel'
import { useThreadsActionsContext } from '../context/threads-context'
import { Button } from 'react-bootstrap'
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import MaterialIcon from '@/shared/components/material-icon'
import { ExpandableContent } from './review-panel-expandable-content'
import { ReviewPanelCommentContent } from './review-panel-comment-content'
import { Change, CommentOperation } from '../../../../../types/change'
import Tooltip from '@/shared/components/tooltip'
import classNames from 'classnames'
import { debugConsole } from '@/utils/debugging'
import { useModalsContext } from '@/features/ide-react/context/modals-context'
@ -73,25 +72,25 @@ export const ReviewPanelResolvedThread: FC<{
/>
</div>
<div className="review-panel-resolved-comment-buttons">
<Tooltip
<OLTooltip
id="reopen-thread"
overlayProps={{ placement: 'bottom' }}
description={t('reopen')}
>
<Button onClick={handleReopenThread}>
<button type="button" className="btn" onClick={handleReopenThread}>
<MaterialIcon type="refresh" accessibilityLabel={t('reopen')} />
</Button>
</Tooltip>
</button>
</OLTooltip>
<Tooltip
<OLTooltip
id="delete-thread"
overlayProps={{ placement: 'bottom' }}
description={t('delete')}
>
<Button onClick={handleDeleteThread}>
<button type="button" className="btn" onClick={handleDeleteThread}>
<MaterialIcon type="delete" accessibilityLabel={t('delete')} />
</Button>
</Tooltip>
</button>
</OLTooltip>
</div>
</div>
<div className="review-panel-resolved-comment-quoted-text">

View file

@ -1,9 +1,12 @@
import React, { FC, useRef, useState } from 'react'
import Icon from '@/shared/components/icon'
import OLOverlay from '@/features/ui/components/ol/ol-overlay'
import OLPopover from '@/features/ui/components/ol/ol-popover'
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import { ReviewPanelResolvedThreadsMenu } from './review-panel-resolved-threads-menu'
import { Overlay, Popover } from 'react-bootstrap'
import Tooltip from '@/shared/components/tooltip'
import { useTranslation } from 'react-i18next'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import MaterialIcon from '@/shared/components/material-icon'
export const ReviewPanelResolvedThreadsButton: FC = () => {
const [expanded, setExpanded] = useState(false)
@ -12,7 +15,7 @@ export const ReviewPanelResolvedThreadsButton: FC = () => {
return (
<>
<Tooltip
<OLTooltip
id="resolved-comments"
overlayProps={{ placement: 'bottom' }}
description={t('resolved_comments')}
@ -22,27 +25,30 @@ export const ReviewPanelResolvedThreadsButton: FC = () => {
ref={buttonRef}
onClick={() => setExpanded(true)}
>
<Icon type="inbox" fw />
<BootstrapVersionSwitcher
bs3={<Icon type="inbox" fw />}
bs5={<MaterialIcon type="inbox" />}
/>
</button>
</Tooltip>
</OLTooltip>
{expanded && (
<Overlay
<OLOverlay
show
onHide={() => setExpanded(false)}
animation={false}
transition={false}
container={this}
containerPadding={0}
placement="bottom"
rootClose
target={buttonRef.current ?? undefined}
target={buttonRef.current}
>
<Popover
<OLPopover
id="popover-resolved-threads"
className="review-panel-resolved-comments review-panel-new"
>
<ReviewPanelResolvedThreadsMenu />
</Popover>
</Overlay>
</OLPopover>
</OLOverlay>
)}
</>
)

View file

@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next'
import { ReviewPanelResolvedThread } from './review-panel-resolved-thread'
import useProjectRanges from '../hooks/use-project-ranges'
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
import Icon from '@/shared/components/icon'
import { Change, CommentOperation } from '../../../../../types/change'
import { ThreadId } from '../../../../../types/review-panel/review-panel'
import LoadingSpinner from '@/shared/components/loading-spinner'
export const ReviewPanelResolvedThreadsMenu: FC = () => {
const { t } = useTranslation()
@ -59,11 +59,7 @@ export const ReviewPanelResolvedThreadsMenu: FC = () => {
}, [threads])
if (loading) {
return (
<div className="review-panel-resolved-comments-loading">
<Icon type="spinner" spin />
</div>
)
return <LoadingSpinner className="ms-auto me-auto" />
}
if (!resolvedThreads.length) {

View file

@ -2,6 +2,8 @@ import { FC, memo } from 'react'
import { Trans } from 'react-i18next'
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
import Icon from '@/shared/components/icon'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import MaterialIcon from '@/shared/components/material-icon'
const ReviewPanelTrackChangesMenuButton: FC<{
menuExpanded: boolean
@ -26,7 +28,12 @@ const ReviewPanelTrackChangesMenuButton: FC<{
components={{ strong: <strong /> }}
/>
)}
<Icon type={menuExpanded ? 'angle-down' : 'angle-right'} />
<BootstrapVersionSwitcher
bs3={<Icon type={menuExpanded ? 'angle-down' : 'angle-right'} />}
bs5={
<MaterialIcon type={menuExpanded ? 'expand_more' : 'chevron_right'} />
}
/>
</button>
)
}

View file

@ -24,7 +24,7 @@ const ReviewPanel: FC<{ mini?: boolean }> = ({ mini = false }) => {
return (
<div className={className} style={style}>
<div className="review-panel-inner">
<div id="review-panel-inner" className="review-panel-inner">
{!mini && <ReviewPanelHeader />}
{activeSubView === 'cur_file' && <ReviewPanelCurrentFile />}

View file

@ -1,3 +1,5 @@
import OLFormSwitch from '@/features/ui/components/ol/ol-form-switch'
type TrackChangesToggleProps = {
id: string
description: string
@ -14,20 +16,13 @@ function TrackChangesToggle({
value,
}: TrackChangesToggleProps) {
return (
<div className="input-switch">
<input
<OLFormSwitch
id={`input-switch-${id}`}
disabled={disabled}
type="checkbox"
autoComplete="off"
className="input-switch-hidden-input"
onChange={handleToggle}
checked={value}
label={description}
/>
<label htmlFor={`input-switch-${id}`} className="input-switch-btn">
<span className="sr-only">{description}</span>
</label>
</div>
)
}

View file

@ -103,9 +103,11 @@ const ForwardReferredDropdownItem = fixedForwardRef(DropdownItem, {
export { ForwardReferredDropdownItem as DropdownItem }
export function DropdownToggle(props: DropdownToggleProps) {
return <BS5DropdownToggle {...props} />
}
export const DropdownToggle = forwardRef<
typeof BS5DropdownToggle,
DropdownToggleProps
>((props, ref) => <BS5DropdownToggle {...props} ref={ref} />)
DropdownToggle.displayName = 'DropdownToggle'
export const DropdownMenu = forwardRef<
typeof BS5DropdownMenu,

View file

@ -4,7 +4,7 @@ import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/boots
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
type OLFormCheckboxProps = React.ComponentProps<(typeof Form)['Check']> & {
inputRef?: React.MutableRefObject<HTMLInputElement | undefined>
inputRef?: React.MutableRefObject<HTMLInputElement | null>
bs3Props?: Record<string, unknown>
}

View file

@ -0,0 +1,54 @@
import { FormCheck, FormCheckProps, FormLabel } from 'react-bootstrap-5'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
type OLFormSwitchProps = FormCheckProps & {
inputRef?: React.MutableRefObject<HTMLInputElement | null>
bs3Props?: React.InputHTMLAttributes<HTMLInputElement>
}
function OLFormSwitch(props: OLFormSwitchProps) {
const { bs3Props, inputRef, label, id, ...rest } = props
const bs3FormSwitchProps: React.InputHTMLAttributes<HTMLInputElement> = {
id,
checked: rest.checked,
required: rest.required,
readOnly: rest.readOnly,
disabled: rest.disabled,
autoComplete: rest.autoComplete,
defaultChecked: rest.defaultChecked,
onChange: rest.onChange as (e: React.ChangeEvent<unknown>) => void,
...getAriaAndDataProps(rest),
...bs3Props,
}
return (
<BootstrapVersionSwitcher
bs3={
<div className="input-switch">
<input
id={id}
type="checkbox"
className="input-switch-hidden-input"
ref={inputRef}
{...bs3FormSwitchProps}
/>
<label htmlFor={id} className="input-switch-btn">
<span className="sr-only">{label}</span>
</label>
</div>
}
bs5={
<>
<FormCheck type="switch" ref={inputRef} id={id} {...rest} />
<FormLabel htmlFor={id} visuallyHidden>
{label}
</FormLabel>
</>
}
/>
)
}
export default OLFormSwitch

View file

@ -0,0 +1,31 @@
import OLFormSwitch from '@/features/ui/components/ol/ol-form-switch'
import { bsVersionDecorator } from '../../.storybook/utils/with-bootstrap-switcher'
import { disableControlsOf } from './utils/arg-types'
export const Unchecked = () => {
return <OLFormSwitch onChange={() => {}} checked={false} />
}
export const UncheckedDisabled = () => {
return <OLFormSwitch onChange={() => {}} checked={false} disabled />
}
export const Checked = () => {
return <OLFormSwitch onChange={() => {}} checked />
}
export const CheckedDisabled = () => {
return <OLFormSwitch onChange={() => {}} checked disabled />
}
export default {
title: 'Shared / Components / Input Switch',
component: OLFormSwitch,
argTypes: {
...bsVersionDecorator.argTypes,
...disableControlsOf('inputRef', 'bs3Props'),
},
args: {
...bsVersionDecorator.args,
},
}

View file

@ -1,14 +0,0 @@
import Switch from '../js/shared/components/switch'
export const Unchecked = () => {
return <Switch onChange={() => {}} checked={false} />
}
export const Checked = () => {
return <Switch onChange={() => {}} checked />
}
export default {
title: 'Shared / Components / Switch',
component: Switch,
}

View file

@ -86,7 +86,7 @@
.review-panel-entry-actions-icon {
padding: @spacing-01;
font-size: 20px;
font-size: @font-size-05;
}
}
}
@ -111,7 +111,7 @@
.review-panel-entry-icon {
border-radius: @border-radius-base-new;
padding: @spacing-02;
font-size: 16px;
font-size: @font-size-03;
}
.review-panel-entry-change-icon {
@ -138,7 +138,6 @@
top: var(--review-panel-top);
width: var(--review-panel-width);
height: @review-panel-header-height;
z-index: 2;
display: flex;
flex-direction: column;
justify-content: center;
@ -187,7 +186,7 @@
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
font-size: @font-size-02;
i {
width: 8px;
@ -202,7 +201,7 @@
.review-panel-label {
font-family: Lato, sans-serif;
font-size: 14px;
font-size: @font-size-02;
font-weight: bold;
margin: 0;
}
@ -245,10 +244,6 @@
pointer-events: none;
}
.review-panel-resolved-comments-loading {
text-align: center;
}
.review-panel-resolved-comments-empty {
text-align: center;
}
@ -472,7 +467,7 @@
width: var(--review-panel-width);
z-index: 2;
background-color: white;
border-top: 1px solid #d9d9d9;
border-top: 1px solid @rp-border-grey;
display: flex;
.review-panel-tab {
@ -507,11 +502,6 @@
min-height: 44px;
}
.review-panel-add-comment {
position: absolute;
z-index: 2;
}
.review-panel-add-comment-buttons {
display: flex;
justify-content: flex-end;
@ -545,12 +535,11 @@
}
}
// Would not be migrated
.review-panel-more-comments-button {
display: flex;
justify-content: center;
align-items: center;
padding: 2px 8px;
height: 24px;
}
&.review-panel-subview-overview {

View file

@ -6,6 +6,7 @@ $prefix: bs-;
// Quickly modify global styling by enabling or disabling optional features.
$enable-validation-icons: false;
$component-active-color: $white;
// Fonts
$font-family-sans-serif: 'Noto Sans', sans-serif;
@ -115,6 +116,15 @@ $form-check-input-border-radius: $border-radius-base;
$form-check-input-focus-border: var(--border-primary);
$form-check-input-disabled-opacity: 1;
// form-switch-variables
$form-switch-width: 34px;
$form-switch-padding-start: $spacing-03;
$form-switch-color: $component-active-color;
$form-switch-width: 34px;
$form-switch-padding-start: $spacing-03;
$form-switch-focus-color: $component-active-color;
$form-switch-checked-color: $component-active-color;
// Form validation
// form-feedback-variables

View file

@ -73,6 +73,32 @@
}
}
.form-switch {
.form-check-input {
--bs-form-check-bg: var(--bg-dark-tertiary);
height: 20px;
margin: 0;
border: 0;
&:hover {
cursor: pointer;
}
&:disabled {
opacity: 0.32;
background-color: var(--bg-dark-tertiary);
&:checked {
background-color: var(--bg-accent-01);
background-image: #{escape-svg(
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$component-active-color}'/></svg>")
)};
}
}
}
}
.form-text {
display: inline-flex;
gap: $spacing-02;

View file

@ -22,6 +22,7 @@
@import 'editor/publish-modal';
@import 'editor/share';
@import 'editor/tags-input';
@import 'editor/review-panel-new';
@import 'website-redesign';
@import 'group-settings';
@import 'templates-v2';

View file

@ -0,0 +1,638 @@
.review-panel-new {
$review-panel-header-height: 69px;
&.review-panel-container {
height: 100%;
flex-shrink: 0;
position: relative;
}
.review-panel-inner {
z-index: 6;
flex-shrink: 0;
background-color: var(--neutral-10);
border-left: solid 0 var(--neutral-20);
color: var(--content-primary);
font-family: $font-family-base;
line-height: $line-height-base;
font-size: var(--font-size-01);
box-sizing: content-box;
width: var(--review-panel-width);
min-height: var(--review-panel-height);
.dropdown-menu {
z-index: 1;
min-width: var(--bs-dropdown-min-width);
}
}
.review-panel-entry {
background-color: var(--white);
border-radius: var(--border-radius-base);
border: 1px solid var(--neutral-20);
padding: var(--spacing-04);
width: calc(100% - var(--spacing-04));
margin-left: var(--spacing-02);
z-index: 1;
}
.review-panel-entry.review-panel-entry-disabled {
opacity: 0.5;
pointer-events: none;
}
.review-panel-entry-indicator {
display: none;
}
.review-panel-entry-content {
display: flex;
flex-direction: column;
font-size: var(--font-size-01);
gap: var(--spacing-04);
}
.review-panel-entry-focused,
.review-panel-entry-highlighted {
margin-left: var(--spacing-01);
border: 1px solid var(--border-active);
}
.review-panel-entry-header {
display: flex;
justify-content: space-between;
margin-bottom: var(--spacing-01);
.review-panel-entry-user {
color: var(--bg-info-01);
font-size: 110%;
}
.review-panel-entry-time {
color: var(--content-secondary);
}
.review-panel-entry-actions {
display: flex;
align-items: center;
gap: var(--spacing-03);
.btn {
background-color: transparent;
border-width: 0;
padding: 0;
height: 24px;
width: 24px;
&:hover,
&:focus {
background-color: var(--neutral-20);
color: var(--content-primary);
}
}
.dropdown-toggle::after {
display: none;
}
.review-panel-entry-actions-icon {
padding: var(--spacing-01);
font-size: var(--font-size-05);
}
}
}
.review-panel-change-body {
display: flex;
align-items: flex-start;
color: var(--content-secondary);
gap: var(--spacing-02);
overflow-wrap: anywhere;
}
.review-panel-content-highlight {
color: var(--content-primary);
text-decoration: none;
}
del.review-panel-content-highlight {
text-decoration: line-through;
}
.review-panel-entry-icon {
border-radius: var(--border-radius-base);
padding: var(--spacing-02);
font-size: var(--font-size-03);
}
.review-panel-entry-change-icon {
margin-top: calc(-1 * var(--spacing-01));
}
.review-panel-entry-icon-accept {
background-color: var(--bg-accent-03);
color: var(--bg-accent-01);
}
.review-panel-entry-icon-reject {
background-color: var(--bg-danger-03);
color: var(--bg-danger-01);
}
.review-panel-entry-icon-changed {
background-color: var(--neutral-20);
color: var(--content-secondary);
}
.review-panel-header {
position: fixed;
top: var(--review-panel-top);
width: var(--review-panel-width);
height: $review-panel-header-height;
display: flex;
flex-direction: column;
justify-content: center;
border-bottom: 1px solid var(--rp-border-grey);
background-color: white;
text-align: center;
z-index: 4;
}
// TODO: Update this when we move the track changes menu to the new design
.rp-tc-state {
background-color: var(--white);
max-height: calc(
100vh - var(--review-panel-top) - $review-panel-header-height
);
overflow-y: auto;
}
.review-panel-tools {
display: flex;
align-items: center;
justify-content: space-between;
padding-left: var(--spacing-02);
padding-right: var(--spacing-05);
flex-shrink: 0;
flex-basis: 32px;
}
.resolved-comments-toggle {
display: flex;
align-items: center;
justify-content: center;
}
.track-changes-indicator-circle {
width: 8px;
height: 8px;
border-radius: 100%;
background-color: var(--bg-accent-01);
}
.track-changes-menu-button {
border: none;
background: none;
padding: 0;
display: flex;
align-items: center;
gap: var(--spacing-02);
font-size: var(--font-size-02);
i {
width: 8px;
}
}
.review-panel-heading {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-03) var(--spacing-02);
.review-panel-label {
font-size: var(--font-size-02);
font-weight: bold;
margin: 0;
}
.review-panel-split-test-badge {
margin-left: var(--spacing-02);
}
.review-panel-close-button {
display: flex;
align-items: center;
border: none;
background-color: transparent;
color: var(--content-primary);
padding: var(--spacing-01);
&:hover,
&:focus {
background-color: var(--neutral-20);
}
}
}
&.review-panel-resolved-comments {
--bs-popover-border-width: 1px;
--bs-popover-bg: var(--bg-light-secondary);
--bs-popover-body-color: var(--content-secondary);
width: 280px;
.popover-body {
overflow-y: auto;
max-height: calc(100vh - 180px);
display: flex;
flex-direction: column;
gap: var(--spacing-02);
padding: var(--spacing-04) var(--spacing-03);
}
}
.review-panel-resolved-disabled {
opacity: 0.5;
pointer-events: none;
}
.review-panel-resolved-comments-empty {
text-align: center;
}
.review-panel-resolved-comments-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-02) 0;
}
.review-panel-resolved-comments-label {
font-weight: bold;
font-size: var(--font-size-02);
}
.review-panel-resolved-comments-count {
background-color: var(--neutral-20);
border-radius: var(--border-radius-base);
padding: var(--spacing-01);
}
.review-panel-resolved-comment {
background-color: var(--white);
border-radius: var(--border-radius-base);
padding: var(--spacing-04);
display: flex;
flex-direction: column;
gap: var(--spacing-04);
}
.review-panel-resolved-comment-header {
display: flex;
justify-content: space-between;
align-items: center;
color: var(--content-secondary);
font-size: var(--font-size-01);
}
.review-panel-resolved-comment-filename {
color: var(--content-primary);
}
.review-panel-resolved-comment-buttons {
display: flex;
align-items: center;
gap: var(--spacing-03);
.btn {
background-color: transparent;
border-width: 0;
color: var(--content-primary);
padding: var(--spacing-01);
height: 24px;
width: 24px;
&:hover,
&:focus {
background-color: var(--neutral-20);
}
}
.material-symbols {
font-size: var(--font-size-05);
}
}
.review-panel-resolved-comment-quoted-text {
background-color: var(--neutral-20);
border-radius: var(--border-radius-base);
padding: var(--spacing-02) var(--spacing-04);
}
.review-panel-resolved-comment-quoted-text-label {
color: var(--content-secondary);
font-size: var(--font-size-01);
}
.review-panel-resolved-comment-quoted-text-quote {
overflow-wrap: anywhere;
font-size: var(--font-size-02);
}
.review-panel-comment-wrapper {
display: flex;
gap: var(--spacing-04);
}
.review-panel-comment {
flex-grow: 1;
}
.review-panel-comment-reply-divider {
border-left: 2px solid var(--yellow-20);
}
.review-panel-comment-body {
font-size: var(--font-size-02);
color: var(--content-primary);
overflow-wrap: anywhere;
}
.review-panel-content-expandable {
display: -webkit-box;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
overflow: hidden;
}
.review-panel-content-expanded {
display: block;
}
.review-panel-comment-input {
width: 100%;
font-size: var(--rp-base-font-size);
padding: 2px var(--spacing-03);
border-radius: var(--border-radius-base);
border: solid 1px var(--neutral-60);
resize: vertical;
color: var(--rp-type-darkgrey);
background-color: var(--white);
height: 25px;
min-height: 25px;
overflow-x: hidden;
max-height: 400px;
}
.review-panel-empty-state {
position: fixed;
width: var(--review-panel-width);
top: 0;
bottom: 0;
pointer-events: none;
}
.review-panel-empty-state-inner {
position: sticky;
top: 50%;
transform: translateY(-50%);
width: 100%;
padding-left: var(--spacing-06);
padding-right: var(--spacing-06);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
margin-bottom: 0;
text-align: center;
}
}
.review-panel-empty-state-comment-icon {
width: 80px;
height: 80px;
background-color: white;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--spacing-06);
.material-symbols {
font-size: 32px;
}
}
.review-panel-overview {
padding: var(--spacing-02);
position: absolute;
top: $review-panel-header-height;
bottom: 59px;
width: 100%;
overflow: auto;
overscroll-behavior-block: none;
.review-panel-entry {
margin-left: 0;
width: 100%;
}
}
.review-panel-overview-file-header {
all: unset;
padding: var(--spacing-03) var(--spacing-04);
font-size: var(--font-size-02);
cursor: pointer;
display: flex;
align-items: center;
gap: var(--spacing-04);
box-sizing: border-box;
width: 100%;
}
.review-panel-overfile-divider {
border-bottom: 1px solid var(--border-divider);
margin: var(--spacing-01) 0;
}
.review-panel-overview-file-entries {
overflow: hidden;
padding-top: var(--spacing-02);
padding-bottom: var(--spacing-03);
}
.review-panel-overview-file-entry-count {
background-color: var(--neutral-20);
padding: var(--spacing-01) var(--spacing-02);
margin-left: auto;
border-radius: var(--border-radius-base);
}
.review-panel-footer {
position: fixed;
height: 60px;
bottom: 0;
width: var(--review-panel-width);
z-index: 2;
background-color: white;
border-top: 1px solid var(--rp-border-grey);
display: flex;
.review-panel-tab {
flex: 0 0 50%;
padding: var(--spacing-03) 0;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--spacing-02);
border: 0;
border-top: solid 3px transparent;
background: none;
color: var(--content-secondary);
font-size: var(--font-size-02);
&:hover,
&:focus {
text-decoration: none;
color: var(--content-primary);
}
&-active {
color: var(--content-primary);
border-top: solid 3px var(--bg-accent-01);
}
}
}
.review-panel-add-comment-textarea {
padding: var(--spacing-01) var(--spacing-03);
resize: vertical;
min-height: 44px;
}
.review-panel-add-comment-buttons {
display: flex;
justify-content: flex-end;
gap: var(--spacing-04);
}
.review-panel-add-comment-cancel-button {
background-color: transparent;
&:hover,
&:focus {
background-color: var(--neutral-20);
color: var(--content-primary);
}
}
.review-panel-more-comments-button-container {
position: fixed;
width: var(--review-panel-width);
display: flex;
justify-content: center;
z-index: 3;
&.downwards {
// TODO: fix this to not use a magic number when we have updated the footer ui
top: calc(100% - 102px);
}
&.upwards {
top: calc(var(--review-panel-top) + $review-panel-header-height + 16px);
}
}
&.review-panel-subview-overview {
&.review-panel-container {
overflow-y: hidden;
position: sticky;
top: 0;
}
.review-panel-inner {
min-height: auto;
height: 100%;
overflow: hidden;
}
}
&.review-panel-mini {
overflow: visible !important;
.review-panel-inner {
width: 24px;
}
.review-panel-entry {
margin-left: 0;
background-color: transparent;
border: none;
width: 100%;
}
.review-panel-entry-indicator {
position: absolute;
left: 0;
top: 0;
display: flex;
color: var(--content-secondary);
cursor: pointer;
}
.review-panel-entry-content {
display: none;
background: var(--white);
border: 1px solid var(--rp-border-grey);
border-radius: var(--border-radius-base);
width: 200px;
padding: var(--spacing-02);
}
.review-panel-entry-hover {
.review-panel-entry-content {
display: initial;
position: absolute;
left: -200px;
top: 0;
}
}
.review-panel-more-comments-button-container {
display: none;
}
.review-panel-footer {
display: none;
}
}
}
.review-tooltip-menu {
box-shadow: 0 2px 4px 0 #1e253029;
border: none;
border-radius: var(--border-radius-base);
padding: var(--spacing-02);
}
.review-tooltip-menu-button {
background-color: inherit;
border: none;
display: flex;
align-items: center;
gap: var(--spacing-01);
padding: var(--spacing-01);
}
.review-tooltip-add-comment-button {
padding: var(--spacing-01) var(--spacing-04);
}
.review-panel-tooltip {
pointer-events: none; // this is to prevent mouseLeave event from firing when hovering over the tooltip
}

View file

@ -1,5 +1,11 @@
@use 'sass:color';
:root {
--review-icon: url('../../../../../public/img/ol-icons/review-icon-dark-theme.svg');
--rp-base-font-size: var(--font-size-01);
--rp-bg-dim-blue: #fafafa;
--rp-border-grey: #d9d9d9;
--rp-type-darkgrey: #3f3f3f;
}
@include theme('light') {
@ -15,6 +21,43 @@
}
}
$rp-type-blue: #6b7797;
.rp-tc-state {
position: absolute;
top: 100%;
left: 0;
right: 0;
overflow: hidden;
list-style: none;
padding: 0 var(--spacing-03);
margin: 0;
border-bottom: 1px solid var(--rp-border-grey);
background-color: var(--rp-bg-dim-blue);
text-align: left;
}
.rp-tc-state-item {
display: flex;
align-items: center;
padding: var(--spacing-02) 0;
&:last-of-type {
padding-bottom: var(--spacing-03);
}
}
.rp-tc-state-item-name {
@extend .text-truncate;
flex-grow: 1;
font-weight: 600;
}
.rp-tc-state-item-name-disabled {
opacity: 0.35;
}
.review-icon {
display: inline-block;
background-image: var(--review-icon);
@ -27,3 +70,22 @@
content: '\00a0'; // Non-breakable space. A non-breakable character here makes this icon work like font-awesome.
}
}
.resolved-comments-toggle {
background-color: var(--bg-light-secondary);
font-size: var(--font-size-02);
color: color.adjust($rp-type-blue, $lightness: 25%);
border: solid 1px var(--rp-border-grey);
border-radius: var(--border-radius-base);
padding: 0 var(--spacing-02);
display: block;
height: 22px;
width: 22px;
line-height: 1.4;
&:hover,
&:focus {
text-decoration: none;
color: $rp-type-blue;
}
}

View file

@ -1,6 +1,5 @@
.input-switch {
display: inline-block;
vertical-align: middle;
padding-left: 5px;
}
.input-switch-hidden-input {