"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
exports.selectProps = selectProps;
var _vue = require("vue");
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _BaseSelect = _interopRequireWildcard(require("./BaseSelect"));
var _OptionList = _interopRequireDefault(require("./OptionList"));
var _useOptions = _interopRequireDefault(require("./hooks/useOptions"));
var _SelectContext = require("./SelectContext");
var _useId = _interopRequireDefault(require("./hooks/useId"));
var _valueUtil = require("./utils/valueUtil");
var _warningPropsUtil = _interopRequireDefault(require("./utils/warningPropsUtil"));
var _commonUtil = require("./utils/commonUtil");
var _useFilterOptions = _interopRequireDefault(require("./hooks/useFilterOptions"));
var _useCache = _interopRequireDefault(require("./hooks/useCache"));
var _vueDemi = require("vue-demi");
var _vueTypes = _interopRequireDefault(require("../_util/vue-types"));
var _propsUtil = require("../_util/props-util");
var _useMergedState = _interopRequireDefault(require("../_util/hooks/useMergedState"));
var _useState = _interopRequireDefault(require("../_util/hooks/useState"));
var _toReactive = require("../_util/toReactive");
var _omit = _interopRequireDefault(require("../_util/omit"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
 * To match accessibility requirement, we always provide an input in the component.
 * Other element will not set `tabindex` to avoid `onBlur` sequence problem.
 * For focused select, we set `aria-live="polite"` to update the accessibility content.
 *
 * ref:
 * - keyboard: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role#Keyboard_interactions
 *
 * New api:
 * - listHeight
 * - listItemHeight
 * - component
 *
 * Remove deprecated api:
 * - multiple
 * - tags
 * - combobox
 * - firstActiveValue
 * - dropdownMenuStyle
 * - openClassName (Not list in api)
 *
 * Update:
 * - `backfill` only support `combobox` mode
 * - `combobox` mode not support `labelInValue` since it's meaningless
 * - `getInputElement` only support `combobox` mode
 * - `onChange` return OptionData instead of ReactNode
 * - `filterOption` `onChange` `onSelect` accept OptionData instead of ReactNode
 * - `combobox` mode trigger `onChange` will get `undefined` if no `value` match in Option
 * - `combobox` mode not support `optionLabelProp`
 */

const OMIT_DOM_PROPS = ['inputValue'];
function selectProps() {
  return (0, _extends2.default)((0, _extends2.default)({}, (0, _BaseSelect.baseSelectPropsWithoutPrivate)()), {
    prefixCls: String,
    id: String,
    backfill: {
      type: Boolean,
      default: undefined
    },
    // >>> Field Names
    fieldNames: Object,
    // >>> Search
    /** @deprecated Use `searchValue` instead */
    inputValue: String,
    searchValue: String,
    onSearch: Function,
    autoClearSearchValue: {
      type: Boolean,
      default: undefined
    },
    // >>> Select
    onSelect: Function,
    onDeselect: Function,
    // >>> Options
    /**
     * In Select, `false` means do nothing.
     * In TreeSelect, `false` will highlight match item.
     * It's by design.
     */
    filterOption: {
      type: [Boolean, Function],
      default: undefined
    },
    filterSort: Function,
    optionFilterProp: String,
    optionLabelProp: String,
    options: Array,
    defaultActiveFirstOption: {
      type: Boolean,
      default: undefined
    },
    virtual: {
      type: Boolean,
      default: undefined
    },
    listHeight: Number,
    listItemHeight: Number,
    // >>> Icon
    menuItemSelectedIcon: _vueTypes.default.any,
    mode: String,
    labelInValue: {
      type: Boolean,
      default: undefined
    },
    value: _vueTypes.default.any,
    defaultValue: _vueTypes.default.any,
    onChange: Function,
    children: Array
  });
}
function isRawValue(value) {
  return !value || typeof value !== 'object';
}
var _default = exports.default = (0, _vueDemi.defineComponent)({
  compatConfig: {
    MODE: 3
  },
  name: 'VcSelect',
  inheritAttrs: false,
  props: (0, _propsUtil.initDefaultProps)(selectProps(), {
    prefixCls: 'vc-select',
    autoClearSearchValue: true,
    listHeight: 200,
    listItemHeight: 20,
    dropdownMatchSelectWidth: true
  }),
  setup(props, _ref) {
    let {
      expose,
      attrs,
      slots
    } = _ref;
    const mergedId = (0, _useId.default)((0, _vueDemi.toRef)(props, 'id'));
    const multiple = (0, _vueDemi.computed)(() => (0, _BaseSelect.isMultiple)(props.mode));
    const childrenAsData = (0, _vueDemi.computed)(() => !!(!props.options && props.children));
    const mergedFilterOption = (0, _vueDemi.computed)(() => {
      if (props.filterOption === undefined && props.mode === 'combobox') {
        return false;
      }
      return props.filterOption;
    });
    // ========================= FieldNames =========================
    const mergedFieldNames = (0, _vueDemi.computed)(() => (0, _valueUtil.fillFieldNames)(props.fieldNames, childrenAsData.value));
    // =========================== Search ===========================
    const [mergedSearchValue, setSearchValue] = (0, _useMergedState.default)('', {
      value: (0, _vueDemi.computed)(() => props.searchValue !== undefined ? props.searchValue : props.inputValue),
      postState: search => search || ''
    });
    // =========================== Option ===========================
    const parsedOptions = (0, _useOptions.default)((0, _vueDemi.toRef)(props, 'options'), (0, _vueDemi.toRef)(props, 'children'), mergedFieldNames);
    const {
      valueOptions,
      labelOptions,
      options: mergedOptions
    } = parsedOptions;
    // ========================= Wrap Value =========================
    const convert2LabelValues = draftValues => {
      // Convert to array
      const valueList = (0, _commonUtil.toArray)(draftValues);
      // Convert to labelInValue type
      return valueList.map(val => {
        var _a, _b;
        let rawValue;
        let rawLabel;
        let rawKey;
        let rawDisabled;
        // Fill label & value
        if (isRawValue(val)) {
          rawValue = val;
        } else {
          rawKey = val.key;
          rawLabel = val.label;
          rawValue = (_a = val.value) !== null && _a !== void 0 ? _a : rawKey;
        }
        const option = valueOptions.value.get(rawValue);
        if (option) {
          // Fill missing props
          if (rawLabel === undefined) rawLabel = option === null || option === void 0 ? void 0 : option[props.optionLabelProp || mergedFieldNames.value.label];
          if (rawKey === undefined) rawKey = (_b = option === null || option === void 0 ? void 0 : option.key) !== null && _b !== void 0 ? _b : rawValue;
          rawDisabled = option === null || option === void 0 ? void 0 : option.disabled;
          // Warning if label not same as provided
          // if (process.env.NODE_ENV !== 'production' && !isRawValue(val)) {
          //   const optionLabel = option?.[mergedFieldNames.value.label];
          //   if (optionLabel !== undefined && optionLabel !== rawLabel) {
          //     warning(false, '`label` of `value` is not same as `label` in Select options.');
          //   }
          // }
        }
        return {
          label: rawLabel,
          value: rawValue,
          key: rawKey,
          disabled: rawDisabled,
          option
        };
      });
    };
    // =========================== Values ===========================
    const [internalValue, setInternalValue] = (0, _useMergedState.default)(props.defaultValue, {
      value: (0, _vueDemi.toRef)(props, 'value')
    });
    // Merged value with LabelValueType
    const rawLabeledValues = (0, _vueDemi.computed)(() => {
      var _a;
      const values = convert2LabelValues(internalValue.value);
      // combobox no need save value when it's empty
      if (props.mode === 'combobox' && !((_a = values[0]) === null || _a === void 0 ? void 0 : _a.value)) {
        return [];
      }
      return values;
    });
    // Fill label with cache to avoid option remove
    const [mergedValues, getMixedOption] = (0, _useCache.default)(rawLabeledValues, valueOptions);
    const displayValues = (0, _vueDemi.computed)(() => {
      // `null` need show as placeholder instead
      // https://github.com/ant-design/ant-design/issues/25057
      if (!props.mode && mergedValues.value.length === 1) {
        const firstValue = mergedValues.value[0];
        if (firstValue.value === null && (firstValue.label === null || firstValue.label === undefined)) {
          return [];
        }
      }
      return mergedValues.value.map(item => {
        var _a;
        return (0, _extends2.default)((0, _extends2.default)({}, item), {
          label: (_a = typeof item.label === 'function' ? item.label() : item.label) !== null && _a !== void 0 ? _a : item.value
        });
      });
    });
    /** Convert `displayValues` to raw value type set */
    const rawValues = (0, _vueDemi.computed)(() => new Set(mergedValues.value.map(val => val.value)));
    (0, _vueDemi.watchEffect)(() => {
      var _a;
      if (props.mode === 'combobox') {
        const strValue = (_a = mergedValues.value[0]) === null || _a === void 0 ? void 0 : _a.value;
        if (strValue !== undefined && strValue !== null) {
          setSearchValue(String(strValue));
        }
      }
    }, {
      flush: 'post'
    });
    // ======================= Display Option =======================
    // Create a placeholder item if not exist in `options`
    const createTagOption = (val, label) => {
      const mergedLabel = label !== null && label !== void 0 ? label : val;
      return {
        [mergedFieldNames.value.value]: val,
        [mergedFieldNames.value.label]: mergedLabel
      };
    };
    // Fill tag as option if mode is `tags`
    const filledTagOptions = (0, _vueDemi.shallowRef)();
    (0, _vueDemi.watchEffect)(() => {
      if (props.mode !== 'tags') {
        filledTagOptions.value = mergedOptions.value;
        return;
      }
      // >>> Tag mode
      const cloneOptions = mergedOptions.value.slice();
      // Check if value exist in options (include new patch item)
      const existOptions = val => valueOptions.value.has(val);
      // Fill current value as option
      [...mergedValues.value].sort((a, b) => a.value < b.value ? -1 : 1).forEach(item => {
        const val = item.value;
        if (!existOptions(val)) {
          cloneOptions.push(createTagOption(val, item.label));
        }
      });
      filledTagOptions.value = cloneOptions;
    });
    const filteredOptions = (0, _useFilterOptions.default)(filledTagOptions, mergedFieldNames, mergedSearchValue, mergedFilterOption, (0, _vueDemi.toRef)(props, 'optionFilterProp'));
    // Fill options with search value if needed
    const filledSearchOptions = (0, _vueDemi.computed)(() => {
      if (props.mode !== 'tags' || !mergedSearchValue.value || filteredOptions.value.some(item => item[props.optionFilterProp || 'value'] === mergedSearchValue.value)) {
        return filteredOptions.value;
      }
      // Fill search value as option
      return [createTagOption(mergedSearchValue.value), ...filteredOptions.value];
    });
    const orderedFilteredOptions = (0, _vueDemi.computed)(() => {
      if (!props.filterSort) {
        return filledSearchOptions.value;
      }
      return [...filledSearchOptions.value].sort((a, b) => props.filterSort(a, b));
    });
    const displayOptions = (0, _vueDemi.computed)(() => (0, _valueUtil.flattenOptions)(orderedFilteredOptions.value, {
      fieldNames: mergedFieldNames.value,
      childrenAsData: childrenAsData.value
    }));
    // =========================== Change ===========================
    const triggerChange = values => {
      const labeledValues = convert2LabelValues(values);
      setInternalValue(labeledValues);
      if (props.onChange && (
      // Trigger event only when value changed
      labeledValues.length !== mergedValues.value.length || labeledValues.some((newVal, index) => {
        var _a;
        return ((_a = mergedValues.value[index]) === null || _a === void 0 ? void 0 : _a.value) !== (newVal === null || newVal === void 0 ? void 0 : newVal.value);
      }))) {
        const returnValues = props.labelInValue ? labeledValues.map(v => {
          return (0, _extends2.default)((0, _extends2.default)({}, v), {
            originLabel: v.label,
            label: typeof v.label === 'function' ? v.label() : v.label
          });
        }) : labeledValues.map(v => v.value);
        const returnOptions = labeledValues.map(v => (0, _valueUtil.injectPropsWithOption)(getMixedOption(v.value)));
        props.onChange(
        // Value
        multiple.value ? returnValues : returnValues[0],
        // Option
        multiple.value ? returnOptions : returnOptions[0]);
      }
    };
    // ======================= Accessibility ========================
    const [activeValue, setActiveValue] = (0, _useState.default)(null);
    const [accessibilityIndex, setAccessibilityIndex] = (0, _useState.default)(0);
    const mergedDefaultActiveFirstOption = (0, _vueDemi.computed)(() => props.defaultActiveFirstOption !== undefined ? props.defaultActiveFirstOption : props.mode !== 'combobox');
    const onActiveValue = function (active, index) {
      let {
        source = 'keyboard'
      } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      setAccessibilityIndex(index);
      if (props.backfill && props.mode === 'combobox' && active !== null && source === 'keyboard') {
        setActiveValue(String(active));
      }
    };
    // ========================= OptionList =========================
    const triggerSelect = (val, selected) => {
      const getSelectEnt = () => {
        var _a;
        const option = getMixedOption(val);
        const originLabel = option === null || option === void 0 ? void 0 : option[mergedFieldNames.value.label];
        return [props.labelInValue ? {
          label: typeof originLabel === 'function' ? originLabel() : originLabel,
          originLabel,
          value: val,
          key: (_a = option === null || option === void 0 ? void 0 : option.key) !== null && _a !== void 0 ? _a : val
        } : val, (0, _valueUtil.injectPropsWithOption)(option)];
      };
      if (selected && props.onSelect) {
        const [wrappedValue, option] = getSelectEnt();
        props.onSelect(wrappedValue, option);
      } else if (!selected && props.onDeselect) {
        const [wrappedValue, option] = getSelectEnt();
        props.onDeselect(wrappedValue, option);
      }
    };
    // Used for OptionList selection
    const onInternalSelect = (val, info) => {
      let cloneValues;
      // Single mode always trigger select only with option list
      const mergedSelect = multiple.value ? info.selected : true;
      if (mergedSelect) {
        cloneValues = multiple.value ? [...mergedValues.value, val] : [val];
      } else {
        cloneValues = mergedValues.value.filter(v => v.value !== val);
      }
      triggerChange(cloneValues);
      triggerSelect(val, mergedSelect);
      // Clean search value if single or configured
      if (props.mode === 'combobox') {
        // setSearchValue(String(val));
        setActiveValue('');
      } else if (!multiple.value || props.autoClearSearchValue) {
        setSearchValue('');
        setActiveValue('');
      }
    };
    // ======================= Display Change =======================
    // BaseSelect display values change
    const onDisplayValuesChange = (nextValues, info) => {
      triggerChange(nextValues);
      if (info.type === 'remove' || info.type === 'clear') {
        info.values.forEach(item => {
          triggerSelect(item.value, false);
        });
      }
    };
    // =========================== Search ===========================
    const onInternalSearch = (searchText, info) => {
      var _a;
      setSearchValue(searchText);
      setActiveValue(null);
      // [Submit] Tag mode should flush input
      if (info.source === 'submit') {
        const formatted = (searchText || '').trim();
        // prevent empty tags from appearing when you click the Enter button
        if (formatted) {
          const newRawValues = Array.from(new Set([...rawValues.value, formatted]));
          triggerChange(newRawValues);
          triggerSelect(formatted, true);
          setSearchValue('');
        }
        return;
      }
      if (info.source !== 'blur') {
        if (props.mode === 'combobox') {
          triggerChange(searchText);
        }
        (_a = props.onSearch) === null || _a === void 0 ? void 0 : _a.call(props, searchText);
      }
    };
    const onInternalSearchSplit = words => {
      let patchValues = words;
      if (props.mode !== 'tags') {
        patchValues = words.map(word => {
          const opt = labelOptions.value.get(word);
          return opt === null || opt === void 0 ? void 0 : opt.value;
        }).filter(val => val !== undefined);
      }
      const newRawValues = Array.from(new Set([...rawValues.value, ...patchValues]));
      triggerChange(newRawValues);
      newRawValues.forEach(newRawValue => {
        triggerSelect(newRawValue, true);
      });
    };
    const realVirtual = (0, _vueDemi.computed)(() => props.virtual !== false && props.dropdownMatchSelectWidth !== false);
    (0, _SelectContext.useProvideSelectProps)((0, _toReactive.toReactive)((0, _extends2.default)((0, _extends2.default)({}, parsedOptions), {
      flattenOptions: displayOptions,
      onActiveValue,
      defaultActiveFirstOption: mergedDefaultActiveFirstOption,
      onSelect: onInternalSelect,
      menuItemSelectedIcon: (0, _vueDemi.toRef)(props, 'menuItemSelectedIcon'),
      rawValues,
      fieldNames: mergedFieldNames,
      virtual: realVirtual,
      listHeight: (0, _vueDemi.toRef)(props, 'listHeight'),
      listItemHeight: (0, _vueDemi.toRef)(props, 'listItemHeight'),
      childrenAsData
    })));
    // ========================== Warning ===========================
    if (process.env.NODE_ENV !== 'production') {
      (0, _vueDemi.watchEffect)(() => {
        (0, _warningPropsUtil.default)(props);
      }, {
        flush: 'post'
      });
    }
    const selectRef = (0, _vueDemi.ref)();
    expose({
      focus() {
        var _a;
        (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.focus();
      },
      blur() {
        var _a;
        (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.blur();
      },
      scrollTo(arg) {
        var _a;
        (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.scrollTo(arg);
      }
    });
    const pickProps = (0, _vueDemi.computed)(() => {
      return (0, _omit.default)(props, ['id', 'mode', 'prefixCls', 'backfill', 'fieldNames',
      // Search
      'inputValue', 'searchValue', 'onSearch', 'autoClearSearchValue',
      // Select
      'onSelect', 'onDeselect', 'dropdownMatchSelectWidth',
      // Options
      'filterOption', 'filterSort', 'optionFilterProp', 'optionLabelProp', 'options', 'children', 'defaultActiveFirstOption', 'menuItemSelectedIcon', 'virtual', 'listHeight', 'listItemHeight',
      // Value
      'value', 'defaultValue', 'labelInValue', 'onChange']);
    });
    return () => {
      return (0, _vue.createVNode)(_BaseSelect.default, (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, pickProps.value), attrs), {}, {
        "id": mergedId,
        "prefixCls": props.prefixCls,
        "ref": selectRef,
        "omitDomProps": OMIT_DOM_PROPS,
        "mode": props.mode,
        "displayValues": displayValues.value,
        "onDisplayValuesChange": onDisplayValuesChange,
        "searchValue": mergedSearchValue.value,
        "onSearch": onInternalSearch,
        "onSearchSplit": onInternalSearchSplit,
        "dropdownMatchSelectWidth": props.dropdownMatchSelectWidth,
        "OptionList": _OptionList.default,
        "emptyOptions": !displayOptions.value.length,
        "activeValue": activeValue.value,
        "activeDescendantId": `${mergedId}_list_${accessibilityIndex.value}`
      }), slots);
    };
  }
});