{"version":3,"file":"async.cjs","names":[],"sources":["../src/feedback/async/async.ts"],"sourcesContent":["import { bind, define, html, prop, useEmit, when } from '@vielzeug/ore';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nimport { reducedMotionMixin } from '../../styles';\nimport componentStyles from './async.css?inline';\n\nexport type AsyncStatus = 'idle' | 'loading' | 'empty' | 'error' | 'success';\n\nexport type OreAsyncEvents = {\n  retry: undefined;\n};\n\nexport type OreAsyncProps = {\n  'empty-description'?: string;\n  'empty-label'?: string;\n  'error-description'?: string;\n  'error-label'?: string;\n  retryable?: boolean;\n  status?: AsyncStatus;\n};\n\n/**\n * A container for handling asynchronous states (loading, empty, error, success).\n * Simplifies data fetching UI by providing consistent fallbacks.\n *\n * @element ore-async\n *\n * @attr {string} status - current state: 'idle' | 'loading' | 'empty' | 'error' | 'success' (default: 'success')\n * @attr {boolean} retryable - show retry button in error state (default: false)\n * @attr {string} empty-label - title for empty state (default: 'No content yet')\n * @attr {string} empty-description - optional text for empty state\n * @attr {string} error-label - title for error state (default: 'Something went wrong')\n * @attr {string} error-description - optional text for error state\n *\n * @fires retry - Emitted when the retry button is clicked (no detail payload)\n *\n * @slot - default content shown in 'success' state\n * @slot loading - custom loading UI (overrides default skeletons)\n * @slot empty - custom empty UI (overrides default icon/label)\n * @slot error - custom error UI (overrides default icon/label)\n *\n * @cssprop --async-color - Text/icon color used by default async state content\n * @cssprop --async-gap - Vertical spacing between icon, title, description, and actions\n * @cssprop --async-icon-size - Icon size for built-in loading/empty/error visuals\n * @example\n * ```html\n * <!-- Success state: default slot is shown -->\n * <ore-async status=\"success\">\n *   <ul><li>Item one</li><li>Item two</li></ul>\n * </ore-async>\n *\n * <!-- Loading state: shows skeleton placeholders -->\n * <ore-async status=\"loading\"></ore-async>\n *\n * <!-- Empty state with custom message -->\n * <ore-async status=\"empty\" empty-label=\"No results\" empty-description=\"Try adjusting your filters.\"></ore-async>\n *\n * <!-- Error state with retry button -->\n * <ore-async status=\"error\" retryable error-label=\"Failed to load\" error-description=\"Check your connection.\"></ore-async>\n * ```\n */\nexport const ASYNC_TAG = 'ore-async' as const;\ndefine<OreAsyncProps>(ASYNC_TAG, {\n  props: {\n    'empty-description': prop.string(),\n    'empty-label': prop.string('No content yet'),\n    'error-description': prop.string(),\n    'error-label': prop.string('Something went wrong'),\n    retryable: prop.bool(false),\n    status: prop.oneOf(['idle', 'loading', 'empty', 'error', 'success'] as const, 'success'),\n  },\n  setup(props) {\n    const emit = useEmit<OreAsyncEvents>();\n\n    const hasLoadingSlot = signal(false);\n    const hasEmptySlot = signal(false);\n    const hasErrorSlot = signal(false);\n\n    // Reflect status onto the host so CSS can show/hide each region.\n    // ARIA attributes are driven reactively by bind().\n    bind({\n      attr: {\n        ariaBusy: () => (props.status.value === 'loading' ? 'true' : 'false'),\n        ariaLabel: () => (props.status.value === 'loading' ? 'Loading…' : null),\n        ariaLive: () => (props.status.value === 'error' ? 'assertive' : 'polite'),\n        status: props.status,\n      },\n    });\n\n    const renderText = (className: 'title' | 'description', text: Readable<string | undefined> | undefined) => () =>\n      text?.value\n        ? html`\n            <p class=\"${className}\">${text}</p>\n          `\n        : '';\n\n    // All four regions are always in the shadow DOM — CSS on :host([status=\"…\"])\n    // toggles their visibility. This means:\n    // - No DOM churn on status transitions (no teardown/rebuild of slot elements).\n    // - Live regions are always present, so screen readers announce correctly.\n    // - focus is never lost across status changes.\n    return html`\n      <div class=\"region region-idle\" role=\"presentation\"></div>\n\n      <div class=\"region region-loading\" role=\"status\">\n        <slot\n          name=\"loading\"\n          @slotchange=${(e: Event) => {\n            hasLoadingSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n          }}></slot>\n        ${when(\n          hasLoadingSlot,\n          () => html``,\n          () => html`\n            <div class=\"loading-default\" aria-hidden=\"true\">\n              <ore-skeleton variant=\"text\" lines=\"1\" width=\"40%\"></ore-skeleton>\n              <ore-skeleton variant=\"text\" lines=\"3\" width=\"100%\"></ore-skeleton>\n              <ore-skeleton variant=\"text\" lines=\"1\" width=\"60%\"></ore-skeleton>\n            </div>\n          `,\n        )}\n      </div>\n\n      <div class=\"region region-empty\">\n        <slot\n          name=\"empty\"\n          @slotchange=${(e: Event) => {\n            hasEmptySlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n          }}></slot>\n        ${when(\n          hasEmptySlot,\n          () => html``,\n          () => html`\n            <div class=\"empty-state\" role=\"status\">\n              <div class=\"icon\">\n                <ore-icon name=\"package\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n              </div>\n              ${renderText('title', props['empty-label'])} ${renderText('description', props['empty-description'])}\n            </div>\n          `,\n        )}\n      </div>\n\n      <div class=\"region region-error\">\n        <slot\n          name=\"error\"\n          @slotchange=${(e: Event) => {\n            hasErrorSlot.value = (e.target as HTMLSlotElement).assignedNodes().length > 0;\n          }}></slot>\n        ${when(\n          hasErrorSlot,\n          () => html``,\n          () => html`\n            <div class=\"error-state\" role=\"alert\">\n              <div class=\"icon\">\n                <ore-icon name=\"triangle-alert\" size=\"100%\" stroke-width=\"1.75\" aria-hidden=\"true\"></ore-icon>\n              </div>\n              ${renderText('title', props['error-label'])} ${renderText('description', props['error-description'])}\n              ${when(\n                () => Boolean(props.retryable.value),\n                () => html`\n                  <button class=\"retry-btn\" type=\"button\" @click=${() => emit('retry')}>\n                    <ore-icon name=\"refresh-cw\" size=\"1em\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n                    Try again\n                  </button>\n                `,\n              )}\n            </div>\n          `,\n        )}\n      </div>\n\n      <div class=\"region region-success\" role=\"presentation\">\n        <slot></slot>\n      </div>\n    `;\n  },\n  styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"qOA6DA,IAAa,EAAY,aACzB,EAAA,EAAA,OAAA,CAAsB,EAAW,CAC/B,MAAO,CACL,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,gBAAgB,EAC3C,oBAAqB,EAAA,KAAK,OAAO,EACjC,cAAe,EAAA,KAAK,OAAO,sBAAsB,EACjD,UAAW,EAAA,KAAK,KAAK,EAAK,EAC1B,OAAQ,EAAA,KAAK,MAAM,CAAC,OAAQ,UAAW,QAAS,QAAS,SAAS,EAAY,SAAS,CACzF,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAO,EAAA,QAAA,CAAwB,EAE/B,GAAA,EAAiB,EAAA,OAAA,CAAO,EAAK,EAC7B,GAAA,EAAe,EAAA,OAAA,CAAO,EAAK,EAC3B,GAAA,EAAe,EAAA,OAAA,CAAO,EAAK,GAIjC,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,aAAiB,EAAM,OAAO,QAAU,UAAY,OAAS,QAC7D,cAAkB,EAAM,OAAO,QAAU,UAAY,WAAa,KAClE,aAAiB,EAAM,OAAO,QAAU,QAAU,YAAc,SAChE,OAAQ,EAAM,MAChB,CACF,CAAC,EAED,IAAM,GAAc,EAAoC,QACtD,GAAM,MACF,EAAA,IAAI;wBACU,EAAU,IAAI,EAAK;YAEjC,GAON,MAAO,GAAA,IAAI;;;;;;wBAMU,GAAa,CAC1B,EAAe,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAChF,EAAE;WACF,EAAA,EAAA,KAAA,CACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;;WAOZ,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;WACF,EAAA,EAAA,KAAA,CACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;;WAG3G,EAAE;;;;;;wBAMe,GAAa,CAC1B,EAAa,MAAS,EAAE,OAA2B,cAAc,CAAC,CAAC,OAAS,CAC9E,EAAE;WACF,EAAA,EAAA,KAAA,CACA,MACM,EAAA,IAAI,OACJ,EAAA,IAAI;;;;;gBAKJ,EAAW,QAAS,EAAM,cAAc,EAAE,GAAG,EAAW,cAAe,EAAM,oBAAoB,EAAE;iBACnG,EAAA,EAAA,KAAA,KACM,EAAQ,EAAM,UAAU,UACxB,EAAA,IAAI;uEAC+C,EAAK,OAAO,EAAE;;;;iBAKzE,EAAE;;WAGR,EAAE;;;;;;KAOR,EACA,OAAQ,CAAC,EAAA,mBAAoB,EAAA,OAAe,CAC9C,CAAC"}