vue-svg-inline-component
Version:
Vue component for inline use of SVG files.
153 lines (122 loc) • 4.12 kB
JavaScript
import { defineComponent, toRefs, ref, computed, watch, onMounted, nextTick, h as render } from "vue";
// import PACKAGE_JSON from "../package.json";
// const PACKAGE_NAME = PACKAGE_JSON.name;
// const LIBRARY_NAME = PACKAGE_NAME.split("-").map(character => character.charAt(0).toUpperCase() + character.slice(1).toLowerCase()).join("");
const PACKAGE_NAME = "vue-svg-inline-component";
const LIBRARY_NAME = "VueSvgInlineComponent";
const WINDOW_CACHE_ID = `${LIBRARY_NAME}Cache`;
const WINDOW_VUE_VERSION = window?.Vue?.version;
const EVENTS = [ "update", "error" ];
const DEFAULT_ATTRIBUTES = { class: PACKAGE_NAME };
const REGEXP_SVG_FILENAME = /\.svg(?:[?#].*)?$/i;
const validateSvgFilename = filename => typeof filename === "string" && filename !== "" ? REGEXP_SVG_FILENAME.test(filename) : true;
window[WINDOW_CACHE_ID] = window[WINDOW_CACHE_ID] || new Map;
export default WINDOW_VUE_VERSION && !WINDOW_VUE_VERSION.startsWith("3.") ? console.error(`[${PACKAGE_NAME}] Only Vue@3 is supported! [Vue.version=${WINDOW_VUE_VERSION}]`) : defineComponent({ // eslint-disable-line no-console
name: PACKAGE_NAME,
props: {
source: {
type: String,
required: true,
default: null,
validator: validateSvgFilename
},
tag: {
type: String,
required: false,
default: "div"
},
attributes: {
type: Object,
required: false,
default: DEFAULT_ATTRIBUTES
},
cache: {
type: Boolean,
required: false,
default: true
},
fetchOptions: {
type: Object,
required: false,
default: null
},
transformFunction: {
type: Function,
required: false,
default: null
},
transformFunctionOptions: {
// type: any,
required: false,
default: null
},
emitUpdates: {
type: Boolean,
required: false,
default: false
},
emitErrors: {
type: Boolean,
required: false,
default: false
},
logErrors: {
type: Boolean,
required: false,
default: true
}
},
emits: EVENTS,
setup(props, { emit }) {
const {
source,
cache: isCacheEnabled,
fetchOptions,
transformFunction,
transformFunctionOptions,
emitUpdates: canEmitUpdate,
emitErrors: canEmitError,
logErrors: canLogError
} = toRefs(props);
const canUseCache = computed(() => isCacheEnabled.value && !fetchOptions.value);
const svg = ref();
const emitError = error => {
if(canEmitError.value) emit(EVENTS[1], error);
if(canLogError.value) console.error(`[${PACKAGE_NAME}] ${error}`); // eslint-disable-line no-console
};
const emitUpdate = () => {
if(canEmitUpdate.value) nextTick()
.then(() => emit(EVENTS[0], svg.value))
.catch(emitError);
};
const fetchSvg = () => {
if(!source.value) return svg.value = null;
(canUseCache.value && window[WINDOW_CACHE_ID].has(source.value) ? Promise.resolve(window[WINDOW_CACHE_ID].get(source.value)) : window.fetch(source.value, fetchOptions.value)
.then(response => {
if(response.status < 200 || response.status >= 400) throw new Error(`Wrong HTTP response status code! [response.status=${response.status}]`);
return response.text();
}))
.then(response => {
if(canUseCache.value && !window[WINDOW_CACHE_ID].has(source.value)) window[WINDOW_CACHE_ID].set(source.value, response);
svg.value = transformFunction.value ? transformFunction.value.call(null, response, transformFunctionOptions.value, props) : response;
})
.catch(emitError);
};
onMounted(() => {
fetchSvg();
watch([ source, fetchOptions, transformFunction, transformFunctionOptions ], fetchSvg);
watch(svg, emitUpdate);
});
return { svg };
},
render() {
const { tag, attributes, svg } = this;
if(!svg) return;
if(!tag) {
const { tagName: tag, attributes, innerHTML } = (new DOMParser()).parseFromString(svg, "text/xml").getElementsByTagName("svg")[0];
return render(tag, Object.assign({}, Array.from(attributes).reduce((accumulator, { name, value }) => Object.assign(accumulator, { [name]: value }), {}), { innerHTML }));
}
return render(tag, Object.assign({}, attributes, { innerHTML: svg }));
}
});
;