Skip to content

Commit

Permalink
Update UniqueIdentifier interface to allow number ids
Browse files Browse the repository at this point in the history
  • Loading branch information
clauderic committed May 21, 2022
1 parent 131de61 commit da1337a
Show file tree
Hide file tree
Showing 34 changed files with 169 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .changeset/drop-animation-refactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ For even more advanced use-cases, consumers may also provide a function to the `
```ts
interface DropAnimationFunctionArguments {
active: {
id: string;
id: UniqueIdentifier;
data: DataRef;
node: HTMLElement;
rect: ClientRect;
Expand Down
28 changes: 28 additions & 0 deletions .changeset/number-unique-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
'@dnd-kit/core': major
'@dnd-kit/sortable': major
---

The `UniqueIdentifier` type has been updated to now accept either `string` or `number` identifiers. As a result, the `id` property of `useDraggable`, `useDroppable` and `useSortable` and the `items` prop of `<SortableContext>` now all accept either `string` or `number` identifiers.

#### Migration steps

For consumers that are using TypeScript, import the `UniqueIdentifier` type to have strongly typed local state:

```diff
+ import type {UniqueIdentifier} from '@dnd-kit/core';

function MyComponent() {
- const [items, setItems] = useState(['A', 'B', 'C']);
+ const [items, setItems] = useState<UniqueIdentifier>(['A', 'B', 'C']);
}
```

Alternatively, consumers can cast or convert the `id` property to a `string` when reading the `id` property of interfaces such as `Active`, `Over`, `DroppableContainer` and `DraggableNode`.

The `draggableNodes` object has also been converted to a map. Consumers that were reading from the `draggableNodes` property that is available on the public context of `<DndContext>` should follow these migration steps:

```diff
- draggableNodes[someId];
+ draggableNodes.get(someId);
```
3 changes: 1 addition & 2 deletions packages/core/src/components/Accessibility/Accessibility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {createPortal} from 'react-dom';
import {useUniqueId} from '@dnd-kit/utilities';
import {HiddenText, LiveRegion, useAnnouncement} from '@dnd-kit/accessibility';

import type {UniqueIdentifier} from '../../types';
import {DndMonitorListener, useDndMonitor} from '../DndMonitor';

import type {Announcements, ScreenReaderInstructions} from './types';
Expand All @@ -16,7 +15,7 @@ interface Props {
announcements?: Announcements;
container?: Element;
screenReaderInstructions?: ScreenReaderInstructions;
hiddenTextDescribedById: UniqueIdentifier;
hiddenTextDescribedById: string;
}

export function Accessibility({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function RestoreFocus({disabled}: Props) {
return;
}

const draggableNode = draggableNodes[previousActiveId];
const draggableNode = draggableNodes.get(previousActiveId);

if (!draggableNode) {
return;
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/components/DndContext/DndContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const DndContext = memo(function DndContext({
draggable: {active: activeId, nodes: draggableNodes, translate},
droppable: {containers: droppableContainers},
} = state;
const node = activeId ? draggableNodes[activeId] : null;
const node = activeId ? draggableNodes.get(activeId) : null;
const activeRects = useRef<Active['rect']['current']>({
initial: null,
translated: null,
Expand Down Expand Up @@ -202,7 +202,7 @@ export const DndContext = memo(function DndContext({
);

useLayoutShiftScrollCompensation({
activeNode: activeId ? draggableNodes[activeId] : null,
activeNode: activeId ? draggableNodes.get(activeId) : null,
config: autoScrollOptions.layoutShiftCompensation,
initialRect: initialActiveNodeRect,
measure: measuringConfiguration.draggable.measure,
Expand Down Expand Up @@ -329,11 +329,11 @@ export const DndContext = memo(function DndContext({
event: React.SyntheticEvent,
{sensor: Sensor, options}: SensorDescriptor<any>
) => {
if (!activeRef.current) {
if (activeRef.current == null) {
return;
}

const activeNode = draggableNodes[activeRef.current];
const activeNode = draggableNodes.get(activeRef.current);

if (!activeNode) {
return;
Expand All @@ -352,11 +352,11 @@ export const DndContext = memo(function DndContext({
onStart(initialCoordinates) {
const id = activeRef.current;

if (!id) {
if (id == null) {
return;
}

const draggableNode = draggableNodes[id];
const draggableNode = draggableNodes.get(id);

if (!draggableNode) {
return;
Expand Down Expand Up @@ -456,7 +456,7 @@ export const DndContext = memo(function DndContext({
): SyntheticListener['handler'] => {
return (event, active) => {
const nativeEvent = event.nativeEvent as DndEvent;
const activeDraggableNode = draggableNodes[active];
const activeDraggableNode = draggableNodes.get(active);

if (
// Another sensor is already instantiating
Expand Down Expand Up @@ -546,7 +546,7 @@ export const DndContext = memo(function DndContext({

if (
!active ||
!activeRef.current ||
activeRef.current == null ||
!activatorEvent ||
!scrollAdjustedTranslate
) {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/components/DragOverlay/DragOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export const DragOverlay = React.memo(
<AnimationManager animation={dropAnimation}>
{active && key ? (
<PositionedOverlay
key={`${key}-${active.id}`}
key={key}
id={active.id}
ref={ref}
as={wrapperElement}
activatorEvent={activatorEvent}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, {cloneElement, useState} from 'react';
import {useIsomorphicLayoutEffect, usePrevious} from '@dnd-kit/utilities';

import type {UniqueIdentifier} from '../../../../types';

export type Animation = (
key: string,
key: UniqueIdentifier,
node: HTMLElement
) => Promise<void> | void;

Expand All @@ -29,15 +31,13 @@ export function AnimationManager({animation, children}: Props) {
}

const key = clonedChildren?.key;
const id = clonedChildren?.props.id;

if (typeof key !== 'string') {
if (key == null || id == null) {
setClonedChildren(null);
return;
}

const [prefix] = key.split('-', 1);
const id = key.substring(prefix.length + 1);

Promise.resolve(animation(id, element)).then(() => {
setClonedChildren(null);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {CSS, isKeyboardEvent} from '@dnd-kit/utilities';
import type {Transform} from '@dnd-kit/utilities';

import {getRelativeTransformOrigin} from '../../../../utilities';
import type {ClientRect} from '../../../../types';
import type {ClientRect, UniqueIdentifier} from '../../../../types';

type TransitionGetter = (
activatorEvent: Event | null
Expand All @@ -16,6 +16,7 @@ export interface Props {
adjustScale?: boolean;
children?: React.ReactNode;
className?: string;
id: UniqueIdentifier;
rect: ClientRect | null;
style?: React.CSSProperties;
transition?: string | TransitionGetter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
DraggableNodes,
DroppableContainers,
} from '../../../store';
import type {ClientRect} from '../../../types';
import type {ClientRect, UniqueIdentifier} from '../../../types';
import {getMeasurableNode} from '../../../utilities/nodes';
import {scrollIntoViewIfNeeded} from '../../../utilities/scroll';
import {parseTransform} from '../../../utilities/transform';
Expand All @@ -16,7 +16,7 @@ import type {Animation} from '../components';

interface SharedParameters {
active: {
id: string;
id: UniqueIdentifier;
data: Active['data'];
node: HTMLElement;
rect: ClientRect;
Expand Down Expand Up @@ -171,7 +171,7 @@ export function useDropAnimation({
return;
}

const activeDraggable: DraggableNode | undefined = draggableNodes[id];
const activeDraggable: DraggableNode | undefined = draggableNodes.get(id);

if (!activeDraggable) {
return;
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/components/DragOverlay/hooks/useKey.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {useMemo} from 'react';

import type {UniqueIdentifier} from '../../../types';

let key = 0;

export function useKey(id: string | undefined) {
export function useKey(id: UniqueIdentifier | undefined) {
return useMemo(() => {
if (id == null) {
return;
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/hooks/useDraggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
} from '@dnd-kit/utilities';

import {InternalContext, Data} from '../store';
import type {UniqueIdentifier} from '../types';
import {ActiveDraggableContext} from '../components/DndContext';
import {useSyntheticListeners, SyntheticListenerMap} from './utilities';

export interface UseDraggableArguments {
id: string;
id: UniqueIdentifier;
data?: Data;
disabled?: boolean;
attributes?: {
Expand Down Expand Up @@ -68,13 +69,13 @@ export function useDraggable({

useIsomorphicLayoutEffect(
() => {
draggableNodes[id] = {id, key, node, activatorNode, data: dataRef};
draggableNodes.set(id, {id, key, node, activatorNode, data: dataRef});

return () => {
const node = draggableNodes[id];
const node = draggableNodes.get(id);

if (node && node.key === key) {
delete draggableNodes[id];
draggableNodes.delete(id);
}
};
},
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/useDroppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function useDroppable({

callbackId.current = setTimeout(() => {
measureDroppableContainers(
typeof ids.current === 'string' ? [ids.current] : ids.current
Array.isArray(ids.current) ? ids.current : [ids.current]
);
callbackId.current = null;
}, resizeObserverTimeout);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/utilities/useCachedNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function useCachedNode(
draggableNodes: DraggableNodes,
id: UniqueIdentifier | null
): DraggableNode['node']['current'] {
const draggableNode = id !== null ? draggableNodes[id] : undefined;
const draggableNode = id !== null ? draggableNodes.get(id) : undefined;
const node = draggableNode ? draggableNode.node.current : null;

return useLazyMemo(
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/hooks/utilities/useSyntheticListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type SyntheticListenerMap = Record<string, Function>;

export function useSyntheticListeners(
listeners: SyntheticListeners,
id: string
id: UniqueIdentifier
): SyntheticListenerMap {
return useMemo(() => {
return listeners.reduce<SyntheticListenerMap>(
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/store/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const defaultPublicContext: PublicContextDescriptor = {
activeNodeRect: null,
collisions: null,
containerNodeRect: null,
draggableNodes: {},
draggableNodes: new Map(),
droppableRects: new Map(),
droppableContainers: new DroppableContainersMap(),
over: null,
Expand Down Expand Up @@ -40,7 +40,7 @@ export const defaultInternalContext: InternalContextDescriptor = {
draggable: '',
},
dispatch: noop,
draggableNodes: {},
draggableNodes: new Map(),
over: null,
measureDroppableContainers: noop,
};
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/store/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function getInitialState(): State {
draggable: {
active: null,
initialCoordinates: {x: 0, y: 0},
nodes: {},
nodes: new Map(),
translate: {x: 0, y: 0},
},
droppable: {
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,7 @@ export type DraggableNode = {
data: DataRef;
};

export type DraggableNodes = Record<
UniqueIdentifier,
DraggableNode | undefined
>;
export type DraggableNodes = Map<UniqueIdentifier, DraggableNode | undefined>;

export type DroppableContainers = DroppableContainersMap;

Expand Down Expand Up @@ -107,7 +104,7 @@ export interface InternalContextDescriptor {
active: Active | null;
activeNodeRect: ClientRect | null;
ariaDescribedById: {
draggable: UniqueIdentifier;
draggable: string;
};
dispatch: React.Dispatch<Actions>;
draggableNodes: DraggableNodes;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types/other.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export type UniqueIdentifier = string;
export type UniqueIdentifier = string | number;
4 changes: 2 additions & 2 deletions packages/sortable/src/components/SortableContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export function SortableContext({
} = useDndContext();
const containerId = useUniqueId(ID_PREFIX, id);
const useDragOverlay = Boolean(dragOverlay.rect !== null);
const items = useMemo(
const items = useMemo<UniqueIdentifier[]>(
() =>
userDefinedItems.map((item) =>
typeof item === 'string' ? item : item.id
typeof item === 'object' && 'id' in item ? item.id : item
),
[userDefinedItems]
);
Expand Down
17 changes: 10 additions & 7 deletions stories/2 - Presets/Sortable/1-Vertical.story.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {MeasuringStrategy} from '@dnd-kit/core';
import {MeasuringStrategy, UniqueIdentifier} from '@dnd-kit/core';
import {restrictToWindowEdges} from '@dnd-kit/modifiers';
import {
AnimateLayoutChanges,
Expand Down Expand Up @@ -102,12 +102,15 @@ export const VariableHeights = () => {
);
};

export const DisabledItems = () => (
<Sortable
{...props}
isDisabled={(value) => ['1', '5', '8', '13', '20'].includes(value)}
/>
);
export const DisabledItems = () => {
const disabledItems: UniqueIdentifier[] = ['1', '5', '8', '13', '20'];
return (
<Sortable
{...props}
isDisabled={(value) => disabledItems.includes(value)}
/>
);
};

export const MarginBetweenItems = () => {
const getMargin = (index: number) => {
Expand Down
4 changes: 2 additions & 2 deletions stories/2 - Presets/Sortable/4-MultipleContainers.story.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import type {CancelDrop} from '@dnd-kit/core';
import type {CancelDrop, UniqueIdentifier} from '@dnd-kit/core';
import {rectSortingStrategy} from '@dnd-kit/sortable';

import {MultipleContainers, TRASH_ID} from './MultipleContainers';
Expand Down Expand Up @@ -27,7 +27,7 @@ export const ManyItems = () => (
export const Vertical = () => <MultipleContainers itemCount={5} vertical />;

export const TrashableItems = ({confirmDrop}: {confirmDrop: boolean}) => {
const [activeId, setActiveId] = React.useState<string | null>(null);
const [activeId, setActiveId] = React.useState<UniqueIdentifier | null>(null);
const resolveRef = React.useRef<(value: boolean) => void>();

const cancelDrop: CancelDrop = async ({active, over}) => {
Expand Down
Loading

0 comments on commit da1337a

Please sign in to comment.