quasar
Version:
Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
243 lines (206 loc) • 5.93 kB
JavaScript
import { h, computed, getCurrentInstance } from 'vue'
import useSize, {
useSizeProps
} from '../../composables/private.use-size/use-size.js'
import { createComponent } from '../../utils/private.create/create.js'
import { hSlot, hMergeSlot } from '../../utils/private.render/render.js'
const defaultViewBox = '0 0 24 24'
const sameFn = i => i
const ionFn = i => `ionicons ${i}`
const libMap = {
'mdi-': i => `mdi ${i}`,
'icon-': sameFn, // fontawesome equiv
'bt-': i => `bt ${i}`,
'eva-': i => `eva ${i}`,
'ion-md': ionFn,
'ion-ios': ionFn,
'ion-logo': ionFn,
'iconfont ': sameFn,
'ti-': i => `themify-icon ${i}`,
'bi-': i => `bootstrap-icons ${i}`,
'i-': sameFn // UnoCSS pure icons
}
const matMap = {
o_: '-outlined',
r_: '-round',
s_: '-sharp'
}
const symMap = {
sym_o_: '-outlined',
sym_r_: '-rounded',
sym_s_: '-sharp'
}
const libRE = new RegExp('^(' + Object.keys(libMap).join('|') + ')')
const matRE = new RegExp('^(' + Object.keys(matMap).join('|') + ')')
const symRE = new RegExp('^(' + Object.keys(symMap).join('|') + ')')
const mRE = /^[Mm]\s?[-+]?\.?\d/
const imgRE = /^img:/
const svgUseRE = /^svguse:/
const ionRE = /^ion-/
const faRE =
/^(fa-(classic|sharp|solid|regular|light|brands|duotone|thin)|[lf]a[srlbdk]?) /
export default createComponent({
name: 'QIcon',
props: {
...useSizeProps,
tag: {
type: String,
default: 'i'
},
name: String,
color: String,
left: Boolean,
right: Boolean
},
setup(props, { slots }) {
const {
proxy: { $q }
} = getCurrentInstance()
const sizeStyle = useSize(props)
const classes = computed(
() =>
'q-icon' +
(props.left === true ? ' on-left' : '') + // TODO Qv3: drop this
(props.right === true ? ' on-right' : '') +
(props.color !== void 0 ? ` text-${props.color}` : '')
)
const type = computed(() => {
let cls
let icon = props.name
if (icon === 'none' || !icon) {
return { none: true }
}
if ($q.iconMapFn !== null) {
const res = $q.iconMapFn(icon)
if (res !== void 0) {
if (res.icon !== void 0) {
icon = res.icon
if (icon === 'none' || !icon) {
return { none: true }
}
} else {
return {
cls: res.cls,
content: res.content !== void 0 ? res.content : ' '
}
}
}
}
if (mRE.test(icon) === true) {
const [def, viewBox = defaultViewBox] = icon.split('|')
return {
svg: true,
viewBox,
nodes: def.split('&&').map(path => {
const [d, style, transform] = path.split('@@')
return h('path', { style, d, transform })
})
}
}
if (imgRE.test(icon) === true) {
return {
img: true,
src: icon.substring(4)
}
}
if (svgUseRE.test(icon) === true) {
const [def, viewBox = defaultViewBox] = icon.split('|')
return {
svguse: true,
src: def.substring(7),
viewBox
}
}
let content = ' '
const matches = icon.match(libRE)
if (matches !== null) {
cls = libMap[matches[1]](icon)
} else if (faRE.test(icon) === true) {
cls = icon
} else if (ionRE.test(icon) === true) {
cls = `ionicons ion-${$q.platform.is.ios === true ? 'ios' : 'md'}${icon.substring(3)}`
} else if (symRE.test(icon) === true) {
// "notranslate" class is for Google Translate
// to avoid tampering with Material Symbols ligature font
//
// Caution: To be able to add suffix to the class name,
// keep the 'material-symbols' at the end of the string.
cls = 'notranslate material-symbols'
const symMatches = icon.match(symRE)
if (symMatches !== null) {
icon = icon.substring(6)
cls += symMap[symMatches[1]]
}
content = icon
} else {
// "notranslate" class is for Google Translate
// to avoid tampering with Material Icons ligature font
//
// Caution: To be able to add suffix to the class name,
// keep the 'material-icons' at the end of the string.
cls = 'notranslate material-icons'
const matMatches = icon.match(matRE)
if (matMatches !== null) {
icon = icon.substring(2)
cls += matMap[matMatches[1]]
}
content = icon
}
return {
cls,
content
}
})
return () => {
const data = {
class: classes.value,
style: sizeStyle.value,
'aria-hidden': 'true'
}
if (type.value.none === true) {
return h(props.tag, data, hSlot(slots.default))
}
if (type.value.img === true) {
return h(
props.tag,
data,
hMergeSlot(slots.default, [h('img', { src: type.value.src })])
)
}
if (type.value.svg === true) {
return h(
props.tag,
data,
hMergeSlot(slots.default, [
h(
'svg',
{
viewBox: type.value.viewBox || '0 0 24 24'
},
type.value.nodes
)
])
)
}
if (type.value.svguse === true) {
return h(
props.tag,
data,
hMergeSlot(slots.default, [
h(
'svg',
{
viewBox: type.value.viewBox
},
[h('use', { 'xlink:href': type.value.src })]
)
])
)
}
if (type.value.cls !== void 0) {
data.class += ' ' + type.value.cls
}
return h(props.tag, data, hMergeSlot(slots.default, [type.value.content]))
}
}
})