'use strict';

const index = require('./index-5428622a.js');

// Adopted and modified solution from willmcpo
// https://github.com/willmcpo/body-scroll-lock/blob/master/src/bodyScrollLock.js
// which
// Adopted and modified solution from Bohdan Didukh (2017)
// https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi
let locks = [];
let previousBodyOverflowSetting;
let previousBodyPaddingRight;
const setOverflowHidden = (options) => {
  // If previousBodyPaddingRight is already set, don't set it again.
  if (previousBodyPaddingRight === undefined) {
    const reserveScrollBarGap = !!options && options.reserveScrollBarGap === true;
    const scrollBarGap = window.innerWidth - document.documentElement.clientWidth;
    if (reserveScrollBarGap && scrollBarGap > 0) {
      const computedBodyPaddingRight = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'), 10);
      previousBodyPaddingRight = document.body.style.paddingRight;
      document.body.style.paddingRight = `${computedBodyPaddingRight + scrollBarGap}px`;
    }
  }
  // If previousBodyOverflowSetting is already set, don't set it again.
  if (previousBodyOverflowSetting === undefined) {
    previousBodyOverflowSetting = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
  }
};
const restoreOverflowSetting = () => {
  if (previousBodyPaddingRight !== undefined) {
    document.body.style.paddingRight = previousBodyPaddingRight;
    // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it
    // can be set again.
    previousBodyPaddingRight = undefined;
  }
  if (previousBodyOverflowSetting !== undefined) {
    document.body.style.overflow = previousBodyOverflowSetting;
    // Restore previousBodyOverflowSetting to undefined
    // so setOverflowHidden knows it can be set again.
    previousBodyOverflowSetting = undefined;
  }
};
const disableBodyScroll = (targetElement, options) => {
  // targetElement must be provided
  if (!targetElement) {
    // eslint-disable-next-line no-console
    console.error('disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.');
    return;
  }
  // disableBodyScroll must not have been called on this targetElement before
  if (locks.some((lock) => lock.targetElement === targetElement)) {
    return;
  }
  const lock = {
    targetElement,
    options: options || {},
  };
  locks = [...locks, lock];
  setOverflowHidden(options);
};
const clearAllBodyScrollLocks = () => {
  restoreOverflowSetting();
  locks = [];
};
const enableBodyScroll = (targetElement) => {
  if (!targetElement) {
    // eslint-disable-next-line no-console
    console.error('enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.');
    return;
  }
  locks = locks.filter((lock) => lock.targetElement !== targetElement);
  restoreOverflowSetting();
};

