first commit

This commit is contained in:
Artem Anufrij
2026-10-07 18:56:27 +02:00
commit 2a8a60b725
7768 changed files with 1550851 additions and 0 deletions
@@ -0,0 +1,262 @@
import { classList, objectWithKey } from '../utils'
import { computed, defineComponent, watch } from 'vue'
import { icon as faIcon, parse as faParse } from '@fortawesome/fontawesome-svg-core'
import convert from '../converter'
import log from '../logger'
function normalizeIconArgs(icon) {
if (icon && typeof icon === 'object' && icon.prefix && icon.iconName && icon.icon) {
return icon
}
if (faParse.icon) {
return faParse.icon(icon)
}
if (icon === null) {
return null
}
if (typeof icon === 'object' && icon.prefix && icon.iconName) {
return icon
}
if (Array.isArray(icon) && icon.length === 2) {
return { prefix: icon[0], iconName: icon[1] }
}
if (typeof icon === 'string') {
return { prefix: 'fas', iconName: icon }
}
}
export default defineComponent({
name: 'FontAwesomeIcon',
props: {
border: {
type: Boolean,
default: false
},
// the fixedWidth property has been deprecated as of version 7
fixedWidth: {
type: Boolean,
default: false
},
flip: {
type: [Boolean, String],
default: false,
validator: (value) => [true, false, 'horizontal', 'vertical', 'both'].indexOf(value) > -1
},
icon: {
type: [Object, Array, String],
required: true
},
mask: {
type: [Object, Array, String],
default: null
},
maskId: {
type: String,
default: null
},
listItem: {
type: Boolean,
default: false
},
pull: {
type: String,
default: null,
validator: (value) => ['right', 'left'].indexOf(value) > -1
},
pulse: {
type: Boolean,
default: false
},
rotation: {
type: [String, Number],
default: null,
validator: (value) => [90, 180, 270].indexOf(Number.parseInt(value, 10)) > -1
},
// the rotateBy property is only supported in version 7.0.0 and later
rotateBy: {
type: Boolean,
default: false
},
swapOpacity: {
type: Boolean,
default: false
},
size: {
type: String,
default: null,
validator: (value) => ['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1
},
spin: {
type: Boolean,
default: false
},
transform: {
type: [String, Object],
default: null
},
symbol: {
type: [Boolean, String],
default: false
},
title: {
type: String,
default: null
},
titleId: {
type: String,
default: null
},
inverse: {
type: Boolean,
default: false
},
bounce: {
type: Boolean,
default: false
},
shake: {
type: Boolean,
default: false
},
beat: {
type: Boolean,
default: false
},
fade: {
type: Boolean,
default: false
},
beatFade: {
type: Boolean,
default: false
},
flash: {
type: Boolean,
default: false
},
spinPulse: {
type: Boolean,
default: false
},
spinReverse: {
type: Boolean,
default: false
},
// the widthAuto property is only supported in version 7.0.0 and later
widthAuto: {
type: Boolean,
default: false
},
// the canvasSquare and canvasRoomy properties are only supported in version 7.3.0 and later
canvasSquare: {
type: Boolean,
default: false
},
canvasRoomy: {
type: Boolean,
default: false
},
gradientFill: {
type: Object,
default: null,
validator(value) {
if (typeof value.id !== 'string' || !value.id) {
console.warn('FontAwesomeIcon: gradientFill.id must be a non-empty string')
return false
}
if (value.type !== 'linear' && value.type !== 'radial') {
console.warn('FontAwesomeIcon: gradientFill.type must be "linear" or "radial"')
return false
}
return true
}
},
// the following animation properties are only supported in version 7.3.0 and later
flip360: {
type: Boolean,
default: false
},
buzz: {
type: Boolean,
default: false
},
float: {
type: Boolean,
default: false
},
jello: {
type: Boolean,
default: false
},
spinSnap: {
type: Boolean,
default: false
},
spinSnap4: {
type: Boolean,
default: false
},
spinSnap8: {
type: Boolean,
default: false
},
swing: {
type: Boolean,
default: false
},
wag: {
type: Boolean,
default: false
}
},
setup(props, { attrs }) {
const icon = computed(() => normalizeIconArgs(props.icon))
const classes = computed(() => objectWithKey('classes', classList(props)))
const transform = computed(() => objectWithKey('transform', typeof props.transform === 'string' ? faParse.transform(props.transform) : props.transform))
const mask = computed(() => objectWithKey('mask', normalizeIconArgs(props.mask)))
const renderedIcon = computed(() => {
const iconProps = {
...classes.value,
...transform.value,
...mask.value,
symbol: props.symbol,
maskId: props.maskId
}
// the title attribute will only apply to versions prior to version 7.0.0
iconProps.title = props.title
iconProps.titleId = props.titleId
return faIcon(icon.value, iconProps)
})
watch(
renderedIcon,
(value) => {
if (!value) {
return log('Could not find one or more icon(s)', icon.value, mask.value)
}
},
{ immediate: true }
)
if (props.gradientFill && props.symbol) {
log('gradientFill is not supported when symbol is true and will be ignored')
}
const vnode = computed(() =>
renderedIcon.value ? convert(renderedIcon.value.abstract[0], { gradientFill: props.symbol ? null : props.gradientFill }, attrs) : null
)
return () => vnode.value
}
})
@@ -0,0 +1,21 @@
import { computed, defineComponent, h } from 'vue'
import { config } from '@fortawesome/fontawesome-svg-core'
export default defineComponent({
name: 'FontAwesomeLayers',
props: {
fixedWidth: {
type: Boolean,
default: false
}
},
setup(props, { slots }) {
const { familyPrefix } = config
const className = computed(() => [`${familyPrefix}-layers`, ...(props.fixedWidth ? [`${familyPrefix}-fw`] : [])])
return () => h('div', { class: className.value }, slots.default ? slots.default() : [])
}
})
@@ -0,0 +1,50 @@
import { config, parse, text } from '@fortawesome/fontawesome-svg-core'
import { defineComponent, computed } from 'vue'
import convert from '../converter'
import { objectWithKey } from '../utils'
export default defineComponent({
name: 'FontAwesomeLayersText',
props: {
value: {
type: [String, Number],
default: ''
},
transform: {
type: [String, Object],
default: null
},
counter: {
type: Boolean,
default: false
},
position: {
type: String,
default: null,
validator: (value) => ['bottom-left', 'bottom-right', 'top-left', 'top-right'].indexOf(value) > -1
}
},
setup(props, { attrs }) {
const { familyPrefix } = config
const classes = computed(() =>
objectWithKey('classes', [
...(props.counter ? [`${familyPrefix}-layers-counter`] : []),
...(props.position ? [`${familyPrefix}-layers-${props.position}`] : [])
])
)
const transform = computed(() => objectWithKey('transform', typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform))
const abstractElement = computed(() => {
const { abstract } = text(props.value.toString(), { ...transform.value, ...classes.value })
if (props.counter) {
abstract[0].attributes.class = abstract[0].attributes.class.replace('fa-layers-text', '')
}
return abstract[0]
})
const vnode = computed(() => convert(abstractElement.value, {}, attrs))
return () => vnode.value
}
})
+153
View File
@@ -0,0 +1,153 @@
import { h } from 'vue'
import humps from 'humps'
/**
* Converts a CSS style into a plain Javascript object.
* @param {String} style The style to converts into a plain Javascript object.
* @returns {Object}
*/
function styleToObject(style) {
return style
.split(';')
.map((s) => s.trim())
.filter((s) => s)
.reduce((output, pair) => {
const idx = pair.indexOf(':')
const prop = humps.camelize(pair.slice(0, idx))
const value = pair.slice(idx + 1).trim()
output[prop] = value
return output
}, {})
}
/**
* Converts a CSS class list into a plain Javascript object.
* @param {Array<String>} classes The class list to convert.
* @returns {Object}
*/
function classToObject(classes) {
return classes.split(/\s+/).reduce((output, className) => {
output[className] = true
return output
}, {})
}
/**
* Creates a Vue VNode for an SVG gradient stop element.
* @param {Object} stop The gradient stop definition.
* @param {string|number} stop.offset The offset of the stop.
* @param {string} stop.color The color of the stop.
* @param {number} [stop.opacity] The opacity of the stop.
* @param {number} index The index of the stop.
* @returns {VNode}
*/
function createGradientStop(stop, index) {
return h('stop', {
'key': `${index}-${stop.offset}`,
'offset': stop.offset,
'stop-color': stop.color,
...(stop.opacity !== undefined && { 'stop-opacity': stop.opacity })
})
}
/**
* Recursively removes the fill attribute from all path elements in an abstract element tree.
* @param {AbstractElement | String} abstractElement
* @returns {AbstractElement | String}
*/
function stripFillsFromPaths(abstractElement) {
if (typeof abstractElement === 'string') return abstractElement
const children = (abstractElement.children || []).map(stripFillsFromPaths)
if (abstractElement.tag === 'path' && abstractElement.attributes && 'fill' in abstractElement.attributes) {
return { ...abstractElement, attributes: { ...abstractElement.attributes, fill: undefined }, children }
}
return { ...abstractElement, children }
}
/**
* Converts a FontAwesome abstract element of an icon into a Vue VNode.
* @param {AbstractElement | String} abstractElement The element to convert.
* @param {Object} props Options including gradientFill and any extra VNode props.
* @param {Object} attrs The user-defined native HTML attributes.
* @returns {VNode}
*/
export default function convert(abstractElement, props = {}, attrs = {}) {
// If the abstract element is a string, we'll just return a string render function
if (typeof abstractElement === 'string') {
return abstractElement
}
const { gradientFill = null, ...renderProps } = props
// If a gradientFill (or fill attr) is provided, strip fill from all descendant path elements
// up front so the gradient/fill takes precedence over the icon's built-in fill
const shouldStripFills = !!gradientFill || 'fill' in attrs
const element = shouldStripFills ? stripFillsFromPaths(abstractElement) : abstractElement
// Converting abstract element children into Vue VNodes
const children = (element.children || []).map((child) => convert(child, {}, {}))
// Converting abstract element attributes into valid Vue format
const mixins = Object.keys(element.attributes || {}).reduce(
(mixins, key) => {
const value = element.attributes[key]
switch (key) {
case 'class':
mixins.class = classToObject(value)
break
case 'style':
mixins.style = styleToObject(value)
break
default:
mixins.attrs[key] = value
}
return mixins
},
{
attrs: {},
class: {},
style: {}
}
)
// Now, we'll return the VNode
const { class: _aClass = {}, style: aStyle = {}, ...otherAttrs } = attrs
// If a valid gradientFill is provided, inject the gradient element and set fill to reference it
if (gradientFill && gradientFill.id && (gradientFill.type === 'linear' || gradientFill.type === 'radial')) {
const { type: gradientType, stops = [], id, ...gradientProps } = gradientFill
const gradientTag = gradientType === 'linear' ? 'linearGradient' : 'radialGradient'
const gradientVNode = h(gradientTag, { ...gradientProps, id }, stops.map(createGradientStop))
return h(
element.tag,
{
...renderProps,
class: mixins.class,
style: { ...mixins.style, ...aStyle },
...mixins.attrs,
...otherAttrs,
fill: `url(#${id})`
},
[gradientVNode, ...children]
)
}
return h(
abstractElement.tag,
{
...renderProps,
class: mixins.class,
style: { ...mixins.style, ...aStyle },
...mixins.attrs,
...otherAttrs
},
children
)
}
+3
View File
@@ -0,0 +1,3 @@
export { default as FontAwesomeIcon } from './components/FontAwesomeIcon'
export { default as FontAwesomeLayers } from './components/FontAwesomeLayers'
export { default as FontAwesomeLayersText } from './components/FontAwesomeLayersText'
+11
View File
@@ -0,0 +1,11 @@
let PRODUCTION = false
try {
PRODUCTION = process.env.NODE_ENV === 'production'
} catch (e) { }
export default function (...args) {
if (!PRODUCTION && console && typeof console.error === 'function') {
console.error(...args)
}
}
+56
View File
@@ -0,0 +1,56 @@
export function objectWithKey(key, value) {
return (Array.isArray(value) && value.length > 0) || (!Array.isArray(value) && value) ? { [key]: value } : {}
}
export function classList(props) {
let classes = {
'fa-spin': props.spin,
'fa-pulse': props.pulse,
// the fixedWidth property has been deprecated as of version 7.0.0
'fa-fw': props.fixedWidth,
'fa-border': props.border,
'fa-li': props.listItem,
'fa-inverse': props.inverse,
'fa-flip': props.flip === true,
'fa-flip-horizontal': props.flip === 'horizontal' || props.flip === 'both',
'fa-flip-vertical': props.flip === 'vertical' || props.flip === 'both',
[`fa-${props.size}`]: props.size !== null,
[`fa-rotate-${props.rotation}`]: props.rotation !== null,
'fa-rotate-by': props.rotateBy,
[`fa-pull-${props.pull}`]: props.pull !== null,
'fa-swap-opacity': props.swapOpacity,
'fa-bounce': props.bounce,
'fa-shake': props.shake,
'fa-beat': props.beat,
'fa-fade': props.fade,
'fa-beat-fade': props.beatFade,
'fa-flash': props.flash,
'fa-spin-pulse': props.spinPulse,
'fa-spin-reverse': props.spinReverse,
// the widthAuto property is only supported in version 7.0.0 and later
'fa-width-auto': props.widthAuto,
// the canvasSquare and canvasRoomy properties are only supported in version 7.3.0 and later
'fa-canvas-square': props.canvasSquare,
'fa-canvas-roomy': props.canvasRoomy,
// the following animations are only supported in version 7.3.0 and later
'fa-flip-360': props.flip360,
'fa-buzz': props.buzz,
'fa-float': props.float,
'fa-jello': props.jello,
'fa-spin-snap': props.spinSnap,
'fa-spin-snap-4': props.spinSnap4,
'fa-spin-snap-8': props.spinSnap8,
'fa-swing': props.swing,
'fa-wag': props.wag
}
return Object.keys(classes)
.map((key) => (classes[key] ? key : null))
.filter((key) => key)
}
export function addStaticClass(to, what) {
const val = (to || '').length === 0 ? [] : [to]
return val.concat(what).join(' ')
}