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

sensors.mddocs/

0

# Sensors

1

2

Sensors detect and handle different types of input events (mouse, touch, keyboard, native drag) and convert them into unified sensor events that the draggable system can process. They provide a pluggable architecture for supporting various input methods.

3

4

## Capabilities

5

6

### Base Sensor Class

7

8

All sensors extend the base Sensor class which provides common functionality.

9

10

```typescript { .api }

11

/**

12

* Base sensor class for input detection and handling

13

*/

14

class Sensor {

15

constructor(containers: HTMLElement | HTMLElement[] | NodeList, options?: SensorOptions);

16

attach(): this;

17

detach(): this;

18

addContainer(...containers: HTMLElement[]): void;

19

removeContainer(...containers: HTMLElement[]): void;

20

trigger(element: HTMLElement, sensorEvent: SensorEvent): SensorEvent;

21

}

22

23

interface SensorOptions {

24

delay?: number | DelayOptions;

25

}

26

```

27

28

**Usage Example:**

29

30

```typescript

31

import { MouseSensor, TouchSensor } from "@shopify/draggable";

32

33

// Create custom sensor configuration

34

const containers = document.querySelectorAll('.container');

35

const mouseSensor = new MouseSensor(containers, {

36

delay: { mouse: 100 }

37

});

38

39

const touchSensor = new TouchSensor(containers, {

40

delay: { touch: 200 }

41

});

42

43

// Use with draggable

44

const draggable = new Draggable(containers, {

45

sensors: [mouseSensor, touchSensor]

46

});

47

```

48

49

### Built-in Sensors

50

51

Draggable includes several built-in sensor implementations.

52

53

```typescript { .api }

54

// Available through Draggable.Sensors static property

55

interface Sensors {

56

DragSensor: typeof DragSensor;

57

MouseSensor: typeof MouseSensor;

58

TouchSensor: typeof TouchSensor;

59

ForceTouchSensor: typeof ForceTouchSensor;

60

}

61

62

class MouseSensor extends Sensor {}

63

class TouchSensor extends Sensor {}

64

class DragSensor extends Sensor {}

65

class ForceTouchSensor extends Sensor {}

66

```

67

68

**Built-in Sensors:**

69

70

- **MouseSensor**: Handles mouse click and drag events

71

- **TouchSensor**: Handles touch events for mobile devices

72

- **DragSensor**: Handles native HTML5 drag and drop events

73

- **ForceTouchSensor**: Handles force touch/pressure-sensitive input

74

75

**Usage Example:**

76

77

```typescript

78

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

79

80

// Access built-in sensors

81

const { MouseSensor, TouchSensor, ForceTouchSensor } = Draggable.Sensors;

82

83

const draggable = new Draggable(containers, {

84

sensors: [MouseSensor, TouchSensor, ForceTouchSensor],

85

exclude: {

86

sensors: [TouchSensor] // Exclude touch on desktop

87

}

88

});

89

```

90

91

### Sensor Events

92

93

Sensors generate unified sensor events that contain input information.

94

95

```typescript { .api }

96

class SensorEvent extends AbstractEvent {

97

readonly originalEvent: Event;

98

readonly clientX: number;

99

readonly clientY: number;

100

readonly target: HTMLElement;

101

readonly container: HTMLElement;

102

readonly pressure: number;

103

}

104

105

class DragStartSensorEvent extends SensorEvent {}

106

class DragMoveSensorEvent extends SensorEvent {}

107

class DragStopSensorEvent extends SensorEvent {}

108

class DragPressureSensorEvent extends SensorEvent {}

109

```

110

111

**Usage Example:**

112

113

```typescript

114

draggable.on('drag:move', (event) => {

115

const sensorEvent = event.sensorEvent;

116

console.log('Mouse/touch position:', sensorEvent.clientX, sensorEvent.clientY);

117

console.log('Pressure:', sensorEvent.pressure);

118

console.log('Original browser event:', sensorEvent.originalEvent);

119

});

120

```

121

122

### Sensor Management

123

124

Methods for managing sensors on draggable instances.

125

126

