import { h } from './index-d7539aa6.js';

// import { CSS_UTILITY } from './resources';
// import { guid } from './guid';
// export const getThemeName = (element: HTMLElement): Theme =>
//   closestElementCrossShadowBoundary(element, `.${CSS_UTILITY.darkTheme}`) ? 'dark' : 'light';
const getElementDir = (element) => { var _a; return ((_a = closestElementCrossShadowBoundary(element, '[dir]')) === null || _a === void 0 ? void 0 : _a.getAttribute('dir')) || 'ltr'; };
// export const getElementProp = (element: Element, prop: string, fallbackValue: any): any => {
//   const closest = element.closest(`[${prop}]`);
//   return closest ? closest.getAttribute(prop) : fallbackValue;
// };
const getRootNode = (element) => element.getRootNode();
const getHost = (root) => root.host || null;
// // Queries an element's rootNode and any ancestor rootNodes.
// // based on https://stackoverflow.com/q/54520554/194216
// export const queryElementsRoots = <T extends Element = Element>(element: Element, selector: string): T[] => {
//   // Gets the rootNode and any ancestor rootNodes (shadowRoot or document) of an element and queries them for a selector.
//   const queryFromAll = <T extends Element = Element>(element: Element, allResults: T[]): T[] => {
//     if (!element) return allResults;
//     if ((element as Slottable).assignedSlot) element = (element as Slottable).assignedSlot;
//     const rootNode = getRootNode(element);
//     const results = [...rootNode.querySelectorAll(selector)] as T[];
//     const uniqueResults = results.filter((result) => !allResults.includes(result));
//     allResults = [...allResults, ...uniqueResults];
//     const host = getHost(rootNode);
//     return host ? queryFromAll(host, allResults) : allResults;
//   };
//   return queryFromAll(element, []);
// };
// Queries element's rootNode and ancestor rootNodes.
// Based on https://stackoverflow.com/q/54520554/194216
const queryElementRoots = (element, selector) => {
  // Gets the rootNode and any ancestor rootNodes (shadowRoot or document) of an element and queries them for a selector.
  const queryFrom = (element) => {
    if (!element)
      return null;
    if (element.assignedSlot)
      element = element.assignedSlot;
    const rootNode = getRootNode(element);
    const found = rootNode.querySelector(selector);
    const host = getHost(rootNode);
    return found ? found : host ? queryFrom(host) : null;
  };
  return queryFrom(element);
};
const closestElementCrossShadowBoundary = (element, selector) => {
  // based on https://stackoverflow.com/q/54520554/194216
  const closestFrom = (element) => element ? element.closest(selector) || closestFrom(getHost(getRootNode(element))) : null;
  return closestFrom(element);
};
// export interface AbdsFocusableElement extends HTMLElement {
//   setFocus?: () => Promise<void>;
// }
// export const isAbdsFocusable = (element: AbdsFocusableElement): boolean => typeof element?.setFocus === 'function';
// export const focusElement = async (element: AbdsFocusableElement): Promise<void> => {
//   if (!element) return;
//   return isAbdsFocusable(element) ? element.setFocus() : element.focus();
// };
// interface GetSlottedOptions {
//   all?: boolean;
//   direct?: boolean;
//   selector?: string;
// }
// export function getSlotted<T extends Element = Element>(
//   element: Element,
//   slotName: string,
//   options: GetSlottedOptions & { all: true }
// ): T[];
// export function getSlotted<T extends Element = Element>(
//   element: Element,
//   slotName: string,
//   options?: GetSlottedOptions
// ): T | null;
// export function getSlotted<T extends Element = Element>(
//   element: Element,
//   slotName: string,
//   options?: GetSlottedOptions
// ): (T | null) | T[] {
//   return options?.all
//     ? queryMultiple<T>(element, `[slot="${slotName}"]`, options)
//     : querySingle<T>(element, `[slot="${slotName}"]`, options);
// }
// const queryMultiple = <T extends Element = Element>(
//   element: Element,
//   slotSelector: string,
//   { direct, selector }: GetSlottedOptions
// ): T[] => {
//   let matches = [...element.querySelectorAll<T>(slotSelector)];
//   matches = direct === false ? matches : matches.filter((el) => el.parentElement === element);
//   return selector
//     ? matches
//         .map((item) => Array.from(item.querySelectorAll<T>(selector)))
//         .reduce((previousValue, currentValue) => [...previousValue, ...currentValue], [])
//         .filter((match) => !!match)
//     : matches;
// };
// const querySingle = <T extends Element = Element>(
//   element: Element,
//   slotSelector: string,
//   options?: GetSlottedOptions
// ): T | null => {
//   let match = element.querySelector<T>(slotSelector);
//   match = options && options.direct === false ? match : match?.parentElement === element ? match : null;
//   const selector = options?.selector;
//   return selector ? match.querySelector<T>(selector) : match;
// };
// export const filterDirectChildren = <T extends Element>(element: Element, selector: string): T[] =>
//   [...element.children].filter((child): child is T => child.matches(selector));
// export const hasLabel(labelEl: HTMLCalciteLabelElement, element: HTMLElement): boolean => labelEl.contains(element);
// // set a default icon from a defined set or allow an override with an icon name string
// export const setRequestedIcon = (iconObject: Record<string, string>, iconValue: string, matchedValue: string): string =>
//   typeof iconValue === 'string' && iconValue ? iconValue : typeof iconValue === 'string' && iconObject[matchedValue];
// export const intersects = (rect1: DOMRect, rect2: DOMRect): boolean =>
//   (rect1.right < rect2.left ||  rect1.left > rect2.right || rect1.bottom < rect2.top ||  rect1.top > rect2.bottom);
const dom = { closestElementCrossShadowBoundary, getElementDir, getHost, getRootNode, queryElementRoots };

