Skip to content

Commit 2ccdd4f

Browse files
committed
select next item #9
1 parent 4724da0 commit 2ccdd4f

File tree

7 files changed

+168
-8
lines changed

7 files changed

+168
-8
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "macos-multi-select",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "Given a list of ids, and an action, return a list of selected items with the same behaviour of macOS finder list view selection.",
55
"main": "dist/index.js",
66
"repository": "[email protected]:codingedgar/macos-multi-select.git",

src/index.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { difference, head, take, union, without } from "ramda";
1+
import { difference, head, last, take, union, without } from "ramda";
22
import { findAdjacentToPivotInSortedArray, findNextPivot } from "./arrayUtils";
33

44
export type Context = {
@@ -12,6 +12,7 @@ type Command =
1212
| { type: "TOGGLE SELECTION", id: string }
1313
| { type: "DESELECT ALL" }
1414
| { type: "SELECT ADJACENT", id: string }
15+
| { type: "SELECT NEXT" }
1516

1617
function listIncludesAndIsNotEmpty(list: string[], item: string) {
1718
return list.length > 0 && list.includes(item)
@@ -108,6 +109,44 @@ export function multiselect(context: Context, command: Command): Context {
108109
...context,
109110
selected: union(without(toRemove, context.selected), nextSelection)
110111
}
112+
} else if(
113+
command.type === "SELECT NEXT" &&
114+
context.list.length &&
115+
context.adjacentPivot === undefined
116+
) {
117+
return {
118+
...context,
119+
selected: [context.list[0]],
120+
adjacentPivot: context.list[0],
121+
}
122+
} else if (
123+
command.type === "SELECT NEXT" &&
124+
context.list.length &&
125+
context.adjacentPivot !== undefined
126+
) {
127+
const pivotIndex = context.list.indexOf(context.adjacentPivot)
128+
129+
if (pivotIndex < context.list.length - 1) {
130+
const nextItem = context.list[pivotIndex + 1];
131+
return {
132+
...context,
133+
selected: [nextItem],
134+
adjacentPivot: nextItem
135+
}
136+
} else if (
137+
!context.selected.length ||
138+
!(
139+
context.selected.length === 1 &&
140+
last(context.selected) === last(context.list)
141+
)
142+
) {
143+
return {
144+
...context,
145+
selected: [context.list[pivotIndex]],
146+
}
147+
} else {
148+
return context;
149+
}
111150
} else {
112151
return context;
113152
}

src/spec/arbitraries.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import fc from 'fast-check';
2+
3+
export function subsequentSubarray(arr: string[]) {
4+
return fc.tuple(fc.nat(arr.length), fc.nat(arr.length))
5+
.map(([a, b]) => a < b ? [a, b] : [b, a])
6+
.map(([from, to]) => arr.slice(from, to));
7+
}
8+
9+
export function nonEmptySubsequentSubarray(nonEmptyArray: string[]) {
10+
return fc.tuple(fc.nat(nonEmptyArray.length), fc.nat(nonEmptyArray.length))
11+
.map(([a, b]) => a < b ? [a, b + 1] : [b, a + 1])
12+
.map(([from, to]) => nonEmptyArray.slice(from, to));
13+
}
14+
15+
export function list() {
16+
return fc.set(fc.string())
17+
}

src/spec/selectAdjacent.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ describe('Select Adjacent', () => {
114114
);
115115
});
116116

117-
test.only('should perform a minus between the old and new selection', () => {
117+
test('should perform a minus between the old and new selection', () => {
118118
fc.assert(
119119
fc.property(
120120
fc.tuple(

src/spec/selectNext.spec.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import fc from 'fast-check';
2+
import { last } from 'ramda';
3+
import { Context, multiselect } from '../index';
4+
import { nonEmptySubsequentSubarray } from './arbitraries';
5+
6+
describe('Select Next Item', () => {
7+
test('should do nothing if no items', () => {
8+
const initialContext: Context = {
9+
adjacentPivot: undefined,
10+
list: [],
11+
selected: []
12+
};
13+
14+
expect(multiselect(initialContext, {
15+
type: "SELECT NEXT",
16+
}))
17+
.toEqual(initialContext)
18+
19+
});
20+
21+
test('should start from the top', () => {
22+
23+
fc.assert(
24+
fc.property(
25+
fc.set(fc.string(), { minLength: 1 }),
26+
(list) => {
27+
expect(multiselect({
28+
adjacentPivot: undefined,
29+
list,
30+
selected: []
31+
}, {
32+
type: "SELECT NEXT",
33+
}))
34+
.toEqual({
35+
list,
36+
selected: [list[0]],
37+
adjacentPivot: list[0],
38+
})
39+
}
40+
)
41+
)
42+
43+
});
44+
45+
test('should never select beyond last item', () => {
46+
fc.assert(
47+
fc.property(
48+
fc.tuple(
49+
fc.integer(1, 20),
50+
fc.set(
51+
fc.string(), { minLength: 1 }
52+
)
53+
)
54+
.chain(([extra, list]) =>
55+
nonEmptySubsequentSubarray(list)
56+
.map(selected => ({
57+
list,
58+
selected,
59+
adjacentPivot: last(selected),
60+
extra
61+
}))
62+
)
63+
,
64+
({
65+
adjacentPivot,
66+
list,
67+
selected,
68+
extra
69+
}) => {
70+
71+
let prevContext: Context = {
72+
adjacentPivot,
73+
list,
74+
selected
75+
}
76+
77+
const startOn = list.indexOf(last(selected)!) + 1
78+
79+
for (let index = startOn; index < list.length + extra; index++) {
80+
81+
const nextContext = multiselect(prevContext, {
82+
type: "SELECT NEXT",
83+
});
84+
85+
const selected = [index < list.length - 1 ? list[index]: last(list)];
86+
87+
expect(nextContext)
88+
.toEqual({
89+
list,
90+
selected,
91+
adjacentPivot: last(selected),
92+
})
93+
94+
prevContext = nextContext;
95+
96+
}
97+
98+
}
99+
)
100+
)
101+
})
102+
103+
})

src/spec/selectOne.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ describe('Select One Item', () => {
88
fc.assert(
99
fc.property(
1010
fc.set(
11-
fc.string()
11+
fc.string(),
12+
{ minLength: 1 }
1213
)
13-
.filter(list => list.length > 0)
1414
.chain(list =>
1515
fc.record({
1616
list: fc.constant(list),
@@ -49,9 +49,9 @@ describe('Select One Item', () => {
4949
fc.assert(
5050
fc.property(
5151
fc.set(
52-
fc.string()
52+
fc.string(),
53+
{ minLength: 2 }
5354
)
54-
.filter(list => list.length > 1)
5555
.chain(list =>
5656
fc.record({
5757
list: fc.constant(list),

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"src"
8080
],
8181
"exclude": [
82-
"src/**/*.spec.ts"
82+
"src/**/*.spec.ts",
83+
"src/spec"
8384
]
8485
}

0 commit comments

Comments
 (0)