mirror of
https://github.com/RealKai42/qwerty-learner.git
synced 2026-04-04 22:09:04 +08:00
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { clamp } from 'lodash'
|
|
import { useMemo } from 'react'
|
|
import classNames from 'classnames'
|
|
|
|
export type RemarkRingProps = {
|
|
remark: string
|
|
caption: string
|
|
/**
|
|
* `null` if the percentage is not appliable.
|
|
* Otherwise, this is an integer between 0 and 100.
|
|
*/
|
|
percentage?: number | null
|
|
/**
|
|
* Default to 7 rem.
|
|
*/
|
|
size?: number
|
|
}
|
|
|
|
const rootFontSize = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue('font-size'))
|
|
|
|
export default function RemarkRing({ remark, caption, percentage = null, size = 7 }: RemarkRingProps) {
|
|
const clipPath = useMemo((): string | undefined => {
|
|
if (percentage === null) {
|
|
return undefined
|
|
}
|
|
const clamped = clamp(percentage, 0, 100)
|
|
if (clamped === 100) {
|
|
return undefined
|
|
}
|
|
const alpha = Math.PI * 2 * (clamped / 100)
|
|
const r = (rootFontSize * size) / 2
|
|
const path = `M ${r},0 A ${r},${r} 0 ${clamped > 50 ? 1 : 0},1 ${r + Math.sin(alpha) * r},${r + -Math.cos(alpha) * r} L ${r},${r} Z`
|
|
return `path("${path}")`
|
|
}, [percentage, size])
|
|
return (
|
|
<div
|
|
className={classNames(
|
|
'relative flex flex-shrink-0 flex-col items-center justify-center rounded-full border-8 border-indigo-200 bg-transparent dark:border-gray-700',
|
|
)}
|
|
style={{
|
|
width: `${size}rem`,
|
|
height: `${size}rem`,
|
|
}}
|
|
>
|
|
{percentage !== null && (
|
|
<div
|
|
className="absolute -inset-2 rounded-full border-8 border-indigo-400 bg-transparent dark:border-indigo-500"
|
|
style={{ clipPath }}
|
|
aria-hidden
|
|
/>
|
|
)}
|
|
<span className="text-xl tabular-nums text-gray-800 dark:text-gray-300">{remark}</span>
|
|
<span className="text-sm font-medium text-gray-600 dark:text-gray-500">{caption}</span>
|
|
</div>
|
|
)
|
|
}
|