```typescript { .api }

127

/**

128

* Add sensors to a draggable instance

129

* @param sensors - Sensor classes to add

130

*/

131

addSensor(...sensors: (typeof Sensor)[]): this;

132

133

/**

134

* Remove sensors from a draggable instance

135

* @param sensors - Sensor classes to remove

136

*/

137

removeSensor(...sensors: (typeof Sensor)[]): this;

138

```

139

140

**Usage Example:**

141

142

```typescript

143

const draggable = new Draggable(containers);

144

145

// Add additional sensors

146

draggable.addSensor(Draggable.Sensors.ForceTouchSensor);

147

148

// Remove default sensors

149

draggable.removeSensor(Draggable.Sensors.TouchSensor);

150

151

// Add custom sensor

152

class KeyboardSensor extends Sensor {

153

attach() {

154

// Custom keyboard handling

155

document.addEventListener('keydown', this.onKeyDown);

156

return this;

157

}

158

159

detach() {

160

document.removeEventListener('keydown', this.onKeyDown);

161

return this;

162

}

163

164

onKeyDown = (event) => {

165

// Custom keyboard drag logic

166

}

167

}

168

169

draggable.addSensor(KeyboardSensor);

170

```

171

172

### Container Management

173

174

Sensors can dynamically manage which containers they monitor.

175

176

```typescript { .api }

177

/**

178

* Add containers to sensor monitoring

179

* @param containers - Container elements to add

180

*/

181

addContainer(...containers: HTMLElement[]): void;

182

183

/**

184

* Remove containers from sensor monitoring

185

* @param containers - Container elements to remove

186

*/

187

removeContainer(...containers: HTMLElement[]): void;

188

```

189

190

**Usage Example:**

191

192

```typescript

193

const mouseSensor = new MouseSensor([container1, container2]);

194

195

// Add more containers later

196

mouseSensor.addContainer(container3, container4);

197

198

// Remove a container

199

mouseSensor.removeContainer(container1);

200

```

201

202

## Custom Sensor Implementation

203

204

```typescript

205

class CustomSensor extends Sensor {

206

constructor(containers, options = {}) {

207

super(containers, options);

208

this.handleStart = this.handleStart.bind(this);

209

this.handleMove = this.handleMove.bind(this);

210

this.handleEnd = this.handleEnd.bind(this);

211

}

212

213

attach() {

214

// Add event listeners to containers

215

this.containers.forEach(container => {

216

container.addEventListener('customstart', this.handleStart);

217

container.addEventListener('custommove', this.handleMove);

218

container.addEventListener('customend', this.handleEnd);

219

});

220

return this;

221

}

222

223

detach() {

224

// Remove event listeners

225

this.containers.forEach(container => {

226

container.removeEventListener('customstart', this.handleStart);

227

container.removeEventListener('custommove', this.handleMove);

228

container.removeEventListener('customend', this.handleEnd);

229

});

230

return this;

231

}

232

233

handleStart(event) {

234

const sensorEvent = new DragStartSensorEvent({

235

originalEvent: event,

236

clientX: event.clientX || 0,

237

clientY: event.clientY || 0,

238

target: event.target,

239

container: event.currentTarget,

240

pressure: 0

241

});

242

243

this.trigger(event.target, sensorEvent);

244

}

245

246

handleMove(event) {

247

const sensorEvent = new DragMoveSensorEvent({

248

originalEvent: event,

249

clientX: event.clientX || 0,

250

clientY: event.clientY || 0,

251

target: event.target,

252

container: event.currentTarget,

253

pressure: 0

254

});

255

256

this.trigger(event.target, sensorEvent);

257

}

258

259

handleEnd(event) {

260

const sensorEvent = new DragStopSensorEvent({

261

originalEvent: event,

262

clientX: event.clientX || 0,

263

clientY: event.clientY || 0,

264

target: event.target,

265

container: event.currentTarget,

266

pressure: 0

267

});

268

269

this.trigger(event.target, sensorEvent);

270

}

271

}

272

273

// Use custom sensor

274

const draggable = new Draggable(containers, {

275

sensors: [CustomSensor]

276

});

277

```

278

279

## Sensor Configuration

280

281

