+ {data.map((item, index) => {
+ const isActive = dragState?.activeIndex === index
+
+ // Clamp translation so the item stays within list bounds.
+ const rawTranslationY = isActive
+ ? dragState.currentY - dragState.startY
+ : 0
+ const translationY = isActive
+ ? Math.max(
+ -index * itemHeight,
+ Math.min(rawTranslationY, (data.length - 1 - index) * itemHeight),
+ )
+ : 0
+
+ // Non-dragged items shift to make room for the dragged item.
+ let offset = 0
+ if (dragState && !isActive) {
+ const orig = dragState.activeIndex
+ if (orig < newPos && index > orig && index <= newPos) {
+ offset = -itemHeight
+ } else if (orig > newPos && index < orig && index >= newPos) {
+ offset = itemHeight
+ }
+ }
+
+ const dragHandle = (
+ ) =>
+ handlePointerDown(e, index)
+ }
+ style={{
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingLeft: 8,
+ paddingRight: 8,
+ paddingTop: 12,
+ paddingBottom: 12,
+ cursor: isActive ? 'grabbing' : 'grab',
+ touchAction: 'none',
+ userSelect: 'none',
+ }}>
+
+
+ )
+
+ return (
+
+ {renderItem(item, dragHandle)}
+
+ )
+ })}
+
+ )
+}
diff --git a/src/components/dms/ActionsWrapper.web.tsx b/src/components/dms/ActionsWrapper.web.tsx
--- a/src/components/dms/ActionsWrapper.web.tsx
+++ b/src/components/dms/ActionsWrapper.web.tsx
@@ -9,7 +9,7 @@
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme} from '#/alf'
import {MessageContextMenu} from '#/components/dms/MessageContextMenu'
-import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontalIcon} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontalIcon} from '#/components/icons/DotGrid'
import {EmojiSmile_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji'
import {EmojiReactionPicker} from './EmojiReactionPicker'
import {hasReachedReactionLimit} from './util'
diff --git a/src/components/dms/ConvoMenu.tsx b/src/components/dms/ConvoMenu.tsx
--- a/src/components/dms/ConvoMenu.tsx
+++ b/src/components/dms/ConvoMenu.tsx
@@ -24,7 +24,7 @@
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
-import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
import {
diff --git a/src/components/dms/EmojiReactionPicker.web.tsx b/src/components/dms/EmojiReactionPicker.web.tsx
--- a/src/components/dms/EmojiReactionPicker.web.tsx
+++ b/src/components/dms/EmojiReactionPicker.web.tsx
@@ -10,7 +10,7 @@
import {type Emoji} from '#/view/com/composer/text-input/web/EmojiPicker'
import {useWebPreloadEmoji} from '#/view/com/composer/text-input/web/useWebPreloadEmoji'
import {atoms as a, flatten, useTheme} from '#/alf'
-import {DotGrid_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
import * as Menu from '#/components/Menu'
import {type TriggerProps} from '#/components/Menu/types'
import {Text} from '#/components/Typography'
diff --git a/src/components/icons/DotGrid.tsx b/src/components/icons/DotGrid.tsx
--- a/src/components/icons/DotGrid.tsx
+++ b/src/components/icons/DotGrid.tsx
@@ -1,5 +1,9 @@
import {createSinglePathSVG} from './TEMPLATE'
-export const DotGrid_Stroke2_Corner0_Rounded = createSinglePathSVG({
+export const DotGrid3x1_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M2 12a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm16 0a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm-6-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4Z',
+})
+
+export const DotGrid2x3_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M9 17a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm6 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm-6-7a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm6 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4ZM9 3a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm6 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4Z',
})
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -45,7 +45,7 @@
import {CircleQuestion_Stroke2_Corner2_Rounded as CircleQuestionIcon} from '#/components/icons/CircleQuestion'
import {CodeBrackets_Stroke2_Corner2_Rounded as CodeBracketsIcon} from '#/components/icons/CodeBrackets'
import {Contacts_Stroke2_Corner2_Rounded as ContactsIcon} from '#/components/icons/Contacts'
-import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe'
import {Lock_Stroke2_Corner2_Rounded as LockIcon} from '#/components/icons/Lock'
import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller'
diff --git a/src/screens/StarterPack/StarterPackScreen.tsx b/src/screens/StarterPack/StarterPackScreen.tsx
--- a/src/screens/StarterPack/StarterPackScreen.tsx
+++ b/src/screens/StarterPack/StarterPackScreen.tsx
@@ -55,7 +55,7 @@
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox'
import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
-import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {ListSparkle_Stroke2_Corner0_Rounded as ListSparkle} from '#/components/icons/ListSparkle'
import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
diff --git a/src/components/PostControls/PostMenu/index.tsx b/src/components/PostControls/PostMenu/index.tsx
--- a/src/components/PostControls/PostMenu/index.tsx
+++ b/src/components/PostControls/PostMenu/index.tsx
@@ -11,7 +11,7 @@
import {type Shadow} from '#/state/cache/post-shadow'
import {EventStopper} from '#/view/com/util/EventStopper'
-import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {useMenuControl} from '#/components/Menu'
import * as Menu from '#/components/Menu'
import {PostControlButton, PostControlButtonIcon} from '../PostControlButton'
diff --git a/src/screens/Profile/components/ProfileFeedHeader.tsx b/src/screens/Profile/components/ProfileFeedHeader.tsx
--- a/src/screens/Profile/components/ProfileFeedHeader.tsx
+++ b/src/screens/Profile/components/ProfileFeedHeader.tsx
@@ -30,7 +30,7 @@
import {useRichText} from '#/components/hooks/useRichText'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
-import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {
Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled,
Heart2_Stroke2_Corner0_Rounded as Heart,
diff --git a/src/screens/ProfileList/components/MoreOptionsMenu.tsx b/src/screens/ProfileList/components/MoreOptionsMenu.tsx
--- a/src/screens/ProfileList/components/MoreOptionsMenu.tsx
+++ b/src/screens/ProfileList/components/MoreOptionsMenu.tsx
@@ -20,7 +20,7 @@
import {CreateOrEditListDialog} from '#/components/dialogs/lists/CreateOrEditListDialog'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ShareIcon} from '#/components/icons/ArrowOutOfBox'
import {ChainLink_Stroke2_Corner0_Rounded as ChainLink} from '#/components/icons/ChainLink'
-import {DotGrid_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
import {PencilLine_Stroke2_Corner0_Rounded as PencilLineIcon} from '#/components/icons/Pencil'
import {PersonCheck_Stroke2_Corner0_Rounded as PersonCheckIcon} from '#/components/icons/Person'
import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin'
diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx
--- a/src/view/com/profile/ProfileMenu.tsx
+++ b/src/view/com/profile/ProfileMenu.tsx
@@ -32,7 +32,7 @@
import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck'
import {CircleX_Stroke2_Corner0_Rounded as CircleXIcon} from '#/components/icons/CircleX'
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
-import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
import {ListSparkle_Stroke2_Corner0_Rounded as List} from '#/components/icons/ListSparkle'
import {Live_Stroke2_Corner0_Rounded as LiveIcon} from '#/components/icons/Live'
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -43,7 +43,7 @@
BulletList_Filled_Corner0_Rounded as ListFilled,
BulletList_Stroke2_Corner0_Rounded as List,
} from '#/components/icons/BulletList'
-import {DotGrid_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as EllipsisIcon} from '#/components/icons/DotGrid'
import {EditBig_Stroke2_Corner0_Rounded as EditBig} from '#/components/icons/EditBig'
import {
Hashtag_Filled_Corner0_Rounded as HashtagFilled,
diff --git a/src/view/shell/desktop/SidebarTrendingTopics.tsx b/src/view/shell/desktop/SidebarTrendingTopics.tsx
--- a/src/view/shell/desktop/SidebarTrendingTopics.tsx
+++ b/src/view/shell/desktop/SidebarTrendingTopics.tsx
@@ -11,7 +11,7 @@
import {useTrendingConfig} from '#/state/service-config'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
-import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
import * as Prompt from '#/components/Prompt'
import {TrendingTopicLink} from '#/components/TrendingTopics'
diff --git a/src/view/com/composer/drafts/DraftItem.tsx b/src/view/com/composer/drafts/DraftItem.tsx
--- a/src/view/com/composer/drafts/DraftItem.tsx
+++ b/src/view/com/composer/drafts/DraftItem.tsx
@@ -11,7 +11,7 @@
import {Button} from '#/components/Button'
import {CirclePlus_Stroke2_Corner0_Rounded as CirclePlusIcon} from '#/components/icons/CirclePlus'
import {type Props as SVGIconProps} from '#/components/icons/common'
-import {DotGrid_Stroke2_Corner0_Rounded as DotsIcon} from '#/components/icons/DotGrid'
+import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsIcon} from '#/components/icons/DotGrid'
import {CloseQuote_Stroke2_Corner0_Rounded as CloseQuoteIcon} from '#/components/icons/Quote'
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
import * as MediaPreview from '#/components/MediaPreview'