diff --git a/components/vc-select/DropdownMenu.vue b/components/vc-select/DropdownMenu.vue index e69de29bb..35bf7f5ef 100644 --- a/components/vc-select/DropdownMenu.vue +++ b/components/vc-select/DropdownMenu.vue @@ -0,0 +1,186 @@ + diff --git a/components/vc-select/OptGroup.vue b/components/vc-select/OptGroup.vue new file mode 100644 index 000000000..3d7d08eea --- /dev/null +++ b/components/vc-select/OptGroup.vue @@ -0,0 +1,5 @@ + diff --git a/components/vc-select/Option.vue b/components/vc-select/Option.vue new file mode 100644 index 000000000..af6f3ab5b --- /dev/null +++ b/components/vc-select/Option.vue @@ -0,0 +1,13 @@ + diff --git a/components/vc-select/PropTypes.js b/components/vc-select/PropTypes.js new file mode 100644 index 000000000..6b6b2bc18 --- /dev/null +++ b/components/vc-select/PropTypes.js @@ -0,0 +1,84 @@ +import PropTypes from '../_util/vue-types' + +function valueType (props, propName, componentName) { + const basicType = PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]) + + const labelInValueShape = PropTypes.shape({ + key: basicType.isRequired, + label: PropTypes.node, + }) + if (props.labelInValue) { + const validate = PropTypes.oneOfType([ + PropTypes.arrayOf(labelInValueShape), + labelInValueShape, + ]) + const error = validate(...arguments) + if (error) { + return new Error( + `Invalid prop \`${propName}\` supplied to \`${componentName}\`, ` + + `when you set \`labelInValue\` to \`true\`, \`${propName}\` should in ` + + `shape of \`{ key: string | number, label?: ReactNode }\`.` + ) + } + } else if ( + (props.mode === 'multiple' || props.mode === 'tags' || props.multiple || props.tags) && + props[propName] === '' + ) { + return new Error( + `Invalid prop \`${propName}\` of type \`string\` supplied to \`${componentName}\`, ` + + `expected \`array\` when \`multiple\` or \`tags\` is \`true\`.` + ) + } else { + const validate = PropTypes.oneOfType([ + PropTypes.arrayOf(basicType), + basicType, + ]) + return validate(...arguments) + } +} + +export const SelectPropTypes = { + defaultActiveFirstOption: PropTypes.bool, + multiple: PropTypes.bool, + filterOption: PropTypes.any, + // children: PropTypes.any, + showSearch: PropTypes.bool, + disabled: PropTypes.bool, + allowClear: PropTypes.bool, + showArrow: PropTypes.bool, + tags: PropTypes.bool, + prefixCls: PropTypes.string, + // className: PropTypes.string, + transitionName: PropTypes.string, + optionLabelProp: PropTypes.string, + optionFilterProp: PropTypes.string, + animation: PropTypes.string, + choiceTransitionName: PropTypes.string, + // onChange: PropTypes.func, + // onBlur: PropTypes.func, + // onFocus: PropTypes.func, + // onSelect: PropTypes.func, + // onSearch: PropTypes.func, + // onPopupScroll: PropTypes.func, + // onMouseEnter: PropTypes.func, + // onMouseLeave: PropTypes.func, + // onInputKeyDown: PropTypes.func, + placeholder: PropTypes.any, + // onDeselect: PropTypes.func, + labelInValue: PropTypes.bool, + value: valueType, + defaultValue: valueType, + dropdownStyle: PropTypes.object, + maxTagTextLength: PropTypes.number, + maxTagCount: PropTypes.number, + maxTagPlaceholder: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.func, + ]), + tokenSeparators: PropTypes.arrayOf(PropTypes.string), + getInputElement: PropTypes.func, + showAction: PropTypes.arrayOf(PropTypes.string), +} diff --git a/components/vc-select/Select.vue b/components/vc-select/Select.vue new file mode 100644 index 000000000..e69de29bb diff --git a/components/vc-select/SelectTrigger.vue b/components/vc-select/SelectTrigger.vue new file mode 100644 index 000000000..e69de29bb diff --git a/components/vc-select/util.js b/components/vc-select/util.js new file mode 100644 index 000000000..24e88768c --- /dev/null +++ b/components/vc-select/util.js @@ -0,0 +1,165 @@ +export function getValuePropValue (child) { + const props = child.props + if ('value' in props) { + return props.value + } + if (child.key) { + return child.key + } + if (child.type && child.type.isSelectOptGroup && props.label) { + return props.label + } + throw new Error( + `Need at least a key or a value or a label (only for OptGroup) for ${child}` + ) +} + +export function getPropValue (child, prop) { + if (prop === 'value') { + return getValuePropValue(child) + } + return child.props[prop] +} + +export function isMultiple (props) { + return props.multiple +} + +export function isCombobox (props) { + return props.combobox +} + +export function isMultipleOrTags (props) { + return props.multiple || props.tags +} + +export function isMultipleOrTagsOrCombobox (props) { + return isMultipleOrTags(props) || isCombobox(props) +} + +export function isSingleMode (props) { + return !isMultipleOrTagsOrCombobox(props) +} + +export function toArray (value) { + let ret = value + if (value === undefined) { + ret = [] + } else if (!Array.isArray(value)) { + ret = [value] + } + return ret +} + +export function preventDefaultEvent (e) { + e.preventDefault() +} + +export function findIndexInValueByKey (value, key) { + let index = -1 + for (let i = 0; i < value.length; i++) { + if (value[i].key === key) { + index = i + break + } + } + return index +} + +export function findIndexInValueByLabel (value, label) { + let index = -1 + for (let i = 0; i < value.length; i++) { + if (toArray(value[i].label).join('') === label) { + index = i + break + } + } + return index +} + +export function getSelectKeys (menuItems, value) { + if (value === null || value === undefined) { + return [] + } + let selectedKeys = [] + menuItems.forEach(item => { + if (item.type.isMenuItemGroup) { + selectedKeys = selectedKeys.concat( + getSelectKeys(item.props.children, value) + ) + } else { + const itemValue = getValuePropValue(item) + const itemKey = item.key + if (findIndexInValueByKey(value, itemValue) !== -1 && itemKey) { + selectedKeys.push(itemKey) + } + } + }) + return selectedKeys +} + +export const UNSELECTABLE_STYLE = { + userSelect: 'none', + WebkitUserSelect: 'none', +} + +export const UNSELECTABLE_ATTRIBUTE = { + unselectable: 'unselectable', +} + +export function findFirstMenuItem (children) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + if (child.type.isMenuItemGroup) { + const found = findFirstMenuItem(child.props.children) + if (found) { + return found + } + } else if (!child.props.disabled) { + return child + } + } + return null +} + +export function includesSeparators (string, separators) { + for (let i = 0; i < separators.length; ++i) { + if (string.lastIndexOf(separators[i]) > 0) { + return true + } + } + return false +} + +export function splitBySeparators (string, separators) { + const reg = new RegExp(`[${separators.join()}]`) + return string.split(reg).filter(token => token) +} + +export function defaultFilterFn (input, child) { + if (child.props.disabled) { + return false + } + const value = String(getPropValue(child, this.props.optionFilterProp)) + return ( + value.toLowerCase().indexOf(input.toLowerCase()) > -1 + ) +} + +export function validateOptionValue (value, props) { + if (isSingleMode(props) || isMultiple(props)) { + return + } + if (typeof value !== 'string') { + throw new Error( + `Invalid \`value\` of type \`${typeof value}\` supplied to Option, ` + + `expected \`string\` when \`tags/combobox\` is \`true\`.` + ) + } +} + +export function saveRef (instance, name) { + return (node) => { + instance[name] = node + } +}