Repository navigation
feat(lab): ListInput reorders through core's createReorderable #8163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
cad762b
feat(core): createReorderable makes List rows reorderable
vjeux d0c1590
feat(List): reorder mode's hint, its moves and Done as buttons
vjeux 6ff3d0b
fix(List): a move that renames a row keeps reorder mode
vjeux 66fc409
fix(cli): a create* factory with its own hook lists beside that hook
vjeux 32d137f
docs(List): split comment sentences over 25 words
vjeux 13c6534
fix(List): a finger held on a reorder handle selects no text
vjeux ca8a76b
fix(List): a drag crossing rows reads no layout
vjeux 8de33bf
fix(List): the reorder imports the touch hit area from internal
vjeux e5f5e02
fix(List): the reorder finds its scroller through core's shared walk
vjeux 77fb567
fix(List): a dragged row's preview and line keep its inline start in RTL
vjeux 1baf07c
fix(List): picking a row up lays the page out once
vjeux 28b44c3
spec(AST-103): a held drag near a scroll edge scrolls toward it
vjeux 0abe4b2
fix(List): a dragged row's preview joins no form or radio group
vjeux 4b907b5
fix(List): a scroll during a drag keeps the drop under the pointer
vjeux c89aa9c
spec(react-component-runtime): INV18 allows the reorder's scroll read
vjeux dd99dfa
feat(lab): TransferList reorders through core's createReorderable
vjeux 59c9ffb
fix(lab): a disabled TransferList keeps reordering off
vjeux 2608bef
docs(lab): split comment sentences over 25 words
vjeux 6beee21
chore(lint): the provenance baseline drops List's two removed citations
vjeux f5f1796
fix(lab): a disabled TransferList keeps focusable disabled handles
vjeux 51c7f0c
fix(TransferList): its JSDoc is on the component again
vjeux f2e6a1c
docs(TransferList): it reorders only with reorderable
vjeux 1ee5ffc
docs(TransferList): the anatomy names the reorder hint
vjeux b44d561
spec(react-component-runtime): INV13 lists List's reorder session
vjeux 9a29d9e
feat(lab): ListInput reorders through core's createReorderable
vjeux 4ad7c1c
fix(ListInput): the Add button and the handles stay in the columns
vjeux c03aeed
fix(TransferList): the phone layout's panels stop clipping
vjeux c7702fb
fix(ListInput): a record that stays put keeps Remove in its column
vjeux 05c5150
Merge branch 'main' into uikit/c13-list-reorder
cixzhang d9379cc
Merge branch 'uikit/c13-list-reorder' into uikit/c13-transferlist-reo…
cixzhang 3a1c5e5
Merge branch 'uikit/c13-transferlist-reorder' into uikit/c13-listinpu…
cixzhang 9a497c6
fix(List): a drop or key move reads its rows once, before it writes
vjeux 3384ec6
docs(List): a key move's first glides cost a style pass per row
vjeux 39059ef
Merge branch 'uikit/c13-list-reorder' into uikit/c13-transferlist-reo…
cixzhang 7a333bd
Merge main into createReorderable's List reorder
vjeux 4678530
docs(motion): the reorder measurements name no batch label
vjeux d858d64
Merge branch 'uikit/c13-transferlist-reorder' into uikit/c13-listinpu…
cixzhang c66c799
Merge branch 'uikit/c13-list-reorder' into uikit/c13-transferlist-reo…
cixzhang d3b8e98
Merge branch 'uikit/c13-transferlist-reorder' into uikit/c13-listinpu…
cixzhang ed28397
Merge branch 'main' into uikit/c13-list-reorder
cixzhang ff96b6f
Merge main into createReorderable's List reorder
vjeux 92365e4
refactor(List): the reorder warns once through createDevWarnings
vjeux ba4c743
Merge #8139's final head
vjeux f9029bb
Merge main, where #8139 squash-merged
vjeux 52f7a74
Merge #8143's head, which merges main after #8139
vjeux 1e21069
chore(changeset): Core's internal reorder entry serves TransferList
vjeux 25b6734
Merge #8143's head, which adds its Changeset
vjeux d50b386
chore(changeset): Core's internal reorder entry serves ListInput
vjeux 9bb411b
Merge main, where #8143 squash-merged
vjeux File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [chore] Internal: the undocumented `@astryxdesign/core/internal/reorder` entry also gives Lab's `ListInput` List's reorder session for rows that are not ListItems: `useListReorderScope` and `ListReorderScope`. `List` runs its own rows' session through the same session hooks, so its callers see no change. A ListInput string joins Core's message catalog. Internal entries are outside the public API and its compatibility promise. | ||
|
|
||
| @vjeux |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| /** | ||
| * @file ListInputA11y.stories.tsx | ||
| * @input ListInput with reordering, where the owner keeps the first record in | ||
| * its place | ||
| * @output A fixture for the Chromium check that a record that stays put keeps | ||
| * its Remove in the column of every other record's Remove. It is excluded | ||
| * from the visual matrix | ||
| * @position Browser fixture for ListInput's record columns | ||
| */ | ||
|
|
||
| import {useMemo, useState} from 'react'; | ||
| import type {Meta, StoryObj} from '@storybook/react'; | ||
| import {createReorderable} from '@astryxdesign/core'; | ||
| import {TextInput} from '@astryxdesign/core/TextInput'; | ||
| import {ListInput, type ListInputColumn} from '@astryxdesign/lab'; | ||
|
|
||
| const meta: Meta = { | ||
| title: 'A11y/ListInput', | ||
| tags: ['no-visual'], | ||
| }; | ||
|
|
||
| export default meta; | ||
|
|
||
| interface Guest { | ||
| id: string; | ||
| name: string; | ||
| } | ||
|
|
||
| const COLUMNS: ListInputColumn<Guest>[] = [ | ||
| { | ||
| key: 'name', | ||
| header: 'Name', | ||
| renderInput: ({item, label, isLabelHidden, isDisabled, updateItem}) => ( | ||
| <TextInput | ||
| label={label} | ||
| isLabelHidden={isLabelHidden} | ||
| value={item.name} | ||
| onChange={name => updateItem({...item, name}, 'name')} | ||
| isDisabled={isDisabled} | ||
| /> | ||
| ), | ||
| }, | ||
| ]; | ||
|
|
||
| export const FixedRecord: StoryObj = { | ||
| render: function FixedRecordStory() { | ||
| const [guests, setGuests] = useState<Guest[]>([ | ||
| {id: 'host', name: 'Host'}, | ||
| {id: 'ada', name: 'Ada'}, | ||
| {id: 'grace', name: 'Grace'}, | ||
| ]); | ||
| const reorderable = useMemo( | ||
| () => | ||
| createReorderable({ | ||
| // The host stays first. | ||
| isReorderable: (key: string | number) => key !== 'host', | ||
| onReorder: ({key, to}: {key: string | number; to: number}) => | ||
| setGuests(current => { | ||
| const moved = current.find(guest => guest.id === key); | ||
| if (moved == null) { | ||
| return current; | ||
| } | ||
| const rest = current.filter(guest => guest !== moved); | ||
| return [...rest.slice(0, to), moved, ...rest.slice(to)]; | ||
| }), | ||
| }), | ||
| [], | ||
| ); | ||
| return ( | ||
| <ListInput<Guest> | ||
| label="Guests" | ||
| value={guests} | ||
| onChange={setGuests} | ||
| getItemKey={guest => guest.id} | ||
| createItem={() => ({id: crypto.randomUUID(), name: ''})} | ||
| columns={COLUMNS} | ||
| itemName="guest" | ||
| reorderable={reorderable} | ||
| /> | ||
| ); | ||
| }, | ||
| }; |
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| // Copyright (c) Meta Platforms, Inc. and affiliates. | ||
|
|
||
| 'use client'; | ||
|
|
||
| /** | ||
| * @file ListReorderScope.tsx | ||
| * @input A reorderable object made by createReorderable, and the element | ||
| * whose children are the rows | ||
| * @output `useListReorderScope`, a reorder session over rows that are not | ||
| * ListItems. `ListReorderScope` gives those rows the session as a | ||
| * reorderable List gives its own: `ListReorderHandle` inside a row finds it | ||
| * @position Internal to core; Lab's ListInput reaches it through | ||
| * `@astryxdesign/core/internal/reorder`. It runs the same engine a | ||
| * reorderable List runs, List/listReorderEngine.tsx, which ships with the | ||
| * object. So the rows drag, take keys and announce their moves alike. | ||
| * | ||
| * SYNC: When modified, update List/List.tsx, which runs the same session for | ||
| * its own rows through the hooks in List/listReorderContext.ts. | ||
| */ | ||
|
|
||
| import {useMemo, type ReactNode} from 'react'; | ||
| import type {Reorderable} from '../reorderable/createReorderable'; | ||
| import {ListContext, type ListContextValue} from './ListContext'; | ||
| import { | ||
| useReorderCommit, | ||
| useReorderSession, | ||
| type ListReorderContextValue, | ||
| } from './listReorderContext'; | ||
|
|
||
| /** | ||
| * The reorder session over the children of the element `getList` returns, | ||
| * run by `reorderable`'s engine, or null without an object. `hintId` names | ||
| * the handles' instructions, which `ListReorderScope` renders. The object may | ||
| * be made on every render: the session lasts as long as its engine and reads | ||
| * the object's latest callbacks. | ||
| */ | ||
| export function useListReorderScope( | ||
| reorderable: Reorderable | undefined, | ||
| getList: () => HTMLElement | null, | ||
| hintId: string, | ||
| ): ListReorderContextValue | null { | ||
| const {session, options} = useReorderSession(reorderable, getList, hintId); | ||
| useReorderCommit(session); | ||
| return useMemo( | ||
| () => (session == null || options == null ? null : {session, options}), | ||
| [session, options], | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Gives the rows inside it the reorder session, so `ListReorderHandle` in a | ||
| * row drags that row, and renders the session's instructions and reorder-mode | ||
| * hint. Without a session it renders its children alone. | ||
| */ | ||
| export function ListReorderScope({ | ||
| reorder, | ||
| children, | ||
| }: { | ||
| reorder: ListReorderContextValue | null; | ||
| children: ReactNode; | ||
| }): ReactNode { | ||
| // Only `reorder` is read inside a reorder scope; the rest is a List's | ||
| // defaults for any row that reads it. | ||
| const value = useMemo<ListContextValue | null>( | ||
| () => | ||
| reorder == null | ||
| ? null | ||
| : { | ||
| density: 'balanced', | ||
| hasDividers: false, | ||
| edge: 'padded', | ||
| listStyle: 'none', | ||
| reorder, | ||
| }, | ||
| [reorder], | ||
| ); | ||
| if (reorder == null || value == null) { | ||
| return children; | ||
| } | ||
| return ( | ||
| <ListContext value={value}> | ||
| {children} | ||
| <reorder.session.Host reorder={reorder} /> | ||
| </ListContext> | ||
| ); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.