isu-elements
Version:
Polymer components for building web apps.
398 lines (340 loc) • 9.42 kB
JavaScript
import { html, PolymerElement } from '@polymer/polymer'
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class'
import '@polymer/iron-icon/iron-icon'
import '@polymer/iron-icons/iron-icons.js'
import { BaseBehavior } from './behaviors/base-behavior'
import './isu-input.js'
/**
* `isu-mask`
*
* Example:
* ```html
* <isu-mask label="输入框" class="mask">
* <isu-input placeholder="测试输入" value="Test"></isu-input>
* </isu-mask>
* ```
*
* ## Styling
*
* The following custom properties and mixins are available for styling:
*
* |Custom property | Description | Default|
* |----------------|-------------|----------|
* |`--isu-mask-label` | Mixin applied to the label of mask | {}
* |`--isu-mask-width` | The width of the isu-mask| 320px
*
* @customElement
* @polymer
* @demo demo/isu-mask/index.html
*/
class IsuMask extends mixinBehaviors([BaseBehavior], PolymerElement) {
static get template () {
return html`
<style include="isu-elements-shared-styles">
:host {
display: flex;
width: var(--isu-mask-width, 320px);
height: var(--isu-mask-height, var(--isu-default-line-height, 34px));
line-height: var(--isu-mask-height, var(--isu-default-line-height, 34px));
font-family: var(--isu-ui-font-family), sans-serif;
font-size: var(--isu-ui-font-size);
}
:host .mask__container {
position: relative;
flex: 1;
line-height: inherit;
height: inherit;
}
:host #mask__viewer:hover {
border-color: #cccccc;
}
:host #mask__viewer:hover .mask__viewer__editor {
display: block;
}
:host #mask__viewer {
position: absolute;
top: -1px;
right: 1px;
bottom: -1px;
left: -1px;
border: 1px solid transparent;
border-radius: 4px;
display: flex;
flex-flow: row nowrap;
align-items: center;
}
:host .mask__viewer__editor {
display: none;
background: var(--isu-ui-bg);
justify-self: flex-end;
padding: 0 4px;
height: inherit;
border-left: 1px solid #ccc;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
cursor: pointer;
}
:host .mask__viewer__container {
flex: 1;
text-align: left;
font-size: inherit;
padding: 0px 8px;
line-height: inherit;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
:host #mask__editor:focus {
display: block;
}
:host #mask__editor {
display: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: inherit;
line-height: inherit;
}
:host .button-container {
position: absolute;
right: 5px;
padding: 4px;
border: 1px solid #ccc;
border-top: none;
border-radius: 0 0 4px 4px;
background-color: #f0f0f0;
box-shadow: 0 3px 6px rgba(111, 111, 111, 0.2);
z-index: 5;
display: flex;
flex-flow: row nowrap;
}
:host .button-container > button {
background: var(--isu-ui-bg);
border: 1px solid #cccccc;
cursor: pointer;
border-radius: 4px;
padding: 0;
width: 22px;
height: 22px;
}
:host .button-container > button:hover {
background-color: #ffffff;
border-color: #66afe9;
}
:host iron-icon {
width: 20px;
height: 20px;
color: var(--isu-ui-color_white);
}
:host .btn--cancel {
margin-left: 3px;
}
::slotted(*) {
width: inherit;
height: inherit;
line-height: inherit;
font-size: inherit;
}
:host([data-edit-mode]) #mask__viewer {
display: none ;
}
:host([data-edit-mode]) #mask__editor {
display: block ;
}
</style>
<template is="dom-if" if="[[ toBoolean(label) ]]">
<div class="isu-label-div"><span class$="isu-label [[fontSize]]">[[label]]</span><span class="isu-label-after-extension"></span></div>
</template>
<div class$="mask__container [[fontSize]]">
<div id="mask__editor">
<slot>
<div id="defaultSlot"></div>
</slot>
<div class="button-container">
<button title="提交" on-click="_submit">
<iron-icon icon="icons:check"></iron-icon>
</button>
<button title="取消" class="btn--cancel" on-click="_cancel">
<iron-icon icon="icons:clear"></iron-icon>
</button>
</div>
</div>
<div id="mask__viewer">
<div class="mask__viewer__container">[[_viewValue]]</div>
<div id="editBtn" class="mask__viewer__editor" title="点击开始编辑">
<iron-icon icon="icons:create"></iron-icon>
</div>
</div>
</div>
`
}
static get properties () {
return {
/**
* The value of the slotted node.
*/
value: {
type: String,
notify: true
},
/**
* 当前的数据
*/
_valueObj: {
type: Object
},
/**
* 最近一次保存的数据
*/
_lastValueObj: {
type: Object
},
_viewValue: {
type: String
},
/**
* The label of the mask.
*/
label: {
type: String
},
/**
* The placeholder of the mask.
*/
placeholder: {
type: String
},
/**
* Set to true, if the selection is required.
* @type {boolean}
* @default false
*/
required: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* The attribute name of the item to display on mask.
*/
attrForDisplay: {
type: String,
value: 'label'
},
/**
* slot插槽中的组件
*/
_slotNode: {
type: Object
}
}
}
static get is () {
return 'isu-mask'
}
static get observers () {
return []
}
ready () {
super.ready()
const viewField = this.$.mask__viewer
const slotNodes = this.root.querySelector('slot')
.assignedNodes()
.filter(n => n.nodeType === Node.ELEMENT_NODE)
this._slotNode = slotNodes[0]
// a hack to get the init value
const valueChangedHandler = (e) => {
this.__initValueOfSlottedElement()
this.__updateViewer()
if (e.type === 'value-changed') {
this._slotNode.removeEventListener('value-changed', valueChangedHandler)
} else if (e.type === 'selected-values-changed') {
this._slotNode.removeEventListener('selected-values-changed', valueChangedHandler)
}
}
this._slotNode.addEventListener('value-changed', valueChangedHandler)
this._slotNode.addEventListener('selected-values-changed', valueChangedHandler)
viewField.addEventListener('click', e => {
e.stopPropagation()
this.__initValueOfSlottedElement()
this._displayEditField(true)
this.isFunction(this._slotNode.doFocus) && this._slotNode.doFocus()
})
/**
* 组件失去焦点的时候关闭组件
*/
this.addEventListener('focusout', e => {
e.stopPropagation()
setTimeout(() => {
// double check the focus is out
if (!this._slotNode.shadowRoot.activeElement) {
this.__getValueOfSlottedElement()
this.__updateViewer()
this._displayEditField(false)
}
}, 100)
})
setTimeout(() => {
this.__initValueOfSlottedElement()
this.__updateViewer()
}, 0)
}
/**
* 显示编辑区域
* @private
*/
_displayEditField (display) {
if (display) {
this.setAttribute('data-edit-mode', '')
} else {
this.removeAttribute('data-edit-mode')
}
}
__initValueOfSlottedElement () {
const { value, selectedValues } = this._slotNode
this._lastValueObj = { value, selectedValues }
this._valueObj = { value, selectedValues }
this.value = this._slotNode.value
}
__getValueOfSlottedElement () {
this._lastValueObj = this.deepClone(this._valueObj)
const { value, selectedValues } = this._slotNode
this._valueObj = { value, selectedValues }
this.value = this._slotNode.value
}
__resetValueOfSlottedElement () {
this._valueObj = this._lastValueObj
this._slotNode.set('value', this._valueObj.value)
}
__updateViewer () {
const selectedValues = this._valueObj.selectedValues
let _viewValue
if (Array.isArray(selectedValues)) {
_viewValue = selectedValues.map(selected => selected[this.attrForDisplay]).join(', ')
}
this._viewValue = _viewValue || this._valueObj.value || '无'
}
/**
* 提交操作
* @private
*/
_submit (e) {
e.stopPropagation()
this.__getValueOfSlottedElement()
this.__updateViewer()
this._displayEditField(false)
}
/**
* 取消操作
* @private
*/
_cancel (e) {
e.stopPropagation()
this.__resetValueOfSlottedElement()
this.__updateViewer()
this._displayEditField(false)
}
}
window.customElements.define(IsuMask.is, IsuMask)