diff --git a/base-filters/.gitignore b/base-filters/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/base-filters/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/base-filters/README.md b/base-filters/README.md
new file mode 100644
index 0000000..27c7be0
--- /dev/null
+++ b/base-filters/README.md
@@ -0,0 +1,89 @@
+# Описание
+
+Модуль содержит реализацию следующих типов фильтров:
+
+1. Выбор одного из доступных значений списка
+2. Выбор нескольких доступных значений из списка
+3. добавить остальные по ходу реализаации
+
+# Использование
+
+## Выбор одного/нескольких из доступных значений списка
+
+### Как использовать
+``` kotlin
+val selectorView = ListSelectionView>(context)
+ .Builder()
+ .setItems(navArgs.items)
+ .addItemDecoration((TopDividerItemDecoration(
+ context = requireContext(),
+ drawableId = R.drawable.list_divider_1dp,
+ startMargin = START_MARGIN_DIVIDER_DP.px
+ )))
+ .withSelectionType(ListSelectionView.SelectionType.SINGLE_SELECT)
+ .onResultListener { items ->
+ viewModel.dispatchAction(SelectItemAction.SelectItem(items))
+ }
+ .build()
+```
+### Конфигурации
+* при создании `ListSelectionView` необходимо передлать `ItemType` - класс модели данных в списке, `HolderType` - класс viewHolder-а в recyclerView.
+Для использования дефолтной реализации необходимо использовать типы `>`
+* в метод `setItems(List)` необходимо передать список объектов
+* метод `addItemDecoration(itemDecoration: RecyclerView.ItemDecoration)` можно использовать для передачи объекта `RecyclerView.ItemDecoration`
+* метод `withSelectionType(type: SelectionType)` используется для указания типа выбора:
+ * `SINGLE_SELECT` - по умолчанию - позволяет выбрать один выариант, при этом будет выбран всегда как минимум один вариант
+ * `MULTI_SELECT` - позволяет выбрать несколько вариантов из списка, при этом можно полностью выбрать все варианты и убрать выделение со всех вариантов
+* метод `showInHolder(HolderFactoryType)` используется для определения кастомного viewHolder для списка с недефолтной разметкой.
+``` kotlin
+val selectorView = ListSelectionView(context)
+ .Builder()
+ .showInHolder { parent, clickListener, selectionType ->
+ TestItemViewHolder(
+ binding = TestSelectionItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
+ onItemSelectAction = clickListener,
+ selectionType = selectionType
+ )
+ }
+ ...
+ .build()
+```
+* колбэк `onSelectedItemsListener(listener: OnSelectedItemsListener)` можно использовать для получения списка всех элементов `ItemType` после каждого выбора
+* колбэк `onSelectedItemListener(listener: OnSelectedItemListener)` можно использовать для получения элемента списка `ItemType`, по которому произошел клик
+* после вызова конфигурационных методов обязательно необходимо вызать метод `build()`
+
+### Кастомизация стиля дефолтной реализации ViewHolder без необходимости создания кастомного layout и viewHolder
+
+#### 1. Определить кастомную тему и стили элементов
+1. Стиль для **текста элемента списка** должен быть наследником стиля `Widget.FilterSelection.Item`
+``` xml
+
+```
+2. Стиль для **индикатора выбора** должен быть наследником стиля `Widget.FilterSelection.Radio`
+Передайте `selector-drawable` для кастомизации вида индикатора в конце строки
+``` xml
+
+```
+3. Создайте **тему**, которая должна быть наследником `Theme.FilterSelection`
+``` xml
+
+```
+#### 2. Применить тему при создании view
+При создании вью в коде можно указать тему, используя `ContextThemeWrapper`
+``` kotlin
+val newContext = ContextThemeWrapper(requireContext(), R.style.Theme_Custom_FilterSelection)
+val selectorView = ListSelectionView(newContext)
+ .Builder()
+ ...
+ .build()
+```
diff --git a/base-filters/build.gradle b/base-filters/build.gradle
new file mode 100644
index 0000000..6c57558
--- /dev/null
+++ b/base-filters/build.gradle
@@ -0,0 +1,41 @@
+apply from: "../android-configs/lib-config.gradle"
+apply plugin: 'kotlin-parcelize'
+
+android {
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+ implementation project(":utils")
+ implementation project(":recyclerview-adapters")
+ implementation project(":navigation-base")
+ implementation project(":kotlin-extensions")
+
+ implementation("org.jetbrains.kotlin:kotlin-stdlib")
+
+ implementation("androidx.core:core-ktx")
+
+ implementation("androidx.appcompat:appcompat")
+ implementation("com.google.android.material:material")
+ implementation("androidx.constraintlayout:constraintlayout") {
+ version {
+ require '2.0.0'
+ }
+ }
+
+ constraints {
+ implementation("androidx.appcompat:appcompat") {
+ version {
+ require '1.0.0'
+ }
+ }
+
+ implementation("androidx.core:core-ktx") {
+ version {
+ require '1.0.0'
+ }
+ }
+ }
+}
diff --git a/base-filters/src/main/AndroidManifest.xml b/base-filters/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..14e5b35
--- /dev/null
+++ b/base-filters/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/ListSelectionView.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/ListSelectionView.kt
new file mode 100644
index 0000000..c19db8e
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/ListSelectionView.kt
@@ -0,0 +1,136 @@
+package ru.touchin.roboswag.base_filters.select_list_item
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ContextThemeWrapper
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.annotation.StyleRes
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import ru.touchin.roboswag.base_filters.databinding.SelectionItemBinding
+import ru.touchin.roboswag.base_filters.select_list_item.adapter.BaseSelectionViewHolder
+import ru.touchin.roboswag.base_filters.select_list_item.adapter.HolderFactoryType
+import ru.touchin.roboswag.base_filters.select_list_item.adapter.SelectionItemViewHolder
+import ru.touchin.roboswag.base_filters.select_list_item.adapter.SheetSelectionAdapter
+import ru.touchin.roboswag.base_filters.select_list_item.model.BaseSelectionItem
+
+private typealias OnSelectedItemListener = (item: ItemType) -> Unit
+private typealias OnSelectedItemsListener = (items: List) -> Unit
+
+/**
+ * Base [ListSelectionView] to use in filters screen for choosing single or multi items in list.
+ *
+ * @param ItemType Type of model's element in list.
+ * It must implement [BaseSelectionItem] abstract class.
+ *
+ * @param HolderType Type of viewHolder in recyclerView.
+ * It must extend [BaseSelectionViewHolder] abstract class.
+ *
+ **/
+
+class ListSelectionView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : RecyclerView(context, attrs, defStyleAttr)
+ where ItemType : BaseSelectionItem,
+ HolderType : BaseSelectionViewHolder {
+
+ enum class SelectionType { SINGLE_SELECT, MULTI_SELECT }
+
+ constructor(context: Context, @StyleRes themeResId: Int) : this(ContextThemeWrapper(context, themeResId))
+
+ private var mutableItems: List = emptyList()
+ private var selectionType = SelectionType.SINGLE_SELECT
+
+ private var onSelectedItemChanged: OnSelectedItemListener? = null
+ private var onSelectedItemsChanged: OnSelectedItemsListener? = null
+ private var factory: HolderFactoryType = getDefaultFactory()
+
+ init {
+ layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
+ layoutManager = LinearLayoutManager(context)
+ }
+
+ private fun getDefaultFactory(): HolderFactoryType = { parent, clickListener, selectionType ->
+ SelectionItemViewHolder(
+ binding = SelectionItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
+ onItemSelectAction = clickListener,
+ selectionType = selectionType
+ )
+ }
+
+ private val selectionAdapter by lazy {
+ SheetSelectionAdapter(
+ onItemSelectAction = onItemSelectedListener,
+ selectionType = selectionType,
+ factory = factory
+ )
+ }
+
+ private val onItemSelectedListener: (item: ItemType) -> Unit = { item ->
+ onSelectedItemChanged?.invoke(item)
+ updateAfterSelection(item)
+ onSelectedItemsChanged?.invoke(mutableItems)
+ }
+
+ fun updateItems(items: List) {
+ mutableItems = items
+ updateList()
+ }
+
+ private fun updateList() {
+ selectionAdapter.submitList(mutableItems)
+ }
+
+ private fun updateAfterSelection(selectedItem: ItemType) {
+ mutableItems = mutableItems.map { item ->
+ when {
+ item.isItemTheSame(selectedItem) -> selectedItem
+ selectionType == SelectionType.SINGLE_SELECT -> item.copyWithSelection(isSelected = false)
+ else -> item
+ }
+ }
+ updateList()
+ }
+
+ inner class Builder {
+
+ fun setItems(items: List) = apply {
+ mutableItems = items
+ }
+
+ fun setItems(
+ source: List,
+ mapper: (T) -> ItemType
+ ) = setItems(source.map { item -> mapper.invoke(item) })
+
+ fun showInHolder(holderFactory: HolderFactoryType) = apply {
+ factory = holderFactory
+ }
+
+ fun addItemDecoration(itemDecoration: RecyclerView.ItemDecoration) = apply {
+ this@ListSelectionView.addItemDecoration(itemDecoration)
+ }
+
+ fun onSelectedItemListener(listener: OnSelectedItemListener) = apply {
+ this@ListSelectionView.onSelectedItemChanged = listener
+ }
+
+ fun onSelectedItemsListener(listener: OnSelectedItemsListener) = apply {
+ this@ListSelectionView.onSelectedItemsChanged = listener
+ }
+
+ fun withSelectionType(type: SelectionType) = apply {
+ selectionType = type
+ }
+
+ fun build() = this@ListSelectionView.also {
+ it.adapter = selectionAdapter
+ updateList()
+ }
+ }
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/BaseSelectionViewHolder.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/BaseSelectionViewHolder.kt
new file mode 100644
index 0000000..847bfc2
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/BaseSelectionViewHolder.kt
@@ -0,0 +1,11 @@
+package ru.touchin.roboswag.base_filters.select_list_item.adapter
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import ru.touchin.roboswag.base_filters.select_list_item.model.BaseSelectionItem
+
+abstract class BaseSelectionViewHolder(val view: View)
+ : RecyclerView.ViewHolder(view) {
+
+ abstract fun bind(item: ItemType)
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SelectionItemViewHolder.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SelectionItemViewHolder.kt
new file mode 100644
index 0000000..0fc9a88
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SelectionItemViewHolder.kt
@@ -0,0 +1,33 @@
+package ru.touchin.roboswag.base_filters.select_list_item.adapter
+
+import android.view.View
+import ru.touchin.roboswag.base_filters.databinding.SelectionItemBinding
+import ru.touchin.roboswag.base_filters.select_list_item.ListSelectionView
+import ru.touchin.roboswag.base_filters.select_list_item.model.BaseSelectionItem
+
+class SelectionItemViewHolder(
+ private val binding: SelectionItemBinding,
+ private val onItemSelectAction: (ItemType) -> Unit,
+ private val selectionType: ListSelectionView.SelectionType
+) : BaseSelectionViewHolder(binding.root) {
+
+ override fun bind(item: ItemType) {
+ binding.itemTitle.text = item.title
+ binding.itemRadiobutton.isChecked = item.isSelected
+
+ setupCheckListener(item)
+ }
+
+ private fun setupCheckListener(item: ItemType) = with(binding) {
+ val checkListener = View.OnClickListener {
+ itemRadiobutton.isChecked = true
+ onItemSelectAction.invoke(item.copyWithSelection(isSelected = when (selectionType) {
+ ListSelectionView.SelectionType.SINGLE_SELECT -> true
+ else -> !item.isSelected
+ }))
+ }
+ root.setOnClickListener(checkListener)
+ itemRadiobutton.setOnClickListener(checkListener)
+ }
+
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionAdapter.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionAdapter.kt
new file mode 100644
index 0000000..b8d6449
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionAdapter.kt
@@ -0,0 +1,30 @@
+package ru.touchin.roboswag.base_filters.select_list_item.adapter
+
+import androidx.recyclerview.widget.DiffUtil
+import ru.touchin.roboswag.base_filters.select_list_item.ListSelectionView
+import ru.touchin.roboswag.base_filters.select_list_item.model.BaseSelectionItem
+import ru.touchin.roboswag.recyclerview_adapters.adapters.DelegationListAdapter
+
+class SheetSelectionAdapter(
+ onItemSelectAction: (ItemType) -> Unit,
+ selectionType: ListSelectionView.SelectionType,
+ factory: HolderFactoryType
+) : DelegationListAdapter(object : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: BaseSelectionItem, newItem: BaseSelectionItem): Boolean =
+ oldItem.isItemTheSame(newItem)
+
+ override fun areContentsTheSame(oldItem: BaseSelectionItem, newItem: BaseSelectionItem): Boolean =
+ oldItem.isContentTheSame(newItem)
+
+}) {
+
+ init {
+ addDelegate(SheetSelectionDelegate(
+ onItemSelectAction = onItemSelectAction,
+ selectionType = selectionType,
+ factory = factory
+ ))
+ }
+
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionDelegate.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionDelegate.kt
new file mode 100644
index 0000000..14f5460
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/adapter/SheetSelectionDelegate.kt
@@ -0,0 +1,28 @@
+package ru.touchin.roboswag.base_filters.select_list_item.adapter
+
+import android.view.ViewGroup
+import ru.touchin.roboswag.base_filters.select_list_item.ListSelectionView.SelectionType
+import ru.touchin.roboswag.base_filters.select_list_item.model.BaseSelectionItem
+import ru.touchin.roboswag.recyclerview_adapters.adapters.ItemAdapterDelegate
+
+typealias HolderFactoryType = (ViewGroup, (ItemType) -> Unit, SelectionType) -> BaseSelectionViewHolder
+
+class SheetSelectionDelegate(
+ private val onItemSelectAction: (ItemType) -> Unit,
+ private val selectionType: SelectionType,
+ private val factory: HolderFactoryType
+) : ItemAdapterDelegate, ItemType>()
+ where ItemType : BaseSelectionItem {
+
+ override fun onCreateViewHolder(parent: ViewGroup): BaseSelectionViewHolder =
+ factory.invoke(parent, onItemSelectAction, selectionType)
+
+ override fun onBindViewHolder(
+ holder: BaseSelectionViewHolder,
+ item: ItemType,
+ adapterPosition: Int,
+ collectionPosition: Int,
+ payloads: MutableList
+ ) = holder.bind(item)
+
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/BaseSelectionItem.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/BaseSelectionItem.kt
new file mode 100644
index 0000000..8f59990
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/BaseSelectionItem.kt
@@ -0,0 +1,14 @@
+package ru.touchin.roboswag.base_filters.select_list_item.model
+
+abstract class BaseSelectionItem(
+ open val id: Int,
+ open val title: String,
+ open val isSelected: Boolean
+) {
+
+ abstract fun isItemTheSame(compareItem: BaseSelectionItem): Boolean
+
+ abstract fun isContentTheSame(compareItem: BaseSelectionItem): Boolean
+
+ abstract fun copyWithSelection(isSelected: Boolean): ItemType
+}
diff --git a/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/DefaultSelectionItem.kt b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/DefaultSelectionItem.kt
new file mode 100644
index 0000000..603961f
--- /dev/null
+++ b/base-filters/src/main/java/ru/touchin/roboswag/base_filters/select_list_item/model/DefaultSelectionItem.kt
@@ -0,0 +1,24 @@
+package ru.touchin.roboswag.base_filters.select_list_item.model
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class DefaultSelectionItem(
+ override val id: Int,
+ override val title: String,
+ override val isSelected: Boolean = false
+) : BaseSelectionItem(id, title, isSelected), Parcelable {
+
+ override fun isItemTheSame(compareItem: BaseSelectionItem): Boolean = when {
+ compareItem is DefaultSelectionItem && id == compareItem.id -> true
+ else -> false
+ }
+
+ override fun isContentTheSame(compareItem: BaseSelectionItem): Boolean =
+ this == compareItem
+
+ @Suppress("UNCHECKED_CAST")
+ override fun copyWithSelection(isSelected: Boolean): ItemType =
+ this.copy(isSelected = isSelected) as ItemType
+}
diff --git a/base-filters/src/main/res/layout/selection_item.xml b/base-filters/src/main/res/layout/selection_item.xml
new file mode 100644
index 0000000..72bb7eb
--- /dev/null
+++ b/base-filters/src/main/res/layout/selection_item.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/base-filters/src/main/res/values/attrs.xml b/base-filters/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..bd6618a
--- /dev/null
+++ b/base-filters/src/main/res/values/attrs.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/base-filters/src/main/res/values/styles.xml b/base-filters/src/main/res/values/styles.xml
new file mode 100644
index 0000000..380b4da
--- /dev/null
+++ b/base-filters/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+