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