or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-draggable.mddroppable.mdevents.mdindex.mdplugins.mdsensors.mdsortable.mdswappable.md

sortable.mddocs/

0

# Sortable Lists

1

2

Sortable extends Draggable to provide reordering functionality for elements within or between containers. It automatically tracks position changes and provides events for handling sort operations.

3

4

## Capabilities

5

6

### Sortable Constructor

7

8

Creates a sortable instance with the same interface as Draggable but with additional sorting-specific functionality.

9

10

```typescript { .api }

11

/**

12

* Creates a new sortable instance for reordering elements

13

* @param containers - Elements that contain sortable items

14

* @param options - Configuration options (same as Draggable)

15

*/

16

class Sortable<T = SortableEventNames> extends Draggable<T> {

17

constructor(containers: DraggableContainer, options?: DraggableOptions);

18

}

19

```

20

21

**Usage Example:**

22

23

```typescript

24

import { Sortable } from "@shopify/draggable";

25

26

const sortable = new Sortable(document.querySelectorAll('.sortable-list'), {

27

draggable: '.sortable-item'

28

});

29

30

// Listen for sort events

31

sortable.on('sortable:sorted', (event) => {

32

console.log(`Moved from index ${event.oldIndex} to ${event.newIndex}`);

33

console.log('Old container:', event.oldContainer);

34

console.log('New container:', event.newContainer);

35

});

36

```

37

38

### Index Management

39

40

Methods for getting element positions within containers.

41

42

```typescript { .api }

43

/**

44

* Returns the current index of an element within its container during drag

45

* @param element - Element to get index for

46

* @returns Zero-based index of the element

47

*/

48

index(element: HTMLElement): number;

49

50

/**

51

* Returns sortable elements for a specific container, excluding mirror and original source

52

* @param container - Container to get elements from

53

* @returns Array of sortable elements

54

*/

55

getSortableElementsForContainer(container: HTMLElement): HTMLElement[];

56

```

57

58

**Usage Example:**

59

60

```typescript

61

sortable.on('sortable:sort', (event) => {

62

const currentIndex = sortable.index(event.dragEvent.source);

63

const allItems = sortable.getSortableElementsForContainer(event.dragEvent.sourceContainer);

64

console.log(`Item ${currentIndex + 1} of ${allItems.length}`);

65

});

66

```

67

68

### Sort Events

69

70

Sortable-specific events that fire during sort operations.

71

72

```typescript { .api }

73

type SortableEventNames =

74

| 'sortable:start'

75

| 'sortable:sort'

76

| 'sortable:sorted'

77

| 'sortable:stop'

78

| DraggableEventNames;

79

```

80

81

**Event Details:**

82

83

- **sortable:start**: Fired when a sortable drag operation begins

84

- **sortable:sort**: Fired continuously while sorting (before position changes)

85

- **sortable:sorted**: Fired after an element has been moved to a new position

86

- **sortable:stop**: Fired when the sort operation ends

87

88

**Event Handlers Example:**

89

90

```typescript

91

sortable.on('sortable:start', (event) => {

92

console.log('Sort started');

93

console.log('Start index:', event.startIndex);

94

console.log('Start container:', event.startContainer);

95

});

96

97

sortable.on('sortable:sort', (event) => {

98

// This fires before the move happens - you can cancel it

99

if (shouldPreventSort(event.dragEvent.over)) {

100

event.cancel();

101

}

102

});

103

104

sortable.on('sortable:sorted', (event) => {

105

// This fires after the element has been moved

106

updateDataModel(event.oldIndex, event.newIndex, event.oldContainer, event.newContainer);

107

});

108

109

sortable.on('sortable:stop', (event) => {

110

console.log('Final position - Old:', event.oldIndex, 'New:', event.newIndex);

111

saveChangesToServer();

112

});

113

```

114

115

## Event Types

116

117

```typescript { .api }

118

class SortableEvent extends AbstractEvent {

119

readonly dragEvent: DragEvent;

120

}

121

122

class SortableStartEvent extends SortableEvent {

123

readonly startIndex: number;

124

readonly startContainer: HTMLElement;

125

}

126

127

class SortableSortEvent extends SortableEvent {

128

readonly oldIndex: number;

129

readonly newIndex: number;

130

readonly oldContainer: HTMLElement;

131

readonly newContainer: HTMLElement;

132

}

133

134

class SortableSortedEvent extends SortableEvent {

135

readonly oldIndex: number;

136

readonly newIndex: number;

137

readonly oldContainer: HTMLElement;

138

readonly newContainer: HTMLElement;

139

}

140

141

class SortableStopEvent extends SortableEvent {

142

readonly oldIndex: number;

143

readonly newIndex: number;

144

readonly oldContainer: HTMLElement;

145

readonly newContainer: HTMLElement;

146

}

147

```

148

149

## Complete Example

150

151

```typescript

152

import { Sortable } from "@shopify/draggable";

153

154

// Create sortable for todo lists

155

const todoSortable = new Sortable(document.querySelectorAll('.todo-list'), {

156

draggable: '.todo-item',

157

handle: '.todo-handle',

158

classes: {

159

'source:dragging': 'todo-dragging',

160

'container:over': 'todo-drop-zone'

161

}

162

});

163

164

// Track changes for persistence

165

let pendingChanges = [];

166

167

todoSortable.on('sortable:sorted', (event) => {

168

const change = {

169

itemId: event.dragEvent.source.dataset.id,

170

oldIndex: event.oldIndex,

171

newIndex: event.newIndex,

172

oldListId: event.oldContainer.dataset.listId,

173

newListId: event.newContainer.dataset.listId

174

};

175

176

pendingChanges.push(change);

177

178

// Show visual feedback

179

event.dragEvent.source.classList.add('recently-moved');

180

setTimeout(() => {

181

event.dragEvent.source.classList.remove('recently-moved');

182

}, 1000);

183

});

184

185

todoSortable.on('sortable:stop', () => {

186

// Batch save all changes

187

if (pendingChanges.length > 0) {

188

saveTodoChanges(pendingChanges);

189

pendingChanges = [];

190

}

191

});

192

193

// Cleanup

194

function destroyTodoSortable() {

195

todoSortable.destroy();

196

}

197

```

198

199

## Multi-Container Sorting

200

201

Sortable automatically handles sorting between different containers:

202

203

```typescript

204

const kanbanSortable = new Sortable([

205

document.querySelector('.backlog'),

206

document.querySelector('.in-progress'),

207

document.querySelector('.done')

208

], {

209

draggable: '.kanban-card'

210

});

211

212

kanbanSortable.on('sortable:sorted', (event) => {

213

const card = event.dragEvent.source;

214

const newStatus = event.newContainer.dataset.status;

215

const oldStatus = event.oldContainer.dataset.status;

216

217

if (newStatus !== oldStatus) {

218

// Update card status when moved between columns

219

updateCardStatus(card.dataset.id, newStatus);

220

card.querySelector('.status').textContent = newStatus;

221

}

222

});

223

```