Commit 1953251a23b82a5ff4302d8cfb54b0f591cd09d2
Committed by
GitHub
Merge pull request #3369 from lison16/anchor
add anchor component
Showing
13 changed files
with
452 additions
and
5 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 | + <Button @click="andLink">添加一个连接</Button> | ||
6 | + <Anchor @on-change="handleChange" @on-select="handleSelect" :style="{right: '100px'}" :affix="true" :offset-top="30" :container="scrollCon" show-ink-in-fixed> | ||
7 | + <AnchorLink v-if="(link - 1) % 30 === 0" v-for="link in 300" :key="`link${link}`" :href="`#title-${link}`" :title="`title-${link}`"> | ||
8 | + <AnchorLink v-if="link === 61" href="#title-child-69" title="title-child-69"/> | ||
9 | + </AnchorLink> | ||
10 | + <AnchorLink v-if="showNewLink" href="#new-link" title="这是动态添加的连接"/> | ||
11 | + </Anchor> | ||
12 | + </div> | ||
13 | + <div v-if="con === 'div'" ref="listWrapper" id="listWrapper" class="list-wrapper"> | ||
14 | + <div style="height: 100px;"></div> | ||
15 | + <template v-for="i in 300"> | ||
16 | + <h1 v-if="(i - 1) % 30 === 0" :key="`h1${i}`" :id="`title-${i}`">{{ `title-${i}` }}</h1> | ||
17 | + <h1 v-if="i === 69" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
18 | + <h1 v-if="i === 75" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
19 | + <p v-else :key="`p${i}`">{{ `content-row-index-${i}` }}</p> | ||
20 | + <Collapse v-if="i === 3" v-model="value1" :key="`collapse-${i}`"> | ||
21 | + <Panel name="1"> | ||
22 | + 史蒂夫·乔布斯 | ||
23 | + <p v-for="index in 50" :key="`ppp-${index}`" slot="content">{{ index }}</p> | ||
24 | + </Panel> | ||
25 | + <Panel name="2"> | ||
26 | + 斯蒂夫·盖瑞·沃兹尼亚克 | ||
27 | + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p> | ||
28 | + </Panel> | ||
29 | + <Panel name="3"> | ||
30 | + 乔纳森·伊夫 | ||
31 | + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p> | ||
32 | + </Panel> | ||
33 | + </Collapse> | ||
34 | + </template> | ||
35 | + <!-- <h1 id="new-link">这是新添加的哦哦哦哦哦 哦 </h1> | ||
36 | + <p v-for="i in 50" :key="`new-${i}`">这是信息司大是大非胜多负少的{{i}}</p> --> | ||
37 | + </div> | ||
38 | + <div v-else> | ||
39 | + <template v-for="i in 300"> | ||
40 | + <h1 v-if="(i - 1) % 30 === 0" :key="`h1${i}`" :id="`title-${i}`">{{ `title-${i}` }}</h1> | ||
41 | + <h1 v-if="i === 69" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
42 | + <h1 v-if="i === 75" :key="`h1${i}`" :id="`title-child-${i}`">{{ `title-${i}` }}</h1> | ||
43 | + <p v-else :key="`p${i}`">{{ `content-row-index-${i}` }}</p> | ||
44 | + <Collapse v-if="i === 3" v-model="value1" :key="`collapse-${i}`"> | ||
45 | + <Panel name="1"> | ||
46 | + 史蒂夫·乔布斯 | ||
47 | + <p v-for="index in 50" :key="`ppp-${index}`" slot="content">{{ index }}</p> | ||
48 | + </Panel> | ||
49 | + <Panel name="2"> | ||
50 | + 斯蒂夫·盖瑞·沃兹尼亚克 | ||
51 | + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p> | ||
52 | + </Panel> | ||
53 | + <Panel name="3"> | ||
54 | + 乔纳森·伊夫 | ||
55 | + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p> | ||
56 | + </Panel> | ||
57 | + </Collapse> | ||
58 | + </template> | ||
59 | + <h1 id="new-link">这是新添加的哦哦哦哦哦 哦 </h1> | ||
60 | + <p v-for="i in 50" :key="`new-${i}`">这是信息司大是大非胜多负少的{{i}}</p> | ||
61 | + </div> | ||
62 | + | ||
63 | + </div> | ||
64 | +</template> | ||
65 | +<script> | ||
66 | +export default { | ||
67 | + data () { | ||
68 | + return { | ||
69 | + container: null, | ||
70 | + value1: '1', | ||
71 | + scrollCon: '', | ||
72 | + con: 'div', | ||
73 | + showNewLink: false | ||
74 | + } | ||
75 | + }, | ||
76 | + methods: { | ||
77 | + changeCon () { | ||
78 | + this.con = 'window'; | ||
79 | + this.scrollCon = undefined; | ||
80 | + }, | ||
81 | + handleChange (newHref, oldHref) { | ||
82 | + console.log(`${oldHref} => ${newHref}`) | ||
83 | + }, | ||
84 | + handleSelect (href) { | ||
85 | + console.log(`select ${href}`) | ||
86 | + }, | ||
87 | + andLink () { | ||
88 | + this.showNewLink = true; | ||
89 | + } | ||
90 | + }, | ||
91 | + mounted () { | ||
92 | + this.scrollCon = this.$refs.listWrapper | ||
93 | + } | ||
94 | +} | ||
95 | +</script> | ||
96 | +<style lang="less"> | ||
97 | +.anchor-wrapper{ | ||
98 | + .link-wrapper{ | ||
99 | + position: absolute; | ||
100 | + top: 200px; | ||
101 | + right: 100px; | ||
102 | + width: 200px; | ||
103 | + } | ||
104 | + .list-wrapper{ | ||
105 | + height: 600px; | ||
106 | + overflow: auto; | ||
107 | + } | ||
108 | +} | ||
109 | +</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 | + mounted () { | ||
45 | + this.$nextTick(() => { | ||
46 | + this.parentAnchor.init(); | ||
47 | + }); | ||
48 | + } | ||
49 | +}; | ||
50 | +</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 | + this.currentLink = href; | ||
83 | + this.$router.push({ | ||
84 | + path: href | ||
85 | + }); | ||
86 | + this.$emit('on-select', href); | ||
87 | + }, | ||
88 | + handleHashChange () { | ||
89 | + const url = window.location.href; | ||
90 | + const sharpLinkMatch = sharpMatcherRegx.exec(url); | ||
91 | + this.currentLink = sharpLinkMatch[0]; | ||
92 | + this.currentId = sharpLinkMatch[1]; | ||
93 | + }, | ||
94 | + handleScrollTo () { | ||
95 | + const anchor = document.getElementById(this.currentId); | ||
96 | + if (!anchor) return; | ||
97 | + const offsetTop = anchor.offsetTop - this.wrapperTop; | ||
98 | + this.animating = true; | ||
99 | + scrollTop(this.scrollContainer, this.scrollElement.scrollTop, offsetTop, 600, () => { | ||
100 | + this.animating = false; | ||
101 | + }); | ||
102 | + this.handleSetInkTop(); | ||
103 | + }, | ||
104 | + handleSetInkTop () { | ||
105 | + const currentLinkElementA = document.querySelector(`a[data-href="${this.currentLink}"]`); | ||
106 | + if (!currentLinkElementA) return; | ||
107 | + const elementATop = currentLinkElementA.offsetTop; | ||
108 | + const top = (elementATop < 0 ? this.offsetTop : elementATop); | ||
109 | + this.inkTop = top; | ||
110 | + }, | ||
111 | + updateTitleOffset () { | ||
112 | + const links = findComponentsDownward(this, 'AnchorLink').map(link => { | ||
113 | + return link.href; | ||
114 | + }); | ||
115 | + const idArr = links.map(link => { | ||
116 | + return link.split('#')[1]; | ||
117 | + }); | ||
118 | + let offsetArr = []; | ||
119 | + idArr.forEach(id => { | ||
120 | + const titleEle = document.getElementById(id); | ||
121 | + if (titleEle) offsetArr.push({ | ||
122 | + link: `#${id}`, | ||
123 | + offset: titleEle.offsetTop - this.scrollElement.offsetTop | ||
124 | + }); | ||
125 | + }); | ||
126 | + this.titlesOffsetArr = offsetArr; | ||
127 | + }, | ||
128 | + getCurrentScrollAtTitleId (scrollTop) { | ||
129 | + let i = -1; | ||
130 | + let len = this.titlesOffsetArr.length; | ||
131 | + let titleItem = { | ||
132 | + link: '#', | ||
133 | + offset: 0 | ||
134 | + }; | ||
135 | + scrollTop += this.bounds; | ||
136 | + while (++i < len) { | ||
137 | + let currentEle = this.titlesOffsetArr[i]; | ||
138 | + let nextEle = this.titlesOffsetArr[i + 1]; | ||
139 | + if (scrollTop >= currentEle.offset && scrollTop < ((nextEle && nextEle.offset) || Infinity)) { | ||
140 | + titleItem = this.titlesOffsetArr[i]; | ||
141 | + break; | ||
142 | + } | ||
143 | + } | ||
144 | + this.currentLink = titleItem.link; | ||
145 | + this.handleSetInkTop(); | ||
146 | + }, | ||
147 | + getContainer () { | ||
148 | + this.scrollContainer = this.container ? (typeof this.container === 'string' ? document.querySelector(this.container) : this.container) : window; | ||
149 | + this.scrollElement = this.container ? this.scrollContainer : (document.documentElement || document.body); | ||
150 | + }, | ||
151 | + removeListener () { | ||
152 | + off(this.scrollContainer, 'scroll', this.handleScroll); | ||
153 | + off(window, 'hashchange', this.handleHashChange); | ||
154 | + }, | ||
155 | + init () { | ||
156 | + const anchorLink = findComponentDownward(this, 'AnchorLink'); | ||
157 | + this.linkHeight = anchorLink ? anchorLink.$el.getBoundingClientRect().height : 0; | ||
158 | + this.handleHashChange(); | ||
159 | + this.$nextTick(() => { | ||
160 | + this.removeListener(); | ||
161 | + this.getContainer(); | ||
162 | + this.wrapperTop = this.containerIsWindow ? 0 : this.scrollElement.offsetTop; | ||
163 | + this.handleScrollTo(); | ||
164 | + this.handleSetInkTop(); | ||
165 | + this.updateTitleOffset(); | ||
166 | + this.upperFirstTitle = this.scrollElement.scrollTop < this.titlesOffsetArr[0].offset; | ||
167 | + on(this.scrollContainer, 'scroll', this.handleScroll); | ||
168 | + on(window, 'hashchange', this.handleHashChange); | ||
169 | + }); | ||
170 | + } | ||
171 | + }, | ||
172 | + watch: { | ||
173 | + '$route' () { | ||
174 | + this.handleHashChange(); | ||
175 | + this.handleScrollTo(); | ||
176 | + }, | ||
177 | + container () { | ||
178 | + this.init(); | ||
179 | + }, | ||
180 | + currentLink (newHref, oldHref) { | ||
181 | + this.$emit('on-change', newHref, oldHref); | ||
182 | + } | ||
183 | + }, | ||
184 | + mounted () { | ||
185 | + this.init(); | ||
186 | + }, | ||
187 | + beforeDestroy () { | ||
188 | + this.removeListener(); | ||
189 | + } | ||
190 | +}; | ||
191 | +</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'; |
@@ -56,6 +58,8 @@ import locale from './locale/index'; | @@ -56,6 +58,8 @@ import locale from './locale/index'; | ||
56 | const components = { | 58 | const components = { |
57 | Affix, | 59 | Affix, |
58 | Alert, | 60 | Alert, |
61 | + Anchor, | ||
62 | + AnchorLink, | ||
59 | AutoComplete, | 63 | AutoComplete, |
60 | Avatar, | 64 | Avatar, |
61 | BackTop, | 65 | 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
@@ -44,4 +44,5 @@ | @@ -44,4 +44,5 @@ | ||
44 | @import "avatar"; | 44 | @import "avatar"; |
45 | @import "color-picker"; | 45 | @import "color-picker"; |
46 | @import "auto-complete"; | 46 | @import "auto-complete"; |
47 | -@import "time"; | ||
48 | \ No newline at end of file | 47 | \ No newline at end of file |
48 | +@import "anchor"; | ||
49 | +@import "time"; |
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 = /#([^#]+)$/; |