feat: version select picker and versions storage
parent
677716346a
commit
a85908386e
@ -0,0 +1,36 @@
|
||||
import type { GenRes } from '@/service/debug'
|
||||
import { useSessionStorageState } from 'ahooks'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Params = {
|
||||
storageKey: string
|
||||
}
|
||||
const keyPrefix = 'gen-data-'
|
||||
const useGenData = ({ storageKey }: Params) => {
|
||||
const [versions, setVersions] = useSessionStorageState<GenRes[]>(`${keyPrefix}${storageKey}-versions`, {
|
||||
defaultValue: [],
|
||||
})
|
||||
|
||||
const [currentVersionIndex, setCurrentVersionIndex] = useSessionStorageState<number>(`${keyPrefix}${storageKey}-version-index`, {
|
||||
defaultValue: 0,
|
||||
})
|
||||
|
||||
const current = versions[currentVersionIndex]
|
||||
|
||||
const addVersion = useCallback((version: GenRes) => {
|
||||
setCurrentVersionIndex(() => versions.length)
|
||||
setVersions((prev) => {
|
||||
return [...prev!, version]
|
||||
})
|
||||
}, [setVersions, setCurrentVersionIndex, versions.length])
|
||||
|
||||
return {
|
||||
versions,
|
||||
addVersion,
|
||||
currentVersionIndex,
|
||||
setCurrentVersionIndex,
|
||||
current,
|
||||
}
|
||||
}
|
||||
|
||||
export default useGenData
|
||||
@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react'
|
||||
|
||||
type Option = {
|
||||
label: string
|
||||
value: number
|
||||
}
|
||||
|
||||
type VersionSelectorProps = {
|
||||
versionLen: number;
|
||||
value: number;
|
||||
onChange: (index: number) => void;
|
||||
}
|
||||
|
||||
const VersionSelector: React.FC<VersionSelectorProps> = ({ versionLen, value, onChange }) => {
|
||||
const [isOpen, {
|
||||
setFalse: handleOpenFalse,
|
||||
toggle: handleOpenToggle,
|
||||
set: handleOpenSet,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const versions = Array.from({ length: versionLen }, (_, index) => ({
|
||||
label: `Version ${index + 1}${index === versionLen - 1 ? ' · Latest' : ''}`,
|
||||
value: index,
|
||||
}))
|
||||
|
||||
const isLatest = value === versionLen - 1
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
placement={'bottom-start'}
|
||||
offset={{
|
||||
mainAxis: 4,
|
||||
crossAxis: -8,
|
||||
}}
|
||||
open={isOpen}
|
||||
onOpenChange={handleOpenSet}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={handleOpenToggle}
|
||||
asChild
|
||||
>
|
||||
|
||||
<div className='system-xs-medium flex cursor-pointer items-center text-text-secondary'>
|
||||
<div>Version {value + 1}{isLatest && ' · Latest'}</div>
|
||||
<RiArrowDownSLine className='size-3 ' />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger >
|
||||
<PortalToFollowElemContent className={cn(
|
||||
'z-[99]',
|
||||
)}>
|
||||
<div
|
||||
className={cn(
|
||||
'w-[208px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg',
|
||||
)}
|
||||
>
|
||||
<div className='system-xs-medium-uppercase flex h-[22px] items-center px-3 pl-3 text-text-tertiary'>
|
||||
Versions
|
||||
</div>
|
||||
{
|
||||
versions.map(option => (
|
||||
<div
|
||||
key={option.value}
|
||||
className={cn(
|
||||
'system-sm-medium flex h-7 cursor-pointer items-center rounded-lg px-2 text-text-secondary hover:bg-state-base-hover',
|
||||
)}
|
||||
title={option.label}
|
||||
onClick={() => {
|
||||
onChange(option.value)
|
||||
handleOpenFalse()
|
||||
}}
|
||||
>
|
||||
<div className='mr-1 grow truncate px-1 pl-1'>
|
||||
{option.label}
|
||||
</div>
|
||||
{
|
||||
value === option.value && <RiCheckLine className='h-4 w-4 shrink-0 text-text-accent' />
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem >
|
||||
)
|
||||
}
|
||||
|
||||
export default VersionSelector
|
||||
Loading…
Reference in New Issue