You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

109 lines
3.6 KiB

import { TEmoji } from '@/types'
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
function parseNativeEmoji(unified: string): string {
return String.fromCodePoint(...unified.split('-').map((h) => parseInt(h, 16)))
}
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function truncateText(text: string, maxWords: number): string {
if (!text) return ''
const words = text.trim().split(/\s+/)
if (words.length <= maxWords) return text
return words.slice(0, maxWords).join(' ') + '...'
}
/**
* Remove emoji characters from a string
* This regex covers most emoji ranges including:
* - Emoticons (😀-🙏)
* - Misc Symbols & Pictographs (🚀-🗿)
* - Transport & Map Symbols (🚁-🛿)
* - Enclosed characters (Ⓜ, ©, etc.)
* - Regional indicator symbols (flags)
* - And other emoji ranges
*/
export function removeEmojis(text: string): string {
if (!text) return ''
// Comprehensive emoji regex pattern covering major emoji Unicode ranges
const emojiRegex = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1F018}-\u{1F270}]|[\u{238C}-\u{2454}]|[\u{20D0}-\u{20FF}]|[\u{FE00}-\u{FE0F}]|[\u{FE20}-\u{FE2F}]|[\u{E0020}-\u{E007F}]/gu
return text.replace(emojiRegex, '').trim().replace(/\s+/g, ' ')
}
export function isSafari() {
if (typeof window === 'undefined' || !window.navigator) return false
const ua = window.navigator.userAgent
const vendor = window.navigator.vendor
return /Safari/.test(ua) && /Apple Computer/.test(vendor) && !/Chrome/.test(ua)
}
export function isAndroid() {
if (typeof window === 'undefined' || !window.navigator) return false
const ua = window.navigator.userAgent
return /android/i.test(ua)
}
export function isTorBrowser() {
if (typeof window === 'undefined' || !window.navigator) return false
const ua = window.navigator.userAgent
return /torbrowser/i.test(ua)
}
export function isTouchDevice() {
if (typeof window === 'undefined' || !window.navigator) return false
return 'ontouchstart' in window || navigator.maxTouchPoints > 0
}
export function isInViewport(el: HTMLElement) {
const rect = el.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}
export function isPartiallyInViewport(el: HTMLElement) {
const rect = el.getBoundingClientRect()
return (
rect.top < (window.innerHeight || document.documentElement.clientHeight) &&
rect.bottom > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.right > 0
)
}
export function isSupportCheckConnectionType() {
if (typeof window === 'undefined' || !(navigator as any).connection) return false
return typeof (navigator as any).connection.type === 'string'
}
export function isEmail(email: string) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
export function isDevEnv() {
return process.env.NODE_ENV === 'development'
}
export function parseEmojiPickerUnified(unified: string): string | TEmoji | undefined {
if (unified.startsWith(':')) {
const secondColonIndex = unified.indexOf(':', 1)
if (secondColonIndex < 0) return undefined
const shortcode = unified.slice(1, secondColonIndex)
const url = unified.slice(secondColonIndex + 1)
return { shortcode, url }
} else {
return parseNativeEmoji(unified)
}
}