diff --git a/src/form/input.js b/src/form/input.js index 4ba5855..416361e 100644 --- a/src/form/input.js +++ b/src/form/input.js @@ -343,7 +343,7 @@ class Input extends Component { renderClose() { return html`` } @@ -365,7 +365,7 @@ class Input extends Component { /> ${this.closeable && this.value ? this.renderClose() : ''} ${this.icon - ? html`` + ? html`` : html``} ` diff --git a/src/form/number.js b/src/form/number.js new file mode 100644 index 0000000..7efacf0 --- /dev/null +++ b/src/form/number.js @@ -0,0 +1,330 @@ +/** + * {数字文本框组件} + * @author yutent + * @date 2023/03/06 15:17:25 + */ + +import { css, html, Component, nextTick } from '@bd/core' + +class WcNumber extends Component { + static props = { + type: 'primary', + size: 'l', + max: null, + min: null, + step: 1, + value: { + type: Number, + default: 0, + attribute: false + }, + readonly: false, + autofocus: false, + disabled: false, + lazy: 1000 // 并发拦截时间, 单位毫秒 + } + + static styles = [ + // 基础样式 + css` + :host { + overflow: hidden; + display: inline-flex; + min-width: 128px; + height: 36px; + user-select: none; + -moz-user-select: none; + color: var(--color-dark-1); + border-radius: 3px; + cursor: text; + transition: box-shadow 0.15s linear; + } + + :host(:focus-within) { + box-shadow: 0 0 0 2px var(--color-plain-a); + } + .label { + display: flex; + width: 100%; + height: 100%; + margin: 0 auto; + line-height: 1; + font-size: 14px; + border: 1px solid var(--color-grey-2); + border-radius: inherit; + background: var(--bg-color, #fff); + color: inherit; + cursor: text; + + span { + display: flex; + justify-content: center; + align-items: center; + width: 34px; + height: 100%; + font-size: 18px; + cursor: pointer; + + &:first-child { + border-radius: 3px 0 0 3px; + border-right: 1px solid var(--color-grey-a); + } + &:last-child { + border-radius: 0 3px 3px 0; + border-left: 1px solid var(--color-grey-a); + } + + &.disabled { + cursor: not-allowed; + opacity: 0.6; + } + } + + input { + flex: 1; + min-width: 32px; + width: 0; + height: 100%; + padding: 0 6px; + border: 0; + border-radius: inherit; + color: inherit; + font: inherit; + text-align: center; + background: none; + outline: none; + box-shadow: none; + cursor: inherit; + + &::placeholder { + color: var(--color-grey-1); + } + } + /* ----- */ + } + `, + // 尺寸 + css` + @use 'sass:map'; + $sizes: ( + s: ( + w: 52px, + h: 20px, + f: 12px + ), + m: ( + w: 72px, + h: 24px, + f: 12px + ), + l: ( + w: 108px, + h: 32px, + f: 14px + ), + xl: ( + w: 132px, + h: 36px, + f: 14px + ), + xxl: ( + w: 160px, + h: 44px, + f: 14px + ), + xxxl: ( + w: 192px, + h: 52px, + f: 16px + ), + xxxxl: ( + w: 212px, + h: 64px, + f: 18px + ) + ); + + @loop $s, $v in $sizes { + :host([size='#{$s}']) { + min-width: map.get($v, 'w'); + height: map.get($v, 'h'); + font-size: map.get($v, 'f'); + + .icon { + --size: #{map.get($v, 'f')}; + } + } + :host([size='#{$s}'][circle]) { + width: map.get($v, 'h'); + height: map.get($v, 'h'); + } + } + + :host([round]) { + border-radius: 99rem; + } + `, + // 配色 + css` + $colors: ( + primary: 'teal', + info: 'blue', + success: 'green', + warning: 'orange', + danger: 'red', + secondary: 'dark', + help: 'grey' + ); + + @loop $t, $c in $colors { + :host([type='#{$t}']:focus-within) { + box-shadow: 0 0 0 2px var(--color-#{$c}-a); + } + :host([type='#{$t}']) span { + border-color: var(--color-#{$c}-a); + } + :host([type='#{$t}']) .label { + border-color: var(--color-#{$c}-2); + } + } + `, + // 状态 + css` + :host([disabled]) { + cursor: not-allowed; + opacity: 0.6; + } + /* --- */ + :host([readonly]) .label { + cursor: default; + opacity: 0.8; + span { + cursor: inherit; + } + } + :host([disabled]) .label { + background: var(--color-plain-1); + cursor: not-allowed; + opacity: 0.6; + span { + cursor: inherit; + } + } + ` + ] + + created() { + this.stamp = 0 + } + + mounted() { + if (this.autofocus) { + // 需要focus()才能聚焦成功 + nextTick(_ => this.$refs.input.focus()) + } + this.value = this.clapm(this.value) + this.$refs.input.value = this.value + } + + onClick(e) { + if (e.target.classList.contains('disabled')) { + return + } + const { disabled, readOnly } = this + if (disabled || readOnly) { + return + } + const { act } = e.target.dataset + this.updateValue(act) + } + updateValue(act) { + const { max, min, step, value } = this + if (act === '+') { + if (max === null || value + step <= max) { + this.value += step + } else { + this.value = max + } + } else { + if (min === null || value - step >= min) { + this.value -= step + } else { + this.value = min + } + } + this.$emit('input') + } + onChange(e) { + this.value = this.clapm(e.target.value) + this.$refs.input.value = this.value + } + onKeydown(ev) { + const now = Date.now() + const { lazy, disabled, readOnly } = this + + if (disabled || readOnly) { + return + } + + // up: 38, down: 40 + if (ev.keyCode === 38 || ev.keyCode === 40) { + ev.preventDefault() + return this.updateValue(ev.keyCode === 38 ? '+' : '-') + } + // 回车触发submit事件 + if (ev.keyCode === 13) { + ev.preventDefault() + + // 并发拦截 + if (lazy && now - this.stamp < lazy) { + return + } + + this.stamp = now + this.value = this.clapm(ev.target.value) + this.$refs.input.value = this.value + this.$emit('submit', { value: this.value }) + } + } + + clapm(val) { + const max = this.max ?? Number.MAX_SAFE_INTEGER + const min = this.min ?? Number.MIN_SAFE_INTEGER + return Math.min(max, Math.max(+val, min)) + } + render() { + return html` +
+ - + + this.max + ? 'disabled' + : ''} + @click=${this.onClick} + >+ +
+ ` + } +} + +customElements.define('wc-number', WcNumber)