diff --git a/Readme.md b/Readme.md index 07646e4..f9f0609 100644 --- a/Readme.md +++ b/Readme.md @@ -24,7 +24,7 @@ - [ ] `wc-drag`拖拽组件 - [x] `wc-button`表单组件-按钮 - [x] `wc-link`表单组件-链接按钮 -- [ ] `wc-checkbox`表单组件-复选框 +- [x] `wc-checkbox`表单组件-复选框 - [ ] `wc-input`表单组件-文本输入框 - [x] `wc-passwd`表单组件-文本输入框 - [ ] `wc-number`表单组件-步进数字输入 diff --git a/src/form/checkbox.js b/src/form/checkbox.js new file mode 100644 index 0000000..b780228 --- /dev/null +++ b/src/form/checkbox.js @@ -0,0 +1,268 @@ +/** + * {} + * @author yutent + * @date 2023/03/21 16:14:10 + */ + +import { nextTick, css, html, Component } from '@bd/core' +import '../icon/index.js' + +class Checkbox extends Component { + static props = { + value: { + type: Array, + default: [], + attribute: false, + observer() { + this.#updateChildrenStat() + } + }, + disabled: false, + readonly: false + } + + static styles = css` + :host { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + } + ` + + mounted() { + this.$on('child-change', ev => { + ev.stopPropagation() + let idx = this.value.indexOf(ev.value) + if (idx > -1) { + this.value.splice(idx, 1) + } else { + this.value.push(ev.value) + } + this.$emit('change', { data: this.value }) + }) + + this.#updateChildrenStat(true) + } + + #updateChildrenStat(checkAll) { + Array.from(this.children).forEach(it => { + if (it.tagName === 'WC-CHECKBOX') { + if (it.root) { + if (checkAll) { + it.disabled = this.disabled + it.readOnly = this.readOnly + } + + it.checked = this.value.includes(it.value) + } + } else { + it.remove() + } + }) + } +} + +class CheckboxItem extends Component { + static props = { + value: { + type: String, + default: '', + attribute: false + }, + checked: false, + disabled: false, + readonly: false + } + + static styles = [ + css` + :host { + display: inline-flex; + align-items: center; + line-height: 1; + font-size: 14px; + cursor: pointer; + + label { + display: flex; + justify-content: center; + align-items: center; + min-width: 32px; + padding-right: 16px; + line-height: 1; + -moz-user-select: none; + user-select: none; + white-space: nowrap; + cursor: inherit; + outline: none; + color: var(--color-dark-1); + } + + .dot { + display: flex; + justify-content: center; + align-items: center; + width: 16px; + height: 16px; + margin-right: 4px; + border: 1px solid var(--color-dark-1); + border-radius: 4px; + background: #fff; + transition: box-shadow 0.15s linear; + + wc-icon { + display: block; + visibility: hidden; + width: 10px; + height: 10px; + transform: scale(0); + transition: transform 0.15s linear; + } + } + &:host([checked]) .dot wc-icon { + visibility: visible; + transform: scale(1); + } + } + `, + + css` + :host(:focus-within) .dot { + box-shadow: 0 0 0 2px var(--color-plain-a); + } + `, + // 尺寸 + css` + @use 'sass:map'; + $sizes: ( + 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 + ) + ); + + @loop $s, $v in $sizes { + :host([size='#{$s}']) { + height: map.get($v, 'h'); + font-size: map.get($v, 'f'); + + .dot { + width: #{map.get($v, 'f')}; + height: #{map.get($v, 'f')}; + } + } + } + `, + // 配色 + css` + $colors: ( + primary: 'teal', + info: 'blue', + success: 'green', + warning: 'orange', + danger: 'red', + secondary: 'dark', + help: 'grey' + ); + + @loop $t, $c in $colors { + :host([type='#{$t}']) { + label { + color: var(--color-#{$c}-2); + } + + .dot { + border-color: var(--color-#{$c}-2); + + &::after { + background: var(--color-#{$c}-2); + } + } + + &:host(:focus-within) .dot { + box-shadow: 0 0 0 2px var(--color-#{$c}-a); + } + } + } + `, + // 状态 + css` + :host([readonly]), + :host([disabled]) { + cursor: not-allowed; + opacity: 0.6; + } + :host([readonly]) { + cursor: default; + } + ` + ] + + toggleCheck(ev) { + if (this.disabled || this.readOnly) { + return + } + + ev.stopPropagation() + + this.checked = !this.checked + let data = { + value: this.value, + checked: this.checked + } + + if (this.inGroup) { + this.parentNode.$emit('child-change', data) + } else { + this.$emit('change', data) + } + } + + handleClick(ev) { + if (ev.type === 'click' || ev.keyCode === 32) { + this.toggleCheck(ev) + } + } + + mounted() { + if (this.parentNode?.tagName === 'WC-CHECKBOX-GROUP') { + this.inGroup = true + } + } + + render() { + return html` ` + } +} + +Checkbox.reg('checkbox-group') +CheckboxItem.reg('checkbox') diff --git a/src/form/radio.js b/src/form/radio.js index bc053aa..2aa75cc 100644 --- a/src/form/radio.js +++ b/src/form/radio.js @@ -30,6 +30,7 @@ class Radio extends Component { mounted() { this.$on('child-change', ev => { + ev.stopPropagation() this.value = ev.value this.$emit('change', { data: ev.value }) })