<script setup>
import { computed, ref, watch } from "vue";
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { useConfig } from "@yh-ui/hooks";
import TransferPanel from "./transfer-panel.vue";
defineOptions({
  name: "YhTransfer"
});
const props = defineProps({
  modelValue: { type: Array, required: false, default: () => [] },
  data: { type: Array, required: false, default: () => [] },
  filterable: { type: Boolean, required: false, default: false },
  filterMethod: { type: Function, required: false },
  filterPlaceholder: { type: String, required: false },
  targetOrder: { type: String, required: false, default: "original" },
  titles: { type: Array, required: false },
  buttonTexts: { type: Array, required: false, default: () => [] },
  renderContent: { type: Function, required: false },
  leftDefaultChecked: { type: Array, required: false, default: () => [] },
  rightDefaultChecked: { type: Array, required: false, default: () => [] },
  props: { type: Object, required: false },
  disabled: { type: Boolean, required: false, default: false },
  size: { type: null, required: false, default: "default" },
  validateEvent: { type: Boolean, required: false, default: true },
  virtual: { type: Boolean, required: false, default: false },
  itemHeight: { type: Number, required: false, default: 40 },
  height: { type: Number, required: false, default: 280 },
  leftTitle: { type: String, required: false },
  rightTitle: { type: String, required: false },
  showAllCheckbox: { type: Boolean, required: false, default: true },
  emptyText: { type: String, required: false },
  leftEmptyText: { type: String, required: false },
  rightEmptyText: { type: String, required: false },
  themeOverrides: { type: null, required: false }
});
const emit = defineEmits(["update:modelValue", "change", "left-check-change", "right-check-change"]);
const ns = useNamespace("transfer");
const { t } = useLocale();
const { globalSize } = useConfig();
const { themeStyle } = useComponentTheme(
  "transfer",
  computed(() => props.themeOverrides)
);
const leftPanelRef = ref();
const rightPanelRef = ref();
const leftChecked = ref([...props.leftDefaultChecked]);
const rightChecked = ref([...props.rightDefaultChecked]);
const keyProp = computed(() => props.props?.key || "key");
const getKey = (item) => {
  return item[keyProp.value] ?? "";
};
const targetKeys = computed(() => new Set(props.modelValue));
const leftData = computed(() => {
  return props.data.filter((item) => !targetKeys.value.has(getKey(item)));
});
const rightData = computed(() => {
  if (props.targetOrder === "original") {
    return props.data.filter((item) => targetKeys.value.has(getKey(item)));
  }
  const dataMap = new Map(props.data.map((item) => [getKey(item), item]));
  return (props.modelValue ?? []).map((key) => dataMap.get(key)).filter((item) => item !== void 0);
});
const leftTitle = computed(() => props.leftTitle || props.titles?.[0] || t("transfer.titles.0"));
const rightTitle = computed(() => props.rightTitle || props.titles?.[1] || t("transfer.titles.1"));
const leftEmptyText = computed(() => props.leftEmptyText || props.emptyText || t("transfer.noData"));
const rightEmptyText = computed(
  () => props.rightEmptyText || props.emptyText || t("transfer.noData")
);
const filterPlaceholder = computed(() => props.filterPlaceholder ?? t("transfer.filterPlaceholder"));
const canMoveToRight = computed(() => leftChecked.value.length > 0);
const canMoveToLeft = computed(() => rightChecked.value.length > 0);
const containerClasses = computed(() => [
  ns.b(),
  ns.m(props.size || globalSize.value || "default"),
  ns.is("disabled", props.disabled)
]);
const moveToRight = () => {
  if (!canMoveToRight.value || props.disabled) return;
  const movedKeys = [...leftChecked.value];
  let newValue;
  if (props.targetOrder === "unshift") {
    newValue = [...movedKeys, ...props.modelValue ?? []];
  } else {
    newValue = [...props.modelValue ?? [], ...movedKeys];
  }
  if (props.targetOrder === "original") {
    const keySet = new Set(newValue);
    newValue = props.data.filter((item) => keySet.has(getKey(item))).map((item) => getKey(item));
  }
  emit("update:modelValue", newValue);
  emit("change", newValue, "right", movedKeys);
  leftChecked.value = [];
};
const moveToLeft = () => {
  if (!canMoveToLeft.value || props.disabled) return;
  const movedKeys = [...rightChecked.value];
  const movedKeysSet = new Set(movedKeys);
  const newValue = (props.modelValue ?? []).filter((key) => !movedKeysSet.has(key));
  emit("update:modelValue", newValue);
  emit("change", newValue, "left", movedKeys);
  rightChecked.value = [];
};
const handleLeftCheckedChange = (value, movedKeys) => {
  leftChecked.value = value;
  emit("left-check-change", value, movedKeys);
};
const handleRightCheckedChange = (value, movedKeys) => {
  rightChecked.value = value;
  emit("right-check-change", value, movedKeys);
};
const clearLeftChecked = () => {
  leftChecked.value = [];
};
const clearRightChecked = () => {
  rightChecked.value = [];
};
watch(
  () => props.modelValue,
  () => {
    const leftDataKeys = new Set(leftData.value.map((item) => getKey(item)));
    leftChecked.value = leftChecked.value.filter((key) => leftDataKeys.has(key));
    const rightDataKeys = new Set(rightData.value.map((item) => getKey(item)));
    rightChecked.value = rightChecked.value.filter((key) => rightDataKeys.has(key));
  },
  { deep: true }
);
defineExpose({
  clearLeftChecked,
  clearRightChecked,
  leftPanel: leftPanelRef,
  rightPanel: rightPanelRef
});
</script>

