UNPKG

element-ui-sticky-table

Version:
330 lines (301 loc) 9.68 kB
import { Table } from 'element-ui'; // const wait = (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; var script = { name: "sticky-table", inheritAttrs: true, props: { sticky: { default: false, type: Boolean, }, stickyOffsetTop: { default: 0, type: Number, }, }, components: { ElTable: Table, }, data() { return { oldScrollableParentNodes: [], tableEl: undefined, tableHeader: undefined, tableHeaderFixed: undefined, tableHeaderFixedRight: undefined, requestId: null, }; }, computed: { scrollableParentNodes() { const nodes = [document]; try { let parentNode = this.tableEl.parentElement; while (parentNode !== null) { if ( ["auto", "scroll", "visible"].includes( getComputedStyle(parentNode).overflowY ) ) { nodes.push(parentNode); } parentNode = parentNode.parentElement; } } catch (error) {} return nodes; }, }, watch: { scrollableParentNodes: { immediate: true, handler() { this.oldScrollableParentNodes.forEach((node) => { node.removeEventListener("scroll", this.adjust); node.removeEventListener("wheel", this.adjust); }); this.scrollableParentNodes.forEach((node) => { node.addEventListener("scroll", this.adjust); node.addEventListener("wheel", this.adjust); }); this.oldScrollableParentNodes = [...this.scrollableParentNodes]; }, }, tableEl() { const elTable = this.tableEl; const tableHeader = [...elTable.children].find((el) => el.classList.contains("el-table__header-wrapper") ); if (tableHeader !== undefined) { this.tableHeader = tableHeader; } const elTableFixed = [...elTable.children].find((el) => el.classList.contains("el-table__fixed") ); if (elTableFixed !== undefined) { this.tableHeaderFixed = [...elTableFixed.children].find((el) => el.classList.contains("el-table__fixed-header-wrapper") ); } const elTableFixedRight = [...elTable.children].find((el) => el.classList.contains("el-table__fixed-right") ); if (elTableFixedRight !== undefined) { this.tableHeaderFixedRight = [ ...elTableFixedRight.children, ].find((el) => el.classList.contains("el-table__fixed-header-wrapper")); } }, tableHeader() { if (this.tableHeader === undefined) return; this.tableHeader.style.position = "relative"; // default table zIndex = 1 this.tableHeader.style.zIndex = "2"; }, tableHeaderFixed() { if (this.tableHeaderFixed === undefined) return; this.tableHeaderFixed.style.position = "relative"; // fixed table zIndex = 3 this.tableHeaderFixed.style.zIndex = "4"; }, tableHeaderFixedRight() { if (this.tableHeaderFixedRight === undefined) return; this.tableHeaderFixedRight.style.position = "relative"; // fixed table zIndex = 3 this.tableHeaderFixedRight.style.zIndex = "4"; }, data: { immediate: true, handler() { this.adjust(); }, }, }, async mounted() { if (this.sticky === false) return; // wait for nested element rendering await this.$nextTick(); this.tableEl = this.$refs.table.$el; window.addEventListener("resize", this.adjust); window.addEventListener("sticky-table:expand", this.adjust); }, beforeDestroy() { this.scrollableParentNodes.forEach((node) => { node.removeEventListener("scroll", this.adjust); node.removeEventListener("wheel", this.adjust); }); window.removeEventListener("resize", this.adjust); window.removeEventListener("sticky-table:expand", this.adjust); }, methods: { handleExpand() { window.dispatchEvent(new Event("sticky-table:expand")); }, adjust() { cancelAnimationFrame(this.requestId); this.requestId = requestAnimationFrame(async () => { const top = this.$refs.table.$el.getBoundingClientRect().top - this.stickyOffsetTop; const finalTop = top >= 0 ? "0" : Math.abs(top) + "px"; [this.tableHeader, this.tableHeaderFixed, this.tableHeaderFixedRight] .filter((el) => el !== undefined) .forEach((el) => { el.style.top = finalTop; }); // TODO: figure out the problem // try to fix wrong offset is calculated on first render await wait(166); if (this.tableHeaderFixedRight === undefined) return; const parentWidth = getComputedStyle( this.tableHeaderFixedRight.parentElement ).width; const childWidth = getComputedStyle( [...this.tableHeaderFixedRight.children].find((el) => el.classList.contains("el-table__header") ) ).width; this.tableHeaderFixedRight.style.left = `calc(-${childWidth} + ${parentWidth})`; }); }, }, }; function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { if (typeof shadowMode !== 'boolean') { createInjectorSSR = createInjector; createInjector = shadowMode; shadowMode = false; } // Vue.extend constructor export interop. const options = typeof script === 'function' ? script.options : script; // render functions if (template && template.render) { options.render = template.render; options.staticRenderFns = template.staticRenderFns; options._compiled = true; // functional template if (isFunctionalTemplate) { options.functional = true; } } // scopedId if (scopeId) { options._scopeId = scopeId; } let hook; if (moduleIdentifier) { // server build hook = function (context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__; } // inject component styles if (style) { style.call(this, createInjectorSSR(context)); } // register component module identifier for async chunk inference if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier); } }; // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook; } else if (style) { hook = shadowMode ? function (context) { style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); } : function (context) { style.call(this, createInjector(context)); }; } if (hook) { if (options.functional) { // register for functional component in vue file const originalRender = options.render; options.render = function renderWithStyleInjection(h, context) { hook.call(context); return originalRender(h, context); }; } else { // inject component registration as beforeCreate hook const existing = options.beforeCreate; options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; } } return script; } /* script */ const __vue_script__ = script; /* template */ var __vue_render__ = function() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c( "el-table", _vm._g( _vm._b( { ref: "table", on: { "expand-change": _vm.handleExpand }, scopedSlots: _vm._u( [ _vm._l(_vm.$scopedSlots, function(_, slot) { return { key: slot, fn: function(scope) { return [_vm._t(slot, null, null, scope)] } } }) ], null, true ) }, "el-table", _vm.$attrs, false ), _vm.$listeners ) ) }; var __vue_staticRenderFns__ = []; __vue_render__._withStripped = true; /* style */ const __vue_inject_styles__ = undefined; /* scoped */ const __vue_scope_id__ = undefined; /* module identifier */ const __vue_module_identifier__ = undefined; /* functional template */ const __vue_is_functional_template__ = false; /* style inject */ /* style inject SSR */ /* style inject shadow dom */ const __vue_component__ = /*#__PURE__*/normalizeComponent( { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined ); export default __vue_component__;