diff --git a/example/Main.san b/example/Main.san index 7a15a31..943fc88 100644 --- a/example/Main.san +++ b/example/Main.san @@ -4,8 +4,8 @@

- + +

This is a popover!

+

popover content!

+
+ +

This is a popover!

+

popover content!

+
+ +

This is a popover!

+

popover content!

+
+ +

This is a popover!

+

popover content!

+
+ +

This is a popover!

+

popover content!

+
@@ -62,6 +82,7 @@ import AppBar from '../src/AppBar'; import {Card, CardHeader, CardMedia, CardTitle, CardText, CardActions} from '../src/Card'; import Slider from '../src/Slider'; import Icon from '../src/Icon'; +import Popover from '../src/Popover'; export default { components: { @@ -75,16 +96,28 @@ export default { 'san-cardtext': CardText, 'san-cardactions': CardActions, 'san-slider': Slider, - 'icon': Icon + 'icon': Icon, + 'popover': Popover }, initData() { return { bar: 'Title', - text: '散落在指尖的阳光,我试着轻轻抓住光影的踪迹,它却在眉宇间投下一片淡淡的阴影。 调皮的阳光掀动了四月的心帘,温暖如约的歌声渐起。 似乎在诉说着,我也可以在漆黑的角落里,找到阴影背后的阳光, 找到阳光与阴影奏出和谐的旋律。我要用一颗敏感赤诚的心迎接每一缕滑过指尖的阳光' + text: '散落在指尖的阳光,我试着轻轻抓住光影的踪迹,它却在眉宇间投下一片淡淡的阴影。 调皮的阳光掀动了四月的心帘,温暖如约的歌声渐起。 似乎在诉说着,我也可以在漆黑的角落里,找到阴影背后的阳光, 找到阳光与阴影奏出和谐的旋律。我要用一颗敏感赤诚的心迎接每一缕滑过指尖的阳光', + triggerLeft: document.getElementById('popover-left'), + triggerTop: document.getElementById('popover-top'), + triggerBottom: document.getElementById('popover-bottom'), + triggerRight: document.getElementById('popover-right'), + triggerFocus: document.getElementById('input') } }, labelClicker(data) { console.log(data); + }, + onShow() { + console.log('popover show'); + }, + onHide() { + console.log('popover hide'); } } diff --git a/example/index.html b/example/index.html index 2a270e4..475affc 100644 --- a/example/index.html +++ b/example/index.html @@ -11,6 +11,13 @@
+
+ + + + + +
diff --git a/src/Popover/index.js b/src/Popover/index.js new file mode 100644 index 0000000..666b7e4 --- /dev/null +++ b/src/Popover/index.js @@ -0,0 +1,204 @@ +/** + * @file Popover/index.js Popover component for San + * @author zhangzhiqiang(zhiqiangzhang37@gmail.com) + */ + +import './index.styl'; +import san from 'san'; +import template from './index.tpl'; + +const openKey = 'open'; +const animationKey = 'animation'; +const showArrowKey = 'showArrow'; +const placementKey = 'placement'; +const triggerKey = 'trigger'; +const triggerOperationKey = 'triggerOperation'; +const defaultPlacement = 'bottom'; +const defaultTriggerOperation = 'click'; +const placementType = [defaultPlacement, 'right', 'top', 'left']; +const triggerOperationType = [defaultTriggerOperation, 'focus']; + +function getOffset(ele) { + + if (!ele) { + return {}; + } + + let res = { + offsetTop: 0, + offsetLeft: 0, + offsetWidth: ele.offsetWidth, + offsetHeight: ele.offsetHeight + }; + while (ele) { + res.offsetTop += ele.offsetTop; + res.offsetLeft += ele.offsetLeft; + ele = ele.offsetParent; + } + + return res; +} + +export default san.defineComponent({ + + template, + + initData() { + // default data + return { + [openKey]: false, + [showArrowKey]: true, + [animationKey]: true, + [placementKey]: defaultPlacement, + [triggerOperationKey]: defaultTriggerOperation + }; + }, + inited() { + // 处理一些初始化数据 + this.transBoolean(animationKey); + this.transBoolean(showArrowKey); + this.transBoolean(openKey); + this.compatErrorData(triggerOperationKey, triggerOperationType); + this.compatErrorData(placementKey, placementType); + }, + created() { + document.body.appendChild(this.el); + // 默认显示 + if (this.data.get(openKey)) { + this.show(); + } + this.bindEvent(); + }, + show() { + let triggerEle = this.getTriggerEle(); + + if (!triggerEle) { + return; + } + + this.data.set(openKey, true); + this.setPosition(triggerEle); + this.fire('show'); + }, + setPosition(triggerEle) { + let posTop; + let posLeft; + let placement = this.data.get(placementKey); + let { + offsetTop: triggerEleOffsetTop, + offsetLeft: triggerEleOffsetLeft, + offsetWidth: triggerEleOffsetWidth, + offsetHeight: triggerEleOffsetHeight + } = getOffset(triggerEle); + let { + offsetWidth: popoverEleOffsetWidth, + offsetHeight: popoverEleOffsetHeight + } = getOffset(this.el); + let horizonPosTop = triggerEleOffsetTop + triggerEleOffsetHeight / 2 - popoverEleOffsetHeight / 2; + let verticalPosLeft = triggerEleOffsetLeft + triggerEleOffsetWidth / 2 - popoverEleOffsetWidth / 2; + switch (placement) { + case 'right': + posLeft = triggerEleOffsetLeft + triggerEleOffsetWidth; + posTop = horizonPosTop; + break; + case 'bottom': + posLeft = verticalPosLeft; + posTop = triggerEleOffsetTop + triggerEleOffsetHeight; + break; + case 'left': + posLeft = triggerEleOffsetLeft - popoverEleOffsetWidth; + posTop = horizonPosTop; + break; + default: + posLeft = verticalPosLeft; + posTop = triggerEleOffsetTop - popoverEleOffsetHeight; + break; + } + this.data.set('posLeft', posLeft); + this.data.set('posTop', posTop); + }, + hide() { + this.data.set(openKey, false); + this.fire('hide'); + }, + toggle() { + let isOpen = this.data.get(openKey); + if (isOpen) { + this.hide(); + } + else { + this.show(); + } + }, + bindEvent() { + let me = this; + let triggerEle = me.getTriggerEle(); + + if (!triggerEle) { + return; + } + + let hide = me.hide.bind(me); + let triggerOperation = me.data.get(triggerOperationKey); + // 注册显示popover的事件 + triggerEle.addEventListener(triggerOperation, () => { + if (!me.data.get(openKey)) { + me.show(); + me.data.clickNoHide = true; + } + }); + + // 注册隐藏popover事件 + if (triggerOperation === 'focus') { + triggerEle.addEventListener('blur', hide); + } + else { + // 除了点击popover浮层,点击其他地方都隐藏浮层 + me.el.addEventListener(defaultTriggerOperation, () => { + me.data.clickNoHide = true; + }); + document.addEventListener(defaultTriggerOperation, e => { + if (!me.data.clickNoHide && me.data.get(openKey)) { + hide(); + } + me.data.clickNoHide = false; + }); + } + + }, + getTriggerEle() { + let triggerEle = this.data.get(triggerKey); + + if (!triggerEle || !triggerEle.tagName) { + console.error('Please provide trigger'); + return; + } + + return triggerEle; + }, + + /** + * 布尔值转换,字符串false转换为布尔值false,其他则按正常转换进行转换 + * + * @param {string} key 要转换的数据key + */ + transBoolean(key) { + let value = this.data.get(key); + this.data.set(key, value === 'false' ? false : !!value); + }, + + /** + * 数据容错,不在可支持列表里就兼容为默认值 + * + * @param {string} key 要容错的数据key + * @param {Array} supportList 数据支持列表 + */ + compatErrorData(key, supportList) { + + let value = this.data.get(key); + + if (supportList) { + this.data.set(key, supportList.indexOf(value) !== -1 ? value : supportList[0]); + } + } +}); diff --git a/src/Popover/index.styl b/src/Popover/index.styl new file mode 100644 index 0000000..906204c --- /dev/null +++ b/src/Popover/index.styl @@ -0,0 +1,75 @@ +$arrow-width=11 +$border-color=#ccc + +.sm-popover + border: solid 1px $border-color + box-shadow: 0 5px 10px 1px #bbb + opacity: 0 + border-radius: 5px + padding: 8px + position: absolute + display: inline-block + z-index: 999 + background-color: #fff + &.hide + left: -999999999px !important + &.show + opacity: 1 + left: 0 + &.animation + transition: ease .5s opacity + .arrow + width: 0 + height: 0 + border: solid ($arrow-width)px transparent + position: absolute + &:after + position: absolute + content: '' + width: 0 + height: 0 + border: solid ($arrow-width - 1)px transparent + &-bottom, + &-top + border-top-width: 0 + border-bottom-color: $border-color + top: -($arrow-width)px + left: 50% + margin-left: -($arrow-width)px + &:after + border-top-width: 0 + border-bottom-color: #fff + left: -($arrow-width - 1)px + bottom: -($arrow-width)px + &-left, + &-right + border-right-width: 0 + border-left-color: $border-color + top: 50% + right: -($arrow-width)px + margin-top: -($arrow-width)px + &:after + border-right-width: 0 + border-left-color: #fff + left: -($arrow-width)px + bottom: -($arrow-width - 1)px + + + &.top + margin-top: -($arrow-width)px + .arrow + transform: rotate(180deg) + top: initial + bottom: -($arrow-width)px + &.right + margin-left: ($arrow-width)px + .arrow + transform: rotate(180deg) + right: initial + left: -($arrow-width)px + &.bottom + margin-top: ($arrow-width)px + &.left + margin-left: -($arrow-width)px + &.no-arrow + margin: 0 diff --git a/src/Popover/index.tpl b/src/Popover/index.tpl new file mode 100644 index 0000000..59fe1b5 --- /dev/null +++ b/src/Popover/index.tpl @@ -0,0 +1,7 @@ +
+
+
+ +
+
diff --git a/tool/webpack.base.conf.js b/tool/webpack.base.conf.js index 62c4c46..f8206cb 100644 --- a/tool/webpack.base.conf.js +++ b/tool/webpack.base.conf.js @@ -85,6 +85,10 @@ export default { limit: 100000, name: assetsPath('fonts/[name].[hash:7].[ext]') } + }, + { + test: /\.(html|tpl)(\?.*)?$/, + loader: 'html-loader' } ] },