<template>
  <div :class="containerClasses" :style="themeStyle">
    <TransferPanel
      ref="leftPanelRef"
      :data="leftData"
      :checked="leftChecked"
      :title="leftTitle"
      :filterable="filterable"
      :filter-placeholder="filterPlaceholder"
      :filter-method="filterMethod"
      :disabled="disabled"
      :size="size"
      :props="props.props"
      :render-content="renderContent"
      :virtual="virtual"
      :item-height="itemHeight"
      :height="height"
      :show-all-checkbox="showAllCheckbox"
      :empty-text="leftEmptyText"
      @update:checked="handleLeftCheckedChange"
      @checked-change="handleLeftCheckedChange"
    >
      <template v-if="$slots['left-header']" #header>
        <slot name="left-header" />
      </template>
      <template v-if="$slots.default" #default="{ option }">
        <slot :option="option" />
      </template>
      <template v-if="$slots['left-empty']" #empty>
        <slot name="left-empty" />
      </template>
      <template v-if="$slots['left-footer']" #footer>
        <slot name="left-footer" />
      </template>
    </TransferPanel>

    <div :class="ns.e('buttons')">
      <slot
        name="buttons"
        :move-to-left="moveToLeft"
        :move-to-right="moveToRight"
        :left-checked="leftChecked"
        :right-checked="rightChecked"
      >
        <button
          type="button"
          :class="[ns.e('button'), {
  'is-disabled': !canMoveToRight || disabled
}]"
          :disabled="!canMoveToRight || disabled"
          @click="moveToRight"
        >
          <svg :class="ns.e('button__icon')" viewBox="0 0 1024 1024">
            <path
              fill="currentColor"
              d="M340.864 149.312a30.592 30.592 0 000 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0042.752 43.458l355.136-362.88a30.592 30.592 0 000-43.52L383.616 106.56a30.592 30.592 0 00-42.752 42.752z"
            />
          </svg>
          <span v-if="buttonTexts && buttonTexts[0]" :class="ns.e('button__text')">{{
            buttonTexts[0]
          }}</span>
        </button>

        <button
          type="button"
          :class="[ns.e('button'), {
  'is-disabled': !canMoveToLeft || disabled
}]"
          :disabled="!canMoveToLeft || disabled"
          @click="moveToLeft"
        >
          <svg :class="ns.e('button__icon')" viewBox="0 0 1024 1024">
            <path
              fill="currentColor"
              d="M685.248 104.256a30.592 30.592 0 010 42.752L373.376 512l311.872 364.992a30.592 30.592 0 11-42.752 43.458L287.38 555.52a30.592 30.592 0 010-43.52l355.136-364.93a30.592 30.592 0 0142.752 0z"
            />
          </svg>
          <span v-if="buttonTexts && buttonTexts[1]" :class="ns.e('button__text')">{{
            buttonTexts[1]
          }}</span>
        </button>
      </slot>
    </div>

    <TransferPanel
      ref="rightPanelRef"
      :data="rightData"
      :checked="rightChecked"
      :title="rightTitle"
      :filterable="filterable"
      :filter-placeholder="filterPlaceholder"
      :filter-method="filterMethod"
      :disabled="disabled"
      :size="size"
      :props="props.props"
      :render-content="renderContent"
      :virtual="virtual"
      :item-height="itemHeight"
      :height="height"
      :show-all-checkbox="showAllCheckbox"
      :empty-text="rightEmptyText"
      @update:checked="handleRightCheckedChange"
      @checked-change="handleRightCheckedChange"
    >
      <template v-if="$slots['right-header']" #header>
        <slot name="right-header" />
      </template>
      <template v-if="$slots.default" #default="{ option }">
        <slot :option="option" />
      </template>
      <template v-if="$slots['right-empty']" #empty>
        <slot name="right-empty" />
      </template>
      <template v-if="$slots['right-footer']" #footer>
        <slot name="right-footer" />
      </template>
    </TransferPanel>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
