Compare commits
3 commits
2d52a1668f
...
7184049186
| Author | SHA1 | Date | |
|---|---|---|---|
| 7184049186 | |||
| 928b46f13a | |||
| 01bbbbd6af |
4 changed files with 54 additions and 28 deletions
|
|
@ -25,22 +25,24 @@ export function detectVariant(title) {
|
|||
|
||||
/**
|
||||
* 금요일 기준의 이번 주차 시작일 (YYYY-MM-DD, KST) 반환
|
||||
* 금/토/일 → 직전 금요일
|
||||
* 월/화/수/목 → 이전 주 금요일
|
||||
* 목 → 다음 날 금요일 (금요일 공휴일로 목요일 선공개되는 경우 대응)
|
||||
* 금/토/일 → 이번 주 금요일
|
||||
* 월/화/수 → 지난 주 금요일
|
||||
*/
|
||||
export function currentWeekFriday(now = dayjs().tz(KST)) {
|
||||
const dow = now.day(); // 0=일 ... 5=금 6=토
|
||||
// 금요일 기준 diff: 금(5)이면 0, 토(6)이면 -1, 일(0)이면 -2, 월(1)이면 -3 ...
|
||||
const diff = dow >= 5 ? dow - 5 : dow + 2;
|
||||
const dow = now.day(); // 0=일 ... 4=목 5=금 6=토
|
||||
// 목요일은 내일이 금요일이므로 -1
|
||||
const diff = dow === 4 ? -1 : (dow >= 5 ? dow - 5 : dow + 2);
|
||||
return now.startOf('day').subtract(diff, 'day').format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
/**
|
||||
* 금~일요일인지
|
||||
* 썬데이 메이플 표시 가능한 요일대 (목~일)
|
||||
* 목요일은 금요일 공휴일 케이스 대응용. 해당 주차 row 가 없으면 어차피 available: false.
|
||||
*/
|
||||
export function isInSundayWindow(now = dayjs().tz(KST)) {
|
||||
const dow = now.day();
|
||||
return dow === 5 || dow === 6 || dow === 0;
|
||||
return dow === 4 || dow === 5 || dow === 6 || dow === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ async function runPolling() {
|
|||
}
|
||||
|
||||
/**
|
||||
* 매주 금요일 09:00 KST 실행
|
||||
* 매주 목/금 09:00 KST 실행 (금요일이 공휴일이면 목요일에 게시되는 경우 대응)
|
||||
*/
|
||||
export function scheduleSundayMapleCron() {
|
||||
cron.schedule('0 9 * * 5', runPolling, { timezone: 'Asia/Seoul' });
|
||||
console.log('[sunday-maple cron] 매주 금요일 09:00 KST 스케줄 등록');
|
||||
cron.schedule('0 9 * * 4,5', runPolling, { timezone: 'Asia/Seoul' });
|
||||
console.log('[sunday-maple cron] 매주 목/금 09:00 KST 스케줄 등록');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,42 +17,62 @@ const DELAY_DEFAULT = 200
|
|||
*/
|
||||
export default function GlobalTooltip() {
|
||||
const [state, setState] = useState({ open: false, text: '', placement: 'top', coords: null })
|
||||
const triggerRef = useRef(null)
|
||||
const triggerRef = useRef(null) // 툴팁이 실제로 떠있는 타겟
|
||||
const pendingRef = useRef(null) // delay 중이라 title 을 벗긴 타겟 (아직 툴팁 안 뜸)
|
||||
const tooltipRef = useRef(null)
|
||||
const timerRef = useRef(null)
|
||||
const titleMap = useRef(new WeakMap())
|
||||
|
||||
useEffect(() => {
|
||||
function restoreTitle() {
|
||||
const el = triggerRef.current
|
||||
function stripTitle(el) {
|
||||
const t = el.getAttribute('title')
|
||||
if (t && !titleMap.current.has(el)) {
|
||||
titleMap.current.set(el, t)
|
||||
el.removeAttribute('title')
|
||||
}
|
||||
}
|
||||
|
||||
function restoreTitle(el) {
|
||||
if (el && titleMap.current.has(el)) {
|
||||
const prev = titleMap.current.get(el)
|
||||
el.setAttribute('title', prev)
|
||||
el.setAttribute('title', titleMap.current.get(el))
|
||||
titleMap.current.delete(el)
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clearTimeout(timerRef.current)
|
||||
restoreTitle()
|
||||
restoreTitle(pendingRef.current)
|
||||
restoreTitle(triggerRef.current)
|
||||
pendingRef.current = null
|
||||
triggerRef.current = null
|
||||
setState((s) => (s.open ? { ...s, open: false } : s))
|
||||
}
|
||||
|
||||
function showFor(target) {
|
||||
// 이미 같은 타겟이 처리중이면 무시
|
||||
if (triggerRef.current === target || pendingRef.current === target) return
|
||||
|
||||
// 다른 pending 이 있다면 먼저 정리
|
||||
if (pendingRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
restoreTitle(pendingRef.current)
|
||||
pendingRef.current = null
|
||||
}
|
||||
|
||||
const nativeTitle = target.getAttribute('title')
|
||||
const dataTooltip = target.getAttribute('data-tooltip')
|
||||
const text = nativeTitle || dataTooltip
|
||||
if (!text) return
|
||||
if (nativeTitle && !titleMap.current.has(target)) {
|
||||
titleMap.current.set(target, nativeTitle)
|
||||
target.removeAttribute('title')
|
||||
}
|
||||
|
||||
if (nativeTitle) stripTitle(target)
|
||||
pendingRef.current = target
|
||||
|
||||
const placement = target.getAttribute('data-tooltip-placement') || 'top'
|
||||
const delay = Number(target.getAttribute('data-tooltip-delay')) || DELAY_DEFAULT
|
||||
clearTimeout(timerRef.current)
|
||||
timerRef.current = setTimeout(() => {
|
||||
triggerRef.current = target
|
||||
pendingRef.current = null
|
||||
setState({ open: true, text, placement, coords: null })
|
||||
}, delay)
|
||||
}
|
||||
|
|
@ -60,19 +80,22 @@ export default function GlobalTooltip() {
|
|||
function handleOver(e) {
|
||||
const target = e.target.closest?.('[title], [data-tooltip]')
|
||||
if (!target) return
|
||||
if (triggerRef.current === target) return
|
||||
// 다른 타겟으로 이동 시 기존 정리
|
||||
if (triggerRef.current === target || pendingRef.current === target) return
|
||||
|
||||
// 다른 trigger 가 떠있으면 먼저 정리
|
||||
if (triggerRef.current && triggerRef.current !== target) {
|
||||
restoreTitle()
|
||||
restoreTitle(triggerRef.current)
|
||||
triggerRef.current = null
|
||||
setState((s) => (s.open ? { ...s, open: false } : s))
|
||||
}
|
||||
showFor(target)
|
||||
}
|
||||
|
||||
function handleOut(e) {
|
||||
if (!triggerRef.current) return
|
||||
const active = triggerRef.current || pendingRef.current
|
||||
if (!active) return
|
||||
const rt = e.relatedTarget
|
||||
if (rt && triggerRef.current.contains(rt)) return
|
||||
if (rt && active.contains(rt)) return
|
||||
hide()
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +121,8 @@ export default function GlobalTooltip() {
|
|||
window.removeEventListener('scroll', hide, true)
|
||||
window.removeEventListener('resize', hide)
|
||||
clearTimeout(timerRef.current)
|
||||
restoreTitle()
|
||||
restoreTitle(pendingRef.current)
|
||||
restoreTitle(triggerRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export const SECTIONS = {
|
||||
notice: { label: '메이플스토리 공지사항', dataKey: 'notice', pageSize: 5, kind: 'text' },
|
||||
update: { label: '메이플스토리 업데이트', dataKey: 'update_notice', pageSize: 5, kind: 'text' },
|
||||
notice: { label: '공지사항', dataKey: 'notice', pageSize: 5, kind: 'text' },
|
||||
update: { label: '업데이트', dataKey: 'update_notice', pageSize: 5, kind: 'text' },
|
||||
event: {
|
||||
label: '진행 중인 이벤트',
|
||||
dataKey: 'event_notice',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue