import { isServer, nothing } from 'lit';
export const asPercent = (part, whole) => (part / whole) * 100;
export const clamp = (number, min, max) => Math.max(min, Math.min(number, max));
export function numberOfDecimals(number) {
    const [_, decimals] = number.toString().split('.');
    return decimals ? decimals.length : 0;
}
export function roundPrecise(number, magnitude = 1) {
    const factor = 10 ** magnitude;
    return Math.round(number * factor) / factor;
}
export function numberInRangeInclusive(value, min, max) {
    return value >= min && value <= max;
}
export function createCounter() {
    let i = 0;
    return () => {
        i++;
        return i;
    };
}
export function isLTR(element) {
    return element.matches(':dir(ltr)');
}
export function formatString(template, ...params) {
    const length = params.length;
    return template.replace(/{(\d+)}/g, (match, index) => index >= length ? match : `${params[index]}`);
}
export function asNumber(value, fallback = 0) {
    const parsed = Number.parseFloat(value);
    return Number.isNaN(parsed) ? fallback : parsed;
}
export function wrap(min, max, value) {
    if (value < min) {
        return max;
    }
    if (value > max) {
        return min;
    }
    return value;
}
export function isDefined(value) {
    return value !== undefined;
}
function createNodeFilter(predicate) {
    return {
        acceptNode: (node) => !predicate || predicate(node)
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_SKIP,
    };
}
export function* iterNodes(root, options) {
    if (!isDefined(globalThis.document)) {
        return;
    }
    const whatToShow = options?.show
        ? NodeFilter[options.show]
        : NodeFilter.SHOW_ALL;
    const nodeFilter = options?.filter
        ? createNodeFilter(options.filter)
        : undefined;
    const treeWalker = document.createTreeWalker(root, whatToShow, nodeFilter);
    while (treeWalker.nextNode()) {
        yield treeWalker.currentNode;
    }
}
export function getRoot(element, options) {
    return element.getRootNode(options);
}
export function getElementByIdFromRoot(root, id) {
    return getRoot(root).getElementById(id);
}
export function isElement(node) {
    return node instanceof Node && node.nodeType === Node.ELEMENT_NODE;
}
export function getElementsFromEventPath(event) {
    return event.composedPath().filter((item) => isElement(item));
}
export function findElementFromEventPath(predicate, event) {
    const func = isString(predicate)
        ? (e) => e.matches(predicate)
        : (e) => predicate(e);
    return getElementsFromEventPath(event).find(func);
}
export function groupBy(array, key) {
    const result = {};
    const _get = isFunction(key) ? key : (item) => item[key];
    for (const item of array) {
        const category = _get(item);
        const group = result[category];
        if (Array.isArray(group)) {
            group.push(item);
        }
        else {
            result[category] = [item];
        }
    }
    return result;
}
export function first(arr) {
    return arr.at(0);
}
export function last(arr) {
    return arr.at(-1);
}
export function modulo(n, d) {
    return ((n % d) + d) % d;
}
export function take(iterable, n) {
    const result = [];
    let i = 0;
    let current = iterable.next();
    while (i < n && !current.done) {
        result.push(current.value);
        current = iterable.next();
        i++;
    }
    return result;
}
export function* chunk(arr, size) {
    if (size < 1) {
        throw new Error('size must be an integer >= 1');
    }
    for (let i = 0; i < arr.length; i += size) {
        yield arr.slice(i, i + size);
    }
}
export function splitToWords(text) {
    const input = text.replaceAll(/[^a-zA-Z0-9\s-_]/g, '');
    if (/[\s-_]+/.test(input))
        return input.split(/[\s-_]+/);
    return input.split(/(?=[A-Z])+/);
}
export function toKebabCase(text) {
    const input = text.trim();
    return splitToWords(input).join('-').toLocaleLowerCase();
}
export function isFunction(value) {
    return typeof value === 'function';
}
export function isString(value) {
    return typeof value === 'string';
}
export function isObject(value) {
    return value != null && typeof value === 'object';
}
export function isPlainObject(value) {
    if (!isObject(value)) {
        return false;
    }
    const proto = Object.getPrototypeOf(value);
    const hasObjectPrototype = proto === null ||
        proto === Object.prototype ||
        Object.getPrototypeOf(proto) === null;
    return hasObjectPrototype
        ? Object.prototype.toString.call(value) === '[object Object]'
        : false;
}
function isUnsafeProperty(key) {
    return key === '__proto__' || key === 'constructor' || key === 'prototype';
}
export function isEventListenerObject(x) {
    return isObject(x) && 'handleEvent' in x;
}
export function addWeakEventListener(element, event, listener, options) {
    const weakRef = new WeakRef(listener);
    const wrapped = (evt) => {
        const handler = weakRef.deref();
        return isEventListenerObject(handler)
            ? handler.handleEvent(evt)
            : handler?.(evt);
    };
    element.addEventListener(event, wrapped, options);
}
export function addSafeEventListener(target, eventName, handler, options) {
    if (isServer) {
        return;
    }
    const boundHandler = (event) => handler.call(target, event);
    target.addEventListener(eventName, boundHandler, options);
}
export function isEmpty(x) {
    return 'length' in x ? x.length < 1 : x.size < 1;
}
export function asArray(value) {
    if (!isDefined(value))
        return [];
    return Array.isArray(value) ? value : [value];
}
export function partition(array, isTruthy) {
    const truthy = [];
    const falsy = [];
    for (const item of array) {
        (isTruthy(item) ? truthy : falsy).push(item);
    }
    return [truthy, falsy];
}
export function getCenterPoint(element) {
    const { left, top, width, height } = element.getBoundingClientRect();
    return {
        x: left + width * 0.5,
        y: top + height * 0.5,
    };
}
export function roundByDPR(value) {
    const dpr = globalThis.devicePixelRatio || 1;
    return Math.round(value * dpr) / dpr;
}
export function scrollIntoView(element, config) {
    if (!element) {
        return;
    }
    element.scrollIntoView(Object.assign({
        behavior: 'auto',
        block: 'nearest',
        inline: 'nearest',
    }, config));
}
export function isRegExp(value) {
    return value != null && value.constructor === RegExp;
}
export function equal(a, b, visited = new WeakSet()) {
    if (Object.is(a, b)) {
        return true;
    }
    if (isObject(a) && isObject(b)) {
        if (a.constructor !== b.constructor) {
            return false;
        }
        if (visited.has(a) && visited.has(b)) {
            return true;
        }
        visited.add(a);
        visited.add(b);
        if (isRegExp(a) && isRegExp(b)) {
            return a.source === b.source && a.flags === b.flags;
        }
        if (a instanceof Map && b instanceof Map) {
            if (a.size !== b.size) {
                return false;
            }
            for (const [keyA, valueA] of a.entries()) {
                let found = false;
                for (const [keyB, valueB] of b.entries()) {
                    if (equal(keyA, keyB, visited) && equal(valueA, valueB, visited)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    return false;
                }
            }
            return true;
        }
        if (a instanceof Set && b instanceof Set) {
            if (a.size !== b.size) {
                return false;
            }
            for (const valueA of a) {
                let found = false;
                for (const valueB of b) {
                    if (equal(valueA, valueB, visited)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    return false;
                }
            }
            return true;
        }
        if (Array.isArray(a) && Array.isArray(b)) {
            const length = a.length;
            if (length !== b.length) {
                return false;
            }
            for (let i = 0; i < length; i++) {
                if (!equal(a[i], b[i], visited)) {
                    return false;
                }
            }
            return true;
        }
        if (a.valueOf !== Object.prototype.valueOf) {
            return a.valueOf() === b.valueOf();
        }
        if (a.toString !== Object.prototype.toString) {
            return a.toString() === b.toString();
        }
        const aKeys = Object.keys(a);
        const bKeys = Object.keys(b);
        if (aKeys.length !== bKeys.length) {
            return false;
        }
        for (const key of aKeys) {
            if (!Object.hasOwn(b, key)) {
                return false;
            }
        }
        for (const key of aKeys) {
            if (!equal(a[key], b[key], visited)) {
                return false;
            }
        }
        visited.delete(a);
        visited.delete(b);
        return true;
    }
    return false;
}
export function setStyles(element, styles) {
    merge(element.style, styles);
}
export function merge(target, source) {
    const sourceKeys = Object.keys(source);
    const length = sourceKeys.length;
    for (let i = 0; i < length; i++) {
        const key = sourceKeys[i];
        if (isUnsafeProperty(key)) {
            continue;
        }
        const sourceValue = source[key];
        const targetValue = target[key];
        if (Array.isArray(sourceValue)) {
            if (Array.isArray(targetValue)) {
                target[key] = merge(targetValue, sourceValue);
            }
            else {
                target[key] = merge([], sourceValue);
            }
        }
        else if (isPlainObject(sourceValue)) {
            if (isPlainObject(targetValue)) {
                target[key] = merge(targetValue, sourceValue);
            }
            else {
                target[key] = merge({}, sourceValue);
            }
        }
        else if (targetValue === undefined || sourceValue !== undefined) {
            target[key] = sourceValue;
        }
    }
    return target;
}
export function toMerged(target, source) {
    return merge(structuredClone(target), source);
}
export function bindIf(assertion, value) {
    return assertion
        ? (value ?? nothing)
        : nothing;
}
//# sourceMappingURL=util.js.map