diff --git a/Readme.md b/Readme.md index 3f20038..f9f0609 100644 --- a/Readme.md +++ b/Readme.md @@ -24,12 +24,12 @@ - [ ] `wc-drag`拖拽组件 - [x] `wc-button`表单组件-按钮 - [x] `wc-link`表单组件-链接按钮 -- [ ] `wc-checkbox`表单组件-复选框 +- [x] `wc-checkbox`表单组件-复选框 - [ ] `wc-input`表单组件-文本输入框 - [x] `wc-passwd`表单组件-文本输入框 - [ ] `wc-number`表单组件-步进数字输入 - [ ] `wc-star`表单组件-评分 -- [ ] `wc-radio`表单组件-单选框 +- [x] `wc-radio`表单组件-单选框 - [ ] `wc-select`表单组件-下拉选择 - [ ] `wc-cascadar`表单组件-多级联动 - [ ] `wc-switch`表单组件-开关 diff --git a/src/form/checkbox.js b/src/form/checkbox.js new file mode 100644 index 0000000..16dfbe5 --- /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: 14px; + height: 14px; + 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 332cde2..2aa75cc 100644 --- a/src/form/radio.js +++ b/src/form/radio.js @@ -13,18 +13,28 @@ class Radio extends Component { default: '', attribute: false, observer() { - // this.#updateChildrenStat() + 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 => { - console.log(ev) + ev.stopPropagation() this.value = ev.value + this.$emit('change', { data: ev.value }) }) + this.#updateChildrenStat(true) } #updateChildrenStat(checkAll) { @@ -47,13 +57,6 @@ class Radio extends Component { } }) } - - render() { - return html`` - } } class RadioItem extends Component { @@ -132,11 +135,6 @@ class RadioItem extends Component { css` @use 'sass:map'; $sizes: ( - s: ( - w: 52px, - h: 20px, - f: 12px - ), m: ( w: 72px, h: 24px, @@ -161,50 +159,19 @@ class RadioItem extends Component { 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')}; + .dot { + width: #{map.get($v, 'f')}; + height: #{map.get($v, 'f')}; } } - :host([size='#{$s}'][circle]) { - width: map.get($v, 'h'); - height: map.get($v, 'h'); - } - } - - :host([dashed]) button { - border-style: dashed; - } - - :host([round]) { - border-radius: 32px; - } - :host([circle]) { - min-width: 0; - border-radius: 50%; - button { - padding: 0; - } - .icon { - margin-right: 0; - } - - slot { - display: none; - } } `, // 配色 @@ -241,11 +208,14 @@ class RadioItem extends Component { `, // 状态 css` - :host([loading]), + :host([readonly]), :host([disabled]) { cursor: not-allowed; opacity: 0.6; } + :host([readonly]) { + cursor: default; + } ` ] @@ -272,16 +242,15 @@ class RadioItem extends Component { } mounted() { - console.log(this.value) if (this.parentNode?.tagName === 'WC-RADIO-GROUP') { this.inGroup = true } } render() { - console.log('render', this.value) - return html`