vant/src/checkbox/Checker.tsx
2021-02-10 22:53:55 +08:00

149 lines
3.5 KiB
TypeScript

import { ref, computed, defineComponent, PropType } from 'vue';
import { addUnit } from '../utils';
import Icon from '../icon';
export type CheckerShape = 'square' | 'round';
export type CheckerLabelPosition = 'left' | 'right';
type CheckerParent = {
props: {
disabled?: boolean;
iconSize?: number | string;
direction?: 'horizontal' | 'vertical';
checkedColor?: string;
};
};
export const checkerProps = {
name: null as any,
disabled: Boolean,
iconSize: [Number, String],
modelValue: null as any,
checkedColor: String,
labelPosition: String as PropType<CheckerLabelPosition>,
labelDisabled: Boolean,
shape: {
type: String as PropType<CheckerShape>,
default: 'round',
},
};
export default defineComponent({
props: {
...checkerProps,
role: String,
parent: Object as PropType<CheckerParent | null>,
checked: Boolean,
bindGroup: {
type: Boolean,
default: true,
},
bem: {
type: Function,
required: true,
},
},
emits: ['click', 'toggle'],
setup(props, { emit, slots }) {
const iconRef = ref<HTMLElement>();
const getParentProp = <T extends keyof CheckerParent['props']>(name: T) => {
if (props.parent && props.bindGroup) {
return props.parent.props[name];
}
};
const disabled = computed(
() => getParentProp('disabled') || props.disabled
);
const direction = computed(() => getParentProp('direction'));
const iconStyle = computed(() => {
const checkedColor = props.checkedColor || getParentProp('checkedColor');
if (checkedColor && props.checked && !disabled.value) {
return {
borderColor: checkedColor,
backgroundColor: checkedColor,
};
}
});
const onClick = (event: MouseEvent) => {
const { target } = event;
const icon = iconRef.value;
const iconClicked = icon === target || icon!.contains(target as Node);
if (!disabled.value && (iconClicked || !props.labelDisabled)) {
emit('toggle');
}
emit('click', event);
};
const renderIcon = () => {
const { bem, shape, checked } = props;
const iconSize = props.iconSize || getParentProp('iconSize');
return (
<div
ref={iconRef}
class={bem('icon', [shape, { disabled: disabled.value, checked }])}
style={{ fontSize: addUnit(iconSize) }}
>
{slots.icon ? (
slots.icon({ checked })
) : (
<Icon name="success" style={iconStyle.value} />
)}
</div>
);
};
const renderLabel = () => {
if (slots.default) {
return (
<span
class={props.bem('label', [
props.labelPosition,
{ disabled: disabled.value },
])}
>
{slots.default()}
</span>
);
}
};
return () => {
const nodes: (JSX.Element | undefined)[] = [renderIcon()];
if (props.labelPosition === 'left') {
nodes.unshift(renderLabel());
} else {
nodes.push(renderLabel());
}
return (
<div
role={props.role}
class={props.bem([
{
disabled: disabled.value,
'label-disabled': props.labelDisabled,
},
direction.value,
])}
tabindex={disabled.value ? -1 : 0}
aria-checked={props.checked}
onClick={onClick}
>
{nodes}
</div>
);
};
},
});