Skip to content

Commit

Permalink
refactor!: indexMap to object; remove add event
Browse files Browse the repository at this point in the history
  • Loading branch information
wiidede committed Oct 21, 2023
1 parent 82468d2 commit bd529c6
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 56 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ generic="T = any, U = number | RangeData\<T>"
| max | number | The maximum value allowed | 100 |
| step | number | Step | 1 |
| vertical | boolean | Determines if the range is vertical. Note that it will generate new classes like 'm-range-v-xxx' | false |
| addable | boolean | Determines if new data can be added/deleted. This will emit event 'addThumb' | false |
| addable | boolean | Determines if new data can be added/deleted. You can specific the data to be add by `addData` prop | false |
| addData | (value: number) => RangeData<T, U> | Data to be added. This will only effect while modelValue is RangeData[]. It will return { value } by default | undefined |
| limit | number | the limit can be add | undefined |
| smooth | boolean | Determines if the thumb(s) should only be displayed on the stop points or not | false |
| deduplicate | boolean | Determines if the thumb(s) can be duplicated | true |
Expand All @@ -113,12 +114,6 @@ generic="T = any, U = number | RangeData\<T>"
| renderBottomOnActive | boolean | Specifies whether to render only while the thumb is active | false |
| marks | RangeMarks | Show marks under the track | undefined |

## events

| Name | Type| Description |
| --- | --- | --- |
| add | (value: number): void | add event while click the track |

## slots

| Name | Type | Description |
Expand Down
24 changes: 24 additions & 0 deletions playground/src/Count.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
const count = ref(0)
let interval: number
onMounted(() => {
interval = window.setInterval(() => {
count.value++
}, 1000)
})
onUnmounted(() => {
clearInterval(interval)
})
</script>

<template>
<span>{{ count }}</span>
</template>

<style scoped>
</style>
6 changes: 3 additions & 3 deletions playground/src/ranges/01Number.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
const modelNumber = ref<number>(3)
const model = ref<number>(3)
</script>

<template>
Expand All @@ -12,10 +12,10 @@ const modelNumber = ref<number>(3)
<br>
<div class="flex items-baseline">
<span class="label">modelValue</span>
<pre class="value">{{ modelNumber }}</pre>
<pre class="value">{{ model }}</pre>
</div>
<Range
v-model="modelNumber"
v-model="model"
class="w-full py1"
:min="0"
:max="10"
Expand Down
6 changes: 3 additions & 3 deletions playground/src/ranges/02NumberList.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref } from 'vue'
const modelNumberList = ref<number[]>([10, 20])
const model = ref<number[]>([10, 20])
</script>

