<script setup>
import { provide, reactive, toRefs, computed } from "vue";
import { formProps, FormContextKey } from "./form";
import { useNamespace } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
defineOptions({
  name: "YhForm"
});
const props = defineProps(formProps);
const emit = defineEmits(["validate"]);
const ns = useNamespace("form");
const { themeStyle } = useComponentTheme(
  "form",
  computed(() => props.themeOverrides)
);
const fields = [];
const addField = (field) => {
  if (field.prop) {
    fields.push(field);
  }
};
const removeField = (field) => {
  if (field.prop) {
    const index = fields.indexOf(field);
    if (index > -1) fields.splice(index, 1);
  }
};
const validate = async (propsToValidateOrCb = [], callback) => {
  let innerCallback = callback;
  let propsToValidate = [];
  if (typeof propsToValidateOrCb === "function") {
    innerCallback = propsToValidateOrCb;
    propsToValidate = [];
  } else {
    propsToValidate = propsToValidateOrCb;
  }
  const propList = Array.isArray(propsToValidate) ? propsToValidate : propsToValidate ? [propsToValidate] : [];
  const fieldsToValidate = propList.length > 0 ? fields.filter((field) => propList.includes(field.prop)) : fields;
  if (fieldsToValidate.length === 0 && propList.length === 0) {
    innerCallback?.(true);
    return true;
  }
  let isValid = true;
  const invalidFields = {};
  for (const field of fieldsToValidate) {
    try {
      await field.validate("");
    } catch (error) {
      isValid = false;
      if (field.prop) {
        invalidFields[field.prop] = error;
      }
    }
  }
  if (context.scrollToError && !isValid) {
    const firstProp = Object.keys(invalidFields)[0];
    if (firstProp) scrollToField(firstProp);
  }
  innerCallback?.(isValid, invalidFields);
  emit("validate", isValid, invalidFields);
  if (!isValid) {
    if (innerCallback) {
      return false;
    }
    return Promise.reject(invalidFields);
  }
  return true;
};
const resetFields = (props2 = []) => {
  const propsToReset = Array.isArray(props2) ? props2 : props2 ? [props2] : [];
  const fieldsToReset = propsToReset.length > 0 ? fields.filter((field) => propsToReset.includes(field.prop)) : fields;
  fieldsToReset.forEach((field) => field.resetField());
};
const clearValidate = (props2 = []) => {
  const propsArray = Array.isArray(props2) ? props2 : props2 ? [props2] : [];
  const fieldsToClear = propsArray.length > 0 ? fields.filter(
    (field) => field.prop && (typeof propsArray === "string" ? propsArray === field.prop : propsArray.includes(field.prop))
  ) : fields;
  fieldsToClear.forEach((field) => field.clearValidate());
};
const scrollToField = (prop) => {
  const field = fields.find((f) => f.prop === prop);
  if (field) {
    const el = document.querySelector(`[data-prop="${prop}"]`);
    if (el) {
      if (props.scrollToErrorOffset) {
        const top = el.getBoundingClientRect().top + window.pageYOffset - props.scrollToErrorOffset;
        window.scrollTo({
          top,
          behavior: typeof context.scrollIntoViewOptions === "object" ? context.scrollIntoViewOptions.behavior : "smooth"
        });
      } else {
        const options = typeof context.scrollIntoViewOptions === "object" ? context.scrollIntoViewOptions : { behavior: "smooth", block: "center" };
        el.scrollIntoView(options);
      }
    }
  }
};
const context = reactive({
  ...toRefs(props),
  addField,
  removeField
});
provide(FormContextKey, context);
defineExpose({
  validate,
  resetFields,
  clearValidate,
  scrollToField
});
</script>

<template>
  <form
    :class="[ns.b(), ns.m(layout), ns.m(size), ns.m(`label-${labelPosition}`)]"
    :style="themeStyle"
    @submit.prevent
  >
    <slot />
  </form>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
:root {
  --yh-form-item-height: 32px;
  --yh-form-item-margin-bottom: 22px;
  --yh-form-label-font-size: 14px;
}

