Skip to content

Commit 6bb673c

Browse files
authored
Merge pull request #441 from zachahn/classnames-array
Allow specifying an array of classNames
2 parents 81c697c + 31ee6ee commit 6bb673c

File tree

7 files changed

+130
-51
lines changed

7 files changed

+130
-51
lines changed

index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ declare module '@shopify/draggable' {
176176
delay?: number | DelayOptions;
177177
plugins?: Array<typeof AbstractPlugin>;
178178
sensors?: Sensor[];
179-
classes?: { [key in DraggableClassNames]: string };
179+
classes?: { [key in DraggableClassNames]: string | string[] };
180180
announcements?: AnnouncementOptions;
181181
collidables?: Collidables;
182182
mirror?: MirrorOptions;
@@ -204,6 +204,7 @@ declare module '@shopify/draggable' {
204204
addContainer(...containers: HTMLElement[]): this;
205205
removeContainer(...containers: HTMLElement[]): this;
206206
getClassNameFor(name: DraggableClassNames): string;
207+
getClassNamesFor(name: DraggableClassNames): string[];
207208
isDragging(): boolean;
208209
getDraggableElementsForContainer(container: HTMLElement): HTMLElement[];
209210
}

src/Draggable/Draggable.js

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,23 @@ export default class Draggable {
329329
* @return {String|null}
330330
*/
331331
getClassNameFor(name) {
332-
return this.options.classes[name];
332+
return this.getClassNamesFor(name)[0];
333+
}
334+
335+
/**
336+
* Returns class names for class identifier
337+
* @return {String[]}
338+
*/
339+
getClassNamesFor(name) {
340+
const classNames = this.options.classes[name];
341+
342+
if (classNames instanceof Array) {
343+
return classNames;
344+
} else if (typeof classNames === 'string' || classNames instanceof String) {
345+
return [classNames];
346+
} else {
347+
return [];
348+
}
333349
}
334350

335351
/**
@@ -393,8 +409,8 @@ export default class Draggable {
393409

394410
if (this.lastPlacedSource && this.lastPlacedContainer) {
395411
clearTimeout(this.placedTimeoutID);
396-
this.lastPlacedSource.classList.remove(this.getClassNameFor('source:placed'));
397-
this.lastPlacedContainer.classList.remove(this.getClassNameFor('container:placed'));
412+
this.lastPlacedSource.classList.remove(...this.getClassNamesFor('source:placed'));
413+
this.lastPlacedContainer.classList.remove(...this.getClassNamesFor('container:placed'));
398414
}
399415

400416
this.source = this.originalSource.cloneNode(true);
@@ -418,10 +434,10 @@ export default class Draggable {
418434
return;
419435
}
420436

421-
this.originalSource.classList.add(this.getClassNameFor('source:original'));
422-
this.source.classList.add(this.getClassNameFor('source:dragging'));
423-
this.sourceContainer.classList.add(this.getClassNameFor('container:dragging'));
424-
document.body.classList.add(this.getClassNameFor('body:dragging'));
437+
this.originalSource.classList.add(...this.getClassNamesFor('source:original'));
438+
this.source.classList.add(...this.getClassNamesFor('source:dragging'));
439+
this.sourceContainer.classList.add(...this.getClassNamesFor('container:dragging'));
440+
document.body.classList.add(...this.getClassNamesFor('body:dragging'));
425441
applyUserSelect(document.body, 'none');
426442

427443
requestAnimationFrame(() => {
@@ -479,7 +495,7 @@ export default class Draggable {
479495
over: this.currentOver,
480496
});
481497

482-
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
498+
this.currentOver.classList.remove(...this.getClassNamesFor('draggable:over'));
483499
this.currentOver = null;
484500

485501
this.trigger(dragOutEvent);
@@ -494,14 +510,14 @@ export default class Draggable {
494510
overContainer: this.currentOverContainer,
495511
});
496512

497-
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
513+
this.currentOverContainer.classList.remove(...this.getClassNamesFor('container:over'));
498514
this.currentOverContainer = null;
499515

500516
this.trigger(dragOutContainerEvent);
501517
}
502518

503519
if (isOverContainer) {
504-
overContainer.classList.add(this.getClassNameFor('container:over'));
520+
overContainer.classList.add(...this.getClassNamesFor('container:over'));
505521

506522
const dragOverContainerEvent = new DragOverContainerEvent({
507523
source: this.source,
@@ -517,7 +533,7 @@ export default class Draggable {
517533
}
518534

519535
if (isOverDraggable) {
520-
target.classList.add(this.getClassNameFor('draggable:over'));
536+
target.classList.add(...this.getClassNamesFor('draggable:over'));
521537

522538
const dragOverEvent = new DragOverEvent({
523539
source: this.source,
@@ -559,32 +575,32 @@ export default class Draggable {
559575
this.source.parentNode.removeChild(this.source);
560576
this.originalSource.style.display = '';
561577

562-
this.source.classList.remove(this.getClassNameFor('source:dragging'));
563-
this.originalSource.classList.remove(this.getClassNameFor('source:original'));
564-
this.originalSource.classList.add(this.getClassNameFor('source:placed'));
565-
this.sourceContainer.classList.add(this.getClassNameFor('container:placed'));
566-
this.sourceContainer.classList.remove(this.getClassNameFor('container:dragging'));
567-
document.body.classList.remove(this.getClassNameFor('body:dragging'));
578+
this.source.classList.remove(...this.getClassNamesFor('source:dragging'));
579+
this.originalSource.classList.remove(...this.getClassNamesFor('source:original'));
580+
this.originalSource.classList.add(...this.getClassNamesFor('source:placed'));
581+
this.sourceContainer.classList.add(...this.getClassNamesFor('container:placed'));
582+
this.sourceContainer.classList.remove(...this.getClassNamesFor('container:dragging'));
583+
document.body.classList.remove(...this.getClassNamesFor('body:dragging'));
568584
applyUserSelect(document.body, '');
569585

570586
if (this.currentOver) {
571-
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
587+
this.currentOver.classList.remove(...this.getClassNamesFor('draggable:over'));
572588
}
573589

574590
if (this.currentOverContainer) {
575-
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
591+
this.currentOverContainer.classList.remove(...this.getClassNamesFor('container:over'));
576592
}
577593

578594
this.lastPlacedSource = this.originalSource;
579595
this.lastPlacedContainer = this.sourceContainer;
580596

581597
this.placedTimeoutID = setTimeout(() => {
582598
if (this.lastPlacedSource) {
583-
this.lastPlacedSource.classList.remove(this.getClassNameFor('source:placed'));
599+
this.lastPlacedSource.classList.remove(...this.getClassNamesFor('source:placed'));
584600
}
585601

586602
if (this.lastPlacedContainer) {
587-
this.lastPlacedContainer.classList.remove(this.getClassNameFor('container:placed'));
603+
this.lastPlacedContainer.classList.remove(...this.getClassNamesFor('container:placed'));
588604
}
589605

590606
this.lastPlacedSource = null;

src/Draggable/Plugins/Mirror/Mirror.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export default class Mirror extends AbstractPlugin {
275275
* @private
276276
*/
277277
[onMirrorCreated]({mirror, source, sensorEvent}) {
278-
const mirrorClass = this.draggable.getClassNameFor('mirror');
278+
const mirrorClasses = this.draggable.getClassNamesFor('mirror');
279279

280280
const setState = ({mirrorOffset, initialX, initialY, ...args}) => {
281281
this.mirrorOffset = mirrorOffset;
@@ -292,7 +292,7 @@ export default class Mirror extends AbstractPlugin {
292292
mirror,
293293
source,
294294
sensorEvent,
295-
mirrorClass,
295+
mirrorClasses,
296296
scrollOffset: this.scrollOffset,
297297
options: this.options,
298298
passedThreshX: true,
@@ -446,14 +446,14 @@ function resetMirror({mirror, source, options, ...args}) {
446446
* Applys mirror class on mirror element
447447
* @param {Object} state
448448
* @param {HTMLElement} state.mirror
449-
* @param {String} state.mirrorClass
449+
* @param {String[]} state.mirrorClasses
450450
* @return {Promise}
451451
* @private
452452
*/
453-
function addMirrorClasses({mirror, mirrorClass, ...args}) {
453+
function addMirrorClasses({mirror, mirrorClasses, ...args}) {
454454
return withPromise((resolve) => {
455-
mirror.classList.add(mirrorClass);
456-
resolve({mirror, mirrorClass, ...args});
455+
mirror.classList.add(...mirrorClasses);
456+
resolve({mirror, mirrorClasses, ...args});
457457
});
458458
}
459459

src/Draggable/README.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ Removes containers from this draggable instance.
7575
**`draggable.getClassNameFor(name: String): String`**
7676
Returns class name for class identifier, check the classes table below for identifiers.
7777

78+
**`draggable.getClassNamesFor(name: String): String[]`**
79+
Returns array of class name for class identifier, useful when working with atomic css, check the classes table below for identifiers.
80+
7881
**`draggable.isDragging(): Boolean`**
7982
Returns true or false, depending on this draggables dragging state.
8083

@@ -117,9 +120,11 @@ plugins controls the mirror movement. Default: `[]`
117120
Sensors dictate how drag operations get triggered, by listening to native browser events.
118121
By default draggable includes the `MouseSensor` & `TouchSensor`. Default: `[]`
119122

120-
**`classes {Object}`**
123+
**`classes {{String: String|String[]}}`**
121124
Draggable adds classes to elements to indicate state. These classes can be used to add styling
122-
on elements in certain states.
125+
on elements in certain states. Accept String or Array of strings.
126+
127+
**NOTE**: When specifying multiple classes to an indicate state, the first class MUST be unique for that state to avoid duplicate classes for other states. IE doesn't support add or remove multiple classes. If you want to use multiple classes in IE, you need to add a classList polyfill to your project first.
123128

124129
**`exclude {plugins: Plugin[], sensors: Sensor[]}`**
125130
Allow excluding default plugins and default sensors. Use with caution as it may create strange behavior.
@@ -156,15 +161,15 @@ Allow excluding default plugins and default sensors. Use with caution as it may
156161

157162
| Name | Description | Default |
158163
| -------------------- | ------------------------------------------------------------------- | ---------------------------------- |
159-
| `body:dragging` | Class added on the document body while dragging | `draggable--is-dragging` |
160-
| `container:dragging` | Class added on the container where the draggable was picked up from | `draggable-container--is-dragging` |
161-
| `source:dragging` | Class added on the draggable element that has been picked up | `draggable-source--is-dragging` |
162-
| `source:placed` | Class added on the draggable element on `drag:stop` | `draggable-source--placed` |
163-
| `container:placed` | Class added on the draggable container element on `drag:stop` | `draggable-container--placed` |
164-
| `draggable:over` | Class added on draggable element you are dragging over | `draggable--over` |
165-
| `container:over` | Class added on draggable container element you are dragging over | `draggable-container--over` |
166-
| `source:original` | Class added on the original source element, which is hidden on drag | `draggable--original` |
167-
| `mirror` | Class added on the mirror element | `draggable-mirror` |
164+
| `body:dragging` | Classes added on the document body while dragging | `draggable--is-dragging` |
165+
| `container:dragging` | Classes added on the container where the draggable was picked up from | `draggable-container--is-dragging` |
166+
| `source:dragging` | Classes added on the draggable element that has been picked up | `draggable-source--is-dragging` |
167+
| `source:placed` | Classes added on the draggable element on `drag:stop` | `draggable-source--placed` |
168+
| `container:placed` | Classes added on the draggable container element on `drag:stop` | `draggable-container--placed` |
169+
| `draggable:over` | Classes added on draggable element you are dragging over | `draggable--over` |
170+
| `container:over` | Classes added on draggable container element you are dragging over | `draggable-container--over` |
171+
| `source:original` | Classes added on the original source element, which is hidden on drag | `draggable--original` |
172+
| `mirror` | Classes added on the mirror element | `draggable-mirror` |
168173

169174
### Example
170175

@@ -195,3 +200,16 @@ const draggable = new Draggable(document.querySelectorAll('ul'), {
195200
}
196201
});
197202
```
203+
204+
Create draggable with specific classes:
205+
206+
```js
207+
import { Draggable } from '@shopify/draggable';
208+
209+
const draggable = new Draggable(document.querySelectorAll('ul'), {
210+
draggable: 'li',
211+
classes: {
212+
'draggable:over': ['draggable--over', 'bg-red-200', 'bg-opacity-25'],
213+
},
214+
});
215+
```

src/Draggable/tests/Draggable.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,50 @@ describe('Draggable', () => {
869869
expect(draggableElement.classList.contains(newInstance.getClassNameFor('source:original'))).toBeFalsy();
870870
});
871871

872+
it('should have multiple classes for `source:original` on start', () => {
873+
const sourceOriginalClasses = ['draggable--original', 'class1', 'class2'];
874+
const newInstance = new Draggable(containers, {
875+
draggable: 'li',
876+
classes: {
877+
'source:original': sourceOriginalClasses,
878+
},
879+
});
880+
const draggableElement = sandbox.querySelector('li');
881+
document.elementFromPoint = () => draggableElement;
882+
883+
triggerEvent(draggableElement, 'mousedown', {button: 0});
884+
885+
// Wait for delay
886+
waitForDragDelay();
887+
888+
expect(newInstance.getClassNamesFor('source:original')).toEqual(sourceOriginalClasses);
889+
890+
triggerEvent(document.body, 'mouseup', {button: 0});
891+
});
892+
893+
it('should removes all draggable classes for `source:original` on stop', () => {
894+
const sourceOriginalClasses = ['draggable--original', 'class1', 'class2'];
895+
const newInstance = new Draggable(containers, {
896+
draggable: 'li',
897+
classes: {
898+
'source:original': sourceOriginalClasses,
899+
},
900+
});
901+
const draggableElement = sandbox.querySelector('li');
902+
document.elementFromPoint = () => draggableElement;
903+
904+
triggerEvent(draggableElement, 'mousedown', {button: 0});
905+
906+
// Wait for delay
907+
waitForDragDelay();
908+
909+
triggerEvent(document.body, 'mouseup', {button: 0});
910+
911+
newInstance.getClassNamesFor('source:original').forEach((className) => {
912+
expect(draggableElement.classList).not.toContain(className);
913+
});
914+
});
915+
872916
it('`drag:out:container` event specifies leaving container', () => {
873917
const newInstance = new Draggable(containers, {
874918
draggable: 'li',

src/Droppable/Droppable.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export default class Droppable extends Draggable {
160160
continue;
161161
}
162162

163-
dropzoneElement.classList.add(this.getClassNameFor('droppable:active'));
163+
dropzoneElement.classList.add(...this.getClassNamesFor('droppable:active'));
164164
}
165165
}
166166

@@ -198,14 +198,14 @@ export default class Droppable extends Draggable {
198198

199199
this.trigger(droppableStopEvent);
200200

201-
const occupiedClass = this.getClassNameFor('droppable:occupied');
201+
const occupiedClasses = this.getClassNamesFor('droppable:occupied');
202202

203203
for (const dropzone of this.dropzones) {
204-
dropzone.classList.remove(this.getClassNameFor('droppable:active'));
204+
dropzone.classList.remove(...this.getClassNamesFor('droppable:active'));
205205
}
206206

207207
if (this.lastDropzone && this.lastDropzone !== this.initialDropzone) {
208-
this.initialDropzone.classList.remove(occupiedClass);
208+
this.initialDropzone.classList.remove(...occupiedClasses);
209209
}
210210

211211
this.dropzones = null;
@@ -231,14 +231,14 @@ export default class Droppable extends Draggable {
231231
return false;
232232
}
233233

234-
const occupiedClass = this.getClassNameFor('droppable:occupied');
234+
const occupiedClasses = this.getClassNamesFor('droppable:occupied');
235235

236236
if (this.lastDropzone) {
237-
this.lastDropzone.classList.remove(occupiedClass);
237+
this.lastDropzone.classList.remove(...occupiedClasses);
238238
}
239239

240240
dropzone.appendChild(event.source);
241-
dropzone.classList.add(occupiedClass);
241+
dropzone.classList.add(...occupiedClasses);
242242

243243
return true;
244244
}
@@ -261,7 +261,7 @@ export default class Droppable extends Draggable {
261261
}
262262

263263
this.initialDropzone.appendChild(event.source);
264-
this.lastDropzone.classList.remove(this.getClassNameFor('droppable:occupied'));
264+
this.lastDropzone.classList.remove(...this.getClassNamesFor('droppable:occupied'));
265265
}
266266

267267
/**

src/Plugins/Snappable/Snappable.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,12 @@ export default class Snappable extends AbstractPlugin {
127127
this.mirror.style.display = 'none';
128128
}
129129

130-
source.classList.remove(this.draggable.getClassNameFor('source:dragging'));
131-
source.classList.add(this.draggable.getClassNameFor('source:placed'));
130+
source.classList.remove(...this.draggable.getClassNamesFor('source:dragging'));
131+
source.classList.add(...this.draggable.getClassNamesFor('source:placed'));
132132

133133
// Need to cancel this in drag out
134134
setTimeout(() => {
135-
source.classList.remove(this.draggable.getClassNameFor('source:placed'));
135+
source.classList.remove(...this.draggable.getClassNamesFor('source:placed'));
136136
}, this.draggable.options.placedTimeout);
137137
}
138138

@@ -163,7 +163,7 @@ export default class Snappable extends AbstractPlugin {
163163
this.mirror.style.display = '';
164164
}
165165

166-
source.classList.add(this.draggable.getClassNameFor('source:dragging'));
166+
source.classList.add(...this.draggable.getClassNamesFor('source:dragging'));
167167
}
168168

169169
/**

0 commit comments

Comments
 (0)