Repository navigation
Releases
v0.22.2
🩹 Fixes
-
background-image: var(--g)now draws the gradients its token holds,currentColorin a background token's last layer is now the text colour, and a writtenbackground: platform-color(...)in a stylesheet now takes away a weaker rule's image, as Chrome does for each. (#1395)background-image: var(--g)was dropped with a warning in a stylesheet and in astyleattribute or[style.background-image]binding. It now reads a token from a stylesheet, one set on an element, or a fallback. A token ofnone, one with a colour in it or one that holds no image leaves no image, as in Chrome, where such a value is invalid.A token such as
--x: center currentColorinbackground: linear-gradient(...), var(--x)orbackground: var(--x)was read as invalid, so nothing was drawn. Its colour is now the element's text colour, its own or inherited, and follows it when it changes. The same holds for a token set on an element, avar()fallback, andbackground: linear-gradient(...), currentColorin astyleattribute or[style.background]binding, which was dropped with a warning.background: platform-color(link)in a stylesheet now writes no image and the initial position, size and repeat, asbackground: reddoes, where a weaker rule's image stayed. A system colour such asbackground: LinkTextalready did this, and so did astyleattribute or a[style]binding. -
ScreenCapture.protectAppSwitcher()now hides the app's snapshot in recent apps on Android 13 and later without blocking screenshots, where it rejected with anUnavailabilityErrorfromexpo-screen-capture. (#1414, #1413)It calls
Activity.setRecentsScreenshotEnabled(false)through this package's native half, andunprotectAppSwitcher()calls it withtrue. The setting is applied again to an activity that is recreated while it is on. It needs a development build. On Android before 13, and in Expo Go on Android, both calls now resolve without doing anything.blurIntensityis used on iOS only, which is unchanged. -
A
text-shadowwith no colour, or incurrentColorwritten out, in a token or as the fallback of avar(), in a stylesheet is now drawn in the element's text colour, as Chrome draws it, where it was dropped with a warning. It follows the text colour when that changes, as abox-shadowwith no colour does, and a text inside it with a colour of its own draws the shadow it inherits in that colour, as CSS inheritscurrentColoras the keyword. (#1398) -
A
nullbound to an optional number or boolean input in@ng-native/componentsor@ng-native/expo, or to an optional boolean input in@ng-native/router, is now unset, asundefinedis. Before, a number input sentNaNto native and a boolean input sentfalse. (#1409, #1408)Among them are
[numberOfLines]on<text>,[maxLength]on<text-input>,[spacing]on<expo-glass-container>and[collapsable]on<view>;[size]on<expo-symbol>takes its default of 24. A binding that turns from a value tonullremoves the prop, so native goes back to its default. A boolean bound tonullnow takes the default an unset one takes rather thanfalse, which shows most where that default istrue:[editable]on<text-input>leaves the field editable,[animating]on<activity-indicator>spins and[scrollEnabled]on<scroll-view>scrolls. The same goes for[accessible]and[focusable]on a component that is accessible or focusable by default,[stickySectionHeadersEnabled](sticky on iOS),[alwaysBounceVertical]on a vertical scroll view, andaria-busy,aria-checked,aria-disabled,aria-expandedandaria-selected, which are left out of the accessibility state rather than set tofalse. -
A property children inherit now eases in them while an element transitions or animates it: the text inside a view whose
colortransitions takes each frame's colour, where it jumped to the end colour at once, and the text inside a view whosecolora@keyframesanimation plays follows the frames, where it kept no colour at all. A child that sets the property itself keeps its own. (#1397) -
Tabs that are each
flex: 1 1 0%(Tailwind'sflex-1) in a row as wide as its content now share the row's width as in Chrome, so tabs of different lengths come out the same width wherever that keeps each word whole, where aw-fit,max-w-maxorinline-flexrow had each tab at the width of its own text. (#1403)A tab whose even share would be narrower than its longest word is held at that word, and the other tabs share what is left, so no word is broken. That applies to an item with a
flex-basisof0%or0in any row fitted to its content, including one kept from stretching byalign-self: flex-startand one stretched across awidth: fit-contentbox, where such tabs were the same width even when that was narrower than a word. Chrome under the layout reset keeps the even share and lets the word overflow the tab, so there the widths differ from Chrome's. An item with amin-widthof its own keeps that minimum, and none is held wider than its row.An item with a basis of
0%that does not grow is held at its longest word in such a row, where it was at the width of its text, or 0 wide in a row kept from stretching. One with a basis of0points that does not grow stays 0 wide, as in Chrome.Reading each tab's longest word takes two more commits when such a row is first laid out or what it holds changes. An item's own
min-width: autoin such a row now holds as in Chrome too, where it had the even share or the width of its text.
❤️ Thank You
- Ashley Hunter
- erKam @erkamyaman
- Gabriel Silva @GabeSilvaDev
v0.22.1
🩹 Fixes
injectToken('--accent')from@ng-native/devicereads a CSS custom property's resolved value as a signal, at the root or at a given node; on iOS and Android it follows the colour scheme, media conditions and stylesheet changes, and on the web it follows class and inline style changes under the mount point, thedarkclassmountkeeps included.Engine.tokenandEngine.watchTokendo the same without Angular. A shape'sfillorstrokewritten asvar()on the shape itself is resolved against the tokens in scope. (#1411, #1410)
❤️ Thank You
- Ashley Hunter
v0.22.0
🩹 Fixes
-
On iOS a large title no longer drops into place on every push when a header item holds a
ui-hostwithmatchContents, on a stack whose root page has no header, in a development build with@ng-native/expo. (#1400, #1399, #1405)react-native-screens lays the navigation bar out at once whenever a header item's size changes, and such a host has its size only once SwiftUI lays it out, during the push.
@ng-native/exponow defers that layout until the push or pop ends, for every header item. Expo Go keeps the drop; a host with a fixed size, orsystemImageName, avoids it there. -
A box whose
box-sizingalone changes is now laid out again on iOS and Android, where it kept the size it had: React Native's Yoga does not compareboxSizingwhen a view's props change. To do that, acontent-boxbox whose sizes, padding and border are each in points or unset is committed as the border box it makes, withboxSizing: 'border-box'and its padding and border added to itswidth,heightand their least and most. It lays out the same, but the props a test reads from a commit change for such a box, a web-compatdivand text field among them. (#1393)A
content-boxbox with a percentage size or padding is committed as it was, and still keeps its size on iOS and Android when itsbox-sizingalone changes.A
content-boxbox with anaspect-ratioand awidthorheightin points is committed with the ratio of its border box, so the ratio is of its content box, as in Chrome, where Yoga took it of the border box. One with anaspect-ratioand no size in points, or with a least or most size, is committed as it was. The padding added to the width is read in the box's direction, its own, inherited, or the app's. -
A
z-indexof a math function in a stylesheet, such ascalc(3 / 2)ormin(-2.5, 4), now compiles to the integer Chrome rounds it to, as the same value set in a style already did, where it was dropped with a warning. (#1387)A math function that comes to a length, such as
calc(1px), is still dropped with a warning, as Chrome drops it. One that comes to NaN, such assqrt(-1), compiles to 0, as Chrome reads it. Around()to the nearest, such asround(-1.5, 1), is dropped with a warning, since the compiler would round a negative tie away from zero where Chrome rounds it up. Around()with a negative interval, such asround(up, 3, -2), rounds as Chrome does, and the function and strategy names are read in any case. A z-index past the range of a 32-bit integer, such ascalc(infinity)in a stylesheet orcalc(1e20)in either, is clamped to that range, as Chrome clamps it. One with a string in it, such ascalc("2"), is dropped with a warning, as Chrome drops it. -
On iOS a stack's root page with a large title now launches with the title expanded, where it showed collapsed in the bar until the page was pulled down. (#1402, #1401)
<native-stack-outlet>commits itsRNSScreenStackonly once its first screen has joined it, with that screen, as react-native-screens' own JavaScript does. Committed empty, the stack was laid out with a compact bar before the root page arrived, and the page's scroll view kept the offset that bar gave it. A test that reads the committed tree of an outlet with no screen no longer finds anRNSScreenStackthere. -
On Android a tap on an open
<native-search-bar>no longer presses the<native-header>item hidden under it: while the field is open, the header's other<native-header-item>s take no touches. (#1406, #1404)react-native-screens hides those items when the field opens, but a hidden view keeps its bounds, and React Native's Android touch dispatch finds a target by bounds alone. The items' content now inherits
pointer-events: nonebetween the search bar'stopOpenandtopClose, so the props committed for that content change while the field is open, including an item added while it is open. Content that sets its ownpointer-eventstoautostill takes touches. iOS sends no open or close and is unchanged.
❤️ Thank You
- Ashley Hunter
v0.21.0
🩹 Fixes
-
background: <gradient>, var(--x)now keeps or drops its gradients as Chrome does for a token holding a position, a size or a colour beside them, such as10% 20%,center / coverorcenter red, and a stylesheet's token can now hold a gradient. (#1388)A last layer the token holds is read as a whole: a position with an optional size, a repeat, an attachment, up to two boxes and a colour, each at most once, so
none none,fixed scrollandcenter center centernow make the shorthand invalid, as in Chrome. This holds for a token in a stylesheet, one set on an element, and a stylesheet fallback such asvar(--x, 10px 20px).--g: linear-gradient(...)in a stylesheet is no longer dropped with a warning, andbackground: var(--g)draws it.background: var(--x)of a colour now takes away an image a weaker rule declared, as Chrome does, where the image was left before.[style.background-image]and abackground-imagein astyleattribute now draw linear gradients andnone, where the text was committed as it was before. Any other value, a token among them, is dropped with a warning. -
A stylesheet's
background: <gradient>, var(--x, center / cover)now keeps the gradient when--xis unset, as Chrome does, where a fallback of several words with no colour made the shorthand invalid. (#1390)In development such a fallback no longer draws a warning that the custom property is undefined and the declaration dropped, which it said for
var(--x, none)too. -
@ng-native/expohas aTextRecognitionservice whoserecognize(imageUri, { languages, level })reads the text in an image on iOS, answering the whole text and each line with its confidence and its box in the image's pixels. (#1385)It is Vision's
VNRecognizeTextRequest, from new native code in@ng-native/expo, so it needs a new development build. It runs offline and does not need Apple Intelligence. A box is measured from the image's top left corner with the orientation in the file's metadata applied. In Expo Go, on Android, on the web and in Node,availableis false andrecognizerejects with aMissingModuleError; a test provides a stand-in throughTextRecognition.SOURCE. -
@ng-native/routerhas aTabBarInsetsservice whosebottomsignal is what the tab bar covers of the bottom of the window now, for an overlay outside the tabs: 0 while the bar is hidden or covered, or with no tabs. (#1375, #1359)On iOS, each opened tab of a
<native-tabs-outlet>holds a view of no width after its page to measure the bar, and a<safe-area-provider>in the app is what tells an iPad's bar at the top from one along the bottom. On iOS, a page presented in a tab's own stack counts as covering the bar. On Android, it does not, since the bar stays.On Android, the height comes from a new native module in
@ng-native/expo, so the app needs that package and a new development build. In Android Expo Go, which cannot load it,bottomstays 0. The value includes the home indicator or the system navigation bar, so an overlay takes the larger of it and the safe-area inset rather than their sum. -
A
box-shadowwith no colour in a stylesheet is now drawn in the element's text colour, as Chrome draws it, where it was dropped with a warning, and a stylesheet'sfontshorthand now putsfont-styleback tonormal, so a weaker rule's italic no longer shows through. (#1389)The shadow follows the text colour when it changes, own or inherited, as a border with no colour does. Such a shadow in a
@keyframesframe is still dropped with a warning, as other uses ofcurrentColorthere are. -
A box written as wide as its content in a right-to-left box, with a physical inset on one side and a logical one on the other, such as
left: 10px; inset-inline-start: 5px, is fitted between the two as in a browser, where it was stretched between them. (#1377)In a right-to-left box
inset-inline-startis the right, so withleftit gives the box an inset on each side. -
A development reload while a page of another tab is presented over the tab in front comes back with that page presented, where it logged
NG04002: Cannot match any routesand came back without it. (#1379, #1368) -
A
nullbound to[width]on<native-toolbar-item>, or to[titleFontSize],[largeTitleFontSize]or[backTitleFontSize]on<native-header>, is now unset asundefinedis, where it sent nativeNaN. (#1378, #1370) -
An item with a percentage
flex-basisand a width of its own, such asflex: 0 0 50%; width: 10px, is measured at that width in a row kept from stretching byalign-self: flex-startor its column'salign-items, as in a browser, where it was measured at its content. (#1381)The share is then taken of the width the row comes to. A change to that width, set on the element or by a class, has the row measured again; before, a row fitted to its content kept its old width until something else in it changed.
-
@ng-native/routerlays a stack's hidden navigation bar out with a large title before the first<native-header largeTitle>page is pushed onto it, so on iOS that push no longer starts from the compact bar, with the inline title fading out and the large title dropping into place. (#1396, #1394)The bar is hidden while the stack's root page has no header, and react-native-screens turns large titles on only once the push has started. A header with a large title now asks a new native module in
@ng-native/expoto lay the hidden bar out once beforehand, so the app needs that package and a new development build. In Expo Go, which cannot load it, the first such push still starts from the compact bar. -
An
aspect-ratioof a width and a height set in a style, such as16 / 9orcalc(16 / 9) / 1, is now committed as the one number native takes, and a style value or a custom property with a bracket left open, such astranslateX(1px, is read with it closed, as Chrome reads both. (#1386)Before, a ratio with math on either side reached native as text and drew no ratio, and a value with a bracket left open was dropped or reached native as text. A ratio with a side of 0, or with
autoin it, still reaches native as it is written.A ratio in a style with a side CSS reads no number from, such as
1 / 0x10,1 / Infinityor1 / -2, or with a third side, is dropped with a warning, so the stylesheet's ratio shows, as in Chrome. -
A negative written out in a style, such as
width: -4px,-1emor-10vw, on a property with no value below zero is now dropped with a warning, as Chrome drops it, so the stylesheet's value shows instead. (#1384)This covers the props a math function below zero is clamped on: sizes, padding, font size, line height, border widths and radii, gaps, flex basis, grow and shrink, and aspect ratio. A negative
opacityis kept, since CSS clamps it. This includes a binding such as[style.width.px]that comes out negative, which Angular sends as-4px. A style object's own numbers, such as{ width: -4 }, are React Native's and are unchanged.A shorthand in a style with one part a browser drops, such as
padding: 1px -1pxorpadding: 1px calc(1px + 2), is now dropped whole with a warning, where its other sides were kept before. -
A
font-sizeof 0 no longer throws on Android or draws its text at the default size on iOS: the engine commits a size of 0.001 in its place, too small to see. (#1376, #1369)React Native on Android requires a font size above zero, so
font-size: 0,0emor acalc()clamped to 0 threwFontSize should be a positive value, from a stylesheet, an inline style or a token alike. Android draws no text smaller than one device pixel, so there the text leaves a faint mark one pixel tall in a line box about 1dp tall, where Chrome leaves no box. -
@ng-native/routerlets acanDeactivateguard decide a native pop of a pushed screen, and follows a screen native has already dismissed without asking its guards, so a refusing guard no longer leaves the url on a screen that is gone. (#1380, #1366, #1367)The pushed screen in front holds
preventNativeDismisswhile leaving it would run a guard, as a presented screen already did, and lets go once it would not. On iOS a swipe back, the back button and a pick from its menu then go to the router, where the guard answers, as Android's Back button and header back arrow already did. On iOS a held push cannot be swiped back interactively: the swipe asks rather than following the finger, as with React Navigation'susePreventRemove.A screen native dismisses anyway, such as a
formSheetdragged down or closed by a tap outside it on Android, is followed without asking its guards or those of the routes showing inside it. A refusal used to keep the router on the dismissed screen, where its guard went on refusi...
v0.20.0
🩹 Fixes
-
A
backgroundshorthand of gradients over a token's colour,background: linear-gradient(180deg, #ffffff33 0%, #ffffff00 67.7%), var(--primary), compiles to the gradients as its image and the token as itsbackground-color, where Metro dropped the whole declaration with "'background' mixes var() with other values". The token has to be the whole of the last layer, with no token in the other layers. A last layer that is a gradient with a token in it goes through the existing gradient path as before, and a token anywhere else is still dropped with that warning. (#1308, #1295) -
@ng-native/expohasAddressSearch, from@ng-native/expo/address-search:suggest(query, options)answers a signal of the postal addresses that match a query signal, andresolve(suggestion)answers the picked one's line one, town, postcode, country and other parts, with no permission and no API key. (#1310, #1300)On iOS it uses MapKit's
MKLocalSearchCompleterandMKLocalSearch. On Android it uses the platform'sGeocoderonce the query has not changed for 300 ms, and that geocoder answers whole addresses rather than completions.region: 'GB'keeps the suggestions to that country on Android, to the area around it on iOS 18 and later, and puts that area first on earlier iOS versions. It is native code of@ng-native/expo, so an app needs a new development build to use it. In Expo Go the suggestions stay empty. -
In
@ng-native/router, a<native-header-item>at theleftor therightwith no content and asystemImageNameor atitleis a nativeUIBarButtonItemon iOS, with<native-toolbar-item>'s inputs and(press). (#1309)It commits in the header's
headerLeftBarButtonItemsorheaderRightBarButtonItemsrather than as a custom view, in its place among the side's items as written, and takesbarButtonItemStyle(plainorprominent),tintColor,disabled,accessibilityLabel,hidesSharedBackgroundandsharesBackground. An item with content stays a custom view, as before. The newhiddeninput leaves any header item out of the bar. Android's header draws nothing for a bar button and says so once in development. -
In
@ng-native/expo,Location.geocode()andLocation.reverseGeocode()no longer ask for the location permission on iOS, so an app that only geocodes shows no location prompt there. (#1307)Apple's geocoder needs no permission, and
expo-locationasks for none before geocoding on iOS. Before, both methods asked for the foreground permission first on every platform and answered an empty list when it was refused. On Android they still ask first and answer an empty list without it, becauseexpo-locationrefuses to geocode there without the foreground permission. -
FontRegistry.loadSheet()warns in development when it is given anullsheet, asloadFonts()does. (#1346, #1338)The warning is said once per process, whichever of the two receives the
null, and its wording names both. Anullsheet is whatstyleSheetOf()returns for a class with no compiled sheet. -
A percentage width capped at
max-contentis the smaller of the two, awidth: fit-contentormax-contentcapped box between aleftand arightstays as wide as its content, and a percentageflex-basisin a box fitted to its content is a share of that box's width, as in a browser. (#1348, #1341, #1342, #1343)These change committed layout. A
width: 50%; max-width: max-contentbox with wide content took all of it, and now stops at the percentage:maxWidthis committed as the percentage, and a height capped the same way behaves alike. An absolute or fixed box written as wide as its content filled the room between its insets, and is now fitted within what they and its margins leave, which needs layout read back from native and costs two more commits when something in it changes. An item withflex: 0 0 50%in awidth: fit-contentrow was as wide as its content, and now takes half of the row. A basis of0%is unchanged. Withdirection: rtlon a subtree, such a box between two insets sits at the left inset. -
A row that is
width: fit-contentormax-width: max-contentstays as wide as its items when one of them grows, as in a browser, where withlayoutConformance: 'strict'it had no width at all when the item grew from a basis of zero points (flex: 1 1 0px, or React Native'sflex: 1), and could otherwise fill all the room on offer. (#1327, #1315)The engine fits such a row to its content, as it already fits a row with
align-self: flex-startin a column, and fits awidth: fit-contentbox around a row the same way. A row first laid out with a width, a stretch or a growth of its own, and then let go of it in itsstyle, is fitted to its content again, where it kept the width Yoga gave it, which could be none or all the room on offer. -
A row as wide as its content, such as one with
align-self: flex-startin a column, stays as wide as its items when one of them grows byflex-growor React Native'sflex, as in a browser, where it filled all the room it could have; and aflexof several values, a keyword or a basis in astyleattribute or a[style]binding commits its grow, shrink and basis. (#1305, #1295)React Native's Yoga gives such a row all the room on offer once any item grows, so a segmented control whose tabs are
flex: 1filled the screen. The engine fits the row to its content with the items' growth set aside, as it already did for items with a percentage basis, and commits it at that width, which costs two more commits when something in the row changes. A row that is stretched, or has a width, still shares all of it among its items.style="flex: 1 1 auto"was committed as a string native drops, so the item did not grow at all. Aflexsuch as1 1 auto,2 3,none,autoor30%now commitsflexGrow,flexShrinkandflexBasis, as a stylesheet does, and one that is no CSSflex, such as1 -1or four values, is dropped with a warning in development. One number,flex: 1, is still React Native's ownflexin a style attribute, a binding and a style object. -
@ng-native/expohas a config plugin with apointerEventsoption, off by default, that turns on React Native's W3C pointer events on iOS and Android, so a trackpad, a mouse or a pencil reachesPressable.hoveredand@defer (on hover). (#1357, #1325)Add
["@ng-native/expo", { "pointerEvents": true }]to the plugins inapp.json, then runnpx expo prebuildand make a new development build. It also setsUIApplicationSupportsIndirectInputEventsin Info.plist, which iOS 16 needs to tell a trackpad or a mouse from a finger. It has no effect in Expo Go. A finger's pointer events do not sethoveredor fire@defer (on hover). The development warning for@defer (on hover)now names the option. -
styleSheetOf()andloadFonts()warn in development in a browser build too, wherengDevModestands in for the__DEV__a browser bundler does not define. (#1347, #1339)The warning about a
styleSheetOf()read from a static field of the component's own class, and the one about anullsheet given toloadFonts()orFontRegistry.loadSheet(), were said on a device only.__DEV__still decides wherever it is defined, so a release build on a device stays silent. TheglobalStyles: nullwarning ofmount()from@ng-native/platformis still a device warning, since@ng-native/webhas its ownmount(). -
Clamp a math function that comes out below zero to 0 on a property that cannot be negative, in a stylesheet, a
styleattribute, a[style]binding and acalc()of tokens, and refuse a length added to a number with a warning. (#1352, #1330, #1337)The properties are sizes, padding,
font-size,line-height,gap, border and outline widths, border radii,flex-basis,flex-grow,flex-shrink,aspect-ratioandopacity.width: calc(1px - 5px)commits0where it committed-4. A margin, an inset andletter-spacingkeep their negative.A negative written out on one of these properties other than
opacityin a stylesheet, such aswidth: -4px, is now dropped with a warning, as a browser drops it, where it was committed.opacityclamps a negative however it is written. A token or fallback that holds a negative written out and is used whole on one other thanopacity,width: var(--t)with--t: -4px, leaves the declaration unset, in a stylesheet and set on an element, as it is invalid there in a browser. A fallback inemor a viewport unit is clamped to 0 instead.A stylesheet
calc()ormin()that mixes a length and a number, such aswidth: calc(1px + 2), is dropped with a warning where it compi...
v0.19.2
🩹 Fixes
-
On iOS,
Notificationsputs the data of a push sent through FCM or straight to APNs inrequest.content.data, whichexpo-notificationsfills only for a push from Expo's push service. (#1289, #1287)This covers
latest,response,take(),presented(), and the notification asetHandler()handler is passed, including the one on a timeout error given to itshandleError. The keys come fromrequest.trigger.payloadand are used only whencontent.datais null or missing. The keys the push services add are left out:aps, FCM'sfcm_options,gcm.andgoogle.keys, and on a push from Expo's service itsexperienceId,projectIdandscopeKey. Android, local notifications and an Expo push whosedatathe module already read are unchanged. -
fileRoutesgives a lazy page'scanMatchguards the same instances of itsrouteMetaproviders that the page and its resolvers get, as Angular does for an eager page. (#1292)Before, a lazy page's
canMatchguards ran in an injector of their own, made for each navigation and destroyed after it, so a service a guard wrote was not the one the page read. The injector is now made once for the page's route, and the page's routes are loaded into it. WithwithAutoCleanupInjectors(), it is destroyed when the page is left and made again on the next visit, as an eager page's is. -
On iOS, a
<refresh-control>that is already refreshing when its page is pushed, or when its tab is shown for the first time, starts spinning once the screen has appeared, so it no longer leaves a gap under a large title. (#1298)Before, native began it as the page mounted, before UIKit had laid the page out under its bars. Under a large title that left a gap the spinner's height between the title and the content, which stayed after the spinner stopped and showed on pages shown after it. React Native's own
RefreshControldoes the same. Now a control under a<native-stack-outlet>or<native-tabs-outlet>commitsrefreshingfrom the screen'sappear(a tab'sdidAppear), so the spinner shows as the push finishes rather than during it. On Android, and on a view no outlet shows, nothing changes.SCREEN_SHOWNin@ng-native/deviceis the signal it reads: false for a screen until native says the screen has appeared, then true, and always true outside an outlet.In a Node test rendered for iOS, a refresh control under an outlet now commits
refreshing: falseuntil the test emits the screen'stopAppear(for a tab,topDidAppear) withfabric.emit, since the fake Fabric sends no appear events. -
nx g @ng-native/nx:libraryin the TypeScript preset adds the app's Babel 7@babel/coreto the library's dev dependencies, so pnpm 10 no longer prints "Failed to create bin" forexpoand unmet@babel/corepeers on every install. (#1288, #1285, #1286)@angular/compiler-clidepends on Babel 8, which otherwise answered React Native's@babel/corepeer in the library under pnpm 10, giving the library its own copies ofreact-nativeandexpo. pnpm 11 resolves the peer to the root's Babel 7 already. A library generated before this can add"@babel/core": "^7.29.7"to itsdevDependenciesby hand.
❤️ Thank You
- Ashley Hunter
v0.19.1
🩹 Fixes
-
FirebaseMessaging.unregister()in@ng-native/firebase-messagingdeletes the app's Firebase installation, so FCM answersUNREGISTEREDfor its FID, setsinstallationIdto null, and stops re-registering on a token roll and re-reading the FID on coming back to the front untilregister()runs again. Call it on sign-out when pushes belong to the signed-in user. (#1284, #1282, #1283)On Android, a call without
google-services.jsonin the app now rejects withERR_NO_FIREBASE_CONFIG, as on iOS, rather than a generic error.FirebaseMessagingreads nothing fromNotificationsuntilregister()runs, so a test that fakesNotificationswithoutdevicePushTokencan inject it. The docs show anapp.config.jsthat leaves out a missing Firebase config file, so prebuild works without one.
❤️ Thank You
- Ashley Hunter
v0.19.0
🩹 Fixes
-
On iOS,
@ng-native/exporegisters its@expo/uimodifiers once per app launch instead of on every reload, which could crash a development build inViewModifierRegistry.registerduring a hot reload. (#1261)@expo/ui's modifier registry is shared by the whole process and has no lock, and Expo recreates modules on the JavaScript thread on each reload while the main thread can still be reading the registry for SwiftUI views on screen. The modifiers are the same on every reload, so they are now registered the first time the module is created. -
On iOS, a
ui-bottom-sheetwhose content is in a navigation stack puts itsmodifierson the content inside that stack, soignoreSafeArealets the content run under the home indicator. (#1269)The content is in a navigation stack where the build has SwiftUI's toolbar (
@expo/ui57.0.20 and later) and the sheet is not sized to its content. The stack sets up the safe area again for what is inside it, so the modifiers were outside its reach on theui-group; they are now on theui-zstackinside the stack, and the sheet's own presentation modifiers stay on the group. Presentation modifiers given this way, such aspresentationCornerRadius, still apply. A content modifier such asbackgroundorpaddingthere applies below the sheet's bar, where it has one. A sheet sized to its content, and a build without the toolbar, are unchanged. -
A
<virtual-list>row whose item comes back to the window after leaving it, with nothing arriving to take its place (a filter put on below the scroll position and taken off again), takes back the slot it left. It took another parked slot, so the window held its key twice: withtrack row.keyAngular threwNG0955(duplicated keys) and the row was drawn twice, and withtrack row.slota hidden copy of it was kept. An item that comes back as anotheritemTypegets a slot of that type, and its old one is let go. (#1265) -
A
<native-tabs-outlet>under a route parameter, such ascity/:name, now finds a url inside a tab when the parameter is written with characters a url escapes (an accent or a space,são-paulo,new york) or with matrix parameters (rio;lang=pt). It compared the router's url with the tab's url built from the unescaped segment, so no url was inside a tab: tapping back to a tab opened its first screen instead of the page it was on, and a tap dropped the matrix parameters. A page pushed into such a tab before it was opened now has the tab's first screen under it, as it has elsewhere. (#1266)A url that gives a tab's own segment matrix parameters, such as
library;sort=a/7, is now inside that tab too, under a parameter or not. It was in no tab, so tapping away and back opened the tab's first screen without them. -
Fixes from a security review. (#1279)
@ng-native/web-compat:XMLSerializerescapes the namespace it writes in anxmlnsattribute, so a namespace holding a quote or markup can no longer add attributes or elements to the output.@ng-native/web-compat: only an<iframe>in the HTML namespace becomes a web view or an emulated frame. One in SVG or MathML, such as one inside an SVG image's markup, or one made withcreateElementNSin another namespace, is a plain element, and itssrcdocis not loaded.@ng-native/web-compat: on Android, a message from a frame inside a web view's page that reports its origin asnullarrives with the originnullinstead of the web view's own origin. Asrcdocpage loaded with no base, which Android also reports asnull, is still heard from the app's origin.@ng-native/web-compat: a message posted to an iframe for an origin (not*) waits until the page its newsrcorsrcdocnames has loaded. On Android, one posted assrcdocreplaced adata:page reached that page.@ng-native/web-compat: on iOS, an iframe whosesrchas no host, such as adata:URL, is shown as an empty page with a warning. react-native-webview there threw on it and closed the app.@ng-native/web-compat: an SVG<img>in a template's content, aDOMParserdocument or markupsetHTMLis sanitizing is not fetched until it is put in a document that loads it.@ng-native/web-compat: an emulated frame more than ten frames deep stays blank, so a page that frames itself no longer loads without end.@ng-native/web-compat:DOMParserparses XML that declares a namespace prefix on every level of deep nesting in time that grows with the declarations instead of with their square.@ng-native/router: native tabs leave a tap on a tab that was visited and sits behind acanActivate,canActivateChildorcanMatchguard to the router: the tab is selected once the guard agrees, instead of showing its kept page while the guard runs. This needs react-native-screens 4.30 or later; earlier versions select at once as before.@ng-native/router: a pagepresent()shows over another tab runs each guard of the routes above it with the providers down to that route, not with the page's own.@ng-native/router:fileRoutesruns a page'scanMatchguards with the providers in itsrouteMeta, eager or lazy, as Angular runs a route's guards with its own providers.@ng-native/components: the Markdown component compares each changed block once when a newsourcearrives, where deeply nested blocks such as quotes inside quotes cost three times more for every level.@ng-native/tailwind: avar()name holding a character that has a meaning in a regular expression, such as--a**, no longer stops the build with aSyntaxError. A line break in the Tailwind entry's file name no longer ends the generated module's header comment.@ng-native/web: an image whosereferrerPolicyis set is painted with an<img>that sends that policy, since a CSS background is fetched with the page's.@ng-native/metro: the dev server keeps a parked route history exactly as the app sent it, so a deeply nested body can no longer crash the server.
-
The safe HTML methods in
@ng-native/web-compat(setHTML,appendHTML,prependHTML,beforeHTML,afterHTMLandreplaceWithHTML) now remove an<iframe>inside<svg>or<math>, and any element registered as a web view, such as<web-view>or<dom-component>. Before, such an element passed the safe sanitizer and became a react-native-webview with scripts on, so untrusted markup could run a script at the page's origin and post messages to the app as that origin. (#1271)<dom-component>in@ng-native/exponow sends a changed input only to its own page. Before, once a link in the content took the web view to another site, that site could definewindow.__ngNativeand receive every input the app changed afterwards. -
On iOS 26 and later, a stack page's
:hostlayout (flex-direction,justify-content,align-items,gap) lays out the elements at the root of its template again, and a:host > ...selector matches them again. (#1260, #1212)The safe area view the stack outlet puts a page's content in on iOS 26 and later, which starts it below an opaque
<native-header>, is now made by the engine as a view of the screen's own (Engine.wrapChildren) rather than as an element of the page's tree. The page's template stays in its host, the screen, so its:hostrules and selectors reach it as written, and the layout:hostgives its children is committed on the safe area view, which holds them. For a page whose:hostgives no such layout, the native views and their props are the ones committed before, and the header is still a child of the screen either way. Before iOS 26 and on Android nothing changes. -
On iOS, a navigation that would put a screen on a stack hidden behind a
fullScreenModalanother stack presents now resolves false and leaves the url and the screens as they were, where before it resolved true and showed nothing. (#1270, #1253)A screen a tab's own stack presents as
fullScreenModaltakes the app's stack out of the window, and react-native-screens' iOS stack makes no change while it is out of the window. Sopresent()orpush()of a top-level route,present()of a page of another tab, or a plain router navigation to such a route, from that modal, changed the url without showing the screen, and the nextback()took off the screen nobody saw. These are now aborted before the url changes. A navigation inside the stack presenting the modal, to another tab, back to a screen still on the app's stack, or areplace()orreset(), goes ahead as before. Android is unchanged. -
A running Metro dev server compiles a component again once its
templateUrlorstyleUrlfile changes or is deleted, so a reload, a relaunch or a hot update gets the component as the file is now rather than as it was compiled before. (#1259, #1166)Metro transforms again only the file that changed, and kept the component's own module, with the old template or sheet compiled into it, until the component's
.tsfile changed or Metro restarted; a touch that left the.tsfile's text as it was did not help.withAngularNativenow wrapsserver.enhanceMiddlewareto reach the dev server, which checks the templates and stylesheets each module was compiled from before it answers a bundle request or sends a hot update. A ...
v0.18.2
🩹 Fixes
-
Keyboard.windowTopon Android now includes how far down the screen the app's window starts, in the lower half of a split screen or in a freeform window, whenprovideNativeKeyboard()from@ng-native/expois provided. (#1246, #1189)<keyboard-avoiding-view>, a focused<text-input>and web-compat'sinnerHeightcompared the keyboard's top on the screen with views measured in a window that starts lower down, so they treated the keyboard as lower than it was. AKeyboardSourcecan now report the window's top on the screen asscreenTop, and theNgNativeKeyboardmodule reads it. Expo Go, and apps withoutprovideNativeKeyboard(), still leave it out: React Native has no way to read it. -
@ng-native/expo/calendarreads and writes the device's own calendars, their events and, on iOS, their reminders. (#1244)inject(Calendar)lists the calendars and the events in a range, finds a calendar, event or reminder by id, creates a calendar, and holds full, write-only (iOS 17 and later) and reminders permissions as signals. It wrapsexpo-calendar's object API, which needs a development build. What only iOS has, the default calendar, the picker, the sources and the reminders, answers empty on Android instead of throwing.EntityTypes,FrequencyandAvailabilitycome with it, usable without loading the module. -
<text-input>on iOS 26 and later no longer loses characters typed quickly into an empty field, because iOS is sent the latesteventCountas themostRecentEventCountprop only with a change to a multiline field's text style. (#1257, #1251)UIKit gives an empty field's first typed text a paragraph style whose line break strategy differs from the one React Native compares an echoed
textprop with, so an echo carrying the latest count replaced that text with the same string and dropped the keystrokes queued behind the replace. On iOS the prop now stays at 0, the count a field starts at, so an initial value and a value set before anything is typed still arrive as the prop; text set from code after typing, a refused or rewritten keystroke and a bound selection arrive with the count throughsetTextAndSelection, as before. The commit that changes a multiline field's color, font or another text style carries the latest count, so the text already typed is restyled. Android is unchanged. In@ng-native/testing,fireEvent.changeTextanduserEvent.typecount each field's changes themselves, so an iOS field's committedmostRecentEventCountreads 0 after typing, but after a multiline field is restyled. -
A
<text-input>focused while the keyboard is up now scrolls clear of the keyboard in a sheet attached to the bottom of the screen on iOS, and no longer asks a scroll view resting under a navigation bar to scroll to zero. (#1258, #1247)On iOS, React Native measures a view in a presented sheet from the sheet's top, while the keyboard's top counts from the top of the screen, so
engine.reveal()stopped short by the sheet's distance from the top of the screen and could leave the field behind the keyboard. It now takes that distance off the keyboard's top for a screen presented asmodal,formSheetorpageSheet, and for a<modal>withpresentationStylepageSheetorformSheet, when the sheet is as wide as the window and taller than the room above the keyboard: such a sheet is attached to the bottom of the screen. A narrower sheet (an iPad's, or one floating at a partial detent) and a shorter one, which may have risen over the keyboard, are measured as before. Android is unchanged.reveal()also clamped its target at zero, so a scroll view resting above zero under a navigation bar (contentInsetAdjustmentBehavior="automatic") was sent to zero, under the bar, for a move of a few points. It now sends it no further up than zero or where it already is, whichever is further up. React Native's ownscrollToon iOS still stops at zero when the scroll view has nocontentInsetof its own. -
nx g @ng-native/nx:nativescript <app>sets up@ng-native/nativescriptin an app: the packages, the config plugin, the iOS and Android types, andprovideNativeScript(). (#931) -
@ng-native/nativescriptcalls iOS and Android APIs from TypeScript through@nativescript/react-native, and shows native views built in TypeScript. (#931)provideNativeScript()installs the runtime,NativeScript.call()runs a function for the platform on the JavaScript thread,runOnUI()runs a function on the platform's main thread,<ns-view>shows a UIView or an Android View a worklet builds, andandroidContext()is the Activity to build an Android view with. The app's config plugins list"@ng-native/nativescript". See https://ng-native.com/packages/nativescript.On Android the config plugin sets
android.enableMinifyInReleaseBuilds=falseandnsSkipStaticBindings=trueingradle.properties, since@nativescript/react-native9.1.2 cannot build or run a release on Expo SDK 58 with R8 on, and its release bindings task needs React Native's CLI, which an Expo app does not have. A release APK is larger without R8. -
An app with
@nativescript/react-nativeinstalled and no Babel config of its own gets the worklets plugin withstrictGlobal, so a worklet naming a native class, such asUILabel.new(), no longer fails as the app starts withProperty 'UILabel' doesn't exist. (#931)An app with its own Babel config gets a warning saying what to add to it.
-
An equal
padding-inline-startandpadding-inline-endcompile topaddingStartandpaddingEndin a stylesheet with an@mediarule in it, as they do in one without, and so domargin-inline-*,inset-inline-*andborder-inline-*. They were compiled to left and right. A sheet whose only blocks inside blocks are top-level at-rules such as@mediais no longer printed again by lightningcss before it is compiled, so it compiles as it would without them. (#868)An
overflow-xand anoverflow-yset to the same value in one rule, which compiled tooverflowbeside an@mediarule, are now refused there with the warning to writeoverflow, as they already were in a sheet without one.
❤️ Thank You
- Ashley Hunter
- erKam
- Shahar Kazaz
v0.18.1
🩹 Fixes
-
An app or library
@ng-native/nxgenerates in a workspace with tsconfig path aliases resolves them in its tests withvite-tsconfig-paths, so on Nx 24 a test that imports one of the workspace's libraries no longer fails withCannot find package. (#1242)nxViteTsPathsfrom@nx/vite, which the generators wrote before, is a stub that resolves nothing from Nx 24. Thevitest-tsconfig-pathsmigration rewrites each Vitest config the generators wrote that way, and addsvite-tsconfig-pathsto the rootpackage.jsonwhen it lists none.
❤️ Thank You
- Ashley Hunter