/* 穿梭框面板独立样式 */
.yh-transfer-panel {
  display: inline-flex;
  flex-direction: column;
  background: var(--yh-fill-color-blank, #fff);
  border: 1px solid var(--yh-border-color-lighter, #ebeef5);
  border-radius: var(--yh-radius-base, 4px);
  overflow: hidden;
  box-sizing: border-box;
  width: 200px;
  height: 300px;
}
.yh-transfer-panel__header {
  height: 40px;
  background: var(--yh-fill-color-light, #f5f7fa);
  padding: 0 12px;
  border-bottom: 1px solid var(--yh-border-color-lighter, #ebeef5);
  display: flex;
  align-items: center;
  color: var(--yh-text-color-primary, #303133);
  box-sizing: border-box;
}

.yh-transfer-panel__check-all {
  display: flex;
  align-items: center;
  cursor: pointer;
  margin-right: 10px;
}
.yh-transfer-panel__check-all.is-disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.yh-transfer-panel__title {
  flex: 1;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.yh-transfer-panel__count {
  font-size: 12px;
  color: var(--yh-text-color-secondary, #909399);
  margin-left: 4px;
}

.yh-transfer-panel__filter {
  padding: 10px;
  box-sizing: border-box;
}
.yh-transfer-panel__filter-input {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--yh-border-color, #dcdfe6);
  border-radius: var(--yh-radius-base, 4px);
  font-size: 12px;
  outline: none;
  box-sizing: border-box;
  transition: border-color var(--yh-transition-base, 0.2s);
}
.yh-transfer-panel__filter-input:focus {
  border-color: var(--yh-color-primary, #409eff);
}

.yh-transfer-panel__body {
  flex: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  position: relative;
}

.yh-transfer-panel__list {
  margin: 0;
  padding: 6px 0;
  list-style: none;
  flex: 1;
  overflow-y: auto;
  box-sizing: border-box;
}

.yh-transfer-panel__virtual-wrapper {
  overflow-y: auto;
  width: 100%;
  flex: 1;
}

.yh-transfer-panel__item {
  padding: 0 12px;
  height: 30px;
  line-height: 30px;
  display: flex;
  align-items: center;
  cursor: pointer;
  font-size: 14px;
  color: var(--yh-text-color-regular, #606266);
}
.yh-transfer-panel__item:hover:not(.is-disabled) {
  background: var(--yh-fill-color-light, #f5f7fa);
  color: var(--yh-color-primary, #409eff);
}
.yh-transfer-panel__item.is-disabled {
  color: var(--yh-text-color-disabled, #c0c4cc);
  cursor: not-allowed;
}

.yh-transfer-panel__item-checkbox {
  width: 14px;
  height: 14px;
  border: 1px solid var(--yh-border-color, #dcdfe6);
  border-radius: 2px;
  margin-right: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--yh-fill-color-blank, #fff);
  position: relative;
  flex-shrink: 0;
  transition: all 0.2s;
}
.yh-transfer-panel__item-checkbox.is-checked {
  background-color: var(--yh-color-primary, #409eff);
  border-color: var(--yh-color-primary, #409eff);
  color: #fff;
}

.yh-transfer-panel__item-checkbox.is-indeterminate {
  background-color: var(--yh-color-primary, #409eff);
  border-color: var(--yh-color-primary, #409eff);
  color: #fff;
}

.yh-transfer-panel__item-checkbox__icon {
  width: 100%;
  height: 100%;
  transform: scale(0.8);
}

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

.yh-transfer-panel__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--yh-text-color-secondary, #909399);
  padding: 20px 0;
}
.yh-transfer-panel__empty-text {
  font-size: 14px;
}

.yh-transfer-panel__empty-icon {
  width: 48px;
  height: 48px;
  margin-bottom: 8px;
  color: var(--yh-border-color-light, #dcdfe6);
}

.yh-transfer-panel__footer {
  border-top: 1px solid var(--yh-border-color-lighter, #ebeef5);
  background: var(--yh-fill-color-blank, #fff);
}

/* 穿梭框整体样式 */
.yh-transfer {
  display: inline-flex;
  align-items: center;
  font-size: 14px;
  vertical-align: middle;
}
.yh-transfer__buttons {
  display: inline-flex;
  flex-direction: column;
  padding: 0 12px;
  gap: 10px;
}

.yh-transfer__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  border-radius: 4px;
  border: 1px solid var(--yh-border-color, #dcdfe6);
  background: var(--yh-fill-color-light, #f5f7fa);
  color: var(--yh-text-color-regular, #909399);
  cursor: pointer;
  padding: 0 10px;
  transition: var(--yh-transition-base, 0.3s);
  white-space: nowrap;
}
.yh-transfer__button:hover:not(:disabled) {
  color: var(--yh-color-primary, #409eff);
  border-color: var(--yh-color-primary, #409eff);
}
.yh-transfer__button.is-active:not(:disabled) {
  background: var(--yh-color-primary, #409eff);
  color: #fff;
  border-color: var(--yh-color-primary, #409eff);
}

.yh-transfer__button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.yh-transfer__button svg {
  width: 14px;
  height: 14px;
}
.yh-transfer__text {
  margin: 0 4px;
}

.yh-transfer {
  /* 尺寸支持 */
}
.yh-transfer--large .yh-transfer-panel {
  width: 240px;
  height: 360px;
}

.yh-transfer--small .yh-transfer-panel {
  width: 180px;
  height: 260px;
}
</style>
