uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
140 lines (114 loc) • 3.11 kB
JavaScript
import {
addClass,
attr,
css,
includes,
isTag,
memoize,
once,
removeAttr,
startsWith,
} from 'uikit-util';
import { mutation } from '../api/observables';
import Svg from '../mixin/svg';
import { getMaxPathLength } from '../util/svg';
export default {
mixins: [Svg],
args: 'src',
props: {
src: String,
icon: String,
attributes: 'list',
strokeAnimation: Boolean,
},
data: {
strokeAnimation: false,
},
observe: [
mutation({
async handler() {
const svg = await this.svg;
if (svg) {
applyAttributes.call(this, svg);
}
},
options: {
attributes: true,
attributeFilter: ['id', 'class', 'style'],
},
}),
],
async connected() {
if (includes(this.src, '#')) {
[this.src, this.icon] = this.src.split('#');
}
const svg = await this.svg;
if (svg) {
applyAttributes.call(this, svg);
if (this.strokeAnimation) {
applyAnimation(svg);
}
}
},
methods: {
async getSvg() {
if (isTag(this.$el, 'img') && !this.$el.complete && this.$el.loading === 'lazy') {
await new Promise((resolve) => once(this.$el, 'load', resolve));
}
return parseSVG(await loadSVG(this.src), this.icon) || Promise.reject('SVG not found.');
},
},
};
function applyAttributes(el) {
const { $el } = this;
addClass(el, attr($el, 'class'), 'uk-svg');
for (let i = 0; i < $el.style.length; i++) {
const prop = $el.style[i];
css(el, prop, css($el, prop));
}
for (const attribute in this.attributes) {
const [prop, value] = this.attributes[attribute].split(':', 2);
attr(el, prop, value);
}
if (!this.$el.id) {
removeAttr(el, 'id');
}
}
const loadSVG = memoize(async (src) => {
if (src) {
if (startsWith(src, 'data:')) {
return decodeURIComponent(src.split(',')[1]);
} else {
return (await fetch(src)).text();
}
} else {
return Promise.reject();
}
});
function parseSVG(svg, icon) {
if (icon && includes(svg, '<symbol')) {
svg = parseSymbols(svg)[icon] || svg;
}
return stringToSvg(svg);
}
const symbolRe = /<symbol([^]*?id=(['"])(.+?)\2[^]*?<\/)symbol>/g;
const parseSymbols = memoize(function (svg) {
const symbols = {};
symbolRe.lastIndex = 0;
let match;
while ((match = symbolRe.exec(svg))) {
symbols[match[3]] = `<svg ${match[1]}svg>`;
}
return symbols;
});
function applyAnimation(el) {
const length = getMaxPathLength(el);
if (length) {
css(el, '--uk-animation-stroke', length);
}
}
export function stringToSvg(string) {
const container = document.createElement('template');
container.innerHTML = string;
return container.content.firstElementChild;
}