```typescript

282

// Configure sensor delays

283

const draggable = new Draggable(containers, {

284

delay: {

285

mouse: 150, // 150ms delay for mouse

286

touch: 200, // 200ms delay for touch

287

drag: 0 // No delay for native drag

288

}

289

});

290

291

// Or configure per sensor

292

const mouseSensor = new MouseSensor(containers, {

293

delay: { mouse: 100 }

294

});

295

296

const touchSensor = new TouchSensor(containers, {

297

delay: { touch: 300 }

298

});

299

```

300

301

## Complete Sensor Example

302

303

```typescript

304

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

305

306

// Custom gesture sensor for advanced touch interactions

307

class GestureSensor extends Sensor {

308

constructor(containers, options) {

309

super(containers, options);

310

this.touches = new Map();

311

this.gestureThreshold = options.gestureThreshold || 50;

312

}

313

314

attach() {

315

this.containers.forEach(container => {

316

container.addEventListener('touchstart', this.onTouchStart, { passive: false });

317

container.addEventListener('touchmove', this.onTouchMove, { passive: false });

318

container.addEventListener('touchend', this.onTouchEnd);

319

});

320

return this;

321

}

322

323

detach() {

324

this.containers.forEach(container => {

325

container.removeEventListener('touchstart', this.onTouchStart);

326

container.removeEventListener('touchmove', this.onTouchMove);

327

container.removeEventListener('touchend', this.onTouchEnd);

328

});

329

return this;

330

}

331

332

onTouchStart = (event) => {

333

// Track multiple touches for gesture detection

334

Array.from(event.touches).forEach(touch => {

335

this.touches.set(touch.identifier, {

336

startX: touch.clientX,

337

startY: touch.clientY,

338

currentX: touch.clientX,

339

currentY: touch.clientY

340

});

341

});

342

343

if (event.touches.length === 1) {

344

// Single touch - start drag

345

const touch = event.touches[0];

346

const sensorEvent = new DragStartSensorEvent({

347

originalEvent: event,

348

clientX: touch.clientX,

349

clientY: touch.clientY,

350

target: event.target,

351

container: event.currentTarget,

352

pressure: touch.force || 0

353

});

354

355

this.trigger(event.target, sensorEvent);

356

}

357

};

358

359

onTouchMove = (event) => {

360

if (event.touches.length === 1) {

361

// Single touch - continue drag

362

const touch = event.touches[0];

363

const sensorEvent = new DragMoveSensorEvent({

364

originalEvent: event,

365

clientX: touch.clientX,

366

clientY: touch.clientY,

367

target: event.target,

368

container: event.currentTarget,

369

pressure: touch.force || 0

370

});

371

372

this.trigger(event.target, sensorEvent);

373

} else if (event.touches.length === 2) {

374

// Two touches - handle pinch/zoom gesture

375

this.handlePinchGesture(event);

376

}

377

};

378

379

onTouchEnd = (event) => {

380

// Clean up touch tracking

381

Array.from(event.changedTouches).forEach(touch => {

382

this.touches.delete(touch.identifier);

383

});

384

385

if (event.touches.length === 0) {

386

// No more touches - end drag

387

const touch = event.changedTouches[0];

388

const sensorEvent = new DragStopSensorEvent({

389

originalEvent: event,

390

clientX: touch.clientX,

391

clientY: touch.clientY,

392

target: event.target,

393

container: event.currentTarget,

394

pressure: 0

395

});

396

397

this.trigger(event.target, sensorEvent);

398

}

399

};

400

401

handlePinchGesture(event) {

402

// Custom pinch gesture logic

403

const [touch1, touch2] = event.touches;

404

const distance = Math.sqrt(

405

Math.pow(touch2.clientX - touch1.clientX, 2) +

406

Math.pow(touch2.clientY - touch1.clientY, 2)

407

);

408

409

// Emit custom gesture event

410

const gestureEvent = new CustomEvent('pinch', {

411

detail: { distance, touches: [touch1, touch2] }

412

});

413

event.target.dispatchEvent(gestureEvent);

414

}

415

}

416

417

// Use the custom sensor

418

const draggable = new Draggable(containers, {

419

sensors: [GestureSensor],

420

gestureThreshold: 75

421

});

422

```