feat(anchor): showRail, showBackground

This commit is contained in:
07akioni 2021-02-27 13:35:59 +08:00
parent bfd1c84852
commit 97ece8c8d6
9 changed files with 183 additions and 173 deletions

View File

@ -10,12 +10,10 @@ import {
} from 'vue' } from 'vue'
import { getScrollParent, unwrapElement } from 'seemly' import { getScrollParent, unwrapElement } from 'seemly'
import { useStyle } from '../../_mixins' import { useStyle } from '../../_mixins'
import { warn } from '../../_utils' import { warn, keysOf } from '../../_utils'
import style from './styles/index.cssr' import style from './styles/index.cssr'
export default defineComponent({ export const affixProps = {
name: 'Affix',
props: {
listenTo: { listenTo: {
type: [String, Object] as PropType< type: [String, Object] as PropType<
string | (() => HTMLElement) | undefined string | (() => HTMLElement) | undefined
@ -51,7 +49,13 @@ export default defineComponent({
}, },
default: undefined default: undefined
} }
}, } as const
export const affixPropKeys = keysOf(affixProps)
export default defineComponent({
name: 'Affix',
props: affixProps,
setup (props) { setup (props) {
useStyle('Affix', style) useStyle('Affix', style)
const scrollElementRef = ref<HTMLElement | null>(null) const scrollElementRef = ref<HTMLElement | null>(null)

View File

@ -1,8 +1,11 @@
# Basic # Basic
```html ```html
<div> <n-space style="margin-bottom: 12px;">
<n-anchor> <n-switch v-model:value="showRail" /> Show Rail
<n-switch v-model:value="showBackground" /> Show Background
</n-space>
<n-anchor>
<n-anchor-link title="Demos" href="#Demos"> <n-anchor-link title="Demos" href="#Demos">
<n-anchor-link title="Basic" href="#basic" /> <n-anchor-link title="Basic" href="#basic" />
<n-anchor-link title="Ignore-Gap" href="#ignore-gap" /> <n-anchor-link title="Ignore-Gap" href="#ignore-gap" />
@ -10,6 +13,18 @@
<n-anchor-link title="Scroll To" href="#scrollto" /> <n-anchor-link title="Scroll To" href="#scrollto" />
</n-anchor-link> </n-anchor-link>
<n-anchor-link title="Props" href="#Props" /> <n-anchor-link title="Props" href="#Props" />
</n-anchor> </n-anchor>
</div> ```
```js
import { ref } from 'vue'
export default {
setup () {
return {
showRail: ref(true),
showBackground: ref(true)
}
}
}
``` ```

View File

@ -11,7 +11,6 @@ basic
ignore-gap ignore-gap
affix affix
scrollto scrollto
``` ```
## Props ## Props
@ -22,6 +21,8 @@ scrollto
| bound | `number` | `12` | | | bound | `number` | `12` | |
| ignore-gap | `boolean` | `false` | If set to `true`, it will be displayed on the exact href | | ignore-gap | `boolean` | `false` | If set to `true`, it will be displayed on the exact href |
| listen-to | `string \| HTMLElement` | `undefined` | The scrolling element to listen scrolling. If not set it will listen to the nearest scrollable ascendant element. | | listen-to | `string \| HTMLElement` | `undefined` | The scrolling element to listen scrolling. If not set it will listen to the nearest scrollable ascendant element. |
| show-rail | `boolean` | `true` | Whether to show the sider rail. |
| show-background | `boolean` | `true` | Whether to show background of links. |
## Methods ## Methods

View File

@ -1,8 +1,11 @@
# 基础用法 # 基础用法
```html ```html
<div> <n-space style="margin-bottom: 12px;">
<n-anchor> <n-switch v-model:value="showRail" /> 展示轨道
<n-switch v-model:value="showBackground" /> 展示背景
</n-space>
<n-anchor :show-rail="showRail" :show-background="showBackground">
<n-anchor-link title="演示" href="#演示"> <n-anchor-link title="演示" href="#演示">
<n-anchor-link title="基础用法" href="#basic" /> <n-anchor-link title="基础用法" href="#basic" />
<n-anchor-link title="忽略间隔" href="#ignore-gap" /> <n-anchor-link title="忽略间隔" href="#ignore-gap" />
@ -10,6 +13,18 @@
<n-anchor-link title="滚动到" href="#scrollto" /> <n-anchor-link title="滚动到" href="#scrollto" />
</n-anchor-link> </n-anchor-link>
<n-anchor-link title="Props" href="#Props" /> <n-anchor-link title="Props" href="#Props" />
</n-anchor> </n-anchor>
</div> ```
```js
import { ref } from 'vue'
export default {
setup () {
return {
showRail: ref(true),
showBackground: ref(true)
}
}
}
``` ```

View File

@ -21,6 +21,8 @@ scrollto
| bound | `number` | `12` | | | bound | `number` | `12` | |
| ignore-gap | `boolean` | `false` | 如果设定为 `true`, 导航将显示在准确的 href 区域 | | ignore-gap | `boolean` | `false` | 如果设定为 `true`, 导航将显示在准确的 href 区域 |
| listen-to | `string \| HTMLElement` | `undefined` | 需要监听滚动的元素,如果未设定则会监听最近的可滚动祖先元素 | | listen-to | `string \| HTMLElement` | `undefined` | 需要监听滚动的元素,如果未设定则会监听最近的可滚动祖先元素 |
| show-rail | `boolean` | `true` | 是否展示侧面的轨道 |
| show-background | `boolean` | `true` | 是否展示 link 的背景 |
## Methods ## Methods

View File

@ -1,11 +1,13 @@
import { h, defineComponent, computed, ref, CSSProperties, PropType } from 'vue' import { h, defineComponent, computed, ref, CSSProperties } from 'vue'
import { NAffix } from '../../affix' import { NAffix } from '../../affix'
import { affixProps, affixPropKeys } from '../../affix/src/Affix'
import { useTheme } from '../../_mixins' import { useTheme } from '../../_mixins'
import type { ThemeProps } from '../../_mixins' import type { ThemeProps } from '../../_mixins'
import { keep } from '../../_utils'
import { anchorLight } from '../styles' import { anchorLight } from '../styles'
import type { AnchorTheme } from '../styles' import type { AnchorTheme } from '../styles'
import style from './styles/index.cssr' import style from './styles/index.cssr'
import NBaseAnchor from './BaseAnchor' import NBaseAnchor, { baseAnchorProps, baseAnchorPropKeys } from './BaseAnchor'
import type { BaseAnchorRef } from './BaseAnchor' import type { BaseAnchorRef } from './BaseAnchor'
export interface AnchorRef { export interface AnchorRef {
@ -16,52 +18,12 @@ export default defineComponent({
name: 'Anchor', name: 'Anchor',
props: { props: {
...(useTheme.props as ThemeProps<AnchorTheme>), ...(useTheme.props as ThemeProps<AnchorTheme>),
top: {
type: Number,
default: undefined
},
affix: { affix: {
type: Boolean, type: Boolean,
default: false default: false
}, },
position: { ...affixProps,
type: String, ...baseAnchorProps
default: undefined
},
bottom: {
type: Number,
default: undefined
},
offsetBottom: {
type: Number,
default: undefined
},
offsetTop: {
type: Number,
default: undefined
},
bound: {
type: Number,
default: 12
},
ignoreGap: {
type: Boolean,
default: false
},
listenTo: {
type: [String, Object] as PropType<
string | (() => HTMLElement) | undefined
>,
default: undefined
},
// deprecated
target: {
type: Function as PropType<(() => HTMLElement) | undefined>,
validator: () => {
return true
},
default: undefined
}
}, },
setup (props) { setup (props) {
const themeRef = useTheme('Anchor', 'Anchor', style, anchorLight, props) const themeRef = useTheme('Anchor', 'Anchor', style, anchorLight, props)
@ -105,10 +67,7 @@ export default defineComponent({
<NBaseAnchor <NBaseAnchor
ref="anchorRef" ref="anchorRef"
style={this.cssVars as CSSProperties} style={this.cssVars as CSSProperties}
listenTo={this.listenTo} {...keep(this, baseAnchorPropKeys)}
bound={this.bound}
target={this.target}
ignoreGap={this.ignoreGap}
> >
{this.$slots} {this.$slots}
</NBaseAnchor> </NBaseAnchor>
@ -116,15 +75,7 @@ export default defineComponent({
return !this.affix ? ( return !this.affix ? (
anchorNode anchorNode
) : ( ) : (
<NAffix <NAffix {...keep(this, affixPropKeys)}>
listenTo={this.listenTo}
top={this.top}
bottom={this.bottom}
offsetTop={this.offsetTop}
offsetBottom={this.offsetBottom}
position={this.position}
target={this.target}
>
{{ default: () => anchorNode }} {{ default: () => anchorNode }}
</NAffix> </NAffix>
) )

View File

@ -14,7 +14,7 @@ import {
} from 'vue' } from 'vue'
import { getScrollParent, unwrapElement } from 'seemly' import { getScrollParent, unwrapElement } from 'seemly'
import { onFontsReady } from 'vooks' import { onFontsReady } from 'vooks'
import { warn } from '../../_utils' import { warn, keysOf } from '../../_utils'
import type { AnchorInjection } from './Link' import type { AnchorInjection } from './Link'
export interface BaseAnchorRef { export interface BaseAnchorRef {
@ -36,10 +36,16 @@ function getOffset (
} }
} }
export default defineComponent({ export const baseAnchorProps = {
name: 'BaseAnchor',
props: {
listenTo: [String, Object] as PropType<string | (() => HTMLElement)>, listenTo: [String, Object] as PropType<string | (() => HTMLElement)>,
showRail: {
type: Boolean,
default: true
},
showBackground: {
type: Boolean,
default: true
},
bound: { bound: {
type: Number, type: Number,
default: 12 default: 12
@ -53,16 +59,19 @@ export default defineComponent({
type: Function as PropType<(() => HTMLElement) | undefined>, type: Function as PropType<(() => HTMLElement) | undefined>,
validator: () => { validator: () => {
if (__DEV__) { if (__DEV__) {
warn( warn('anchor', '`target` is deprecated, please use`listen-to` instead.')
'anchor',
'`target` is deprecated, please use`listen-to` instead.'
)
} }
return true return true
}, },
default: undefined default: undefined
} }
}, } as const
export const baseAnchorPropKeys = keysOf(baseAnchorProps)
export default defineComponent({
name: 'BaseAnchor',
props: baseAnchorProps,
setup (props) { setup (props) {
let scrollElement: HTMLElement | null let scrollElement: HTMLElement | null
const collectedLinkHrefs: string[] = markRaw([]) const collectedLinkHrefs: string[] = markRaw([])
@ -111,10 +120,10 @@ export default defineComponent({
const { value: barEl } = barRef const { value: barEl } = barRef
const { value: slotEl } = slotRef const { value: slotEl } = slotRef
const { value: selfEl } = selfRef const { value: selfEl } = selfRef
if (!selfEl || !barEl || !slotEl) return if (!selfEl || !barEl) return
if (!transition) { if (!transition) {
barEl.style.transition = 'none' barEl.style.transition = 'none'
slotEl.style.transition = 'none' if (slotEl) slotEl.style.transition = 'none'
} }
const { offsetHeight, offsetWidth } = linkTitleEl const { offsetHeight, offsetWidth } = linkTitleEl
const { const {
@ -129,15 +138,17 @@ export default defineComponent({
const offsetLeft = linkTitleClientLeft - anchorClientLeft const offsetLeft = linkTitleClientLeft - anchorClientLeft
barEl.style.top = `${offsetTop}px` barEl.style.top = `${offsetTop}px`
barEl.style.height = `${offsetHeight}px` barEl.style.height = `${offsetHeight}px`
if (slotEl) {
slotEl.style.top = `${offsetTop}px` slotEl.style.top = `${offsetTop}px`
slotEl.style.height = `${offsetHeight}px` slotEl.style.height = `${offsetHeight}px`
slotEl.style.maxWidth = `${offsetWidth + offsetLeft}px` slotEl.style.maxWidth = `${offsetWidth + offsetLeft}px`
}
void barEl.offsetHeight void barEl.offsetHeight
void slotEl.offsetHeight if (slotEl) void slotEl.offsetHeight
if (!transition) { if (!transition) {
barEl.style.transition = '' barEl.style.transition = ''
slotEl.style.transition = '' if (slotEl) slotEl.style.transition = ''
} }
} }
function setActiveHref (href: string, transition = true): void { function setActiveHref (href: string, transition = true): void {
@ -286,8 +297,14 @@ export default defineComponent({
}, },
render () { render () {
return ( return (
<div class="n-anchor" ref="selfRef"> <div
class={['n-anchor', this.showRail && 'n-anchor--show-rail']}
ref="selfRef"
>
{this.showRail && this.showBackground ? (
<div ref="slotRef" class="n-anchor-link-background" /> <div ref="slotRef" class="n-anchor-link-background" />
) : null}
{this.showRail ? (
<div class="n-anchor-rail"> <div class="n-anchor-rail">
<div <div
ref="barRef" ref="barRef"
@ -299,6 +316,7 @@ export default defineComponent({
]} ]}
/> />
</div> </div>
) : null}
{renderSlot(this.$slots, 'default')} {renderSlot(this.$slots, 'default')}
</div> </div>
) )

View File

@ -1,4 +1,4 @@
import { c, cE, cB, cM } from '../../../_utils/cssr' import { c, cE, cB, cM, cNotM } from '../../../_utils/cssr'
// vars: // vars:
// --link-color // --link-color
@ -51,15 +51,16 @@ export default cB('anchor', `
cM('active', { cM('active', {
backgroundColor: 'var(--rail-color-active)' backgroundColor: 'var(--rail-color-active)'
}) })
])
]), ]),
c('+', [ cNotM('show-rail', [
c('>', [
cB('anchor-link', { cB('anchor-link', {
marginTop: 0 paddingLeft: 0
}) })
]) ])
]), ]),
cB('anchor-link', ` cB('anchor-link', `
margin-top: .5em;
padding-left: 16px; padding-left: 16px;
position: relative; position: relative;
line-height: 1.5; line-height: 1.5;
@ -68,10 +69,10 @@ export default cB('anchor', `
display: flex; display: flex;
flex-direction: column; flex-direction: column;
`, [ `, [
c('+', [ c('+, >', [
cB('anchor-link', { cB('anchor-link', `
paddingLeft: '16px' margin-top: .5em;
}) `)
]), ]),
cE('title', ` cE('title', `
outline: none; outline: none;

View File

@ -22,6 +22,9 @@
- `target` => `listen-to` - `target` => `listen-to`
- [x] alert - [x] alert
- [x] anchor - [x] anchor
- new
- `show-rail` props
- `show-background` props
- deprecate - deprecate
- `target` => `listen-to` - `target` => `listen-to`
- [x] auto-complete - [x] auto-complete