@coreui/vue-pro
Version:
UI Components Library for Vue.js
151 lines (125 loc) • 4.1 kB
text/typescript
import type { Option, OptionsGroup, Search } from './types'
export const filterOptions = (
options: (Option | OptionsGroup)[],
search: string
): (Option | OptionsGroup)[] => {
const result: (Option | OptionsGroup)[] = []
options.forEach((item: Option | OptionsGroup) => {
if (typeof item !== 'string' && 'options' in item && Array.isArray(item.options)) {
const filteredOptions = filterOptions(item.options, search)
if (filteredOptions.length > 0) {
result.push({ ...item, options: filteredOptions })
}
} else if (getOptionLabel(item).toLowerCase().includes(search.toLowerCase())) {
result.push(item)
}
})
return result
}
export const flattenOptionsArray = (
options: (Option | OptionsGroup)[],
keepGroupLabel?: boolean
): (Option | OptionsGroup)[] => {
const optionsList: (Option | OptionsGroup)[] = []
for (const option of options) {
if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
if (keepGroupLabel) {
// Add group header (without options property to indicate it's a header)
const { options: _, ...groupHeader } = option
optionsList.push(groupHeader)
}
optionsList.push(...option.options)
} else {
optionsList.push(option)
}
}
return optionsList
}
export const isExternalSearch = (search: Search | undefined): boolean => {
return (
(typeof search === 'string' && search === 'external') ||
(typeof search === 'object' && search.external === true)
)
}
export const isGlobalSearch = (search: Search | undefined): boolean => {
return (
(typeof search === 'string' && search === 'global') ||
(typeof search === 'object' && search.global === true)
)
}
export const getOptionLabel = (option: Option) =>
typeof option === 'string' ? option : option.label
const ESCAPE_HTML_MAP: Record<string, string> = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
}
const escapeHtml = (string: string): string =>
String(string).replaceAll(/[&<>"']/g, (character) => ESCAPE_HTML_MAP[character])
const escapeRegExp = (string: string): string =>
String(string).replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)
export const highlightSubstring = (string: string, query?: string) => {
if (!query) {
return escapeHtml(string)
}
const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi')
return String(string)
.split(regex)
.map((part, index) =>
index % 2 === 0 ? escapeHtml(part) : `<strong>${escapeHtml(part)}</strong>`
)
.join('')
}
export const isOptionDisabled = (option: Option | OptionsGroup | string) =>
typeof option === 'string' ? false : option.disabled
export const isOptionSelected = (
option: Option | OptionsGroup | string,
selected: Option | null
) => {
if (!selected) {
return false
}
if (typeof option === 'string' && typeof selected === 'string') {
return selected === option
}
if (typeof option !== 'string' && typeof selected !== 'string') {
return selected.label === option.label
}
return false
}
export const getFirstOptionByLabel = (
value: string,
options: (Option | OptionsGroup)[]
): Option | null => {
for (const option of options) {
if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
const found = getFirstOptionByLabel(value, option.options)
if (found) {
return found
}
}
if (getOptionLabel(option).toLowerCase() === value.toLowerCase()) {
return option
}
}
return null
}
export const getFirstOptionByValue = (
value: number | string,
options: (Option | OptionsGroup)[]
): Option | null => {
for (const option of options) {
if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
const found = getFirstOptionByValue(value, option.options)
if (found) {
return found
}
}
if (typeof option !== 'string' && 'value' in option && option.value === value) {
return option
}
}
return null
}