115 lines
3.5 KiB
JavaScript
115 lines
3.5 KiB
JavaScript
/**
|
|
* SNS 아이콘 프리셋
|
|
* @type {ReadonlyArray<{ icon: string, label: string, placeholder: string }>}
|
|
*/
|
|
export const SOCIAL_ICON_PRESETS = [
|
|
{ icon: 'x', label: 'X', placeholder: 'https://x.com/...' },
|
|
{ icon: 'github', label: 'GitHub', placeholder: 'https://github.com/...' },
|
|
{ icon: 'instagram', label: 'Instagram', placeholder: 'https://instagram.com/...' },
|
|
{ icon: 'youtube', label: 'YouTube', placeholder: 'https://youtube.com/...' },
|
|
{ icon: 'facebook', label: 'Facebook', placeholder: 'https://facebook.com/...' },
|
|
{ icon: 'linkedin', label: 'LinkedIn', placeholder: 'https://linkedin.com/in/...' },
|
|
{ icon: 'rss', label: 'RSS', placeholder: '/rss/' },
|
|
{ icon: 'link', label: 'Link', placeholder: 'https://...' }
|
|
]
|
|
|
|
/** @type {number} 최대 SNS 링크 개수 */
|
|
export const MAX_SOCIAL_LINK_COUNT = 12
|
|
|
|
/**
|
|
* SNS 아이콘 프리셋을 찾는다.
|
|
* @param {unknown} icon - 아이콘 키
|
|
* @returns {{ icon: string, label: string, placeholder: string }} 아이콘 프리셋
|
|
*/
|
|
export const getSocialIconPreset = (icon) => {
|
|
const key = String(icon || '').trim().toLowerCase()
|
|
return SOCIAL_ICON_PRESETS.find((preset) => preset.icon === key) || SOCIAL_ICON_PRESETS[SOCIAL_ICON_PRESETS.length - 1]
|
|
}
|
|
|
|
/**
|
|
* SNS 링크 URL을 정리한다.
|
|
* @param {unknown} value - 입력 URL
|
|
* @returns {string} 정리된 URL
|
|
*/
|
|
export const normalizeSocialLinkUrl = (value) => {
|
|
const trimmed = String(value || '').trim()
|
|
|
|
if (!trimmed) {
|
|
return ''
|
|
}
|
|
|
|
if (trimmed.startsWith('/')) {
|
|
return trimmed
|
|
}
|
|
|
|
if (/^mailto:[^\s@]+@[^\s@]+\.[^\s@]+$/i.test(trimmed)) {
|
|
return trimmed
|
|
}
|
|
|
|
try {
|
|
const parsed = new URL(trimmed)
|
|
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
|
return parsed.toString()
|
|
}
|
|
} catch {
|
|
return ''
|
|
}
|
|
|
|
return ''
|
|
}
|
|
|
|
/**
|
|
* SNS 링크 항목을 정리한다.
|
|
* @param {unknown} item - 입력 항목
|
|
* @param {number} index - 항목 순서
|
|
* @returns {{ id: string, icon: string, label: string, url: string }|null} 정리된 항목
|
|
*/
|
|
export const normalizeSocialLinkItem = (item, index = 0) => {
|
|
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
return null
|
|
}
|
|
|
|
const preset = getSocialIconPreset(item.icon)
|
|
const url = normalizeSocialLinkUrl(item.url)
|
|
|
|
if (!url) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
id: String(item.id || `${preset.icon}-${index + 1}`).trim().slice(0, 80),
|
|
icon: preset.icon,
|
|
label: String(item.label || preset.label).trim().slice(0, 80),
|
|
url
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SNS 링크 목록을 저장 가능한 형태로 정리한다.
|
|
* @param {unknown} value - 입력 목록
|
|
* @returns {Array<{ id: string, icon: string, label: string, url: string }>} 정리된 SNS 링크 목록
|
|
*/
|
|
export const normalizeSocialLinks = (value) => {
|
|
const source = Array.isArray(value)
|
|
? value
|
|
: Object.entries(value && typeof value === 'object' ? value : {}).map(([icon, url]) => ({ icon, url }))
|
|
|
|
return source
|
|
.slice(0, MAX_SOCIAL_LINK_COUNT)
|
|
.map(normalizeSocialLinkItem)
|
|
.filter(Boolean)
|
|
}
|
|
|
|
/**
|
|
* 저장된 SNS 링크 중 실제 노출할 항목만 반환한다.
|
|
* @param {unknown} value - SNS 링크 목록
|
|
* @returns {Array<{ id: string, label: string, href: string, icon: string, external: boolean }>} 노출 링크
|
|
*/
|
|
export const getVisibleSocialLinks = (value) => normalizeSocialLinks(value).map((item) => ({
|
|
id: item.id,
|
|
label: item.label,
|
|
href: item.url,
|
|
icon: item.icon,
|
|
external: !item.url.startsWith('/') && !item.url.startsWith('mailto:')
|
|
}))
|