.yh-form--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 18px;
}
.yh-form--inline .yh-form-item {
  display: inline-flex;
  align-items: flex-start;
  margin-right: 0;
  margin-bottom: 18px;
  vertical-align: top;
}
.yh-form--inline .yh-form-item__label {
  height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
}
.yh-form--inline .yh-form-item__content {
  line-height: var(--yh-form-item-height);
}
.yh-form--grid {
  display: flex;
  flex-wrap: wrap;
  margin-left: -12px;
  margin-right: -12px;
}
.yh-form--grid .yh-form-col {
  padding-left: 12px;
  padding-right: 12px;
  box-sizing: border-box;
  flex-shrink: 0;
  width: 100%;
}
.yh-form--grid .yh-form-col--1 {
  flex: 0 0 calc(4.1666666667% * 1);
  max-width: calc(4.1666666667% * 1);
}
.yh-form--grid .yh-form-col--2 {
  flex: 0 0 calc(4.1666666667% * 2);
  max-width: calc(4.1666666667% * 2);
}
.yh-form--grid .yh-form-col--3 {
  flex: 0 0 calc(4.1666666667% * 3);
  max-width: calc(4.1666666667% * 3);
}
.yh-form--grid .yh-form-col--4 {
  flex: 0 0 calc(4.1666666667% * 4);
  max-width: calc(4.1666666667% * 4);
}
.yh-form--grid .yh-form-col--5 {
  flex: 0 0 calc(4.1666666667% * 5);
  max-width: calc(4.1666666667% * 5);
}
.yh-form--grid .yh-form-col--6 {
  flex: 0 0 calc(4.1666666667% * 6);
  max-width: calc(4.1666666667% * 6);
}
.yh-form--grid .yh-form-col--7 {
  flex: 0 0 calc(4.1666666667% * 7);
  max-width: calc(4.1666666667% * 7);
}
.yh-form--grid .yh-form-col--8 {
  flex: 0 0 calc(4.1666666667% * 8);
  max-width: calc(4.1666666667% * 8);
}
.yh-form--grid .yh-form-col--9 {
  flex: 0 0 calc(4.1666666667% * 9);
  max-width: calc(4.1666666667% * 9);
}
.yh-form--grid .yh-form-col--10 {
  flex: 0 0 calc(4.1666666667% * 10);
  max-width: calc(4.1666666667% * 10);
}
.yh-form--grid .yh-form-col--11 {
  flex: 0 0 calc(4.1666666667% * 11);
  max-width: calc(4.1666666667% * 11);
}
.yh-form--grid .yh-form-col--12 {
  flex: 0 0 calc(4.1666666667% * 12);
  max-width: calc(4.1666666667% * 12);
}
.yh-form--grid .yh-form-col--13 {
  flex: 0 0 calc(4.1666666667% * 13);
  max-width: calc(4.1666666667% * 13);
}
.yh-form--grid .yh-form-col--14 {
  flex: 0 0 calc(4.1666666667% * 14);
  max-width: calc(4.1666666667% * 14);
}
.yh-form--grid .yh-form-col--15 {
  flex: 0 0 calc(4.1666666667% * 15);
  max-width: calc(4.1666666667% * 15);
}
.yh-form--grid .yh-form-col--16 {
  flex: 0 0 calc(4.1666666667% * 16);
  max-width: calc(4.1666666667% * 16);
}
.yh-form--grid .yh-form-col--17 {
  flex: 0 0 calc(4.1666666667% * 17);
  max-width: calc(4.1666666667% * 17);
}
.yh-form--grid .yh-form-col--18 {
  flex: 0 0 calc(4.1666666667% * 18);
  max-width: calc(4.1666666667% * 18);
}
.yh-form--grid .yh-form-col--19 {
  flex: 0 0 calc(4.1666666667% * 19);
  max-width: calc(4.1666666667% * 19);
}
.yh-form--grid .yh-form-col--20 {
  flex: 0 0 calc(4.1666666667% * 20);
  max-width: calc(4.1666666667% * 20);
}
.yh-form--grid .yh-form-col--21 {
  flex: 0 0 calc(4.1666666667% * 21);
  max-width: calc(4.1666666667% * 21);
}
.yh-form--grid .yh-form-col--22 {
  flex: 0 0 calc(4.1666666667% * 22);
  max-width: calc(4.1666666667% * 22);
}
.yh-form--grid .yh-form-col--23 {
  flex: 0 0 calc(4.1666666667% * 23);
  max-width: calc(4.1666666667% * 23);
}
.yh-form--grid .yh-form-col--24 {
  flex: 0 0 calc(4.1666666667% * 24);
  max-width: calc(4.1666666667% * 24);
}
.yh-form--large {
  --yh-form-item-height: 40px;
  --yh-form-label-font-size: 14px;
  --yh-component-size-default: 40px;
}

