0.9.1-alpha.12
Alpha@xaui/native@0.9.1-alpha.12
Patch Changes
-
cd06df1: Fix the press scale, which lurched on wide controls, and align the touch feedback with the reference implementation's values.
The scale was a flat
0.975for every control. What the eye reads is the displacement, not the ratio: that same ratio moves a 360pt row nine points and a 96pt chip two. It is now0.985adjusted by a width coefficient, so the movement stays roughly constant in points whatever the control's width — the reference width is 300pt, andpressScaleForcarries the arithmetic with a test that asserts a chip and a full-width row travel the same distance. The curve is 300ms eased out, in both directions, instead of 100ms in and 150ms out.The wash goes to
0.1over 200ms. The ripple is Material'sInkRipplerather than an approximation of it: full ink in 75ms held while the circle keeps growing, a circle starting at 30% of its target instead of at a point, a target radius of half the diagonal, and a centre travelling from the finger to the middle of the control. The expansion runs a second while the finger is down and finishes in 225ms once it lifts, so the wave catches up rather than being cut.The ripple now draws, and the waves belong to the root. It never drew, and the first fix was wrong: the handlers went onto the overlay's own
View, which only hears touches that land on it. The overlay is a sibling of the component's children, not their parent, so a ripple worked on a button's padding and did nothing on its label — a bug that looks like a rendering problem. Touches bubble to thePressable, so that is where the handlers live; the root drives the two waves and publishes them, and the overlay only draws them.feedbackVariantis gone, and overlays are composed. The prop named a cross-product in a string —scale,highlight,ripple,scale-highlight,scale-ripple,none— which could name five of the six combinations it had and none of the ones a third overlay would add. A wash and a wave together, which is what Material actually does, was unreachable.The root scales, and anything laid over it is a part that wraps what it sits under:
<PressableFeedback isPressed={isPressed} style={styles.root}> <PressableFeedback.Ripple> <Label /> </PressableFeedback.Ripple> </PressableFeedback>Wrapping costs nothing, which is the part worth knowing: the children are not boxed. They come back as siblings of the wave's layer in a fragment, which has no presence in the host tree, so the root's
flexDirection,gapandalignItemsstill reach them directly and the rendered tree is identical to writing the overlay as a bare sibling. A real wrappingViewwould have been the trap — the root's layout would apply to the wrapper, the primitive would need to be handed the row'sgapto give it back, and it would add the view depth §8 removed.Written bare,
<PressableFeedback.Ripple />is that sibling and order does not matter: the root pulls its bare overlays out and paints them under everything else, so one written after the label does not end up on top of it — a 10% wash over text is subtle enough to ship by accident. A wrapping overlay is left where it is, since it already contains its content.markOverlayis exported, so a third party's own overlay part gets the same treatment.This is also the only shape that survives
asChild, and that was a real hole: the caller's element is the pressable there, so the primitive has no sibling to inject and mounted no overlay at all. AnasChildcontrol could not have one. Now the caller renders it.ButtondropsfeedbackVariantrather than renaming it. It has one treatment and always did: the recipe'spressedstate paints the variant's own pressed colour, so a wash on top would darken the control twice. It scales, and mounts nothing.The ink and the corners are resolved, not configured. The root flattens its own
styleonce and publishes both:backgroundColordecides the contrasting ink, and the radius keys decide the shape an overlay clips itself to. A purple fill gets light ink, a pale surface gets dark ink, and a translucent…Softtoken or no background at all falls back to the theme'sforeground— honest, because the control is showing what is behind it. The perf harness caught thatcontrastOnthrows on thergba()those soft tokens carry, which would have crashed every soft variant on first press.Carrying the clip on the overlay rather than on the root fixes a second thing: the root no longer sets
overflow: 'hidden', so a child that legitimately overflows — a badge on a button's corner — is no longer cut by a decision about the press.inkFor,radiusFromandpartitionOverlaysare pure and tested, as arepressScaleFor,rippleRadiusFor,resolveAnimationandresolveSlotAnimation— thirty-four assertions where the docs previously claimed a test that did not exist. Three carry a decision rather than an implementation: every control travels the same distance in points whatever its width, a translucent background falls back to the foreground instead of throwing, and a bare overlay written last comes back first while a wrapping one stays put. -
51986e5: Style as props (R14) —
useStylePropsandsplitStylePropson@xaui/native/system, and theButtonon them.<Button padding={16} marginTop={8} width="100%">Envoyer</Button> <Button.Label fontSize={18} letterSpacing={1}>Envoyer</Button.Label>Full React Native names, and therefore full React Native values:
padding={16}is 16 points, exactly asstylewould be — a prop carrying the RN key's name while multiplying its value by a scale would be the trap you only catch by measuring on screen. The scale stays one word away,padding={t.spacing(4)}.The set is the node's style type minus the directional forms R13 bans, which are not exposed at all:
ViewStylePropson a root,TextStylePropson a text slot. A name the component already uses stays the component's —sizeis the control's scale,coloris R7's tint. They resolve outside the style cache, after the tint and beforestyle, which is still the last word.Button.Icondeliberately takes none: two ofIcon's three forms render no view of ours.