/** Checks if component has checked property. */
function canBeCheckable(component) {
  return 'checked' in component;
}
/** Checks if component has indeterminate property. */
function canBeIndeterminate(component) {
  return 'indeterminate' in component;
}
const onFormResetMap = new WeakMap();
const formComponentSet = new WeakSet();
/**
 * Checks if a given component is registered to a different form than the given one.
 * @function
 * @param {HTMLFormElement} form - The form to check against.
 * @param {HTMLElement} formComponentEl - The element to check for.
 * @return {boolean} element registered to a different form.
 */
function hasRegisteredFormComponentParent(form, formComponentEl) {
  // we use events as a way to test for nested form-associated components across shadow bounds
  const formComponentRegisterEventName = 'internalFormComponentRegister';
  let isRegistered = false;
  form.addEventListener(formComponentRegisterEventName, (event) => {
    isRegistered = event.composedPath().some((element) => formComponentSet.has(element));
    event.stopPropagation();
  }, { once: true });
  formComponentEl.dispatchEvent(new CustomEvent(formComponentRegisterEventName, {
    bubbles: true,
    composed: true,
  }));
  return isRegistered;
}
/** Reset properties of a form component to their defaults. */
function onFormReset() {
  this.value = this.defaultValue;
  if (canBeCheckable(this))
    this.checked = this.defaultChecked;
  if (canBeIndeterminate(this))
    this.indeterminate = this.defaultIndeterminate;
}
/** Helper to set up form interactions on connectedCallback. */
function connectForm(component) {
  const { host, value } = component;
  const form = closestElementCrossShadowBoundary(host, 'form');
  const boundOnFormReset = (component.onFormReset || onFormReset).bind(component);
  if (!form || hasRegisteredFormComponentParent(form, host))
    return;
  form.addEventListener('reset', boundOnFormReset);
  formComponentSet.add(host);
  component.formEl = form;
  component.defaultValue = value;
  if (canBeCheckable(component))
    component.defaultChecked = component.checked;
  if (canBeIndeterminate(component))
    component.defaultIndeterminate = component.indeterminate;
}
/** Helper to tear down form interactions on disconnectedCallback. */
function disconnectForm(component) {
  const { host, formEl } = component;
  const boundOnFormReset = onFormResetMap.get(host);
  if (!formEl)
    return;
  formEl.removeEventListener('reset', boundOnFormReset);
  onFormResetMap.delete(host);
  component.formEl = null;
  formComponentSet.delete(host);
}
/**
 * Helper for maintaining a form-associated's hidden input in sync with the component.
 *
 * Based on Ionic's approach: https://github.com/ionic-team/ionic-framework/blob/e4bf052794af9aac07f887013b9250d2a045eba3/core/src/utils/helpers.ts#L198
 */
