mirror of
https://github.com/overleaf/overleaf.git
synced 2024-09-16 02:52:31 -04:00
Merge pull request #18369 from overleaf/ii-bs5-downshift
[web] BS5 downshift in account settings page GitOrigin-RevId: acfbde3ec87ae8038e0b19ddaca35e6c7392743b
This commit is contained in:
parent
2d75652f61
commit
f469d8e5e3
7 changed files with 258 additions and 121 deletions
|
@ -5,6 +5,8 @@ import classnames from 'classnames'
|
|||
import countries, { CountryCode } from '../../../data/countries-list'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
|
||||
import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
|
||||
type CountryInputProps = {
|
||||
setValue: React.Dispatch<React.SetStateAction<CountryCode | null>>
|
||||
|
@ -46,16 +48,24 @@ function Downshift({ setValue, inputRef }: CountryInputProps) {
|
|||
},
|
||||
})
|
||||
|
||||
const shouldOpen = isOpen && inputItems.length
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'ui-select-container ui-select-bootstrap dropdown',
|
||||
{
|
||||
open: isOpen && inputItems.length,
|
||||
}
|
||||
'dropdown',
|
||||
bsVersion({
|
||||
bs5: 'd-block',
|
||||
bs3: classnames('ui-select-container ui-select-bootstrap', {
|
||||
open: shouldOpen,
|
||||
}),
|
||||
})
|
||||
)}
|
||||
>
|
||||
<div {...getComboboxProps()} className="ui-select-toggle">
|
||||
<div
|
||||
{...getComboboxProps()}
|
||||
className={bsVersion({ bs3: 'ui-select-toggle' })}
|
||||
>
|
||||
{/* eslint-disable-next-line jsx-a11y/label-has-for */}
|
||||
<label
|
||||
{...getLabelProps()}
|
||||
|
@ -81,25 +91,52 @@ function Downshift({ setValue, inputRef }: CountryInputProps) {
|
|||
</div>
|
||||
<ul
|
||||
{...getMenuProps()}
|
||||
className="ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu"
|
||||
className={classnames(
|
||||
'dropdown-menu',
|
||||
bsVersion({
|
||||
bs5: classnames('select-dropdown-menu', { show: shouldOpen }),
|
||||
bs3: 'ui-select-choices ui-select-choices-content ui-select-dropdown',
|
||||
})
|
||||
)}
|
||||
>
|
||||
{inputItems.map((item, index) => (
|
||||
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
||||
<li
|
||||
className="ui-select-choices-group"
|
||||
className={bsVersion({ bs3: 'ui-select-choices-group' })}
|
||||
key={`${item.name}-${index}`}
|
||||
{...getItemProps({ item, index })}
|
||||
aria-selected={selectedItem?.name === item.name}
|
||||
>
|
||||
<div
|
||||
className={classnames('ui-select-choices-row', {
|
||||
active: selectedItem?.name === item.name,
|
||||
'ui-select-choices-row--highlighted':
|
||||
highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
<span className="ui-select-choices-row-inner">
|
||||
<span>{item.name}</span>
|
||||
</span>
|
||||
</div>
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<div
|
||||
className={classnames('ui-select-choices-row', {
|
||||
active: selectedItem?.name === item.name,
|
||||
'ui-select-choices-row--highlighted':
|
||||
highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
<span className="ui-select-choices-row-inner">
|
||||
<span>{item.name}</span>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
bs5={
|
||||
<DropdownItem
|
||||
as="span"
|
||||
role={undefined}
|
||||
className={classnames({
|
||||
active: selectedItem?.name === item.name,
|
||||
'dropdown-item--highlighted': highlightedIndex === index,
|
||||
})}
|
||||
trailingIcon={
|
||||
selectedItem?.name === item.name ? 'check' : undefined
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</DropdownItem>
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -4,6 +4,8 @@ import classnames from 'classnames'
|
|||
import { escapeRegExp } from 'lodash'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
import OLFormControl from '@/features/ui/components/ol/ol-form-control'
|
||||
import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
|
||||
type DownshiftInputProps = {
|
||||
highlightMatches?: boolean
|
||||
|
@ -77,13 +79,18 @@ function Downshift({
|
|||
)
|
||||
}
|
||||
|
||||
const shouldOpen = isOpen && inputItems.length
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'ui-select-container ui-select-bootstrap dropdown',
|
||||
{
|
||||
open: isOpen && inputItems.length,
|
||||
}
|
||||
'dropdown',
|
||||
bsVersion({
|
||||
bs5: 'd-block',
|
||||
bs3: classnames('ui-select-container ui-select-bootstrap', {
|
||||
open: shouldOpen,
|
||||
}),
|
||||
})
|
||||
)}
|
||||
>
|
||||
<div {...getComboboxProps()}>
|
||||
|
@ -116,28 +123,62 @@ function Downshift({
|
|||
</div>
|
||||
<ul
|
||||
{...getMenuProps()}
|
||||
className="ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu"
|
||||
className={classnames(
|
||||
'dropdown-menu',
|
||||
bsVersion({
|
||||
bs5: classnames('select-dropdown-menu', { show: shouldOpen }),
|
||||
bs3: 'ui-select-choices ui-select-choices-content ui-select-dropdown',
|
||||
})
|
||||
)}
|
||||
>
|
||||
{showSuggestedText && inputItems.length && (
|
||||
<li className="ui-select-title">{itemsTitle}</li>
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={<li className="ui-select-title">{itemsTitle}</li>}
|
||||
bs5={
|
||||
<li>
|
||||
<DropdownItem as="span" role={undefined} disabled>
|
||||
{itemsTitle}
|
||||
</DropdownItem>
|
||||
</li>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{inputItems.map((item, index) => (
|
||||
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
||||
<li
|
||||
className="ui-select-choices-group"
|
||||
className={bsVersion({ bs3: 'ui-select-choices-group' })}
|
||||
key={`${item}${index}`}
|
||||
{...getItemProps({ item, index })}
|
||||
aria-selected={selectedItem === item}
|
||||
>
|
||||
<div
|
||||
className={classnames('ui-select-choices-row', {
|
||||
active: selectedItem === item,
|
||||
'ui-select-choices-row--highlighted':
|
||||
highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
<span className="ui-select-choices-row-inner">
|
||||
<span>{highlightMatchedCharacters(item, inputValue)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<div
|
||||
className={classnames('ui-select-choices-row', {
|
||||
active: selectedItem === item,
|
||||
'ui-select-choices-row--highlighted':
|
||||
highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
<span className="ui-select-choices-row-inner">
|
||||
<span>{highlightMatchedCharacters(item, inputValue)}</span>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
bs5={
|
||||
<DropdownItem
|
||||
as="span"
|
||||
role={undefined}
|
||||
className={classnames({
|
||||
active: selectedItem === item,
|
||||
'dropdown-item--highlighted': highlightedIndex === index,
|
||||
})}
|
||||
trailingIcon={selectedItem === item ? 'check' : undefined}
|
||||
>
|
||||
{highlightMatchedCharacters(item, inputValue)}
|
||||
</DropdownItem>
|
||||
}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react'
|
||||
import React, { forwardRef } from 'react'
|
||||
import {
|
||||
Dropdown as BS5Dropdown,
|
||||
DropdownToggle as BS5DropdownToggle,
|
||||
|
@ -18,22 +18,22 @@ export function Dropdown({ ...props }: DropdownProps) {
|
|||
return <BS5Dropdown {...props} />
|
||||
}
|
||||
|
||||
export function DropdownItem({
|
||||
active,
|
||||
children,
|
||||
description,
|
||||
leadingIcon,
|
||||
trailingIcon,
|
||||
...props
|
||||
}: DropdownItemProps) {
|
||||
const trailingIconType = active ? 'check' : trailingIcon
|
||||
return (
|
||||
<li>
|
||||
export const DropdownItem = forwardRef<
|
||||
typeof BS5DropdownItem,
|
||||
DropdownItemProps
|
||||
>(
|
||||
(
|
||||
{ active, children, description, leadingIcon, trailingIcon, ...props },
|
||||
ref
|
||||
) => {
|
||||
const trailingIconType = active ? 'check' : trailingIcon
|
||||
return (
|
||||
<BS5DropdownItem
|
||||
active={active}
|
||||
className={description ? 'dropdown-item-description-container' : ''}
|
||||
role="menuitem"
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
{leadingIcon && (
|
||||
<MaterialIcon
|
||||
|
@ -52,9 +52,10 @@ export function DropdownItem({
|
|||
<span className="dropdown-item-description">{description}</span>
|
||||
)}
|
||||
</BS5DropdownItem>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownItem.displayName = 'DropdownItem'
|
||||
|
||||
export function DropdownToggle({ ...props }: DropdownToggleProps) {
|
||||
return <BS5DropdownToggle {...props} />
|
||||
|
|
|
@ -40,9 +40,9 @@ export function SplitButton({
|
|||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
{items.map((item, index) => (
|
||||
<DropdownItem key={index} eventKey={item.eventKey}>
|
||||
{item.label}
|
||||
</DropdownItem>
|
||||
<li key={index}>
|
||||
<DropdownItem eventKey={item.eventKey}>{item.label}</DropdownItem>
|
||||
</li>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
|
|
|
@ -22,12 +22,14 @@ export type DropdownItemProps = PropsWithChildren<{
|
|||
as?: ElementType
|
||||
description?: string
|
||||
disabled?: boolean
|
||||
eventKey: string | number
|
||||
eventKey?: string | number
|
||||
href?: string
|
||||
leadingIcon?: string
|
||||
onClick?: () => void
|
||||
trailingIcon?: string
|
||||
variant?: 'default' | 'danger'
|
||||
className?: string
|
||||
role?: string
|
||||
}>
|
||||
|
||||
export type DropdownToggleProps = PropsWithChildren<{
|
||||
|
|
|
@ -10,16 +10,22 @@ type Args = React.ComponentProps<typeof DropdownMenu>
|
|||
export const Default = (args: Args) => {
|
||||
return (
|
||||
<DropdownMenu show>
|
||||
<DropdownItem eventKey="1" href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<DropdownItem eventKey="2" href="#/action-2">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="1" href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem eventKey="2" href="#/action-2">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<DropdownDivider />
|
||||
<DropdownItem eventKey="3" disabled={args.disabled} href="#/action-3">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="3" disabled={args.disabled} href="#/action-3">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
@ -27,16 +33,27 @@ export const Default = (args: Args) => {
|
|||
export const Active = (args: Args) => {
|
||||
return (
|
||||
<DropdownMenu show>
|
||||
<DropdownItem eventKey="1" href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<DropdownItem eventKey="2" active href="#/action-2" trailingIcon="check">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="1" href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem
|
||||
eventKey="2"
|
||||
active
|
||||
href="#/action-2"
|
||||
trailingIcon="check"
|
||||
>
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<DropdownDivider />
|
||||
<DropdownItem eventKey="3" disabled={args.disabled} href="#/action-3">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="3" disabled={args.disabled} href="#/action-3">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
@ -44,16 +61,22 @@ export const Active = (args: Args) => {
|
|||
export const Danger = (args: Args) => {
|
||||
return (
|
||||
<DropdownMenu show>
|
||||
<DropdownItem eventKey="1" disabled={args.disabled} href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<DropdownItem eventKey="2" href="#/action-2">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="1" disabled={args.disabled} href="#/action-1">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem eventKey="2" href="#/action-2">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<DropdownDivider />
|
||||
<DropdownItem eventKey="3" href="#/action-3" variant="danger">
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem eventKey="3" href="#/action-3" variant="danger">
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
@ -61,23 +84,27 @@ export const Danger = (args: Args) => {
|
|||
export const Description = (args: Args) => {
|
||||
return (
|
||||
<DropdownMenu show>
|
||||
<DropdownItem
|
||||
disabled={args.disabled}
|
||||
eventKey="1"
|
||||
href="#/action-1"
|
||||
description="Description of the menu"
|
||||
>
|
||||
Example
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
active
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
description="Description of the menu"
|
||||
trailingIcon="check"
|
||||
>
|
||||
Example
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem
|
||||
disabled={args.disabled}
|
||||
eventKey="1"
|
||||
href="#/action-1"
|
||||
description="Description of the menu"
|
||||
>
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem
|
||||
active
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
description="Description of the menu"
|
||||
trailingIcon="check"
|
||||
>
|
||||
Example
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
@ -85,28 +112,44 @@ export const Description = (args: Args) => {
|
|||
export const Icon = (args: Args) => {
|
||||
return (
|
||||
<DropdownMenu show>
|
||||
<DropdownItem
|
||||
disabled={args.disabled}
|
||||
eventKey="1"
|
||||
href="#/action-1"
|
||||
leadingIcon="view_column_2"
|
||||
>
|
||||
Editor & PDF
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
active
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
leadingIcon="terminal"
|
||||
>
|
||||
Editor only
|
||||
</DropdownItem>
|
||||
<DropdownItem eventKey="2" href="#/action-2" leadingIcon="picture_as_pdf">
|
||||
PDF only
|
||||
</DropdownItem>
|
||||
<DropdownItem eventKey="2" href="#/action-2" leadingIcon="select_window">
|
||||
PDF in separate tab
|
||||
</DropdownItem>
|
||||
<li>
|
||||
<DropdownItem
|
||||
disabled={args.disabled}
|
||||
eventKey="1"
|
||||
href="#/action-1"
|
||||
leadingIcon="view_column_2"
|
||||
>
|
||||
Editor & PDF
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem
|
||||
active
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
leadingIcon="terminal"
|
||||
>
|
||||
Editor only
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
leadingIcon="picture_as_pdf"
|
||||
>
|
||||
PDF only
|
||||
</DropdownItem>
|
||||
</li>
|
||||
<li>
|
||||
<DropdownItem
|
||||
eventKey="2"
|
||||
href="#/action-2"
|
||||
leadingIcon="select_window"
|
||||
>
|
||||
PDF in separate tab
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -113,3 +113,16 @@
|
|||
border-left: 1px solid var(--neutral-10);
|
||||
}
|
||||
}
|
||||
|
||||
.select-dropdown-menu {
|
||||
top: 100%;
|
||||
margin-top: var(--spacing-04);
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.dropdown-item--highlighted {
|
||||
background-color: var(--bg-light-secondary);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue