{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "block-ui",
  "title": "Block Ui",
  "type": "registry:ui",
  "description": "Overlay that blocks interaction during loading. Wraps content and shows a spinner + message overlay. The blocking prop toggles the blocked state with configurable overlay opacity, color, background blur, and custom icon/message slots. Composes the spinner primitive.",
  "categories": [
    "feedback",
    "utility"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/block-ui/block-ui.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { styleMap } from 'lit/directives/style-map.js'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { blockUiVariants } from './block-ui.variants'\n\nconst trueByDefault = { fromAttribute: (v: string | null) => v !== 'false', toAttribute: () => null }\n\n/**\n * <uip-block-ui> — BlockUi web component.\n */\nexport class UipBlockUi extends LitElement {\n  static styles = [tailwind, css`:host { display: inline-block; position: relative; width: 100%; }`]\n\n  static properties = {\n    blocking: { type: Boolean, reflect: true },\n    message: { type: String },\n    opacity: { type: Number },\n    overlayColor: { attribute: 'overlay-color' },\n    blurred: { type: Boolean, attribute: 'blur' },\n    showSpinner: { attribute: 'show-spinner', converter: trueByDefault },\n    hasIconSlot: { state: true },\n    hasMessageSlot: { state: true },\n  }\n\n  blocking = false\n  message: string | null = 'Loading...'\n  opacity = 0.6\n  overlayColor = ''\n  blurred = false\n  showSpinner = true\n  private hasIconSlot = false\n  private hasMessageSlot = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'block-ui')\n  }\n\n  protected updated(changed: Map<string, unknown>) {\n    if (changed.has('blocking')) {\n      if (this.blocking) {\n        this.setAttribute('data-blocked', '')\n      } else {\n        this.removeAttribute('data-blocked')\n      }\n    }\n  }\n\n  render() {\n    const overlayBgStyle = {\n      opacity: String(this.opacity),\n      ...(this.overlayColor ? { backgroundColor: this.overlayColor } : {}),\n    }\n\n    return html`\n      <div class=${cn(blockUiVariants(), 'w-full')}>\n        <!-- Wrapped content -->\n        <div\n          part=\"content\"\n          class=${cn(\n            'block-ui-content w-full',\n            this.blocking && 'pointer-events-none',\n            this.blurred && this.blocking && 'blur-[2px] transition-[filter]',\n          )}\n          aria-hidden=${this.blocking ? 'true' : nothing}\n          ?inert=${this.blocking}\n        >\n          <slot></slot>\n        </div>\n\n        <!-- Blocking overlay -->\n        ${this.blocking\n          ? html`\n              <div\n                part=\"overlay\"\n                class=\"absolute inset-0 z-50 flex flex-col items-center justify-center gap-3\"\n                role=\"status\"\n                aria-live=\"polite\"\n                aria-busy=\"true\"\n              >\n                <!-- Background layer -->\n                <div\n                  part=\"backdrop\"\n                  class=${cn('absolute inset-0', !this.overlayColor && 'bg-background')}\n                  style=${styleMap(overlayBgStyle)}\n                ></div>\n\n                <!-- Content layer -->\n                <div class=\"relative z-10 flex flex-col items-center justify-center gap-3\">\n                  <slot\n                    name=\"icon\"\n                    @slotchange=${(e: Event) =>\n                      (this.hasIconSlot = (e.target as HTMLSlotElement).assignedNodes().length > 0)}\n                  >\n                    ${this.showSpinner\n                      ? html`\n                          <svg\n                            class=\"text-primary size-8 animate-spin\"\n                            xmlns=\"http://www.w3.org/2000/svg\"\n                            fill=\"none\"\n                            viewBox=\"0 0 24 24\"\n                          >\n                            <circle\n                              class=\"opacity-25\"\n                              cx=\"12\"\n                              cy=\"12\"\n                              r=\"10\"\n                              stroke=\"currentColor\"\n                              stroke-width=\"4\"\n                            ></circle>\n                            <path\n                              class=\"opacity-75\"\n                              fill=\"currentColor\"\n                              d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n                            ></path>\n                          </svg>\n                        `\n                      : nothing}\n                  </slot>\n\n                  <slot\n                    name=\"message\"\n                    @slotchange=${(e: Event) =>\n                      (this.hasMessageSlot = (e.target as HTMLSlotElement).assignedNodes().length > 0)}\n                  >\n                    ${this.message\n                      ? html`<p part=\"message\" class=\"text-foreground text-sm font-medium\">${this.message}</p>`\n                      : nothing}\n                  </slot>\n                </div>\n              </div>\n            `\n          : nothing}\n      </div>\n    `\n  }\n}\n\ncustomElements.get('uip-block-ui') || customElements.define('uip-block-ui', UipBlockUi)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-block-ui': UipBlockUi\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/block-ui/block-ui.ts"
    },
    {
      "path": "packages/elements-lit/src/components/block-ui/block-ui.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const blockUiVariants = cva('relative inline-block')\n\nexport type BlockUiVariants = VariantProps<typeof blockUiVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/block-ui/block-ui.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
