@cactuslab/native-navigation-react
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -5,2 +5,3 @@ import { initViewHandler } from '@cactuslab/native-navigation'; | ||
import { createReactContext, Context } from './context'; | ||
import { initSync, prepareWindowForSync } from './sync'; | ||
export { useNativeNavigationContext } from './context'; | ||
@@ -11,3 +12,5 @@ export async function initReact(options) { | ||
const internalPlugin = plugin; | ||
const views = {}; | ||
const reactRoots = {}; | ||
initSync(views); | ||
initViewHandler({ | ||
@@ -29,2 +32,4 @@ plugin, | ||
if (rootElement) { | ||
prepareWindowForSync(viewWindow); | ||
views[id] = viewWindow; | ||
const reactRoot = ReactDOM.createRoot(rootElement); | ||
@@ -58,2 +63,3 @@ const context = createReactContext({ | ||
delete reactRoots[id]; | ||
delete views[id]; | ||
} | ||
@@ -60,0 +66,0 @@ } |
@@ -63,2 +63,87 @@ 'use strict'; | ||
/** | ||
* Initialise syncing document.head node changes from `window` into the additional windows we create. | ||
* @param views the windows we've created; note that this collection is expected to change as new windows are created | ||
*/ | ||
function initSync(views) { | ||
let copyNodeId = 1; | ||
/* | ||
* Add a sentinel node to the window's head so we always have a previous sibling with an | ||
* id for future additions so we can put them in the right place. | ||
*/ | ||
const mainSentinel = window.document.createElement('META'); | ||
mainSentinel.dataset['capacitorNativeNavigationId'] = 'sentinel'; | ||
window.document.head.appendChild(mainSentinel); | ||
const observer = new MutationObserver(function (mutations) { | ||
for (const mutation of mutations) { | ||
if (mutation.type !== 'childList') { | ||
return; | ||
} | ||
if (mutation.addedNodes.length) { | ||
/* Assign each added node an id */ | ||
mutation.addedNodes.forEach(function (node) { | ||
if (node.nodeType === Node.ELEMENT_NODE) { | ||
node.dataset['capacitorNativeNavigationId'] = `${copyNodeId++}`; | ||
} | ||
}); | ||
const prevSiblingId = mutation.previousSibling && mutation.previousSibling.nodeType === Node.ELEMENT_NODE ? mutation.previousSibling.dataset['capacitorNativeNavigationId'] : undefined; | ||
if (prevSiblingId) { | ||
/* Copy added nodes to each view */ | ||
for (const viewId of Object.keys(views)) { | ||
const view = views[viewId]; | ||
const prevSibling = view.document.head.querySelector(`[data-capacitor-native-navigation-id="${prevSiblingId}"]`); | ||
if (!prevSibling) { | ||
console.warn(`Marker "${prevSiblingId}" not found in head for view: ${viewId}`); | ||
continue; | ||
} | ||
let marker = prevSibling; | ||
mutation.addedNodes.forEach(function (node) { | ||
if (node.nodeType === Node.ELEMENT_NODE) { | ||
const clone = node.cloneNode(true); | ||
marker.insertAdjacentElement('afterend', clone); | ||
marker = clone; | ||
} | ||
}); | ||
} | ||
} | ||
else { | ||
console.warn('Nodes added to head in an unexpected location'); | ||
} | ||
} | ||
if (mutation.removedNodes.length) { | ||
mutation.removedNodes.forEach(function (node) { | ||
const nodeId = node.dataset['capacitorNativeNavigationId']; | ||
if (nodeId) { | ||
for (const viewId of Object.keys(views)) { | ||
const view = views[viewId]; | ||
const nodeToRemove = view.document.head.querySelector(`[data-capacitor-native-navigation-id="${nodeId}"]`); | ||
if (nodeToRemove) { | ||
nodeToRemove.remove(); | ||
} | ||
} | ||
} | ||
else { | ||
console.warn('Ignoring unknown node removed from head'); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
try { | ||
observer.observe(window.document.head, { | ||
childList: true, | ||
}); | ||
} | ||
catch (error) { | ||
console.warn('Failed to install document head synchronisation', error instanceof Error ? error.message : error); | ||
} | ||
} | ||
function prepareWindowForSync(viewWindow) { | ||
/* Copy all of the nodes with ids to the new window, this will include the sentinel */ | ||
const nodes = window.document.head.querySelectorAll('[data-capacitor-native-navigation-id]'); | ||
nodes.forEach(function (node) { | ||
viewWindow.document.head.append(node.cloneNode(true)); | ||
}); | ||
} | ||
async function initReact(options) { | ||
@@ -68,3 +153,5 @@ const { plugin, root } = options; | ||
const internalPlugin = plugin; | ||
const views = {}; | ||
const reactRoots = {}; | ||
initSync(views); | ||
nativeNavigation.initViewHandler({ | ||
@@ -86,2 +173,4 @@ plugin, | ||
if (rootElement) { | ||
prepareWindowForSync(viewWindow); | ||
views[id] = viewWindow; | ||
const reactRoot = ReactDOM__default["default"].createRoot(rootElement); | ||
@@ -115,2 +204,3 @@ const context = createReactContext({ | ||
delete reactRoots[id]; | ||
delete views[id]; | ||
} | ||
@@ -117,0 +207,0 @@ } |
@@ -58,2 +58,87 @@ var CapacitorNativeNavigationReact = (function (exports, nativeNavigation, React, ReactDOM) { | ||
/** | ||
* Initialise syncing document.head node changes from `window` into the additional windows we create. | ||
* @param views the windows we've created; note that this collection is expected to change as new windows are created | ||
*/ | ||
function initSync(views) { | ||
let copyNodeId = 1; | ||
/* | ||
* Add a sentinel node to the window's head so we always have a previous sibling with an | ||
* id for future additions so we can put them in the right place. | ||
*/ | ||
const mainSentinel = window.document.createElement('META'); | ||
mainSentinel.dataset['capacitorNativeNavigationId'] = 'sentinel'; | ||
window.document.head.appendChild(mainSentinel); | ||
const observer = new MutationObserver(function (mutations) { | ||
for (const mutation of mutations) { | ||
if (mutation.type !== 'childList') { | ||
return; | ||
} | ||
if (mutation.addedNodes.length) { | ||
/* Assign each added node an id */ | ||
mutation.addedNodes.forEach(function (node) { | ||
if (node.nodeType === Node.ELEMENT_NODE) { | ||
node.dataset['capacitorNativeNavigationId'] = `${copyNodeId++}`; | ||
} | ||
}); | ||
const prevSiblingId = mutation.previousSibling && mutation.previousSibling.nodeType === Node.ELEMENT_NODE ? mutation.previousSibling.dataset['capacitorNativeNavigationId'] : undefined; | ||
if (prevSiblingId) { | ||
/* Copy added nodes to each view */ | ||
for (const viewId of Object.keys(views)) { | ||
const view = views[viewId]; | ||
const prevSibling = view.document.head.querySelector(`[data-capacitor-native-navigation-id="${prevSiblingId}"]`); | ||
if (!prevSibling) { | ||
console.warn(`Marker "${prevSiblingId}" not found in head for view: ${viewId}`); | ||
continue; | ||
} | ||
let marker = prevSibling; | ||
mutation.addedNodes.forEach(function (node) { | ||
if (node.nodeType === Node.ELEMENT_NODE) { | ||
const clone = node.cloneNode(true); | ||
marker.insertAdjacentElement('afterend', clone); | ||
marker = clone; | ||
} | ||
}); | ||
} | ||
} | ||
else { | ||
console.warn('Nodes added to head in an unexpected location'); | ||
} | ||
} | ||
if (mutation.removedNodes.length) { | ||
mutation.removedNodes.forEach(function (node) { | ||
const nodeId = node.dataset['capacitorNativeNavigationId']; | ||
if (nodeId) { | ||
for (const viewId of Object.keys(views)) { | ||
const view = views[viewId]; | ||
const nodeToRemove = view.document.head.querySelector(`[data-capacitor-native-navigation-id="${nodeId}"]`); | ||
if (nodeToRemove) { | ||
nodeToRemove.remove(); | ||
} | ||
} | ||
} | ||
else { | ||
console.warn('Ignoring unknown node removed from head'); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
try { | ||
observer.observe(window.document.head, { | ||
childList: true, | ||
}); | ||
} | ||
catch (error) { | ||
console.warn('Failed to install document head synchronisation', error instanceof Error ? error.message : error); | ||
} | ||
} | ||
function prepareWindowForSync(viewWindow) { | ||
/* Copy all of the nodes with ids to the new window, this will include the sentinel */ | ||
const nodes = window.document.head.querySelectorAll('[data-capacitor-native-navigation-id]'); | ||
nodes.forEach(function (node) { | ||
viewWindow.document.head.append(node.cloneNode(true)); | ||
}); | ||
} | ||
async function initReact(options) { | ||
@@ -63,3 +148,5 @@ const { plugin, root } = options; | ||
const internalPlugin = plugin; | ||
const views = {}; | ||
const reactRoots = {}; | ||
initSync(views); | ||
nativeNavigation.initViewHandler({ | ||
@@ -81,2 +168,4 @@ plugin, | ||
if (rootElement) { | ||
prepareWindowForSync(viewWindow); | ||
views[id] = viewWindow; | ||
const reactRoot = ReactDOM__default["default"].createRoot(rootElement); | ||
@@ -110,2 +199,3 @@ const context = createReactContext({ | ||
delete reactRoots[id]; | ||
delete views[id]; | ||
} | ||
@@ -112,0 +202,0 @@ } |
{ | ||
"name": "@cactuslab/native-navigation-react", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "React support for Native navigation for Capacitor apps", | ||
@@ -5,0 +5,0 @@ "main": "dist/plugin.cjs.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75793
15
666