Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v7 #300

Merged
merged 17 commits into from
Jun 29, 2022
Merged

v7 #300

Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add slidesToScroll: 'auto' option value
  • Loading branch information
davidcetinkaya authored and davidjerleke committed Jun 1, 2022
commit 45fb28a1acca5fdb580be0117613f8de94e96480
4 changes: 2 additions & 2 deletions packages/embla-carousel-docs/src/content/pages/api/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ Choose content direction between `ltr` and `rtl`. Please note that when using `r

### slidesToScroll

Type: <BrandPrimaryText>`number`</BrandPrimaryText>
Type: <BrandPrimaryText>`string | number`</BrandPrimaryText>
Default: <BrandSecondaryText>`1`</BrandSecondaryText>

Group slides together. Drag interactions, dot navigation, and previous/next buttons are mapped to group slides into the given number.
Group slides together. Drag interactions, dot navigation, and previous/next buttons are mapped to group slides into the given number, which has to be an integer. Set it to `auto` if you want Embla to group slides automatically.

### draggable

Expand Down
40 changes: 40 additions & 0 deletions packages/embla-carousel/src/__tests__/SlidesToScroll.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SlidesToScroll } from '../components/SlidesToScroll'

const viewSize = 100
const slideSizesWithGaps = [80, 50, 50, 39, 85, 15, 10]
const slideSizesEmpty: [] = []

describe('SlidesToScroll', () => {
describe('Group by slide sizes', () => {
const slidesToScroll = SlidesToScroll(viewSize, slideSizesWithGaps, 'auto')

test('Groups items into groups where slide sizes <= view size', () => {
const groups = slidesToScroll.groupSlides(slideSizesWithGaps)
expect(groups).toEqual([[80], [50, 50], [39], [85, 15], [10]])
})

test('Returns an empty array if an empty list is provided', () => {
const groups = slidesToScroll.groupSlides(slideSizesEmpty)
expect(groups).toEqual(slideSizesEmpty)
})

test('Does not throw when a slide size > view size', () => {
const func = () => slidesToScroll.groupSlides([viewSize + 1])
expect(func).not.toThrow()
})
})

describe('Group by number', () => {
const slidesToScroll = SlidesToScroll(viewSize, slideSizesWithGaps, 2)

test('Groups items into groups that equal the size of the given number', () => {
const groups = slidesToScroll.groupSlides(slideSizesWithGaps)
expect(groups).toEqual([[80, 50], [50, 39], [85, 15], [10]])
})

test('Returns an empty array if an empty list is provided', () => {
const groups = slidesToScroll.groupSlides(slideSizesEmpty)
expect(groups).toEqual(slideSizesEmpty)
})
})
})
4 changes: 3 additions & 1 deletion packages/embla-carousel/src/components/Alignment.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isNumber } from './utils'

export type AlignmentOptionType = 'start' | 'center' | 'end' | number

export type AlignmentType = {
Expand Down Expand Up @@ -27,7 +29,7 @@ export function Alignment(
}

function measure(n: number): number {
if (typeof align === 'number') return percent()
if (isNumber(align)) return percent()
return predefined[align](n)
}

Expand Down
10 changes: 9 additions & 1 deletion packages/embla-carousel/src/components/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ScrollTo, ScrollToType } from './ScrollTo'
import { SlideLooper, SlideLooperType } from './SlideLooper'
import { SlidesInView, SlidesInViewType } from './SlidesInView'
import { SlideSizes } from './SlideSizes'
import { SlidesToScroll, SlidesToScrollType } from './SlidesToScroll'
import { Translate, TranslateType } from './Translate'
import { arrayKeys, arrayLast, arrayLastIndex } from './utils'
import { Vector1D, Vector1DType } from './Vector1d'
Expand All @@ -44,6 +45,7 @@ export type Engine = {
eventStore: EventStoreType
slideLooper: SlideLooperType
slidesInView: SlidesInViewType
slidesToScroll: SlidesToScrollType
target: Vector1DType
translate: TranslateType
scrollTo: ScrollToType
Expand Down Expand Up @@ -71,7 +73,7 @@ export function Engine(
loop,
speed,
dragFree,
slidesToScroll,
slidesToScroll: groupSlides,
skipSnaps,
containScroll,
} = options
Expand All @@ -90,6 +92,11 @@ export function Engine(
slideRects,
loop,
)
const slidesToScroll = SlidesToScroll(
viewSize,
slideSizesWithGaps,
groupSlides,
)
const { snaps, snapsAligned } = ScrollSnap(
axis,
alignment,
Expand Down Expand Up @@ -229,6 +236,7 @@ export function Engine(
location,
slides,
),
slidesToScroll,
slidesInView,
slideIndexes,
target,
Expand Down
5 changes: 3 additions & 2 deletions packages/embla-carousel/src/components/Options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AlignmentOptionType } from './Alignment'
import { AxisOptionType } from './Axis'
import { SlidesToScrollOptionType } from './SlidesToScroll'
import { DirectionOptionType } from './Direction'
import { ScrollContainOptionType } from './ScrollContain'

Expand All @@ -8,12 +9,12 @@ export type OptionsType = {
axis: AxisOptionType
containScroll: ScrollContainOptionType
direction: DirectionOptionType
slidesToScroll: SlidesToScrollOptionType
dragFree: boolean
draggable: boolean
inViewThreshold: number
loop: boolean
skipSnaps: boolean
slidesToScroll: number
speed: number
startIndex: number
}
Expand All @@ -23,12 +24,12 @@ export const defaultOptions: OptionsType = {
axis: 'x',
containScroll: '',
direction: 'ltr',
slidesToScroll: 1,
dragFree: false,
draggable: true,
inViewThreshold: 0,
loop: false,
skipSnaps: false,
slidesToScroll: 1,
speed: 10,
startIndex: 0,
}
Expand Down
10 changes: 6 additions & 4 deletions packages/embla-carousel/src/components/ScrollSnap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AlignmentType } from './Alignment'
import { AxisType } from './Axis'
import { arrayLast, arrayGroup, mathAbs } from './utils'
import { SlidesToScrollType } from './SlidesToScroll'
import { arrayLast, mathAbs } from './utils'

export type ScrollSnapType = {
snaps: number[]
Expand All @@ -12,14 +13,15 @@ export function ScrollSnap(
alignment: AlignmentType,
containerRect: DOMRect,
slideRects: DOMRect[],
slidesToScroll: number,
slidesToScroll: SlidesToScrollType,
): ScrollSnapType {
const { startEdge, endEdge } = axis
const { groupSlides } = slidesToScroll
const snaps = measureUnaligned()
const snapsAligned = measureAligned()

function measureSizes(): number[] {
return arrayGroup(slideRects, slidesToScroll)
return groupSlides(slideRects)
.map((rects) => arrayLast(rects)[endEdge] - rects[0][startEdge])
.map(mathAbs)
}
Expand All @@ -31,7 +33,7 @@ export function ScrollSnap(
}

function measureAligned(): number[] {
const groupedSnaps = arrayGroup(snaps, slidesToScroll).map((g) => g[0])
const groupedSnaps = groupSlides(snaps).map((g) => g[0])
const alignments = measureSizes().map(alignment.measure)
return groupedSnaps.map((snap, index) => snap + alignments[index])
}
Expand Down
43 changes: 43 additions & 0 deletions packages/embla-carousel/src/components/SlidesToScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { arrayKeys, arrayLast, isNumber } from './utils'

export type SlidesToScrollOptionType = 'auto' | number

export type SlidesToScrollType = {
groupSlides: <GenericType>(array: GenericType[]) => GenericType[][]
}

export function SlidesToScroll(
viewSize: number,
slideSizesWithGaps: number[],
slidesToScroll: SlidesToScrollOptionType,
): SlidesToScrollType {
const groupByNumber = isNumber(slidesToScroll)

function byNumber<GenericType>(
array: GenericType[],
groupSize: number,
): GenericType[][] {
return arrayKeys(array)
.filter((i) => i % groupSize === 0)
.map((i) => array.slice(i, i + groupSize))
}

function bySize<GenericType>(array: GenericType[]): GenericType[][] {
return arrayKeys(array)
.reduce((groupSizes: number[], i) => {
const chunk = slideSizesWithGaps.slice(arrayLast(groupSizes), i + 1)
const chunkSize = chunk.reduce((a, s) => a + s, 0)
return !i || chunkSize > viewSize ? groupSizes.concat(i) : groupSizes
}, [])
.map((start, i, groupSizes) => array.slice(start, groupSizes[i + 1]))
}

function groupSlides<GenericType>(array: GenericType[]): GenericType[][] {
return groupByNumber ? byNumber(array, slidesToScroll) : bySize(array)
}

const self: SlidesToScrollType = {
groupSlides,
}
return self
}
4 changes: 3 additions & 1 deletion packages/embla-carousel/src/components/Vector1d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isNumber } from './utils'

export type Vector1DType = {
get: () => number
set: (v: Vector1DType | number) => Vector1DType
Expand Down Expand Up @@ -46,7 +48,7 @@ export function Vector1D(value: number): Vector1DType {
}

function readNumber(n: Vector1DType | number): number {
return typeof n === 'number' ? n : n.get()
return isNumber(n) ? n : n.get()
}

const self: Vector1DType = {
Expand Down
15 changes: 4 additions & 11 deletions packages/embla-carousel/src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export function map(
return oStart + (oStop - oStart) * ((value - iStart) / (iStop - iStart))
}

export function isNumber(subject: unknown): subject is number {
return typeof subject === 'number'
}

export function mathAbs(n: number): number {
return Math.abs(n)
}
Expand All @@ -32,17 +36,6 @@ export function roundToDecimals(decimalPoints: number): (n: number) => number {
return (n: number): number => Math.round(n * pow) / pow
}

export function arrayGroup<GenericType>(
array: GenericType[],
size: number,
): GenericType[][] {
const groups = []
for (let i = 0; i < array.length; i += size) {
groups.push(array.slice(i, i + size))
}
return groups
}

export function arrayKeys<GenericType>(array: GenericType[]): number[] {
return Object.keys(array).map(Number)
}
Expand Down