<template>
Expand All @@ -14,10 +14,10 @@ const modelNumberList = ref<number[]>([10, 20])
</div>
<div class="flex items-baseline">
<span class="label">modelValue</span>
<pre class="value">{{ JSON.stringify(modelNumberList) }}</pre>
<pre class="value">{{ JSON.stringify(model) }}</pre>
</div>
<Range
v-model="modelNumberList"
v-model="model"
class="w-full py1"
range-highlight
size="medium"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<script setup lang="ts">
import { h, ref } from 'vue'
import type { RangeMarks } from 'vue-range-multi'
import Count from '../Count.vue'
const modelNumberListAdd = ref<number[]>([10, 20, 30, 40, 50, 60, 70, 80, 90])
function handleAddNumbers(value: number) {
modelNumberListAdd.value.push(value)
}
const model = ref<number[]>([10, 30, 50, 70, 90])
const marks: RangeMarks = {
15: '15%',
25: { label: '25%', class: 'c-zinc-400' },
Expand All @@ -26,10 +24,10 @@ const marks: RangeMarks = {
</div>
<div class="flex items-baseline">
<span class="label">modelValue</span>
<pre class="value">{{ JSON.stringify(modelNumberListAdd) }}</pre>
<pre class="value">{{ JSON.stringify(model) }}</pre>
</div>
<Range
v-model="modelNumberListAdd"
v-model="model"
class="range-lime w-full pb1 pt8"
:step="5"
addable
Expand All @@ -39,8 +37,7 @@ const marks: RangeMarks = {
thumb-type="square"
thumb-size="large"
:marks="marks"
:render-top="(data) => h('div', { class: 'c-primary' }, data)"
@add="handleAddNumbers"
:render-top="() => h(Count, { class: 'c-primary' })"
/>
</div>
</template>
Expand Down
12 changes: 6 additions & 6 deletions playground/src/ranges/04RangeDataList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
import { h, ref } from 'vue'
import type { RangeData } from 'vue-range-multi'
const modelDataList = ref<RangeData<string>[]>([
const model = ref<RangeData<string>[]>([
{ data: '00:00', value: 10, disabled: true },
{ data: '20:00', value: 40 },
{ data: '59:59', value: 90, unremovable: true },
])
function handleAddData(value: number) {
const date = new Date()
modelDataList.value.push({
return {
data: `${date.getMinutes()}:${date.getSeconds()}`,
value,
})
}
}
</script>

Expand All @@ -29,19 +29,19 @@ function handleAddData(value: number) {
</div>
<div class="flex items-baseline">
<span class="label">modelValue</span>
<pre class="value">{{ JSON.stringify(modelDataList) }}</pre>
<pre class="value">{{ JSON.stringify(model) }}</pre>
</div>
<Range
v-model="modelDataList"
v-model="model"
class="w-full py8"
addable
:add-data="handleAddData"
size="large"
thumb-type="rect"
render-bottom-on-active
:limit="5"
:render-top="(data) => h('div', data.data)"
:render-bottom="(data) => h('div', data.value)"
@add="handleAddData"
/>
</div>
</template>
Expand Down
9 changes: 6 additions & 3 deletions playground/src/ranges/05Vertical.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import { h, ref } from 'vue'
import type { RangeData } from 'vue-range-multi'
const modelDataList = ref<RangeData<string>[]>([
const model = ref<RangeData<string>[]>([
{ data: 'disabled', value: 1, disabled: true },
{ data: 'random1', value: 3 },
{ data: 'unremovable', value: 6, unremovable: true },
])
function handleAddData(value: number) {
modelDataList.value.push({
model.value.push({
data: `random${Math.floor(Math.random() * 10)}`,
value,
})
Expand All @@ -25,7 +25,7 @@ function handleAddData(value: number) {
<span class="tag">slot</span>
</div>
<Range
v-model="modelDataList"
v-model="model"
class="h-60 pl-80 pr8"
:max="10"

Expand All @@ -44,4 +44,7 @@ function handleAddData(value: number) {
</template>

<style scoped>
.m-range {
--c-drop: #80808080
}
</style>
63 changes: 41 additions & 22 deletions src/Range.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const props = withDefaults(defineProps<{
step?: number
vertical?: boolean
addable?: boolean
addData?: (value: number) => RangeData<T, U>
limit?: number
smooth?: boolean
deduplicate?: boolean
Expand All @@ -38,7 +39,6 @@ const props = withDefaults(defineProps<{
const emits = defineEmits<{
(e: 'update:modelValue', modelValue: typeof props.modelValue): void
(e: 'add', value: number): void
}>()
defineSlots<{
Expand Down Expand Up @@ -94,30 +94,41 @@ const stops = computed(() => {
return -1
})
const indexMap = ref<number[]>([])
const indexMap = ref<Record<number, number>>({})
const indexMapReversed = computed(() => Object.fromEntries(Object.entries(indexMap.value).map(([k, v]) => [v, Number.parseInt(k)])))
function sort(val: RangeData<T, U>[]) {
let changed = false
for (let i = val.length; i > 0; i--) {
for (let j = 0; j < i - 1; j++) {
if (val[j].value > val[j + 1].value) {
swap(val, j, j + 1)
swap(indexMap.value, j, j + 1)
swap(indexMap.value, indexMapReversed.value[j], indexMapReversed.value[j + 1])
changed = true
}
}
}
changed && nextTick(() => model.value = val)
}
function getAddableIndex(arr: number[]) {
[...arr].sort()
let i = 0
for (; i < arr.length; i++) {
if (i !== arr[i])
return i
}
return i
}
watch(model, (val) => {
if (val.length > indexMap.value.length) {
for (let i = indexMap.value.length; i < val.length; i++)
indexMap.value.push(i)
const length = Object.keys(indexMap.value).length
if (val.length > length) {
for (let i = length; i < val.length; i++)
indexMap.value[getAddableIndex(Object.keys(indexMap.value).map(key => Number.parseInt(key)))] = i
sort(val)
}
else if (val.length < indexMap.value.length) {
for (let i = indexMap.value.length - 1; i >= val.length; i--) {
const index = indexMap.value.indexOf(i)
index > -1 && indexMap.value.splice(index, 1)
else if (val.length < length) {
for (let i = length - 1; i >= val.length; i--) {
const index = indexMapReversed.value[i]
index > -1 && delete indexMap.value[index]
}
sort(val)
}
Expand All @@ -144,11 +155,11 @@ function setCurrentPercentage(val: number) {
currentPercentage.value = -1
}, 300)
}
const position = computed(() => indexMap.value.map(
(index, idx) => (props.smooth && current.value === idx && currentPercentage.value > -1)
? currentPercentage.value
: getPercentage(model.value[index].value),
))
const position = computed(() => Object.fromEntries(Object.entries(indexMap.value).map(
([idx, index]) => (props.smooth && current.value === Number.parseInt(idx) && currentPercentage.value > -1)
? [idx, currentPercentage.value]
: [idx, getPercentage(model.value[index].value)],
)))
function onUpdate(percentage: number) {
setCurrentPercentage(percentage)
Expand All @@ -164,7 +175,7 @@ function onUpdate(percentage: number) {
const prev = values[i - 1]
if (value < prev) {
swap(modelValue, i, i - 1)
swap(indexMap.value, indexMap.value.indexOf(i), indexMap.value.indexOf(i - 1))
swap(indexMap.value, indexMapReversed.value[i], indexMapReversed.value[i - 1])
index -= 1
}
}
Expand All @@ -174,7 +185,7 @@ function onUpdate(percentage: number) {
const next = values[i + 1]
if (value > next) {
swap(modelValue, i, i + 1)
swap(indexMap.value, indexMap.value.indexOf(i), indexMap.value.indexOf(i + 1))
swap(indexMap.value, indexMapReversed.value[i], indexMapReversed.value[i + 1])
index += 1
}
}
Expand All @@ -188,8 +199,8 @@ function onDelete() {
const modelValue = model.value
modelValue.splice(valueIndex, 1)
model.value = modelValue
indexMap.value.splice(current.value, 1)
indexMap.value = indexMap.value.map(idx => idx >= valueIndex ? idx - 1 : idx)
delete indexMap.value[current.value]
indexMap.value = Object.fromEntries(Object.entries(indexMap.value).map(([idx, index]) => index >= valueIndex ? [idx, index - 1] : [idx, index]))
}
let addTiming = false
Expand All @@ -205,7 +216,15 @@ function addThumb(e: MouseEvent) {
const value = getValue(percent)
if (model.value.some(item => item.value === value))
return
emits('add', value)
const modelValue = model.value
if (modelType.value === 'numberList') {
modelValue.push({ value })
model.value = modelValue
}
else if (modelType.value === 'dataList') {
modelValue.push(props.addData ? props.addData(value) : { value })
model.value = modelValue
}
}
provide(RangeTrackRefKey, trackRef)
Expand All @@ -227,8 +246,8 @@ provide(RangeTrackRefKey, trackRef)
<div
:class="vertical ? 'm-range-v-highlight' : 'm-range-highlight'"
:style="vertical
? { top: `${Math.min(...position)}%`, bottom: `${100 - Math.max(...position)}%` }
: { left: `${Math.min(...position)}%`, right: `${100 - Math.max(...position)}%` }"
? { top: `${Math.min(...Object.values(position))}%`, bottom: `${100 - Math.max(...Object.values(position))}%` }
: { left: `${Math.min(...Object.values(position))}%`, right: `${100 - Math.max(...Object.values(position))}%` }"
/>
</div>
<div v-if="stops > 0" class="m-range-points-area">
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function swap<T>(arr: T[], i: number, j: number): void {
export function swap<T extends object>(arr: T, i: keyof T, j: keyof T): void {
[arr[i], arr[j]] = [arr[j], arr[i]]
}

Expand Down
5 changes: 5 additions & 0 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ describe('swap', () => {
swap(arr, 0, 2)
expect(arr).toEqual([3, 2, 1])
})
it('should swap elements in an object', () => {
const obj = { a: 1, b: 2, c: 3 }
swap(obj, 'a', 'c')
expect(obj).toEqual({ a: 3, b: 2, c: 1 })
})
})

describe('value and percentage conversion', () => {
Expand Down
7 changes: 4 additions & 3 deletions unocss.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export default defineConfig({
// classes
['m-range', 'min-h-1 min-w-1 box-content'],
// theme
['m-range-theme', '[--c-primary:#409EFF] [--c-fill:#E4E7ED] [--c-fill-stop:#F5F5F5] [--c-fill-thumb:#fff]'],
['m-range-theme-dark', '[--c-primary:#3070ED] [--c-fill:#444] [--c-fill-stop:#555] [--c-fill-thumb:#333]'],
['m-range-theme', '[--c-primary:#409EFF] [--c-fill:#E4E7ED] [--c-fill-stop:#F5F5F5] [--c-fill-thumb:#fff] [--c-drop:var(--c-primary)]'],
['m-range-theme-dark', '[--c-primary:#3070ED] [--c-fill:#444] [--c-fill-stop:#555] [--c-fill-thumb:#333] [--c-drop:var(--c-primary)]'],
// track
['m-range-small', 'h2 [--m-range-h:0.5rem]'],
['m-range-medium', 'h4 [--m-range-h:1rem]'],
Expand Down Expand Up @@ -48,7 +48,7 @@ export default defineConfig({
['m-range-thumb-small', 'top--0 bottom--0 [--m-range-thumb-h:0rem]'],
['m-range-thumb-medium', 'top--1 bottom--1 [--m-range-thumb-h:0.25rem]'],
['m-range-thumb-large', 'top--2 bottom--2 [--m-range-thumb-h:0.5rem]'],
['m-range-thumb-active', 'z-1 drop-shadow-[0.1rem_0.15rem_0.25rem_var(--c-primary)]'],
['m-range-thumb-active', 'z-1 drop-shadow-[0.1rem_0.15rem_0.25rem_var(--c-drop)]'],
['m-range-transition-container', 'absolute h-full w0 left-50%'],
['m-range-thumb-top-container', 'absolute left-50% top-0 translate-x--50% translate-y-[calc(-100%_-_4px)]'],
['m-range-thumb-bottom-container', 'absolute left-50% bottom-0 translate-x--50% translate-y-[calc(100%_+_4px)]'],
Expand All @@ -70,6 +70,7 @@ export default defineConfig({
stop: 'var(--c-fill-stop)',
thumb: 'var(--c-fill-thumb)',
},
drop: 'var(--c-drop)',
primary: 'var(--c-primary)',
},
},
Expand Down

0 comments on commit bd529c6

Please sign in to comment.