.yh-form--default {
  --yh-form-item-height: 32px;
  --yh-form-label-font-size: 14px;
  --yh-component-size-default: 32px;
}

.yh-form--small {
  --yh-form-item-height: 24px;
  --yh-form-label-font-size: 12px;
  --yh-component-size-default: 24px;
}

.yh-form-item {
  display: flex;
  align-items: flex-start;
  margin-bottom: var(--yh-form-item-margin-bottom);
  position: relative;
}
.yh-form--label-left .yh-form-item__label {
  justify-content: flex-start;
}
.yh-form--label-top .yh-form-item {
  flex-direction: column;
}
.yh-form--label-top .yh-form-item__label {
  justify-content: flex-start;
  margin-bottom: 8px;
  width: 100% !important;
  text-align: left;
  height: auto;
  line-height: 1.5;
}
.yh-form--label-top .yh-form-item__content {
  width: 100%;
  align-self: stretch;
}
.yh-form-item__label {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 12px;
  font-size: var(--yh-form-label-font-size);
  color: var(--yh-text-color-regular, #606266);
  height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
  box-sizing: border-box;
  flex-shrink: 0;
  white-space: nowrap;
}
.yh-form-item__content {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  flex: 1;
  position: relative;
  font-size: 14px;
  min-width: 0;
  min-height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
}
.yh-form-item__content > .yh-input,
.yh-form-item__content > .yh-select,
.yh-form-item__content > .yh-input-number,
.yh-form-item__content > .yh-date-picker {
  width: 100%;
}
.yh-form-item__error {
  color: var(--yh-color-danger, #f56c6c);
  font-size: 12px;
  line-height: 1;
  padding-top: 4px;
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  z-index: 1;
}
.yh-form-item__error.is-center {
  text-align: center;
}
.yh-form-item__error.is-right {
  text-align: right;
}
.yh-form-item__status-icon {
  position: absolute;
  right: 10px;
  top: 0;
  height: var(--yh-form-item-height);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  pointer-events: none;
}
.yh-form-item__status-icon svg {
  width: 14px;
  height: 14px;
}
.yh-form-item__status-icon.is-success {
  color: var(--yh-color-success, #67c23a);
}
.yh-form-item__status-icon.is-error {
  color: var(--yh-color-danger, #f56c6c);
}
.yh-form-item__status-icon.is-validating {
  color: var(--yh-color-primary, #409eff);
}
.yh-form-item__status-icon.is-validating svg {
  animation: yh-rotating 2s linear infinite;
}
.yh-form-item.is-required:not(.is-no-asterisk) > .yh-form-item__label::before {
  content: "*";
  color: var(--yh-color-danger, #f56c6c);
  margin-right: 4px;
}
.yh-form-item.is-error .yh-input__inner,
.yh-form-item.is-error .yh-input-number__wrapper,
.yh-form-item.is-error .yh-select__wrapper,
.yh-form-item.is-error .yh-textarea__inner {
  border-color: var(--yh-color-danger, #f56c6c) !important;
}
.yh-form-item.is-error .yh-input__inner:hover, .yh-form-item.is-error .yh-input__inner:focus,
.yh-form-item.is-error .yh-input-number__wrapper:hover,
.yh-form-item.is-error .yh-input-number__wrapper:focus,
.yh-form-item.is-error .yh-select__wrapper:hover,
.yh-form-item.is-error .yh-select__wrapper:focus,
.yh-form-item.is-error .yh-textarea__inner:hover,
.yh-form-item.is-error .yh-textarea__inner:focus {
  border-color: var(--yh-color-danger, #f56c6c) !important;
}
.yh-form-item.is-success .yh-input__inner,
.yh-form-item.is-success .yh-input-number__wrapper,
.yh-form-item.is-success .yh-select__wrapper,
.yh-form-item.is-success .yh-textarea__inner {
  border-color: var(--yh-color-success, #67c23a) !important;
}
.yh-form-item--large {
  --yh-form-item-height: 40px;
  --yh-component-size-default: 40px;
}

.yh-form-item--default {
  --yh-form-item-height: 32px;
  --yh-component-size-default: 32px;
}

.yh-form-item--small {
  --yh-form-item-height: 24px;
  --yh-form-label-font-size: 12px;
  --yh-component-size-default: 24px;
}

@keyframes yh-rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
.yh-fade-enter-active,
.yh-fade-leave-active {
  transition: opacity 0.2s ease, transform 0.2s ease;
}

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