Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed public/ad_leidian.jpg
Binary file not shown.
Binary file added public/ad_leidian.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/OperationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const OperationCard = ({ operation }: { operation: Operation }) => {
const { data: levels } = useLevels()

return (
<li className="mb-4 sm:mb-2 last:mb-0">
<li className="mb-4 sm:mb-2 last:mb-0 relative">
<ReLinkRenderer
search={{ op: operation.id }}
render={({ onClick, onKeyDown }) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const OperatorBackToTop: FC<OperatorBackToTopProp> = ({ toTop }) => {
<Button
minimal
icon="symbol-triangle-up"
disabled={current < 3}
disabled={current < 2}
title={
t.components.editor.operator.sheet.sheetOperator.toolbox
.OperatorBackToTop.back_to_top
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
useOperatorFilterProvider,
} from '../SheetOperatorFilterProvider'

const selectClass = 'scale-90'

export interface OperatorRaritySelectProp {}

export const OperatorRaritySelect: FC<OperatorRaritySelectProp> = () => {
Expand All @@ -18,8 +20,6 @@ export const OperatorRaritySelect: FC<OperatorRaritySelectProp> = () => {
useRarityFilterState: [{ selectedRarity, reverse }, setRarityFilter],
} = useOperatorFilterProvider()

const selectClass = 'scale-90'

return (
<>
<div className="flex items-center">
Expand Down
2 changes: 1 addition & 1 deletion src/components/editor2/action/ActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const ActionItem: FC<ActionItemProps> = memo(
typeInfo.accentText,
)}
>
<div className="flex flex-wrap items-center">
<div className="flex flex-wrap items-center !text-inherit">
<h4
className={clsx(
'relative shrink-0 self-stretch w-[5em] text-2xl font-serif bg-gray-100 dark:bg-gray-700 cursor-move select-none touch-manipulation',
Expand Down
235 changes: 235 additions & 0 deletions src/components/editor2/operator/sheet/ProfClassification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { Divider, H4, H5, Icon } from '@blueprintjs/core'

import clsx from 'clsx'
import { languageAtom, useTranslation } from 'i18n/i18n'
import { useAtomValue } from 'jotai'
import {
FC,
HTMLAttributes,
ImgHTMLAttributes,
useEffect,
useMemo,
useRef,
useState,
} from 'react'

import {
DEFAULTPROFID,
DEFAULTSUBPROFID,
useOperatorFilterProvider,
} from 'components/editor/operator/sheet/sheetOperator/SheetOperatorFilterProvider'
import { PROFESSIONS } from 'models/operator'

export interface ProfClassification {}

export const ProfClassification: FC<ProfClassification> = () => {
const t = useTranslation()
const language = useAtomValue(languageAtom)
const {
useProfFilterState: [{ selectedProf }, setProfFilter],
} = useOperatorFilterProvider()

const formattedProfessions = useMemo(
() => [
{
id: DEFAULTPROFID.ALL,
name: t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.all,
name_en:
t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.all,
sub: [],
},
{
id: DEFAULTPROFID.FAV,
name: t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.favorites,
name_en:
t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.favorites,
sub: [],
},
...PROFESSIONS.map((profession) => ({
...profession,
name_en: profession.name_en || profession.name,
})),
{
id: DEFAULTPROFID.OTHERS,
name: t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.others,
name_en:
t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.others,
sub: [],
},
],
[t],
)
const subProfs = useMemo(() => {
return [
{
id: DEFAULTSUBPROFID.ALL,
name: t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.all,
name_en:
t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.all,
},
{
id: DEFAULTSUBPROFID.SELECTED,
name: t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.selected,
name_en:
t.components.editor.operator.sheet.sheetOperator
.ProfClassificationWithFilters.selected,
},
...(formattedProfessions.find(({ id }) => id === selectedProf[0])?.sub ||
[]),
]
}, [selectedProf, formattedProfessions, t])

return (
<div>
<UlWithArrow className="px-4 py-2 gap-4 " key={selectedProf[0]}>
{subProfs.map((subProf) => (
<li key={subProf.id} className="flex items-center justify-center">
<H4
className={clsx(
'truncate cursor-pointer opacity-50 hover:underline hover:opacity-90 m-0',
selectedProf.includes(subProf.id) && '!opacity-100 underline',
)}
onClick={() =>
setProfFilter(({ selectedProf, ...rest }) => ({
selectedProf: [selectedProf[0], subProf.id],
...rest,
}))
}
>
{language === 'en' && subProf.name_en
? subProf.name_en
: subProf.name}
</H4>
</li>
))}
</UlWithArrow>
<Divider className="m-0" />
<UlWithArrow className="py-1">
{formattedProfessions.map((prof) => (
<ProfIcon
key={prof.id}
profId={prof.id}
name={language === 'en' && prof.name_en ? prof.name_en : prof.name}
selected={selectedProf.includes(prof.id)}
onProfClick={() =>
setProfFilter((prev) => ({
...prev,
selectedProf: [prof.id, DEFAULTSUBPROFID.ALL],
}))
}
/>
))}
</UlWithArrow>
</div>
)
}

interface ProfIconProp extends ImgHTMLAttributes<HTMLImageElement> {
name: string
profId: string
selected: boolean
onProfClick: () => void
}

const ProfIcon: FC<ProfIconProp> = ({
name,
profId,
selected,
onProfClick,
...restImgProps
}) => {
return (
<li
className="grow cursor-pointer relative flex justify-center items-center p-2 min-w-[48px]"
title={name}
role="presentation"
onClick={onProfClick}
>
{selected && (
<div className="w-full h-1 bg-black dark:bg-white absolute bottom-full left-0 rounded" />
)}
{(Object.values(DEFAULTPROFID) as string[]).includes(profId) ? (
<H5 className="!text-xs sm:!text-base truncate m-0">{name}</H5>
) : (
<img
{...restImgProps}
className="invert dark:invert-0"
src={'/assets/prof-icons/' + profId + '.png'}
alt=""
title={name}
/>
)}
</li>
)
}

interface UlWithArrowProp extends HTMLAttributes<HTMLUListElement> {}

const UlWithArrow: FC<UlWithArrowProp> = ({ className, ...ulProps }) => {
const containerRef = useRef<HTMLUListElement>(null)
const [showLeft, setShowLeft] = useState(false)
const [showRight, setShowRight] = useState(false)

const isOverflow = useMemo(() => showLeft || showRight, [showLeft, showRight])

useEffect(() => {
const checkScroll = () => {
const el = containerRef.current
if (!el) return
const { scrollLeft, scrollWidth, clientWidth } = el

setShowLeft(scrollLeft > 0)
setShowRight(scrollLeft + clientWidth < scrollWidth - 1)
}

const el = containerRef.current
if (!el) return

checkScroll()
el.addEventListener('scroll', checkScroll)

const resizeObserver = new ResizeObserver(checkScroll)
resizeObserver.observe(el)
resizeObserver.observe(el.firstElementChild as Element)

return () => {
el.removeEventListener('scroll', checkScroll)
resizeObserver.disconnect()
}
}, [])

return (
<div className="flex items-center relative w-full">
{showLeft && (
<Icon
icon="chevron-left"
className="absolute left-0 top-1/2 -translate-y-1/2 invert z-10"
/>
)}
<ul
{...ulProps}
ref={containerRef}
className={clsx(
'flex overflow-auto items-center w-full',
!isOverflow && 'justify-center',
className,
)}
/>
{showRight && (
<Icon
icon="chevron-right"
className="absolute right-0 top-1/2 -translate-y-1/2 invert z-10"
/>
)}
</div>
)
}
83 changes: 59 additions & 24 deletions src/components/editor2/operator/sheet/SheetList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { FC, useCallback, useRef } from 'react'
import { Button } from '@blueprintjs/core'
import { Popover2 } from '@blueprintjs/popover2'

import { FC, useCallback, useEffect, useRef } from 'react'

import { OperatorBackToTop } from 'components/editor/operator/sheet/sheetOperator/toolBox/OperatorBackToTop'
import { OperatorMutipleSelect } from 'components/editor/operator/sheet/sheetOperator/toolBox/OperatorMutipleSelect'
import { OperatorRaritySelect } from 'components/editor/operator/sheet/sheetOperator/toolBox/OperatorRaritySelect'

import { OperatorNoData } from '../../../editor/operator/sheet/SheetNoneData'
import { ProfClassificationWithFilters } from '../../../editor/operator/sheet/sheetOperator/ProfClassificationWithFilters'
import { useOperatorFilterProvider } from '../../../editor/operator/sheet/sheetOperator/SheetOperatorFilterProvider'
import {
defaultPagination,
useOperatorFilterProvider,
} from '../../../editor/operator/sheet/sheetOperator/SheetOperatorFilterProvider'
import { SheetOperatorItem } from '../../../editor/operator/sheet/sheetOperator/SheetOperatorItem'
import { ShowMore } from '../../../editor/operator/sheet/sheetOperator/ShowMore'
import { ProfClassification } from './ProfClassification'

interface SheetListProps {}

Expand All @@ -23,31 +33,56 @@ export const SheetList: FC<SheetListProps> = () => {

const {
operatorFiltered: { data: operatorFilteredData },
useProfFilterState: [{ selectedProf }],
usePaginationFilterState: [_, setPaginationFilter],
} = useOperatorFilterProvider()

useEffect(() => {
toTop()
setPaginationFilter(defaultPagination)
}, [selectedProf, setPaginationFilter, toTop])

return (
<div className="flex h-full">
<div className="grow px-1 py-4 overflow-auto">
{operatorFilteredData.length ? (
<>
<div
key="operatorContainer"
className="flex flex-wrap items-start content-start overscroll-contain relative"
ref={operatorScrollRef}
>
{operatorFilteredData.map(({ name }, index) => (
<div className="flex items-center flex-0 w-32 h-32" key={index}>
<SheetOperatorItem {...{ name }} />
</div>
))}
</div>
<ShowMore {...{ toTop }} />
</>
) : (
<OperatorNoData />
)}
<div className="flex flex-col h-full">
<div className="flex h-full w-full overflow-auto">
<div className="grow px-1 py-4">
{operatorFilteredData.length ? (
<>
<div
key="operatorContainer"
className="grid auto-rows-auto grid-cols-[repeat(auto-fit,minmax(128px,1fr))]"
ref={operatorScrollRef}
>
{operatorFilteredData.map(({ name }, index) => (
<div
className="flex items-center flex-0 w-full h-32"
key={index}
>
<SheetOperatorItem {...{ name }} />
</div>
))}
</div>
<ShowMore {...{ toTop }} />
</>
) : (
<OperatorNoData />
)}
</div>
<div className="h-full sticky top-0 self-start shrink-0 z-10 flex flex-col items-center justify-center">
<Popover2
content={
<>
<OperatorRaritySelect />
</>
}
>
<Button minimal icon="filter-list" />
</Popover2>
<OperatorMutipleSelect />
<OperatorBackToTop {...{ toTop }} />
</div>
</div>
<ProfClassificationWithFilters {...{ toTop }} />
<ProfClassification />
</div>
)
}
Loading