Skip to content
This repository has been archived by the owner on Jan 8, 2025. It is now read-only.

Commit

Permalink
feat(ui/index-bar): add component index-bar
Browse files Browse the repository at this point in the history
  • Loading branch information
ayangweb committed Feb 27, 2022
1 parent da0d468 commit b6e03b9
Show file tree
Hide file tree
Showing 21 changed files with 1,678 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/varlet-vue2-eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,6 @@ module.exports = {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'vue/valid-v-bind': 'off',
},
}
95 changes: 95 additions & 0 deletions packages/varlet-vue2-ui/src/index-anchor/IndexAnchor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<component
:is="sticky ? 'var-sticky' : 'transition'"
:offset-top="sticky ? stickyOffsetTop : null"
:z-index="sticky ? zIndex : null"
:disabled="disabled && !cssMode"
:css-mode="cssMode"
ref="anchorEl"
>
<div class="var-index-anchor" v-bind="$attrs">
<slot>{{ name }}</slot>
</div>
</component>
</template>

<script>
import VarSticky from '../sticky'
import { defineComponent } from '../utils/create'
import { props } from './props'
import { createChildrenMixin } from '../utils/mixins/relation'
export default defineComponent({
name: 'VarIndexAnchor',
mixins: [
createChildrenMixin('indexBar', { indexKey: 'localIndex', parentKey: 'indexBar', childrenKey: 'indexAnchors' }),
],
components: {
VarSticky,
},
inheritAttrs: false,
props,
data: () => ({
ownTop: 0,
disabled: false,
}),
mounted() {
console.log(this)
},
computed: {
name() {
return this.index
},
active() {
return this.indexBar.active
},
sticky() {
return this.indexBar.sticky
},
cssMode() {
return this.indexBar.cssMode
},
stickyOffsetTop() {
return this.indexBar.stickyOffsetTop
},
zIndex() {
return this.indexBar.zIndex
},
},
methods: {
setOwnTop() {
const {
$refs: { anchorEl },
} = this
if (!anchorEl) return
this.ownTop = anchorEl.$el ? anchorEl.$el.offsetTop : anchorEl.offsetTop
},
setDisabled(value) {
this.disabled = value
},
},
})
</script>

<style lang="less">
@import '../styles/common';
@import '../sticky/sticky';
.var-index-anchor {
position: relative;
}
</style>
13 changes: 13 additions & 0 deletions packages/varlet-vue2-ui/src/index-anchor/docs/zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## API

### 属性

| 参数 | 说明 | 类型 | 默认值 |
| ----- | -------------- | -------- | ---------- |
| `index` | 索引字符 | _number_ \| _string_ | - |

### 插槽

| 名称 | 说明 | 参数 |
| --- | --- | --- |
| `default` | 自定义索引字符 | - |
10 changes: 10 additions & 0 deletions packages/varlet-vue2-ui/src/index-anchor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { VueConstructor } from 'vue'
import IndexAnchor from './IndexAnchor.vue'

IndexAnchor.install = function (app: VueConstructor) {
app.component(IndexAnchor.name, IndexAnchor)
}

export const _IndexAnchorComponent = IndexAnchor

export default IndexAnchor
5 changes: 5 additions & 0 deletions packages/varlet-vue2-ui/src/index-anchor/props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const props = {
index: {
type: [Number, String],
},
}
137 changes: 137 additions & 0 deletions packages/varlet-vue2-ui/src/index-bar/IndexBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<template>
<div class="var-index-bar" ref="barEl">
<slot />
<ul
class="var-index-bar__anchor-list"
:style="{ zIndex: toNumber(zIndex) + 2, display: hideList ? 'none' : 'block' }"
>
<li
v-for="anchorName in anchorNameList"
:key="anchorName"
class="var-index-bar__anchor-item"
:class="{ 'var-index-bar__anchor-item--active': active === anchorName }"
:style="{ color: active === anchorName && highlightColor ? highlightColor : '' }"
@click="anchorClick(anchorName)"
>
{{ anchorName }}
</li>
</ul>
</div>
</template>

<script>
import { defineComponent } from '../utils/create'
import { easeInOutCubic, isPlainObject, toNumber } from '../utils/shared'
import {
doubleRaf,
getParentScroller,
getScrollLeft,
nextTickFrame,
requestAnimationFrame,
scrollTo,
} from '../utils/elements'
import { props } from './props'
import { createParentMixin } from '../utils/mixins/relation'
export default defineComponent({
name: 'VarIndexBar',
mixins: [createParentMixin('indexBar', { childrenKey: 'indexAnchors' })],
props,
data: () => ({
scrollEl: null,
clickedName: '',
scroller: null,
anchorNameList: [],
active: null,
}),
async mounted() {
await doubleRaf()
this.scroller = getParentScroller(this.$refs.barEl)
this.scrollEl = this.scroller === window ? this.scroller.document.documentElement : this.scroller
this.scroller?.addEventListener('scroll', this.handleScroll)
},
beforeDestroy() {
this.scroller?.removeEventListener('scroll', this.handleScroll)
},
watch: {
async indexAnchors(newValue) {
await doubleRaf()
newValue.forEach(({ name, setOwnTop }) => {
if (name) this.anchorNameList.push(name)
setOwnTop()
})
},
},
methods: {
// expose
scrollTo(index) {
requestAnimationFrame(() => this.anchorClick(index, true))
},
toNumber,
emitEvent(anchor) {
const anchorName = isPlainObject(anchor) ? anchor.name : anchor
if (anchorName === this.active || anchorName === undefined) return
this.active = anchorName
this.getListeners().onChange?.(anchorName)
},
handleScroll() {
const { scrollTop, scrollHeight } = this.scrollEl
const { offsetTop } = this.$refs.barEl
this.indexAnchors.forEach((anchor, index) => {
const anchorTop = anchor.ownTop
const top = scrollTop - anchorTop + this.stickyOffsetTop - offsetTop
const distance =
index === this.indexAnchors.length - 1 ? scrollHeight : this.indexAnchors[index + 1].ownTop - anchor.ownTop
if (top >= 0 && top < distance && !this.clickedName) {
if (index && !this.cssMode) {
this.indexAnchors[index - 1].setDisabled(true)
}
anchor.setDisabled(false)
this.emitEvent(anchor)
}
})
},
async anchorClick(anchorName, manualCall) {
if (manualCall) this.getListeners().onClick?.(anchorName)
if (anchorName === this.active) return
const indexAnchor = this.indexAnchors.find(({ name }) => anchorName === name)
if (!indexAnchor) return
const top = indexAnchor.ownTop - this.stickyOffsetTop + this.$refs.barEl.offsetTop
const left = getScrollLeft(this.scrollEl)
this.clickedName = anchorName
this.emitEvent(anchorName)
await scrollTo(this.scrollEl, {
left,
top,
animation: easeInOutCubic,
duration: toNumber(this.duration),
})
nextTickFrame(() => {
this.clickedName = ''
})
},
},
})
</script>
<style lang="less">
@import '../styles/common';
@import './indexBar';
</style>
Loading

0 comments on commit b6e03b9

Please sign in to comment.