refactor: checkbox (#5091)

* style: remove not use pkg

* refactor: vc-checkbox

* refactor: checkbox

* fix: radio type
pull/5092/head
tangjinzhou 2021-12-28 16:47:52 +08:00 committed by GitHub
parent 620214d536
commit 26496ba0a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 494 additions and 946 deletions

View File

@ -1,36 +1,15 @@
import type { ExtractPropTypes } from 'vue'; import { watchEffect, onMounted, defineComponent, inject, onBeforeUnmount, ref } from 'vue';
import { defineComponent, inject, nextTick } from 'vue';
import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import VcCheckbox from '../vc-checkbox'; import VcCheckbox from '../vc-checkbox/Checkbox';
import hasProp, { getOptionProps, getSlot } from '../_util/props-util'; import { flattenChildren } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import warning from '../_util/warning'; import warning from '../_util/warning';
import type { RadioChangeEvent } from '../radio/interface'; import type { RadioChangeEvent } from '../radio/interface';
import type { EventHandler } from '../_util/EventInterface'; import type { EventHandler } from '../_util/EventInterface';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
function noop() {} import useConfigInject from '../_util/hooks/useConfigInject';
export const checkboxProps = () => { import type { CheckboxProps } from './interface';
return { import { CheckboxGroupContextKey, checkboxProps } from './interface';
prefixCls: PropTypes.string,
defaultChecked: PropTypes.looseBool,
checked: PropTypes.looseBool,
disabled: PropTypes.looseBool,
isGroup: PropTypes.looseBool,
value: PropTypes.any,
name: PropTypes.string,
id: PropTypes.string,
indeterminate: PropTypes.looseBool,
type: PropTypes.string.def('checkbox'),
autofocus: PropTypes.looseBool,
onChange: PropTypes.func,
'onUpdate:checked': PropTypes.func,
skipGroup: PropTypes.looseBool,
};
};
export type CheckboxProps = Partial<ExtractPropTypes<ReturnType<typeof checkboxProps>>>;
export default defineComponent({ export default defineComponent({
name: 'ACheckbox', name: 'ACheckbox',
@ -38,124 +17,91 @@ export default defineComponent({
__ANT_CHECKBOX: true, __ANT_CHECKBOX: true,
props: checkboxProps(), props: checkboxProps(),
emits: ['change', 'update:checked'], emits: ['change', 'update:checked'],
setup() { setup(props, { emit, attrs, slots, expose }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
return { const { prefixCls, direction } = useConfigInject('checkbox', props);
formItemContext, const checkboxGroup = inject(CheckboxGroupContextKey, undefined);
configProvider: inject('configProvider', defaultConfigProvider), const uniId = Symbol('checkboxUniId');
checkboxGroupContext: inject('checkboxGroupContext', undefined),
};
},
watch: { watchEffect(() => {
value(value, prevValue) { if (!props.skipGroup && checkboxGroup) {
if (this.skipGroup) { checkboxGroup.registerValue(uniId, props.value);
return;
} }
nextTick(() => {
const { checkboxGroupContext: checkboxGroup = {} } = this;
if (checkboxGroup.registerValue && checkboxGroup.cancelValue) {
checkboxGroup.cancelValue(prevValue);
checkboxGroup.registerValue(value);
}
});
},
},
mounted() {
const { value, checkboxGroupContext: checkboxGroup = {} } = this;
if (checkboxGroup.registerValue) {
checkboxGroup.registerValue(value);
}
warning(
hasProp(this, 'checked') || this.checkboxGroupContext || !hasProp(this, 'value'),
'Checkbox',
'`value` is not validate prop, do you mean `checked`?',
);
},
beforeUnmount() {
const { value, checkboxGroupContext: checkboxGroup = {} } = this;
if (checkboxGroup.cancelValue) {
checkboxGroup.cancelValue(value);
}
},
methods: {
handleChange(event: RadioChangeEvent) {
const targetChecked = event.target.checked;
this.$emit('update:checked', targetChecked);
// this.$emit('input', targetChecked);
this.$emit('change', event);
},
focus() {
(this.$refs.vcCheckbox as HTMLInputElement).focus();
},
blur() {
(this.$refs.vcCheckbox as HTMLInputElement).blur();
},
},
render() {
const props = getOptionProps(this);
const { checkboxGroupContext: checkboxGroup, $attrs } = this;
const children = getSlot(this);
const {
indeterminate,
prefixCls: customizePrefixCls,
skipGroup,
id = this.formItemContext.id.value,
...restProps
} = props;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
const {
onMouseenter = noop,
onMouseleave = noop,
onInput,
class: className,
style,
...restAttrs
} = $attrs;
const checkboxProps: any = {
...restProps,
id,
prefixCls,
...restAttrs,
};
if (checkboxGroup && !skipGroup) {
checkboxProps.onChange = (...args) => {
this.$emit('change', ...args);
this.formItemContext.onFieldChange();
checkboxGroup.toggleOption({ label: children, value: props.value });
};
checkboxProps.name = checkboxGroup.name;
checkboxProps.checked = checkboxGroup.sValue.indexOf(props.value) !== -1;
checkboxProps.disabled = props.disabled || checkboxGroup.disabled;
checkboxProps.indeterminate = indeterminate;
} else {
checkboxProps.onChange = this.handleChange;
}
const classString = classNames(
{
[`${prefixCls}-wrapper`]: true,
[`${prefixCls}-wrapper-checked`]: checkboxProps.checked,
[`${prefixCls}-wrapper-disabled`]: checkboxProps.disabled,
},
className,
);
const checkboxClass = classNames({
[`${prefixCls}-indeterminate`]: indeterminate,
}); });
return ( onBeforeUnmount(() => {
<label if (checkboxGroup) {
class={classString} checkboxGroup.cancelValue(uniId);
style={style} }
onMouseenter={onMouseenter as EventHandler} });
onMouseleave={onMouseleave as EventHandler} onMounted(() => {
> warning(
<VcCheckbox {...checkboxProps} class={checkboxClass} ref="vcCheckbox" /> props.checked !== undefined || checkboxGroup || props.value === undefined,
{children.length ? <span>{children}</span> : null} 'Checkbox',
</label> '`value` is not validate prop, do you mean `checked`?',
); );
});
const handleChange = (event: RadioChangeEvent) => {
const targetChecked = event.target.checked;
emit('update:checked', targetChecked);
emit('change', event);
};
const checkboxRef = ref();
const focus = () => {
checkboxRef.value?.focus();
};
const blur = () => {
checkboxRef.value?.blur();
};
expose({
focus,
blur,
});
return () => {
const children = flattenChildren(slots.default?.());
const { indeterminate, skipGroup, id = formItemContext.id.value, ...restProps } = props;
const { onMouseenter, onMouseleave, onInput, class: className, style, ...restAttrs } = attrs;
const checkboxProps: CheckboxProps = {
...restProps,
id,
prefixCls: prefixCls.value,
...restAttrs,
};
if (checkboxGroup && !skipGroup) {
checkboxProps.onChange = (...args) => {
emit('change', ...args);
checkboxGroup.toggleOption({ label: children, value: props.value });
};
checkboxProps.name = checkboxGroup.name.value;
checkboxProps.checked = checkboxGroup.mergedValue.value.indexOf(props.value) !== -1;
checkboxProps.disabled = props.disabled || checkboxGroup.disabled.value;
checkboxProps.indeterminate = indeterminate;
} else {
checkboxProps.onChange = handleChange;
}
const classString = classNames(
{
[`${prefixCls.value}-wrapper`]: true,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-wrapper-checked`]: checkboxProps.checked,
[`${prefixCls.value}-wrapper-disabled`]: checkboxProps.disabled,
},
className,
);
const checkboxClass = classNames({
[`${prefixCls.value}-indeterminate`]: indeterminate,
});
return (
<label
class={classString}
style={style}
onMouseenter={onMouseenter as EventHandler}
onMouseleave={onMouseleave as EventHandler}
>
<VcCheckbox {...checkboxProps} class={checkboxClass} ref={checkboxRef} />
{children.length ? <span>{children}</span> : null}
</label>
);
};
}, },
}); });

View File

@ -1,134 +1,116 @@
import type { PropType } from 'vue'; import { computed, ref, watch, defineComponent, provide } from 'vue';
import { defineComponent, inject, provide } from 'vue';
import PropTypes from '../_util/vue-types';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import hasProp, { getSlot } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import type { VueNode } from '../_util/type';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
import useConfigInject from '../_util/hooks/useConfigInject';
import type { CheckboxOptionType } from './interface';
import { CheckboxGroupContextKey, checkboxGroupProps } from './interface';
export type CheckboxValueType = string | number | boolean;
export interface CheckboxOptionType {
label: VueNode;
value: CheckboxValueType;
disabled?: boolean;
indeterminate?: boolean;
onChange?: (e: Event) => void;
}
function noop() {}
export default defineComponent({ export default defineComponent({
name: 'ACheckboxGroup', name: 'ACheckboxGroup',
props: { props: checkboxGroupProps(),
name: PropTypes.string,
prefixCls: PropTypes.string,
defaultValue: { type: Array as PropType<Array<CheckboxValueType>> },
value: { type: Array as PropType<Array<CheckboxValueType>> },
options: { type: Array as PropType<Array<CheckboxOptionType | string>> },
disabled: PropTypes.looseBool,
onChange: PropTypes.func,
id: PropTypes.string,
},
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup() { setup(props, { slots, emit, expose }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
return { const { prefixCls, direction } = useConfigInject('checkbox', props);
formItemContext, const mergedValue = ref((props.value === undefined ? props.defaultValue : props.value) || []);
configProvider: inject('configProvider', defaultConfigProvider), watch(
}; () => props.value,
}, () => {
mergedValue.value = props.value || [];
data() { },
const { value, defaultValue } = this; );
return { const options = computed(() => {
sValue: value || defaultValue || [], return props.options.map(option => {
registeredValues: [],
};
},
watch: {
value(val) {
this.sValue = val || [];
},
},
created() {
provide('checkboxGroupContext', this);
},
methods: {
getOptions() {
const { options = [], $slots } = this;
return options.map(option => {
if (typeof option === 'string') { if (typeof option === 'string') {
return { return {
label: option, label: option,
value: option, value: option,
}; };
} }
let label = option.label; return option;
if (label === undefined && $slots.label) {
label = $slots.label(option);
}
return { ...option, label };
}); });
}, });
cancelValue(value: CheckboxValueType) { const triggerUpdate = ref(Symbol());
this.registeredValues = this.registeredValues.filter(val => val !== value); const registeredValuesMap = ref<Map<Symbol, string>>(new Map());
}, const cancelValue = (id: Symbol) => {
registeredValuesMap.value.delete(id);
triggerUpdate.value = Symbol();
};
const registerValue = (id: Symbol, value: string) => {
registeredValuesMap.value.set(id, value);
triggerUpdate.value = Symbol();
};
registerValue(value: CheckboxValueType) { const registeredValues = ref(new Map());
this.registeredValues = [...this.registeredValues, value]; watch(triggerUpdate, () => {
}, const valuseMap = new Map();
toggleOption(option: CheckboxOptionType) { for (const value of registeredValuesMap.value.values()) {
const { registeredValues } = this; valuseMap.set(value, true);
const optionIndex = this.sValue.indexOf(option.value); }
const value = [...this.sValue]; registeredValues.value = valuseMap;
});
const toggleOption = (option: CheckboxOptionType) => {
const optionIndex = mergedValue.value.indexOf(option.value);
const value = [...mergedValue.value];
if (optionIndex === -1) { if (optionIndex === -1) {
value.push(option.value); value.push(option.value);
} else { } else {
value.splice(optionIndex, 1); value.splice(optionIndex, 1);
} }
if (!hasProp(this, 'value')) { if (props.value === undefined) {
this.sValue = value; mergedValue.value = value;
} }
const options = this.getOptions();
const val = value const val = value
.filter(val => registeredValues.indexOf(val) !== -1) .filter(val => registeredValues.value.has(val))
.sort((a, b) => { .sort((a, b) => {
const indexA = options.findIndex(opt => opt.value === a); const indexA = options.value.findIndex(opt => opt.value === a);
const indexB = options.findIndex(opt => opt.value === b); const indexB = options.value.findIndex(opt => opt.value === b);
return indexA - indexB; return indexA - indexB;
}); });
// this.$emit('input', val); emit('update:value', val);
this.$emit('update:value', val); emit('change', val);
this.$emit('change', val); formItemContext.onFieldChange();
this.formItemContext.onFieldChange(); };
}, provide(CheckboxGroupContextKey, {
}, cancelValue,
render() { registerValue,
const { $props: props, $data: state } = this; toggleOption,
const { prefixCls: customizePrefixCls, options, id = this.formItemContext.id.value } = props; mergedValue,
const getPrefixCls = this.configProvider.getPrefixCls; name: computed(() => props.name),
const prefixCls = getPrefixCls('checkbox', customizePrefixCls); disabled: computed(() => props.disabled),
let children = getSlot(this); });
const groupPrefixCls = `${prefixCls}-group`; expose({
if (options && options.length > 0) { mergedValue,
children = this.getOptions().map(option => ( });
<Checkbox return () => {
prefixCls={prefixCls} const { id = formItemContext.id.value } = props;
key={option.value.toString()} let children = null;
disabled={'disabled' in option ? option.disabled : props.disabled} const groupPrefixCls = `${prefixCls.value}-group`;
indeterminate={option.indeterminate} if (options.value && options.value.length > 0) {
value={option.value} children = options.value.map(option => (
checked={state.sValue.indexOf(option.value) !== -1} <Checkbox
onChange={option.onChange || noop} prefixCls={prefixCls.value}
class={`${groupPrefixCls}-item`} key={option.value.toString()}
disabled={'disabled' in option ? option.disabled : props.disabled}
indeterminate={option.indeterminate}
value={option.value}
checked={mergedValue.value.indexOf(option.value) !== -1}
onChange={option.onChange}
class={`${groupPrefixCls}-item`}
>
{option.label === undefined ? slots.label?.(option) : option.label}
</Checkbox>
));
}
return (
<div
class={[groupPrefixCls, { [`${groupPrefixCls}-rtl`]: direction.value === 'rtl' }]}
id={id}
> >
{option.label} {children || slots.default?.()}
</Checkbox> </div>
)); );
} };
return (
<div class={groupPrefixCls} id={id}>
{children}
</div>
);
}, },
}); });

View File

@ -3,8 +3,10 @@
exports[`renders ./components/checkbox/demo/basic.vue correctly 1`] = `<label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>Checkbox</span></label>`; exports[`renders ./components/checkbox/demo/basic.vue correctly 1`] = `<label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>Checkbox</span></label>`;
exports[`renders ./components/checkbox/demo/check-all.vue correctly 1`] = ` exports[`renders ./components/checkbox/demo/check-all.vue correctly 1`] = `
<div style="border-bottom: 1px solid #E9E9E9;"><label class="ant-checkbox-wrapper"><span class="ant-checkbox ant-checkbox-indeterminate"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span> Check all </span></label></div> <div><label class="ant-checkbox-wrapper"><span class="ant-checkbox ant-checkbox-indeterminate"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span> Check all </span></label></div>
<br> <div class="ant-divider ant-divider-horizontal" role="separator">
<!---->
</div>
<div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div> <div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div>
`; `;
@ -22,7 +24,7 @@ exports[`renders ./components/checkbox/demo/disabled.vue correctly 1`] = `
<!----> <!---->
</label> </label>
<br> <br>
<label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span> <label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled"><span class="ant-checkbox ant-checkbox-checked ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span>
<!----> <!---->
</label> </label>
`; `;
@ -30,15 +32,18 @@ exports[`renders ./components/checkbox/demo/disabled.vue correctly 1`] = `
exports[`renders ./components/checkbox/demo/group.vue correctly 1`] = ` exports[`renders ./components/checkbox/demo/group.vue correctly 1`] = `
<div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div> <div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input name="checkboxgroup" type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div>
<br> <br>
<br>
<div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div> <div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div>
<br> <br>
<br>
<div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div> <div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span>Apple</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked"><input type="checkbox" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-group-item"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div>
<br> <br>
<br>
<div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span><span style="color: red;">Apple</span></span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div> <div class="ant-checkbox-group"><label class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-checked ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Apple"><span class="ant-checkbox-inner"></span></span><span><span style="color: red;">Apple</span></span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Pear"><span class="ant-checkbox-inner"></span></span><span>Pear</span></label><label class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"><span class="ant-checkbox ant-checkbox-disabled"><input type="checkbox" disabled="" class="ant-checkbox-input" value="Orange"><span class="ant-checkbox-inner"></span></span><span>Orange</span></label></div>
`; `;
exports[`renders ./components/checkbox/demo/layout.vue correctly 1`] = ` exports[`renders ./components/checkbox/demo/layout.vue correctly 1`] = `
<div class="ant-checkbox-group"> <div class="ant-checkbox-group" style="width: 100%;">
<div class="ant-row"> <div class="ant-row">
<div class="ant-col ant-col-8"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="A"><span class="ant-checkbox-inner"></span></span><span>A</span></label></div> <div class="ant-col ant-col-8"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="A"><span class="ant-checkbox-inner"></span></span><span>A</span></label></div>
<div class="ant-col ant-col-8"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="B"><span class="ant-checkbox-inner"></span></span><span>B</span></label></div> <div class="ant-col ant-col-8"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input" value="B"><span class="ant-checkbox-inner"></span></span><span>B</span></label></div>

View File

@ -103,11 +103,10 @@ describe('CheckboxGroup', () => {
props: { options }, props: { options },
sync: false, sync: false,
}); });
expect(wrapper.vm.mergedValue).toEqual([]);
expect(wrapper.vm.sValue).toEqual([]);
wrapper.setProps({ value: ['Apple'] }); wrapper.setProps({ value: ['Apple'] });
await asyncExpect(() => { await asyncExpect(() => {
expect(wrapper.vm.sValue).toEqual(['Apple']); expect(wrapper.vm.mergedValue).toEqual(['Apple']);
}); });
}); });

View File

@ -17,7 +17,7 @@ The `indeterminate` property can help you to achieve a 'check all' effect.
</docs> </docs>
<template> <template>
<div :style="{ borderBottom: '1px solid #E9E9E9' }"> <div>
<a-checkbox <a-checkbox
v-model:checked="checkAll" v-model:checked="checkAll"
:indeterminate="indeterminate" :indeterminate="indeterminate"
@ -26,7 +26,7 @@ The `indeterminate` property can help you to achieve a 'check all' effect.
Check all Check all
</a-checkbox> </a-checkbox>
</div> </div>
<br /> <a-divider />
<a-checkbox-group v-model:value="checkedList" :options="plainOptions" /> <a-checkbox-group v-model:value="checkedList" :options="plainOptions" />
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -27,7 +27,7 @@ export default defineComponent({
setup() { setup() {
return { return {
checked1: ref(false), checked1: ref(false),
checked2: ref(false), checked2: ref(true),
}; };
}, },
}); });

View File

@ -19,10 +19,13 @@ Generate a group of checkboxes from an array
<template> <template>
<a-checkbox-group v-model:value="value1" name="checkboxgroup" :options="plainOptions" /> <a-checkbox-group v-model:value="value1" name="checkboxgroup" :options="plainOptions" />
<br /> <br />
<br />
<a-checkbox-group v-model:value="value2" :options="plainOptions" /> <a-checkbox-group v-model:value="value2" :options="plainOptions" />
<br /> <br />
<br />
<a-checkbox-group v-model:value="value3" :options="options" /> <a-checkbox-group v-model:value="value3" :options="options" />
<br /> <br />
<br />
<a-checkbox-group v-model:value="value4" :options="optionsWithDisabled" disabled> <a-checkbox-group v-model:value="value4" :options="optionsWithDisabled" disabled>
<template #label="{ value }"> <template #label="{ value }">
<span style="color: red">{{ value }}</span> <span style="color: red">{{ value }}</span>
@ -51,7 +54,6 @@ export default defineComponent({
value3: ['Pear'], value3: ['Pear'],
value4: ['Apple'], value4: ['Apple'],
}); });
return { return {
plainOptions, plainOptions,
options, options,

View File

@ -17,7 +17,7 @@ We can use Checkbox and Grid Checkbox.group, to implement complex layout
</docs> </docs>
<template> <template>
<a-checkbox-group v-model:value="value"> <a-checkbox-group v-model:value="value" style="width: 100%">
<a-row> <a-row>
<a-col :span="8"> <a-col :span="8">
<a-checkbox value="A">A</a-checkbox> <a-checkbox value="A">A</a-checkbox>
@ -41,8 +41,9 @@ We can use Checkbox and Grid Checkbox.group, to implement complex layout
import { defineComponent, ref } from 'vue'; import { defineComponent, ref } from 'vue';
export default defineComponent({ export default defineComponent({
setup() { setup() {
const value = ref([]);
return { return {
value: ref([]), value,
}; };
}, },
}); });

View File

@ -1,7 +1,8 @@
import type { App, Plugin } from 'vue'; import type { App, Plugin } from 'vue';
import Checkbox, { checkboxProps } from './Checkbox'; import Checkbox from './Checkbox';
import CheckboxGroup from './Group'; import CheckboxGroup from './Group';
export type { CheckboxProps } from './Checkbox'; export type { CheckboxProps, CheckboxGroupProps, CheckboxOptionType } from './interface';
export { checkboxProps, checkboxGroupProps } from './interface';
Checkbox.Group = CheckboxGroup; Checkbox.Group = CheckboxGroup;
@ -11,7 +12,7 @@ Checkbox.install = function (app: App) {
app.component(CheckboxGroup.name, CheckboxGroup); app.component(CheckboxGroup.name, CheckboxGroup);
return app; return app;
}; };
export { CheckboxGroup, checkboxProps }; export { CheckboxGroup };
export default Checkbox as typeof Checkbox & export default Checkbox as typeof Checkbox &
Plugin & { Plugin & {
readonly Group: typeof CheckboxGroup; readonly Group: typeof CheckboxGroup;

View File

@ -0,0 +1,78 @@
import type { ExtractPropTypes, InjectionKey, PropType, Ref } from 'vue';
import type { VueNode } from '../_util/type';
import PropTypes from '../_util/vue-types';
export type CheckboxValueType = string | number | boolean;
export interface CheckboxOptionType {
label?: VueNode;
value: CheckboxValueType;
disabled?: boolean;
indeterminate?: boolean;
onChange?: (e: Event) => void;
}
export const abstractCheckboxGroupProps = () => {
return {
name: String,
prefixCls: String,
options: {
type: Array as PropType<Array<CheckboxOptionType | string>>,
default: () => [] as Array<CheckboxOptionType | string>,
},
disabled: Boolean,
id: String,
};
};
export const checkboxGroupProps = () => {
return {
...abstractCheckboxGroupProps(),
defaultValue: { type: Array as PropType<Array<CheckboxValueType>> },
value: { type: Array as PropType<Array<CheckboxValueType>> },
onChange: { type: Function as PropType<(checkedValue: Array<CheckboxValueType>) => void> },
'onUpdate:value': {
type: Function as PropType<(checkedValue: Array<CheckboxValueType>) => void>,
},
};
};
export type CheckboxGroupProps = Partial<ExtractPropTypes<ReturnType<typeof checkboxGroupProps>>>;
export const abstractCheckboxProps = () => {
return {
prefixCls: String,
defaultChecked: { type: Boolean, default: undefined },
checked: { type: Boolean, default: undefined },
disabled: { type: Boolean, default: undefined },
isGroup: { type: Boolean, default: undefined },
value: PropTypes.any,
name: String,
id: String,
indeterminate: { type: Boolean, default: undefined },
type: { type: String, default: 'checkbox' },
autofocus: { type: Boolean, default: undefined },
onChange: PropTypes.func,
'onUpdate:checked': PropTypes.func,
skipGroup: { type: Boolean, default: false },
};
};
export const checkboxProps = () => {
return {
...abstractCheckboxProps(),
indeterminate: { type: Boolean, default: false },
};
};
export type CheckboxProps = Partial<ExtractPropTypes<ReturnType<typeof checkboxProps>>>;
export type CheckboxGroupContext = {
cancelValue: (id: Symbol) => void;
registerValue: (id: Symbol, value: string) => void;
toggleOption: (option: CheckboxOptionType) => void;
name: Ref<string>;
disabled: Ref<boolean>;
mergedValue: Ref<CheckboxValueType[]>;
};
export const CheckboxGroupContextKey: InjectionKey<CheckboxGroupContext> =
Symbol('CheckboxGroupContext');

View File

@ -2,3 +2,5 @@
@import './mixin'; @import './mixin';
.antCheckboxFn(); .antCheckboxFn();
@import './rtl';

View File

@ -7,11 +7,9 @@
.reset-component(); .reset-component();
position: relative; position: relative;
top: -0.09em; top: 0.2em;
display: inline-block;
line-height: 1; line-height: 1;
white-space: nowrap; white-space: nowrap;
vertical-align: middle;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
@ -28,7 +26,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 1px solid @checkbox-color; border: 1px solid @checkbox-color;
border-radius: @border-radius-sm; border-radius: @border-radius-base;
visibility: hidden; visibility: hidden;
animation: antCheckboxEffect 0.36s ease-in-out; animation: antCheckboxEffect 0.36s ease-in-out;
animation-fill-mode: backwards; animation-fill-mode: backwards;
@ -47,9 +45,10 @@
display: block; display: block;
width: @checkbox-size; width: @checkbox-size;
height: @checkbox-size; height: @checkbox-size;
direction: ltr;
background-color: @checkbox-check-bg; background-color: @checkbox-check-bg;
border: @checkbox-border-width @border-style-base @border-color-base; border: @checkbox-border-width @border-style-base @border-color-base;
border-radius: @border-radius-sm; border-radius: @checkbox-border-radius;
// Fix IE checked style // Fix IE checked style
// https://github.com/ant-design/ant-design/issues/12597 // https://github.com/ant-design/ant-design/issues/12597
border-collapse: separate; border-collapse: separate;
@ -61,6 +60,8 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
// https://github.com/ant-design/ant-design/pull/19452
// https://github.com/ant-design/ant-design/pull/31726
left: 21.5%; left: 21.5%;
display: table; display: table;
width: @check-width; width: @check-width;
@ -126,6 +127,7 @@
.@{checkbox-inner-prefix-cls} { .@{checkbox-inner-prefix-cls} {
background-color: @input-disabled-bg; background-color: @input-disabled-bg;
border-color: @border-color-base !important; border-color: @border-color-base !important;
&::after { &::after {
border-color: @input-disabled-bg; border-color: @input-disabled-bg;
border-collapse: separate; border-collapse: separate;
@ -147,13 +149,22 @@
.@{checkbox-prefix-cls}-wrapper { .@{checkbox-prefix-cls}-wrapper {
.reset-component(); .reset-component();
display: inline-flex;
display: inline-block; align-items: baseline;
line-height: unset; line-height: unset;
cursor: pointer; cursor: pointer;
&::after {
display: inline-block;
width: 0;
overflow: hidden;
content: '\a0';
}
&.@{checkbox-prefix-cls}-wrapper-disabled { &.@{checkbox-prefix-cls}-wrapper-disabled {
cursor: not-allowed; cursor: not-allowed;
} }
& + & { & + & {
margin-left: 8px; margin-left: 8px;
} }
@ -166,15 +177,16 @@
.@{checkbox-prefix-cls}-group { .@{checkbox-prefix-cls}-group {
.reset-component(); .reset-component();
display: inline-block; display: inline-block;
&-item { &-item {
display: inline-block; margin-right: @checkbox-group-item-margin-right;
margin-right: 8px;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
} }
} }
&-item + &-item { &-item + &-item {
margin-left: 0; margin-left: 0;
} }
@ -183,7 +195,7 @@
// 半选状态 // 半选状态
.@{checkbox-prefix-cls}-indeterminate { .@{checkbox-prefix-cls}-indeterminate {
.@{checkbox-inner-prefix-cls} { .@{checkbox-inner-prefix-cls} {
background-color: @component-background; background-color: @checkbox-check-bg;
border-color: @border-color-base; border-color: @border-color-base;
} }
.@{checkbox-inner-prefix-cls}::after { .@{checkbox-inner-prefix-cls}::after {
@ -213,6 +225,7 @@
transform: scale(1); transform: scale(1);
opacity: 0.5; opacity: 0.5;
} }
100% { 100% {
transform: scale(1.6); transform: scale(1.6);
opacity: 0; opacity: 0;

View File

@ -0,0 +1,28 @@
@import '../../style/mixins/index';
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-checkbox') {
.@{checkbox-prefix-cls}-rtl {
direction: rtl;
}
.@{checkbox-prefix-cls}-group {
&-item {
.@{checkbox-prefix-cls}-group-rtl & {
margin-right: 0;
margin-left: @checkbox-group-item-margin-right;
}
&:last-child {
.@{checkbox-prefix-cls}-group-rtl & {
margin-left: 0 !important;
}
}
}
&-item + &-item {
.@{checkbox-prefix-cls}-group-rtl & {
margin-left: @checkbox-group-item-margin-right;
}
}
}
}

View File

@ -40,6 +40,7 @@ export { default as Carousel } from './carousel';
export type { CascaderProps } from './cascader'; export type { CascaderProps } from './cascader';
export { default as Cascader } from './cascader'; export { default as Cascader } from './cascader';
export type { CheckboxProps, CheckboxGroupProps, CheckboxOptionType } from './checkbox';
export { default as Checkbox, CheckboxGroup } from './checkbox'; export { default as Checkbox, CheckboxGroup } from './checkbox';
export type { ColProps } from './col'; export type { ColProps } from './col';

View File

@ -17,7 +17,7 @@ const RadioGroupOptionTypes = tuple('default', 'button');
export type RadioGroupOption = typeof RadioGroupOptionTypes[number]; export type RadioGroupOption = typeof RadioGroupOptionTypes[number];
export type RadioGroupChildOption = { export type RadioGroupChildOption = {
label: any; label?: any;
value: any; value: any;
disabled?: boolean; disabled?: boolean;
}; };

View File

@ -1,7 +1,7 @@
import type { ExtractPropTypes } from 'vue'; import type { ExtractPropTypes } from 'vue';
import { defineComponent, inject, ref } from 'vue'; import { defineComponent, inject, ref } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import VcCheckbox from '../vc-checkbox'; import VcCheckbox from '../vc-checkbox/Checkbox';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import type { RadioChangeEvent, RadioGroupContext } from './interface'; import type { RadioChangeEvent, RadioGroupContext } from './interface';

View File

@ -241,6 +241,8 @@
@checkbox-check-color: #fff; @checkbox-check-color: #fff;
@checkbox-check-bg: @checkbox-check-color; @checkbox-check-bg: @checkbox-check-color;
@checkbox-border-width: @border-width-base; @checkbox-border-width: @border-width-base;
@checkbox-border-radius: @border-radius-base;
@checkbox-group-item-margin-right: 8px;
// Descriptions // Descriptions
@descriptions-bg: #fafafa; @descriptions-bg: #fafafa;

View File

@ -0,0 +1,157 @@
// based on rc-checkbox 2.3.2
import type { HTMLAttributes } from 'vue';
import { nextTick, defineComponent, ref, watch, onMounted } from 'vue';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
const checkboxProps = {
prefixCls: String,
name: String,
id: String,
type: String,
defaultChecked: { type: [Boolean, Number], default: undefined },
checked: { type: [Boolean, Number], default: undefined },
disabled: Boolean,
tabindex: { type: [Number, String] },
readonly: Boolean,
autofocus: Boolean,
value: PropTypes.any,
required: Boolean,
};
export default defineComponent({
name: 'Checkbox',
inheritAttrs: false,
props: initDefaultProps(checkboxProps, {
prefixCls: 'rc-checkbox',
type: 'checkbox',
defaultChecked: false,
}),
emits: ['click', 'change'],
setup(props, { attrs, emit, expose }) {
const checked = ref(props.checked === undefined ? props.defaultChecked : props.checked);
const inputRef = ref<HTMLInputElement>();
watch(
() => props.checked,
() => {
checked.value = props.checked;
},
);
onMounted(() => {
nextTick(() => {
if (process.env.NODE_ENV === 'test') {
if (props.autofocus) {
inputRef.value?.focus();
}
}
});
});
expose({
focus() {
inputRef.value?.focus();
},
blur() {
inputRef.value?.blur();
},
});
const eventShiftKey = ref();
const handleChange = e => {
if (props.disabled) {
return;
}
if (props.checked === undefined) {
checked.value = e.target.checked;
}
e.shiftKey = eventShiftKey.value;
const eventObj = {
target: {
...props,
checked: e.target.checked,
},
stopPropagation() {
e.stopPropagation();
},
preventDefault() {
e.preventDefault();
},
nativeEvent: e,
};
// fix https://github.com/vueComponent/ant-design-vue/issues/3047
//
if (props.checked !== undefined) {
inputRef.value.checked = !!props.checked;
}
emit('change', eventObj);
eventShiftKey.value = false;
};
const onClick = (e: MouseEvent) => {
emit('click', e);
// onChangeshiftKey使onClick hack
eventShiftKey.value = e.shiftKey;
};
return () => {
const {
prefixCls,
name,
id,
type,
disabled,
readonly,
tabindex,
autofocus,
value,
required,
...others
} = props;
const {
class: className,
onFocus,
onBlur,
onKeydown,
onKeypress,
onKeyup,
} = attrs as HTMLAttributes;
const globalProps = Object.keys({ ...others, ...attrs }).reduce((prev, key) => {
if (key.substr(0, 5) === 'aria-' || key.substr(0, 5) === 'data-' || key === 'role') {
prev[key] = others[key];
}
return prev;
}, {});
const classString = classNames(prefixCls, className, {
[`${prefixCls}-checked`]: checked.value,
[`${prefixCls}-disabled`]: disabled,
});
const inputProps = {
name,
id,
type,
readonly,
disabled,
tabindex,
class: `${prefixCls}-input`,
checked: !!checked.value,
autofocus,
value,
...globalProps,
onChange: handleChange,
onClick,
onFocus,
onBlur,
onKeydown,
onKeypress,
onKeyup,
required,
};
return (
<span class={classString}>
<input ref={inputRef} {...inputProps} />
<span class={`${prefixCls}-inner`} />
</span>
);
};
},
});

View File

@ -1,155 +0,0 @@
@checkboxWarpPrefixCls: rc-checkbox;
@checkboxInnerPrefixCls: ~'@{checkboxWarpPrefixCls}-inner';
/* 一般状态 */
.@{checkboxWarpPrefixCls} {
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-block;
position: relative;
line-height: 1;
vertical-align: middle;
&:hover .@{checkboxInnerPrefixCls},
&-input:focus + .@{checkboxInnerPrefixCls} {
border-color: #3dbcf6;
}
&-inner {
&:after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
position: absolute;
left: 4px;
top: 1px;
display: table;
width: 5px;
height: 8px;
border: 2px solid #ffffff;
border-top: 0;
border-left: 0;
content: ' ';
animation-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
animation-duration: 0.3s;
animation-name: amCheckboxOut;
}
position: relative;
top: 0;
left: 0;
display: inline-block;
width: 14px;
height: 14px;
border-width: 1px;
border-style: solid;
border-radius: 3px;
border-color: #d9d9d9;
background-color: #ffffff;
transition: border-color 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55),
background-color 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
&-input {
position: absolute;
left: 0;
z-index: 9999;
cursor: pointer;
opacity: 0;
top: 0;
bottom: 0;
right: 0;
}
}
/* 选中状态 */
.@{checkboxWarpPrefixCls}-checked {
&:hover {
.@{checkboxInnerPrefixCls} {
border-color: #3dbcf6;
}
}
.@{checkboxInnerPrefixCls} {
border-color: #3dbcf6;
background-color: #3dbcf6;
&:after {
transform: rotate(45deg);
position: absolute;
left: 4px;
top: 1px;
display: table;
width: 5px;
height: 8px;
border: 2px solid #ffffff;
border-top: 0;
border-left: 0;
content: ' ';
animation-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
animation-duration: 0.3s;
animation-name: amCheckboxOut;
}
}
}
.@{checkboxWarpPrefixCls}-disabled {
&.@{checkboxWarpPrefixCls}-checked {
&:hover {
.@{checkboxInnerPrefixCls} {
border-color: #d9d9d9;
}
}
.@{checkboxInnerPrefixCls} {
background-color: #f3f3f3;
border-color: #d9d9d9;
&:after {
animation-name: none;
border-color: #cccccc;
}
}
}
&:hover {
.@{checkboxInnerPrefixCls} {
border-color: #d9d9d9;
}
}
.@{checkboxInnerPrefixCls} {
border-color: #d9d9d9;
background-color: #f3f3f3;
&:after {
animation-name: none;
border-color: #f3f3f3;
}
}
.@{checkboxInnerPrefixCls}-input {
cursor: default;
}
}
@keyframes amCheckboxIn {
0% {
opacity: 0;
transform-origin: 50% 50%;
transform: scale(0, 0) rotate(45deg);
}
100% {
opacity: 1;
transform-origin: 50% 50%;
transform: scale(1, 1) rotate(45deg);
}
}
@keyframes amCheckboxOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -1,2 +0,0 @@
// based on rc-checkbox 2.1.7
export { default } from './src/';

View File

@ -1,143 +0,0 @@
import { nextTick, defineComponent } from 'vue';
import classNames from '../../_util/classNames';
import PropTypes, { withUndefined } from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
import { getOptionProps, hasProp, initDefaultProps } from '../../_util/props-util';
export default defineComponent({
name: 'Checkbox',
mixins: [BaseMixin],
inheritAttrs: false,
props: initDefaultProps(
{
prefixCls: PropTypes.string,
name: PropTypes.string,
id: PropTypes.string,
type: PropTypes.string,
defaultChecked: withUndefined(PropTypes.oneOfType([PropTypes.number, PropTypes.looseBool])),
checked: withUndefined(PropTypes.oneOfType([PropTypes.number, PropTypes.looseBool])),
disabled: PropTypes.looseBool,
// onFocus: PropTypes.func,
// onBlur: PropTypes.func,
// onChange: PropTypes.func,
// onClick: PropTypes.func,
tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
readonly: PropTypes.looseBool,
autofocus: PropTypes.looseBool,
value: PropTypes.any,
},
{
prefixCls: 'rc-checkbox',
type: 'checkbox',
defaultChecked: false,
},
),
data() {
const checked = hasProp(this, 'checked') ? this.checked : this.defaultChecked;
return {
sChecked: checked,
};
},
watch: {
checked(val) {
this.sChecked = val;
},
},
mounted() {
nextTick(() => {
if (process.env.NODE_ENV === 'test') {
if (this.autofocus) {
this.$refs.input && this.$refs.input.focus();
}
}
});
},
methods: {
focus() {
this.$refs.input.focus();
},
blur() {
this.$refs.input.blur();
},
handleChange(e) {
const props = getOptionProps(this);
if (props.disabled) {
return;
}
if (!('checked' in props)) {
this.sChecked = e.target.checked;
}
e.shiftKey = this.eventShiftKey;
const eventObj = {
target: {
...props,
checked: e.target.checked,
},
stopPropagation() {
e.stopPropagation();
},
preventDefault() {
e.preventDefault();
},
nativeEvent: e,
};
// fix https://github.com/vueComponent/ant-design-vue/issues/3047
//
if ('checked' in props) {
this.$refs.input.checked = props.checked;
}
this.__emit('change', eventObj);
this.eventShiftKey = false;
},
onClick(e) {
this.__emit('click', e);
// onChangeshiftKey使onClick hack
this.eventShiftKey = e.shiftKey;
},
},
render() {
const { prefixCls, name, id, type, disabled, readonly, tabindex, autofocus, value, ...others } =
getOptionProps(this);
const { class: className, onFocus, onBlur } = this.$attrs;
const globalProps = Object.keys({ ...others, ...this.$attrs }).reduce((prev, key) => {
if (key.substr(0, 5) === 'aria-' || key.substr(0, 5) === 'data-' || key === 'role') {
prev[key] = others[key];
}
return prev;
}, {});
const { sChecked } = this;
const classString = classNames(prefixCls, className, {
[`${prefixCls}-checked`]: sChecked,
[`${prefixCls}-disabled`]: disabled,
});
const inputProps = {
name,
id,
type,
readonly,
disabled,
tabindex,
class: `${prefixCls}-input`,
checked: !!sChecked,
autofocus,
value,
...globalProps,
onChange: this.handleChange,
onClick: this.onClick,
onFocus,
onBlur,
};
return (
<span class={classString}>
<input ref="input" {...inputProps} />
<span class={`${prefixCls}-inner`} />
</span>
);
},
});

View File

@ -1,3 +0,0 @@
import Checkbox from './Checkbox';
export default Checkbox;

View File

@ -1,2 +0,0 @@
import LazyLoad from './src/LazyLoad';
export default LazyLoad;

View File

@ -1,151 +0,0 @@
import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
import addEventListener from '../../vc-util/Dom/addEventListener';
import { initDefaultProps, findDOMNode, getSlot } from '../../_util/props-util';
import warning from '../../_util/warning';
import debounce from 'lodash-es/debounce';
import throttle from 'lodash-es/throttle';
import parentScroll from './utils/parentScroll';
import inViewport from './utils/inViewport';
import { watchEffect, defineComponent } from 'vue';
const lazyLoadProps = {
debounce: PropTypes.looseBool,
elementType: PropTypes.string,
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
offset: PropTypes.number,
offsetBottom: PropTypes.number,
offsetHorizontal: PropTypes.number,
offsetLeft: PropTypes.number,
offsetRight: PropTypes.number,
offsetTop: PropTypes.number,
offsetVertical: PropTypes.number,
threshold: PropTypes.number,
throttle: PropTypes.number,
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
export default defineComponent({
name: 'LazyLoad',
mixins: [BaseMixin],
inheritAttrs: false,
props: initDefaultProps(lazyLoadProps, {
elementType: 'div',
debounce: true,
offset: 0,
offsetBottom: 0,
offsetHorizontal: 0,
offsetLeft: 0,
offsetRight: 0,
offsetTop: 0,
offsetVertical: 0,
throttle: 250,
}),
data() {
if (this.throttle > 0) {
if (this.debounce) {
this.lazyLoadHandler = debounce(this.lazyLoadHandler, this.throttle);
} else {
this.lazyLoadHandler = throttle(this.lazyLoadHandler, this.throttle);
}
}
return {
visible: false,
};
},
mounted() {
this.$nextTick(() => {
watchEffect(() => {
if (!this.visible) {
this.lazyLoadHandler(this.$props);
}
});
const eventNode = this.getEventNode();
if (this.lazyLoadHandler.flush) {
this.lazyLoadHandler.flush();
}
this.resizeHander = addEventListener(window, 'resize', this.lazyLoadHandler);
this.scrollHander = addEventListener(eventNode, 'scroll', this.lazyLoadHandler);
});
},
beforeUnmount() {
if (this.lazyLoadHandler.cancel) {
this.lazyLoadHandler.cancel();
}
this.detachListeners();
},
methods: {
getEventNode() {
return parentScroll(findDOMNode(this));
},
getOffset() {
const {
offset,
offsetVertical,
offsetHorizontal,
offsetTop,
offsetBottom,
offsetLeft,
offsetRight,
threshold,
} = this.$props;
const _offsetAll = threshold || offset;
const _offsetVertical = offsetVertical || _offsetAll;
const _offsetHorizontal = offsetHorizontal || _offsetAll;
return {
top: offsetTop || _offsetVertical,
bottom: offsetBottom || _offsetVertical,
left: offsetLeft || _offsetHorizontal,
right: offsetRight || _offsetHorizontal,
};
},
lazyLoadHandler() {
if (!this._.isMounted) {
return;
}
const offset = this.getOffset();
const node = findDOMNode(this);
const eventNode = this.getEventNode();
if (inViewport(node, eventNode, offset)) {
this.setState({ visible: true }, () => {
this.__emit('contentVisible');
});
this.detachListeners();
}
},
detachListeners() {
this.resizeHander && this.resizeHander.remove();
this.scrollHander && this.scrollHander.remove();
},
},
render() {
const children = getSlot(this);
if (children.length !== 1) {
warning(false, 'lazyLoad组件只能包含一个子元素');
return null;
}
const { height, width, elementType: ElementType } = this.$props;
const { visible } = this;
const { class: className } = this.$attrs;
const elStyles = {
height: typeof height === 'number' ? height + 'px' : height,
width: typeof width === 'number' ? width + 'px' : width,
};
const elClasses = {
LazyLoad: true,
'is-visible': visible,
[className]: className,
};
return (
<ElementType class={elClasses} style={elStyles}>
{visible ? children[0] : null}
</ElementType>
);
},
});

View File

@ -1,12 +0,0 @@
/*
* Finds element's position relative to the whole document,
* rather than to the viewport as it is the case with .getBoundingClientRect().
*/
export default function getElementPosition(element) {
const rect = element.getBoundingClientRect();
return {
top: rect.top + window.pageYOffset,
left: rect.left + window.pageXOffset,
};
}

View File

@ -1,37 +0,0 @@
import getElementPosition from './getElementPosition';
const isHidden = element => element.offsetParent === null;
export default function inViewport(element, container, customOffset) {
if (isHidden(element)) {
return false;
}
let top;
let bottom;
let left;
let right;
if (typeof container === 'undefined' || container === window) {
top = window.pageYOffset;
left = window.pageXOffset;
bottom = top + window.innerHeight;
right = left + window.innerWidth;
} else {
const containerPosition = getElementPosition(container);
top = containerPosition.top;
left = containerPosition.left;
bottom = top + container.offsetHeight;
right = left + container.offsetWidth;
}
const elementPosition = getElementPosition(element);
return (
top <= elementPosition.top + element.offsetHeight + customOffset.top &&
bottom >= elementPosition.top - customOffset.bottom &&
left <= elementPosition.left + element.offsetWidth + customOffset.left &&
right >= elementPosition.left - customOffset.right
);
}

View File

@ -1,39 +0,0 @@
const style = (element, prop) => {
let styleVal = '';
if (typeof getComputedStyle !== 'undefined') {
styleVal = window.getComputedStyle(element, null).getPropertyValue(prop);
} else {
styleVal = element.style[prop];
}
return styleVal;
};
const overflow = element =>
style(element, 'overflow') + style(element, 'overflow-y') + style(element, 'overflow-x');
const scrollParent = element => {
if (!(element instanceof window.HTMLElement)) {
return window;
}
let parent = element;
while (parent) {
if (parent === document.body || parent === document.documentElement) {
break;
}
if (!parent.parentNode) {
break;
}
if (/(scroll|auto)/.test(overflow(parent))) {
return parent;
}
parent = parent.parentNode;
}
return window;
};
export default scrollParent;

View File

@ -1,3 +0,0 @@
// based on 2.0.0
import TouchFeedback from './src/TouchFeedback';
export default TouchFeedback;

View File

@ -1,13 +0,0 @@
import PropTypes from '../../_util/vue-types';
export const ITouchProps = {
disabled: PropTypes.looseBool,
activeClassName: PropTypes.string,
activeStyle: PropTypes.any,
// onTouchStart: PropTypes.func,
// onTouchEnd: PropTypes.func,
// onTouchCancel: PropTypes.func,
// onMouseDown: PropTypes.func,
// onMouseUp: PropTypes.func,
// onMouseLeave: PropTypes.func,
};

View File

@ -1,109 +0,0 @@
import classNames from '../../_util/classNames';
import { initDefaultProps, getSlot } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode';
import warning from '../../_util/warning';
import BaseMixin from '../../_util/BaseMixin';
import { ITouchProps } from './PropTypes';
import { defineComponent } from 'vue';
import supportsPassive from '../../_util/supportsPassive';
export default defineComponent({
name: 'TouchFeedback',
mixins: [BaseMixin],
inheritAttrs: false,
props: initDefaultProps(ITouchProps, {
disabled: false,
}),
data() {
this.child = null;
return {
active: false,
};
},
mounted() {
this.$nextTick(() => {
if (this.disabled && this.active) {
this.setState({
active: false,
});
}
});
},
methods: {
triggerEvent(type, isActive, ev) {
const eventType = `on${type}`;
const { child } = this;
if (child.props[eventType]) {
child.props[eventType](ev);
}
if (isActive !== this.active) {
this.setState({
active: isActive,
});
}
},
onTouchStart(e) {
this.triggerEvent('Touchstart', true, e);
},
onTouchMove(e) {
this.triggerEvent('Touchmove', false, e);
},
onTouchEnd(e) {
this.triggerEvent('Touchend', false, e);
},
onTouchCancel(e) {
this.triggerEvent('Touchcancel', false, e);
},
onMouseDown(e) {
// pc simulate mobile
this.triggerEvent('Mousedown', true, e);
},
onMouseUp(e) {
this.triggerEvent('Mouseup', false, e);
},
onMouseLeave(e) {
this.triggerEvent('Mouseleave', false, e);
},
},
render() {
const { disabled, activeClassName = '', activeStyle = {} } = this.$props;
let child = getSlot(this);
if (child.length !== 1) {
warning(false, 'm-feedback组件只能包含一个子元素');
return null;
}
const events = disabled
? undefined
: {
[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart']: this.onTouchStart,
[supportsPassive ? 'onTouchmovePassive' : 'onTouchmove']: this.onTouchMove,
onTouchend: this.onTouchEnd,
onTouchcancel: this.onTouchCancel,
onMousedown: this.onMouseDown,
onMouseup: this.onMouseUp,
onMouseleave: this.onMouseLeave,
};
child = child[0];
this.child = child;
if (!disabled && this.active) {
let { style, class: className } = child.props;
if (activeStyle !== false) {
if (activeStyle) {
style = { ...style, ...activeStyle };
}
className = classNames(className, activeClassName);
}
return cloneElement(child, {
class: className,
style,
...events,
});
}
return cloneElement(child, events);
},
});