Update RaidCombobox and RaidItem

Also removes RaidSelect, which has been removed
This commit is contained in:
Justin Edmund 2023-06-30 14:07:23 -07:00
parent 8cb41bc82c
commit a092a5b6ad
6 changed files with 208 additions and 316 deletions

View file

@ -1,22 +0,0 @@
.Raid.Select {
min-width: 420px;
.Top {
display: flex;
flex-direction: column;
gap: $unit;
padding: $unit 0;
.SegmentedControl {
width: 100%;
}
.Input.Bound {
background-color: var(--select-contained-bg);
&:hover {
background-color: var(--select-contained-bg-hover);
}
}
}
}

View file

@ -1,170 +0,0 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import * as RadixSelect from '@radix-ui/react-select'
import classNames from 'classnames'
import Overlay from '~components/common/Overlay'
import ChevronIcon from '~public/icons/Chevron.svg'
import styles from './index.module.scss'
import SegmentedControl from '~components/common/SegmentedControl'
import Segment from '~components/common/Segment'
import Input from '~components/common/Input'
// Props
interface Props
extends React.DetailedHTMLProps<
React.SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
> {
altText?: string
currentSegment: number
iconSrc?: string
open: boolean
trigger?: React.ReactNode
children?: React.ReactNode
onOpenChange?: () => void
onValueChange?: (value: string) => void
onSegmentClick: (segment: number) => void
onClose?: () => void
triggerClass?: string
overlayVisible?: boolean
}
const RaidSelect = React.forwardRef<HTMLButtonElement, Props>(function Select(
props: Props,
forwardedRef
) {
// Import translations
const { t } = useTranslation('common')
const searchInput = React.createRef<HTMLInputElement>()
const [open, setOpen] = useState(false)
const [value, setValue] = useState('')
const [query, setQuery] = useState('')
const triggerClasses = classNames(
{
SelectTrigger: true,
Disabled: props.disabled,
},
props.triggerClass
)
useEffect(() => {
setOpen(props.open)
}, [props.open])
useEffect(() => {
if (props.value && props.value !== '') setValue(`${props.value}`)
else setValue('')
}, [props.value])
function onValueChange(newValue: string) {
setValue(`${newValue}`)
if (props.onValueChange) props.onValueChange(newValue)
}
function onCloseAutoFocus() {
setOpen(false)
if (props.onClose) props.onClose()
}
function onEscapeKeyDown() {
setOpen(false)
if (props.onClose) props.onClose()
}
function onPointerDownOutside() {
setOpen(false)
if (props.onClose) props.onClose()
}
return (
<RadixSelect.Root
open={open}
value={value !== '' ? value : undefined}
onValueChange={onValueChange}
onOpenChange={props.onOpenChange}
>
<RadixSelect.Trigger
className={triggerClasses}
placeholder={props.placeholder}
ref={forwardedRef}
>
{props.iconSrc ? <img alt={props.altText} src={props.iconSrc} /> : ''}
<RadixSelect.Value placeholder={props.placeholder} />
{!props.disabled ? (
<RadixSelect.Icon className="SelectIcon">
<ChevronIcon />
</RadixSelect.Icon>
) : (
''
)}
</RadixSelect.Trigger>
<RadixSelect.Portal className="Select">
<>
<Overlay
open={open}
visible={props.overlayVisible != null ? props.overlayVisible : true}
/>
<RadixSelect.Content
className="Raid Select"
onCloseAutoFocus={onCloseAutoFocus}
onEscapeKeyDown={onEscapeKeyDown}
onPointerDownOutside={onPointerDownOutside}
>
<div className="Top">
<Input
autoComplete="off"
className="Search Bound"
name="query"
placeholder={t('search.placeholders.raid')}
ref={searchInput}
value={query}
onChange={() => {}}
/>
<SegmentedControl blended={true}>
<Segment
groupName="raid_section"
name="events"
selected={props.currentSegment === 1}
onClick={() => props.onSegmentClick(1)}
>
{t('raids.sections.events')}
</Segment>
<Segment
groupName="raid_section"
name="raids"
selected={props.currentSegment === 0}
onClick={() => props.onSegmentClick(0)}
>
{t('raids.sections.raids')}
</Segment>
<Segment
groupName="raid_section"
name="solo"
selected={props.currentSegment === 2}
onClick={() => props.onSegmentClick(2)}
>
{t('raids.sections.solo')}
</Segment>
</SegmentedControl>
</div>
<RadixSelect.Viewport>{props.children}</RadixSelect.Viewport>
</RadixSelect.Content>
</>
</RadixSelect.Portal>
</RadixSelect.Root>
)
})
RaidSelect.defaultProps = {
overlayVisible: true,
}
export default RaidSelect

