diff --git a/components/content-picker/DraggableChip.tsx b/components/content-picker/DraggableChip.tsx index 56fbd0d0..e7a31135 100644 --- a/components/content-picker/DraggableChip.tsx +++ b/components/content-picker/DraggableChip.tsx @@ -1,4 +1,4 @@ -import { Flex, FlexItem } from '@wordpress/components'; +import { Flex, FlexItem, __experimentalTruncate as Truncate } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import styled from '@emotion/styled'; import { DragHandle } from '../drag-handle'; @@ -19,6 +19,7 @@ const Chip = styled.div` font-size: 13px; line-height: 1.4; white-space: nowrap; + max-width: min(300px, 100%); svg { fill: currentColor; @@ -40,7 +41,9 @@ export const DraggableChip = (props: DraggableChipProps) => { - {title} + + {title} + diff --git a/components/content-picker/PickedItem.tsx b/components/content-picker/PickedItem.tsx index aa9f1172..5497dda7 100644 --- a/components/content-picker/PickedItem.tsx +++ b/components/content-picker/PickedItem.tsx @@ -4,8 +4,14 @@ import { CSS } from '@dnd-kit/utilities'; import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; -import { close } from '@wordpress/icons'; -import { Button, __experimentalTreeGridRow as TreeGridRow } from '@wordpress/components'; +import { close, chevronUp, chevronDown } from '@wordpress/icons'; +import { + Button, + __experimentalTreeGridRow as TreeGridRow, + VisuallyHidden, + __experimentalVStack as VStack, + __experimentalTruncate as Truncate, +} from '@wordpress/components'; import { DragHandle } from '../drag-handle'; import { ContentSearchMode } from '../content-search/types'; @@ -18,12 +24,15 @@ export type PickedItemType = { }; const PickedItemContainer = styled.div<{ isDragging?: boolean; isOrderable?: boolean }>` + box-sizing: border-box; position: relative; display: flex; align-items: center; gap: 8px; padding: 6px 8px; min-height: 36px; + max-width: 100%; + width: 100%; color: #1e1e1e; opacity: ${({ isDragging }) => (isDragging ? 0.5 : 1)}; background: ${({ isDragging }) => (isDragging ? '#f0f0f0' : 'transparent')}; @@ -37,6 +46,13 @@ const PickedItemContainer = styled.div<{ isDragging?: boolean; isOrderable?: boo &:hover { background: #f0f0f0; + + .move-up-button, + .move-down-button, + .remove-button { + opacity: 1; + pointer-events: auto; + } } .components-button.has-icon { @@ -76,6 +92,7 @@ const RemoveButton = styled(Button)<{ isDragging?: boolean }>` const ItemContent = styled.div` flex: 1; min-width: 0; + max-width: calc(100% - 80px); /* Account for the width of buttons */ display: flex; flex-direction: column; gap: 2px; @@ -88,9 +105,6 @@ const ItemTitle = styled.span` line-height: 1.4; font-weight: 500; color: #1e1e1e; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; `; const ItemURL = styled.span` @@ -102,6 +116,28 @@ const ItemURL = styled.span` text-overflow: ellipsis; `; +const MoveButton = styled(Button)` + &.components-button.has-icon { + min-width: 20px; + padding: 0; + height: 14px; + } + + &.components-button.has-icon svg { + width: 18px; + height: 18px; + } + + opacity: 0; + pointer-events: none; + transition: opacity 0.1s linear; + + &:focus { + opacity: 1; + pointer-events: auto; + } +`; + interface PickedItemProps { item: PickedItemType; isOrderable?: boolean; @@ -111,6 +147,8 @@ interface PickedItemProps { isDragging?: boolean; positionInSet?: number; setSize?: number; + onMoveUp?: () => void; + onMoveDown?: () => void; } /** @@ -127,6 +165,8 @@ const PickedItem: React.FC = ({ isDragging = false, positionInSet = 1, setSize = 1, + onMoveUp, + onMoveDown, }) => { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id, @@ -137,6 +177,9 @@ const PickedItem: React.FC = ({ transition, }; + const isFirst = positionInSet === 1; + const isLast = positionInSet === setSize; + return ( = ({ )} - {decodeEntities(item.title)} + + {decodeEntities(item.title)} + {item.url && ( {filterURLForDisplay(safeDecodeURI(item.url)) || ''} )} + {isOrderable && !isDragging && ( + + { + e.stopPropagation(); + onMoveUp?.(); + }} + className="move-up-button" + > + + {__('Move item up', '10up-block-components')} + + + { + e.stopPropagation(); + onMoveDown?.(); + }} + className="move-down-button" + > + + {__('Move item down', '10up-block-components')} + + + + )} {!isDragging && ( = ({ posts, isOrderable = false, @@ -176,6 +190,16 @@ const SortableList: React.FC = ({ const preparedItem = preparedItems[post.uuid]; if (!preparedItem) return null; + const handleMoveUp = () => { + if (index === 0) return; + setPosts(arrayMove(posts, index, index - 1)); + }; + + const handleMoveDown = () => { + if (index === items.length - 1) return; + setPosts(arrayMove(posts, index, index + 1)); + }; + return ( = ({ id={post.uuid} positionInSet={index + 1} setSize={items.length} + onMoveUp={handleMoveUp} + onMoveDown={handleMoveDown} /> ); }); @@ -194,14 +220,14 @@ const SortableList: React.FC = ({ // If not orderable or only one item, render simple list if (!isOrderable || !hasMultiplePosts) { return ( - {}} onExpandRow={() => {}} > {renderItems(posts)} - + ); } @@ -214,7 +240,7 @@ const SortableList: React.FC = ({ onDragEnd={handleDragEnd} onDragCancel={handleDragCancel} > - {}} @@ -223,7 +249,7 @@ const SortableList: React.FC = ({ {renderItems(posts)} - + {activeId && activePost ? : null}