Repository navigation
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 compiled to a length.In a
styleattribute or a binding, a math function CSS refuses, by its types or for the property, is dropped where it was left as text over a stylesheet's value, and warns in development. That covers a transform with such a function in its arguments, which was already dropped.flex: calc(1 - 2)in a style is a grow of 0 where it was refused, andline-height: calc(1 - 2)is 0 where it was a negative multiple. -
A
backgroundof gradients over avar()now draws neither when the token is unset or is a word that fits no part of a background, as Chrome does, where the gradients stayed and only the colour went. (#1356, #1317)background: var(--x)of such a token takes away an image a weaker rule declared. A gradient set as the token on an element is drawn as one more layer under the written ones, and its colour under them. A length, two lengths,none, or words such ascenterorno-repeatin the token or its fallback leave the written gradients and no colour, as in a browser. -
In
@ng-native/web-compat, a checked radio put in the page, or moved into another group by setting itsname,formortype, unchecks the radio that was checked in that group, as in a browser. Before, both stayed checked. (#1290)An
<input>whosetypeHTML does not know, such asdatetimeor an empty string, is a text field: it can be typed into, has a selection, and matches:read-writeand:placeholder-shown. Before, it was drawn as a plain view that took no text. -
In a development build,
styleSheetOf()in@ng-native/fabricwarns when it is called too early to see a class's own sheet, as from a static field of the component's own class, where it readsnullor a styled base class's sheet.mount()in@ng-native/platformwarns when givenglobalStyles: null, andloadFonts()in@ng-native/expowarns when given anullsheet.styleSheetOf()warns once per class, the other two once per app run; a release build never warns. (#1321, #1312) -
ngNative()has Vitest process every installed package that lists@angular/coreas a dependency or a peer, so a library such as@tanstack/angular-query-experimentalshares the test's@angular/coreand itsinject()no longer fails with NG0203. (#1301, #1299)Vitest handed such a package to Node, which loaded a second instance of
@angular/corefor it, so the library'sinject()saw none of the test's injection context and a partial-compiled one reached the runtime unlinked. A package that does not list@angular/corestill goes inngNative({ inline }). The plugin's patterns are now added only whentest.server.deps.inlineis nottrue, where they stopped the run with "ex.test is not a function". -
userEvent.typefrom@ng-native/testingtypes each character into the text the field shows. After a keystroke the app refused or rewrote and put its own text back, the next character is added to that text, as on a device, where before it was added to the refused text: typinga3into a field that takes only digits and holds12now ends with123, not12. (#1323) -
userEvent.typein@ng-native/testingtypes each character at the field's caret, replacing a selection, rather than always at the end of the text. (#1349, #1340)A field that moves the caret or the selection with
setSelection()while handling a keystroke now has the next character typed there, whereuserEvent.typeused to append it. A test that typed into such a field and expected the appended text needs the text a text field gives: for a caret set to 0 after each key, typingabends withba. A field that never moves the caret, or that refuses or rewrites a key, behaves as before.When the app puts different text back without a selection, the caret keeps its distance from the end of the old text, as an iOS field does. A key that does not fit in what
maxLengthleaves, such as an emoji for one free unit, is dropped whole. -
In
@ng-native/metro, a component's compiled sheet is on its class as soon as the class is defined, sostyleSheetOf(Component)called at the top level of the module that declares it returns the sheet. Before, the sheet was attached after the rest of the module had run, and such a call returnednull. (#1303, #1296) -
In
@ng-native/router,NativeNavigation.back()called while a navigation is still running waits past a guard's redirect and goes back once the redirected navigation lands. Before, the redirect dropped the back, and the screen the guard redirected to stayed on top. (#1291) -
In
@ng-native/router, a page of another tab shown withpresent()shares one instance of each service the routes above it provide between those routes' guards, the page's guards and resolvers, and the page, as in its tab. (#1304, #1280)Before, the presented copy put every provider on the page's own route, so a service an ancestor's guard wrote and the page or a resolver read was two instances. A page with providers of its own that had been shown in its tab first also reused the injector Angular made for it there, under the tab's instances rather than the presented ones. The presented page's
ActivatedRoutenow has a parent route for each route it sits inside, each without a component, and reads their data and params through Angular's inheritance. -
In
@ng-native/router, a page of another tab shown withpresent()keeps thetitle,canDeactivate,runGuardsAndResolversandresourcesof the routes above it, as in its tab, andpresent()shows a page behind acanLoadguard where its url puts it. (#1324, #1313)Before, the presented copy of each route above the page kept only its providers,
canActivate,canActivateChild,resolveanddata. A title set above a page with none of its own was missing from the presented page's route, acanDeactivateguard above the page did not run when it was dismissed, and resolvers above it ran again on the defaultparamsChangewhatever the route said. Such a guard now runs as the presented page is dismissed, withnullfor the component, since the copy of a route above the page has none.A route above the page with a
canLoadguard had its children loaded without running the guard, and the page presented. The page is now shown where its url puts it, where the router runs the guard. -
On iOS, a swipe no longer dismisses a presented screen that a
canDeactivateguard refuses to leave, and a refused swipe no longer strands the router on a page nobody can see; on Android, dragging aformSheetdown still dismisses it whatever the guard says. (#1351, #1331, #1332)A screen shown with
present(), on the stack or over another tab, now setspreventNativeDismisswhen leaving it would run acanDeactivateguard, and a dismissal it refuses goes to the router, where the guard answers. A swipe the guard allows dismisses the screen after it has answered. Android's Back button and header back arrow go the same way. A page that also bindspreventNativeDismisshearsnativeDismissCancelledfor these attempts, even while its binding is false. Pushed screens are not held: on iOS, a swipe back from one is not stopped by its guard.A
canDeactivateguard of a route above a page presented from another tab is given no component. When it fails, the failure now goes to the app'sErrorHandlerand the page is dismissed, where the navigation failed and the page stayed up. -
A page of another tab presented with
present()is the page its url names, whatever was presented before it: presenting/library/newafter/library/7, where the tab hasnewbefore:id, showed the:idpage withidset tonew. The routes presented pages are copied to at the root now keep the order their routes have in the tab. (#1328) -
On iOS, a pointer event on an engine node is no longer dropped by Fabric when an app has turned on React Native's W3C pointer events, so
Pressable.hoveredand@defer (on hover)follow the pointer in such an app. (#1354)React Native leaves those events off by default on iOS and Android, so nothing changes in an app that has not set
RCTSetDispatchW3CPointerEvents(YES).fabric.emitfrom@ng-native/testingnow drops atopPointer*event on a view whose node has no committed view, as Fabric does. -
<markdown>can draw links in the app's own schemes, such asmyapp:item/42, as links reported through(linkPress): list them in a new[linkSchemes]input, or for every<markdown>withprovideMarkdownLinkSchemes('myapp')from@ng-native/components/markdown. (#1364, #1362)A listed scheme is never opened by
<markdown>, even when no handler callspreventDefault(), so the app routes it. The input replaces the provider's list when it is set, an empty one included. Schemes are matched without regard to case. A scheme that is not listed is still drawn as the link's text,javascript:among them, andhttp,https,mailtoandtelare opened on press as before. Changing the list draws the document again, a streaming one too. -
LanguageModel.session()keeps a conversation with the on-device model across turns, with tools the model can call throughdefineTool(), on iOS. (#1311)A session's
stream()andrespond()see the turns before them,transcriptis the conversation as plain JSON, andtoolCallslists the tool calls running now, on the session and on each reply. Tool handlers run in the session's injection context, so they caninject(). A cancelled or failed turn is left out of the transcript, as the model leaves it out of its context. A conversation that outgrows the context window fails withLanguageModelContextExceededError. The session is released when the injector it was made in is destroyed, or withrelease(). On Android,expo-local-llmrefuses a session with tools, and that error reaches the reply unchanged.LanguageModelStreamgains atoolCallssignal, which is always empty for the one-shotstream(). -
A
<keyboard-avoiding-view behavior="height">laid out for the first time while the keyboard is already up keeps the height it shrinks to. It took the height it had shrunk to as its own, so on the next layout it measured no overlap and grew back under the keyboard. (#1329) -
border,outline,box-shadow,background,fontandtext-decorationin astyleattribute or a[style]binding now draw, committing the props a stylesheet commits, where each was committed as text native does not read. (#1355, #1336, #1318)The same goes for
border-topand the other sides, logical sides included, and forborder-width,border-colorandborder-radiusof several values. A value these do not read (abackgroundwith a position, size,url()or a token in a gradient, abox-shadowwith a token beside other parts, afontwithsmall-caps) is dropped with a warning in development.[style.border-color]and the other colour properties exceptcolortakecurrentColor. -
Work out
calc(),min(),max()andclamp()in astyleattribute or a[style]binding as a stylesheet does, for lengths inemand viewport units, percentages, numbers and transform arguments. (#1350, #1344, #1333, #1334, #1335)A value set this way commits what a stylesheet commits for the same declaration where it was committed as text native drops, or dropped. That covers
opacity,flex-grow,flex-shrink,aspect-ratio, a unitlessline-height,font-weight, theflexshorthand, and the arguments of a transform. AtranslateX(1em)in a bound transform is measured against the font size in scope too.A
calc()of tokens set on an element now needs whitespace either side of+and-, as CSS does:calc(var(--gap)+2px)is invalid, so the declaration that reads it is unset. Aclamp()inside one, with avar()in it, is read.A value with a comparison only the box or the font size can settle, such as
min(50%, 100px)ormin(1em, 20px), is left as written, and native drops it. -
A
calc()of lengths inpxorremand numbers, set in astyleattribute or bound with[style]or[style.x], is committed as the points it comes to, as the same value in a stylesheet is:flex: 1 1 calc(10px + 10px)andwidth: calc(2 * 1rem)were committed as their text, which native drops. Acalc()with a percentage, anemor a viewport unit is left as before. (#1322, #1316) -
@ng-native/routerhasinjectPageFocused(), a signal that is true while a page's tab is selected and nothing is stacked over it, for stopping work such as a poll or a socket while the page stays mounted behind another. (#1361, #1358)It reads the same state as
SCREEN_IN_FRONTfrom@ng-native/device. It does not follow the app going to the background, whichAppStatedoes. -
In
@ng-native/router, a bar button<native-header-item>takes a[menu]that opens a nativeUIMenuon iOS and reports the action chosen through(menuSelect), and an item with a[spacing]is a fixed gap among the bar buttons on iOS. (#1353)The new types are
HeaderMenu,HeaderMenuActionandHeaderSubmenu. A tap on a bar button with a menu opens the menu rather than sending(press). Android's header draws neither, and its one development warning for bar buttons now names them. The header has no flexible spacer.
❤️ Thank You
- Ashley Hunter
- erKam @erkamyaman
- Gabriel Silva @GabeSilvaDev