Merge pull request #1982 from QingWei-Li/feat/ssr

SSR feature
pull/2002/head
baiyaaaaa 2016-12-26 23:31:10 +08:00 committed by GitHub
commit 769ab5aa8d
49 changed files with 737 additions and 76 deletions

View File

@ -42,7 +42,7 @@ if [ "$TRAVIS_TAG" ]; then
export SUB_FOLDER=$(echo "$TRAVIS_TAG" | grep -o -E "\d+\.\d+") export SUB_FOLDER=$(echo "$TRAVIS_TAG" | grep -o -E "\d+\.\d+")
echo $SUB_FOLDER echo $SUB_FOLDER
SUB_FOLDER='1.0' SUB_FOLDER='1.1'
mkdir $SUB_FOLDER mkdir $SUB_FOLDER
rm -rf *.js *.css *.map static rm -rf *.js *.css *.map static
rm -rf $SUB_FOLDER/** rm -rf $SUB_FOLDER/**

View File

@ -1,5 +1,5 @@
<script> <script>
import { addClass } from 'wind-dom/src/class'; import { addClass } from 'element-ui/src/utils/dom';
export default { export default {
data() { data() {
return { return {
@ -78,7 +78,7 @@ Different colors represent different meanings.
``` ```
::: :::
### Icon Button ### Icon Button
Use icons to add more meaning to Button. You can use icon alone to save some space, or with text together. Use icons to add more meaning to Button. You can use icon alone to save some space, or with text together.

View File

@ -1,4 +1,4 @@
## Pagination ## Pagination
If you have too much data to display in one page, use pagination. If you have too much data to display in one page, use pagination.
@ -117,7 +117,7 @@ Add more modules based on your scenario.
``` ```
::: :::
<script> <script>
import { addClass } from 'wind-dom/src/class'; import { addClass } from 'element-ui/src/utils/dom';
export default { export default {
data() { data() {
return { return {

View File

@ -1,5 +1,5 @@
<script> <script>
import { addClass } from 'wind-dom/src/class'; import { addClass } from 'element-ui/src/utils/dom';
export default { export default {
data() { data() {
return { return {

View File

@ -117,7 +117,7 @@
``` ```
::: :::
<script> <script>
import { addClass } from 'wind-dom/src/class'; import { addClass } from 'element-ui/src/utils/dom';
export default { export default {
methods: { methods: {
handleSizeChange(val) { handleSizeChange(val) {

View File

@ -47,9 +47,7 @@
"async-validator": "^1.6.6", "async-validator": "^1.6.6",
"babel-helper-vue-jsx-merge-props": "^2.0.0", "babel-helper-vue-jsx-merge-props": "^2.0.0",
"deepmerge": "^1.2.0", "deepmerge": "^1.2.0",
"throttle-debounce": "^1.0.1", "throttle-debounce": "^1.0.1"
"vue-popup": "^0.2.14",
"wind-dom": "0.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^2.1.6" "vue": "^2.1.6"

View File

@ -12,6 +12,5 @@
"author": "long.zhang@ele.me", "author": "long.zhang@ele.me",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -32,7 +32,7 @@
<script> <script>
import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, DAY_DURATION } from '../util'; import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, DAY_DURATION } from '../util';
import { hasClass } from 'wind-dom/src/class'; import { hasClass } from 'element-ui/src/utils/dom';
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
const clearHours = function(time) { const clearHours = function(time) {

View File

@ -49,7 +49,7 @@
<script type="text/babel"> <script type="text/babel">
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
import { hasClass } from 'wind-dom/src/class'; import { hasClass } from 'element-ui/src/utils/dom';
export default { export default {
props: { props: {

View File

@ -44,7 +44,7 @@
</template> </template>
<script type="text/babel"> <script type="text/babel">
import { hasClass } from 'wind-dom/src/class'; import { hasClass } from 'element-ui/src/utils/dom';
export default { export default {
props: { props: {

View File

@ -375,6 +375,7 @@ export default {
}, },
showPicker() { showPicker() {
if (this.$isServer) return;
if (!this.picker) { if (!this.picker) {
this.panel.defaultValue = this.internalValue; this.panel.defaultValue = this.internalValue;
this.picker = new Vue(this.panel).$mount(document.createElement('div')); this.picker = new Vue(this.panel).$mount(document.createElement('div'));

View File

@ -22,7 +22,7 @@
</template> </template>
<script> <script>
import Popup from 'vue-popup'; import Popup from 'element-ui/src/utils/popup';
export default { export default {
name: 'el-dialog', name: 'el-dialog',

View File

@ -13,6 +13,5 @@
"license": "MIT", "license": "MIT",
"repository": "https://github.com/ElemeFE/element/tree/master/packages/input-number", "repository": "https://github.com/ElemeFE/element/tree/master/packages/input-number",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -22,7 +22,7 @@
</template> </template>
<template slot="append" v-if="$slots.append"> <template slot="append" v-if="$slots.append">
<slot name="append"></slot> <slot name="append"></slot>
</template> </template>
</el-input> </el-input>
<span <span
v-if="controls" v-if="controls"
@ -46,7 +46,7 @@
</template> </template>
<script> <script>
import ElInput from 'element-ui/packages/input'; import ElInput from 'element-ui/packages/input';
import { once, on } from 'wind-dom/src/event'; import { once, on } from 'element-ui/src/utils/dom';
export default { export default {
name: 'ElInputNumber', name: 'ElInputNumber',
@ -79,11 +79,8 @@
let interval = null; let interval = null;
let startTime; let startTime;
const handler = () => { const handler = () => vnode.context[binding.expression]();
vnode.context[binding.expression](); const clear = () => {
};
const clear = function() {
if (new Date() - startTime < 100) { if (new Date() - startTime < 100) {
handler(); handler();
} }
@ -91,12 +88,10 @@
interval = null; interval = null;
}; };
on(el, 'mousedown', function() { on(el, 'mousedown', () => {
startTime = new Date(); startTime = new Date();
once(document, 'mouseup', clear); once(document, 'mouseup', clear);
interval = setInterval(function() { interval = setInterval(handler, 100);
handler();
}, 100);
}); });
} }
} }

View File

@ -117,10 +117,9 @@
this.$refs.input.select(); this.$refs.input.select();
}, },
resizeTextarea() { resizeTextarea() {
if (this.$isServer) return;
var { autosize, type } = this; var { autosize, type } = this;
if (!autosize || type !== 'textarea') { if (!autosize || type !== 'textarea') return;
return;
}
const minRows = autosize.minRows; const minRows = autosize.minRows;
const maxRows = autosize.maxRows; const maxRows = autosize.maxRows;

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -1,8 +1,9 @@
import Vue from 'vue'; import Vue from 'vue';
import { addClass, removeClass } from 'wind-dom/src/class'; import { addClass, removeClass } from 'element-ui/src/utils/dom';
let Mask = Vue.extend(require('./loading.vue')); let Mask = Vue.extend(require('./loading.vue'));
exports.install = Vue => { exports.install = Vue => {
if (Vue.prototype.$isServer) return;
let toggleLoading = (el, binding) => { let toggleLoading = (el, binding) => {
if (binding.value) { if (binding.value) {
Vue.nextTick(() => { Vue.nextTick(() => {

View File

@ -61,6 +61,7 @@ const addStyle = (options, parent, instance) => {
}; };
const Loading = (options = {}) => { const Loading = (options = {}) => {
if (Vue.prototype.$isServer) return;
options = merge({}, defaults, options); options = merge({}, defaults, options);
if (typeof options.target === 'string') { if (typeof options.target === 'string') {
options.target = document.querySelector(options.target); options.target = document.querySelector(options.target);

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -102,6 +102,7 @@ const showNextMsg = () => {
}; };
const MessageBox = function(options, callback) { const MessageBox = function(options, callback) {
if (Vue.prototype.$isServer) return;
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {
message: options message: options

View File

@ -24,11 +24,11 @@
</template> </template>
<script type="text/babel"> <script type="text/babel">
import Popup from 'vue-popup'; import Popup from 'element-ui/src/utils/popup';
import Locale from 'element-ui/src/mixins/locale'; import Locale from 'element-ui/src/mixins/locale';
import ElInput from 'element-ui/packages/input'; import ElInput from 'element-ui/packages/input';
import ElButton from 'element-ui/packages/button'; import ElButton from 'element-ui/packages/button';
import { addClass, removeClass } from 'wind-dom/src/class'; import { addClass, removeClass } from 'element-ui/src/utils/dom';
import { t } from 'element-ui/src/locale'; import { t } from 'element-ui/src/locale';
let typeMap = { let typeMap = {

View File

@ -1,5 +1,5 @@
import Vue from 'vue'; import Vue from 'vue';
import { PopupManager } from 'vue-popup'; import { PopupManager } from 'element-ui/src/utils/popup';
let MessageConstructor = Vue.extend(require('./main.vue')); let MessageConstructor = Vue.extend(require('./main.vue'));
let instance; let instance;
@ -7,6 +7,7 @@ let instances = [];
let seed = 1; let seed = 1;
var Message = function(options) { var Message = function(options) {
if (Vue.prototype.$isServer) return;
options = options || {}; options = options || {};
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {

View File

@ -1,5 +1,5 @@
import Vue from 'vue'; import Vue from 'vue';
import { PopupManager } from 'vue-popup'; import { PopupManager } from 'element-ui/src/utils/popup';
let NotificationConstructor = Vue.extend(require('./main.vue')); let NotificationConstructor = Vue.extend(require('./main.vue'));
let instance; let instance;
@ -7,6 +7,7 @@ let instances = [];
let seed = 1; let seed = 1;
var Notification = function(options) { var Notification = function(options) {
if (Vue.prototype.$isServer) return;
options = options || {}; options = options || {};
let userOnClose = options.onClose; let userOnClose = options.onClose;
let id = 'notification_' + seed++; let id = 'notification_' + seed++;

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -17,7 +17,7 @@
<script> <script>
import Popper from 'element-ui/src/utils/vue-popper'; import Popper from 'element-ui/src/utils/vue-popper';
import { on, off } from 'wind-dom/src/event'; import { on, off } from 'element-ui/src/utils/dom';
export default { export default {
name: 'el-popover', name: 'el-popover',

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -24,7 +24,7 @@
</template> </template>
<script type="text/babel"> <script type="text/babel">
import { hasClass } from 'wind-dom/src/class'; import { hasClass } from 'element-ui/src/utils/dom';
export default { export default {
name: 'el-rate', name: 'el-rate',

View File

@ -12,7 +12,6 @@
"license": "MIT", "license": "MIT",
"repository": "https://github.com/ElemeFE/element/tree/master/packages/select", "repository": "https://github.com/ElemeFE/element/tree/master/packages/select",
"devDependencies": { "devDependencies": {
"throttle-debounce": "^1.0.1", "throttle-debounce": "^1.0.1"
"wind-dom": "0.0.3"
} }
} }

View File

@ -94,7 +94,7 @@
import ElScrollbar from 'element-ui/packages/scrollbar'; import ElScrollbar from 'element-ui/packages/scrollbar';
import debounce from 'throttle-debounce/debounce'; import debounce from 'throttle-debounce/debounce';
import Clickoutside from 'element-ui/src/utils/clickoutside'; import Clickoutside from 'element-ui/src/utils/clickoutside';
import { addClass, removeClass, hasClass } from 'wind-dom/src/class'; import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'; import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
import { t } from 'element-ui/src/locale'; import { t } from 'element-ui/src/locale';
const sizeMap = { const sizeMap = {
@ -303,6 +303,7 @@
}, },
options(val) { options(val) {
if (this.$isServer) return;
this.optionsAllDisabled = val.length === val.filter(item => item.disabled === true).length; this.optionsAllDisabled = val.length === val.filter(item => item.disabled === true).length;
if (this.multiple) { if (this.multiple) {
this.resetInputHeight(); this.resetInputHeight();

View File

@ -12,6 +12,5 @@
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"wind-dom": "0.0.3"
} }
} }

View File

@ -37,7 +37,7 @@
<script type="text/babel"> <script type="text/babel">
import ElInputNumber from 'element-ui/packages/input-number'; import ElInputNumber from 'element-ui/packages/input-number';
import ElTooltip from 'element-ui/packages/tooltip'; import ElTooltip from 'element-ui/packages/tooltip';
import { getStyle } from 'wind-dom/src/style'; import { getStyle } from 'element-ui/src/utils/dom';
export default { export default {
name: 'ElSlider', name: 'ElSlider',

View File

@ -1,6 +1,7 @@
import Vue from 'vue';
var dropdowns = []; var dropdowns = [];
document.addEventListener('click', function(event) { !Vue.prototype.$isServer && document.addEventListener('click', function(event) {
dropdowns.forEach(function(dropdown) { dropdowns.forEach(function(dropdown) {
var target = event.target; var target = event.target;
if (!dropdown || !dropdown.$el) return; if (!dropdown || !dropdown.$el) return;

View File

@ -226,7 +226,7 @@ export default {
filterPanel.table = table; filterPanel.table = table;
filterPanel.cell = cell; filterPanel.cell = cell;
filterPanel.column = column; filterPanel.column = column;
filterPanel.$mount(document.createElement('div')); !this.$isServer && filterPanel.$mount(document.createElement('div'));
} }
setTimeout(() => { setTimeout(() => {
@ -239,6 +239,7 @@ export default {
}, },
handleMouseDown(event, column) { handleMouseDown(event, column) {
if (this.$isServer) return;
if (column.children && column.children.length > 0) return; if (column.children && column.children.length > 0) return;
/* istanbul ignore if */ /* istanbul ignore if */
if (this.draggingColumn && this.border) { if (this.draggingColumn && this.border) {
@ -329,6 +330,7 @@ export default {
}, },
handleMouseOut() { handleMouseOut() {
if (this.$isServer) return;
document.body.style.cursor = ''; document.body.style.cursor = '';
}, },

View File

@ -74,7 +74,7 @@ export const getColumnByCell = function(table, cell) {
return null; return null;
}; };
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
export const mousewheel = function(element, callback) { export const mousewheel = function(element, callback) {
if (element && element.addEventListener) { if (element && element.addEventListener) {

View File

@ -31,7 +31,5 @@
"gulp-postcss": "^6.1.1", "gulp-postcss": "^6.1.1",
"postcss-salad": "^1.0.5" "postcss-salad": "^1.0.5"
}, },
"dependencies": { "dependencies": {}
"vue-popup": "^0.2.9"
}
} }

