{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "watermark",
  "title": "Watermark",
  "type": "registry:ui",
  "description": "Overlay watermark that repeats text or an image across the content area at a configurable angle. Supports rotate angle, gap between repeats, opacity, font size/color/weight, z-index, and pointer-event control. Wrap any content via children.",
  "categories": [
    "display",
    "utility"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/watermark/watermark.ts",
      "content": "import { LitElement, css, html, isServer } from 'lit'\nimport { styleMap } from 'lit/directives/style-map.js'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\n\n/**\n * <uip-watermark> — the registry Watermark as a web component.\n *\n * Class strings are React's verbatim (`relative` root, `absolute inset-0\n * overflow-hidden` overlay). One structural adaptation: the host can't take\n * the `relative` class, so the positioned container is an inner `part=\"base\"`\n * div wrapping the slot + overlay — React's `className` (borders, padding)\n * goes on the host as usual, with the overlay covering the content box.\n *\n * The overlay tiles a generated SVG data URL (text or `image`) as its\n * background, rebuilt on resize — the same tiling math as React. `interactive`\n * lets the overlay capture pointer events (preview-only views).\n *\n * Parts: `base` (the positioned container), `overlay`.\n */\nexport class UipWatermark extends LitElement {\n  // React's root is a block <div>.\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    content: {},\n    image: {},\n    rotate: { type: Number },\n    gap: { type: Number },\n    opacity: { type: Number },\n    fontSize: { type: Number, attribute: 'font-size' },\n    color: {},\n    fontFamily: { attribute: 'font-family' },\n    fontWeight: { attribute: 'font-weight' },\n    zIndex: { type: Number, attribute: 'z-index' },\n    interactive: { type: Boolean, reflect: true },\n    boxWidth: { state: true },\n    boxHeight: { state: true },\n  }\n\n  content = ''\n  image?: string\n  rotate = -22\n  gap = 100\n  opacity = 0.08\n  fontSize = 16\n  color = 'currentColor'\n  fontFamily = 'sans-serif'\n  fontWeight: number | string = 'normal'\n  zIndex = 9\n  interactive = false\n  private boxWidth = 0\n  private boxHeight = 0\n  private observer?: ResizeObserver\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', 'watermark')\n    if (isServer || typeof ResizeObserver === 'undefined') return\n    const measure = () => {\n      const rect = this.getBoundingClientRect()\n      this.boxWidth = rect.width\n      this.boxHeight = rect.height\n    }\n    measure()\n    this.observer = new ResizeObserver(measure)\n    this.observer.observe(this)\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.observer?.disconnect()\n  }\n\n  // Build a tiled SVG data URL that repeats the text/image across a tile of\n  // size (gap + contentSize) — the same tiling math as React.\n  private watermarkUrl() {\n    if (!this.boxWidth || !this.boxHeight || isServer || typeof btoa === 'undefined') return ''\n    const text = this.content || ''\n    const { gap, fontSize, rotate, opacity, color, fontFamily, fontWeight, image } = this\n\n    if (image) {\n      // For images we tile the image at its natural size within the gap.\n      const tile = gap + 100\n      const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${tile}\" height=\"${tile}\" viewBox=\"0 0 ${tile} ${tile}\">\n  <image href=\"${image}\" x=\"${gap / 2}\" y=\"${gap / 2}\" width=\"100\" height=\"100\" opacity=\"${opacity}\" transform=\"rotate(${rotate} ${tile / 2} ${tile / 2})\"/>\n</svg>`\n      return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svg)))}`\n    }\n\n    if (!text) return ''\n\n    // Estimate text width — rough heuristic, good enough for tiling.\n    const textWidth = text.length * fontSize * 0.6\n    const tileW = gap + textWidth\n    const tileH = gap + fontSize * 1.5\n\n    const escapedText = text\n      .replace(/&/g, '&amp;')\n      .replace(/</g, '&lt;')\n      .replace(/>/g, '&gt;')\n      .replace(/\"/g, '&quot;')\n      .replace(/'/g, '&#39;')\n\n    const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${tileW}\" height=\"${tileH}\" viewBox=\"0 0 ${tileW} ${tileH}\">\n  <text x=\"${gap / 2}\" y=\"${gap / 2 + fontSize}\" font-size=\"${fontSize}\" font-family=\"${fontFamily}\" font-weight=\"${fontWeight}\" fill=\"${color}\" opacity=\"${opacity}\" transform=\"rotate(${rotate} ${gap / 2} ${gap / 2 + fontSize / 2})\">${escapedText}</text>\n</svg>`\n    return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svg)))}`\n  }\n\n  render() {\n    const url = this.watermarkUrl()\n    return html`<div part=\"base\" data-uipkge=\"\" data-slot=\"watermark\" class=\"relative\">\n      <slot></slot>\n      <div\n        part=\"overlay\"\n        data-uipkge=\"\"\n        data-slot=\"watermark-overlay\"\n        class=\"absolute inset-0 overflow-hidden\"\n        style=${styleMap({\n          backgroundImage: url ? `url(\"${url}\")` : 'none',\n          backgroundRepeat: 'repeat',\n          zIndex: String(this.zIndex),\n          pointerEvents: this.interactive ? 'auto' : 'none',\n        })}\n        aria-hidden=\"true\"\n      ></div>\n    </div>`\n  }\n}\n\ncustomElements.get('uip-watermark') || customElements.define('uip-watermark', UipWatermark)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-watermark': UipWatermark\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/watermark/watermark.ts"
    }
  ],
  "dependencies": [
    "lit"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
