@pugpigbolt/bridge 0.1.1 → 0.1.3
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/README.md +2 -2
- package/dist/chunks/bootstrap-B7Gwb_jl.mjs +1 -0
- package/dist/chunks/development-CzUmtUQR.mjs +1 -0
- package/dist/{index-BAapL0vP.d.mts → chunks/index-B2cNakTH.d.mts} +7 -108
- package/dist/chunks/mockBridgeService-M_48ewQL.mjs +27 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -19
- package/dist/vue.d.mts +9 -50
- package/dist/vue.mjs +1 -213
- package/package.json +2 -1
- package/dist/bootstrap-BzOJNz7-.mjs +0 -530
- package/dist/development-Bl8-5E3g.mjs +0 -56
- package/dist/mockBridgeService-vnaAIjCu.mjs +0 -272
package/dist/vue.mjs
CHANGED
|
@@ -1,213 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { onUnmounted, reactive, ref, toRefs, watchEffect } from "vue";
|
|
3
|
-
//#region src/vue/tooltip.ts
|
|
4
|
-
const tooltipsQueue = ref([]);
|
|
5
|
-
const tooltipsRequests = ref([]);
|
|
6
|
-
const isScrolling = ref(false);
|
|
7
|
-
const createIntersectionObserver = (id, element) => {
|
|
8
|
-
if (tooltipsRequests.value.includes(id)) return;
|
|
9
|
-
const observer = new IntersectionObserver(([entry]) => {
|
|
10
|
-
if (!entry.isIntersecting) return;
|
|
11
|
-
if (tooltipsRequests.value.includes(id)) return observer.disconnect();
|
|
12
|
-
tooltipsQueue.value.push({
|
|
13
|
-
id,
|
|
14
|
-
el: element
|
|
15
|
-
});
|
|
16
|
-
observer.disconnect();
|
|
17
|
-
}, { threshold: 1 });
|
|
18
|
-
observer.observe(element);
|
|
19
|
-
return observer;
|
|
20
|
-
};
|
|
21
|
-
function initToolTips() {
|
|
22
|
-
window.addEventListener("scroll", () => {
|
|
23
|
-
isScrolling.value = true;
|
|
24
|
-
window.clearTimeout(window.scrollTimeout);
|
|
25
|
-
window.scrollTimeout = setTimeout(() => isScrolling.value = false, 150);
|
|
26
|
-
}, false);
|
|
27
|
-
watchEffect(() => {
|
|
28
|
-
if (isScrolling.value) return;
|
|
29
|
-
tooltipsQueue.value.forEach((tooltip, index) => {
|
|
30
|
-
if (tooltipsRequests.value.includes(tooltip.id)) return;
|
|
31
|
-
const { x, y, width: w, height: h } = tooltip.el.getBoundingClientRect();
|
|
32
|
-
const source = {
|
|
33
|
-
w,
|
|
34
|
-
h,
|
|
35
|
-
x,
|
|
36
|
-
y: y + window.scrollY
|
|
37
|
-
};
|
|
38
|
-
window.parent?.pugpigBridgeService?.showToolTip?.(JSON.stringify({
|
|
39
|
-
id: tooltip.id,
|
|
40
|
-
source
|
|
41
|
-
}));
|
|
42
|
-
tooltipsQueue.value.splice(index, 1);
|
|
43
|
-
tooltipsRequests.value.push(tooltip.id);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
function registerToolTips({ attribute = "tooltip", observerOptions } = {}) {
|
|
48
|
-
if (!window.parent?.pugpigBridgeService?.showToolTip) return;
|
|
49
|
-
initToolTips();
|
|
50
|
-
const mutationObserverCallback = (mutations) => {
|
|
51
|
-
mutations.forEach((mutation) => mutation.addedNodes.forEach((node) => {
|
|
52
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
53
|
-
const el = node;
|
|
54
|
-
const tooltipValue = el.getAttribute(attribute);
|
|
55
|
-
if (!tooltipValue) return;
|
|
56
|
-
if (tooltipsRequests.value.includes(tooltipValue)) return;
|
|
57
|
-
createIntersectionObserver(tooltipValue, el);
|
|
58
|
-
}));
|
|
59
|
-
};
|
|
60
|
-
const mutationObserver = new MutationObserver(mutationObserverCallback);
|
|
61
|
-
mutationObserverCallback([{ addedNodes: document.body.childNodes }]);
|
|
62
|
-
mutationObserver.observe(document.body, observerOptions ?? {
|
|
63
|
-
childList: true,
|
|
64
|
-
subtree: true
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
function installTooltipDirective(app) {
|
|
68
|
-
if (!window.parent?.pugpigBridgeService?.showToolTip) {
|
|
69
|
-
app.directive("tooltip", {});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
initToolTips();
|
|
73
|
-
app.directive("tooltip", { mounted: (el, binding) => createIntersectionObserver(binding.value, el) });
|
|
74
|
-
}
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/vue/useBridge.ts
|
|
77
|
-
const reactiveBridge = window.boltBridge || reactive(rawBridge);
|
|
78
|
-
function bindAll(obj, ctx) {
|
|
79
|
-
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, typeof v === "function" ? v.bind(ctx) : v]));
|
|
80
|
-
}
|
|
81
|
-
const boundActions = bindAll(outgoingActions, reactiveBridge);
|
|
82
|
-
/**
|
|
83
|
-
* @description Returns the reactive bridge service with all store data as refs and all outgoing action methods bound to the reactive store.
|
|
84
|
-
* @usage const { stories, authorisationStatus, openArticle, sendAnalytics, ... } = useBridge()
|
|
85
|
-
* @returns stories {Ref<Story[]>} Derived story array with locked, hidden, saved, and read flags applied
|
|
86
|
-
* @returns rawStories {Ref<Story[]>} Unprocessed raw stories as received from the bridge
|
|
87
|
-
* @returns feed {Ref<Feed>} Current feed metadata
|
|
88
|
-
* @returns timelines {Ref<Timeline[]>} Available timelines
|
|
89
|
-
* @returns timelinesStates {Ref<Record<string, TimelineState>>} Per-timeline state records
|
|
90
|
-
* @returns themeURL {Ref<string[]>} URLs for the active theme files
|
|
91
|
-
* @returns theme {Ref<Theme>} Active light theme data
|
|
92
|
-
* @returns darkTheme {Ref<Theme>} Active dark theme data
|
|
93
|
-
* @returns timelineMode {Ref<string>} Current timeline display mode
|
|
94
|
-
* @returns timelineType {Ref<string>} Current timeline type (e.g. 'Timeline' | 'SavedTimeline')
|
|
95
|
-
* @returns sourceURL {Ref<string | null>} Source URL of the current feed
|
|
96
|
-
* @returns baseUrl {Ref<string>} Base URL used to resolve relative paths
|
|
97
|
-
* @returns metadataLoaded {Ref<boolean>} Whether feed metadata has been loaded
|
|
98
|
-
* @returns initialBatchSize {Ref<number>} Number of stories in the first rendered batch
|
|
99
|
-
* @returns filter {Ref<Record<string, unknown>>} Active filter state
|
|
100
|
-
* @returns authorisationStatus {Ref<AuthorisationStatus>} Current user authorisation status
|
|
101
|
-
* @returns issueAuthorisationStatus {Ref<IssueAuthorisationStatus>} Per-issue authorisation status
|
|
102
|
-
* @returns openArticle {(story: Story, index: number) => Promise<unknown>} Opens a story in the native reader
|
|
103
|
-
* @returns shareStory {(story: Story, index: number) => Promise<unknown>} Triggers the native share sheet
|
|
104
|
-
* @returns setSaved {(story: Story, index: number, saved: boolean) => Promise<unknown>} Updates the saved state for a story
|
|
105
|
-
* @returns setRead {(story: Story, index: number) => Promise<unknown>} Marks a story as read
|
|
106
|
-
* @returns sendAnalytics {(type: string | Record<string, unknown>, story: Story, index?: number, dimensions?: AnalyticsDimensions) => Promise<unknown>} Sends an analytics event
|
|
107
|
-
* @returns call {(action: string, payload?: unknown) => Promise<unknown>} Dispatches an arbitrary bridge action
|
|
108
|
-
* @returns timelineIsReady {() => void} Signals that the Vue app has mounted
|
|
109
|
-
* @returns fireToast {(type: string, action: string) => Promise<unknown>} Displays a native toast notification
|
|
110
|
-
* @returns openAudio {(story: Story, fallback: Record<string, unknown>, index: number) => Promise<unknown>} Opens the native audio player
|
|
111
|
-
* @returns openImageGallery {(images: StoryImage[] | undefined, initialImage?: number) => Promise<unknown>} Opens the native image gallery
|
|
112
|
-
* @returns addToCalendar {(story: Story, index: number) => Promise<unknown>} Adds an event to the device calendar
|
|
113
|
-
* @returns resolveIssueAccess {(story: Story) => boolean} Resolves whether a story is locked based on issue authorisation
|
|
114
|
-
* @returns $on {(event: string, listener: (...args: unknown[]) => void) => () => void} Subscribes to a bridge event; returns an unsubscribe function
|
|
115
|
-
* @returns $emit {(event: string, ...args: unknown[]) => void} Emits a bridge event
|
|
116
|
-
* @returns $once {(event: string, listener: (...args: unknown[]) => void) => void} Subscribes to a bridge event for a single invocation
|
|
117
|
-
* @returns bridge {BridgeService} The raw reactive bridge instance for direct access
|
|
118
|
-
*/
|
|
119
|
-
function useBridge() {
|
|
120
|
-
return {
|
|
121
|
-
...toRefs(reactiveBridge),
|
|
122
|
-
...boundActions,
|
|
123
|
-
resolveIssueAccess: reactiveBridge.resolveIssueAccess,
|
|
124
|
-
$on: (event, listener) => reactiveBridge.$on(event, listener),
|
|
125
|
-
$emit: (event, ...args) => reactiveBridge.$emit(event, ...args),
|
|
126
|
-
$once: (event, listener) => reactiveBridge.$once(event, listener),
|
|
127
|
-
bridge: reactiveBridge
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/vue/bootstrap.ts
|
|
132
|
-
function useBootstrap(options = {}) {
|
|
133
|
-
const { readyTimeout = 4e3 } = options;
|
|
134
|
-
if ("scrollRestoration" in history) history.scrollRestoration = "manual";
|
|
135
|
-
triggerPugpigUpdateSequence();
|
|
136
|
-
injectThemeAndWait(readyTimeout);
|
|
137
|
-
applyFeedClasses();
|
|
138
|
-
}
|
|
139
|
-
function injectThemeAndWait(readyTimeout = 4e3) {
|
|
140
|
-
const loaded = [];
|
|
141
|
-
const forceReady = setTimeout(() => {
|
|
142
|
-
globalBridge.logInfo?.("debug", `${readyTimeout}ms have passed, forcing timelineIsReady`);
|
|
143
|
-
globalBridge.timelineIsReady?.();
|
|
144
|
-
}, readyTimeout);
|
|
145
|
-
function onDepLoaded(name) {
|
|
146
|
-
loaded.push(name);
|
|
147
|
-
if (!["renderedRequiredStyles", ...globalBridge.themeFileNames ?? []].every((dep) => loaded.includes(dep))) return;
|
|
148
|
-
clearTimeout(forceReady);
|
|
149
|
-
document.fonts.ready.then(() => globalBridge.timelineIsReady?.());
|
|
150
|
-
}
|
|
151
|
-
for (const url of globalBridge.themeURL ?? []) {
|
|
152
|
-
const link = document.createElement("link");
|
|
153
|
-
link.rel = "stylesheet";
|
|
154
|
-
link.href = url;
|
|
155
|
-
link.onload = () => onDepLoaded(url);
|
|
156
|
-
link.onerror = () => onDepLoaded(url);
|
|
157
|
-
document.head.appendChild(link);
|
|
158
|
-
}
|
|
159
|
-
onDepLoaded("renderedRequiredStyles");
|
|
160
|
-
}
|
|
161
|
-
function applyFeedClasses(rootSelector = "#app") {
|
|
162
|
-
const rootEl = document.querySelector(rootSelector);
|
|
163
|
-
const classes = [...globalBridge.feed?.classes ?? []];
|
|
164
|
-
if (globalBridge.isSavedTimeline) classes.push("saved-timeline");
|
|
165
|
-
if (rootEl && classes.length) rootEl.classList.add(...classes);
|
|
166
|
-
}
|
|
167
|
-
function useDependencyTracker(options = {}) {
|
|
168
|
-
const { readyTimeout = 4e3 } = options;
|
|
169
|
-
const loadedDependencies = ref([]);
|
|
170
|
-
const forceReady = setTimeout(() => {
|
|
171
|
-
globalBridge.logInfo?.("debug", `${readyTimeout}ms have passed, forcing timelineIsReady`);
|
|
172
|
-
globalBridge.timelineIsReady?.();
|
|
173
|
-
}, readyTimeout);
|
|
174
|
-
function onDependencyLoaded(dependencyName) {
|
|
175
|
-
if (loadedDependencies.value.includes(dependencyName)) return;
|
|
176
|
-
loadedDependencies.value.push(dependencyName);
|
|
177
|
-
const isLoaded = [
|
|
178
|
-
"mainView",
|
|
179
|
-
"renderedRequiredStyles",
|
|
180
|
-
...globalBridge.themeFileNames ?? []
|
|
181
|
-
].every((dep) => loadedDependencies.value.includes(dep));
|
|
182
|
-
globalBridge.logInfo?.("debug", `Theme dependency '${dependencyName}' loaded (${performance.now()}ms)`);
|
|
183
|
-
if (!isLoaded) return;
|
|
184
|
-
clearTimeout(forceReady);
|
|
185
|
-
globalBridge.logInfo?.("debug", `Theme dependencies complete: ${loadedDependencies.value.join(", ")}`);
|
|
186
|
-
document.fonts.ready.then(() => {
|
|
187
|
-
globalBridge.logInfo?.("debug", `Document fonts loaded (${performance.now()}ms)`);
|
|
188
|
-
globalBridge.timelineIsReady?.();
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
onUnmounted(() => {
|
|
192
|
-
clearTimeout(forceReady);
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
loadedDependencies,
|
|
196
|
-
onDependencyLoaded
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
//#endregion
|
|
200
|
-
//#region src/vue/index.ts
|
|
201
|
-
const globalBridge = window.boltBridge || reactive(rawBridge);
|
|
202
|
-
window.boltBridge = globalBridge;
|
|
203
|
-
const isDebug = window.location.search.includes("preview");
|
|
204
|
-
const BoltBridgeVue = (app, callback) => {
|
|
205
|
-
setupPugpigUpdate(globalBridge);
|
|
206
|
-
app.config.globalProperties.$bridge = globalBridge;
|
|
207
|
-
app.provide("globalBridge", globalBridge);
|
|
208
|
-
app.use(installTooltipDirective);
|
|
209
|
-
if (isDebug) import("./development-Bl8-5E3g.mjs").then(({ injectBridge }) => injectBridge(app, callback)).then(() => callback?.());
|
|
210
|
-
else callback?.();
|
|
211
|
-
};
|
|
212
|
-
//#endregion
|
|
213
|
-
export { BOOTSTRAP_METHODS, BoltBridgeVue, applyFeedClasses, globalBridge, injectThemeAndWait, isDebug, registerToolTips, triggerPugpigUpdateSequence, useBootstrap, useBridge, useDependencyTracker };
|
|
1
|
+
import{i as e,o as t,r as n,s as r,t as i}from"./chunks/bootstrap-B7Gwb_jl.mjs";import{onUnmounted as a,reactive as o,ref as s,toRefs as c,watchEffect as l}from"vue";const u=s([]),d=s([]),f=s(!1),p=(e,t)=>{if(d.value.includes(e))return;let n=new IntersectionObserver(([r])=>{if(r.isIntersecting){if(d.value.includes(e))return n.disconnect();u.value.push({id:e,el:t}),n.disconnect()}},{threshold:1});return n.observe(t),n};function m(){window.addEventListener(`scroll`,()=>{f.value=!0,window.clearTimeout(window.scrollTimeout),window.scrollTimeout=setTimeout(()=>f.value=!1,150)},!1),l(()=>{f.value||u.value.forEach((e,t)=>{if(d.value.includes(e.id))return;let{x:n,y:r,width:i,height:a}=e.el.getBoundingClientRect(),o={w:i,h:a,x:n,y:r+window.scrollY};window.parent?.pugpigBridgeService?.showToolTip?.(JSON.stringify({id:e.id,source:o})),u.value.splice(t,1),d.value.push(e.id)})})}function h({attribute:e=`tooltip`,observerOptions:t}={}){if(!window.parent?.pugpigBridgeService?.showToolTip)return;m();let n=t=>{t.forEach(t=>t.addedNodes.forEach(t=>{if(t.nodeType!==Node.ELEMENT_NODE)return;let n=t,r=n.getAttribute(e);r&&(d.value.includes(r)||p(r,n))}))},r=new MutationObserver(n);n([{addedNodes:document.body.childNodes}]),r.observe(document.body,t??{childList:!0,subtree:!0})}function g(e){if(!window.parent?.pugpigBridgeService?.showToolTip){e.directive(`tooltip`,{});return}m(),e.directive(`tooltip`,{mounted:(e,t)=>p(t.value,e)})}const _=window.boltBridge||o(t);function v(e,t){return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,typeof n==`function`?n.bind(t):n]))}const y=v(r,_);function b(){return{...c(_),...y,resolveIssueAccess:_.resolveIssueAccess,$on:(e,t)=>_.$on(e,t),$emit:(e,...t)=>_.$emit(e,...t),$once:(e,t)=>_.$once(e,t),bridge:_}}function x(e={}){let{readyTimeout:t=4e3}=e;`scrollRestoration`in history&&(history.scrollRestoration=`manual`),n(),S(t),C()}function S(e=4e3){let t=[],n=setTimeout(()=>{T.logInfo?.(`debug`,`${e}ms have passed, forcing timelineIsReady`),T.timelineIsReady?.()},e);function r(e){t.push(e),[`renderedRequiredStyles`,...T.themeFileNames??[]].every(e=>t.includes(e))&&(clearTimeout(n),document.fonts.ready.then(()=>T.timelineIsReady?.()))}for(let e of T.themeURL??[]){let t=document.createElement(`link`);t.rel=`stylesheet`,t.href=e,t.onload=()=>r(e),t.onerror=()=>r(e),document.head.appendChild(t)}r(`renderedRequiredStyles`)}function C(e=`#app`){let t=document.querySelector(e),n=[...T.feed?.classes??[]];T.isSavedTimeline&&n.push(`saved-timeline`),t&&n.length&&t.classList.add(...n)}function w(e={}){let{readyTimeout:t=4e3}=e,n=s([]),r=setTimeout(()=>{T.logInfo?.(`debug`,`${t}ms have passed, forcing timelineIsReady`),T.timelineIsReady?.()},t);function i(e){if(n.value.includes(e))return;n.value.push(e);let t=[`mainView`,`renderedRequiredStyles`,...T.themeFileNames??[]].every(e=>n.value.includes(e));T.logInfo?.(`debug`,`Theme dependency '${e}' loaded (${performance.now()}ms)`),t&&(clearTimeout(r),T.logInfo?.(`debug`,`Theme dependencies complete: ${n.value.join(`, `)}`),document.fonts.ready.then(()=>{T.logInfo?.(`debug`,`Document fonts loaded (${performance.now()}ms)`),T.timelineIsReady?.()}))}return a(()=>{clearTimeout(r)}),{loadedDependencies:n,onDependencyLoaded:i}}const T=window.boltBridge||o(t);window.boltBridge=T;const E=window.location.search.includes(`preview`),D=(t,n)=>{e(T),t.config.globalProperties.$bridge=T,t.provide(`globalBridge`,T),t.use(g),E?import(`./chunks/development-CzUmtUQR.mjs`).then(({injectBridge:e})=>e(t,n)).then(()=>n?.()):n?.()};export{i as BOOTSTRAP_METHODS,D as BoltBridgeVue,C as applyFeedClasses,T as globalBridge,S as injectThemeAndWait,E as isDebug,h as registerToolTips,n as triggerPugpigUpdateSequence,x as useBootstrap,b as useBridge,w as useDependencyTracker};
|