Repository navigation
feat(segmented): the chosen face slides to the new choice (D-055, Android 0.9.0) - #152
Merged
Merged
Conversation
…roid 0.9.0) Choosing another segment used to swap faces: the old one's went, the new one's appeared, only the colors fading. The owner chose a sliding face. One face per control instead of one per segment. It moves to the new choice over motion.duration.base (200ms) with the standard easing, its width following, the same move as DdsTabs's underline. The first layout and a new place for the same choice (rotation, font size, resize) put it there without travel; reduced motion moves it at once. Web: one .dds-segmented__indicator in the track (React and Solid render Ark's SegmentGroup.Indicator; Vue measures it itself). It stays hidden until measured, and until then the chosen item draws its own face, so server- rendered first paint and plain HTML lose nothing. Android: one thumb behind the row; segments draw only press, hover and focus. Tests: browser tests for React, Solid and Vue (face on the chosen item, one face, travels, ends on the new item), a source contract, and device tests (first frame already on the chosen segment, midway between, ends on it).
…t ResizeObserver CI's Solid hydration check failed with "win.ResizeObserver is not a constructor": Ark's SegmentGroup.Indicator makes Zag observe the chosen item unconditionally, through the element's own window. Zag offers no switch for it, and it measures only at start and on a value change, so mounting the indicator later would leave it unplaced. A test-only polyfill would have hidden the same crash in consumers' jsdom tests (the owner asked not to cover it that way). One small DOM function, attachSegmentIndicator, now does the measuring for React, Solid and Vue (the same file in each package; a contract test keeps them identical). Without ResizeObserver it does nothing: the indicator stays hidden and the chosen item paints its own face, so servers, jsdom tests and old browsers lose only the slide. The server and the first client render are the same hidden span, so hydration is unchanged. The fixtures keep their original stubs, so the release checks now run the guarded path. The slide is switched off on transitionend rather than on any resize: in Vue a resize notice arrives right after the click and was cancelling the slide (the local Vue browser run caught it).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The segmented control's chosen face now slides to the new choice, on the web (React, Solid, Vue) and on Android (D-055). Before this, the old segment's face disappeared and the new one's appeared, with only the colors fading. The owner chose the sliding face.
motion.duration.base(200ms) with the standard easing, and its width follows. This is the same move as the AndroidDdsTabsunderline. The face looks as in D-054:bg.raised, a 1dpborder.strongedge, a level-1 shadow.prefers-reduced-motionon the web; on Android it moves at once when animations are off on the device..dds-segmented__indicatorinside the track, on the chosen item's box (--left/--top/--width/--height). One small DOM function,attachSegmentIndicator, does the measuring for React, Solid and Vue. It is the same file in each package, and a contract test keeps the three identical. It slides only when the chosen item changes, follows resizes without a slide, and turns the slide off ontransitionend. WithoutResizeObserverit does nothing: the indicator stayshiddenand the chosen item paints its own face. On a server, in a jsdom test or in an old browser, only the slide is lost. The server and the first client render are the same hidden span, so hydration is unchanged. Once the indicator is placed,:has()clears the item's own face, so there is only ever one.DdsTabspattern). Segments now draw only press, hover and focus. The focus ring lies over the thumb's edge.Other options considered (D-055): Ark's
SegmentGroup.Indicator(the first commit here). Zag observes the chosen item withResizeObserverunconditionally, so CI's Solid hydration check crashed withwin.ResizeObserver is not a constructor, and consumers' jsdom tests would too. Zag has no switch for it and measures only at start and on a value change, so a late mount doesn't work either. Also CSS anchor positioning (no script, but Firefox lacks it, the transition when the anchor changes differs between browsers, and several controls on one page needanchor-scope), keeping only the color fade, and 300ms (it would not match the tab underline).Release
VERSION_NAME, changelog, guide §12, READMEs).minorchangeset fordds-css,dds-react,dds-solidanddds-vue(fixed group → 0.35.0).Tests
tests/browser/segmented-slide.ts, used by the React/Solid component spec and the Vue spec: the indicator sits on the chosen item's box, the chosen item has no face of its own (only one face), the indicator runs a position transition on a change, and it ends on the new item.tests/foundation-contracts.test.mjs: the base duration and standard easing, 0ms under reduced motion, hidden until measured, the:has()handover, and all three frameworks render the indicator.SegmentedOnDeviceTest: on the first frame (clock held) the face is already on the chosen segment and 44dp tall; 100ms into a change it is between the old and new segments, in place and in width; it ends on the new segment; no choice draws no face. The existing pixel, no-shift and screen-reader tests stay.Verified locally
verify:source:stage1-2,verify:source:stage3-4,verify:foundation,verify:foundation:core,verify:release, the release checks for solid (passing without any stub), react, vue, table, svelte and table-vue, the test steps for table, vue, svelte and table-vue,verify:react:browser, the table-react test and browser steps, and the canary dependencies/test/build steps. Package unit tests and type checks pass for dds-react, dds-solid and dds-vue, with new tests for the no-ResizeObserverpath in all three and a placement test in React.verify:react:testandverify:solid:test(mainfails the same way under this load, and those files pass 64/64 on their own);npm installtiming out in oneverify:react:releaserun (it passed in an earlier run);verify:foundation:layoutsandverify:canary:preview(a page or server didn't appear within 5s; neither involves this control). The final Vue browser run timed out across the whole page (page.gotoover 30s at 77% CPU). The earlier Vue run, before thetransitionendfix, failed only the slide check, and that failure is what this commit fixes.SegmentedOnDeviceTest24/24 (the slide tests ran, not skipped). I checked the catalog in light and dark on the phone: the face ends flush on the new segment both ways.