@ulu/frontend-vue 0.6.23 → 0.6.25

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.
Files changed (69) hide show
  1. package/dist/components/collapsible/UluAccordion.vue.d.ts +4 -4
  2. package/dist/components/collapsible/UluCollapsible.vue.d.ts +2 -2
  3. package/dist/components/collapsible/UluModal.vue.d.ts +4 -4
  4. package/dist/components/collapsible/UluTabGroup.vue.d.ts +2 -2
  5. package/dist/components/elements/UluAlert.vue.d.ts +2 -2
  6. package/dist/components/elements/UluBadge.vue.d.ts +4 -4
  7. package/dist/components/elements/UluButton.vue.d.ts +10 -10
  8. package/dist/components/elements/UluButtonVerbose.vue.d.ts +4 -4
  9. package/dist/components/elements/UluCaptionedFigure.vue.d.ts +2 -2
  10. package/dist/components/elements/UluCard.vue.d.ts +4 -4
  11. package/dist/components/elements/UluList.vue.d.ts +2 -2
  12. package/dist/components/elements/UluSlider.vue.d.ts +2 -2
  13. package/dist/components/elements/UluTag.vue.d.ts +2 -2
  14. package/dist/components/forms/UluSearchForm.vue.d.ts +2 -2
  15. package/dist/components/layout/UluTitleRail.vue.d.ts +2 -2
  16. package/dist/components/systems/facets/UluFacetsList.vue.d.ts +2 -2
  17. package/dist/components/systems/facets/UluFacetsSearch.vue.d.ts +2 -2
  18. package/dist/components/systems/facets/UluFacetsSort.vue.d.ts +2 -2
  19. package/dist/components/utils/UluAction.vue.d.ts +2 -2
  20. package/dist/components/visualizations/UluProgressBar.vue.d.ts +2 -2
  21. package/dist/components/visualizations/UluProgressCircle.vue.d.ts +2 -2
  22. package/dist/composables/useUluFloating.d.ts.map +1 -1
  23. package/dist/composables/useUluFloating.js +26 -25
  24. package/dist/index.js +258 -240
  25. package/dist/mcp-data.json +2661 -1964
  26. package/dist/plugins/index.d.ts +2 -1
  27. package/dist/plugins/popovers/UluPopover.vue.d.ts +191 -8
  28. package/dist/plugins/popovers/UluPopover.vue.d.ts.map +1 -1
  29. package/dist/plugins/popovers/UluPopover.vue.js +90 -127
  30. package/dist/plugins/popovers/UluPopoverBase.vue.d.ts +58 -0
  31. package/dist/plugins/popovers/UluPopoverBase.vue.d.ts.map +1 -0
  32. package/dist/plugins/popovers/UluPopoverBase.vue.js +146 -0
  33. package/dist/plugins/popovers/UluTooltipPopover.vue.d.ts +1 -4
  34. package/dist/plugins/popovers/UluTooltipPopover.vue.d.ts.map +1 -1
  35. package/dist/plugins/popovers/UluTooltipPopover.vue.js +24 -45
  36. package/dist/plugins/popovers/index.d.ts +4 -0
  37. package/dist/plugins/popovers/index.d.ts.map +1 -1
  38. package/dist/plugins/popovers/index.js +54 -49
  39. package/dist/plugins/tour/UluTourContent.vue.d.ts +10 -0
  40. package/dist/plugins/tour/UluTourContent.vue.d.ts.map +1 -0
  41. package/dist/plugins/tour/UluTourContent.vue.js +39 -0
  42. package/dist/plugins/tour/UluTourDisplay.vue.d.ts +100 -0
  43. package/dist/plugins/tour/UluTourDisplay.vue.d.ts.map +1 -0
  44. package/dist/plugins/tour/UluTourDisplay.vue.js +133 -0
  45. package/dist/plugins/tour/UluTourPager.vue.d.ts +3 -0
  46. package/dist/plugins/tour/UluTourPager.vue.d.ts.map +1 -0
  47. package/dist/plugins/tour/UluTourPager.vue.js +31 -0
  48. package/dist/plugins/tour/api.d.ts +52 -0
  49. package/dist/plugins/tour/api.d.ts.map +1 -0
  50. package/dist/plugins/tour/api.js +52 -0
  51. package/dist/plugins/tour/index.d.ts +7 -0
  52. package/dist/plugins/tour/index.d.ts.map +1 -0
  53. package/dist/plugins/tour/index.js +14 -0
  54. package/dist/plugins/tour/useTour.d.ts +8 -0
  55. package/dist/plugins/tour/useTour.d.ts.map +1 -0
  56. package/dist/plugins/tour/useTour.js +8 -0
  57. package/lib/composables/useUluFloating.js +2 -0
  58. package/lib/plugins/index.js +3 -3
  59. package/lib/plugins/popovers/UluPopover.vue +61 -124
  60. package/lib/plugins/popovers/UluPopoverBase.vue +198 -0
  61. package/lib/plugins/popovers/UluTooltipPopover.vue +11 -33
  62. package/lib/plugins/popovers/index.js +15 -5
  63. package/lib/plugins/tour/UluTourContent.vue +37 -0
  64. package/lib/plugins/tour/UluTourDisplay.vue +182 -0
  65. package/lib/plugins/tour/UluTourPager.vue +36 -0
  66. package/lib/plugins/tour/api.js +83 -0
  67. package/lib/plugins/tour/index.js +29 -0
  68. package/lib/plugins/tour/useTour.js +10 -0
  69. package/package.json +1 -1
