import { MOTION_TEXT_SPEEDS as y, MOTION_TEXT_ANIMATION_MODES as M, MOTION_TEXT_TIMING_PRESETS as w } from "./motion-text-constants.js";
import { createElementBlock as h, openBlock as s, normalizeStyle as u, normalizeClass as A, createCommentVNode as l, toDisplayString as f, Fragment as p, createTextVNode as W, renderSlot as T, renderList as g, createBlock as C, Transition as _, withCtx as x } from "vue";
import { _ as v } from "../../_plugin-vue_export-helper-CHgC5LLL.js";
const S = {
  compatConfig: { MODE: 3 },
  name: "DtRecipeMotionText",
  inheritAttrs: !1,
  props: {
    /**
     * The text content to animate.
     * @type {string}
     */
    text: {
      type: String,
      default: ""
    },
    /**
     * The animation mode to use for the text reveal.
     * @values gradient-in, fade-in, slide-in, gradient-sweep, shimmer, none
     */
    animationMode: {
      type: String,
      default: "gradient-in",
      validator: (t) => M.includes(t)
    },
    /**
     * Animation speed using t-shirt sizing.
     * @values sm, md, lg
     */
    speed: {
      type: String,
      default: "md",
      validator: (t) => y.includes(t)
    },
    /**
     * Whether to start animation automatically when component is mounted.
     * @values true, false
     */
    autoStart: {
      type: Boolean,
      default: !0
    },
    /**
     * Whether to loop the animation continuously.
     * @values true, false
     */
    loop: {
      type: Boolean,
      default: !1
    },
    /**
     * Whether to respect the user's prefers-reduced-motion system setting.
     * @values true, false
     */
    respectsReducedMotion: {
      type: Boolean,
      default: !0
    },
    /**
     * Alternative text for screen readers. If provided, this will be announced
     * instead of the animated text.
     * @type {string}
     */
    screenReaderText: {
      type: String,
      default: ""
    }
  },
  emits: [
    /**
     * Emitted when the animation starts.
     * @event start
     */
    "start",
    /**
     * Emitted when the animation completes.
     * @event complete
     */
    "complete",
    /**
     * Emitted during animation progress.
     * @event progress
     * @type {{ wordsComplete: number, totalWords: number, progress: number }}
     */
    "progress",
    /**
     * Emitted when the animation is paused.
     * @event pause
     */
    "pause",
    /**
     * Emitted when the animation resumes.
     * @event resume
     */
    "resume"
  ],
  data() {
    return {
      words: [],
      visibleWordCount: 0,
      visibleCharsPerWord: [],
      isAnimating: !1,
      isPaused: !1,
      isLooped: !1,
      animationTimeouts: [],
      prefersReducedMotion: !1,
      animationKey: 0
    };
  },
  computed: {
    /**
     * Get timing preset based on speed prop
     */
    timing() {
      return w[this.speed];
    },
    /**
     * Computed styles with timing CSS variables
     */
    componentStyles() {
      return {
        "--dt-recipe-motion-text-duration": `${this.timing.duration}ms`,
        "--dt-recipe-motion-text-char-duration": `${this.timing.duration}ms`,
        "--dt-recipe-motion-text-word-duration": `${this.timing.duration * 2}ms`
      };
    },
    /**
     * Check if current animation mode is static (gradient-sweep or shimmer)
     */
    isStaticAnimationMode() {
      return this.animationMode === "gradient-sweep" || this.animationMode === "shimmer";
    },
    /**
     * Computed classes for the motion text element
     */
    motionTextClasses() {
      return [
        "dt-recipe-motion-text",
        `dt-recipe-motion-text--${this.animationMode}`,
        {
          "dt-recipe-motion-text--animating": this.isAnimating,
          "dt-recipe-motion-text--paused": this.isPaused,
          "dt-recipe-motion-text--looped": this.isLooped
        },
        this.$attrs.class
      ];
    }
  },
  watch: {
    text() {
      this.reset(), this.initializeContent();
    },
    loop: {
      handler(t) {
        this.isLooped = t;
      },
      immediate: !0
    }
  },
  mounted() {
    this.checkReducedMotion(), this.initializeContent();
  },
  beforeUnmount() {
    this.clearTimeouts();
  },
  methods: {
    /**
     * Self-contained text processing from DOM nodes
     */
    processTextToChars(t) {
      const d = [], e = (n, i = 0) => {
        var r;
        if (n.nodeType === Node.TEXT_NODE) {
          const a = ((r = n.textContent) == null ? void 0 : r.match(/\S+\s*/g)) || [];
          return d.push(...a.map((o, c) => ({
            text: o,
            chars: o.split(""),
            index: i + c
          }))), i + a.length;
        } else if (n.nodeType === Node.ELEMENT_NODE) {
          let a = i;
          return Array.from(n.childNodes).forEach((o) => {
            a = e(o, a);
          }), a;
        }
        return i;
      };
      return e(t), d;
    },
    /**
     * Process direct text prop into word/character data
     */
    processDirectText(t) {
      return t ? (t.match(/\S+\s*/g) || []).map((e, n) => ({
        text: e,
        chars: e.split(""),
        index: n
      })) : [];
    },
    /**
     * Check for reduced motion preference
     */
    checkReducedMotion() {
      typeof window < "u" && window.matchMedia && (this.prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches);
    },
    /**
     * Clear all animation timeouts
     */
    clearTimeouts() {
      this.animationTimeouts.forEach((t) => clearTimeout(t)), this.animationTimeouts = [];
    },
    /**
     * Start the animation
     * @public
     */
    start() {
      if (!this.isAnimating) {
        if (this.isAnimating = !0, this.isPaused = !1, this.$emit("start"), this.respectsReducedMotion && this.prefersReducedMotion) {
          this.showAllContent();
          return;
        }
        if (this.animationMode === "none") {
          this.showAllContent();
          return;
        }
        this.isStaticAnimationMode || this.showNextWord();
      }
    },
    /**
     * Pause the animation
     * @public
     */
    pause() {
      !this.isAnimating || this.isPaused || (this.isPaused = !0, this.clearTimeouts(), this.$emit("pause"));
    },
    /**
     * Resume the animation
     * @public
     */
    resume() {
      this.isPaused && (this.isPaused = !1, this.$emit("resume"), this.showNextWord());
    },
    /**
     * Reset the animation to initial state
     * @public
     */
    reset() {
      this.clearTimeouts(), this.isAnimating = !1, this.isPaused = !1, this.visibleWordCount = 0, this.visibleCharsPerWord = Array(this.words.length).fill(0), this.animationKey++;
    },
    /**
     * Skip to the end of the animation
     * @public
     */
    skipToEnd() {
      this.showAllContent();
    },
    /**
     * Show all content immediately
     */
    showAllContent() {
      this.visibleWordCount = this.words.length, this.visibleCharsPerWord = this.words.map((t) => t.chars.length), setTimeout(() => {
        this.isAnimating = !1, this.$emit("complete");
      }, 0);
    },
    /**
     * Show next word in sequence
     */
    showNextWord() {
      if (this.isPaused || this.visibleWordCount >= this.words.length) {
        this.visibleWordCount >= this.words.length && this.completeAnimation();
        return;
      }
      const t = setTimeout(() => {
        this.visibleWordCount++, this.$emit("progress", {
          wordsComplete: this.visibleWordCount,
          totalWords: this.words.length,
          progress: this.visibleWordCount / this.words.length
        }), this.animateCharsForWord(this.visibleWordCount - 1);
      }, this.timing.wordDelay);
      this.animationTimeouts.push(t);
    },
    /**
     * Animate characters for a specific word
     */
    animateCharsForWord(t) {
      if (this.isPaused || t >= this.words.length) return;
      this.visibleCharsPerWord[t] = 0;
      const d = this.words[t].chars.length, e = () => {
        if (this.isPaused || this.visibleCharsPerWord[t] >= d) {
          this.visibleCharsPerWord[t] >= d && this.showNextWord();
          return;
        }
        this.visibleCharsPerWord[t]++;
        const n = setTimeout(e, this.timing.characterDelay);
        this.animationTimeouts.push(n);
      };
      e();
    },
    /**
     * Complete the animation
     */
    completeAnimation() {
      if (this.isAnimating = !1, this.clearTimeouts(), this.$emit("complete"), this.loop) {
        const t = setTimeout(() => {
          this.reset(), this.$nextTick(() => {
            this.start();
          });
        }, 500);
        this.animationTimeouts.push(t);
      }
    },
    /**
     * Initialize content based on text prop or slot content
     */
    initializeContent() {
      if (this.isStaticAnimationMode) {
        this.autoStart && this.$nextTick(() => this.start());
        return;
      }
      this.text ? this.words = this.processDirectText(this.text) : this.$refs.contentRef && (this.words = this.processTextToChars(this.$refs.contentRef)), this.visibleCharsPerWord = Array(this.words.length).fill(0), this.visibleWordCount = 0, this.autoStart && this.words.length > 0 && this.$nextTick(() => this.start());
    }
  }
}, b = ["data-text-content", "aria-live", "aria-label"], P = {
  key: 0,
  class: "dt-recipe-motion-text__sr-only"
}, k = ["aria-hidden"], E = ["data-text-content"], N = {
  key: 3,
  class: "dt-recipe-motion-text__fallback"
};
function R(t, d, e, n, i, r) {
  return s(), h("span", {
    ref: "contentRef",
    class: A(r.motionTextClasses),
    style: u(r.componentStyles),
    "data-text-content": r.isStaticAnimationMode ? e.text : void 0,
    "aria-live": i.isAnimating ? "polite" : "off",
    "aria-label": e.screenReaderText || void 0
  }, [
    e.screenReaderText ? (s(), h("span", P, f(e.screenReaderText), 1)) : l("", !0),
    r.isStaticAnimationMode ? (s(), h(p, { key: 1 }, [
      W(f(e.text) + " ", 1),
      e.text ? l("", !0) : T(t.$slots, "default", { key: 0 })
    ], 64)) : (s(), h("span", {
      key: i.animationKey,
      class: "dt-recipe-motion-text__content",
      "aria-hidden": i.isAnimating
    }, [
      (s(!0), h(p, null, g(i.words, (a, o) => (s(), C(_, {
        key: `${i.animationKey}-${o}`,
        name: `dt-recipe-motion-text-word-${e.animationMode}`
      }, {
        default: x(() => [
          o < i.visibleWordCount ? (s(), h("span", {
            key: 0,
            class: "dt-recipe-motion-text__word",
            "data-text-content": a.text,
            style: u({ "--word-index": o })
          }, [
            (s(!0), h(p, null, g(a.chars, (c, m) => (s(), C(_, {
              key: `${i.animationKey}-${o}-${m}`,
              name: `dt-recipe-motion-text-char-${e.animationMode}`
            }, {
              default: x(() => [
                m < i.visibleCharsPerWord[o] ? (s(), h("span", {
                  key: 0,
                  class: "dt-recipe-motion-text__char",
                  style: u({
                    "--char-index": m,
                    "--char-delay": `${m * r.timing.characterDelay}ms`
                  })
                }, f(c), 5)) : l("", !0)
              ]),
              _: 2
            }, 1032, ["name"]))), 128))
          ], 12, E)) : l("", !0)
        ]),
        _: 2
      }, 1032, ["name"]))), 128))
    ], 8, k)),
    !i.words.length && !e.text && !r.isStaticAnimationMode ? (s(), h("span", N, [
      T(t.$slots, "default")
    ])) : l("", !0)
  ], 14, b);
}
const $ = /* @__PURE__ */ v(S, [["render", R]]);
export {
  $ as default
};
//# sourceMappingURL=motion-text.js.map
