// LayoutSplitter -- two panes with a divider the reader can move.
//
//   <LayoutSplitter size={40} minSize={20}>
//     <div data-slot="start">...</div>
//     <div data-slot="end">...</div>
//   </LayoutSplitter>
//
// The dragging lives in the reactive runtime behind data-wrn-splitter. Pointer
// moves fire far too often to route through a client function, and a state
// write made inside a pointermove callback is dropped, so the resolved size is
// held in the DOM as the --wrn-split custom property and these styles read it.
//
// This component previously declared resizeStart, resize and resizeEnd with no
// pointer handling whatsoever: a caller wired up @resize and received nothing,
// for ever, with no error.
//
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
// and silently swallows the rule that follows it.
component LayoutSplitter {
  outputs {
    // Named sizeChange rather than resize so it cannot be confused with the
    // native window event a caller may already be listening for. An earlier
    // comment here claimed a natively-named output could never reach a parent
    // binding; that was wrong, and the rename was never what fixed anything.
    sizeChange(payload: { size: number })
  }

  props {
    color: string = "primary"
    size: number = 50
    orientation: string = "horizontal"
    // Smallest share either pane may take, as a percentage. It also fixes the
    // upper bound at 100 - minSize, so neither pane can be dragged away to
    // nothing and left unrecoverable by pointer.
    minSize: number = 15
    step: number = 5
    label: string = "Resize panels"
    class: string = ""
  }

  functions {
    // The runtime resolves the size and announces it; this turns that into the
    // declared output so a parent @resize binding receives it.
    client function reportResize(sourceEvent) {
      var detail = sourceEvent ? sourceEvent.detail : null
      if (!detail) {
        return
      }
      output.sizeChange({ size: detail.size })
    }

    shared function isVertical() {
      return orientation === "vertical"
    }

    shared function lowerBound() {
      var value = Number(minSize)
      if (!value || value < 0) {
        return 15
      }
      return Math.min(45, value)
    }

    shared function upperBound() {
      return 100 - lowerBound()
    }

    // Sizes arrive as HTML attributes, so a value outside the bounds is
    // routine rather than exceptional. Clamp instead of rendering something
    // the reader cannot undo.
    shared function currentSize() {
      var value = Number(size)
      if (!value || value < 0) {
        return 50
      }
      return Math.min(upperBound(), Math.max(lowerBound(), value))
    }

    shared function stepSize() {
      var value = Number(step)
      return value && value > 0 ? value : 5
    }
  }

  view {
    <div
      {...attrs}
      data-ui-component="LayoutSplitter"
      class='wire-splitter {class}'
      data-color='{color}'
      data-orientation='{orientation}'
      data-wrn-splitter='{isVertical() ? "vertical" : "horizontal"}'
      data-wrn-splitter-min='{lowerBound()}'
      data-wrn-splitter-step='{stepSize()}'
      style='--wrn-split:{currentSize()}%;'
      @wrnexus:splitter:resize='reportResize(event)'
    >
      <div class="wire-splitter__pane wire-splitter__pane--start">
        <slot name="start"></slot>
      </div>

      <div
        class="wire-splitter__handle"
        data-wrn-splitter-handle="true"
        role="separator"
        tabindex="0"
        aria-label='{label}'
        aria-orientation='{isVertical() ? "horizontal" : "vertical"}'
        aria-valuenow='{currentSize()}'
        aria-valuemin='{lowerBound()}'
        aria-valuemax='{upperBound()}'
      >
        <span class="wire-splitter__grip" aria-hidden="true"></span>
      </div>

      <div class="wire-splitter__pane wire-splitter__pane--end">
        <slot name="end"></slot>
      </div>

      <slot />
    </div>
  }

  style {
    .wire-splitter {
      --splitter-accent: var(--wire-color-primary);
      display: grid;
      /* The first track follows the size the runtime resolves. */
      grid-template-columns: var(--wrn-split, 50%) auto minmax(0, 1fr);
      align-items: stretch;
      width: 100%;
      min-width: 0;
    }

    .wire-splitter[data-color="secondary"] {
      --splitter-accent: var(--wire-color-secondary);
    }

    .wire-splitter[data-color="success"] {
      --splitter-accent: var(--wire-color-success);
    }

    .wire-splitter[data-color="danger"] {
      --splitter-accent: var(--wire-color-danger);
    }

    .wire-splitter[data-color="info"] {
      --splitter-accent: var(--wire-color-info);
    }

    .wire-splitter[data-orientation="vertical"] {
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: var(--wrn-split, 50%) auto minmax(0, 1fr);
    }

    .wire-splitter__pane {
      min-width: 0;
      min-height: 0;
      overflow: auto;
    }

    .wire-splitter__handle {
      display: flex;
      align-items: center;
      justify-content: center;
      flex: 0 0 auto;
      padding: 0 0.25rem;
      border: 0;
      background: transparent;
      cursor: col-resize;
      /* Without this the pointer drag selects the text in both panes. */
      touch-action: none;
      user-select: none;
    }

    .wire-splitter[data-orientation="vertical"] .wire-splitter__handle {
      padding: 0.25rem 0;
      cursor: row-resize;
    }

    .wire-splitter__grip {
      display: block;
      width: 2px;
      height: 100%;
      min-height: 1.5rem;
      border-radius: 999px;
      background: var(--wire-color-border);
      transition: background 140ms ease;
    }

    .wire-splitter[data-orientation="vertical"] .wire-splitter__grip {
      width: 100%;
      min-width: 1.5rem;
      height: 2px;
    }

    .wire-splitter__handle:hover .wire-splitter__grip,
    .wire-splitter[data-wrn-splitter-dragging="true"] .wire-splitter__grip {
      background: var(--splitter-accent);
    }

    .wire-splitter__handle:focus-visible {
      outline: 2px solid var(--splitter-accent);
      outline-offset: -2px;
      border-radius: var(--wire-radius-sm);
    }

    /*
     * Two panes side by side stop making sense on a phone. Stacking them keeps
     * both readable, and the divider stops being draggable because the grid
     * no longer has a second track to trade against.
     */
    @media (max-width: 639px) {
      .wire-splitter,
      .wire-splitter[data-orientation="vertical"] {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto auto auto;
      }

      .wire-splitter__handle {
        cursor: default;
      }
    }
  }
}
