{"version":3,"file":"step.cjs","names":[],"sources":["../src/content/stepper/step.ts"],"sourcesContent":["import { bind, define, getHost, html, inject, prop, useSlots, watchEffect } from '@vielzeug/ore';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport '../icon/icon';\nimport { disablableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, forcedColorsFocusMixin } from '../../styles';\nimport { isStepNavigable } from './_is-step-navigable';\nimport stepStyles from './step.css?inline';\nimport { STEPPER_CTX } from './stepper';\n\nexport type OreStepProps = {\n  /**\n   * Theme color. Inherited from the parent `ore-stepper` when nested inside one (overrides\n   * this value); only takes effect on its own when `ore-step` is rendered standalone.\n   */\n  color?: ThemeColor;\n  /** Disables this step — it cannot be navigated to and is skipped by keyboard navigation. */\n  disabled?: boolean;\n  /** Marks this step as failed/invalid. Overrides the completed/current indicator visuals. */\n  error?: boolean;\n  /**\n   * Marks this step as optional. Renders a small \"(optional)\" hint next to the label.\n   * Purely presentational — has no effect on navigation.\n   */\n  optional?: boolean;\n  /** Orientation. Inherited from the parent `ore-stepper` when nested inside one. */\n  orientation?: 'horizontal' | 'vertical';\n  /** Component size. Inherited from the parent `ore-stepper` when nested inside one. */\n  size?: ComponentSize;\n  /** Unique identifier, matches `ore-stepper`'s `value` attribute. */\n  value: string;\n};\n\n/**\n * A single step trigger. Must be placed as a direct child of `ore-stepper`, which provides\n * this step's current/completed/navigable/index/total state via context — those are derived,\n * read-only attributes on this element, not settable props.\n *\n * @element ore-step\n *\n * @attr {string} value - Unique identifier, matches the parent ore-stepper's `value` attribute\n * @attr {boolean} disabled - Prevents navigation to this step\n * @attr {boolean} error - Marks the step as failed/invalid\n * @attr {boolean} optional - Renders an \"(optional)\" hint next to the label\n * @attr {boolean} current - Read-only. Derived from position relative to the parent ore-stepper's `value`.\n * @attr {boolean} completed - Read-only. Derived from position relative to the parent ore-stepper's `value`.\n * @attr {boolean} navigable - Read-only. Derived from the parent ore-stepper's `clickable`/`linear`/`disabled`.\n * @attr {number} index - Read-only. This step's 1-based position among its siblings.\n * @attr {number} total - Read-only. Total sibling step count.\n * @attr {string} color - Inherited from ore-stepper: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Inherited from ore-stepper: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Inherited from ore-stepper: 'horizontal' | 'vertical'\n *\n * @slot - Step label\n * @slot description - Optional supporting text shown below the label\n * @slot icon - Custom icon replacing the step number/check/error indicator\n *\n * @part control - Clickable (or static) root element for the step\n * @part indicator - Circular indicator holding the number/icon\n * @part content - Wrapper around the label and description\n * @part label - Step label element\n * @part description - Step description element\n * @part connector - Connector line segments (leading and trailing halves) either side of this step's indicator\n *\n * @example\n * ```html\n * <ore-step value=\"details\">Account details</ore-step>\n * <ore-step value=\"review\" disabled>Review</ore-step>\n * ```\n */\nexport const STEP_TAG = 'ore-step' as const;\ndefine<OreStepProps>(STEP_TAG, {\n  props: {\n    ...disablableBundle,\n    color: prop.string<ThemeColor>(),\n    error: prop.bool(false),\n    optional: prop.bool(false),\n    orientation: prop.oneOf(['horizontal', 'vertical'] as const, 'horizontal'),\n    size: prop.string<ComponentSize>(),\n    value: prop.string(''),\n  },\n  setup(props) {\n    const el = getHost();\n    const slots = useSlots();\n    const stepperCtx = inject(STEPPER_CTX);\n\n    // ────────────────────────────────────────────────────────────────\n    // State derived from the parent ore-stepper's context — see stepper.ts's\n    // module doc comment for why this replaced parent-side attribute fan-out.\n    // ────────────────────────────────────────────────────────────────\n\n    // Plain functions, not `computed()` — every one of these ultimately reads\n    // `stepperCtx.stepValues`/`stepperCtx.currentValue`, which are themselves `computed()`s\n    // owned by the parent `ore-stepper`. Wrapping a *child* `computed()` around a *parent*\n    // `computed()` (a computed-to-computed dependency crossing a `provide()`/`inject()`\n    // boundary) is exactly the shape that surfaced two related bugs: a step's `completed`/\n    // `current` attributes going permanently stale after a few rapid selections, and — worse —\n    // a step's entire clickable/static control silently failing to render at all when its\n    // `ore-step` tag upgrades before its siblings exist in the light DOM yet (true whenever\n    // `ore-step`/`ore-stepper` are already `customElements.define()`d before this markup is\n    // parsed, e.g. every sandboxed live-preview iframe). Reading straight through to the\n    // parent's computeds on every call — no intermediate computed layer of our own to go\n    // stale — made both disappear. Cheap enough to not need memoizing (a couple of `indexOf`/\n    // comparisons over an already-memoized parent computed).\n    const stepIndex = (): number => (stepperCtx ? stepperCtx.stepValues.value.indexOf(props.value.value) : -1);\n    const totalSteps = (): number => stepperCtx?.stepValues.value.length ?? 0;\n    const currentIndex = (): number =>\n      stepperCtx ? stepperCtx.stepValues.value.indexOf(stepperCtx.currentValue.value ?? '') : -1;\n    // 1-based, always-sane values for on-screen text (the step number badge, the sr-only\n    // \"Step X of Y\" label) — falls back to \"1 of 1\" when rendered standalone with no parent\n    // `ore-stepper` to derive a real position from. Kept separate from the `index`/`total`\n    // *attributes* below, which correctly reflect nothing at all in that same standalone case.\n    const displayIndex = (): number => {\n      const index = stepIndex();\n\n      return index >= 0 ? index + 1 : 1;\n    };\n    const displayTotal = (): number => totalSteps() || 1;\n\n    const isCurrent = (): boolean => {\n      const index = stepIndex();\n      const current = currentIndex();\n\n      return index >= 0 && index === current;\n    };\n    // Purely positional — intentionally NOT gated on `props.error.value`. This also drives the\n    // `completed` *attribute* below, which `step.css` uses to color both connector segments\n    // either side of the indicator (`:host([completed]) .connector`); every *consumer* of this\n    // (the icon choice, the sr-only state label) already checks `error` first and short-circuits\n    // before it matters, so folding `!error` in here too would only end up suppressing\n    // `[completed]` on an error step that's before the current one — breaking the connector\n    // color chain right at that step instead of just swapping its icon.\n    const isCompleted = (): boolean => {\n      const index = stepIndex();\n      const current = currentIndex();\n\n      return index >= 0 && current >= 0 && index < current;\n    };\n    const isNavigable = (): boolean => {\n      const index = stepIndex();\n      const current = currentIndex();\n\n      return (\n        !!stepperCtx &&\n        isStepNavigable({\n          disabled: props.disabled.value === true,\n          index,\n          linear: stepperCtx.linear.value,\n          stepperClickable: stepperCtx.clickable.value,\n          stepperCurrentIndex: current,\n        })\n      );\n    };\n    const isDisabled = () => Boolean(props.disabled.value);\n\n    // Purely derived, read-only state — no matching `prop.*` declaration, so `bind()` is the\n    // sole writer and can safely reflect `undefined` (removes the attribute) when this step\n    // isn't nested inside an `ore-stepper` at all.\n    bind({\n      attr: {\n        completed: () => (isCompleted() ? true : undefined),\n        current: () => (isCurrent() ? true : undefined),\n        index: () => {\n          const index = stepIndex();\n\n          return index >= 0 ? String(index + 1) : undefined;\n        },\n        navigable: () => (isNavigable() ? true : undefined),\n        total: () => (stepperCtx ? String(totalSteps()) : undefined),\n      },\n    });\n\n    // `color`/`size`/`orientation` double as regular, independently-settable props (for a step\n    // rendered standalone) *and* stepper-inherited values — mirrors ore-tab-item's handling of\n    // its own `color`/`size`/`variant` inheritance from `ore-tabs`. Only forcibly overwrite the\n    // attribute when a parent context actually exists, so the plain prop reflection is left\n    // alone otherwise.\n    if (stepperCtx) {\n      watchEffect(() => {\n        const color = stepperCtx.color.value;\n        const size = stepperCtx.size.value;\n        const orientation = stepperCtx.orientation.value;\n\n        if (color !== undefined) el.setAttribute('color', color);\n\n        if (size !== undefined) el.setAttribute('size', size);\n\n        el.setAttribute('orientation', orientation);\n      });\n    }\n\n    const stateLabel = (): string | undefined => {\n      if (props.error.value) return 'error';\n\n      if (isCompleted()) return 'completed';\n\n      if (isCurrent()) return 'current step';\n\n      return undefined;\n    };\n\n    const positionLabel = () => `Step ${displayIndex()} of ${displayTotal()}`;\n    const srLabel = () => {\n      const state = stateLabel();\n\n      return state ? `${positionLabel()}, ${state}` : positionLabel();\n    };\n\n    const handleClick = (event: MouseEvent) => {\n      event.stopPropagation();\n\n      if (!isNavigable()) {\n        event.preventDefault();\n\n        return;\n      }\n\n      el.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: { value: props.value.value } }));\n    };\n\n    const indicatorTemplate = () => html`\n      <span class=\"indicator\" part=\"indicator\" aria-hidden=\"true\">\n        <span class=\"icon-slot\" ?hidden=\"${() => !slots.has('icon').value}\"><slot name=\"icon\"></slot></span>\n        ${() =>\n          slots.has('icon').value\n            ? ''\n            : props.error.value\n              ? html`\n                  <ore-icon name=\"x\" size=\"14\" stroke-width=\"3\"></ore-icon>\n                `\n              : isCompleted()\n                ? html`\n                    <ore-icon name=\"check\" size=\"14\" stroke-width=\"3\"></ore-icon>\n                  `\n                : html`\n                    <span class=\"number\">${displayIndex}</span>\n                  `}\n      </span>\n    `;\n\n    const contentTemplate = () => html`\n      <span class=\"content\" part=\"content\">\n        <span class=\"sr-only\">${srLabel}</span>\n        <span class=\"label\" part=\"label\">\n          <span class=\"label-text\"><slot></slot></span>\n          ${() =>\n            props.optional.value\n              ? html`\n                  <span class=\"optional-hint\">(optional)</span>\n                `\n              : ''}\n        </span>\n        <span class=\"description\" part=\"description\" ?hidden=\"${() => !slots.has('description').value}\">\n          <slot name=\"description\"></slot>\n        </span>\n      </span>\n    `;\n\n    return html`\n      <li class=\"step\" role=\"listitem\">\n        <span class=\"connector connector-leading\" part=\"connector\" aria-hidden=\"true\"></span>\n        <span class=\"connector connector-trailing\" part=\"connector\" aria-hidden=\"true\"></span>\n        ${() =>\n          isNavigable()\n            ? html`\n                <button\n                  type=\"button\"\n                  class=\"control\"\n                  part=\"control\"\n                  aria-current=\"${() => (isCurrent() ? 'step' : null)}\"\n                  aria-disabled=\"${isDisabled}\"\n                  tabindex=\"${() => (isCurrent() ? '0' : '-1')}\"\n                  @click=\"${handleClick}\">\n                  ${indicatorTemplate()}${contentTemplate()}\n                </button>\n              `\n            : html`\n                <div class=\"control\" part=\"control\" aria-current=\"${() => (isCurrent() ? 'step' : null)}\">\n                  ${indicatorTemplate()}${contentTemplate()}\n                </div>\n              `}\n      </li>\n    `;\n  },\n  styles: [colorThemeMixin, coarsePointerMixin, forcedColorsFocusMixin('button.control'), stepStyles],\n});\n"],"mappings":"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"}