/**
 * Merge multiple props objects — like Solid's `merge`, but with Base UI's
 * special cases: `class`, `style` and `on*` handlers are composed rather
 * than overwritten, and `undefined` values are **skipped** (so default props
 * from earlier sources win when a later override is absent, matching Base
 * UI React's `mergeProps` semantics, not Solid's `merge` which treats
 * `undefined` as a real value).
 *
 * The merge is lazy and reactive: every property access re-reads the source
 * array, and the returned object's own-keys set re-collects on each
 * enumeration. That means state-attr projections that introduce new `data-*`
 * keys over time are seen by consumers as those keys appear.
 *
 * Behaviour:
 * - `class`: concatenated string ("a b c"). Objects/arrays (clsx-style) are
 *   flattened into a single string.
 * - `style`: merged shallowly, right-most wins per property.
 * - `on*`: chained. Each handler runs right-to-left; the right-most handler
 *   can call `preventBaseUIHandler()` on the event to stop earlier handlers.
 * - everything else: right-most non-`undefined` wins.
 *
 * **Strict-read note:** The proxy's `ownKeys` and `getOwnPropertyDescriptor`
 * traps fire during body-time enumeration (Solid's `mergeProps` runs
 * `Object.getOwnPropertyNames` synchronously in the consumer component's
 * body, which is an intentionally-untracked scope). The reads inside those
 * traps are intentional snapshots — Solid stores the resulting descriptors
 * (which point back at our reactive getter via `getOwnPropertyDescriptor`)
 * so reactivity flows through later, when consumers read individual keys
 * inside their own tracked scopes. We wrap the enumeration-trap bodies in
 * `untrack` to silence the false-positive `[STRICT_READ_UNTRACKED]` warning
 * that the component-body label scope would otherwise raise on every
 * source-key probe.
 */

import { untrack } from "solid-js";
const EVENT_HANDLER_RE = /^on[A-Z]/;
function isEventHandlerKey(key) {
  return EVENT_HANDLER_RE.test(key);
}
function classToString(value) {
  if (value == null || value === false) return "";
  if (typeof value === "string") return value;
  if (typeof value === "number") return String(value);
  if (Array.isArray(value)) {
    let out = "";
    for (const part of value) {
      const s = classToString(part);
      if (s) out = out ? `${out} ${s}` : s;
    }
    return out;
  }
  if (typeof value === "object") {
    let out = "";
    for (const [key, v] of Object.entries(value)) {
      if (v) out = out ? `${out} ${key}` : key;
    }
    return out;
  }
  return "";
}
function mergeClass(sources) {
  let out = "";
  for (const src of sources) {
    if (!src) continue;
    const v = src.class;
    if (v == null) continue;
    const s = classToString(v);
    if (s) out = out ? `${out} ${s}` : s;
  }
  return out || undefined;
}
function mergeStyle(sources) {
  let merged;
  let lastString;
  for (const src of sources) {
    if (!src) continue;
    const v = src.style;
    if (v == null) continue;
    if (typeof v === "string") {
      lastString = v;
      merged = undefined;
      continue;
    }
    lastString = undefined;
    merged = merged ? Object.assign(merged, v) : {
      ...v
    };
  }
  return merged ?? lastString;
}
function looksLikeEvent(value) {
  return value != null && typeof value === "object" && "type" in value;
}
function armEvent(event) {
  event.preventBaseUIHandler = () => {
    event.baseUIHandlerPrevented = true;
  };
}
function chainHandlers(handlers) {
  if (handlers.length === 0) return undefined;
  const [first] = handlers;
  if (handlers.length === 1 && first) {
    return (...args) => {
      const ev = args[0];
      if (looksLikeEvent(ev)) armEvent(ev);
      return first(...args);
    };
  }
  return (...args) => {
    const ev = args[0];
    if (looksLikeEvent(ev)) armEvent(ev);
    let lastResult;
    for (let i = handlers.length - 1; i >= 0; i--) {
      const h = handlers[i];
      if (!h) continue;
      lastResult = h(...args);
      if (looksLikeEvent(ev) && ev.baseUIHandlerPrevented) break;
    }
    return lastResult;
  };
}
function resolveKey(key, sources) {
  if (key === "class") return mergeClass(sources);
  if (key === "style") return mergeStyle(sources);
  if (isEventHandlerKey(key)) {
    const handlers = [];
    for (const src of sources) {
      if (!src) continue;
      const h = src[key];
      if (typeof h === "function") handlers.push(h);
    }
    return chainHandlers(handlers);
  }
  for (let i = sources.length - 1; i >= 0; i--) {
    const src = sources[i];
    if (!src) continue;
    if (!(key in src)) continue;
    const v = src[key];
    if (v === undefined) continue;
    return v;
  }
  return undefined;
}