@@ -0,0 +1,133 @@
1
+ import { computed as i, ref as f, watch as S, nextTick as T, onMounted as b, onUnmounted as L, createBlock as l, openBlock as o, Teleport as F, createElementBlock as x, createCommentVNode as $, unref as v, Fragment as I, resolveDynamicComponent as g, normalizeProps as h, mergeProps as s, createSlots as k, withCtx as c, createVNode as w, normalizeStyle as R } from "vue";
2
+ import V from "../../components/collapsible/UluModal.vue.js";
3
+ import D from "../popovers/UluPopoverBase.vue.js";
4
+ import P from "./UluTourPager.vue.js";
5
+ import _ from "./UluTourContent.vue.js";
6
+ import { useTour as M } from "./useTour.js";
7
+ const H = {
8
+ __name: "UluTourDisplay",
9
+ setup(N) {
10
+ const { api: m, state: a } = M(), t = i(() => a.active?.steps[a.stepIndex]), u = f(null), d = f(null), C = i(() => ({
11
+ placement: t.value?.placement || "bottom",
12
+ arrow: !0,
13
+ offset: 8
14
+ })), E = i(() => ({
15
+ ...a.active?.modalProps || {},
16
+ ...t.value?.modalProps || {}
17
+ })), z = i(() => ({
18
+ modifiers: "large",
19
+ ...a.active?.popoverProps || {},
20
+ ...t.value?.popoverProps || {}
21
+ })), r = f(null), p = () => {
22
+ if (u.value) {
23
+ const e = u.value.getBoundingClientRect();
24
+ r.value = {
25
+ top: e.top,
26
+ left: e.left,
27
+ width: e.width,
28
+ height: e.height
29
+ };
30
+ } else
31
+ r.value = null;
32
+ }, B = i(() => {
33
+ if (!r.value) return {};
34
+ const e = r.value;
35
+ return {
36
+ position: "fixed",
37
+ top: 0,
38
+ left: 0,
39
+ width: "100vw",
40
+ height: "100vh",
41
+ pointerEvents: "auto",
42
+ zIndex: 9998,
43
+ background: "rgba(0,0,0,0.5)",
44
+ clipPath: `polygon(
45
+ 0% 0%,
46
+ 100% 0%,
47
+ 100% 100%,
48
+ 0% 100%,
49
+ 0% ${e.top}px,
50
+ ${e.left}px ${e.top}px,
51
+ ${e.left}px ${e.top + e.height}px,
52
+ ${e.left + e.width}px ${e.top + e.height}px,
53
+ ${e.left + e.width}px ${e.top}px,
54
+ ${e.left}px ${e.top}px,
55
+ 0% ${e.top}px
56
+ )`
57
+ };
58
+ });
59
+ return S(t, async (e) => {
60
+ if (e?.target) {
61
+ await T();
62
+ const n = document.querySelector(e.target);
63
+ n ? (u.value = n, d.value && d.value.update(), e.highlight && p()) : console.warn(`Tour target not found: ${e.target}`);
64
+ } else
65
+ u.value = null, r.value = null;
66
+ }, { immediate: !0 }), b(() => {
67
+ window.addEventListener("resize", p), window.addEventListener("scroll", p);
68
+ }), L(() => {
69
+ window.removeEventListener("resize", p), window.removeEventListener("scroll", p);
70
+ }), (e, n) => (o(), l(F, { to: "body" }, [
71
+ v(a).active ? (o(), x(I, { key: 0 }, [
72
+ t.value?.component ? (o(), l(g(t.value.component), h(s({ key: 0 }, t.value.componentProps)), null, 16)) : t.value?.target ? (o(), l(D, s({
73
+ key: 2,
74
+ ref_key: "popoverBase",
75
+ ref: d,
76
+ trigger: u.value,
77
+ config: C.value,
78
+ style: { zIndex: 9999 },
79
+ isOpen: !0,
80
+ onClose: n[1] || (n[1] = (y) => v(m).stop())
81
+ }, z.value), k({
82
+ default: c(() => [
83
+ t.value?.contentComponent ? (o(), l(g(t.value.contentComponent), h(s({ key: 0 }, t.value.componentProps)), null, 16)) : (o(), l(_, {
84
+ key: 1,
85
+ step: t.value,
86
+ "is-modal": !1
87
+ }, null, 8, ["step"]))
88
+ ]),
89
+ _: 2
90
+ }, [
91
+ t.value?.hideFooter ? void 0 : {
92
+ name: "footer",
93
+ fn: c(() => [
94
+ w(P)
95
+ ]),
96
+ key: "0"
97
+ }
98
+ ]), 1040, ["trigger", "config"])) : (o(), l(V, s({
99
+ key: 1,
100
+ modelValue: !0
101
+ }, E.value, {
102
+ onClose: n[0] || (n[0] = (y) => v(m).stop())
103
+ }), k({
104
+ default: c(() => [
105
+ t.value?.contentComponent ? (o(), l(g(t.value.contentComponent), h(s({ key: 0 }, t.value.componentProps)), null, 16)) : (o(), l(_, {
106
+ key: 1,
107
+ step: t.value,
108
+ "is-modal": !0
109
+ }, null, 8, ["step"]))
110
+ ]),
111
+ _: 2
112
+ }, [
113
+ t.value?.hideFooter ? void 0 : {
114
+ name: "footer",
115
+ fn: c(() => [
116
+ w(P)
117
+ ]),
118
+ key: "0"
119
+ }
120
+ ]), 1040)),
121
+ t.value?.target && t.value?.highlight && r.value ? (o(), x("div", {
122
+ key: 3,
123
+ class: "tour-highlight-backdrop",
124
+ style: R(B.value),
125
+ onClick: n[2] || (n[2] = (y) => v(m).stop())
126
+ }, null, 4)) : $("", !0)
127
+ ], 64)) : $("", !0)
128
+ ]));
129
+ }
130
+ };
131
+ export {
132
+ H as default
133
+ };
@@ -0,0 +1,3 @@
1
+ declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
2
+ export default _default;
3
+ //# sourceMappingURL=UluTourPager.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UluTourPager.vue.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/UluTourPager.vue"],"names":[],"mappings":"AAqBA;"}
@@ -0,0 +1,31 @@
1
+ import { computed as s, createElementBlock as p, openBlock as u, createVNode as i, unref as n } from "vue";
2
+ import { useTour as c } from "./useTour.js";
3
+ import l from "../../components/elements/UluButton.vue.js";
4
+ const d = { class: "tour-pager layout-flex-justified" }, y = {
5
+ __name: "UluTourPager",
6
+ setup(m) {
7
+ const { api: r, state: e } = c(), o = s(() => e.active && e.stepIndex === e.active.steps.length - 1);
8
+ return s(() => e.active && e.stepIndex === 0), (x, t) => (u(), p("div", d, [
9
+ i(l, {
10
+ onClick: t[0] || (t[0] = (a) => n(r).prev()),
11
+ text: "Previous",
12
+ disabled: n(e).stepIndex === 0,
13
+ icon: "type:previous",
14
+ iconBefore: "",
15
+ small: "",
16
+ secondary: "",
17
+ transparent: ""
18
+ }, null, 8, ["disabled"]),
19
+ i(l, {
20
+ primary: "",
21
+ icon: o.value ? null : "type:next",
22
+ onClick: t[1] || (t[1] = (a) => n(r).next()),
23
+ text: o.value ? "Finish" : "Next",
24
+ small: ""
25
+ }, null, 8, ["icon", "text"])
26
+ ]));
27
+ }
28
+ };
29
+ export {
30
+ y as default
31
+ };
@@ -0,0 +1,52 @@
1
+ export function createApi(state: Object): Object;
2
+ export type TourStep = {
3
+ /**
4
+ * - The CSS selector for the target element. If omitted, step renders as a modal.
5
+ */
6
+ target?: string | undefined;
7
+ /**
8
+ * - The title text for the step.
9
+ */
10
+ title?: string | undefined;
11
+ /**
12
+ * - The content text for the step.
13
+ */
14
+ content?: string | undefined;
15
+ /**
16
+ * - A Vue component to completely replace the modal/popover wrapper and layout.
17
+ */
18
+ component?: Object | undefined;
19
+ /**
20
+ * - A Vue component to replace the step text content, keeping the modal/popover wrappers and default footer.
21
+ */
22
+ contentComponent?: Object | undefined;
23
+ /**
24
+ * - Props to bind to the custom component.
25
+ */
26
+ componentProps?: Object | undefined;
27
+ /**
28
+ * - If true, hides the default tour footer (Next/Back buttons).
29
+ */
30
+ hideFooter?: boolean | undefined;
31
+ /**
32
+ * - Floating UI placement string (e.g. 'bottom', 'right').
33
+ */
34
+ placement?: string | undefined;
35
+ /**
36
+ * - If true, darkens the backdrop around the target element.
37
+ */
38
+ highlight?: boolean | undefined;
39
+ /**
40
+ * - Props to pass to the modal (if target is omitted).
41
+ */
42
+ modalProps?: Object | undefined;
43
+ /**
44
+ * - Props to pass to the popover (if target is present).
45
+ */
46
+ popoverProps?: Object | undefined;
47
+ /**
48
+ * - Async hook called before the step is rendered.
49
+ */
50
+ onEnter?: Function | undefined;
51
+ };
52
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/api.js"],"names":[],"mappings":"AAuBO,iCAHI,MAAM,GACJ,MAAM,CA6DjB"}
@@ -0,0 +1,52 @@
1
+ import { markRaw as t } from "vue";
2
+ const o = (e) => ({
3
+ /**
4
+ * Starts a new tour
5
+ * @param {Object} tour - The tour configuration object
6
+ * @param {TourStep[]} tour.steps - Array of steps for the tour
7
+ * @param {String} [tour.name] - Optional name for the tour
8
+ * @param {Object} [tour.modalProps] - Global props applied to all modal steps in this tour (can be overridden by step)
9
+ * @param {Object} [tour.popoverProps] - Global props applied to all popover steps in this tour (can be overridden by step)
10
+ */
11
+ async start(n) {
12
+ n?.steps?.length && (await this._runHooks(n.steps[0]), e.active = t(n), e.stepIndex = 0);
13
+ },
14
+ /**
15
+ * Advances the tour to the next step, or stops if it is the last step
16
+ */
17
+ async next() {
18
+ const n = e.active;
19
+ if (n)
20
+ if (e.stepIndex < n.steps.length - 1) {
21
+ const s = e.stepIndex + 1;
22
+ await this._runHooks(n.steps[s]), e.stepIndex = s;
23
+ } else
24
+ this.stop();
25
+ },
26
+ /**
27
+ * Returns the tour to the previous step
28
+ */
29
+ async prev() {
30
+ const n = e.active;
31
+ if (n && e.stepIndex > 0) {
32
+ const s = e.stepIndex - 1;
33
+ await this._runHooks(n.steps[s]), e.stepIndex = s;
34
+ }
35
+ },
36
+ /**
37
+ * Stops and closes the current tour
38
+ */
39
+ stop() {
40
+ e.active = null, e.stepIndex = 0;
41
+ },
42
+ /**
43
+ * Internal method to run asynchronous lifecycle hooks on a step
44
+ * @param {Object} step - The tour step configuration
45
+ */
46
+ async _runHooks(n) {
47
+ n && typeof n.onEnter == "function" && await n.onEnter();
48
+ }
49
+ });
50
+ export {
51
+ o as createApi
52
+ };
@@ -0,0 +1,7 @@
1
+ import { useTour } from './useTour.js';
2
+ export default function install(app: any): void;
3
+ export { useTour };
4
+ export { default as UluTourDisplay } from './UluTourDisplay.vue';
5
+ export { default as UluTourPager } from './UluTourPager.vue';
6
+ export { default as UluTourContent } from './UluTourContent.vue';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/index.js"],"names":[],"mappings":"AAQA,gDAeC;;;;;wBAjBuB,cAAc"}
@@ -0,0 +1,14 @@
1
+ import { reactive as e } from "vue";
2
+ import u from "./UluTourDisplay.vue.js";
3
+ import { createApi as i } from "./api.js";
4
+ function s(o) {
5
+ const t = e({
6
+ active: null,
7
+ stepIndex: 0
8
+ }), r = i(t);
9
+ o.component("UluTourDisplay", u), o.config.globalProperties.$uluTour = r, o.config.globalProperties.$uluTourState = t, o.provide("uluTour", r), o.provide("uluTourState", t);
10
+ }
11
+ export {
12
+ u as UluTourDisplay,
13
+ s as default
14
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Composable for accessing the Tour API and state
3
+ */
4
+ export function useTour(): {
5
+ api: {};
6
+ state: {};
7
+ };
8
+ //# sourceMappingURL=useTour.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTour.d.ts","sourceRoot":"","sources":["../../../lib/plugins/tour/useTour.js"],"names":[],"mappings":"AAEA;;GAEG;AACH;;;EAIC"}
@@ -0,0 +1,8 @@
1
+ import { useRequiredInject as u } from "../../composables/useRequiredInject.js";
2
+ function r() {
3
+ const t = u("uluTour"), e = u("uluTourState");
4
+ return { api: t, state: e };
5
+ }
6
+ export {
7
+ r as useTour
8
+ };
@@ -38,6 +38,7 @@ export function useUluFloating(trigger, content, config) {
38
38
  const contentArrow = ref(null);
39
39
  const middleware = ref([]);
40
40
  const placementConfig = computed(() => config.value?.placement);
41
+ const strategyConfig = computed(() => config.value?.strategy || 'absolute');
41
42
 
42
43
  const {
43
44
  floatingStyles,
@@ -47,6 +48,7 @@ export function useUluFloating(trigger, content, config) {
47
48
  isPositioned
48
49
  } = useFloating(trigger, content, {
49
50
  placement: placementConfig,
51
+ strategy: strategyConfig,
50
52
  whileElementsMounted: autoUpdate,
51
53
  middleware: middleware,
52
54
  });
@@ -4,9 +4,9 @@
4
4
  * - Used in bundle exports
5
5
  */
6
6
 
7
-
8
7
  export { default as corePlugin } from './core/index.js';
9
- export { default as popoversPlugin, useTooltip, useTooltipFollow } from './popovers/index.js';
8
+ export { default as popoversPlugin, useTooltip, useTooltipFollow, UluPopover, UluPopoverBase, UluTooltipDisplay, UluTooltipPopover } from './popovers/index.js';
10
9
  export { default as modalsPlugin, useModals } from './modals/index.js';
11
10
  export { default as toastPlugin, useToast, UluToast, UluToastDisplay } from "./toast/index.js";
12
- export { default as breakpointsPlugin } from './breakpoints/index.js';
11
+ export { default as breakpointsPlugin } from './breakpoints/index.js';
12
+ export { default as tourPlugin, useTour, UluTourDisplay, UluTourPager, UluTourContent } from './tour/index.js';
@@ -19,143 +19,101 @@
19
19
  {{ triggerText }}
20
20
  </slot>
21
21
  </button>
22
- <span
23
- class="popover"
24
- ref="content"
22
+
23
+ <UluPopoverBase
24
+ ref="popoverBase"
25
+ :trigger="trigger"
26
+ :config="resolvedConfig"
27
+ :isOpen="isOpen"
28
+ :clickOutsideCloses="clickOutsideCloses"
29
+ :directFocus="directFocus"
25
30
  :class="[
26
31
  size ? `popover--${ size }` : '',
27
32
  {
28
- 'popover--no-padding' : noPadding,
29
- 'popover--fixed' : isFixedStrategy,
30
- 'is-active' : isOpen
33
+ 'popover--no-padding' : noPadding
31
34
  },
32
35
  classes.content,
33
36
  ]"
34
37
  :aria-labelledby="triggerId"
35
38
  :id="id"
36
- :style="floatingStyles"
37
- :data-placement="placement"
38
- @keydown.esc="changeTo(false)"
39
- tabindex="-1"
39
+ @close="changeTo(false)"
40
40
  >
41
- <span class="popover__inner">
42
- <slot :isOpen="isOpen" :toggle="toggle" :close="close"/>
43
- </span>
44
- <span v-if="$slots.footer" class="popover__footer">
41
+ <slot :isOpen="isOpen" :toggle="toggle" :close="close"/>
42
+ <template #footer v-if="$slots.footer">
45
43
  <slot name="footer" :close="close"/>
46
- </span>
47
- <span
48
- v-if="resolvedConfig.arrow"
49
- class="popover__arrow"
50
- ref="contentArrow"
51
- :style="arrowStyles"
52
- data-ulu-popover-arrow
53
- ></span>
54
- </span>
44
+ </template>
45
+ </UluPopoverBase>
55
46
  </template>
56
47
  <script setup>
57
- import { ref, computed, unref, nextTick } from "vue";
48
+ import { ref, computed } from "vue";
58
49
  import { useRequiredInject } from "../../composables/useRequiredInject.js";
59
50
  import { POPOVER_OPTIONS_KEY } from "./index.js";
60
51
  import defaults from "./defaults.js";
61
52
  import { newId } from "../../utils/dom.js";
62
- import { useUluFloating } from "../../composables/useUluFloating.js";
53
+ import UluPopoverBase from "./UluPopoverBase.vue";
63
54
 
64
- const emit = defineEmits(["toggle"]);
55
+ const emit = defineEmits(["toggle", "update:modelValue"]);
65
56
  const props = defineProps({
66
57
  /**
67
- * Text for popover button
58
+ * V-model state to control the popover externally
68
59
  */
60
+ modelValue: {
61
+ type: Boolean,
62
+ default: undefined
63
+ },
69
64
  triggerText: String,
70
- /**
71
- * Add optional aria-label to button for icons/etc
72
- */
73
65
  triggerAlt: String,
74
- /**
75
- * Disable trigger button
76
- */
77
66
  disabled: Boolean,
78
- /**
79
- * Tooltip text for trigger button
80
- */
81
67
  tooltip: String,
82
- /**
83
- * Popover size (ie large, etc)
84
- */
85
68
  size: String,
86
- /**
87
- * No padding on popover content
88
- */
89
69
  noPadding: Boolean,
90
- /**
91
- * Floating UI config (merged with defaults for popover)
92
- * - See useUluFloating() composable for config API
93
- */
94
70
  config: {
95
71
  type: Object,
96
72
  default: () => ({})
97
73
  },
98
- /**
99
- * Mount this component already open state
100
- */
101
74
  startOpen: Boolean,
102
- /**
103
- * Active class for trigger button
104
- */
105
75
  activeClass: {
106
76
  type: String,
107
77
  default: "is-active"
108
78
  },
109
- /**
110
- * Add custom classes to specific elements
111
- * { trigger, content }
112
- */
113
79
  classes: {
114
80
  type: Object,
115
81
  default: () => ({})
116
82
  },
117
- /**
118
- * Close popover when click is outside
119
- */
120
83
  clickOutsideCloses: {
121
84
  type: Boolean,
122
85
  default: true
123
86
  },
124
87
  /**
125
- * Direct focus when open/closing popover
88
+ * Direct focus when open/closing popover.
89
+ * Overrides UluPopoverBase's default focus management.
126
90
  */
127
- directFocus: {
128
- type: Function,
129
- default: ({ isOpen, content }) => {
130
- if (isOpen) {
131
- content.focus({ preventScroll: true });
132
- }
133
- }
134
- }
91
+ directFocus: Function
135
92
  });
136
93
 
137
94
  const id = newId();
138
95
  const triggerId = newId();
139
96
 
140
- // Inject global options, falling back to the static defaults file.
141
97
  const injectedOptions = useRequiredInject(POPOVER_OPTIONS_KEY);
142
98
  const baseConfig = injectedOptions ? injectedOptions.popover : defaults.popover;
143
-
144
- // Create a plain config object, same as pre-refactor.
145
99
  const resolvedConfig = computed(() => ({ ...baseConfig, ...props.config }));
146
100
 
147
- const isOpen = ref(props.startOpen || false);
101
+ const internalIsOpen = ref(props.startOpen || false);
148
102
  const trigger = ref(null);
149
- const content = ref(null);
103
+ const popoverBase = ref(null);
150
104
 
151
- const {
152
- floatingStyles,
153
- placement,
154
- update,
155
- arrowStyles,
156
- contentArrow,
157
- isFixedStrategy
158
- } = useUluFloating(trigger, content, resolvedConfig);
105
+ const isOpen = computed({
106
+ get() {
107
+ return props.modelValue !== undefined ? props.modelValue : internalIsOpen.value;
108
+ },
109
+ set(val) {
110
+ if (props.modelValue !== undefined) {
111
+ emit("update:modelValue", val);
112
+ } else {
113
+ internalIsOpen.value = val;
114
+ }
115
+ }
116
+ });
159
117
 
160
118
  const toggle = () => {
161
119
  changeTo(!isOpen.value);
@@ -163,49 +121,28 @@
163
121
 
164
122
  const changeTo = (toOpen) => {
165
123
  isOpen.value = toOpen;
166
-
167
- const focusArgs = {
168
- trigger: unref(trigger),
169
- content: unref(content),
170
- isOpen: unref(isOpen)
171
- };
172
- const eventArgs = { isOpen: focusArgs.isOpen };
173
- nextTick(() => {
174
- if (isOpen.value) {
175
- update();
176
- // Push to next event, without this will get triggered by the original click event
177
- window.setTimeout(() => {
178
- addOutsideClick();
179
- props.directFocus(focusArgs);
180
- emit("toggle", eventArgs);
181
- }, 0);
182
- } else {
183
- destroyOutsideClick();
184
- props.directFocus(focusArgs);
185
- emit("toggle", eventArgs);
186
- }
187
- });
124
+ emit("toggle", { isOpen: toOpen });
188
125
  };
189
126
 
190
- let outsideHandler;
191
- const addOutsideClick = () => {
192
- if (props.clickOutsideCloses) {
193
- if (outsideHandler) {
194
- destroyOutsideClick();
195
- }
196
- outsideHandler = event => {
197
- if (content.value && !content.value.contains(event.target)) {
198
- changeTo(false);
199
- }
200
- };
201
- document.addEventListener("click", outsideHandler);
202
- }
203
- };
204
- const destroyOutsideClick = () => {
205
- if (outsideHandler) {
206
- document.removeEventListener("click", outsideHandler);
207
- outsideHandler = null;
208
- }
209
- };
210
127
  const close = () => changeTo(false);
211
- </script>
128
+
129
+ defineExpose({
130
+ /**
131
+ * The reactive internal open/closed state of the popover
132
+ */
133
+ isOpen,
134
+ /**
135
+ * Method to toggle the popover open/closed
136
+ */
137
+ toggle,
138
+ /**
139
+ * Method to force the popover closed
140
+ */
141
+ close,
142
+ /**
143
+ * Method to explicitly set the open state
144
+ * @param {Boolean} toOpen - The desired state
145
+ */
146
+ changeTo
147
+ });
148
+ </script>