Skip to content

Commit

Permalink
feat(useGroupModel): useGroupModel supports various options now
Browse files Browse the repository at this point in the history
  • Loading branch information
jd-solanki committed Jul 30, 2022
1 parent 54c1f15 commit c087596
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 333 deletions.
14 changes: 7 additions & 7 deletions packages/anu-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@
"vue": "^3.2.37"
},
"devDependencies": {
"@unocss/core": "^0.44.7",
"@unocss/reset": "^0.44.7",
"@unocss/core": "^0.45.1",
"@unocss/reset": "^0.45.1",
"@vitejs/plugin-vue": "^3.0.1",
"@vitejs/plugin-vue-jsx": "^2.0.0",
"@vueuse/core": "^9.0.0",
"@vueuse/core": "^9.0.2",
"sass": "^1.54.0",
"typescript": "^4.7.4",
"unocss": "^0.44.7",
"unocss": "^0.45.1",
"unplugin-vue-components": "^0.21.2",
"vite": "^3.0.3",
"vite": "^3.0.4",
"vite-plugin-dts": "^1.4.0",
"vue-router": "^4.1.2",
"vue-tsc": "^0.39.0"
"vue-router": "^4.1.3",
"vue-tsc": "^0.39.2"
},
"peerDependencies": {
"@unocss/reset": "^0.41.1",
Expand Down
153 changes: 58 additions & 95 deletions packages/anu-vue/src/composables/useGroupModel.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,66 @@
import { computed, ref } from 'vue'
import type { MaybeComputedRef } from '@vueuse/core'
import type { ComputedRef, Ref } from 'vue'
import { computed, ref, toRaw, unref, watch } from 'vue'

// type Options = string | Record<string, any>
type ComposableParams<T> = {
multi?: boolean
} & (
{ options: T[]; count?: never }
| { options?: never; count: number }
)
// TODO: Improve typings

// TODO: Add xor in function params
export const useGroupModel = <T>(params: ComposableParams<T>) => {
/*
In:
- options: {
count?: number => Useful for using composable with undefined options like switches, radio, etc where no id or name is given.
multi: boolean = false => Allow selecting multiple values
}
Out:
- options => Options to render using v-for
- trueValue => Value to set when selected
- falseValue => Value to set when not selected
- value => Current value/state of option
- isSelected => If current option is selected
- key => This will get used to get trueValue from object when object[] is passed ~or finding unique option~
- select => Select function to select an option
- value => modelValue
===
If I pass array of string `['banana', 'apple', 'mango']` then it should return:
```
[
'banana': {
trueValue: 'banana',
falseValue: null,
value: 'banana',
isSelected: false,
},
...so on
]
```
If I pass count then it should return:
```
[
0: {
trueValue: true,
falseValue: false,
value: false,
isSelected: false,
}
]
```
If I pass array of objects [{ key: 'title', title: 'VueJS', color: 'green' }] then it should return:
```
[
'VueJS': {
trueValue: 'VueJS',
falseValue: null,
value: false,
isSelected: false,
title: 'VueJS',
color: 'green',
key: 'title',
}
]
```
Notes: Allow passing extra properties so it can be returned via `options`. e.g. Color
Usage: const { options, value } = useGroupModel({ options: ['banana', 'apple', 'mango'] })
*/

const { options, count, multi } = params

const value = ref<T>()
interface ComposableParams<T> {
multi?: MaybeComputedRef<boolean>
options: T[] | number
}

const select = (option: T) => {
value.value = option
}
interface OptionsOut<T> {
value: T
isSelected: ComputedRef<boolean>
}

// TODO: Remove any
const _options = ref(options?.map(option => {
if (typeof option === 'string') {
return ({
trueValue: option,
falseValue: null,
value: option,
isSelected: computed(() => option === value.value),
})
export const useGroupModel = <T>(params: ComposableParams<T>) => {
const { options, multi } = params

const value = ref<T | number | Set<T | number> | undefined>()

const select = (option: T | number) => {
// If multiple selection is enabled
if (unref(multi)) {
// If value is not set (Means previously multi was false) => Initialize new set and assign it to value
if (!(value.value instanceof Set)) { value.value = new Set([option]) }

// Else toggle option in set
else {
if (value.value.has(option))
value.value.delete(option)
else value.value.add(option)
}
}

// WIP
return null

// other types
}))
else {
value.value = option
}
}
watch(
() => unref(multi),
() => {
value.value = undefined
},
)

const _options = ref([]) as Ref<OptionsOut<T | number>[]>

if (typeof options === 'number') {
_options.value = [...Array(options)].map((_, i) => ({
value: i,
isSelected: computed(() => i === value.value),
}))
}
else {
_options.value = options.map(option => ({
value: option,
isSelected: computed(() => {
return unref(multi)
? value.value instanceof Set ? value.value.has(option) : false
: option === toRaw(value.value)
}),
}))
}

return {
options: _options,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
<script setup lang="ts">
import { useGroupModel } from 'anu-vue'
import { ref } from 'vue'
const isMultiEnabled = ref(false)
const { options, select, value } = useGroupModel<string>({
options: ['banana', 'apple', 'watermelon', 'orange'],
options: ['apple', 'banana', 'orange', 'watermelon'],
multi: isMultiEnabled,
})
const { options: countOptions, select: countSelect, value: countValue } = useGroupModel({ options: 3 })
const { options: objOptions, select: objSelect } = useGroupModel({
options: [
{ title: 'Home', icon: 'i-bx-home' },
{ title: 'Categories', icon: 'i-bx-category' },
{ title: 'Cart', icon: 'i-bx-basket' },
{ title: 'Profile', icon: 'i-bx-user-circle' },
],
})
</script>

<template>
<div>
<div class="flex gap-6 mb-4">
<div class="flex flex-wrap gap-6 mb-4">
<ABtn
v-for="option in options"
:key="option.value"
Expand All @@ -18,6 +32,47 @@ const { options, select, value } = useGroupModel<string>({
{{ option.value }}
</ABtn>
</div>
<small>Selected: {{ value ? value : 'Nothing is selected' }}</small>
<ACheckbox
v-model="isMultiEnabled"
label="Multiple Selection"
class="mb-3"
/>
<small class="block">Selected: {{
isMultiEnabled
? value ? [...value].join(', ') : String(value)
: String(value)
}}</small>

<hr class="!my-12 block">

<div class="flex flex-wrap gap-8 mb-4">
<ABtn
v-for="option in countOptions"
:key="option.value"
:variant="option.isSelected ? 'fill' : 'light'"
@click="countSelect(option.value)"
>
Index: {{ option.value }}
</ABtn>
</div>
<small>Selected: {{ String(countValue) }}</small>

<hr class="!my-12 block">

<div class="flex flex-wrap gap-6 mb-4">
<div
v-for="option in objOptions"
:key="option.value"
class="flex flex-col gap-1 items-center cursor-pointer"
:class="option.isSelected && 'text-primary'"
@click="objSelect(option.value)"
>
<i
:class="option.value.icon"
class="text-lg"
/>
<span>{{ option.value.title }}</span>
</div>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# useGroupModel

:::warning
API might change till I complete this composable
:::

<!-- 👉 Basic -->
<Demo>

Expand Down
8 changes: 4 additions & 4 deletions packages/documentation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
"@iconify-json/bx": "^1.1.3",
"@iconify-json/logos": "^1.1.13",
"@iconify-json/ph": "^1.1.2",
"@unocss/preset-icons": "^0.44.7",
"@unocss/preset-uno": "0.44.7",
"@unocss/preset-icons": "^0.45.1",
"@unocss/preset-uno": "0.45.1",
"postcss-prefix-selector": "^1.16.0",
"unocss": "^0.44.7",
"unocss": "^0.45.1",
"vitepress": "1.0.0-alpha.4"
},
"dependencies": {
"@vueuse/core": "^9.0.0",
"@vueuse/core": "^9.0.2",
"anu-vue": "workspace:*",
"vee-validate": "^4.6.2"
}
Expand Down
Loading

0 comments on commit c087596

Please sign in to comment.