@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/dist/vue.mjs CHANGED
@@ -1,213 +1 @@
1
- import { i as setupPugpigUpdate, o as rawBridge, r as triggerPugpigUpdateSequence, s as outgoingActions, t as BOOTSTRAP_METHODS } from "./bootstrap-BzOJNz7-.mjs";
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};
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@pugpigbolt/bridge",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
+ "license": "MIT",
4
5
  "files": [
5
6
  "dist"
6
7
  ],