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;
min-width: 440px;
.Header {
.header {
background: var(--dialog-bg);
border-top-left-radius: $card-corner;
border-top-right-radius: $card-corner;
@ -13,7 +13,64 @@
padding: $unit;
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;
padding: ($unit * 0.75) $unit-half $unit-half;
display: none;
@ -33,7 +90,7 @@
}
}
.Controls {
.controls {
display: flex;
gap: $unit;
@ -48,25 +105,13 @@
width: auto;
}
}
.Flipped {
transform: rotate(180deg);
}
.SegmentedControlWrapper {
flex-grow: 1;
.SegmentedControl {
width: 100%;
}
}
}
}
.Raids {
.raids {
border-bottom-left-radius: $card-corner;
border-bottom-right-radius: $card-corner;
height: 36vh;
height: 28vh;
overflow-y: scroll;
padding: 0 $unit;
@ -75,30 +120,30 @@
}
&.Searching {
.CommandGroup {
.group {
padding-top: 0;
padding-bottom: 0;
.Label {
.label {
display: none;
}
.SelectItem {
.item {
margin: 0;
}
}
.CommandGroup.Hidden {
.group.hidden {
display: block;
}
}
.CommandGroup {
&.Hidden {
.group {
&.hidden {
display: none;
}
.Label {
.label {
align-items: center;
color: var(--text-tertiary);
display: flex;
@ -109,7 +154,7 @@
gap: $unit;
padding: $unit $unit-2x $unit-half ($unit * 1.5);
.Separator {
.separator {
background: var(--select-separator);
border-radius: 1px;
display: block;
@ -121,67 +166,53 @@
}
}
.trigger {
background: var(--input-bound-bg);
.info {
display: flex;
padding-top: 10px;
padding-bottom: 11px;
min-height: 51px;
align-items: center;
gap: $unit-half;
flex-grow: 1;
}
.value {
display: flex;
gap: $unit-half;
width: 100%;
.extraIndicator {
background: var(--extra-purple-secondary);
border-radius: $full-corner;
color: $grey-100;
display: flex;
font-weight: $bold;
font-size: $font-tiny;
width: $unit-3x;
height: $unit-3x;
justify-content: center;
align-items: center;
}
.info {
display: flex;
align-items: center;
gap: $unit-half;
flex-grow: 1;
}
.group,
.separator {
color: var(--text-tertiary);
}
.extraIndicator {
background: var(--extra-purple-secondary);
border-radius: $full-corner;
color: $grey-100;
display: flex;
font-weight: $bold;
font-size: $font-tiny;
width: $unit-3x;
height: $unit-3x;
justify-content: center;
align-items: center;
}
.raid.wind {
color: var(--wind-text);
}
.Group,
.Separator {
color: var(--text-tertiary);
}
.raid.fire {
color: var(--fire-text);
}
.Raid.wind {
color: var(--wind-text);
}
.raid.water {
color: var(--water-text);
}
.Raid.fire {
color: var(--fire-text);
}
.raid.earth {
color: var(--earth-text);
}
.Raid.water {
color: var(--water-text);
}
.raid.dark {
color: var(--dark-text);
}
.Raid.earth {
color: var(--earth-text);
}
.Raid.dark {
color: var(--dark-text);
}
.Raid.light {
color: var(--light-text);
}
}
.raid.light {
color: var(--light-text);
}
.SelectTrigger.Raid:not(.Highlighted) .Value.Empty {

View file

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

View file

@ -1,33 +1,64 @@
.SelectItem.Raid {
padding-top: $unit;
padding-bottom: $unit;
padding-left: $unit;
.item {
align-items: center;
border-radius: $item-corner;
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 {
.ExtraIndicator {
.extraIndicator {
background: var(--extra-purple-secondary);
color: white;
}
.Selected {
.selected {
background-color: var(--pill-bg-hover);
color: var(--pill-text-hover);
}
}
&.Selected .ExtraIndicator {
&.selected .extraIndicator {
background: var(--extra-purple-secondary);
color: white;
}
.Text {
.text {
flex-grow: 1;
}
.ExtraIndicator {
.extraIndicator {
background: var(--extra-purple-bg);
border-radius: $full-corner;
color: var(--extra-purple-text);
color: var(--extra-purple-dark-text);
display: flex;
font-weight: $bold;
font-size: $font-tiny;
@ -37,7 +68,7 @@
align-items: center;
}
.Selected {
.selected {
background-color: var(--pill-bg);
color: var(--pill-text);
border-radius: $full-corner;

View file

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