@reeka/minisite-popup 0.1.0
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.
- package/LICENSE +5 -0
- package/README.md +25 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +172 -0
- package/dist/style.css +1 -0
- package/dist/types.d.ts +18 -0
- package/package.json +45 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @reeka/minisite-popup
|
|
2
|
+
|
|
3
|
+
Image lightbox marketing popup for Reeka mini-sites and agent listing sites.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import "@reeka/minisite-popup/style.css";
|
|
7
|
+
import { installMinisitePopup } from "@reeka/minisite-popup";
|
|
8
|
+
|
|
9
|
+
app.mount("#app");
|
|
10
|
+
|
|
11
|
+
void installMinisitePopup({
|
|
12
|
+
apiBase: config.apiBaseUrl,
|
|
13
|
+
organizationId: config.portfolioId, // or agentListingId / publicKey
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Behavior
|
|
18
|
+
|
|
19
|
+
- Fetches `GET /public/marketing-popup/config?organizationId=`
|
|
20
|
+
- Shows once after `delayMs` (default 2500) when enabled
|
|
21
|
+
- Dismiss (X / Escape / backdrop) → `sessionStorage` key `reeka_popup_dismissed:{organizationId}`
|
|
22
|
+
- Skips booking/checkout routes by default
|
|
23
|
+
- CMS edit session: force-show preview; listens for `reeka-marketing-popup-draft` CustomEvent
|
|
24
|
+
|
|
25
|
+
Configure under Settings → Website → Marketing popup (or CMS **Popup** panel).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type App } from "vue";
|
|
2
|
+
import type { MinisitePopupInstallOptions } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Mount the marketing popup when the org has it enabled
|
|
5
|
+
* (checked via public config API). Safe to call after `app.mount`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function installMinisitePopup(options: MinisitePopupInstallOptions): Promise<App | null>;
|
|
8
|
+
export type { MinisitePopupInstallOptions, MarketingPopupConfig, } from "./types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { defineComponent as z, ref as k, computed as E, watch as D, onMounted as K, onUnmounted as N, openBlock as f, createBlock as R, Teleport as A, createElementBlock as g, withKeys as P, withModifiers as C, createElementVNode as y, createCommentVNode as b, toDisplayString as V, createApp as j } from "vue";
|
|
2
|
+
const q = ["onKeydown"], F = ["src"], x = ["href", "target", "rel"], O = "reeka_site_cms_session", B = "reeka-marketing-popup-draft", W = /* @__PURE__ */ z({
|
|
3
|
+
__name: "PopupWidget",
|
|
4
|
+
props: {
|
|
5
|
+
apiBase: {},
|
|
6
|
+
organizationId: {},
|
|
7
|
+
skipBookingRoutes: { type: Boolean }
|
|
8
|
+
},
|
|
9
|
+
setup(i) {
|
|
10
|
+
const a = i, t = k(null), s = k(!1), u = k(null);
|
|
11
|
+
let l = null, r = null;
|
|
12
|
+
const _ = E(
|
|
13
|
+
() => `reeka_popup_dismissed:${a.organizationId}`
|
|
14
|
+
), I = E(
|
|
15
|
+
() => {
|
|
16
|
+
var e;
|
|
17
|
+
return !!((e = t.value) != null && e.ctaEnabled) && !!t.value.ctaLabel && !!t.value.ctaUrl;
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
function c() {
|
|
21
|
+
try {
|
|
22
|
+
return !!sessionStorage.getItem(O);
|
|
23
|
+
} catch {
|
|
24
|
+
return !1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function S() {
|
|
28
|
+
if (c()) return !1;
|
|
29
|
+
try {
|
|
30
|
+
return sessionStorage.getItem(_.value) === "1";
|
|
31
|
+
} catch {
|
|
32
|
+
return !1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function h() {
|
|
36
|
+
if (!c())
|
|
37
|
+
try {
|
|
38
|
+
sessionStorage.setItem(_.value, "1");
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function T() {
|
|
43
|
+
if (a.skipBookingRoutes === !1) return !1;
|
|
44
|
+
const e = (window.location.pathname || "").toLowerCase();
|
|
45
|
+
return /\/booking(\/|$)/.test(e) || /\/checkout(\/|$)/.test(e) || /\/book(\/|$)/.test(e) || /\/payment(\/|$)/.test(e);
|
|
46
|
+
}
|
|
47
|
+
function d(e) {
|
|
48
|
+
if (!(e != null && e.enabled) || !e.imageUrl) {
|
|
49
|
+
t.value = null, s.value = !1;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
t.value = e, M();
|
|
53
|
+
}
|
|
54
|
+
function M() {
|
|
55
|
+
var o;
|
|
56
|
+
if (l && (clearTimeout(l), l = null), !((o = t.value) != null && o.enabled) || !t.value.imageUrl || !c() && (S() || T()))
|
|
57
|
+
return;
|
|
58
|
+
const e = typeof t.value.delayMs == "number" && Number.isFinite(t.value.delayMs) ? Math.max(0, Math.min(3e4, t.value.delayMs)) : 2500, n = c() ? 0 : e;
|
|
59
|
+
l = setTimeout(() => {
|
|
60
|
+
s.value = !0;
|
|
61
|
+
}, n);
|
|
62
|
+
}
|
|
63
|
+
function m() {
|
|
64
|
+
s.value = !1, h(), U();
|
|
65
|
+
}
|
|
66
|
+
function L() {
|
|
67
|
+
h();
|
|
68
|
+
}
|
|
69
|
+
function v(e) {
|
|
70
|
+
if (!s.value || e.key !== "Tab" || !u.value) return;
|
|
71
|
+
const n = u.value.querySelectorAll(
|
|
72
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
73
|
+
);
|
|
74
|
+
if (!n.length) return;
|
|
75
|
+
const o = n[0], p = n[n.length - 1];
|
|
76
|
+
e.shiftKey && document.activeElement === o ? (e.preventDefault(), p.focus()) : !e.shiftKey && document.activeElement === p && (e.preventDefault(), o.focus());
|
|
77
|
+
}
|
|
78
|
+
function U() {
|
|
79
|
+
var e;
|
|
80
|
+
(e = r == null ? void 0 : r.focus) == null || e.call(r), r = null;
|
|
81
|
+
}
|
|
82
|
+
D(s, (e) => {
|
|
83
|
+
e ? (r = document.activeElement, requestAnimationFrame(() => {
|
|
84
|
+
var n;
|
|
85
|
+
return (n = u.value) == null ? void 0 : n.focus();
|
|
86
|
+
}), document.addEventListener("keydown", v)) : document.removeEventListener("keydown", v);
|
|
87
|
+
});
|
|
88
|
+
function w(e) {
|
|
89
|
+
const n = e.detail;
|
|
90
|
+
n && typeof n == "object" && d(n);
|
|
91
|
+
}
|
|
92
|
+
async function $() {
|
|
93
|
+
const n = `${a.apiBase.replace(/\/$/, "")}/public/marketing-popup/config?organizationId=${encodeURIComponent(a.organizationId)}`;
|
|
94
|
+
try {
|
|
95
|
+
const o = await fetch(n);
|
|
96
|
+
if (!o.ok) {
|
|
97
|
+
d({ enabled: !1 });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const p = await o.json();
|
|
101
|
+
d(p);
|
|
102
|
+
} catch {
|
|
103
|
+
d({ enabled: !1 });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return K(() => {
|
|
107
|
+
window.addEventListener(B, w), $();
|
|
108
|
+
}), N(() => {
|
|
109
|
+
window.removeEventListener(B, w), document.removeEventListener("keydown", v), l && clearTimeout(l);
|
|
110
|
+
}), (e, n) => {
|
|
111
|
+
var o;
|
|
112
|
+
return f(), R(A, { to: "body" }, [
|
|
113
|
+
s.value ? (f(), g("div", {
|
|
114
|
+
key: 0,
|
|
115
|
+
class: "reeka-popup",
|
|
116
|
+
role: "dialog",
|
|
117
|
+
"aria-modal": "true",
|
|
118
|
+
"aria-label": "Promotion",
|
|
119
|
+
onKeydown: P(C(m, ["prevent"]), ["escape"])
|
|
120
|
+
}, [
|
|
121
|
+
y("div", {
|
|
122
|
+
class: "reeka-popup__backdrop",
|
|
123
|
+
onClick: m
|
|
124
|
+
}),
|
|
125
|
+
y("div", {
|
|
126
|
+
ref_key: "dialogEl",
|
|
127
|
+
ref: u,
|
|
128
|
+
class: "reeka-popup__dialog",
|
|
129
|
+
tabindex: "-1",
|
|
130
|
+
onClick: n[0] || (n[0] = C(() => {
|
|
131
|
+
}, ["stop"]))
|
|
132
|
+
}, [
|
|
133
|
+
y("button", {
|
|
134
|
+
type: "button",
|
|
135
|
+
class: "reeka-popup__close",
|
|
136
|
+
"aria-label": "Close",
|
|
137
|
+
onClick: m
|
|
138
|
+
}, " × "),
|
|
139
|
+
(o = t.value) != null && o.imageUrl ? (f(), g("img", {
|
|
140
|
+
key: 0,
|
|
141
|
+
class: "reeka-popup__image",
|
|
142
|
+
src: t.value.imageUrl,
|
|
143
|
+
alt: ""
|
|
144
|
+
}, null, 8, F)) : b("", !0),
|
|
145
|
+
I.value ? (f(), g("a", {
|
|
146
|
+
key: 1,
|
|
147
|
+
class: "reeka-popup__cta",
|
|
148
|
+
href: t.value.ctaUrl,
|
|
149
|
+
target: t.value.openInNewTab ? "_blank" : void 0,
|
|
150
|
+
rel: t.value.openInNewTab ? "noopener noreferrer" : void 0,
|
|
151
|
+
onClick: L
|
|
152
|
+
}, V(t.value.ctaLabel), 9, x)) : b("", !0)
|
|
153
|
+
], 512)
|
|
154
|
+
], 40, q)) : b("", !0)
|
|
155
|
+
]);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
async function G(i) {
|
|
160
|
+
if (typeof window > "u" || !i.apiBase || !i.organizationId || document.getElementById("reeka-minisite-popup-root")) return null;
|
|
161
|
+
const a = document.createElement("div");
|
|
162
|
+
a.id = "reeka-minisite-popup-root", document.body.appendChild(a);
|
|
163
|
+
const t = j(W, {
|
|
164
|
+
apiBase: i.apiBase,
|
|
165
|
+
organizationId: i.organizationId,
|
|
166
|
+
skipBookingRoutes: i.skipBookingRoutes !== !1
|
|
167
|
+
});
|
|
168
|
+
return t.mount(a), t;
|
|
169
|
+
}
|
|
170
|
+
export {
|
|
171
|
+
G as installMinisitePopup
|
|
172
|
+
};
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.reeka-popup{position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:1rem;box-sizing:border-box}.reeka-popup__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;background:#0f172a8c}.reeka-popup__dialog{position:relative;z-index:1;max-width:min(92vw,420px);max-height:min(88vh,720px);display:flex;flex-direction:column;align-items:stretch;gap:.75rem;outline:none}.reeka-popup__close{position:absolute;top:-.35rem;right:-.35rem;z-index:2;width:2rem;height:2rem;border:none;border-radius:999px;background:#fff;color:#111;font-size:1.35rem;line-height:1;cursor:pointer;box-shadow:0 1px 4px #0003}.reeka-popup__close:hover{background:#f3f4f6}.reeka-popup__image{display:block;width:100%;max-height:min(72vh,560px);object-fit:contain;border-radius:8px;background:#0f172a}.reeka-popup__cta{display:inline-flex;align-items:center;justify-content:center;align-self:center;min-width:10rem;padding:.65rem 1.25rem;border-radius:8px;background:#111827;color:#fff;font:600 .9rem/1.2 system-ui,-apple-system,sans-serif;text-decoration:none}.reeka-popup__cta:hover{background:#1f2937}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type MarketingPopupConfig = {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
delayMs?: number;
|
|
4
|
+
imageUrl?: string;
|
|
5
|
+
ctaEnabled?: boolean;
|
|
6
|
+
ctaLabel?: string;
|
|
7
|
+
ctaUrl?: string;
|
|
8
|
+
openInNewTab?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type MinisitePopupInstallOptions = {
|
|
11
|
+
apiBase: string;
|
|
12
|
+
/** Org Mongo _id, publicKey, or agentListingId */
|
|
13
|
+
organizationId: string;
|
|
14
|
+
/**
|
|
15
|
+
* When true (default), skip showing on booking/checkout paths.
|
|
16
|
+
*/
|
|
17
|
+
skipBookingRoutes?: boolean;
|
|
18
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reeka/minisite-popup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Marketing image lightbox popup for Reeka mini-sites and agent listing sites",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"style": "./dist/style.css",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": "./dist/style.css"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "vite build && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
23
|
+
"dev": "vite build --watch",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"vue": "^3.5.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@vitejs/plugin-vue": "^5.1.4",
|
|
31
|
+
"typescript": "~5.6.2",
|
|
32
|
+
"vite": "^5.4.9",
|
|
33
|
+
"vue": "^3.5.12"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/ReekaTech/minisite-popup.git"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"registry": "https://registry.npmjs.org/"
|
|
42
|
+
},
|
|
43
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
44
|
+
"private": false
|
|
45
|
+
}
|