- 更新 package.json 和 package-lock.json 中的依赖项 - 新增/更新陈云、黄炎培、鲁迅、孙中山的音频文件 - 新增场景和组件的图片资源 - 清理未使用的音频和图片文件 - 优化多个组件和页面的实现逻辑
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import { useEffect, useMemo, useRef } from 'react'
|
||
import { hideHint, showHint } from './HintHost'
|
||
import type { HintPositionOffset, ShowHintOptions } from './HintHost'
|
||
|
||
export type SceneHintProps = {
|
||
/** 是否展示提示 */
|
||
when: boolean
|
||
/** 提示文本(建议直接传 string children) */
|
||
children: string
|
||
/** 延迟展示(ms) */
|
||
delayMs?: number
|
||
/** 自动消失时间(ms)。不传则使用默认值(2000) */
|
||
durationMs?: number
|
||
/** 是否常驻 */
|
||
persistent?: boolean
|
||
/** 相对中心点微调位置 */
|
||
offset?: HintPositionOffset
|
||
/** 自定义层级 */
|
||
zIndex?: number
|
||
/** 进入 when=true 时只展示一次(后续 when=true 的 rerender 不会重复触发) */
|
||
once?: boolean
|
||
/**
|
||
* 触发 key:当此值变化时(且 when=true)会重新触发一次 showHint。
|
||
* 用于“同一个提示需要再次弹出”的场景。
|
||
*/
|
||
triggerKey?: string | number
|
||
/** when 变为 false / 卸载时是否自动 hide */
|
||
hideOnCleanup?: boolean
|
||
}
|
||
|
||
export function SceneHint({
|
||
when,
|
||
children,
|
||
delayMs = 0,
|
||
durationMs,
|
||
persistent = false,
|
||
offset,
|
||
zIndex,
|
||
once = false,
|
||
triggerKey,
|
||
hideOnCleanup = true,
|
||
}: SceneHintProps) {
|
||
const shownOnceRef = useRef(false)
|
||
const delayTimerRef = useRef<number | null>(null)
|
||
|
||
const text = useMemo(() => {
|
||
return typeof children === 'string' ? children : String(children ?? '')
|
||
}, [children])
|
||
|
||
useEffect(() => {
|
||
if (!when) {
|
||
shownOnceRef.current = false
|
||
if (hideOnCleanup) hideHint()
|
||
return
|
||
}
|
||
|
||
if (once && shownOnceRef.current) return
|
||
|
||
const options: ShowHintOptions = {
|
||
persistent,
|
||
...(durationMs !== undefined ? { durationMs } : null),
|
||
...(offset ? { offset } : null),
|
||
...(zIndex !== undefined ? { zIndex } : null),
|
||
}
|
||
|
||
if (delayMs > 0) {
|
||
delayTimerRef.current = window.setTimeout(() => {
|
||
showHint(text, options)
|
||
shownOnceRef.current = true
|
||
}, delayMs)
|
||
} else {
|
||
showHint(text, options)
|
||
shownOnceRef.current = true
|
||
}
|
||
|
||
return () => {
|
||
if (delayTimerRef.current) {
|
||
window.clearTimeout(delayTimerRef.current)
|
||
delayTimerRef.current = null
|
||
}
|
||
if (hideOnCleanup) hideHint()
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [
|
||
when,
|
||
text,
|
||
delayMs,
|
||
durationMs,
|
||
persistent,
|
||
offset?.x,
|
||
offset?.y,
|
||
zIndex,
|
||
once,
|
||
triggerKey,
|
||
hideOnCleanup,
|
||
])
|
||
|
||
return null
|
||
}
|