<script setup>
import { computed, ref, watch, nextTick, provide, onMounted, onBeforeUnmount } from "vue";
import { useNamespace, useFormItem, useId, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { useConfig } from "@yh-ui/hooks";
import YhTooltip from "../../tooltip";
import { CascaderContextKey, defaultCascaderConfig } from "./cascader";
import CascaderPanel from "./cascader-panel.vue";
defineOptions({
  name: "YhCascader"
});
const props = defineProps({
  modelValue: { type: [String, Number, Array], required: false },
  options: { type: Array, required: false },
  placeholder: { type: String, required: false },
  disabled: { type: Boolean, required: false, default: false },
  clearable: { type: Boolean, required: false, default: false },
  size: { type: String, required: false, default: void 0 },
  filterable: { type: Boolean, required: false, default: false },
  filterMethod: { type: Function, required: false },
  separator: { type: String, required: false, default: " / " },
  showAllLevels: { type: Boolean, required: false, default: true },
  collapseTags: { type: Boolean, required: false, default: false },
  collapseTagsTooltip: { type: Boolean, required: false, default: false },
  maxCollapseTags: { type: Number, required: false, default: 1 },
  multiple: { type: Boolean, required: false, default: false },
  checkStrictly: { type: Boolean, required: false, default: false },
  expandTrigger: { type: String, required: false, default: void 0 },
  emitPath: { type: Boolean, required: false, default: true },
  virtual: { type: Boolean, required: false, default: false },
  virtualItemHeight: { type: Number, required: false, default: 34 },
  props: { type: Object, required: false },
  popperClass: { type: String, required: false },
  teleported: { type: Boolean, required: false, default: true },
  tagType: { type: String, required: false, default: "" },
  validateEvent: { type: Boolean, required: false, default: true },
  themeOverrides: { type: null, required: false }
});
const emit = defineEmits(["update:modelValue", "change", "focus", "blur", "clear", "expand-change", "visible-change"]);
const ns = useNamespace("cascader");
const { t } = useLocale();
const inputId = useId();
const { themeStyle } = useComponentTheme(
  "cascader",
  computed(() => props.themeOverrides)
);
const { form, formItem, validate: triggerValidate } = useFormItem();
const { globalSize } = useConfig();
const cascaderSize = computed(
  () => props.size || formItem?.size || form?.size || globalSize.value || "default"
);
const config = computed(() => ({
  ...defaultCascaderConfig,
  ...props.props,
  // 直接属性覆盖
  multiple: props.multiple || props.props?.multiple || false,
  checkStrictly: props.checkStrictly || props.props?.checkStrictly || false,
  expandTrigger: props.expandTrigger || props.props?.expandTrigger || "click",
  emitPath: props.emitPath ?? props.props?.emitPath ?? true
}));
const wrapperRef = ref();
const inputRef = ref();
const dropdownRef = ref();
const visible = ref(false);
const focused = ref(false);
const hovering = ref(false);
const query = ref("");
const expandedPath = ref([]);
const isClickingDropdown = ref(false);
const dropdownStyle = ref({});
const updateDropdownPosition = () => {
  if (typeof window === "undefined" || !wrapperRef.value || !props.teleported) return;
  const rect = wrapperRef.value.getBoundingClientRect();
  const styles = window.getComputedStyle(wrapperRef.value);
  const primary = styles.getPropertyValue("--yh-color-primary").trim();
  const primaryRgb = styles.getPropertyValue("--yh-color-primary-rgb").trim();
  const primaryLight9 = styles.getPropertyValue("--yh-color-primary-light-9").trim();
  dropdownStyle.value = {
    position: "fixed",
    top: `${rect.bottom + 4}px`,
    left: `${rect.left}px`,
    minWidth: `${rect.width}px`,
    zIndex: "2000",
    "--yh-color-primary": primary,
    "--yh-color-primary-rgb": primaryRgb,
    "--yh-color-primary-light-9": primaryLight9
  };
};
watch(visible, (val) => {
  if (val && props.teleported) {
    nextTick(updateDropdownPosition);
  }
});
onMounted(() => {
  if (typeof window !== "undefined") {
    if (props.teleported) {
      window.addEventListener("scroll", updateDropdownPosition, true);
      window.addEventListener("resize", updateDropdownPosition);
    }
  }
});
onBeforeUnmount(() => {
  if (typeof window !== "undefined") {
    if (props.teleported) {
      window.removeEventListener("scroll", updateDropdownPosition, true);
      window.removeEventListener("resize", updateDropdownPosition);
    }
  }
});
const isMultiple = computed(() => config.value.multiple);
const getPathLabels = (path) => {
  const labels = [];
  let currentOptions = props.options || [];
  for (const value of path) {
    const valKey = config.value.value;
    const labelKey = config.value.label;
    const childrenKey = config.value.children;
    const option = currentOptions.find((o) => o[valKey] === value);
    if (option) {
      labels.push(String(option[labelKey] || ""));
      currentOptions = option[childrenKey] || [];
    }
  }
  return labels;
};
const findPathByValue = (targetValue, options) => {
  const valKey = config.value.value;
  const childrenKey = config.value.children;
  for (const option of options) {
    const val = option[valKey];
    const children = option[childrenKey];
    if (val === targetValue) return [val];
    if (children && children.length > 0) {
      const path = findPathByValue(targetValue, children);
      if (path) return [val, ...path];
    }
  }
  return null;
};
const presentText = computed(() => {
  if (isMultiple.value) return "";
  const value = props.modelValue;
  if (value === void 0 || value === null) return "";
  let path = [];
  if (Array.isArray(value)) {
    path = value;
  } else {
    path = findPathByValue(value, props.options || []) || [];
  }
  if (path.length === 0) return "";
  const labels = getPathLabels(path);
  return props.showAllLevels ? labels.join(props.separator) : labels[labels.length - 1];
});
const presentTags = computed(() => {
  if (!isMultiple.value) return [];
  const values = props.modelValue;
  if (!Array.isArray(values) || values.length === 0) return [];
  return values.map((v) => {
    let path = [];
    if (Array.isArray(v)) {
      path = v;
    } else {
      path = findPathByValue(v, props.options || []) || [];
    }
    const labels = getPathLabels(path);
    return {
      path,
      text: props.showAllLevels ? labels.join(props.separator) : labels[labels.length - 1]
    };
  });
});
const displayTags = computed(() => {
  if (props.collapseTags) {
    return presentTags.value.slice(0, props.maxCollapseTags);
  }
  return presentTags.value;
});
const collapsedCount = computed(() => {
  if (!isMultiple.value || !props.collapseTags) return 0;
  return Math.max(0, presentTags.value.length - props.maxCollapseTags);
});
const collapsedTags = computed(() => {
  if (!isMultiple.value || !props.collapseTags) return [];
  return presentTags.value.slice(props.maxCollapseTags);
});
const showClear = computed(() => {
  if (!props.clearable || props.disabled || !hovering.value) return false;
  if (isMultiple.value) {
    return Array.isArray(props.modelValue) && props.modelValue.length > 0;
  } else {
    return props.modelValue !== void 0 && props.modelValue !== null && (Array.isArray(props.modelValue) ? props.modelValue.length > 0 : true);
  }
});
const hasValue = computed(() => {
  if (isMultiple.value) {
    return Array.isArray(props.modelValue) && props.modelValue.length > 0;
  }
  return props.modelValue !== void 0 && props.modelValue !== null && (Array.isArray(props.modelValue) ? props.modelValue.length > 0 : true);
});
const filteredSuggestions = computed(() => {
  if (!props.filterable || !query.value) return [];
  const results = [];
  const keyword = query.value.toLowerCase();
  const traverse = (options, path, labels) => {
    const valKey = config.value.value;
    const labelKey = config.value.label;
    const childrenKey = config.value.children;
    for (const option of options) {
      const value = option[valKey];
      const label = String(option[labelKey] || "");
      const children = option[childrenKey];
      const currentPath = [...path, value];
      const currentLabels = [...labels, label];
      const fullPathLabel = currentLabels.join(props.separator || " / ").toLowerCase();
      const matches = props.filterMethod ? props.filterMethod(option, query.value) : fullPathLabel.includes(keyword);
      const isLeafNode = !children || children.length === 0;
      if (matches && (isLeafNode || config.value.checkStrictly)) {
        results.push({ path: currentPath, labels: currentLabels });
      }
      if (children && children.length > 0) {
        traverse(children, currentPath, currentLabels);
      }
    }
  };
  traverse(props.options || [], [], []);
  return results;
});
const wrapperClasses = computed(() => [
  ns.b(),
  ns.m(cascaderSize.value),
  ns.is("disabled", props.disabled),
  ns.is("focused", focused.value || visible.value)
]);
const handleExpand = (option, level) => {
  const value = option[config.value.value];
  expandedPath.value = [...expandedPath.value.slice(0, level), value];
  emit("expand-change", expandedPath.value);
};
const handleCheck = (option, path) => {
  const disabledKey = config.value.disabled;
  if (option[disabledKey]) return;
  if (isMultiple.value) {
    const values = (props.modelValue || []).slice();
    const pathStr = path.join(",");
    const index = values.findIndex((v) => v.join(",") === pathStr);
    if (index > -1) {
      values.splice(index, 1);
    } else {
      values.push(path);
    }
    emit("update:modelValue", values);
    emit("change", values);
  } else {
    const value = config.value.emitPath ? path : path[path.length - 1];
    emit("update:modelValue", value);
    emit("change", value);
    visible.value = false;
  }
  if (props.validateEvent) {
    triggerValidate("change");
  }
  query.value = "";
};
const isChecked = (path) => {
  if (isMultiple.value) {
    const values = props.modelValue;
    if (!values || !Array.isArray(values)) return false;
    const pathStr = path.join(",");
    return values.some((v) => Array.isArray(v) && v.join(",") === pathStr);
  }
  const value = props.modelValue;
  if (value === void 0 || value === null) return false;
  if (config.value.emitPath) {
    if (!Array.isArray(value)) return false;
    return value.join(",") === path.join(",");
  } else {
    return value === path[path.length - 1];
  }
};
const handleRemoveTag = (path, event) => {
  event.stopPropagation();
  if (props.disabled) return;
  const values = (props.modelValue || []).slice();
  const pathStr = path.join(",");
  const index = values.findIndex((v) => v.join(",") === pathStr);
  if (index > -1) {
    values.splice(index, 1);
    emit("update:modelValue", values);
    emit("change", values);
    if (props.validateEvent) {
      triggerValidate("change");
    }
  }
};
const handleClear = (event) => {
  event.stopPropagation();
  const value = isMultiple.value || config.value.emitPath ? [] : void 0;
  emit("update:modelValue", value);
  emit("change", value);
  emit("clear");
  query.value = "";
  expandedPath.value = [];
  if (props.validateEvent) {
    triggerValidate("change");
  }
};
const toggleDropdown = () => {
  if (props.disabled) return;
  visible.value = !visible.value;
  emit("visible-change", visible.value);
  if (visible.value) {
    nextTick(() => {
      if (props.filterable) {
        inputRef.value?.focus();
      }
    });
  }
};
const handleSelectSuggestion = (suggestion) => {
  if (isMultiple.value) {
    const values = (props.modelValue || []).slice();
    const pathStr = suggestion.path.join(",");
    const index = values.findIndex((v) => v.join(",") === pathStr);
    if (index === -1) {
      values.push(suggestion.path);
      emit("update:modelValue", values);
      emit("change", values);
    }
  } else {
    const value = config.value.emitPath ? suggestion.path : suggestion.path[suggestion.path.length - 1];
    emit("update:modelValue", value);
    emit("change", value);
    visible.value = false;
  }
  query.value = "";
  if (props.validateEvent) {
    triggerValidate("change");
  }
};
const handleInput = (event) => {
  const target = event.target;
  query.value = target.value;
  if (!visible.value && query.value) {
    visible.value = true;
    emit("visible-change", true);
  }
};
const handleFocus = (event) => {
  focused.value = true;
  emit("focus", event);
};
const handleBlur = (event) => {
  if (isClickingDropdown.value) {
    return;
  }
  focused.value = false;
  visible.value = false;
  emit("blur", event);
  emit("visible-change", false);
  if (props.validateEvent) {
    triggerValidate("blur");
  }
};
const handleDropdownMousedown = (event) => {
  event.preventDefault();
  isClickingDropdown.value = true;
};
const handleDropdownMouseup = () => {
  setTimeout(() => {
    isClickingDropdown.value = false;
  }, 0);
};
const handleMouseEnter = () => {
  hovering.value = true;
};
const handleMouseLeave = () => {
  hovering.value = false;
};
const getCheckedNodes = (leafOnly = false) => {
  const nodes = [];
  const findNode = (options, path, index) => {
    if (index >= path.length) return null;
    const valKey = config.value.value;
    const childrenKey = config.value.children;
    const option = options.find((o) => o[valKey] === path[index]);
    if (!option) return null;
    if (index === path.length - 1) return option;
    return findNode(option[childrenKey] || [], path, index + 1);
  };
  if (isMultiple.value) {
    const values = props.modelValue;
    if (values && Array.isArray(values)) {
      for (const path of values) {
        const node = findNode(props.options || [], path, 0);
        if (node && (!leafOnly || !node[config.value.children]?.length)) {
          nodes.push(node);
        }
      }
    }
  } else {
    const value = props.modelValue;
    if (value) {
      const path = Array.isArray(value) ? value : findPathByValue(value, props.options || []);
      if (path) {
        const node = findNode(props.options || [], path, 0);
        if (node && (!leafOnly || !node[config.value.children]?.length)) {
          nodes.push(node);
        }
      }
    }
  }
  return nodes;
};
const focus = () => {
  inputRef.value?.focus();
};
const blur = () => {
  inputRef.value?.blur();
};
provide(CascaderContextKey, {
  props,
  config: computed(() => config.value),
  expandedPath: computed(() => expandedPath.value),
  checkedValue: computed(() => props.modelValue),
  handleExpand,
  handleCheck,
  isChecked
});
defineExpose({
  focus,
  blur,
  getCheckedNodes,
  inputRef
});
</script>

<template>
  <div
    ref="wrapperRef"
    :class="wrapperClasses"
    :style="themeStyle"
    @mouseenter="handleMouseEnter"
    @mouseleave="handleMouseLeave"
    @click="toggleDropdown"
  >
    <!-- 输入区域 -->
    <div :class="ns.e('wrapper')">
      <!-- 多选标签 -->
      <template v-if="isMultiple">
        <span
          v-for="tag in displayTags"
          :key="tag.path.join(',')"
          :class="[ns.e('tag'), tagType ? `yh-tag--${tagType}` : '']"
        >
          <span :class="ns.e('tag-text')">{{ tag.text }}</span>
          <span :class="ns.e('tag-close')" @click="handleRemoveTag(tag.path, $event)">
            <svg viewBox="0 0 1024 1024" width="1em" height="1em">
              <path
                fill="currentColor"
                d="M764.288 214.592L512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
              />
            </svg>
          </span>
        </span>
        <YhTooltip
          v-if="collapsedCount > 0 && collapseTagsTooltip"
          placement="top"
          :class="ns.e('tag')"
        >
          <span>+{{ collapsedCount }}</span>
          <template #content>
            <div
              :class="ns.e('tooltip-content')"
              style="display: flex; flex-wrap: wrap; gap: 4px; padding: 4px"
            >
              <span
                v-for="(tag, idx) in collapsedTags"
                :key="idx"
                :class="ns.e('tooltip-tag')"
                style="white-space: nowrap"
              >
                {{ tag.text }}
              </span>
            </div>
          </template>
        </YhTooltip>
        <span v-else-if="collapsedCount > 0" :class="ns.e('tag')"> +{{ collapsedCount }} </span>
      </template>

      <!-- 输入框 -->
      <input
        ref="inputRef"
        :id="inputId"
        :class="ns.e('inner')"
        :value="filterable ? query : ''"
        :placeholder="hasValue ? '' : placeholder ?? t('cascader.placeholder')"
        :disabled="disabled"
        :readonly="!filterable"
        autocomplete="off"
        role="combobox"
        :aria-expanded="visible"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
      />

      <!-- 单选显示值 -->
      <span v-if="!isMultiple && hasValue && !query" :class="ns.e('selected-value')">
        {{ presentText }}
      </span>

      <!-- 后缀图标 -->
      <span :class="ns.e('suffix')">
        <!-- 清空按钮 -->
        <span v-if="showClear" :class="[ns.e('icon'), ns.e('clear')]" @click.stop="handleClear">
          <svg viewBox="0 0 1024 1024" width="1em" height="1em">
            <path
              fill="currentColor"
              d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336L512 457.664z"
            />
          </svg>
        </span>

        <!-- 箭头图标 -->
        <span :class="[ns.e('icon'), ns.e('arrow'), {
  'is-reverse': visible
}]">
          <svg viewBox="0 0 1024 1024" width="1em" height="1em">
            <path
              fill="currentColor"
              d="M831.872 340.864L512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.712a29.12 29.12 0 0 0 0-41.728 30.592 30.592 0 0 0-42.752 0z"
            />
          </svg>
        </span>
      </span>
    </div>

    <!-- 下拉框 -->
    <Teleport to="body" :disabled="!teleported">
      <Transition :name="ns.b('dropdown')">
        <div
          v-show="visible"
          ref="dropdownRef"
          :class="[ns.e('dropdown'), popperClass]"
          :style="teleported ? dropdownStyle : {}"
          @mousedown="handleDropdownMousedown"
          @mouseup="handleDropdownMouseup"
        >
          <!-- 过滤建议 -->
          <div
            v-if="filterable && query && filteredSuggestions.length > 0"
            :class="ns.e('suggestions')"
          >
            <div
              v-for="suggestion in filteredSuggestions"
              :key="suggestion.path.join(',')"
              :class="[ns.e('suggestion'), ns.is('checked', isChecked(suggestion.path))]"
              @click.stop="handleSelectSuggestion(suggestion)"
            >
              {{ suggestion.labels.join(separator) }}
            </div>
          </div>

          <!-- 无匹配数据 -->
          <div
            v-else-if="filterable && query && filteredSuggestions.length === 0"
            :class="ns.e('empty')"
          >
            <slot name="empty">{{ t("cascader.noMatch") }}</slot>
          </div>

          <!-- 级联面板 -->
          <CascaderPanel
            v-else
            :options="options"
            :expanded-path="expandedPath"
            :config="config"
            :is-multiple="isMultiple"
            :virtual="virtual"
            :item-height="virtualItemHeight"
            @expand="handleExpand"
            @check="handleCheck"
            :is-checked="isChecked"
          >
            <template #default="{ node, data }">
              <slot :node="node" :data="data"></slot>
            </template>
          </CascaderPanel>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-cascader {
  --yh-cascader-height: 32px;
  --yh-cascader-height-large: 40px;
  --yh-cascader-height-small: 24px;
  --yh-cascader-bg-color: var(--yh-fill-color-blank, #fff);
  --yh-cascader-border-color: var(--yh-border-color, #dcdfe6);
  --yh-cascader-border-color-hover: var(--yh-border-color-hover, #c0c4cc);
  --yh-cascader-border-color-focus: var(--yh-color-primary, #409eff);
  --yh-cascader-border-radius: var(--yh-border-radius-base, 4px);
  --yh-cascader-text-color: var(--yh-text-color-regular, #606266);
  --yh-cascader-placeholder-color: var(--yh-text-color-placeholder, #a8abb2);
  --yh-cascader-disabled-bg-color: var(--yh-fill-color-light, #f5f7fa);
  --yh-cascader-disabled-text-color: var(--yh-text-color-placeholder, #a8abb2);
  --yh-cascader-node-height: 34px;
  --yh-cascader-node-bg-color-hover: var(--yh-fill-color-light, #f5f7fa);
  --yh-cascader-node-text-color-hover: var(--yh-text-color-regular, #606266);
  --yh-cascader-node-bg-color-active: var(--yh-fill-color-light, #f5f7fa);
  --yh-cascader-node-text-color-active: var(--yh-color-primary, #409eff);
  --yh-cascader-dropdown-bg-color: var(--yh-bg-color-overlay, #fff);
  --yh-cascader-dropdown-border-color: var(--yh-border-color-light, #e4e7ed);
  --yh-cascader-dropdown-shadow: var(--yh-box-shadow-light, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
  --yh-cascader-menu-min-width: 180px;
  --yh-cascader-menu-max-height: 274px;
  --yh-cascader-menu-border-color: var(--yh-border-color-light, #e4e7ed);
  position: relative;
  display: inline-flex;
  width: 100%;
  font-size: 14px;
  cursor: pointer;
}
.yh-cascader__wrapper {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  position: relative;
  width: 100%;
  min-height: var(--yh-cascader-height);
  padding: 2px 30px 2px 11px;
  background-color: var(--yh-cascader-bg-color);
  border: 1px solid var(--yh-cascader-border-color);
  border-radius: var(--yh-cascader-border-radius);
  box-sizing: border-box;
  transition: border-color 0.2s;
}
.yh-cascader__wrapper:hover {
  border-color: var(--yh-cascader-border-color-hover);
}

.yh-cascader__inner {
  flex: 1;
  min-width: 50px;
  height: calc(var(--yh-cascader-height) - 6px);
  padding: 0;
  color: var(--yh-cascader-text-color);
  font-size: inherit;
  line-height: calc(var(--yh-cascader-height) - 6px);
  background: transparent;
  border: none;
  outline: none;
  cursor: pointer;
}
.yh-cascader__inner::placeholder {
  color: var(--yh-cascader-placeholder-color);
}
.yh-cascader__inner:disabled {
  cursor: not-allowed;
}

.yh-cascader__selected-value {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--yh-cascader-text-color);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - 50px);
}

.yh-cascader__suffix {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--yh-cascader-placeholder-color);
}

.yh-cascader__icon {
  width: 14px;
  height: 14px;
  line-height: 1;
}

.yh-cascader__clear {
  cursor: pointer;
  transition: color 0.2s;
}
.yh-cascader__clear:hover {
  color: var(--yh-text-color-secondary, #909399);
}

.yh-cascader__arrow {
  transition: transform 0.3s;
}
.yh-cascader__arrow.is-reverse {
  transform: rotate(180deg);
}

.yh-cascader__tag {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  height: 22px;
  padding: 0 8px;
  font-size: 12px;
  color: var(--yh-text-color-regular, #606266);
  background-color: var(--yh-fill-color, #f0f2f5);
  border-radius: var(--yh-border-radius-small, 2px);
}

.yh-cascader__tag-text {
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.yh-cascader__tag-close {
  display: inline-flex;
  align-items: center;
  margin-left: 4px;
  cursor: pointer;
  transition: color 0.2s;
}
.yh-cascader__tag-close:hover {
  color: var(--yh-color-danger, #f56c6c);
}
.yh-cascader__tag-close svg {
  width: 12px;
  height: 12px;
}

.yh-cascader__dropdown {
  --yh-cascader-dropdown-bg-color: var(--yh-bg-color-overlay, #fff);
  --yh-cascader-dropdown-border-color: var(--yh-border-color-light, #e4e7ed);
  --yh-cascader-dropdown-shadow: var(--yh-box-shadow-light, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
  --yh-cascader-border-radius: var(--yh-border-radius-base, 4px);
  --yh-cascader-menu-border-color: var(--yh-border-color-light, #e4e7ed);
  --yh-cascader-text-color: var(--yh-text-color-regular, #606266);
  --yh-cascader-node-height: 34px;
  --yh-cascader-node-bg-color-hover: var(--yh-fill-color-light, #f5f7fa);
  --yh-cascader-node-text-color-active: var(--yh-color-primary, #409eff);
  --yh-cascader-menu-min-width: 180px;
  --yh-cascader-menu-max-height: 274px;
  position: absolute;
  z-index: 2000;
  margin-top: 4px;
  background-color: var(--yh-cascader-dropdown-bg-color, #fff);
  border: 1px solid var(--yh-cascader-dropdown-border-color, #e4e7ed);
  border-radius: var(--yh-border-radius-base, 4px);
  box-shadow: var(--yh-cascader-dropdown-shadow, 0 2px 12px 0 rgba(0, 0, 0, 0.1));
}

.yh-cascader__panel {
  display: flex;
  background-color: var(--yh-cascader-dropdown-bg-color, #fff);
  border-radius: var(--yh-cascader-border-radius, 4px);
  overflow: hidden;
}

.yh-cascader__menu {
  min-width: var(--yh-cascader-menu-min-width);
  max-height: var(--yh-cascader-menu-max-height);
  padding: 6px 0;
  overflow-y: auto;
  border-right: 1px solid var(--yh-cascader-menu-border-color);
}
.yh-cascader__menu:last-child {
  border-right: none;
}

.yh-cascader__node {
  display: flex;
  align-items: center;
  padding: 0 30px 0 20px;
  height: var(--yh-cascader-node-height);
  line-height: var(--yh-cascader-node-height);
  font-size: 14px;
  color: var(--yh-cascader-text-color);
  cursor: pointer;
  position: relative;
}
.yh-cascader__node:hover {
  background-color: var(--yh-cascader-node-bg-color-hover);
  color: var(--yh-cascader-node-text-color-hover);
}
.yh-cascader__node.is-expanded {
  background-color: var(--yh-cascader-node-bg-color-active);
  color: var(--yh-cascader-node-text-color-active);
}
.yh-cascader__node.is-checked {
  color: var(--yh-color-primary, #409eff);
  background-color: var(--yh-color-primary-light-9, #ecf5ff);
  font-weight: 600;
}
.yh-cascader__node.is-checked.is-expanded {
  background-color: var(--yh-color-primary-light-9, #ecf5ff);
}
.yh-cascader__node.is-disabled {
  color: var(--yh-cascader-disabled-text-color);
  cursor: not-allowed;
}
.yh-cascader__node.is-disabled:hover {
  background-color: transparent;
}

.yh-cascader__checkbox {
  margin-right: 8px;
}

.yh-cascader__checkbox-inner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--yh-border-color, #dcdfe6);
  border-radius: 2px;
  background-color: var(--yh-bg-color, #fff);
  transition: all 0.2s;
}
.yh-cascader__checkbox-inner.is-checked {
  background-color: var(--yh-color-primary, #409eff);
  border-color: var(--yh-color-primary, #409eff);
}
.yh-cascader__checkbox-inner svg {
  width: 10px;
  height: 10px;
  color: #fff;
}

.yh-cascader__label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.yh-cascader__expand-icon {
  position: absolute;
  right: 10px;
  color: var(--yh-cascader-placeholder-color);
}
.yh-cascader__expand-icon svg {
  width: 12px;
  height: 12px;
}

.yh-cascader__suggestions {
  max-height: var(--yh-cascader-menu-max-height);
  padding: 6px 0;
  overflow-y: auto;
}

.yh-cascader__suggestion {
  padding: 0 20px;
  height: var(--yh-cascader-node-height);
  line-height: var(--yh-cascader-node-height);
  font-size: 14px;
  color: var(--yh-cascader-text-color);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.yh-cascader__suggestion:hover {
  background-color: var(--yh-cascader-node-bg-color-hover);
}
.yh-cascader__suggestion.is-checked {
  color: var(--yh-color-primary, #409eff);
  background-color: var(--yh-color-primary-light-9, #ecf5ff);
  font-weight: 600;
}

.yh-cascader__empty {
  padding: 10px 0;
  text-align: center;
  color: var(--yh-text-color-secondary, #909399);
  font-size: 14px;
}

.yh-cascader.is-focused .yh-cascader__wrapper {
  border-color: var(--yh-cascader-border-color-focus);
}

.yh-cascader.is-disabled {
  cursor: not-allowed;
}
.yh-cascader.is-disabled .yh-cascader__wrapper {
  background-color: var(--yh-cascader-disabled-bg-color);
  border-color: var(--yh-cascader-dropdown-border-color);
  cursor: not-allowed;
}

.yh-cascader.is-disabled .yh-cascader__inner {
  color: var(--yh-cascader-disabled-text-color);
}

.yh-cascader--large {
  font-size: 14px;
}
.yh-cascader--large .yh-cascader__wrapper {
  min-height: var(--yh-cascader-height-large);
}

.yh-cascader--large .yh-cascader__inner {
  height: calc(var(--yh-cascader-height-large) - 6px);
  line-height: calc(var(--yh-cascader-height-large) - 6px);
}

.yh-cascader--large .yh-cascader__tag {
  height: 26px;
}

.yh-cascader--small {
  font-size: 12px;
}
.yh-cascader--small .yh-cascader__wrapper {
  min-height: var(--yh-cascader-height-small);
}

.yh-cascader--small .yh-cascader__inner {
  height: calc(var(--yh-cascader-height-small) - 6px);
  line-height: calc(var(--yh-cascader-height-small) - 6px);
}

.yh-cascader--small .yh-cascader__tag {
  height: 18px;
  font-size: 10px;
}

.yh-cascader-dropdown-enter-active,
.yh-cascader-dropdown-leave-active {
  transition: opacity 0.2s, transform 0.2s;
}

.yh-cascader-dropdown-enter-from,
.yh-cascader-dropdown-leave-to {
  opacity: 0;
  transform: translateY(-10px);
}
</style>