// import { CSS_UTILITY } from './resources';
// import { guid } from './guid';
/** Determines if element contains text */
const hasText = (element) => element.textContent.trim() !== '';
// 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, hasText, 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;
}
/** Checks if component has default selected index */
function hasDefaultSelectedIndex(component) {
  return 'defaultSelectedIndex' 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() {
  if (hasDefaultSelectedIndex(this))
    this.onFormReset();
  else
    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;
  if (hasDefaultSelectedIndex(component))
    component.defaultSelectedIndex = component.defaultSelectedIndex;
}
/** 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, hiddenInput, host, max, maxlength, min, minlength, name, pattern, readonly, required, value } = 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.addEventListener('invalid', (event) => event.preventDefault());
    input.setAttribute('aria-hidden', 'true');
    if (max)
      input.setAttribute('max', max);
    if (maxlength)
      input.setAttribute('maxlength', maxlength);
    if (min)
      input.setAttribute('min', min);
    if (minlength)
      input.setAttribute('minlength', minlength);
    if (pattern)
      input.setAttribute('pattern', pattern);
    if (type)
      input.setAttribute('type', type);
    if (readonly)
      input.readOnly = readonly;
    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 index.h("slot", { name: hiddenFormInputSlotName });
};

const getFocusableChildren = ($root) => {
  const depth = 0;
  const maxDepth = 20;
  const focusableElements = [];
  if (depth >= maxDepth) {
    return focusableElements;
  }
  const children = Array.from($root.children || []);
  for (const $child of children) {
    // skip if the elemnt is hidden
    if ($child.hasAttribute('hidden') || $child.style.display === 'none' || $child.style.visibility === 'hidden') {
      continue;
    }
    // add to the focusableElements if the element is focusable
    if ($child.tabIndex !== -1 || typeof $child.setFocus === 'function') {
      focusableElements.push($child);
    }
    // search for focusable elements in the shadow root if there is one
    if ($child.shadowRoot != null) {
      focusableElements.push(...getFocusableChildren($child.shadowRoot));
    }
    // search assigned nodes for focusable elements if the element is a slot
    else if ($child.tagName === 'SLOT') {
      focusableElements.push(...getFocusableChildrenInSlot($child));
    }
    // serch for focusable elements in the element's children
    else {
      focusableElements.push(...getFocusableChildren($child));
    }
  }
  return focusableElements;
};
const getFocusableChildrenInSlot = ($slot) => {
  const assignedNodes = $slot.assignedNodes().filter((node) => node.nodeType === 1);
  if (assignedNodes.length > 0) {
    const $slotParent = assignedNodes[0].parentElement;
    return getFocusableChildren($slotParent);
  }
  else
    return [];
};

/*
Chain this to a function when you need to wait for the next tick of the event loop to run.
Why request animation frame instead of setTimeout?

setTimeout:

    Calls a function or evaluates an expression after a specified number of milliseconds.
    Doesn't mean that the function will be called after the exactly specified interval.
    Less accurate, it can cause a browser to miss the frame.

requestAnimationFrame:

    Works similarly to setTimeout.
    More accurate. it's called right before the next repaint in the browser occurs.
    Less resources.

nextTick
1. Promise constructor internally calls requestAnimationFrame; the browser internally schedules it as a request for animation.
2. When browser is ready to animate requested frame, it invokes the callback provided to requestAnimationFrame, in this case it's the resolve() callback from the promise.
    In addition resolve will be passed with a timestamp from requestAnimationFrame i.e. the promise will be resolved with a timestamp value.

*/
const nextTick = () => new Promise(requestAnimationFrame);

const getLabelText = (element) => { var _a; return hasText(element) ? element.textContent : ((_a = document.querySelector(`label[for="${element.id}"]`)) === null || _a === void 0 ? void 0 : _a.textContent) || ''; };
const guid = () => [2, 1, 1, 1, 3]
  .map((count) => {
  let out = '';
  for (let i = 0; i < count; i++) {
    out += (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  }
  return out;
})
  .join('-');
const mapGet = (key, obj, fallback = 'default') => obj[key] || obj[fallback] || undefined;

/**
As of version 4 the default sort order of enums is the order in which they are defined.
https://github.com/UselessPickles/ts-enum-util/issues/7#issuecomment-475499821
*/
var ValidatorNames;
(function (ValidatorNames) {
  ValidatorNames["required"] = "required";
  ValidatorNames["max"] = "max";
  ValidatorNames["maxlength"] = "maxlength";
  ValidatorNames["min"] = "min";
  ValidatorNames["minlength"] = "minlength";
  ValidatorNames["pattern"] = "pattern";
})(ValidatorNames || (ValidatorNames = {}));
const defaultErrorMessagesConfig = {
  required: () => 'This entry is required',
  maxlength: (options) => `Please enter a value that is ${options} characters or less`,
  minlength: (options) => `Please enter a value that is ${options} characters or more`,
  pattern: () => 'Please enter a value in the correct format',
};
/**  reducer callback: iterates through validators and returns the first one that fails it's validate function  */
const 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'}
    ]
  */
const composeValidatorList = (componentClass) => Object.values(ValidatorNames)
  .filter((vname) => componentClass[vname] != null)
  .map((validatorEntry) => ({
  name: validatorEntry,
  customErrorMessage: componentClass[`${validatorEntry}Message`],
  options: componentClass[validatorEntry],
}));
const defaultValidator = () => ({
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  validate: (_value) => true,
});
const getValidator = (list) => (list || [])
  .map((validatorEntry) => validatorFactory(validatorEntry))
  .reduce(combineValidators, defaultValidator());
const initializeValidators = (componentClass) => {
  const validatorList = composeValidatorList(componentClass);
  return getValidator(validatorList);
};
const isRequiredValidator = (customErrorMessage) => ({
  errorMessage: customErrorMessage || defaultErrorMessagesConfig.required(),
  validate: (inputElement) => inputElement && 'validity' in inputElement ? !inputElement.validity.valueMissing : true,
});
const maxlengthValidator = (customErrorMessage, options) => ({
  errorMessage: customErrorMessage || defaultErrorMessagesConfig.maxlength(options),
  validate: (inputElement) => inputElement && 'validity' in inputElement ? !inputElement.validity.tooLong : true,
});
const minlengthValidator = (customErrorMessage, options) => ({
  errorMessage: customErrorMessage || defaultErrorMessagesConfig.minlength(options),
  validate: (inputElement) => inputElement && 'validity' in inputElement ? !inputElement.validity.tooShort : true,
});
const patternValidator = (customErrorMessage) => ({
  errorMessage: customErrorMessage || defaultErrorMessagesConfig.pattern(),
  validate: (inputElement) => inputElement && 'validity' in inputElement ? !inputElement.validity.patternMismatch : true,
});
const validatorFactory = (validatorEntry) => {
  const { name, customErrorMessage, options } = validatorEntry;
  const { maxlength, minlength, pattern, required } = ValidatorNames;
  switch (name) {
    case maxlength:
      return maxlengthValidator(customErrorMessage, options);
    case minlength:
      return minlengthValidator(customErrorMessage, options);
    case pattern:
      return patternValidator(customErrorMessage);
    case required:
      return isRequiredValidator(customErrorMessage);
    default:
      return defaultValidator();
  }
};

exports.HiddenFormInputSlot = HiddenFormInputSlot;
exports.clearAllBodyScrollLocks = clearAllBodyScrollLocks;
exports.closestElementCrossShadowBoundary = closestElementCrossShadowBoundary;
exports.connectForm = connectForm;
exports.defaultValidator = defaultValidator;
exports.disableBodyScroll = disableBodyScroll;
exports.disconnectForm = disconnectForm;
exports.enableBodyScroll = enableBodyScroll;
exports.getFocusableChildren = getFocusableChildren;
exports.getLabelText = getLabelText;
exports.guid = guid;
exports.hasText = hasText;
exports.initializeValidators = initializeValidators;
exports.mapGet = mapGet;
exports.nextTick = nextTick;
exports.queryElementRoots = queryElementRoots;

//# sourceMappingURL=index-931f0310.js.map