Commit 43513f702cd573f8078ca582bc9ac264e707028e
1 parent
eeeceb54
add anchor component
Showing
13 changed files
with
434 additions
and
4 deletions
Show diff stats
examples/app.vue
| @@ -18,6 +18,7 @@ nav { | @@ -18,6 +18,7 @@ nav { | ||
| 18 | <ul> | 18 | <ul> |
| 19 | <li><router-link to="/layout">Layout</router-link></li> | 19 | <li><router-link to="/layout">Layout</router-link></li> |
| 20 | <li><router-link to="/affix">Affix</router-link></li> | 20 | <li><router-link to="/affix">Affix</router-link></li> |
| 21 | + <li><router-link to="/anchor">Anchor</router-link></li> | ||
| 21 | <li><router-link to="/grid">Grid</router-link></li> | 22 | <li><router-link to="/grid">Grid</router-link></li> |
| 22 | <li><router-link to="/button">Button</router-link></li> | 23 | <li><router-link to="/button">Button</router-link></li> |
| 23 | <li><router-link to="/input">Input</router-link></li> | 24 | <li><router-link to="/input">Input</router-link></li> |
examples/main.js
| @@ -28,6 +28,10 @@ const router = new VueRouter({ | @@ -28,6 +28,10 @@ const router = new VueRouter({ | ||
| 28 | component: (resolve) => require(['./routers/affix.vue'], resolve) | 28 | component: (resolve) => require(['./routers/affix.vue'], resolve) |
| 29 | }, | 29 | }, |
| 30 | { | 30 | { |
| 31 | + path: '/anchor', | ||
| 32 | + component: (resolve) => require(['./routers/anchor.vue'], resolve) | ||
| 33 | + }, | ||
| 34 | + { | ||
| 31 | path: '/grid', | 35 | path: '/grid', |
| 32 | component: (resolve) => require(['./routers/grid.vue'], resolve) | 36 | component: (resolve) => require(['./routers/grid.vue'], resolve) |
| 33 | }, | 37 | }, |
| 1 | +<template> | ||
| 2 | + <div class="anchor-wrapper"> | ||
| 3 | + <div class="link-wrapper"> | ||
| 4 | + <Button @click="changeCon">修改为Window</Button> | ||
| 5 | + <Anchor @on-change="handleChange" @on-select="handleSelect" :style="{right: '100px'}" :affix="true" :offset-top="30" :container="scrollCon" show-ink-in-fixed> | ||
| 6 | + <AnchorLink v-if="(link - 1) % 30 === 0" v-for="link in 300" :key="`link${link}`" :href="`#title-${link}`" :title="`title-${link}`"> | ||
| 7 | + <AnchorLink v-if="link === 61" href="#title-child-69" title="title-child-69"/> | ||
| 8 | + </AnchorLink> | ||
| 9 | + </Anchor> | ||
| 10 | + </div> | ||
| 11 | + <div v-if="con === 'div'" ref="listWrapper" id="listWrapper" class="list-wrapper"> | ||
| 12 | + <div style="height: 100px;"></div> | ||
| 13 | + <template v-for="i in 300"> | ||
| 14 | + <h1 v-if="(i - 1) % 30 === 0" :key="`h1${i}`" :id="`title-${i}`">{{ `title-${i}` }}</h1> | ||
| 15 | + <h1 v-if="i === 69" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
| 16 | + <h1 v-if="i === 75" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
| 17 | + <p v-else :key="`p${i}`">{{ `content-row-index-${i}` }}</p> | ||
| 18 | + <Collapse v-if="i === 3" v-model="value1" :key="`collapse-${i}`"> | ||
| 19 | + <Panel name="1"> | ||
| 20 | + 史蒂夫·乔布斯 | ||
| 21 | + <p v-for="index in 50" :key="`ppp-${index}`" slot="content">{{ index }}</p> | ||
| 22 | + </Panel> | ||
| 23 | + <Panel name="2"> | ||
| 24 | + 斯蒂夫·盖瑞·沃兹尼亚克 | ||
| 25 | + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p> | ||
| 26 | + </Panel> | ||
| 27 | + <Panel name="3"> | ||
| 28 | + 乔纳森·伊夫 | ||
| 29 | + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p> | ||
| 30 | + </Panel> | ||
| 31 | + </Collapse> | ||
| 32 | + </template> | ||
| 33 | + </div> | ||
| 34 | + <div v-else> | ||
| 35 | + <template v-for="i in 300"> | ||
| 36 | + <h1 v-if="(i - 1) % 30 === 0" :key="`h1${i}`" :id="`title-${i}`">{{ `title-${i}` }}</h1> | ||
| 37 | + <h1 v-if="i === 69" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
| 38 | + <h1 v-if="i === 75" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
| 39 | + <p v-else :key="`p${i}`">{{ `content-row-index-${i}` }}</p> | ||
| 40 | + <Collapse v-if="i === 3" v-model="value1" :key="`collapse-${i}`"> | ||
| 41 | + <Panel name="1"> | ||
| 42 | + 史蒂夫·乔布斯 | ||
| 43 | + <p v-for="index in 50" :key="`ppp-${index}`" slot="content">{{ index }}</p> | ||
| 44 | + </Panel> | ||
| 45 | + <Panel name="2"> | ||
| 46 | + 斯蒂夫·盖瑞·沃兹尼亚克 | ||
| 47 | + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p> | ||
| 48 | + </Panel> | ||
| 49 | + <Panel name="3"> | ||
| 50 | + 乔纳森·伊夫 | ||
| 51 | + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p> | ||
| 52 | + </Panel> | ||
| 53 | + </Collapse> | ||
| 54 | + </template> | ||
| 55 | + </div> | ||
| 56 | + | ||
| 57 | + </div> | ||
| 58 | +</template> | ||
| 59 | +<script> | ||
| 60 | +export default { | ||
| 61 | + data () { | ||
| 62 | + return { | ||
| 63 | + container: null, | ||
| 64 | + value1: '1', | ||
| 65 | + scrollCon: '', | ||
| 66 | + con: 'div' | ||
| 67 | + } | ||
| 68 | + }, | ||
| 69 | + methods: { | ||
| 70 | + changeCon () { | ||
| 71 | + this.con = 'window'; | ||
| 72 | + this.scrollCon = undefined; | ||
| 73 | + }, | ||
| 74 | + handleChange (newHref, oldHref) { | ||
| 75 | + console.log(`${oldHref} => ${newHref}`) | ||
| 76 | + }, | ||
| 77 | + handleSelect (href) { | ||
| 78 | + console.log(`select ${href}`) | ||
| 79 | + } | ||
| 80 | + }, | ||
| 81 | + mounted () { | ||
| 82 | + this.scrollCon = this.$refs.listWrapper | ||
| 83 | + } | ||
| 84 | +} | ||
| 85 | +</script> | ||
| 86 | +<style lang="less"> | ||
| 87 | +.anchor-wrapper{ | ||
| 88 | + .link-wrapper{ | ||
| 89 | + position: absolute; | ||
| 90 | + top: 200px; | ||
| 91 | + right: 100px; | ||
| 92 | + width: 200px; | ||
| 93 | + } | ||
| 94 | + .list-wrapper{ | ||
| 95 | + height: 600px; | ||
| 96 | + overflow: auto; | ||
| 97 | + } | ||
| 98 | +} | ||
| 99 | +</style> |
| 1 | +<template> | ||
| 2 | + <div :class="anchorLinkClasses"> | ||
| 3 | + <a :class="linkTitleClasses" href="javascript:void(0)" :data-href="href" @click="goAnchor" :title="title">{{ title }}</a> | ||
| 4 | + <slot></slot> | ||
| 5 | + </div> | ||
| 6 | +</template> | ||
| 7 | +<script> | ||
| 8 | +import { findComponentUpward } from '../../utils/assist'; | ||
| 9 | +export default { | ||
| 10 | + name: 'AnchorLink', | ||
| 11 | + props: { | ||
| 12 | + href: String, | ||
| 13 | + title: String | ||
| 14 | + }, | ||
| 15 | + data () { | ||
| 16 | + return { | ||
| 17 | + prefix: 'ivu-anchor-link' | ||
| 18 | + }; | ||
| 19 | + }, | ||
| 20 | + computed: { | ||
| 21 | + anchorLinkClasses () { | ||
| 22 | + return [ | ||
| 23 | + this.prefix, | ||
| 24 | + this.currentLink === this.href ? `${this.prefix}-active` : '' | ||
| 25 | + ]; | ||
| 26 | + }, | ||
| 27 | + linkTitleClasses () { | ||
| 28 | + return [ | ||
| 29 | + `${this.prefix}-title` | ||
| 30 | + ]; | ||
| 31 | + }, | ||
| 32 | + parentAnchor () { | ||
| 33 | + return findComponentUpward(this, 'Anchor'); | ||
| 34 | + }, | ||
| 35 | + currentLink () { | ||
| 36 | + return this.parentAnchor.currentLink; | ||
| 37 | + } | ||
| 38 | + }, | ||
| 39 | + methods: { | ||
| 40 | + goAnchor () { | ||
| 41 | + this.parentAnchor.turnTo(this.href); | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | +}; | ||
| 45 | +</script> |
| 1 | +<template> | ||
| 2 | + <component :is="wrapperComponent" :offset-top="offsetTop" :offset-bottom="offsetBottom" @on-change="handleAffixStateChange"> | ||
| 3 | + <div :class="`${prefix}-wrapper`" :style="wrapperStyle"> | ||
| 4 | + <div :class="`${prefix}`"> | ||
| 5 | + <div :class="`${prefix}-ink`"> | ||
| 6 | + <span v-show="showInkBall" :class="`${prefix}-ink-ball`" :style="{top: `${inkTop}px`}"></span> | ||
| 7 | + </div> | ||
| 8 | + <slot></slot> | ||
| 9 | + </div> | ||
| 10 | + </div> | ||
| 11 | + </component> | ||
| 12 | +</template> | ||
| 13 | +<script> | ||
| 14 | +import { scrollTop, findComponentDownward, findComponentsDownward, sharpMatcherRegx } from '../../utils/assist'; | ||
| 15 | +import { on, off } from '../../utils/dom'; | ||
| 16 | +export default { | ||
| 17 | + name: 'Anchor', | ||
| 18 | + data () { | ||
| 19 | + return { | ||
| 20 | + prefix: 'ivu-anchor', | ||
| 21 | + isAffixed: false, // current affixed state | ||
| 22 | + inkTop: 0, | ||
| 23 | + linkHeight: 0, | ||
| 24 | + animating: false, // if is scrolling now | ||
| 25 | + currentLink: '', // current show link => #href -> currentLink = #href | ||
| 26 | + currentId: '', // current show title id => #href -> currentId = href | ||
| 27 | + scrollContainer: null, | ||
| 28 | + scrollElement: null, | ||
| 29 | + titlesOffsetArr: [], | ||
| 30 | + wrapperTop: 0, | ||
| 31 | + upperFirstTitle: true | ||
| 32 | + }; | ||
| 33 | + }, | ||
| 34 | + props: { | ||
| 35 | + affix: { | ||
| 36 | + type: Boolean, | ||
| 37 | + default: true | ||
| 38 | + }, | ||
| 39 | + offsetTop: { | ||
| 40 | + type: Number, | ||
| 41 | + default: 0 | ||
| 42 | + }, | ||
| 43 | + offsetBottom: Number, | ||
| 44 | + bounds: { | ||
| 45 | + type: Number, | ||
| 46 | + default: 5 | ||
| 47 | + }, | ||
| 48 | + container: [String, HTMLElement], | ||
| 49 | + showInkInFixed: { | ||
| 50 | + type: Boolean, | ||
| 51 | + default: false | ||
| 52 | + } | ||
| 53 | + }, | ||
| 54 | + computed: { | ||
| 55 | + wrapperComponent () { | ||
| 56 | + return this.affix ? 'Affix' : 'div'; | ||
| 57 | + }, | ||
| 58 | + wrapperStyle () { | ||
| 59 | + return { | ||
| 60 | + maxHeight: this.offsetTop ? `calc(100vh - ${this.offsetTop}px)` : '100vh' | ||
| 61 | + }; | ||
| 62 | + }, | ||
| 63 | + containerIsWindow () { | ||
| 64 | + return this.scrollContainer === window; | ||
| 65 | + }, | ||
| 66 | + showInkBall () { | ||
| 67 | + return this.showInkInFixed && (this.isAffixed || (!this.isAffixed && !this.upperFirstTitle && this.scrollContainer !== window)); | ||
| 68 | + } | ||
| 69 | + }, | ||
| 70 | + methods: { | ||
| 71 | + handleAffixStateChange (state) { | ||
| 72 | + this.isAffixed = this.affix && state; | ||
| 73 | + }, | ||
| 74 | + handleScroll (e) { | ||
| 75 | + this.upperFirstTitle = e.target.scrollTop < this.titlesOffsetArr[0].offset; | ||
| 76 | + if (this.animating) return; | ||
| 77 | + this.updateTitleOffset(); | ||
| 78 | + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop || e.target.scrollTop; | ||
| 79 | + this.getCurrentScrollAtTitleId(scrollTop); | ||
| 80 | + }, | ||
| 81 | + turnTo (href) { | ||
| 82 | + const oldHref = this.currentLink; | ||
| 83 | + this.currentLink = href; | ||
| 84 | + this.$router.push({ | ||
| 85 | + path: href | ||
| 86 | + }); | ||
| 87 | + this.$emit('on-select', href); | ||
| 88 | + }, | ||
| 89 | + handleHashChange () { | ||
| 90 | + const url = window.location.href; | ||
| 91 | + const sharpLinkMatch = sharpMatcherRegx.exec(url); | ||
| 92 | + this.currentLink = sharpLinkMatch[0]; | ||
| 93 | + this.currentId = sharpLinkMatch[1]; | ||
| 94 | + }, | ||
| 95 | + handleScrollTo () { | ||
| 96 | + const anchor = document.getElementById(this.currentId); | ||
| 97 | + if (!anchor) return; | ||
| 98 | + const offsetTop = anchor.offsetTop - this.wrapperTop; | ||
| 99 | + this.animating = true; | ||
| 100 | + scrollTop(this.scrollContainer, this.scrollElement.scrollTop, offsetTop, 600, () => { | ||
| 101 | + this.animating = false; | ||
| 102 | + }); | ||
| 103 | + this.handleSetInkTop(); | ||
| 104 | + }, | ||
| 105 | + handleSetInkTop () { | ||
| 106 | + const currentLinkElementA = document.querySelector(`a[data-href="${this.currentLink}"]`); | ||
| 107 | + if (!currentLinkElementA) return; | ||
| 108 | + const elementATop = currentLinkElementA.offsetTop; | ||
| 109 | + const top = (elementATop < 0 ? this.offsetTop : elementATop); | ||
| 110 | + this.inkTop = top; | ||
| 111 | + }, | ||
| 112 | + updateTitleOffset () { | ||
| 113 | + const links = findComponentsDownward(this, 'AnchorLink').map(link => { | ||
| 114 | + return link.href; | ||
| 115 | + }); | ||
| 116 | + const offsetArr = links.map(link => { | ||
| 117 | + const id = link.split('#')[1]; | ||
| 118 | + const titleEle = document.getElementById(id); | ||
| 119 | + return { | ||
| 120 | + link: link, | ||
| 121 | + offset: titleEle.offsetTop - this.scrollElement.offsetTop | ||
| 122 | + }; | ||
| 123 | + }); | ||
| 124 | + this.titlesOffsetArr = offsetArr; | ||
| 125 | + }, | ||
| 126 | + getCurrentScrollAtTitleId (scrollTop) { | ||
| 127 | + let i = -1; | ||
| 128 | + let len = this.titlesOffsetArr.length; | ||
| 129 | + let titleItem = { | ||
| 130 | + link: '#', | ||
| 131 | + offset: 0 | ||
| 132 | + }; | ||
| 133 | + scrollTop += this.bounds; | ||
| 134 | + while (++i < len) { | ||
| 135 | + let currentEle = this.titlesOffsetArr[i]; | ||
| 136 | + let nextEle = this.titlesOffsetArr[i + 1]; | ||
| 137 | + if (scrollTop >= currentEle.offset && scrollTop < ((nextEle && nextEle.offset) || Infinity)) { | ||
| 138 | + titleItem = this.titlesOffsetArr[i]; | ||
| 139 | + break; | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + this.currentLink = titleItem.link; | ||
| 143 | + this.handleSetInkTop(); | ||
| 144 | + }, | ||
| 145 | + getContainer () { | ||
| 146 | + this.scrollContainer = this.container ? (typeof this.container === 'string' ? document.querySelector(this.container) : this.container) : window; | ||
| 147 | + this.scrollElement = this.container ? this.scrollContainer : (document.documentElement || document.body); | ||
| 148 | + }, | ||
| 149 | + removeListener () { | ||
| 150 | + off(this.scrollContainer, 'scroll', this.handleScroll); | ||
| 151 | + off(window, 'hashchange', this.handleHashChange); | ||
| 152 | + }, | ||
| 153 | + init () { | ||
| 154 | + const anchorLink = findComponentDownward(this, 'AnchorLink'); | ||
| 155 | + this.linkHeight = anchorLink ? anchorLink.$el.getBoundingClientRect().height : 0; | ||
| 156 | + this.handleHashChange(); | ||
| 157 | + this.$nextTick(() => { | ||
| 158 | + this.removeListener(); | ||
| 159 | + this.getContainer(); | ||
| 160 | + this.wrapperTop = this.containerIsWindow ? 0 : this.scrollElement.offsetTop; | ||
| 161 | + this.handleScrollTo(); | ||
| 162 | + this.handleSetInkTop(); | ||
| 163 | + this.updateTitleOffset(); | ||
| 164 | + this.upperFirstTitle = this.scrollElement.scrollTop < this.titlesOffsetArr[0].offset; | ||
| 165 | + on(this.scrollContainer, 'scroll', this.handleScroll); | ||
| 166 | + on(window, 'hashchange', this.handleHashChange); | ||
| 167 | + }); | ||
| 168 | + } | ||
| 169 | + }, | ||
| 170 | + watch: { | ||
| 171 | + '$route' () { | ||
| 172 | + this.handleHashChange(); | ||
| 173 | + this.handleScrollTo(); | ||
| 174 | + }, | ||
| 175 | + container () { | ||
| 176 | + this.init(); | ||
| 177 | + }, | ||
| 178 | + currentLink (newHref, oldHref) { | ||
| 179 | + this.$emit('on-change', newHref, oldHref); | ||
| 180 | + } | ||
| 181 | + }, | ||
| 182 | + mounted () { | ||
| 183 | + this.init() | ||
| 184 | + }, | ||
| 185 | + beforeDestroy () { | ||
| 186 | + this.removeListener(); | ||
| 187 | + } | ||
| 188 | +}; | ||
| 189 | +</script> |
src/index.js
| 1 | import Affix from './components/affix'; | 1 | import Affix from './components/affix'; |
| 2 | import Alert from './components/alert'; | 2 | import Alert from './components/alert'; |
| 3 | +import Anchor from './components/anchor'; | ||
| 4 | +import AnchorLink from './components/anchor-link'; | ||
| 3 | import AutoComplete from './components/auto-complete'; | 5 | import AutoComplete from './components/auto-complete'; |
| 4 | import Avatar from './components/avatar'; | 6 | import Avatar from './components/avatar'; |
| 5 | import BackTop from './components/back-top'; | 7 | import BackTop from './components/back-top'; |
| @@ -55,6 +57,8 @@ import locale from './locale/index'; | @@ -55,6 +57,8 @@ import locale from './locale/index'; | ||
| 55 | const components = { | 57 | const components = { |
| 56 | Affix, | 58 | Affix, |
| 57 | Alert, | 59 | Alert, |
| 60 | + Anchor, | ||
| 61 | + AnchorLink, | ||
| 58 | AutoComplete, | 62 | AutoComplete, |
| 59 | Avatar, | 63 | Avatar, |
| 60 | BackTop, | 64 | BackTop, |
| 1 | +@anchor-prefix: ~"@{css-prefix}anchor"; | ||
| 2 | + | ||
| 3 | +.@{anchor-prefix}{ | ||
| 4 | + &-wrapper{ | ||
| 5 | + background-color: @body-background; | ||
| 6 | + overflow: auto; | ||
| 7 | + padding-left: 4px; | ||
| 8 | + margin-left: -4px; | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + &{ | ||
| 12 | + position: relative; | ||
| 13 | + padding-left: @anchor-border-width; | ||
| 14 | + | ||
| 15 | + &-ink { | ||
| 16 | + position: absolute; | ||
| 17 | + height: 100%; | ||
| 18 | + left: 0; | ||
| 19 | + top: 0; | ||
| 20 | + &:before { | ||
| 21 | + content: ' '; | ||
| 22 | + position: relative; | ||
| 23 | + width: @anchor-border-width; | ||
| 24 | + height: 100%; | ||
| 25 | + display: block; | ||
| 26 | + background-color: @border-color-split; | ||
| 27 | + margin: 0 auto; | ||
| 28 | + } | ||
| 29 | + &-ball { | ||
| 30 | + display: inline-block; | ||
| 31 | + position: absolute; | ||
| 32 | + width: 8px; | ||
| 33 | + height: 8px; | ||
| 34 | + border-radius: 8px; | ||
| 35 | + border: 2px solid @primary-color; | ||
| 36 | + background-color: @body-background; | ||
| 37 | + left: 50%; | ||
| 38 | + transition: top .3s ease-in-out; | ||
| 39 | + transform: translate(-50%, 2px); | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + &.fixed &-ink &-ink-ball { | ||
| 44 | + display: none; | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + &-link { | ||
| 49 | + padding: 8px 0 8px 16px; | ||
| 50 | + line-height: 1; | ||
| 51 | + | ||
| 52 | + &-title { | ||
| 53 | + display: block; | ||
| 54 | + position: relative; | ||
| 55 | + transition: all .3s; | ||
| 56 | + color: @text-color; | ||
| 57 | + white-space: nowrap; | ||
| 58 | + overflow: hidden; | ||
| 59 | + text-overflow: ellipsis; | ||
| 60 | + margin-bottom: 8px; | ||
| 61 | + &:only-child { | ||
| 62 | + margin-bottom: 0; | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + &-active > &-title { | ||
| 67 | + color: @primary-color; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + &-link &-link { | ||
| 72 | + padding-top: 6px; | ||
| 73 | + padding-bottom: 6px; | ||
| 74 | + } | ||
| 75 | +} |
src/styles/components/index.less
src/styles/custom.less
| @@ -184,4 +184,7 @@ | @@ -184,4 +184,7 @@ | ||
| 184 | @avatar-font-size-sm: 14px; | 184 | @avatar-font-size-sm: 14px; |
| 185 | @avatar-bg: #ccc; | 185 | @avatar-bg: #ccc; |
| 186 | @avatar-color: #fff; | 186 | @avatar-color: #fff; |
| 187 | -@avatar-border-radius: @border-radius-small; | ||
| 188 | \ No newline at end of file | 187 | \ No newline at end of file |
| 188 | +@avatar-border-radius: @border-radius-small; | ||
| 189 | + | ||
| 190 | +// Anchor | ||
| 191 | +@anchor-border-width: 2px; |
src/utils/assist.js
| @@ -138,7 +138,7 @@ function deepCopy(data) { | @@ -138,7 +138,7 @@ function deepCopy(data) { | ||
| 138 | export {deepCopy}; | 138 | export {deepCopy}; |
| 139 | 139 | ||
| 140 | // scrollTop animation | 140 | // scrollTop animation |
| 141 | -export function scrollTop(el, from = 0, to, duration = 500) { | 141 | +export function scrollTop(el, from = 0, to, duration = 500, endCallback) { |
| 142 | if (!window.requestAnimationFrame) { | 142 | if (!window.requestAnimationFrame) { |
| 143 | window.requestAnimationFrame = ( | 143 | window.requestAnimationFrame = ( |
| 144 | window.webkitRequestAnimationFrame || | 144 | window.webkitRequestAnimationFrame || |
| @@ -153,7 +153,10 @@ export function scrollTop(el, from = 0, to, duration = 500) { | @@ -153,7 +153,10 @@ export function scrollTop(el, from = 0, to, duration = 500) { | ||
| 153 | const step = Math.ceil(difference / duration * 50); | 153 | const step = Math.ceil(difference / duration * 50); |
| 154 | 154 | ||
| 155 | function scroll(start, end, step) { | 155 | function scroll(start, end, step) { |
| 156 | - if (start === end) return; | 156 | + if (start === end) { |
| 157 | + endCallback && endCallback(); | ||
| 158 | + return; | ||
| 159 | + } | ||
| 157 | 160 | ||
| 158 | let d = (start + step > end) ? end : start + step; | 161 | let d = (start + step > end) ? end : start + step; |
| 159 | if (start > end) { | 162 | if (start > end) { |
| @@ -322,3 +325,5 @@ export function setMatchMedia () { | @@ -322,3 +325,5 @@ export function setMatchMedia () { | ||
| 322 | window.matchMedia = window.matchMedia || matchMediaPolyfill; | 325 | window.matchMedia = window.matchMedia || matchMediaPolyfill; |
| 323 | } | 326 | } |
| 324 | } | 327 | } |
| 328 | + | ||
| 329 | +export const sharpMatcherRegx = /#([^#]+)$/; |