Files
sori.studio/lib/announcement-bar.js

211 lines
5.9 KiB
JavaScript

/**
* 어나운스 바 배경색 프리셋
* @type {ReadonlyArray<{ id: string, label: string, value: string, textColor: string }>}
*/
export const ANNOUNCEMENT_BACKGROUND_PRESETS = [
{ id: 'black', label: '검정', value: '#15171a', textColor: '#ffffff' },
{ id: 'white', label: '흰색', value: '#ffffff', textColor: '#15171a' },
{ id: 'accent', label: '브랜드', value: '#ff4f2e', textColor: '#ffffff' }
]
/** @type {string} 기본 어나운스 바 배경색 */
export const DEFAULT_ANNOUNCEMENT_BACKGROUND_COLOR = '#15171a'
/** @type {string} 기본 어나운스 바 정렬 */
export const DEFAULT_ANNOUNCEMENT_ALIGNMENT = 'center'
/**
* 어나운스 바 정렬 옵션
* @type {ReadonlyArray<{ value: string, label: string }>}
*/
export const ANNOUNCEMENT_ALIGNMENT_OPTIONS = [
{ value: 'center', label: '중앙' },
{ value: 'left', label: '왼쪽' }
]
/**
* 어나운스 바 배경색 형식이 올바른지 확인한다.
* @param {unknown} value - hex 색상
* @returns {boolean} 유효 여부
*/
export const isValidAnnouncementBackgroundColor = (value) => /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || '').trim())
/**
* 어나운스 바 배경색을 6자리 hex 값으로 정규화한다.
* @param {unknown} value - 입력 색상
* @returns {string} 정규화된 색상
*/
export const normalizeAnnouncementBackgroundColor = (value) => {
const color = String(value || '').trim().toLowerCase()
if (!isValidAnnouncementBackgroundColor(color)) {
return DEFAULT_ANNOUNCEMENT_BACKGROUND_COLOR
}
if (color.length === 4) {
return `#${color[1]}${color[1]}${color[2]}${color[2]}${color[3]}${color[3]}`
}
return color
}
/**
* 어나운스 바 정렬 값을 정리한다.
* @param {unknown} value - 입력 정렬
* @returns {string} 정리된 정렬
*/
export const normalizeAnnouncementAlignment = (value) => {
const alignment = String(value || '').trim().toLowerCase()
return ANNOUNCEMENT_ALIGNMENT_OPTIONS.some((option) => option.value === alignment)
? alignment
: DEFAULT_ANNOUNCEMENT_ALIGNMENT
}
/**
* 어나운스 바 배경색에 맞는 전경색을 반환한다.
* @param {string} backgroundColor - hex 배경색
* @returns {string} 전경 hex 색상
*/
export const getAnnouncementBarTextColor = (backgroundColor) => {
const normalized = normalizeAnnouncementBackgroundColor(backgroundColor)
const preset = ANNOUNCEMENT_BACKGROUND_PRESETS.find((item) => item.value.toLowerCase() === normalized)
if (preset) {
return preset.textColor
}
const hex = normalized.slice(1)
const red = parseInt(hex.slice(0, 2), 16)
const green = parseInt(hex.slice(2, 4), 16)
const blue = parseInt(hex.slice(4, 6), 16)
const luminance = (red * 299 + green * 587 + blue * 114) / 1000
return luminance > 150 ? '#15171a' : '#ffffff'
}
/**
* 어나운스 링크를 정리한다. 빈 값은 링크 미사용.
* @param {string} url - 입력 URL
* @returns {string} 정리된 URL 또는 빈 문자열
*/
export const normalizeAnnouncementUrl = (url) => {
const trimmed = (url || '').trim()
if (!trimmed) {
return ''
}
if (trimmed.startsWith('/')) {
return trimmed
}
try {
const parsed = new URL(trimmed)
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
return parsed.toString()
}
} catch {
return ''
}
return ''
}
/** @type {string} 어나운스 바 7일 숨김 localStorage 키 */
export const ANNOUNCEMENT_DISMISS_STORAGE_KEY = 'SITE_ANNOUNCEMENT_DISMISS'
/** @type {string} 어나운스 바 이번 방문(세션) 숨김 sessionStorage 키 */
export const ANNOUNCEMENT_SESSION_DISMISS_KEY = 'SITE_ANNOUNCEMENT_SESSION_DISMISS'
/** @type {number} 기본 숨김 일수 */
export const ANNOUNCEMENT_SNOOZE_DAYS = 7
/**
* 어나운스 바 닫기 식별 키(설정 저장 시각)
* @param {{ updatedAt?: string | null }} settings - 사이트 설정
* @returns {string} 식별 키
*/
export const getAnnouncementDismissKey = (settings) => settings?.updatedAt || ''
/**
* 어나운스 바가 숨김 상태인지 확인한다.
* @param {{ updatedAt?: string | null }} settings - 사이트 설정
* @returns {boolean} 숨김 여부
*/
export const isAnnouncementDismissed = (settings) => {
if (!import.meta.client) {
return false
}
const key = getAnnouncementDismissKey(settings)
if (!key) {
return false
}
const sessionDismissed = sessionStorage.getItem(ANNOUNCEMENT_SESSION_DISMISS_KEY)
if (sessionDismissed === key) {
return true
}
const raw = localStorage.getItem(ANNOUNCEMENT_DISMISS_STORAGE_KEY)
if (!raw) {
return false
}
try {
const parsed = JSON.parse(raw)
if (parsed?.key !== key) {
return false
}
if (typeof parsed.until === 'number' && Date.now() < parsed.until) {
return true
}
if (parsed.until == null && parsed.key === key) {
return true
}
} catch {
return false
}
return false
}
/**
* 이번 브라우저 방문(세션) 동안만 어나운스 바를 숨긴다.
* @param {{ updatedAt?: string | null }} settings - 사이트 설정
* @returns {void}
*/
export const dismissAnnouncementForSession = (settings) => {
if (!import.meta.client) {
return
}
const key = getAnnouncementDismissKey(settings)
if (!key) {
return
}
sessionStorage.setItem(ANNOUNCEMENT_SESSION_DISMISS_KEY, key)
}
/**
* 지정 일수 동안 어나운스 바를 숨긴다.
* @param {{ updatedAt?: string | null }} settings - 사이트 설정
* @param {number} [days=ANNOUNCEMENT_SNOOZE_DAYS] - 숨김 일수
* @returns {void}
*/
export const dismissAnnouncementForDays = (settings, days = ANNOUNCEMENT_SNOOZE_DAYS) => {
if (!import.meta.client) {
return
}
const key = getAnnouncementDismissKey(settings)
if (!key) {
return
}
const until = Date.now() + days * 24 * 60 * 60 * 1000
localStorage.setItem(ANNOUNCEMENT_DISMISS_STORAGE_KEY, JSON.stringify({ key, until }))
sessionStorage.setItem(ANNOUNCEMENT_SESSION_DISMISS_KEY, key)
}