122 lines
4.0 KiB
JavaScript
122 lines
4.0 KiB
JavaScript
import canUseDom from '../../_util/canUseDom';
|
|
import contains from './contains';
|
|
const APPEND_ORDER = 'data-vc-order';
|
|
const MARK_KEY = `vc-util-key`;
|
|
const containerCache = new Map();
|
|
function getMark() {
|
|
let {
|
|
mark
|
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
if (mark) {
|
|
return mark.startsWith('data-') ? mark : `data-${mark}`;
|
|
}
|
|
return MARK_KEY;
|
|
}
|
|
function getContainer(option) {
|
|
if (option.attachTo) {
|
|
return option.attachTo;
|
|
}
|
|
const head = document.querySelector('head');
|
|
return head || document.body;
|
|
}
|
|
function getOrder(prepend) {
|
|
if (prepend === 'queue') {
|
|
return 'prependQueue';
|
|
}
|
|
return prepend ? 'prepend' : 'append';
|
|
}
|
|
/**
|
|
* Find style which inject by rc-util
|
|
*/
|
|
function findStyles(container) {
|
|
return Array.from((containerCache.get(container) || container).children).filter(node => node.tagName === 'STYLE');
|
|
}
|
|
export function injectCSS(css) {
|
|
let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
if (!canUseDom()) {
|
|
return null;
|
|
}
|
|
const {
|
|
csp,
|
|
prepend
|
|
} = option;
|
|
const styleNode = document.createElement('style');
|
|
styleNode.setAttribute(APPEND_ORDER, getOrder(prepend));
|
|
if (csp === null || csp === void 0 ? void 0 : csp.nonce) {
|
|
styleNode.nonce = csp === null || csp === void 0 ? void 0 : csp.nonce;
|
|
}
|
|
styleNode.innerHTML = css;
|
|
const container = getContainer(option);
|
|
const {
|
|
firstChild
|
|
} = container;
|
|
if (prepend) {
|
|
// If is queue `prepend`, it will prepend first style and then append rest style
|
|
if (prepend === 'queue') {
|
|
const existStyle = findStyles(container).filter(node => ['prepend', 'prependQueue'].includes(node.getAttribute(APPEND_ORDER)));
|
|
if (existStyle.length) {
|
|
container.insertBefore(styleNode, existStyle[existStyle.length - 1].nextSibling);
|
|
return styleNode;
|
|
}
|
|
}
|
|
// Use `insertBefore` as `prepend`
|
|
container.insertBefore(styleNode, firstChild);
|
|
} else {
|
|
container.appendChild(styleNode);
|
|
}
|
|
return styleNode;
|
|
}
|
|
function findExistNode(key) {
|
|
let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
const container = getContainer(option);
|
|
return findStyles(container).find(node => node.getAttribute(getMark(option)) === key);
|
|
}
|
|
export function removeCSS(key) {
|
|
let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
const existNode = findExistNode(key, option);
|
|
if (existNode) {
|
|
const container = getContainer(option);
|
|
container.removeChild(existNode);
|
|
}
|
|
}
|
|
/**
|
|
* qiankun will inject `appendChild` to insert into other
|
|
*/
|
|
function syncRealContainer(container, option) {
|
|
const cachedRealContainer = containerCache.get(container);
|
|
// Find real container when not cached or cached container removed
|
|
if (!cachedRealContainer || !contains(document, cachedRealContainer)) {
|
|
const placeholderStyle = injectCSS('', option);
|
|
const {
|
|
parentNode
|
|
} = placeholderStyle;
|
|
containerCache.set(container, parentNode);
|
|
container.removeChild(placeholderStyle);
|
|
}
|
|
}
|
|
/**
|
|
* manually clear container cache to avoid global cache in unit testes
|
|
*/
|
|
export function clearContainerCache() {
|
|
containerCache.clear();
|
|
}
|
|
export function updateCSS(css, key) {
|
|
let option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
var _a, _b, _c;
|
|
const container = getContainer(option);
|
|
// Sync real parent
|
|
syncRealContainer(container, option);
|
|
const existNode = findExistNode(key, option);
|
|
if (existNode) {
|
|
if (((_a = option.csp) === null || _a === void 0 ? void 0 : _a.nonce) && existNode.nonce !== ((_b = option.csp) === null || _b === void 0 ? void 0 : _b.nonce)) {
|
|
existNode.nonce = (_c = option.csp) === null || _c === void 0 ? void 0 : _c.nonce;
|
|
}
|
|
if (existNode.innerHTML !== css) {
|
|
existNode.innerHTML = css;
|
|
}
|
|
return existNode;
|
|
}
|
|
const newNode = injectCSS(css, option);
|
|
newNode.setAttribute(getMark(option), key);
|
|
return newNode;
|
|
} |