UNPKG

@lcap/builder

Version:
416 lines (374 loc) 13.7 kB
/* eslint-disable no-cond-assign, no-return-assign, no-multi-assign, no-restricted-syntax, no-use-before-define, no-underscore-dangle */ export default function registerIElement(methods, options = {}) { // inspecting element 模式 let inspecting = false; // 当前组件的主要选择器 let componentNodePath = ''; let mainSelectorMap = {}; let rootElements = []; let mainSelectorStr = ''; // 当前组件的所有选择器 let selectors = []; const selected = { _element: null, _elementDOMPath: '', get element() { if (options.useDOMPath && !document.contains(this._element) && this._elementDOMPath) { console.log('[inspected element] 路径元素不存在,重新查找!', this._elementDOMPath); return (this._element = document.querySelector(this._elementDOMPath)); } return this._element; }, set element(value) { this._element && clearIElementState(this._element); selectedElementState = ''; this._element = value; if (options.useDOMPath) { this._elementDOMPath = computeElementDOMPath(value); } }, }; // 当前选中的元素状态 let selectedElementState = ''; // 当前选中的元素结果 let selectedElementResult = { matchedSelectors: [], has: { parent: false, prev: false, next: false, children: false, }, }; // 唯一审查的元素 let tempElement = null; // 审查器popover let INSPECTOR = null; options.postMessage = options.postMessage || ((payload) => window.top.postMessage(payload, '*')); /** * 初始化审查器 div */ function initInspector() { INSPECTOR = document.getElementById('ide-inspector'); if (INSPECTOR) return; INSPECTOR = document.createElement('div'); INSPECTOR.id = 'ide-inspector'; INSPECTOR.classList.add('ide-inspector'); if (!options.addPopoverManually) { INSPECTOR.innerHTML = `<div class="ide-inspector__popover"> <div class="ide-inspector__title"></div> <div class="ide-inspector__content"></div> </div>`; } document.body.appendChild(INSPECTOR); if (!options.addEventsManually) { window.removeEventListener('scroll', onScrollOrResize); window.removeEventListener('resize', onScrollOrResize); window.addEventListener('scroll', onScrollOrResize); window.addEventListener('resize', onScrollOrResize); } } initInspector(); /** * 计算审查器位置,并发送 iElementRect 信息 */ function computeInspector() { const el = tempElement; if (!el) { INSPECTOR.style.display = 'none'; return; } const rect = el.getBoundingClientRect(); const hoveredElementSelector = el.tagName.toLowerCase() + Array.from(el.classList) .filter((cls) => !/^cw-css-rule|^ide-custom-component|^_|vusion|s-empty|_fake|_empty|[dD]esigner|cw-style/.test(cls)) .map((cls) => `.${cls}`) .join(''); if (!options.addPopoverManually) { INSPECTOR.children[0].children[0].textContent = hoveredElementSelector; INSPECTOR.children[0].children[1].textContent = `${rect.width.toFixed(1)}px × ${rect.height.toFixed(1)}px`; if (rect.top < 80) { INSPECTOR.children[0].setAttribute('data-placement', 'bottom'); } else { INSPECTOR.children[0].setAttribute('data-placement', 'top'); } } Object.assign(INSPECTOR.style, { display: !options.addPopoverManually ? 'block' : 'none', top: `${rect.top}px`, left: `${rect.left}px`, width: `${rect.width}px`, height: `${rect.height}px`, }); const payload = { from: 'lcap-theme', type: 'iElementRect', data: { hoveredElementSelector, rect, }, }; options.postMessage(payload); } /** * 发送审查器 iElementResult 信息 */ function sendIElementResult() { // eslint-disable-next-line no-use-before-define selectedElementResult = getIElementResult(); const payload = { from: 'lcap-theme', type: 'iElementResult', data: selectedElementResult }; options.postMessage(payload); } function computeElementDOMPath(el) { if (!(el instanceof Element)) return ''; const path = []; while (el !== document) { let selector = el.tagName.toLowerCase(); if (el.getAttribute('data-nodepath') === componentNodePath) { selector += `[data-nodepath="${componentNodePath}"]`; path.unshift(selector); break; } else { let sib = el; let nth = 1; while (sib.previousElementSibling) { sib = sib.previousElementSibling; nth++; } selector += `:nth-child(${nth})`; } path.unshift(selector); el = el.parentNode; } return path.join('> '); } function onMouseMove(e) { if (!inspecting || !INSPECTOR) return; tempElement = e.target.closest(mainSelectorStr); if (!tempElement) { INSPECTOR.style.display = 'none'; } else { computeInspector(); } } function onClick(e) { if (!inspecting) return; selected.element = tempElement; methods.cancelIElement(); sendIElementResult(); e.stopPropagation(); } function onScrollOrResize(e) { computeInspector(); } function onRefresh() { const selectedElement = selected.element; if (selectedElement) { clearIElementState(selectedElement); const state = selectedElementState; state && selectedElement.classList.add(`_${state}`); } rootElements.forEach((el) => { el.setAttribute('data-root-nodepath', componentNodePath); }); } /** * 计算 nodepath 下的主选择器的 query 字符串 */ function computeMainSelectorStr() { if (!componentNodePath) return Object.keys(mainSelectorMap).join(','); let nodePathStr = `[data-nodepath="${componentNodePath}"]`; Array.from(document.querySelectorAll('[data-root-nodepath]')).forEach((el) => { el.removeAttribute('data-root-nodepath'); }); // findAndMarkRootElement const dataNodePathElements = Array.from(document.querySelectorAll(nodePathStr)); const rootSelectors = Object.keys(mainSelectorMap).filter((key) => mainSelectorMap[key]); rootElements = []; dataNodePathElements.forEach((el) => { rootSelectors.forEach((selector) => { const rootEl = el.closest(selector); if (rootEl && rootEl !== el) rootElements.push(rootEl); }); }); rootElements.forEach((el) => { el.setAttribute('data-root-nodepath', componentNodePath); }); if (rootElements.length) nodePathStr = `[data-root-nodepath="${componentNodePath}"]`; const output = []; Object.keys(mainSelectorMap).forEach((key) => { const value = mainSelectorMap[key]; output.push(`${nodePathStr}${value ? '' : ' '}${key}`); }); return output.join(','); } methods.inspectElement = (data) => { inspecting = true; componentNodePath = data.nodePath; mainSelectorMap = data.mainSelectorMap; mainSelectorStr = computeMainSelectorStr(); selectors = data.selectors; if (!options.addEventsManually) { window.addEventListener('mousemove', onMouseMove); window.addEventListener('click', onClick, true); } }; methods.cancelIElement = () => { inspecting = false; if (!options.addEventsManually) { window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('click', onClick, true); } }; methods.clearIElement = () => { inspecting = false; componentNodePath = ''; mainSelectorMap = {}; rootElements = []; mainSelectorStr = ''; selectors = []; selected.element = null; selectedElementState = ''; selectedElementResult = { matchedSelectors: [], has: { parent: false, prev: false, next: false, children: false, }, }; tempElement = null; computeInspector(); }; function getRelatedElement(el, relation) { if (!el) return undefined; if (relation === 'self') { return el; } if (relation === 'parent') { return el.parentElement.closest(mainSelectorStr); } if (relation === 'prev') { while (el = el.previousElementSibling) { if (el.matches(mainSelectorStr)) return el; } } else if (relation === 'next') { while (el = el.nextElementSibling) { if (el.matches(mainSelectorStr)) return el; } } else if (relation === 'children') { return el.querySelector(mainSelectorStr); } return undefined; } function getIElementResult() { const el = tempElement; let matchedSelectors = []; if (el) { if (selectedElementState) { const filterText = `._${selectedElementState}`; matchedSelectors = selectors.filter((selector) => selector.includes(filterText) && el.matches(selector)); } else { matchedSelectors = selectors.filter((selector) => !/\._hover|\._active|\._focus/g.test(selector) && el.matches(selector)); } } return { matchedSelectors, has: { parent: !!getRelatedElement(el, 'parent'), prev: !!getRelatedElement(el, 'prev'), next: !!getRelatedElement(el, 'next'), children: !!getRelatedElement(el, 'children'), }, }; } methods.hoverIElement = (relation) => { tempElement = getRelatedElement(selected.element, relation); computeInspector(); }; methods.switchIElement = (relation) => { selected.element = tempElement = getRelatedElement(selected.element, relation); computeInspector(); sendIElementResult(); }; function clearIElementState(el) { el && ['hover', 'active', 'focus'].forEach((_state) => el.classList.remove(`_${_state}`)); } methods.changeIElementState = (state) => { selectedElementState = state; const selectedElement = selected.element; if (selectedElement) { clearIElementState(selectedElement); state && selectedElement.classList.add(`_${state}`); } sendIElementResult(); }; methods.showInspector = () => { INSPECTOR.style.display = !options.addPopoverManually ? 'block' : 'none'; }; methods.hideInspector = () => { INSPECTOR.style.display = 'none'; }; return { get inspecting() { return inspecting; }, onMouseMove, onClick, onRefresh, }; } /* debug $('iframe').contentWindow.postMessage({ from: 'lcap', type: 'inspectElement', data: { selectors: [ '[class*=u-panel__]', '[class*=u-panel__][shadow=always]', '[class*=u-panel__][shadow=hover]:hover,[class*=u-panel__][shadow=hover]._hover', '[class*=u-panel__][shadow=always],[class*=u-panel__][shadow=hover]', '[class*=u-panel__][shadow=never]', '[class*=u-panel__][bordered]', '[class*=u-panel_head__]', '[class*=u-panel_title__]', '[class*=u-panel_extra__]', '[class*=u-panel_body__]', '[class*=u-panel_title__] [s-empty]', '[class*=u-panel_head__][flex]', '[class*=u-panel_head__][flex] [class*=u-panel_extra__]', '[class*=u-panel_group__]:not(:last-child)', '[class*=u-panel_group_head__]', '[class*=u-panel_group_body__]', '[class*=u-panel__]:hover,[class*=u-panel__]._hover', '[class*=u-panel__]:active,[class*=u-panel__]._active', '[class*=u-panel__]:focus,[class*=u-panel__]._focus', '[class*=u-panel_head__]:hover,[class*=u-panel_head__]._hover', '[class*=u-panel_head__]:active,[class*=u-panel_head__]._active', '[class*=u-panel_head__]:focus,[class*=u-panel_head__]._focus', '[class*=u-panel_title__]:hover,[class*=u-panel_title__]._hover', '[class*=u-panel_title__]:active,[class*=u-panel_title__]._active', '[class*=u-panel_title__]:focus,[class*=u-panel_title__]._focus', '[class*=u-panel_extra__]:hover,[class*=u-panel_extra__]._hover', '[class*=u-panel_extra__]:active,[class*=u-panel_extra__]._active', '[class*=u-panel_extra__]:focus,[class*=u-panel_extra__]._focus', '[class*=u-panel_body__]:hover,[class*=u-panel_body__]._hover', '[class*=u-panel_body__]:active,[class*=u-panel_body__]._active', '[class*=u-panel_body__]:focus,[class*=u-panel_body__]._focus', '[class*=u-panel_title__] [s-empty]:hover,[class*=u-panel_title__] [s-empty]._hover', '[class*=u-panel_title__] [s-empty]:active,[class*=u-panel_title__] [s-empty]._active', '[class*=u-panel_title__] [s-empty]:focus,[class*=u-panel_title__] [s-empty]._focus', '[class*=u-panel_group__]', '[class*=u-panel_group__]:hover,[class*=u-panel_group__]._hover', '[class*=u-panel_group__]:active,[class*=u-panel_group__]._active', '[class*=u-panel_group__]:focus,[class*=u-panel_group__]._focus', '[class*=u-panel_group_head__]:hover,[class*=u-panel_group_head__]._hover', '[class*=u-panel_group_head__]:active,[class*=u-panel_group_head__]._active', '[class*=u-panel_group_head__]:focus,[class*=u-panel_group_head__]._focus', '[class*=u-panel_group_body__]:hover,[class*=u-panel_group_body__]._hover', '[class*=u-panel_group_body__]:active,[class*=u-panel_group_body__]._active', '[class*=u-panel_group_body__]:focus,[class*=u-panel_group_body__]._focus'], mainSelectors: [ '[class*=u-panel__]', '[class*=u-panel_head__]', '[class*=u-panel_title__]', '[class*=u-panel_extra__]', '[class*=u-panel_body__]', '[class*=u-panel_title__] [s-empty]', '[class*=u-panel_head__] [class*=u-panel_extra__]', '[class*=u-panel_group__]', '[class*=u-panel_group_head__]', '[class*=u-panel_group_body__]', ], } }, '*'); $('iframe').contentWindow.postMessage({ from: 'lcap', type: 'switchIElement', data: 'next' }, '*'); */