View file

@ -1,8 +1,8 @@
.Combobox.Raid { .combobox.raid {
box-sizing: border-box; box-sizing: border-box;
min-width: 440px; min-width: 440px;
.Header { .header {
background: var(--dialog-bg); background: var(--dialog-bg);
border-top-left-radius: $card-corner; border-top-left-radius: $card-corner;
border-top-right-radius: $card-corner; border-top-right-radius: $card-corner;
@ -13,7 +13,64 @@
padding: $unit; padding: $unit;
width: 100%; width: 100%;
.Clear.Button { .input {
-webkit-font-smoothing: antialiased;
background-color: var(--input-bound-bg);
border: 2px solid transparent;
border-radius: $input-corner;
box-sizing: border-box;
display: block;
width: 100%;
&:not(.wrapper) {
padding: $unit * 1.5 $unit-2x;
}
&.wrapper {
$offset: 2px;
align-items: center;
background: var(--input-bg);
border-radius: $input-corner;
border: $offset solid transparent;
box-sizing: border-box;
position: relative;
.counter {
color: var(--text-tertiary);
display: block;
font-weight: $bold;
line-height: 42px;
position: absolute;
right: $unit-2x;
top: 0;
}
input {
background: transparent;
border-radius: $input-corner;
border: none;
box-sizing: border-box;
padding: $unit * 1.5 $unit-2x;
width: 100%;
}
}
&:focus {
border: 2px solid $blue;
outline: none;
}
&.bound {
background-color: var(--input-bound-bg);
&:hover {
background-color: var(--input-bound-bg-hover);
}
}
}
.clear.button {
background: none; background: none;
padding: ($unit * 0.75) $unit-half $unit-half; padding: ($unit * 0.75) $unit-half $unit-half;
display: none; display: none;
@ -33,7 +90,7 @@
} }
} }
.Controls { .controls {
display: flex; display: flex;
gap: $unit; gap: $unit;
@ -48,25 +105,13 @@
width: auto; width: auto;
} }
} }
.Flipped {
transform: rotate(180deg);
}
.SegmentedControlWrapper {
flex-grow: 1;
.SegmentedControl {
width: 100%;
}
}
} }
} }
.Raids { .raids {
border-bottom-left-radius: $card-corner; border-bottom-left-radius: $card-corner;
border-bottom-right-radius: $card-corner; border-bottom-right-radius: $card-corner;
height: 36vh; height: 28vh;
overflow-y: scroll; overflow-y: scroll;
padding: 0 $unit; padding: 0 $unit;
@ -75,30 +120,30 @@
} }
&.Searching { &.Searching {
.CommandGroup { .group {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
.Label { .label {
display: none; display: none;
} }
.SelectItem { .item {
margin: 0; margin: 0;
} }
} }
.CommandGroup.Hidden { .group.hidden {
display: block; display: block;
} }
} }
.CommandGroup { .group {
&.Hidden { &.hidden {
display: none; display: none;
} }
.Label { .label {
align-items: center; align-items: center;
color: var(--text-tertiary); color: var(--text-tertiary);
display: flex; display: flex;
@ -109,7 +154,7 @@
gap: $unit; gap: $unit;
padding: $unit $unit-2x $unit-half ($unit * 1.5); padding: $unit $unit-2x $unit-half ($unit * 1.5);
.Separator { .separator {
background: var(--select-separator); background: var(--select-separator);
border-radius: 1px; border-radius: 1px;
display: block; display: block;
@ -121,18 +166,6 @@
} }
} }
.trigger {
background: var(--input-bound-bg);
display: flex;
padding-top: 10px;
padding-bottom: 11px;
min-height: 51px;
.value {
display: flex;
gap: $unit-half;
width: 100%;
.info { .info {
display: flex; display: flex;
align-items: center; align-items: center;
@ -153,36 +186,34 @@
align-items: center; align-items: center;
} }
.Group, .group,
.Separator { .separator {
color: var(--text-tertiary); color: var(--text-tertiary);
} }
.Raid.wind { .raid.wind {
color: var(--wind-text); color: var(--wind-text);
} }
.Raid.fire { .raid.fire {
color: var(--fire-text); color: var(--fire-text);
} }
.Raid.water { .raid.water {
color: var(--water-text); color: var(--water-text);
} }
.Raid.earth { .raid.earth {
color: var(--earth-text); color: var(--earth-text);
} }
.Raid.dark { .raid.dark {
color: var(--dark-text); color: var(--dark-text);
} }
.Raid.light { .raid.light {
color: var(--light-text); color: var(--light-text);
} }
}
}
.SelectTrigger.Raid:not(.Highlighted) .Value.Empty { .SelectTrigger.Raid:not(.Highlighted) .Value.Empty {
color: var(--text-tertiary); color: var(--text-tertiary);

View file

@ -95,6 +95,17 @@ const RaidCombobox = (props: Props) => {
const inputRef = createRef<HTMLInputElement>() const inputRef = createRef<HTMLInputElement>()
const sortButtonRef = createRef<HTMLButtonElement>() const sortButtonRef = createRef<HTMLButtonElement>()
// Classes
const comboboxClasses = classNames({
[styles.combobox]: true,
[styles.raid]: true,
})
const raidsClasses = classNames({
[styles.raids]: true,
[styles.searching]: query !== '',
})
// ---------------------------------------------- // ----------------------------------------------
// Methods: Lifecycle Hooks // Methods: Lifecycle Hooks
// ---------------------------------------------- // ----------------------------------------------
@ -313,14 +324,14 @@ const RaidCombobox = (props: Props) => {
const options = generateRaidItems(group.raids) const options = generateRaidItems(group.raids)
const groupClassName = classNames({ const groupClassName = classNames({
CommandGroup: true, [styles.group]: true,
Hidden: group.section !== currentSection, [styles.hidden]: group.section !== currentSection,
}) })
const heading = ( const heading = (
<div className="Label"> <div className={styles.label}>
{group.name[locale]} {group.name[locale]}
<div className="Separator" /> <div className={styles.separator} />
</div> </div>
) )
@ -350,7 +361,7 @@ const RaidCombobox = (props: Props) => {
<CommandGroup <CommandGroup
data-section={untitledGroup.section} data-section={untitledGroup.section}
className={classNames({ className={classNames({
CommandGroup: true, [styles.group]: true,
})} })}
key="ungrouped-raids" key="ungrouped-raids"
> >
@ -379,7 +390,7 @@ const RaidCombobox = (props: Props) => {
return ( return (
<RaidItem <RaidItem
className={isSelected ? 'Selected' : ''} className={classNames({ [styles.selected]: isSelected })}
icon={{ alt: raid.name[locale], src: imageUrl }} icon={{ alt: raid.name[locale], src: imageUrl }}
extra={raid.group.extra} extra={raid.group.extra}
key={key} key={key}
@ -400,7 +411,7 @@ const RaidCombobox = (props: Props) => {
// Renders a SegmentedControl component for selecting raid sections. // Renders a SegmentedControl component for selecting raid sections.
function renderSegmentedControl() { function renderSegmentedControl() {
return ( return (
<SegmentedControl blended={true}> <SegmentedControl blended={true} className="raid" wrapperClassName="raid">
<Segment <Segment
groupName="raid_section" groupName="raid_section"
name="events" name="events"
@ -444,9 +455,10 @@ const RaidCombobox = (props: Props) => {
> >
<Button <Button
blended={true} blended={true}
buttonSize="small" bound={true}
size="small"
leftAccessoryIcon={<ArrowIcon />} leftAccessoryIcon={<ArrowIcon />}
leftAccessoryClassName={sort === Sort.DESCENDING ? 'Flipped' : ''} leftAccessoryClassName={sort === Sort.DESCENDING ? 'flipped' : ''}
onClick={reverseSort} onClick={reverseSort}
onKeyDown={handleSortButtonKeyDown} onKeyDown={handleSortButtonKeyDown}
ref={sortButtonRef} ref={sortButtonRef}
@ -462,10 +474,19 @@ const RaidCombobox = (props: Props) => {
const element = ( const element = (
<> <>
{!props.minimal ? ( {!props.minimal ? (
<div className="Info"> <div className={styles.info}>
<span className="Group">{currentRaid.group.name[locale]}</span> <span className={styles.group}>
<span className="Separator">/</span> {currentRaid.group.name[locale]}
<span className={classNames({ Raid: true }, linkClass)}> </span>
<span className={styles.separator}>/</span>
<span
className={classNames(
{
[styles.raid]: true,
},
linkClass?.split(' ').map((className) => styles[className])
)}
>
{currentRaid.name[locale]} {currentRaid.name[locale]}
</span> </span>
</div> </div>
@ -476,7 +497,7 @@ const RaidCombobox = (props: Props) => {
)} )}
{currentRaid.group.extra && !props.minimal && ( {currentRaid.group.extra && !props.minimal && (
<i className="ExtraIndicator">EX</i> <i className={styles.extraIndicator}>EX</i>
)} )}
</> </>
) )
@ -493,9 +514,9 @@ const RaidCombobox = (props: Props) => {
// Renders the search input for the raid combobox // Renders the search input for the raid combobox
function renderSearchInput() { function renderSearchInput() {
return ( return (
<div className="Bound Joined"> <div className={styles.wrapper}>
<CommandInput <CommandInput
className="Input" className={styles.input}
placeholder={t('search.placeholders.raid')} placeholder={t('search.placeholders.raid')}
tabIndex={1} tabIndex={1}
ref={inputRef} ref={inputRef}
@ -504,9 +525,9 @@ const RaidCombobox = (props: Props) => {
/> />
<div <div
className={classNames({ className={classNames({
Button: true, [styles.button]: true,
Clear: true, [styles.clear]: true,
Visible: query.length > 0, [styles.visible]: query.length > 0,
})} })}
onClick={clearSearch} onClick={clearSearch}
> >
@ -540,7 +561,7 @@ const RaidCombobox = (props: Props) => {
// ---------------------------------------------- // ----------------------------------------------
return ( return (
<Popover <Popover
className="Flush" className="raid flush"
open={open} open={open}
onOpenChange={toggleOpen} onOpenChange={toggleOpen}
placeholder={ placeholder={
@ -549,26 +570,26 @@ const RaidCombobox = (props: Props) => {
trigger={{ trigger={{
bound: true, bound: true,
className: classNames({ className: classNames({
Raid: true, raid: true,
Highlighted: props.showAllRaidsOption, highlighted: props.showAllRaidsOption,
}), }),
size: props.size, size: props.size,
}} }}
triggerTabIndex={props.tabIndex} triggerTabIndex={props.tabIndex}
value={renderTriggerContent()} value={renderTriggerContent()}
> >
<Command className="Raid Combobox"> <Command className={comboboxClasses}>
<div className="Header"> <div className={styles.header}>
{renderSearchInput()} {renderSearchInput()}
{!query && ( {!query && (
<div className="Controls"> <div className={styles.controls}>
{renderSegmentedControl()} {renderSegmentedControl()}
{renderSortButton()} {renderSortButton()}
</div> </div>
)} )}
</div> </div>
<div <div
className={classNames({ Raids: true, Searching: query !== '' })} className={raidsClasses}
ref={listRef} ref={listRef}
role="listbox" role="listbox"
tabIndex={6} tabIndex={6}

View file

@ -1,33 +1,64 @@
.SelectItem.Raid { .item {
padding-top: $unit; align-items: center;
padding-bottom: $unit; border-radius: $item-corner;
padding-left: $unit; border: 2px solid transparent;
color: var(--text-tertiary);
display: flex;
gap: $unit;
font-size: $font-regular;
padding: $unit $unit-2x $unit $unit;
&:hover,
&:focus {
background-color: var(--option-bg-hover);
color: var(--text-primary);
cursor: pointer;
outline: none;
}
&:focus {
border: 2px solid $blue;
outline: none;
}
&:first-child {
margin-top: $unit;
}
&:last-child {
margin-bottom: $unit;
}
img {
width: $unit-4x;
height: auto;
}
&:hover { &:hover {
.ExtraIndicator { .extraIndicator {
background: var(--extra-purple-secondary); background: var(--extra-purple-secondary);
color: white; color: white;
} }
.Selected { .selected {
background-color: var(--pill-bg-hover); background-color: var(--pill-bg-hover);
color: var(--pill-text-hover); color: var(--pill-text-hover);
} }
} }
&.Selected .ExtraIndicator { &.selected .extraIndicator {
background: var(--extra-purple-secondary); background: var(--extra-purple-secondary);
color: white; color: white;
} }
.Text { .text {
flex-grow: 1; flex-grow: 1;
} }
.ExtraIndicator { .extraIndicator {
background: var(--extra-purple-bg); background: var(--extra-purple-bg);
border-radius: $full-corner; border-radius: $full-corner;
color: var(--extra-purple-text); color: var(--extra-purple-dark-text);
display: flex; display: flex;
font-weight: $bold; font-weight: $bold;
font-size: $font-tiny; font-size: $font-tiny;
@ -37,7 +68,7 @@
align-items: center; align-items: center;
} }
.Selected { .selected {
background-color: var(--pill-bg); background-color: var(--pill-bg);
color: var(--pill-text); color: var(--pill-text);
border-radius: $full-corner; border-radius: $full-corner;

View file

@ -1,7 +1,7 @@
import React, { ComponentProps, PropsWithChildren } from 'react' import React, { ComponentProps, PropsWithChildren } from 'react'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import { CommandItem } from '~components/common/Command'
import classNames from 'classnames' import classNames from 'classnames'
import { CommandItem } from '~components/common/Command'
import styles from './index.module.scss' import styles from './index.module.scss'
interface Props extends ComponentProps<'div'> { interface Props extends ComponentProps<'div'> {
@ -35,10 +35,9 @@ const RaidItem = React.forwardRef<HTMLDivElement, PropsWithChildren<Props>>(
) { ) {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const classes = classNames( const classes = classNames({
{ SelectItem: true, Raid: true }, [styles.item]: true,
props.className })
)
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Escape' && onEscapeKeyPressed) { if (event.key === 'Escape' && onEscapeKeyPressed) {
@ -69,10 +68,12 @@ const RaidItem = React.forwardRef<HTMLDivElement, PropsWithChildren<Props>>(
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
ref={forwardedRef} ref={forwardedRef}
> >
{icon ? <img alt={icon.alt} src={icon.src} /> : ''} {icon && <img alt={icon.alt} src={icon.src} />}
<span className="Text">{children}</span> <span className={styles.text}>{children}</span>
{selected ? <i className="Selected">{t('combobox.selected')}</i> : ''} {selected && (
{extra ? <i className="ExtraIndicator">EX</i> : ''} <i className={styles.selected}>{t('combobox.selected')}</i>
)}
{extra && <i className={styles.extraIndicator}>EX</i>}
</CommandItem> </CommandItem>
) )
} }