const HiddenFormInputSlot = ({ component, type }) => {
  const { disabled, required, value, name, host, hiddenInput } = component;
  const hiddenFormInputSlotName = 'hidden-form-input';
  let input = host.querySelector(`input[slot="${hiddenFormInputSlotName}"]`);
  if (!input) {
    input = host.ownerDocument.createElement('input');
    if (name)
      input.name = name;
    if (!hiddenInput)
      component.hiddenInput = input;
    input.tabIndex = -1;
    input.slot = hiddenFormInputSlotName;
    input.style.opacity = '0';
    input.style.position = 'absolute';
    input.style.zIndex = '-1';
    input.type = type;
    input.setAttribute('role', type);
    input.setAttribute('aria-labelledby', host.id);
    input.addEventListener('invalid', (event) => event.preventDefault());
    host.appendChild(input);
  }
  if (canBeCheckable(component))
    input.checked = component.checked;
  if (canBeIndeterminate(component))
    input.indeterminate = component.indeterminate;
  input.disabled = disabled;
  input.required = required;
  input.value = value;
  return h("slot", { name: hiddenFormInputSlotName });
};

const mapGet = (key, obj, fallback = 'default') => obj[key] || obj[fallback] || undefined;

var ValidatorNames;
(function (ValidatorNames) {
  ValidatorNames["max"] = "max";
  ValidatorNames["maxLength"] = "maxLength";
  ValidatorNames["min"] = "min";
  ValidatorNames["minLength"] = "minLength";
  ValidatorNames["pattern"] = "pattern";
  ValidatorNames["required"] = "required";
})(ValidatorNames || (ValidatorNames = {}));
const defaultErrorMessagesConfig = {
  required: 'This entry is required',
};
/**  reducer callback: iterates through validators and returns the first one that fails it's validate function  */
function combineValidators(v1, v2) {
  const combined = {
    validate: (vParam) => {
      const result1 = v1.validate(vParam);
      const result2 = v2.validate(vParam);
      if (!result1)
        combined.errorMessage = v1.errorMessage;
      else if (!result2)
        combined.errorMessage = v2.errorMessage;
      return result1 && result2;
    },
  };
  return combined;
}
/** creates a list of ValidatorEntry objects from the ValidatorNames enum that exist on the component class
    example: [
      {name: 'required},
      {name: 'min', options: '5'}
    ]
  */
function composeValidatorList(componentClass) {
  const vList = Object.values(ValidatorNames)
    .filter((vname) => componentClass[vname] != null)
    .map((validatorEntry) => ({ name: validatorEntry, options: componentClass[validatorEntry] }));
  return getValidator(vList);
}
const defaultValidator = () => ({
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  validate: (_value) => true,
});
const getValidator = (list) => (list || [])
  .map((validator) => validatorFactory(validator.name))
  .reduce(combineValidators, defaultValidator());
const isRequiredValidator = () => ({
  errorMessage: defaultErrorMessagesConfig.required,
  validate: (inputElement) => inputElement && 'validity' in inputElement ? !inputElement.validity.valueMissing : true,
});
const validatorFactory = (name) => {
  switch (name) {
    case ValidatorNames.required:
      return isRequiredValidator();
    default:
      return defaultValidator();
  }
};

export { HiddenFormInputSlot as H, composeValidatorList as a, connectForm as b, closestElementCrossShadowBoundary as c, defaultValidator as d, disconnectForm as e, mapGet as m, queryElementRoots as q };

//# sourceMappingURL=index-013df788.js.map