/**
 * Does any source declare `key` as a getter (vs a data property)? A getter
 * is intentionally lazy — its value at enumeration time isn't meaningful,
 * and invoking it during enumeration can cascade into child component
 * body evaluation (which re-establishes `strictRead` under the child's
 * label, defeating the outer `untrack` and surfacing
 * `[STRICT_READ_UNTRACKED]`). For these keys we trust the producer's
 * intent and include them unconditionally; the downstream descriptor we
 * return from `getOwnPropertyDescriptor` still gates the actual value
 * through a getter, so RFC 07 absence semantics still hold for any
 * eventual `undefined` value.
 */
function hasGetterForKey(sources, key) {
  for (const src of sources) {
    if (!src) continue;
    const desc = Object.getOwnPropertyDescriptor(src, key);
    if (desc && typeof desc.get === "function") return true;
  }
  return false;
}
function collectOwnKeys(sources) {
  // `untrack` here clears `strictRead` for the duration of the
  // enumeration. This trap is invoked from body-time `Object.keys` /
  // `getOwnPropertyNames` paths (Solid's `mergeProps`, JSX spread setup),
  // which are intentionally non-tracking and intentionally take a key
  // snapshot — the reactivity flows through the per-key getter
  // descriptors we return from `getOwnPropertyDescriptor`, not through
  // this enumeration. See module doc.
  return untrack(() => {
    const keys = new Set();
    for (const src of sources) {
      if (!src) continue;
      for (const k of Object.keys(src)) keys.add(k);
    }
    // Mirror the resolve-semantics: if every source's value at `k` is
    // `undefined` (the skip case), the key shouldn't appear as "own"
    // either. Without this, Solid 2 DOM spreads would see
    // `class`/`value` as present-with-`undefined` — meaningfully
    // different from absent (RFC 07).
    const out = [];
    for (const k of keys) {
      if (hasGetterForKey(sources, k)) {
        out.push(k);
        continue;
      }
      if (resolveKey(k, sources) !== undefined) out.push(k);
    }
    return out;
  });
}

/**
 * Merge an array of props sources into a reactive proxy. Every lookup re-reads
 * the sources, so getters (including state-projection getters added by
 * `renderElement`) stay live.
 *
 * **Solid 2 quirk**: attributes whose initial getter value is `undefined` are
 * absent from `ownKeys` at first spread and will NOT become reactive later —
 * the DOM binding never subscribes. Consumers that need a late-binding
 * attribute (e.g. `aria-labelledby`) must seed the key (`""`) so it stays
 * in `ownKeys`, or project it imperatively via `setAttribute` (see
 * `<Input>`'s `aria-invalid` handling). See
 * `test/internals/merge-props-undefined-attr.test.tsx` for the pinned
 * verdict.
 */
export function mergePropsN(sources) {
  return new Proxy({}, {
    get(_t, key) {
      if (typeof key === "symbol") return undefined;
      return resolveKey(key, sources);
    },
    has(_t, key) {
      if (typeof key === "symbol") return false;
      // `has` must agree with `get`: if every source resolves the key
      // to `undefined`, the proxy should report it as absent.
      // Otherwise Solid 2's DOM binding layer would treat
      // `<input value={undefined}>` as "present with undefined" rather
      // than "missing" — meaningfully different for form controls per
      // RFC 07.
      return resolveKey(key, sources) !== undefined;
    },
    ownKeys() {
      return collectOwnKeys(sources);
    },
    getOwnPropertyDescriptor(_t, key) {
      if (typeof key === "symbol") return undefined;
      // Same asymmetry fix as `has`: keys that resolve to undefined
      // aren't "own" even if the source object listed them. The
      // presence-probe is wrapped in `untrack` for the same
      // strict-read reason as `collectOwnKeys` — Solid's `mergeProps`
      // calls this trap synchronously during component-body setup.
      // The returned descriptor's `get` runs in the consumer's later
      // (tracked) scope, so reactivity is preserved.
      //
      // Skip the probe for keys backed by a getter on any source —
      // see `hasGetterForKey`.
      if (!hasGetterForKey(sources, key) && untrack(() => resolveKey(key, sources)) === undefined) return undefined;
      // Return a getter-based descriptor so consumers that copy
      // descriptors (e.g. `@solidjs/web`'s `omit`) preserve reactivity
      // rather than snapshotting the current value.
      return {
        enumerable: true,
        configurable: true,
        get: () => resolveKey(key, sources)
      };
    }
  });
}
//# sourceMappingURL=merge-props-n.jsx.map
