Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jordond/drag-select-compose

⚡ A Compose multiplatform library for adding Google Photos style drag-to-select multi-selection to a LazyGrid
https://github.com/jordond/drag-select-compose

android compose compose-desktop compose-ios compose-multiplatform drag-selection drag-selection-library google-photos kotlin kotlin-multiplatform kotlin-multiplatform-library kotlin-multiplatform-mobile

Last synced: 3 months ago
JSON representation

⚡ A Compose multiplatform library for adding Google Photos style drag-to-select multi-selection to a LazyGrid

Awesome Lists containing this project

README

        

# Drag Select Compose

![Maven Central](https://img.shields.io/maven-central/v/com.dragselectcompose/dragselect)
[![Kotlin](https://img.shields.io/badge/kotlin-v2.0.0-blue.svg?logo=kotlin)](http://kotlinlang.org)
[![Build](https://github.com/jordond/drag-select-compose/actions/workflows/ci.yml/badge.svg)](https://github.com/jordond/drag-select-compose/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/jordond/drag-select-compose)](http://www.apache.org/licenses/LICENSE-2.0)

[![Compose Multiplatform](https://img.shields.io/badge/Compose%20Multiplatform-v1.6.10-blue)](https://github.com/JetBrains/compose-multiplatform)
![badge-android](http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat)
![badge-ios](http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat)
![badge-desktop](http://img.shields.io/badge/platform-desktop-DB413D.svg?style=flat)
![badge-js-wasm](http://img.shields.io/badge/platform-js%2Fwasm-FDD835.svg?style=flat)

This is a Compose Multiplatform library that allows you to easily implement a "Google Photos"-style
multi-selection in your Compose apps.

You can run the demo at [demo.dragselectcompose.com](https://demo.dragselectcompose.com) and view the KDocs at [docs.dragselectcompose.com](https://docs.dragselectcompose.com)

demo

## Table of Contents

- [Platforms](#platforms)
- [Inspiration](#inspiration)
- [Setup](#setup)
- [Single Platform](#single-platform)
- [Multiplatform](#multiplatform)
- [Version Catalog](#version-catalog)
- [Usage](#usage)
- [Extensions](#extensions)
- [Grid Wrapper](#wrapper)
- [Demo](#demo)
- [Android](#android-demo)
- [Multiplatform](#multiplatform-demo)
- [License](#license)

## Platforms

This library is written for Compose Multiplatform, and can be used on the following platforms:

- Android
- iOS
- JVM (Desktop)
- JavaScript/wasm (Browser)

## Inspiration

This library was inspired by
this [article](https://medium.com/androiddevelopers/create-a-photo-grid-with-multiselect-behavior-using-jetpack-compose-9a8d588a9b63)
and
the [gist](https://gist.github.com/JolandaVerhoef/bcaf96360b92bba974e3796fe37247e2).

As well as the [drag-select-recyclerview](https://github.com/afollestad/drag-select-recyclerview)
library.

## Setup

You can add this library to your project using Gradle.

### Single Platform

To add to a single platform like Android, add the dependency to your app level `build.gradle.kts`
file:

```kotlin
dependencies {
// Includes the core functionality along with all of the optional modules
implementation("com.dragselectcompose:dragselect:2.3.0")

// Or use the modules you want

// Core functionality
implementation("com.dragselectcompose:core:2.3.0")

// Optional extensions for adding semantics and toggle Modifiers to Grid items
implementation("com.dragselectcompose:extensions:2.3.0")

// Optional wrappers around LazyGrid that implement the selection UI for you
implementation("com.dragselectcompose:grid:2.3.0")
}
```

### Multiplatform

To add to a multiplatform project, add the dependency to the common source-set:

```kotlin
kotlin {
sourceSets {
commonMain {
dependencies {
// Includes the core functionality along with all of the optional modules
implementation("com.dragselectcompose:dragselect:2.3.0")

// Or use the modules you want

// Core functionality
implementation("com.dragselectcompose:core:2.3.0")

// Optional extensions for adding semantics and toggle Modifiers to Grid items
implementation("com.dragselectcompose:extensions:2.3.0")

// Optional wrappers around LazyGrid that implement the selection UI for you
implementation("com.dragselectcompose:grid:2.3.0")
}
}
}
}
```

For the supported platforms, see the badges at the top of the README.

### Version catalog

```toml
[versions]
dragselectcompose = "2.3.0"

[libraries]
dragselect = { module = "com.dragselectcompose:dragselect", version.ref = "dragselectcompose" }
dragselect-core = { module = "com.dragselectcompose:core", version.ref = "dragselectcompose" }
dragselect-extensions = { module = "com.dragselectcompose:extensions", version.ref = "dragselectcompose" }
dragselect-grid = { module = "com.dragselectcompose:grid", version.ref = "dragselectcompose" }
```

## Usage

The `:core` artifact provides a `Modifier` extension for adding a drag-to-select functionality to
your `LazyGrid`:

```kotlin
fun Modifier.gridDragSelect(
items: List,
state: DragSelectState,
enableAutoScroll: Boolean = true,
autoScrollThreshold: Float? = null,
enableHaptics: Boolean = true,
hapticFeedback: HapticFeedback? = null,
): Modifier
```

It provides the following functionality:

- Adds a long-press drag gesture to select items.
- Maintains a list of selected items.
- Expose a `inSelectionMode: Boolean` which you can use to display a unselected state.
- If `enableAutoScroll` is `true` then the list will start to scroll when reaching the top or bottom
of the list.
- Will trigger a "long-press" haptics if `enableHaptics` is `true`.

**Note:** By default selected items will be compared using an equality check. If your item is not
a `data class` you must implement `equals` and `hashCode` for your item. Or you can pass a lambda
to `rememberDragSelectState` to compare your items:

```kotlin
val dragSelectState = rememberDragSelectState(compareSelector = { it.someProperty })
```

You can then use `DragSelectState` to render your list of items:

### Basic Example

```kotlin
data class Model(
val id: Int,
val title: String,
val imageUrl: String,
)

@Composeable
fun MyGrid(models: List) {
val dragSelectState = rememberDragSelectState()
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 128.dp),
state = dragSelectState.lazyGridState,
verticalArrangement = Arrangement.spacedBy(3.dp),
horizontalArrangement = Arrangement.spacedBy(3.dp),
modifier = Modifier.gridDragSelect(
items = models,
state = dragSelectState,
),
) {
items(models, key = { it.id }) { model ->
val isSelected by remember { derivedStateOf { dragSelectState.isSelected(model) } }
val inSelectionMode = dragSelectState.inSelectionMode

// Define your Model Composeable and use `isSelected` or `inSelectionMode`
}
}
}
```

You can see a full basic example
in [`BasicDragSelectPhotoGrid`](demo/android/src/main/kotlin/com/dragselectcompose/demo/BasicDragSelectPhotoGrid.kt).

## Extensions

Included in the `:dragselectcompose` and `:extensions` artifact are a couple extensions on `Modifer`
to easily add support for accessibility semantics and toggling selection while the Grid is in
selection mode.

- [`Modifier.dragSelectSemantics()`](extensions/src/commonMain/kotlin/com/dragselectcompose/extensions/Semantics.kt)
- Adds a long click semantics to the modifier for accessibility.
- [`Modifier.dragSelectToggleable()`](extensions/src/commonMain/kotlin/com/dragselectcompose/extensions/Toggleable.kt)
- Allows you to toggle the item when the Grid is in Selection Mode.
- [`Modifier.dragSelectToggleableItem()`](extensions/src/commonMain/kotlin/com/dragselectcompose/extensions/ToggleableItem.kt)
- Combines the above two extensions.

```kotlin
@Composeable
fun MyGrid(models: List) {
val dragSelectState = rememberDragSelectState()
LazyVerticalGrid(
// ...
) {
items(models, key = { it.id }) { model ->
// Add semantics and toggleable modifiers
MyItemContent(
item = model,
modifier = Modifier.dragSelectToggleable(
state = dragSelectState,
item = model,
),
)
}
}
}
```

You can see a full extensions example
in [`ExtensionsDragSelectPhotoGrid`](demo/android/src/main/kotlin/com/dragselectcompose/demo/ExtensionsDragSelectPhotoGrid.kt).

## Wrapper

Included in the `:grid` artifact is a "all-inclusive" drag-select experience. It includes wrappers
around `LazyHorizontalGrid` and `LazyVerticalGrid` that takes care of adding
the `Modifier.gridDragSelect`.

When using `LazyDragSelectVerticalGrid` or `LazyDragSelectHorizontalGrid` the `content()` is scoped
to a custom scope that provides a helper composable for handling the selection indicator, and
animating the padding.

Here is a quick example:

```kotlin
@Composeable
fun MyGrid(models: List) {
val dragSelectState = rememberDragSelectState()

LazyDragSelectVerticalGrid(
columns = GridCells.Adaptive(minSize = 128.dp),
items = models,
state = dragSelectState,
) {
items(key = { it.id }) { model ->
SelectableItem(item = model) {
// Your Composeable for your item
}
}
}
}
```

Now your item will have an animated padding and clipped shape when selected. As well as displaying
indicator icons when the grid is in selection mode, and the item is selected or not.

See the documentation for `LazyDragSelectVerticalGrid` and `SelectableItem` for all the options you
can customize.

You can see a full example
in [`LazyDragSelectPhotoGrid`](demo/android/src/main/kotlin/com/dragselectcompose/demo/LazyDragSelectPhotoGrid.kt)

## Demo

To run a demo for the library you can look inside of `/demo` to see a standard Android application,
and a Compose Multiplatform application.

### Android Demo

A demo app is included in the `:demo:android` module, run it by following these steps:

```shell
git clone [email protected]:jordond/drag-select-compose.git drag-select-compose
cd drag-select-compose
./gradlew assembleRelease
```

Then install the `demo/android/build/outputs/apk/release/demo-release.apk` file on your device.

### Multiplatform Demo

The demo is inside of `:demo:kmm` module. In order to run it you should have the latest version of
Android studio installed.

Check out [the README](demo/README.md) for more information.

## License

See [LICENSE](LICENSE)