@dark-engine/native-navigation
Advanced tools
Comparing version 0.18.0 to 0.18.1
@@ -10,122 +10,125 @@ 'use strict'; | ||
const NavigationContainer = (0, core_2.forwardRef)( | ||
(0, core_2.createComponent)(({ slot, defaultPathname = constants_1.SLASH, renderActionBar, onNavigate }, ref) => { | ||
const frameRef = (0, core_2.useRef)(null); | ||
const pageRef = (0, core_2.useRef)(null); | ||
const [pathname, setPathname] = (0, core_2.useState)((0, utils_1.normalizePathname)(defaultPathname)); | ||
const [transition, setTransition] = (0, core_2.useState)(null, { priority: core_2.TaskPriority.ANIMATION }); | ||
const scope = (0, core_2.useMemo)( | ||
() => ({ history: null, inTransition: false, transitions: { forward: [], backward: [] } }), | ||
[], | ||
); | ||
const { | ||
transitions: { forward, backward }, | ||
} = scope; | ||
(0, core_2.useLayoutEffect)(() => { | ||
const history = (0, history_1.createNavigationHistory)(pathname, frameRef.current, pageRef.current); | ||
const unsubscribe = history.subscribe((pathname, action, options) => { | ||
const isReplace = action === history_1.HistoryAction.REPLACE; | ||
const isBack = action === history_1.HistoryAction.BACK; | ||
setPathname(pathname); | ||
!isReplace && scheduleTransition(pathname, isBack, options); | ||
(0, core_2.detectIsFunction)(onNavigate) && (0, core_2.detectIsFunction)(pathname); | ||
}); | ||
scope.history = history; | ||
return () => { | ||
unsubscribe(); | ||
history.dispose(); | ||
(0, core_2.createComponent)( | ||
({ slot, defaultPathname = constants_1.SLASH, renderActionBar, onNavigate }, ref) => { | ||
const frameRef = (0, core_2.useRef)(null); | ||
const pageRef = (0, core_2.useRef)(null); | ||
const [pathname, setPathname] = (0, core_2.useState)((0, utils_1.normalizePathname)(defaultPathname)); | ||
const [transition, setTransition] = (0, core_2.useState)(null, { priority: core_2.TaskPriority.ANIMATION }); | ||
const scope = (0, core_2.useMemo)( | ||
() => ({ history: null, inTransition: false, transitions: { forward: [], backward: [] } }), | ||
[], | ||
); | ||
const { | ||
transitions: { forward, backward }, | ||
} = scope; | ||
(0, core_2.useLayoutEffect)(() => { | ||
const history = (0, history_1.createNavigationHistory)(pathname, frameRef.current, pageRef.current); | ||
const unsubscribe = history.subscribe((pathname, action, options) => { | ||
const isReplace = action === history_1.HistoryAction.REPLACE; | ||
const isBack = action === history_1.HistoryAction.BACK; | ||
setPathname(pathname); | ||
!isReplace && scheduleTransition(pathname, isBack, options); | ||
(0, core_2.detectIsFunction)(onNavigate) && (0, core_2.detectIsFunction)(pathname); | ||
}); | ||
scope.history = history; | ||
return () => { | ||
unsubscribe(); | ||
history.dispose(); | ||
}; | ||
}, []); | ||
(0, core_2.useEffect)(() => { | ||
if (!transition) return; | ||
const timeout = transition.options.animated ? transition.options.transition.duration : 0; | ||
const timerId = setTimeout(() => { | ||
scope.inTransition = false; | ||
executeTransitions(); | ||
}, timeout + WAITING_TIMEOUT); | ||
return () => clearTimeout(timerId); | ||
}, [transition]); | ||
const scheduleTransition = (to, isBack, options) => { | ||
if (isBack) { | ||
const transition = backward.pop(); | ||
forward.push(transition); | ||
} else { | ||
const from = scope.history.getBack(); | ||
const forwardTransition = { | ||
from, | ||
to, | ||
isBack: false, | ||
options: resolveNavigationOptions(options), | ||
}; | ||
const backwardTransition = { | ||
...forwardTransition, | ||
isBack: true, | ||
from: forwardTransition.to, | ||
to: forwardTransition.from, | ||
}; | ||
forward.push(forwardTransition); | ||
backward.push(backwardTransition); | ||
} | ||
executeTransitions(); | ||
}; | ||
}, []); | ||
(0, core_2.useEffect)(() => { | ||
if (!transition) return; | ||
const timeout = transition.options.animated ? transition.options.transition.duration : 0; | ||
const timerId = setTimeout(() => { | ||
scope.inTransition = false; | ||
executeTransitions(); | ||
}, timeout + WAITING_TIMEOUT); | ||
return () => clearTimeout(timerId); | ||
}, [transition]); | ||
const scheduleTransition = (to, isBack, options) => { | ||
if (isBack) { | ||
const transition = backward.pop(); | ||
forward.push(transition); | ||
} else { | ||
const from = scope.history.getBack(); | ||
const forwardTransition = { | ||
from, | ||
to, | ||
isBack: false, | ||
options: resolveNavigationOptions(options), | ||
}; | ||
const backwardTransition = { | ||
...forwardTransition, | ||
isBack: true, | ||
from: forwardTransition.to, | ||
to: forwardTransition.from, | ||
}; | ||
forward.push(forwardTransition); | ||
backward.push(backwardTransition); | ||
} | ||
executeTransitions(); | ||
}; | ||
const executeTransitions = () => { | ||
if (scope.inTransition) return; | ||
const transition = forward.shift(); | ||
if (!transition) return setTransition(null); | ||
scope.inTransition = true; | ||
setTransition(transition); | ||
}; | ||
const push = (0, core_2.useEvent)((pathname, options) => scope.history.push(pathname, options)); | ||
const replace = (0, core_2.useEvent)(pathname => scope.history.replace(pathname)); | ||
const back = (0, core_2.useEvent)(() => scope.history.back()); | ||
const getParams = (0, core_2.useEvent)(pathname => scope.history.getParams(pathname)); | ||
const subscribe = (0, core_2.useEvent)(subscriber => scope.history.subscribe(subscriber)); | ||
const contextValue = (0, core_2.useMemo)( | ||
() => ({ pathname, transition, push, replace, back, getParams, subscribe }), | ||
[pathname, transition], | ||
); | ||
(0, core_2.useImperativeHandle)(ref, () => ({ navigateTo: push, goBack: back }), []); | ||
const hasActionBar = (0, core_2.detectIsFunction)(renderActionBar); | ||
const renderAndroid = () => { | ||
return (0, core_2.h)( | ||
core_2.Fragment, | ||
null, | ||
(0, core_2.h)( | ||
const executeTransitions = () => { | ||
if (scope.inTransition) return; | ||
const transition = forward.shift(); | ||
if (!transition) return setTransition(null); | ||
scope.inTransition = true; | ||
setTransition(transition); | ||
}; | ||
const push = (0, core_2.useEvent)((pathname, options) => scope.history.push(pathname, options)); | ||
const replace = (0, core_2.useEvent)(pathname => scope.history.replace(pathname)); | ||
const back = (0, core_2.useEvent)(() => scope.history.back()); | ||
const getParams = (0, core_2.useEvent)(pathname => (scope.history ? scope.history.getParams(pathname) : null)); | ||
const subscribe = (0, core_2.useEvent)(subscriber => scope.history.subscribe(subscriber)); | ||
const contextValue = (0, core_2.useMemo)( | ||
() => ({ pathname, transition, push, replace, back, getParams, subscribe }), | ||
[pathname, transition], | ||
); | ||
(0, core_2.useImperativeHandle)(ref, () => ({ navigateTo: push, goBack: back }), []); | ||
const hasActionBar = (0, core_2.detectIsFunction)(renderActionBar); | ||
const renderAndroid = () => { | ||
return (0, core_2.h)( | ||
core_2.Fragment, | ||
null, | ||
(0, core_2.h)( | ||
'frame', | ||
null, | ||
(0, core_2.h)('page', { actionBarHidden: !hasActionBar }, hasActionBar && renderActionBar(pathname), slot), | ||
), | ||
(0, core_2.h)( | ||
'frame', | ||
{ ref: frameRef, hidden: true }, | ||
(0, core_2.h)('page', { ref: pageRef, actionBarHidden: true }), | ||
), | ||
); | ||
}; | ||
const renderIOS = () => { | ||
return (0, core_2.h)( | ||
'frame', | ||
null, | ||
(0, core_2.h)('page', { actionBarHidden: !hasActionBar }, hasActionBar && renderActionBar(pathname), slot), | ||
), | ||
(0, core_2.h)( | ||
'frame', | ||
{ ref: frameRef, hidden: true }, | ||
(0, core_2.h)('page', { ref: pageRef, actionBarHidden: true }), | ||
), | ||
); | ||
}; | ||
const renderIOS = () => { | ||
return (0, core_2.h)( | ||
'frame', | ||
null, | ||
(0, core_2.h)( | ||
'page', | ||
{ actionBarHidden: !hasActionBar }, | ||
hasActionBar && renderActionBar(pathname), | ||
(0, core_2.h)( | ||
'stack-layout', | ||
null, | ||
'page', | ||
{ actionBarHidden: !hasActionBar }, | ||
hasActionBar && renderActionBar(pathname), | ||
(0, core_2.h)( | ||
'frame', | ||
{ ref: frameRef, hidden: true }, | ||
(0, core_2.h)('page', { ref: pageRef, actionBarHidden: true }), | ||
'stack-layout', | ||
null, | ||
(0, core_2.h)( | ||
'frame', | ||
{ ref: frameRef, hidden: true }, | ||
(0, core_2.h)('page', { ref: pageRef, actionBarHidden: true }), | ||
), | ||
slot, | ||
), | ||
slot, | ||
), | ||
), | ||
); | ||
}; | ||
return (0, core_2.h)( | ||
NavigationContext.Provider, | ||
{ value: contextValue }, | ||
core_1.isAndroid ? renderAndroid() : renderIOS(), | ||
); | ||
}; | ||
return (0, core_2.h)( | ||
NavigationContext.Provider, | ||
{ value: contextValue }, | ||
core_1.isAndroid ? renderAndroid() : renderIOS(), | ||
); | ||
}), | ||
}, | ||
{ displayName: 'NavigationContainer' }, | ||
), | ||
); | ||
@@ -132,0 +135,0 @@ exports.NavigationContainer = NavigationContainer; |
@@ -11,64 +11,71 @@ 'use strict'; | ||
const Navigator = (0, core_2.forwardRef)( | ||
(0, core_2.createComponent)(({ slot, onNavigate }, ref) => { | ||
const { pathname, transition, replace, subscribe } = (0, navigation_container_1.useNavigationContext)(); | ||
const { prefix } = useScreenNavigatorContext(); | ||
const rootRef = (0, core_2.useRef)(null); | ||
const names = slot.map(x => x.props.name); | ||
const pathnames = names.map(x => (0, utils_1.createPathname)(x, prefix)); | ||
const scope = (0, core_2.useMemo)(() => ({ refsMap: {} }), []); | ||
const hiddensMap = (0, core_2.useMemo)( | ||
() => createHiddensMap({ transition, pathnames, pathname, prefix }), | ||
[transition], | ||
); | ||
visitedMap[pathname] = true; | ||
(0, core_2.useLayoutEffect)(() => { | ||
const entry = pathnames[0]; | ||
detectCanReplacePathname(pathname, entry, prefix) && replace(entry); | ||
}, [pathname]); | ||
(0, core_2.useLayoutEffect)(() => { | ||
const canStartTransition = detectCanStartTransition(transition, pathnames, prefix); | ||
if (!canStartTransition || !transition.options.animated) return; | ||
const targetFrom = matchRef(scope.refsMap, transition.from); | ||
const targetTo = matchRef(scope.refsMap, transition.to); | ||
const size = getMeasuredSizeInDPI(rootRef.current); | ||
const animation = createAnimation({ targetFrom, targetTo, transition, size }); | ||
setTimeout(() => { | ||
animation.play().then(() => { | ||
targetFrom.opacity = 1; | ||
targetFrom.translateX = 0; | ||
targetFrom.hidden = true; | ||
targetTo.opacity = 1; | ||
targetTo.translateX = 0; | ||
targetTo.hidden = false; | ||
(0, core_2.createComponent)( | ||
({ slot, onNavigate }, ref) => { | ||
const { pathname, transition, replace, subscribe } = (0, navigation_container_1.useNavigationContext)(); | ||
const { prefix } = useScreenNavigatorContext(); | ||
const rootRef = (0, core_2.useRef)(null); | ||
const names = slot.map(x => x.props.name); | ||
const pathnames = names.map(x => (0, utils_1.createPathname)(x, prefix)); | ||
const scope = (0, core_2.useMemo)(() => ({ refsMap: {} }), []); | ||
const hiddensMap = (0, core_2.useMemo)( | ||
() => createHiddensMap({ transition, pathnames, pathname, prefix }), | ||
[transition], | ||
); | ||
visitedMap[pathname] = true; | ||
(0, core_2.useLayoutEffect)(() => { | ||
const entry = pathnames[0]; | ||
detectCanReplacePathname(pathname, entry, prefix) && replace(entry); | ||
}, [pathname]); | ||
(0, core_2.useLayoutEffect)(() => { | ||
const canStartTransition = detectCanStartTransition(transition, pathnames, prefix); | ||
if (!canStartTransition || !transition.options.animated) return; | ||
const targetFrom = matchRef(scope.refsMap, transition.from); | ||
const targetTo = matchRef(scope.refsMap, transition.to); | ||
const size = getMeasuredSizeInDPI(rootRef.current); | ||
const animation = createAnimation({ targetFrom, targetTo, transition, size }); | ||
setTimeout(() => { | ||
animation.play().then(() => { | ||
targetFrom.opacity = 1; | ||
targetFrom.translateX = 0; | ||
targetFrom.hidden = true; | ||
targetTo.opacity = 1; | ||
targetTo.translateX = 0; | ||
targetTo.hidden = false; | ||
}); | ||
}); | ||
}); | ||
}, [transition]); | ||
(0, core_2.useEffect)(() => { | ||
const syncNavigation = pathname => { | ||
if ((0, core_2.detectIsFunction)(onNavigate)) { | ||
const idx = (0, utils_1.getMatchedIdx)(pathnames, pathname); | ||
onNavigate(pathname, idx); | ||
} | ||
}; | ||
syncNavigation(pathname); | ||
const unsubscribe = subscribe(pathname => syncNavigation(pathname)); | ||
return () => unsubscribe(); | ||
}, []); | ||
(0, core_2.useImperativeHandle)(ref, () => ({ | ||
getPathnameByIdx: idx => pathnames[idx], | ||
})); | ||
return (0, core_2.h)( | ||
'absolute-layout', | ||
{ ref: rootRef, width: FULL, height: FULL }, | ||
slot.map(x => { | ||
const name = x.props.name; | ||
const key = (0, utils_1.createPathname)(name, prefix); | ||
const isHidden = Boolean(hiddensMap[key]); | ||
const setRef = ref => { | ||
scope.refsMap[key] = ref; | ||
}, [transition]); | ||
(0, core_2.useEffect)(() => { | ||
const syncNavigation = pathname => { | ||
if ((0, core_2.detectIsFunction)(onNavigate)) { | ||
const idx = (0, utils_1.getMatchedIdx)(pathnames, pathname); | ||
onNavigate(pathname, idx); | ||
} | ||
}; | ||
return (0, core_2.h)('stack-layout', { ref: setRef, key: key, width: FULL, height: FULL, hidden: isHidden }, x); | ||
}), | ||
); | ||
}), | ||
syncNavigation(pathname); | ||
const unsubscribe = subscribe(pathname => syncNavigation(pathname)); | ||
return () => unsubscribe(); | ||
}, []); | ||
(0, core_2.useImperativeHandle)(ref, () => ({ | ||
getPathnameByIdx: idx => pathnames[idx], | ||
})); | ||
return (0, core_2.h)( | ||
'absolute-layout', | ||
{ ref: rootRef, width: FULL, height: FULL }, | ||
slot.map(x => { | ||
const name = x.props.name; | ||
const key = (0, utils_1.createPathname)(name, prefix); | ||
const isHidden = Boolean(hiddensMap[key]); | ||
const setRef = ref => { | ||
scope.refsMap[key] = ref; | ||
}; | ||
return (0, core_2.h)( | ||
'stack-layout', | ||
{ ref: setRef, key: key, width: FULL, height: FULL, hidden: isHidden }, | ||
x, | ||
); | ||
}), | ||
); | ||
}, | ||
{ displayName: 'StackNavigator.Root' }, | ||
), | ||
); | ||
@@ -88,2 +95,3 @@ const Screen = (0, core_2.createComponent)( | ||
{ | ||
displayName: 'StackNavigator.Screen', | ||
defaultProps: { | ||
@@ -90,0 +98,0 @@ initialParams: {}, |
@@ -7,63 +7,69 @@ 'use strict'; | ||
const navigation_container_1 = require('../navigation-container'); | ||
const Navigator = (0, core_1.createComponent)(({ slot }) => { | ||
const { push } = (0, navigation_container_1.useNavigationContext)(); | ||
const navRef = (0, core_1.useRef)(null); | ||
const [idx, setIdx] = (0, core_1.useState)(0); | ||
const update = (0, core_1.useUpdate)(); | ||
const contextValue = (0, core_1.useMemo)(() => ({ descriptorsMap: {} }), []); | ||
const { descriptorsMap } = contextValue; | ||
(0, core_1.useLayoutEffect)(() => update(), []); | ||
const handleIdxChange = (0, core_1.useEvent)(e => { | ||
const nextIdx = Number(e.sourceEvent.value); | ||
if (nextIdx !== idx) { | ||
const pathname = navRef.current.getPathnameByIdx(nextIdx); | ||
push(pathname, { animated: true }); | ||
} | ||
}); | ||
const handleNavigate = (0, core_1.useEvent)((_, idx) => setIdx(idx)); | ||
const descriptorKeys = Object.keys(descriptorsMap); | ||
return (0, core_1.h)( | ||
TabNavigatorContext.Provider, | ||
{ value: contextValue }, | ||
(0, core_1.h)( | ||
'grid-layout', | ||
{ columns: '*', rows: 'auto, *' }, | ||
const Navigator = (0, core_1.createComponent)( | ||
({ slot }) => { | ||
const { push } = (0, navigation_container_1.useNavigationContext)(); | ||
const navRef = (0, core_1.useRef)(null); | ||
const [idx, setIdx] = (0, core_1.useState)(0); | ||
const update = (0, core_1.useUpdate)(); | ||
const contextValue = (0, core_1.useMemo)(() => ({ descriptorsMap: {} }), []); | ||
const { descriptorsMap } = contextValue; | ||
(0, core_1.useLayoutEffect)(() => update(), []); | ||
const handleIdxChange = (0, core_1.useEvent)(e => { | ||
const nextIdx = Number(e.sourceEvent.value); | ||
if (nextIdx !== idx) { | ||
const pathname = navRef.current.getPathnameByIdx(nextIdx); | ||
push(pathname, { animated: true }); | ||
} | ||
}); | ||
const handleNavigate = (0, core_1.useEvent)((_, idx) => setIdx(idx)); | ||
const descriptorKeys = Object.keys(descriptorsMap); | ||
return (0, core_1.h)( | ||
TabNavigatorContext.Provider, | ||
{ value: contextValue }, | ||
(0, core_1.h)( | ||
'stack-layout', | ||
{ col: 1, row: 1, marginBottom: 50 }, | ||
descriptorKeys.length > 0 && | ||
(0, core_1.h)( | ||
stack_navigator_1.StackNavigator.Root, | ||
{ ref: navRef, onNavigate: handleNavigate }, | ||
descriptorKeys.map(key => { | ||
const { component, slot } = descriptorsMap[key]; | ||
return (0, core_1.h)( | ||
stack_navigator_1.StackNavigator.Screen, | ||
{ key: key, name: key, component: component }, | ||
slot, | ||
); | ||
}), | ||
), | ||
'grid-layout', | ||
{ columns: '*', rows: 'auto, *' }, | ||
(0, core_1.h)( | ||
'stack-layout', | ||
{ col: 1, row: 1, marginBottom: 50 }, | ||
descriptorKeys.length > 0 && | ||
(0, core_1.h)( | ||
stack_navigator_1.StackNavigator.Root, | ||
{ ref: navRef, onNavigate: handleNavigate }, | ||
descriptorKeys.map(key => { | ||
const { component, slot } = descriptorsMap[key]; | ||
return (0, core_1.h)( | ||
stack_navigator_1.StackNavigator.Screen, | ||
{ key: key, name: key, component: component }, | ||
slot, | ||
); | ||
}), | ||
), | ||
), | ||
(0, core_1.h)( | ||
'tab-view', | ||
{ col: 1, row: 2, androidTabsPosition: 'bottom', selectedIndex: idx, onSelectedIndexChange: handleIdxChange }, | ||
slot, | ||
), | ||
), | ||
(0, core_1.h)( | ||
'tab-view', | ||
{ col: 1, row: 2, androidTabsPosition: 'bottom', selectedIndex: idx, onSelectedIndexChange: handleIdxChange }, | ||
slot, | ||
), | ||
), | ||
); | ||
}); | ||
const Screen = (0, core_1.createComponent)(({ name, component, title, iconSource, class: className, slot }) => { | ||
const value = useTabNavigatorContext(); | ||
value.descriptorsMap[name] = { | ||
name, | ||
component, | ||
slot, | ||
}; | ||
return (0, core_1.h)( | ||
'tab-view-item', | ||
{ title: title || name, iconSource: iconSource, class: className, canBeLoaded: true }, | ||
(0, core_1.h)('label', { text: '' }), | ||
); | ||
}); | ||
); | ||
}, | ||
{ displayName: 'TabNavigator.Root' }, | ||
); | ||
const Screen = (0, core_1.createComponent)( | ||
({ name, component, title, iconSource, class: className, slot }) => { | ||
const value = useTabNavigatorContext(); | ||
value.descriptorsMap[name] = { | ||
name, | ||
component, | ||
slot, | ||
}; | ||
return (0, core_1.h)( | ||
'tab-view-item', | ||
{ title: title || name, iconSource: iconSource, class: className, canBeLoaded: true }, | ||
(0, core_1.h)('label', { text: '' }), | ||
); | ||
}, | ||
{ displayName: 'TabNavigator.Screen' }, | ||
); | ||
const TabNavigatorContext = (0, core_1.createContext)(null); | ||
@@ -70,0 +76,0 @@ function useTabNavigatorContext() { |
@@ -23,110 +23,113 @@ import { CoreTypes, isAndroid } from '@nativescript/core'; | ||
const NavigationContainer = forwardRef( | ||
createComponent(({ slot, defaultPathname = SLASH, renderActionBar, onNavigate }, ref) => { | ||
const frameRef = useRef(null); | ||
const pageRef = useRef(null); | ||
const [pathname, setPathname] = useState(normalizePathname(defaultPathname)); | ||
const [transition, setTransition] = useState(null, { priority: TaskPriority.ANIMATION }); | ||
const scope = useMemo( | ||
() => ({ history: null, inTransition: false, transitions: { forward: [], backward: [] } }), | ||
[], | ||
); | ||
const { | ||
transitions: { forward, backward }, | ||
} = scope; | ||
useLayoutEffect(() => { | ||
const history = createNavigationHistory(pathname, frameRef.current, pageRef.current); | ||
const unsubscribe = history.subscribe((pathname, action, options) => { | ||
const isReplace = action === HistoryAction.REPLACE; | ||
const isBack = action === HistoryAction.BACK; | ||
setPathname(pathname); | ||
!isReplace && scheduleTransition(pathname, isBack, options); | ||
detectIsFunction(onNavigate) && detectIsFunction(pathname); | ||
}); | ||
scope.history = history; | ||
return () => { | ||
unsubscribe(); | ||
history.dispose(); | ||
createComponent( | ||
({ slot, defaultPathname = SLASH, renderActionBar, onNavigate }, ref) => { | ||
const frameRef = useRef(null); | ||
const pageRef = useRef(null); | ||
const [pathname, setPathname] = useState(normalizePathname(defaultPathname)); | ||
const [transition, setTransition] = useState(null, { priority: TaskPriority.ANIMATION }); | ||
const scope = useMemo( | ||
() => ({ history: null, inTransition: false, transitions: { forward: [], backward: [] } }), | ||
[], | ||
); | ||
const { | ||
transitions: { forward, backward }, | ||
} = scope; | ||
useLayoutEffect(() => { | ||
const history = createNavigationHistory(pathname, frameRef.current, pageRef.current); | ||
const unsubscribe = history.subscribe((pathname, action, options) => { | ||
const isReplace = action === HistoryAction.REPLACE; | ||
const isBack = action === HistoryAction.BACK; | ||
setPathname(pathname); | ||
!isReplace && scheduleTransition(pathname, isBack, options); | ||
detectIsFunction(onNavigate) && detectIsFunction(pathname); | ||
}); | ||
scope.history = history; | ||
return () => { | ||
unsubscribe(); | ||
history.dispose(); | ||
}; | ||
}, []); | ||
useEffect(() => { | ||
if (!transition) return; | ||
const timeout = transition.options.animated ? transition.options.transition.duration : 0; | ||
const timerId = setTimeout(() => { | ||
scope.inTransition = false; | ||
executeTransitions(); | ||
}, timeout + WAITING_TIMEOUT); | ||
return () => clearTimeout(timerId); | ||
}, [transition]); | ||
const scheduleTransition = (to, isBack, options) => { | ||
if (isBack) { | ||
const transition = backward.pop(); | ||
forward.push(transition); | ||
} else { | ||
const from = scope.history.getBack(); | ||
const forwardTransition = { | ||
from, | ||
to, | ||
isBack: false, | ||
options: resolveNavigationOptions(options), | ||
}; | ||
const backwardTransition = { | ||
...forwardTransition, | ||
isBack: true, | ||
from: forwardTransition.to, | ||
to: forwardTransition.from, | ||
}; | ||
forward.push(forwardTransition); | ||
backward.push(backwardTransition); | ||
} | ||
executeTransitions(); | ||
}; | ||
}, []); | ||
useEffect(() => { | ||
if (!transition) return; | ||
const timeout = transition.options.animated ? transition.options.transition.duration : 0; | ||
const timerId = setTimeout(() => { | ||
scope.inTransition = false; | ||
executeTransitions(); | ||
}, timeout + WAITING_TIMEOUT); | ||
return () => clearTimeout(timerId); | ||
}, [transition]); | ||
const scheduleTransition = (to, isBack, options) => { | ||
if (isBack) { | ||
const transition = backward.pop(); | ||
forward.push(transition); | ||
} else { | ||
const from = scope.history.getBack(); | ||
const forwardTransition = { | ||
from, | ||
to, | ||
isBack: false, | ||
options: resolveNavigationOptions(options), | ||
}; | ||
const backwardTransition = { | ||
...forwardTransition, | ||
isBack: true, | ||
from: forwardTransition.to, | ||
to: forwardTransition.from, | ||
}; | ||
forward.push(forwardTransition); | ||
backward.push(backwardTransition); | ||
} | ||
executeTransitions(); | ||
}; | ||
const executeTransitions = () => { | ||
if (scope.inTransition) return; | ||
const transition = forward.shift(); | ||
if (!transition) return setTransition(null); | ||
scope.inTransition = true; | ||
setTransition(transition); | ||
}; | ||
const push = useEvent((pathname, options) => scope.history.push(pathname, options)); | ||
const replace = useEvent(pathname => scope.history.replace(pathname)); | ||
const back = useEvent(() => scope.history.back()); | ||
const getParams = useEvent(pathname => scope.history.getParams(pathname)); | ||
const subscribe = useEvent(subscriber => scope.history.subscribe(subscriber)); | ||
const contextValue = useMemo( | ||
() => ({ pathname, transition, push, replace, back, getParams, subscribe }), | ||
[pathname, transition], | ||
); | ||
useImperativeHandle(ref, () => ({ navigateTo: push, goBack: back }), []); | ||
const hasActionBar = detectIsFunction(renderActionBar); | ||
const renderAndroid = () => { | ||
return h( | ||
Fragment, | ||
null, | ||
h( | ||
'frame', | ||
const executeTransitions = () => { | ||
if (scope.inTransition) return; | ||
const transition = forward.shift(); | ||
if (!transition) return setTransition(null); | ||
scope.inTransition = true; | ||
setTransition(transition); | ||
}; | ||
const push = useEvent((pathname, options) => scope.history.push(pathname, options)); | ||
const replace = useEvent(pathname => scope.history.replace(pathname)); | ||
const back = useEvent(() => scope.history.back()); | ||
const getParams = useEvent(pathname => (scope.history ? scope.history.getParams(pathname) : null)); | ||
const subscribe = useEvent(subscriber => scope.history.subscribe(subscriber)); | ||
const contextValue = useMemo( | ||
() => ({ pathname, transition, push, replace, back, getParams, subscribe }), | ||
[pathname, transition], | ||
); | ||
useImperativeHandle(ref, () => ({ navigateTo: push, goBack: back }), []); | ||
const hasActionBar = detectIsFunction(renderActionBar); | ||
const renderAndroid = () => { | ||
return h( | ||
Fragment, | ||
null, | ||
h('page', { actionBarHidden: !hasActionBar }, hasActionBar && renderActionBar(pathname), slot), | ||
), | ||
h('frame', { ref: frameRef, hidden: true }, h('page', { ref: pageRef, actionBarHidden: true })), | ||
); | ||
}; | ||
const renderIOS = () => { | ||
return h( | ||
'frame', | ||
null, | ||
h( | ||
'page', | ||
{ actionBarHidden: !hasActionBar }, | ||
hasActionBar && renderActionBar(pathname), | ||
h( | ||
'stack-layout', | ||
'frame', | ||
null, | ||
h('frame', { ref: frameRef, hidden: true }, h('page', { ref: pageRef, actionBarHidden: true })), | ||
slot, | ||
h('page', { actionBarHidden: !hasActionBar }, hasActionBar && renderActionBar(pathname), slot), | ||
), | ||
), | ||
); | ||
}; | ||
return h(NavigationContext.Provider, { value: contextValue }, isAndroid ? renderAndroid() : renderIOS()); | ||
}), | ||
h('frame', { ref: frameRef, hidden: true }, h('page', { ref: pageRef, actionBarHidden: true })), | ||
); | ||
}; | ||
const renderIOS = () => { | ||
return h( | ||
'frame', | ||
null, | ||
h( | ||
'page', | ||
{ actionBarHidden: !hasActionBar }, | ||
hasActionBar && renderActionBar(pathname), | ||
h( | ||
'stack-layout', | ||
null, | ||
h('frame', { ref: frameRef, hidden: true }, h('page', { ref: pageRef, actionBarHidden: true })), | ||
slot, | ||
), | ||
), | ||
); | ||
}; | ||
return h(NavigationContext.Provider, { value: contextValue }, isAndroid ? renderAndroid() : renderIOS()); | ||
}, | ||
{ displayName: 'NavigationContainer' }, | ||
), | ||
); | ||
@@ -133,0 +136,0 @@ const NavigationContext = createContext(null); |
@@ -21,61 +21,64 @@ import { Screen as DeviceScreen, Animation, CoreTypes } from '@nativescript/core'; | ||
const Navigator = forwardRef( | ||
createComponent(({ slot, onNavigate }, ref) => { | ||
const { pathname, transition, replace, subscribe } = useNavigationContext(); | ||
const { prefix } = useScreenNavigatorContext(); | ||
const rootRef = useRef(null); | ||
const names = slot.map(x => x.props.name); | ||
const pathnames = names.map(x => createPathname(x, prefix)); | ||
const scope = useMemo(() => ({ refsMap: {} }), []); | ||
const hiddensMap = useMemo(() => createHiddensMap({ transition, pathnames, pathname, prefix }), [transition]); | ||
visitedMap[pathname] = true; | ||
useLayoutEffect(() => { | ||
const entry = pathnames[0]; | ||
detectCanReplacePathname(pathname, entry, prefix) && replace(entry); | ||
}, [pathname]); | ||
useLayoutEffect(() => { | ||
const canStartTransition = detectCanStartTransition(transition, pathnames, prefix); | ||
if (!canStartTransition || !transition.options.animated) return; | ||
const targetFrom = matchRef(scope.refsMap, transition.from); | ||
const targetTo = matchRef(scope.refsMap, transition.to); | ||
const size = getMeasuredSizeInDPI(rootRef.current); | ||
const animation = createAnimation({ targetFrom, targetTo, transition, size }); | ||
setTimeout(() => { | ||
animation.play().then(() => { | ||
targetFrom.opacity = 1; | ||
targetFrom.translateX = 0; | ||
targetFrom.hidden = true; | ||
targetTo.opacity = 1; | ||
targetTo.translateX = 0; | ||
targetTo.hidden = false; | ||
createComponent( | ||
({ slot, onNavigate }, ref) => { | ||
const { pathname, transition, replace, subscribe } = useNavigationContext(); | ||
const { prefix } = useScreenNavigatorContext(); | ||
const rootRef = useRef(null); | ||
const names = slot.map(x => x.props.name); | ||
const pathnames = names.map(x => createPathname(x, prefix)); | ||
const scope = useMemo(() => ({ refsMap: {} }), []); | ||
const hiddensMap = useMemo(() => createHiddensMap({ transition, pathnames, pathname, prefix }), [transition]); | ||
visitedMap[pathname] = true; | ||
useLayoutEffect(() => { | ||
const entry = pathnames[0]; | ||
detectCanReplacePathname(pathname, entry, prefix) && replace(entry); | ||
}, [pathname]); | ||
useLayoutEffect(() => { | ||
const canStartTransition = detectCanStartTransition(transition, pathnames, prefix); | ||
if (!canStartTransition || !transition.options.animated) return; | ||
const targetFrom = matchRef(scope.refsMap, transition.from); | ||
const targetTo = matchRef(scope.refsMap, transition.to); | ||
const size = getMeasuredSizeInDPI(rootRef.current); | ||
const animation = createAnimation({ targetFrom, targetTo, transition, size }); | ||
setTimeout(() => { | ||
animation.play().then(() => { | ||
targetFrom.opacity = 1; | ||
targetFrom.translateX = 0; | ||
targetFrom.hidden = true; | ||
targetTo.opacity = 1; | ||
targetTo.translateX = 0; | ||
targetTo.hidden = false; | ||
}); | ||
}); | ||
}); | ||
}, [transition]); | ||
useEffect(() => { | ||
const syncNavigation = pathname => { | ||
if (detectIsFunction(onNavigate)) { | ||
const idx = getMatchedIdx(pathnames, pathname); | ||
onNavigate(pathname, idx); | ||
} | ||
}; | ||
syncNavigation(pathname); | ||
const unsubscribe = subscribe(pathname => syncNavigation(pathname)); | ||
return () => unsubscribe(); | ||
}, []); | ||
useImperativeHandle(ref, () => ({ | ||
getPathnameByIdx: idx => pathnames[idx], | ||
})); | ||
return h( | ||
'absolute-layout', | ||
{ ref: rootRef, width: FULL, height: FULL }, | ||
slot.map(x => { | ||
const name = x.props.name; | ||
const key = createPathname(name, prefix); | ||
const isHidden = Boolean(hiddensMap[key]); | ||
const setRef = ref => { | ||
scope.refsMap[key] = ref; | ||
}, [transition]); | ||
useEffect(() => { | ||
const syncNavigation = pathname => { | ||
if (detectIsFunction(onNavigate)) { | ||
const idx = getMatchedIdx(pathnames, pathname); | ||
onNavigate(pathname, idx); | ||
} | ||
}; | ||
return h('stack-layout', { ref: setRef, key: key, width: FULL, height: FULL, hidden: isHidden }, x); | ||
}), | ||
); | ||
}), | ||
syncNavigation(pathname); | ||
const unsubscribe = subscribe(pathname => syncNavigation(pathname)); | ||
return () => unsubscribe(); | ||
}, []); | ||
useImperativeHandle(ref, () => ({ | ||
getPathnameByIdx: idx => pathnames[idx], | ||
})); | ||
return h( | ||
'absolute-layout', | ||
{ ref: rootRef, width: FULL, height: FULL }, | ||
slot.map(x => { | ||
const name = x.props.name; | ||
const key = createPathname(name, prefix); | ||
const isHidden = Boolean(hiddensMap[key]); | ||
const setRef = ref => { | ||
scope.refsMap[key] = ref; | ||
}; | ||
return h('stack-layout', { ref: setRef, key: key, width: FULL, height: FULL, hidden: isHidden }, x); | ||
}), | ||
); | ||
}, | ||
{ displayName: 'StackNavigator.Root' }, | ||
), | ||
); | ||
@@ -95,2 +98,3 @@ const Screen = createComponent( | ||
{ | ||
displayName: 'StackNavigator.Screen', | ||
defaultProps: { | ||
@@ -97,0 +101,0 @@ initialParams: {}, |
@@ -16,59 +16,65 @@ import { | ||
import { useNavigationContext } from '../navigation-container'; | ||
const Navigator = createComponent(({ slot }) => { | ||
const { push } = useNavigationContext(); | ||
const navRef = useRef(null); | ||
const [idx, setIdx] = useState(0); | ||
const update = useUpdate(); | ||
const contextValue = useMemo(() => ({ descriptorsMap: {} }), []); | ||
const { descriptorsMap } = contextValue; | ||
useLayoutEffect(() => update(), []); | ||
const handleIdxChange = useEvent(e => { | ||
const nextIdx = Number(e.sourceEvent.value); | ||
if (nextIdx !== idx) { | ||
const pathname = navRef.current.getPathnameByIdx(nextIdx); | ||
push(pathname, { animated: true }); | ||
} | ||
}); | ||
const handleNavigate = useEvent((_, idx) => setIdx(idx)); | ||
const descriptorKeys = Object.keys(descriptorsMap); | ||
return h( | ||
TabNavigatorContext.Provider, | ||
{ value: contextValue }, | ||
h( | ||
'grid-layout', | ||
{ columns: '*', rows: 'auto, *' }, | ||
const Navigator = createComponent( | ||
({ slot }) => { | ||
const { push } = useNavigationContext(); | ||
const navRef = useRef(null); | ||
const [idx, setIdx] = useState(0); | ||
const update = useUpdate(); | ||
const contextValue = useMemo(() => ({ descriptorsMap: {} }), []); | ||
const { descriptorsMap } = contextValue; | ||
useLayoutEffect(() => update(), []); | ||
const handleIdxChange = useEvent(e => { | ||
const nextIdx = Number(e.sourceEvent.value); | ||
if (nextIdx !== idx) { | ||
const pathname = navRef.current.getPathnameByIdx(nextIdx); | ||
push(pathname, { animated: true }); | ||
} | ||
}); | ||
const handleNavigate = useEvent((_, idx) => setIdx(idx)); | ||
const descriptorKeys = Object.keys(descriptorsMap); | ||
return h( | ||
TabNavigatorContext.Provider, | ||
{ value: contextValue }, | ||
h( | ||
'stack-layout', | ||
{ col: 1, row: 1, marginBottom: 50 }, | ||
descriptorKeys.length > 0 && | ||
h( | ||
StackNavigator.Root, | ||
{ ref: navRef, onNavigate: handleNavigate }, | ||
descriptorKeys.map(key => { | ||
const { component, slot } = descriptorsMap[key]; | ||
return h(StackNavigator.Screen, { key: key, name: key, component: component }, slot); | ||
}), | ||
), | ||
'grid-layout', | ||
{ columns: '*', rows: 'auto, *' }, | ||
h( | ||
'stack-layout', | ||
{ col: 1, row: 1, marginBottom: 50 }, | ||
descriptorKeys.length > 0 && | ||
h( | ||
StackNavigator.Root, | ||
{ ref: navRef, onNavigate: handleNavigate }, | ||
descriptorKeys.map(key => { | ||
const { component, slot } = descriptorsMap[key]; | ||
return h(StackNavigator.Screen, { key: key, name: key, component: component }, slot); | ||
}), | ||
), | ||
), | ||
h( | ||
'tab-view', | ||
{ col: 1, row: 2, androidTabsPosition: 'bottom', selectedIndex: idx, onSelectedIndexChange: handleIdxChange }, | ||
slot, | ||
), | ||
), | ||
h( | ||
'tab-view', | ||
{ col: 1, row: 2, androidTabsPosition: 'bottom', selectedIndex: idx, onSelectedIndexChange: handleIdxChange }, | ||
slot, | ||
), | ||
), | ||
); | ||
}); | ||
const Screen = createComponent(({ name, component, title, iconSource, class: className, slot }) => { | ||
const value = useTabNavigatorContext(); | ||
value.descriptorsMap[name] = { | ||
name, | ||
component, | ||
slot, | ||
}; | ||
return h( | ||
'tab-view-item', | ||
{ title: title || name, iconSource: iconSource, class: className, canBeLoaded: true }, | ||
h('label', { text: '' }), | ||
); | ||
}); | ||
); | ||
}, | ||
{ displayName: 'TabNavigator.Root' }, | ||
); | ||
const Screen = createComponent( | ||
({ name, component, title, iconSource, class: className, slot }) => { | ||
const value = useTabNavigatorContext(); | ||
value.descriptorsMap[name] = { | ||
name, | ||
component, | ||
slot, | ||
}; | ||
return h( | ||
'tab-view-item', | ||
{ title: title || name, iconSource: iconSource, class: className, canBeLoaded: true }, | ||
h('label', { text: '' }), | ||
); | ||
}, | ||
{ displayName: 'TabNavigator.Screen' }, | ||
); | ||
const TabNavigatorContext = createContext(null); | ||
@@ -75,0 +81,0 @@ function useTabNavigatorContext() { |
{ | ||
"name": "@dark-engine/native-navigation", | ||
"version": "0.18.0", | ||
"version": "0.18.1", | ||
"description": "Dark navigation for NativeScript platform", | ||
@@ -5,0 +5,0 @@ "author": "AlexPlex", |
@@ -40,3 +40,5 @@ # @dark-engine/native-navigation 🌖 | ||
import { NavigationContainer, StackNavigator } from '@dark-engine/native-navigation'; | ||
``` | ||
```tsx | ||
const App = createComponent(() => { | ||
@@ -61,3 +63,5 @@ return ( | ||
import { NavigationContainer, TabNavigator } from '@dark-engine/native-navigation'; | ||
``` | ||
```tsx | ||
const App = createComponent(() => { | ||
@@ -64,0 +68,0 @@ return ( |
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
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
122389
1826
172