+ * @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)