View File

@ -0,0 +1,33 @@
.v-modal-enter {
animation: v-modal-in .2s ease;
}
.v-modal-leave {
animation: v-modal-out .2s ease forwards;
}
@keyframes v-modal-in {
0% {
opacity: 0;
}
100% {
}
}
@keyframes v-modal-out {
0% {
}
100% {
opacity: 0;
}
}
.v-modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.5;
background: #000;
}

View File

@ -1,6 +1,6 @@
@charset "UTF-8"; @charset "UTF-8";
@import "./common/var.css"; @import "./common/var.css";
@import "vue-popup/lib/popup.css"; @import "./common/popup.css";
@component-namespace el { @component-namespace el {

View File

@ -1,8 +1,8 @@
@charset "UTF-8"; @charset "UTF-8";
@import "./common/var.css"; @import "./common/var.css";
@import "./common/popup.css";
@import "./button.css"; @import "./button.css";
@import "./input.css"; @import "./input.css";
@import "vue-popup/lib/popup.css";
@component-namespace el { @component-namespace el {

View File

@ -113,7 +113,7 @@ export default {
}, },
mounted() { mounted() {
window.addEventListener('message', (event) => { !this.$isServer && window.addEventListener('message', (event) => {
var targetOrigin = new URL(this.action).origin; var targetOrigin = new URL(this.action).origin;
if (event.origin !== targetOrigin) { if (event.origin !== targetOrigin) {
return false; return false;
@ -158,7 +158,7 @@ export default {
on-change={this.handleChange} on-change={this.handleChange}
accept={this.accept}> accept={this.accept}>
</input> </input>
<input type="hidden" name="documentDomain" value={document.domain} /> <input type="hidden" name="documentDomain" value={ this.$isServer ? '' : document.domain } />
<span ref="data"></span> <span ref="data"></span>
</form> </form>
{!this.showCover ? this.$slots.default : cover} {!this.showCover ? this.$slots.default : cover}

View File

@ -209,7 +209,7 @@ export default {
ref: 'upload-inner' ref: 'upload-inner'
}; };
var uploadComponent = typeof FormData !== 'undefined' var uploadComponent = this.$isServer ? '' : typeof FormData !== 'undefined'
? <upload {...props}>{this.$slots.default}</upload> ? <upload {...props}>{this.$slots.default}</upload>
: <iframeUpload {...props}>{this.$slots.default}</iframeUpload>; : <iframeUpload {...props}>{this.$slots.default}</iframeUpload>;

View File

@ -1,9 +1,10 @@
import { on } from 'wind-dom/src/event'; import Vue from 'vue';
import { on } from 'element-ui/src/utils/dom';
const nodeList = []; const nodeList = [];
const ctx = '@@clickoutsideContext'; const ctx = '@@clickoutsideContext';
on(document, 'click', e => { !Vue.prototype.$isServer && on(document, 'click', e => {
nodeList.forEach(node => node[ctx].documentHandler(e)); nodeList.forEach(node => node[ctx].documentHandler(e));
}); });
/** /**

178
src/utils/dom.js Normal file
View File

@ -0,0 +1,178 @@
/* istanbul ignore next */
import Vue from 'vue';
const isServer = Vue.prototype.$isServer;
const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
const MOZ_HACK_REGEXP = /^moz([A-Z])/;
const ieVersion = isServer ? 0 : Number(document.documentMode);
/* istanbul ignore next */
const trim = function(string) {
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
};
/* istanbul ignore next */
const camelCase = function(name) {
return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
}).replace(MOZ_HACK_REGEXP, 'Moz$1');
};
/* istanbul ignore next */
export const on = (function() {
if (!isServer && document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
/* istanbul ignore next */
export const off = (function() {
if (!isServer && document.removeEventListener) {
return function(element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event) {
element.detachEvent('on' + event, handler);
}
};
}
})();
/* istanbul ignore next */
export const once = function(el, event, fn) {
var listener = function() {
if (fn) {
fn.apply(this, arguments);
}
off(el, event, listener);
};
on(el, event, listener);
};
/* istanbul ignore next */
export function hasClass(el, cls) {
if (!el || !cls) return false;
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
if (el.classList) {
return el.classList.contains(cls);
} else {
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
};
/* istanbul ignore next */
export function addClass(el, cls) {
if (!el) return;
var curClass = el.className;
var classes = (cls || '').split(' ');
for (var i = 0, j = classes.length; i < j; i++) {
var clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.add(clsName);
} else {
if (!hasClass(el, clsName)) {
curClass += ' ' + clsName;
}
}
}
if (!el.classList) {
el.className = curClass;
}
};
/* istanbul ignore next */
export function removeClass(el, cls) {
if (!el || !cls) return;
var classes = cls.split(' ');
var curClass = ' ' + el.className + ' ';
for (var i = 0, j = classes.length; i < j; i++) {
var clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.remove(clsName);
} else {
if (hasClass(el, clsName)) {
curClass = curClass.replace(' ' + clsName + ' ', ' ');
}
}
}
if (!el.classList) {
el.className = trim(curClass);
}
};
/* istanbul ignore next */
export const getStyle = ieVersion < 9 ? function(element, styleName) {
if (isServer) return;
if (!element || !styleName) return null;
styleName = camelCase(styleName);
if (styleName === 'float') {
styleName = 'styleFloat';
}
try {
switch (styleName) {
case 'opacity':
try {
return element.filters.item('alpha').opacity / 100;
} catch (e) {
return 1.0;
}
default:
return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null);
}
} catch (e) {
return element.style[styleName];
}
} : function(element, styleName) {
if (isServer) return;
if (!element || !styleName) return null;
styleName = camelCase(styleName);
if (styleName === 'float') {
styleName = 'cssFloat';
}
try {
var computed = document.defaultView.getComputedStyle(element, '');
return element.style[styleName] || computed ? computed[styleName] : null;
} catch (e) {
return element.style[styleName];
}
};
/* istanbul ignore next */
export function setStyle(element, styleName, value) {
if (!element || !styleName) return;
if (typeof styleName === 'object') {
for (var prop in styleName) {
if (styleName.hasOwnProperty(prop)) {
setStyle(element, prop, styleName[prop]);
}
}
} else {
styleName = camelCase(styleName);
if (styleName === 'opacity' && ieVersion < 9) {
element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')';
} else {
element.style[styleName] = value;
}
}
};

297
src/utils/popup/index.js Normal file
View File

@ -0,0 +1,297 @@
import Vue from 'vue';
import merge from 'element-ui/src/utils/merge';
import PopupManager from 'element-ui/src/utils/popup/popup-manager';
let idSeed = 1;
const transitions = [];
const hookTransition = (transition) => {
if (transitions.indexOf(transition) !== -1) return;
const getVueInstance = (element) => {
let instance = element.__vue__;
if (!instance) {
const textNode = element.previousSibling;
if (textNode.__vue__) {
instance = textNode.__vue__;
}
}
return instance;
};
Vue.transition(transition, {
afterEnter(el) {
const instance = getVueInstance(el);
if (instance) {
instance.doAfterOpen && instance.doAfterOpen();
}
},
afterLeave(el) {
const instance = getVueInstance(el);
if (instance) {
instance.doAfterClose && instance.doAfterClose();
}
}
});
};
let scrollBarWidth;
const getScrollBarWidth = () => {
if (Vue.prototype.$isServer) return;
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
};
const getDOM = function(dom) {
if (dom.nodeType === 3) {
dom = dom.nextElementSibling || dom.nextSibling;
getDOM(dom);
}
return dom;
};
export default {
props: {
value: {
type: Boolean,
default: false
},
transition: {
type: String,
default: ''
},
openDelay: {},
closeDelay: {},
zIndex: {},
modal: {
type: Boolean,
default: false
},
modalFade: {
type: Boolean,
default: true
},
modalClass: {
},
lockScroll: {
type: Boolean,
default: true
},
closeOnPressEscape: {
type: Boolean,
default: false
},
closeOnClickModal: {
type: Boolean,
default: false
}
},
created() {
if (this.transition) {
hookTransition(this.transition);
}
},
beforeMount() {
this._popupId = 'popup-' + idSeed++;
PopupManager.register(this._popupId, this);
},
beforeDestroy() {
PopupManager.deregister(this._popupId);
PopupManager.closeModal(this._popupId);
if (this.modal && this.bodyOverflow !== null && this.bodyOverflow !== 'hidden') {
document.body.style.overflow = this.bodyOverflow;
document.body.style.paddingRight = this.bodyPaddingRight;
}
this.bodyOverflow = null;
this.bodyPaddingRight = null;
},
data() {
return {
opened: false,
bodyOverflow: null,
bodyPaddingRight: null,
rendered: false
};
},
watch: {
value(val) {
if (val) {
if (this._opening) return;
if (!this.rendered) {
this.rendered = true;
Vue.nextTick(() => {
this.open();
});
} else {
this.open();
}
} else {
this.close();
}
}
},
methods: {
open(options) {
if (!this.rendered) {
this.rendered = true;
this.$emit('input', true);
}
const props = merge({}, this, options);
if (this._closeTimer) {
clearTimeout(this._closeTimer);
this._closeTimer = null;
}
clearTimeout(this._openTimer);
const openDelay = Number(props.openDelay);
if (openDelay > 0) {
this._openTimer = setTimeout(() => {
this._openTimer = null;
this.doOpen(props);
}, openDelay);
} else {
this.doOpen(props);
}
},
doOpen(props) {
if (this.$isServer) return;
if (this.willOpen && !this.willOpen()) return;
if (this.opened) return;
this._opening = true;
// 使用 vue-popup 的组件,如果需要和父组件通信显示的状态,应该使用 value它是一个 prop
// 这样在父组件中用 v-model 即可;否则可以使用 visible它是一个 data
this.visible = true;
this.$emit('input', true);
const dom = getDOM(this.$el);
const modal = props.modal;
const zIndex = props.zIndex;
if (zIndex) {
PopupManager.zIndex = zIndex;
}
if (modal) {
if (this._closing) {
PopupManager.closeModal(this._popupId);
this._closing = false;
}
PopupManager.openModal(this._popupId, PopupManager.nextZIndex(), dom, props.modalClass, props.modalFade);
if (props.lockScroll) {
if (!this.bodyOverflow) {
this.bodyPaddingRight = document.body.style.paddingRight;
this.bodyOverflow = document.body.style.overflow;
}
scrollBarWidth = getScrollBarWidth();
let bodyHasOverflow = document.documentElement.clientHeight < document.body.scrollHeight;
if (scrollBarWidth > 0 && bodyHasOverflow) {
document.body.style.paddingRight = scrollBarWidth + 'px';
}
document.body.style.overflow = 'hidden';
}
}
if (getComputedStyle(dom).position === 'static') {
dom.style.position = 'absolute';
}
dom.style.zIndex = PopupManager.nextZIndex();
this.opened = true;
this.onOpen && this.onOpen();
if (!this.transition) {
this.doAfterOpen();
}
},
doAfterOpen() {
this._opening = false;
},
close() {
if (this.willClose && !this.willClose()) return;
if (this._openTimer !== null) {
clearTimeout(this._openTimer);
this._openTimer = null;
}
clearTimeout(this._closeTimer);
const closeDelay = Number(this.closeDelay);
if (closeDelay > 0) {
this._closeTimer = setTimeout(() => {
this._closeTimer = null;
this.doClose();
}, closeDelay);
} else {
this.doClose();
}
},
doClose() {
this.visible = false;
this.$emit('input', false);
this._closing = true;
this.onClose && this.onClose();
if (this.lockScroll) {
setTimeout(() => {
if (this.modal && this.bodyOverflow !== 'hidden') {
document.body.style.overflow = this.bodyOverflow;
document.body.style.paddingRight = this.bodyPaddingRight;
}
this.bodyOverflow = null;
this.bodyPaddingRight = null;
}, 200);
}
this.opened = false;
if (!this.transition) {
this.doAfterClose();
}
},
doAfterClose() {
PopupManager.closeModal(this._popupId);
this._closing = false;
}
}
};
export { PopupManager };

View File

@ -0,0 +1,165 @@
import Vue from 'vue';
import { addClass, removeClass } from 'element-ui/src/utils/dom';
let hasModal = false;
const getModal = function() {
if (Vue.prototype.$isServer) return;
let modalDom = PopupManager.modalDom;
if (modalDom) {
hasModal = true;
} else {
hasModal = false;
modalDom = document.createElement('div');
PopupManager.modalDom = modalDom;
modalDom.addEventListener('touchmove', function(event) {
event.preventDefault();
event.stopPropagation();
});
modalDom.addEventListener('click', function() {
PopupManager.doOnModalClick && PopupManager.doOnModalClick();
});
}
return modalDom;
};
const instances = {};
const PopupManager = {
zIndex: 2000,
modalFade: true,
getInstance: function(id) {
return instances[id];
},
register: function(id, instance) {
if (id && instance) {
instances[id] = instance;
}
},
deregister: function(id) {
if (id) {
instances[id] = null;
delete instances[id];
}
},
nextZIndex: function() {
return PopupManager.zIndex++;
},
modalStack: [],
doOnModalClick: function() {
const topItem = PopupManager.modalStack[PopupManager.modalStack.length - 1];
if (!topItem) return;
const instance = PopupManager.getInstance(topItem.id);
if (instance && instance.closeOnClickModal) {
instance.close();
}
},
openModal: function(id, zIndex, dom, modalClass, modalFade) {
if (Vue.prototype.$isServer) return;
if (!id || zIndex === undefined) return;
this.modalFade = modalFade;
const modalStack = this.modalStack;
for (let i = 0, j = modalStack.length; i < j; i++) {
const item = modalStack[i];
if (item.id === id) {
return;
}
}
const modalDom = getModal();
addClass(modalDom, 'v-modal');
if (this.modalFade && !hasModal) {
addClass(modalDom, 'v-modal-enter');
}
if (modalClass) {
let classArr = modalClass.trim().split(/\s+/);
classArr.forEach(item => addClass(modalDom, item));
}
setTimeout(() => {
removeClass(modalDom, 'v-modal-enter');
}, 200);
if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {
dom.parentNode.appendChild(modalDom);
} else {
document.body.appendChild(modalDom);
}
if (zIndex) {
modalDom.style.zIndex = zIndex;
}
modalDom.style.display = '';
this.modalStack.push({ id: id, zIndex: zIndex, modalClass: modalClass });
},
closeModal: function(id) {
const modalStack = this.modalStack;
const modalDom = getModal();
if (modalStack.length > 0) {
const topItem = modalStack[modalStack.length - 1];
if (topItem.id === id) {
if (topItem.modalClass) {
let classArr = topItem.modalClass.trim().split(/\s+/);
classArr.forEach(item => removeClass(modalDom, item));
}
modalStack.pop();
if (modalStack.length > 0) {
modalDom.style.zIndex = modalStack[modalStack.length - 1].zIndex;
}
} else {
for (let i = modalStack.length - 1; i >= 0; i--) {
if (modalStack[i].id === id) {
modalStack.splice(i, 1);
break;
}
}
}
}
if (modalStack.length === 0) {
if (this.modalFade) {
addClass(modalDom, 'v-modal-leave');
}
setTimeout(() => {
if (modalStack.length === 0) {
if (modalDom.parentNode) modalDom.parentNode.removeChild(modalDom);
modalDom.style.display = 'none';
PopupManager.modalDom = undefined;
}
removeClass(modalDom, 'v-modal-leave');
}, 200);
}
}
};
!Vue.prototype.$isServer && window.addEventListener('keydown', function(event) {
if (event.keyCode === 27) { // ESC
if (PopupManager.modalStack.length > 0) {
const topItem = PopupManager.modalStack[PopupManager.modalStack.length - 1];
if (!topItem) return;
const instance = PopupManager.getInstance(topItem.id);
if (instance.closeOnPressEscape) {
instance.close();
}
}
}
});
export default PopupManager;

View File

@ -3,9 +3,11 @@
* *
* version: 0.5.3 * version: 0.5.3
**/ **/
const isServer = typeof window === 'undefined';
/* istanbul ignore next */ /* istanbul ignore next */
const requestFrame = (function() { const requestFrame = (function() {
if (isServer) return;
const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || const raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
function(fn) { function(fn) {
return window.setTimeout(fn, 20); return window.setTimeout(fn, 20);
@ -17,6 +19,7 @@ const requestFrame = (function() {
/* istanbul ignore next */ /* istanbul ignore next */
const cancelFrame = (function() { const cancelFrame = (function() {
if (isServer) return;
const cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout; const cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout;
return function(id) { return function(id) {
return cancel(id); return cancel(id);
@ -59,7 +62,7 @@ const scrollListener = function(event) {
}; };
/* Detect CSS Animations support to detect element display/re-attach */ /* Detect CSS Animations support to detect element display/re-attach */
const attachEvent = document.attachEvent; const attachEvent = isServer ? {} : document.attachEvent;
const DOM_PREFIXES = 'Webkit Moz O ms'.split(' '); const DOM_PREFIXES = 'Webkit Moz O ms'.split(' ');
const START_EVENTS = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '); const START_EVENTS = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' ');
const RESIZE_ANIMATION_NAME = 'resizeanim'; const RESIZE_ANIMATION_NAME = 'resizeanim';
@ -68,7 +71,7 @@ let keyFramePrefix = '';
let animationStartEvent = 'animationstart'; let animationStartEvent = 'animationstart';
/* istanbul ignore next */ /* istanbul ignore next */
if (!attachEvent) { if (!attachEvent && !isServer) {
const testElement = document.createElement('fakeelement'); const testElement = document.createElement('fakeelement');
if (testElement.style.animationName !== undefined) { if (testElement.style.animationName !== undefined) {
animation = true; animation = true;
@ -91,7 +94,7 @@ if (!attachEvent) {
let stylesCreated = false; let stylesCreated = false;
/* istanbul ignore next */ /* istanbul ignore next */
const createStyles = function() { const createStyles = function() {
if (!stylesCreated) { if (!stylesCreated && !isServer) {
const animationKeyframes = `@${keyFramePrefix}keyframes ${RESIZE_ANIMATION_NAME} { from { opacity: 0; } to { opacity: 0; } } `; const animationKeyframes = `@${keyFramePrefix}keyframes ${RESIZE_ANIMATION_NAME} { from { opacity: 0; } to { opacity: 0; } } `;
const animationStyle = `${keyFramePrefix}animation: 1ms ${RESIZE_ANIMATION_NAME};`; const animationStyle = `${keyFramePrefix}animation: 1ms ${RESIZE_ANIMATION_NAME};`;
@ -119,6 +122,7 @@ const createStyles = function() {
/* istanbul ignore next */ /* istanbul ignore next */
export const addResizeListener = function(element, fn) { export const addResizeListener = function(element, fn) {
if (isServer) return;
if (attachEvent) { if (attachEvent) {
element.attachEvent('onresize', fn); element.attachEvent('onresize', fn);
} else { } else {

View File

@ -1,6 +1,7 @@
import PopperJS from './popper'; import Vue from 'vue';
import { PopupManager } from 'vue-popup'; import { PopupManager } from 'element-ui/src/utils/popup';
const PopperJS = Vue.prototype.$isServer ? function() {} : require('./popper');
const stop = e => e.stopPropagation(); const stop = e => e.stopPropagation();
/** /**
@ -66,6 +67,7 @@ export default {
methods: { methods: {
createPopper() { createPopper() {
if (this.$isServer) return;
if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(this.placement)) { if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(this.placement)) {
return; return;
} }

View File

@ -1,4 +1,4 @@
import VuePopup from 'vue-popup'; import VuePopup from 'element-ui/src/utils/popup';
import { createTest, destroyVM } from '../util'; import { createTest, destroyVM } from '../util';
const Popup = Object.assign({}, VuePopup, { const Popup = Object.assign({}, VuePopup, {

View File

@ -6863,12 +6863,6 @@ vue-markdown-loader@^0.5.1:
markdown-it "^6.0.5" markdown-it "^6.0.5"
rimraf "^2.5.2" rimraf "^2.5.2"
vue-popup@^0.2.14:
version "0.2.14"
resolved "https://registry.yarnpkg.com/vue-popup/-/vue-popup-0.2.14.tgz#aa3d461c6f2ab01ef4546665d730395ec6028302"
dependencies:
wind-dom "0.0.3"
vue-router@^2.0.0: vue-router@^2.0.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-2.1.1.tgz#10c31bbdcb6da92bd3e0223fa12345e73018625a" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-2.1.1.tgz#10c31bbdcb6da92bd3e0223fa12345e73018625a"
@ -7020,10 +7014,6 @@ wide-align@^1.1.0:
dependencies: dependencies:
string-width "^1.0.1" string-width "^1.0.1"
wind-dom@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wind-dom/-/wind-dom-0.0.3.tgz#3456e3d959dbebdcbf76ca68c6a4e8142f26360f"
window-size@^0.2.0: window-size@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"