@ulu/frontend-vue 0.6.34 → 0.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"UluTourContent.vue.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/UluTourContent.vue"],"names":[],"mappings":"AAeA;"}
1
+ {"version":3,"file":"UluTourContent.vue.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/UluTourContent.vue"],"names":[],"mappings":"AA2BA;"}
@@ -1,7 +1,10 @@
1
- import { computed as c, createElementBlock as a, openBlock as o, createBlock as i, createCommentVNode as l, createVNode as u, resolveDynamicComponent as m, normalizeClass as d, withCtx as p, createTextVNode as f, toDisplayString as r } from "vue";
2
- import C from "../../components/elements/UluRule.vue.js";
3
- import { useTour as h } from "./useTour.js";
4
- const v = { class: "tour-content crop-margins" }, y = { key: 1 }, M = {
1
+ import { computed as r, createElementBlock as a, openBlock as o, createBlock as f, createCommentVNode as l, createVNode as h, resolveDynamicComponent as C, normalizeClass as c, withCtx as _, createElementVNode as u, toDisplayString as n } from "vue";
2
+ import y from "../../components/elements/UluRule.vue.js";
3
+ import { useTour as P } from "./useTour.js";
4
+ const k = { class: "tour-content crop-margins" }, x = { class: "tour-content__title-text" }, T = {
5
+ key: 1,
6
+ class: "tour-content__body"
7
+ }, V = {
5
8
  __name: "UluTourContent",
6
9
  props: {
7
10
  /**
@@ -19,23 +22,28 @@ const v = { class: "tour-content crop-margins" }, y = { key: 1 }, M = {
19
22
  default: !1
20
23
  }
21
24
  },
22
- setup(e) {
23
- const { state: s } = h(), t = e, n = c(() => t.step.titleClass ? t.step.titleClass : t.isModal ? s.active?.modalTitleClass || ["h3", "no-margin"] : s.active?.popoverTitleClass || ["h4", "display-block", "no-margin"]);
24
- return (k, T) => (o(), a("div", v, [
25
- e.step?.title ? (o(), i(m(e.isModal ? "h2" : "strong"), {
25
+ setup(t) {
26
+ const { state: e } = P(), s = t, d = r(() => s.step.titleClass ? s.step.titleClass : s.isModal ? e.active?.modalTitleClass || ["h3", "no-margin"] : e.active?.popoverTitleClass || ["h4", "display-block", "no-margin"]), p = r(() => s.step.progressClass || e.active?.progressClass || "headline-label"), m = r(() => e.stepIndex + 1), g = r(() => e.active?.steps?.length || 0), v = r(() => s.step.hideProgress !== void 0 ? !s.step.hideProgress : e.active?.hideProgress !== void 0 ? !e.active.hideProgress : !0);
27
+ return (b, i) => (o(), a("div", k, [
28
+ t.step?.title ? (o(), f(C(t.isModal ? "h2" : "strong"), {
26
29
  key: 0,
27
- class: d(n.value)
30
+ class: c(["tour-content__title", d.value])
28
31
  }, {
29
- default: p(() => [
30
- f(r(e.step.title), 1)
32
+ default: _(() => [
33
+ v.value ? (o(), a("span", {
34
+ key: 0,
35
+ class: c(["tour-content__title-progress", p.value])
36
+ }, " Part " + n(m.value) + " / " + n(g.value), 3)) : l("", !0),
37
+ i[0] || (i[0] = u("span", { class: "hidden-visually" }, ":", -1)),
38
+ u("span", x, n(t.step.title), 1)
31
39
  ]),
32
40
  _: 1
33
41
  }, 8, ["class"])) : l("", !0),
34
- u(C),
35
- e.step?.content ? (o(), a("p", y, r(e.step.content), 1)) : l("", !0)
42
+ h(y),
43
+ t.step?.content ? (o(), a("p", T, n(t.step.content), 1)) : l("", !0)
36
44
  ]));
37
45
  }
38
46
  };
39
47
  export {
40
- M as default
48
+ V as default
41
49
  };
@@ -1,37 +1,44 @@
1
- import { computed as l, ref as h, watch as z, nextTick as L, onMounted as F, onUnmounted as I, createBlock as a, openBlock as n, Teleport as M, createElementBlock as $, createCommentVNode as k, unref as r, Fragment as R, resolveDynamicComponent as p, mergeProps as d, createSlots as w, withCtx as f, normalizeStyle as D } from "vue";
2
- import U from "../../components/collapsible/UluModal.vue.js";
3
- import V from "../popovers/UluPopoverBase.vue.js";
4
- import q from "./UluTourPager.vue.js";
5
- import P from "./UluTourContent.vue.js";
6
- import { useTour as N } from "./useTour.js";
7
- const Q = {
1
+ import { computed as r, ref as h, unref as n, watch as M, nextTick as $, onMounted as R, onUnmounted as q, createBlock as a, openBlock as u, Teleport as U, createElementBlock as P, createCommentVNode as S, Fragment as V, resolveDynamicComponent as i, mergeProps as d, createSlots as _, withCtx as y, normalizeStyle as H } from "vue";
2
+ import N from "../../components/collapsible/UluModal.vue.js";
3
+ import O from "../popovers/UluPopoverBase.vue.js";
4
+ import j from "./UluTourPager.vue.js";
5
+ import E from "./UluTourContent.vue.js";
6
+ import { useTour as A } from "./useTour.js";
7
+ const Z = {
8
8
  __name: "UluTourDisplay",
9
- setup(O) {
10
- const { api: i, state: o } = N(), t = l(() => o.active?.steps[o.stepIndex]), y = l(() => t.value?.component || o.active?.component), c = l(() => t.value?.contentComponent || o.active?.contentComponent), C = l(() => t.value?.pagerComponent || o.active?.pagerComponent || q), _ = l(() => t.value?.modalComponent || o.active?.modalComponent || U), S = l(() => t.value?.popoverComponent || o.active?.popoverComponent || V), v = h(null), g = h(null), E = l(() => ({
9
+ setup(G) {
10
+ const { api: s, state: l } = A(), t = r(() => l.active?.steps[l.stepIndex]), w = r(() => t.value?.component || l.active?.component), f = r(() => t.value?.contentComponent || l.active?.contentComponent), x = r(() => t.value?.pagerComponent || l.active?.pagerComponent || j), T = r(() => t.value?.modalComponent || l.active?.modalComponent || N), B = r(() => t.value?.popoverComponent || l.active?.popoverComponent || O), v = h(null), g = h(null), C = h(null), k = r(() => {
11
+ const e = n(t.value?.target);
12
+ return typeof e == "function" ? n(e()) : e;
13
+ }), b = r(() => {
14
+ const e = n(t.value?.highlightElement);
15
+ return typeof e == "function" ? n(e()) : e;
16
+ }), z = r(() => ({
11
17
  placement: t.value?.placement || "bottom",
12
18
  arrow: !0,
13
19
  offset: 8
14
- })), B = l(() => ({
15
- ...o.active?.modalProps || {},
20
+ })), L = r(() => ({
21
+ ...l.active?.modalProps || {},
16
22
  ...t.value?.modalProps || {}
17
- })), T = l(() => ({
23
+ })), F = r(() => ({
18
24
  modifiers: "large",
19
- ...o.active?.popoverProps || {},
25
+ ...l.active?.popoverProps || {},
20
26
  ...t.value?.popoverProps || {}
21
- })), s = h(null), m = () => {
22
- if (v.value) {
23
- const e = v.value.getBoundingClientRect();
24
- s.value = {
25
- top: e.top,
26
- left: e.left,
27
- width: e.width,
28
- height: e.height
27
+ })), m = h(null), c = () => {
28
+ const e = g.value || v.value;
29
+ if (e) {
30
+ const o = e.getBoundingClientRect();
31
+ m.value = {
32
+ top: o.top,
33
+ left: o.left,
34
+ width: o.width,
35
+ height: o.height
29
36
  };
30
37
  } else
31
- s.value = null;
32
- }, b = l(() => {
33
- if (!s.value) return {};
34
- const e = s.value;
38
+ m.value = null;
39
+ }, I = r(() => {
40
+ if (!m.value) return {};
41
+ const e = m.value;
35
42
  return {
36
43
  position: "fixed",
37
44
  top: 0,
@@ -56,44 +63,51 @@ const Q = {
56
63
  )`
57
64
  };
58
65
  });
59
- return z(t, async (e) => {
60
- if (e?.target) {
61
- await L();
62
- const u = document.querySelector(e.target);
63
- u ? (v.value = u, g.value && g.value.update(), e.highlight && m()) : console.warn(`Tour target not found: ${e.target}`);
66
+ return M([k, b], async ([e, o]) => {
67
+ if (e) {
68
+ await $();
69
+ const p = typeof e == "string" ? document.querySelector(e) : n(e);
70
+ p ? (v.value = p, C.value && C.value.update()) : (console.warn("Tour target not found:", e), v.value = null);
64
71
  } else
65
- v.value = null, s.value = null;
66
- }, { immediate: !0 }), F(() => {
67
- window.addEventListener("resize", m), window.addEventListener("scroll", m);
68
- }), I(() => {
69
- window.removeEventListener("resize", m), window.removeEventListener("scroll", m);
70
- }), (e, u) => (n(), a(M, { to: "body" }, [
71
- r(o).active ? (n(), $(R, { key: 0 }, [
72
- y.value ? (n(), a(p(y.value), d({
72
+ v.value = null;
73
+ if (o) {
74
+ await $();
75
+ const p = typeof o == "string" ? document.querySelector(o) : n(o);
76
+ p ? g.value = p : (console.warn("Tour highlight element not found:", o), g.value = null);
77
+ } else
78
+ g.value = null;
79
+ t.value?.highlight && c();
80
+ }, { immediate: !0 }), R(() => {
81
+ window.addEventListener("resize", c), window.addEventListener("scroll", c);
82
+ }), q(() => {
83
+ window.removeEventListener("resize", c), window.removeEventListener("scroll", c);
84
+ }), (e, o) => (u(), a(U, { to: "body" }, [
85
+ n(l).active ? (u(), P(V, { key: 0 }, [
86
+ w.value ? (u(), a(i(w.value), d({
73
87
  key: 0,
74
88
  "data-ulu-tour-ui": "true",
75
89
  step: t.value,
76
- tourState: r(o),
77
- api: r(i)
78
- }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : t.value?.target ? (n(), a(p(S.value), d({
90
+ tourState: n(l),
91
+ api: n(s)
92
+ }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : k.value ? (u(), a(i(B.value), d({
79
93
  key: 2,
80
94
  ref_key: "popoverBase",
81
- ref: g,
95
+ ref: C,
82
96
  trigger: v.value,
83
- config: E.value,
97
+ config: z.value,
84
98
  style: { zIndex: 9999 },
85
99
  isOpen: !0,
86
100
  "data-ulu-tour-ui": "true",
87
- onClose: u[1] || (u[1] = (x) => r(i).stop())
88
- }, T.value), w({
89
- default: f(() => [
90
- c.value ? (n(), a(p(c.value), d({
101
+ onClose: o[1] || (o[1] = (p) => n(s).stop())
102
+ }, F.value), _({
103
+ default: y(() => [
104
+ f.value ? (u(), a(i(f.value), d({
91
105
  key: 0,
92
106
  step: t.value,
93
- tourState: r(o),
94
- api: r(i),
107
+ tourState: n(l),
108
+ api: n(s),
95
109
  "is-modal": !1
96
- }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : (n(), a(P, {
110
+ }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : (u(), a(E, {
97
111
  key: 1,
98
112
  step: t.value,
99
113
  "is-modal": !1
@@ -103,26 +117,26 @@ const Q = {
103
117
  }, [
104
118
  t.value?.hideFooter ? void 0 : {
105
119
  name: "footer",
106
- fn: f(() => [
107
- (n(), a(p(C.value)))
120
+ fn: y(() => [
121
+ (u(), a(i(x.value)))
108
122
  ]),
109
123
  key: "0"
110
124
  }
111
- ]), 1040, ["trigger", "config"])) : (n(), a(p(_.value), d({
125
+ ]), 1040, ["trigger", "config"])) : (u(), a(i(T.value), d({
112
126
  key: 1,
113
127
  modelValue: !0,
114
128
  "data-ulu-tour-ui": "true"
115
- }, B.value, {
116
- onClose: u[0] || (u[0] = (x) => r(i).stop())
117
- }), w({
118
- default: f(() => [
119
- c.value ? (n(), a(p(c.value), d({
129
+ }, L.value, {
130
+ onClose: o[0] || (o[0] = (p) => n(s).stop())
131
+ }), _({
132
+ default: y(() => [
133
+ f.value ? (u(), a(i(f.value), d({
120
134
  key: 0,
121
135
  step: t.value,
122
- tourState: r(o),
123
- api: r(i),
136
+ tourState: n(l),
137
+ api: n(s),
124
138
  "is-modal": !0
125
- }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : (n(), a(P, {
139
+ }, t.value.componentProps), null, 16, ["step", "tourState", "api"])) : (u(), a(E, {
126
140
  key: 1,
127
141
  step: t.value,
128
142
  "is-modal": !0
@@ -132,23 +146,23 @@ const Q = {
132
146
  }, [
133
147
  t.value?.hideFooter ? void 0 : {
134
148
  name: "footer",
135
- fn: f(() => [
136
- (n(), a(p(C.value)))
149
+ fn: y(() => [
150
+ (u(), a(i(x.value)))
137
151
  ]),
138
152
  key: "0"
139
153
  }
140
154
  ]), 1040)),
141
- t.value?.target && t.value?.highlight && s.value ? (n(), $("div", {
155
+ t.value?.target && t.value?.highlight && m.value ? (u(), P("div", {
142
156
  key: 3,
143
157
  class: "tour-highlight-backdrop",
144
- style: D(b.value),
158
+ style: H(I.value),
145
159
  "data-ulu-tour-ui": "true",
146
- onClick: u[2] || (u[2] = (x) => r(i).stop())
147
- }, null, 4)) : k("", !0)
148
- ], 64)) : k("", !0)
160
+ onClick: o[2] || (o[2] = (p) => n(s).stop())
161
+ }, null, 4)) : S("", !0)
162
+ ], 64)) : S("", !0)
149
163
  ]));
150
164
  }
151
165
  };
152
166
  export {
153
- Q as default
167
+ Z as default
154
168
  };
@@ -1,9 +1,13 @@
1
1
  export function createApi(state: Object): Object;
2
2
  export type TourStep = {
3
3
  /**
4
- * - The CSS selector for the target element. If omitted, step renders as a modal.
4
+ * - The CSS selector, DOM element, or function returning either, for the target element. If omitted or resolves to falsy, step renders as a modal.
5
5
  */
6
- target?: string | undefined;
6
+ target?: string | Function | HTMLElement | undefined;
7
+ /**
8
+ * - Element to highlight if different from target. Falls back to target if not provided.
9
+ */
10
+ highlightElement?: string | Function | HTMLElement | undefined;
7
11
  /**
8
12
  * - The title text for the step.
9
13
  */
@@ -16,6 +20,10 @@ export type TourStep = {
16
20
  * - Class to apply to the pager footer.
17
21
  */
18
22
  pagerClass?: string | Object | any[] | undefined;
23
+ /**
24
+ * - Class to apply to the progress indicator.
25
+ */
26
+ progressClass?: string | Object | any[] | undefined;
19
27
  /**
20
28
  * - The content text for the step.
21
29
  */
@@ -36,6 +44,10 @@ export type TourStep = {
36
44
  * - If true, hides the default tour footer (Next/Back buttons).
37
45
  */
38
46
  hideFooter?: boolean | undefined;
47
+ /**
48
+ * - If true, hides the "Part X / Y" progress indicator for this step.
49
+ */
50
+ hideProgress?: boolean | undefined;
39
51
  /**
40
52
  * - Floating UI placement string (e.g. 'bottom', 'right').
41
53
  */
@@ -44,6 +56,10 @@ export type TourStep = {
44
56
  * - If true, darkens the backdrop around the target element.
45
57
  */
46
58
  highlight?: boolean | undefined;
59
+ /**
60
+ * - If true or returns true, this step will be skipped when navigating the tour.
61
+ */
62
+ skip?: boolean | Function | undefined;
47
63
  /**
48
64
  * - Props to pass to the modal (if target is omitted).
49
65
  */
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/api.js"],"names":[],"mappings":"AA0BO,iCAHI,MAAM,GACJ,MAAM,CAkHjB"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/api.js"],"names":[],"mappings":"AA8BO,iCAHI,MAAM,GACJ,MAAM,CAgJjB"}
@@ -1,5 +1,5 @@
1
- import { markRaw as s } from "vue";
2
- const r = (n) => ({
1
+ import { markRaw as o } from "vue";
2
+ const a = (n) => ({
3
3
  /**
4
4
  * Starts a new tour
5
5
  * @param {Object} tour - The tour configuration object
@@ -10,6 +10,8 @@ const r = (n) => ({
10
10
  * @param {String|Array|Object} [tour.modalTitleClass] - Global class applied to the title of modal steps.
11
11
  * @param {String|Array|Object} [tour.popoverTitleClass] - Global class applied to the title of popover steps.
12
12
  * @param {String|Array|Object} [tour.pagerClass] - Global class applied to the pager.
13
+ * @param {String|Array|Object} [tour.progressClass] - Global class applied to the progress indicator.
14
+ * @param {Boolean} [tour.hideProgress] - Globally hides the progress indicator for all steps unless overridden.
13
15
  * @param {Function} [tour.onStart] - Async hook called before the tour starts.
14
16
  * @param {Function} [tour.onStop] - Async hook called when the tour is closed/finished.
15
17
  */
@@ -18,7 +20,18 @@ const r = (n) => ({
18
20
  n.isTransitioning = !0;
19
21
  try {
20
22
  if (!i?.steps?.length) return;
21
- typeof i.onStart == "function" && await i.onStart(), await this._runHooks(i.steps[0]), n.active = s(i), n.stepIndex = 0;
23
+ typeof i.onStart == "function" && await i.onStart();
24
+ let s = 0;
25
+ for (; s < i.steps.length; ) {
26
+ const e = i.steps[s];
27
+ if (!(typeof e.skip == "function" ? await e.skip() : e.skip)) break;
28
+ s++;
29
+ }
30
+ if (s >= i.steps.length) {
31
+ typeof i.onStop == "function" && await i.onStop();
32
+ return;
33
+ }
34
+ await this._runHooks(i.steps[s]), n.active = o(i), n.stepIndex = s;
22
35
  } finally {
23
36
  n.isTransitioning = !1;
24
37
  }
@@ -33,12 +46,13 @@ const r = (n) => ({
33
46
  if (i) {
34
47
  n.isTransitioning = !0;
35
48
  try {
36
- if (n.stepIndex < i.steps.length - 1) {
37
- await this._runLeaveHook(i.steps[n.stepIndex]);
38
- const e = n.stepIndex + 1;
39
- await this._runHooks(i.steps[e]), n.stepIndex = e;
40
- } else
41
- await this.stop(!0);
49
+ let s = n.stepIndex + 1;
50
+ for (; s < i.steps.length; ) {
51
+ const e = i.steps[s];
52
+ if (!(typeof e.skip == "function" ? await e.skip() : e.skip)) break;
53
+ s++;
54
+ }
55
+ s < i.steps.length ? (await this._runLeaveHook(i.steps[n.stepIndex]), await this._runHooks(i.steps[s]), n.stepIndex = s) : await this.stop(!0);
42
56
  } finally {
43
57
  n.isTransitioning = !1;
44
58
  }
@@ -53,11 +67,13 @@ const r = (n) => ({
53
67
  if (i) {
54
68
  n.isTransitioning = !0;
55
69
  try {
56
- if (n.stepIndex > 0) {
57
- await this._runLeaveHook(i.steps[n.stepIndex]);
58
- const e = n.stepIndex - 1;
59
- await this._runHooks(i.steps[e]), n.stepIndex = e;
70
+ let s = n.stepIndex - 1;
71
+ for (; s >= 0; ) {
72
+ const e = i.steps[s];
73
+ if (!(typeof e.skip == "function" ? await e.skip() : e.skip)) break;
74
+ s--;
60
75
  }
76
+ s >= 0 && (await this._runLeaveHook(i.steps[n.stepIndex]), await this._runHooks(i.steps[s]), n.stepIndex = s);
61
77
  } finally {
62
78
  n.isTransitioning = !1;
63
79
  }
@@ -71,8 +87,8 @@ const r = (n) => ({
71
87
  if (!(!i && n.isTransitioning)) {
72
88
  n.isTransitioning = !0;
73
89
  try {
74
- const e = n.active;
75
- e && (e.steps[n.stepIndex] && await this._runLeaveHook(e.steps[n.stepIndex]), typeof e.onStop == "function" && await e.onStop()), n.active = null, n.stepIndex = 0;
90
+ const s = n.active;
91
+ s && (s.steps[n.stepIndex] && await this._runLeaveHook(s.steps[n.stepIndex]), typeof s.onStop == "function" && await s.onStop()), n.active = null, n.stepIndex = 0;
76
92
  } finally {
77
93
  n.isTransitioning = !1;
78
94
  }
@@ -94,5 +110,5 @@ const r = (n) => ({
94
110
  }
95
111
  });
96
112
  export {
97
- r as createApi
113
+ a as createApi
98
114
  };
@@ -2,13 +2,25 @@
2
2
  <div class="tour-content crop-margins">
3
3
  <component
4
4
  v-if="step?.title"
5
+ class="tour-content__title"
5
6
  :is="isModal ? 'h2' : 'strong'"
6
7
  :class="resolvedTitleClass"
7
8
  >
8
- {{ step.title }}
9
+ <span
10
+ v-if="showProgress"
11
+ class="tour-content__title-progress"
12
+ :class="resolvedProgressClass"
13
+ >
14
+ Part {{ currentProgress }} / {{ totalProgress }}
15
+ </span>
16
+ <span class="hidden-visually">:</span>
17
+ <span class="tour-content__title-text">{{ step.title }}</span>
9
18
  </component>
10
19
  <UluRule />
11
- <p v-if="step?.content">
20
+ <p
21
+ class="tour-content__body"
22
+ v-if="step?.content"
23
+ >
12
24
  {{ step.content }}
13
25
  </p>
14
26
  </div>
@@ -47,4 +59,22 @@
47
59
  return tourState.active?.popoverTitleClass || ['h4', 'display-block', 'no-margin'];
48
60
  }
49
61
  });
62
+
63
+ const resolvedProgressClass = computed(() => {
64
+ return props.step.progressClass || tourState.active?.progressClass || 'headline-label';
65
+ });
66
+
67
+ const currentProgress = computed(() => tourState.stepIndex + 1);
68
+
69
+ const totalProgress = computed(() => tourState.active?.steps?.length || 0);
70
+
71
+ const showProgress = computed(() => {
72
+ if (props.step.hideProgress !== undefined) {
73
+ return !props.step.hideProgress;
74
+ }
75
+ if (tourState.active?.hideProgress !== undefined) {
76
+ return !tourState.active.hideProgress;
77
+ }
78
+ return true;
79
+ });
50
80
  </script>
@@ -15,7 +15,7 @@
15
15
 
16
16
  <!-- 2. RENDER MODAL STEP -->
17
17
  <component
18
- v-else-if="!currentStep?.target"
18
+ v-else-if="!resolvedTargetDescriptor"
19
19
  :is="resolvedModalComponent"
20
20
  :modelValue="true"
21
21
  data-ulu-tour-ui="true"
@@ -80,7 +80,7 @@
80
80
  </template>
81
81
 
82
82
  <script setup>
83
- import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue';
83
+ import { ref, computed, watch, nextTick, onMounted, onUnmounted, unref } from 'vue';
84
84
  import UluModal from '../../components/collapsible/UluModal.vue';
85
85
  import UluPopoverBase from '../popovers/UluPopoverBase.vue';
86
86
  import UluTourPager from './UluTourPager.vue';
@@ -100,8 +100,21 @@
100
100
 
101
101
  // Floating UI Setup for Popovers
102
102
  const targetEl = ref(null);
103
+ const highlightEl = ref(null);
103
104
  const popoverBase = ref(null);
104
105
 
106
+ const resolvedTargetDescriptor = computed(() => {
107
+ const t = unref(currentStep.value?.target);
108
+ if (typeof t === 'function') return unref(t());
109
+ return t;
110
+ });
111
+
112
+ const resolvedHighlightDescriptor = computed(() => {
113
+ const h = unref(currentStep.value?.highlightElement);
114
+ if (typeof h === 'function') return unref(h());
115
+ return h;
116
+ });
117
+
105
118
  const resolvedConfig = computed(() => ({
106
119
  placement: currentStep.value?.placement || 'bottom',
107
120
  arrow: true,
@@ -128,8 +141,9 @@
128
141
  const targetRect = ref(null);
129
142
 
130
143
  const updateTargetRect = () => {
131
- if (targetEl.value) {
132
- const rect = targetEl.value.getBoundingClientRect();
144
+ const el = highlightEl.value || targetEl.value;
145
+ if (el) {
146
+ const rect = el.getBoundingClientRect();
133
147
  targetRect.value = {
134
148
  top: rect.top,
135
149
  left: rect.left,
@@ -169,11 +183,11 @@
169
183
  };
170
184
  });
171
185
 
172
- // Watch for step changes to update floating UI target
173
- watch(currentStep, async (step) => {
174
- if (step?.target) {
186
+ // Watch for step changes to update floating UI target and highlight target
187
+ watch([resolvedTargetDescriptor, resolvedHighlightDescriptor], async ([targetDesc, highlightDesc]) => {
188
+ if (targetDesc) {
175
189
  await nextTick();
176
- const el = document.querySelector(step.target);
190
+ const el = typeof targetDesc === 'string' ? document.querySelector(targetDesc) : unref(targetDesc);
177
191
  if (el) {
178
192
  targetEl.value = el;
179
193
  // UluPopoverBase's isOpen watcher automatically calls update(),
@@ -182,15 +196,29 @@
182
196
  if (popoverBase.value) {
183
197
  popoverBase.value.update();
184
198
  }
185
- if (step.highlight) {
186
- updateTargetRect();
187
- }
188
199
  } else {
189
- console.warn(`Tour target not found: ${step.target}`);
200
+ console.warn(`Tour target not found:`, targetDesc);
201
+ targetEl.value = null;
190
202
  }
191
203
  } else {
192
204
  targetEl.value = null;
193
- targetRect.value = null;
205
+ }
206
+
207
+ if (highlightDesc) {
208
+ await nextTick();
209
+ const hEl = typeof highlightDesc === 'string' ? document.querySelector(highlightDesc) : unref(highlightDesc);
210
+ if (hEl) {
211
+ highlightEl.value = hEl;
212
+ } else {
213
+ console.warn(`Tour highlight element not found:`, highlightDesc);
214
+ highlightEl.value = null;
215
+ }
216
+ } else {
217
+ highlightEl.value = null;
218
+ }
219
+
220
+ if (currentStep.value?.highlight) {
221
+ updateTargetRect();
194
222
  }
195
223
  }, { immediate: true });
196
224