mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-11-21 01:13:16 +08:00
feat(anchor): showRail, showBackground
This commit is contained in:
parent
bfd1c84852
commit
97ece8c8d6
@ -10,12 +10,10 @@ import {
|
||||
} from 'vue'
|
||||
import { getScrollParent, unwrapElement } from 'seemly'
|
||||
import { useStyle } from '../../_mixins'
|
||||
import { warn } from '../../_utils'
|
||||
import { warn, keysOf } from '../../_utils'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Affix',
|
||||
props: {
|
||||
export const affixProps = {
|
||||
listenTo: {
|
||||
type: [String, Object] as PropType<
|
||||
string | (() => HTMLElement) | undefined
|
||||
@ -51,7 +49,13 @@ export default defineComponent({
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
} as const
|
||||
|
||||
export const affixPropKeys = keysOf(affixProps)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Affix',
|
||||
props: affixProps,
|
||||
setup (props) {
|
||||
useStyle('Affix', style)
|
||||
const scrollElementRef = ref<HTMLElement | null>(null)
|
||||
|
@ -1,8 +1,11 @@
|
||||
# Basic
|
||||
|
||||
```html
|
||||
<div>
|
||||
<n-anchor>
|
||||
<n-space style="margin-bottom: 12px;">
|
||||
<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="Basic" href="#basic" />
|
||||
<n-anchor-link title="Ignore-Gap" href="#ignore-gap" />
|
||||
@ -10,6 +13,18 @@
|
||||
<n-anchor-link title="Scroll To" href="#scrollto" />
|
||||
</n-anchor-link>
|
||||
<n-anchor-link title="Props" href="#Props" />
|
||||
</n-anchor>
|
||||
</div>
|
||||
</n-anchor>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
return {
|
||||
showRail: ref(true),
|
||||
showBackground: ref(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -11,7 +11,6 @@ basic
|
||||
ignore-gap
|
||||
affix
|
||||
scrollto
|
||||
|
||||
```
|
||||
|
||||
## Props
|
||||
@ -22,6 +21,8 @@ scrollto
|
||||
| bound | `number` | `12` | |
|
||||
| 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. |
|
||||
| show-rail | `boolean` | `true` | Whether to show the sider rail. |
|
||||
| show-background | `boolean` | `true` | Whether to show background of links. |
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
# 基础用法
|
||||
|
||||
```html
|
||||
<div>
|
||||
<n-anchor>
|
||||
<n-space style="margin-bottom: 12px;">
|
||||
<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="#basic" />
|
||||
<n-anchor-link title="忽略间隔" href="#ignore-gap" />
|
||||
@ -10,6 +13,18 @@
|
||||
<n-anchor-link title="滚动到" href="#scrollto" />
|
||||
</n-anchor-link>
|
||||
<n-anchor-link title="Props" href="#Props" />
|
||||
</n-anchor>
|
||||
</div>
|
||||
</n-anchor>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
setup () {
|
||||
return {
|
||||
showRail: ref(true),
|
||||
showBackground: ref(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -21,6 +21,8 @@ scrollto
|
||||
| bound | `number` | `12` | |
|
||||
| ignore-gap | `boolean` | `false` | 如果设定为 `true`, 导航将显示在准确的 href 区域 |
|
||||
| listen-to | `string \| HTMLElement` | `undefined` | 需要监听滚动的元素,如果未设定则会监听最近的可滚动祖先元素 |
|
||||
| show-rail | `boolean` | `true` | 是否展示侧面的轨道 |
|
||||
| show-background | `boolean` | `true` | 是否展示 link 的背景 |
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -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 { affixProps, affixPropKeys } from '../../affix/src/Affix'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { keep } from '../../_utils'
|
||||
import { anchorLight } from '../styles'
|
||||
import type { AnchorTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import NBaseAnchor from './BaseAnchor'
|
||||
import NBaseAnchor, { baseAnchorProps, baseAnchorPropKeys } from './BaseAnchor'
|
||||
import type { BaseAnchorRef } from './BaseAnchor'
|
||||
|
||||
export interface AnchorRef {
|
||||
@ -16,52 +18,12 @@ export default defineComponent({
|
||||
name: 'Anchor',
|
||||
props: {
|
||||
...(useTheme.props as ThemeProps<AnchorTheme>),
|
||||
top: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
affix: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
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
|
||||
}
|
||||
...affixProps,
|
||||
...baseAnchorProps
|
||||
},
|
||||
setup (props) {
|
||||
const themeRef = useTheme('Anchor', 'Anchor', style, anchorLight, props)
|
||||
@ -105,10 +67,7 @@ export default defineComponent({
|
||||
<NBaseAnchor
|
||||
ref="anchorRef"
|
||||
style={this.cssVars as CSSProperties}
|
||||
listenTo={this.listenTo}
|
||||
bound={this.bound}
|
||||
target={this.target}
|
||||
ignoreGap={this.ignoreGap}
|
||||
{...keep(this, baseAnchorPropKeys)}
|
||||
>
|
||||
{this.$slots}
|
||||
</NBaseAnchor>
|
||||
@ -116,15 +75,7 @@ export default defineComponent({
|
||||
return !this.affix ? (
|
||||
anchorNode
|
||||
) : (
|
||||
<NAffix
|
||||
listenTo={this.listenTo}
|
||||
top={this.top}
|
||||
bottom={this.bottom}
|
||||
offsetTop={this.offsetTop}
|
||||
offsetBottom={this.offsetBottom}
|
||||
position={this.position}
|
||||
target={this.target}
|
||||
>
|
||||
<NAffix {...keep(this, affixPropKeys)}>
|
||||
{{ default: () => anchorNode }}
|
||||
</NAffix>
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from 'vue'
|
||||
import { getScrollParent, unwrapElement } from 'seemly'
|
||||
import { onFontsReady } from 'vooks'
|
||||
import { warn } from '../../_utils'
|
||||
import { warn, keysOf } from '../../_utils'
|
||||
import type { AnchorInjection } from './Link'
|
||||
|
||||
export interface BaseAnchorRef {
|
||||
@ -36,10 +36,16 @@ function getOffset (
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseAnchor',
|
||||
props: {
|
||||
export const baseAnchorProps = {
|
||||
listenTo: [String, Object] as PropType<string | (() => HTMLElement)>,
|
||||
showRail: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showBackground: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
bound: {
|
||||
type: Number,
|
||||
default: 12
|
||||
@ -53,16 +59,19 @@ export default defineComponent({
|
||||
type: Function as PropType<(() => HTMLElement) | undefined>,
|
||||
validator: () => {
|
||||
if (__DEV__) {
|
||||
warn(
|
||||
'anchor',
|
||||
'`target` is deprecated, please use`listen-to` instead.'
|
||||
)
|
||||
warn('anchor', '`target` is deprecated, please use`listen-to` instead.')
|
||||
}
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
} as const
|
||||
|
||||
export const baseAnchorPropKeys = keysOf(baseAnchorProps)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseAnchor',
|
||||
props: baseAnchorProps,
|
||||
setup (props) {
|
||||
let scrollElement: HTMLElement | null
|
||||
const collectedLinkHrefs: string[] = markRaw([])
|
||||
@ -111,10 +120,10 @@ export default defineComponent({
|
||||
const { value: barEl } = barRef
|
||||
const { value: slotEl } = slotRef
|
||||
const { value: selfEl } = selfRef
|
||||
if (!selfEl || !barEl || !slotEl) return
|
||||
if (!selfEl || !barEl) return
|
||||
if (!transition) {
|
||||
barEl.style.transition = 'none'
|
||||
slotEl.style.transition = 'none'
|
||||
if (slotEl) slotEl.style.transition = 'none'
|
||||
}
|
||||
const { offsetHeight, offsetWidth } = linkTitleEl
|
||||
const {
|
||||
@ -129,15 +138,17 @@ export default defineComponent({
|
||||
const offsetLeft = linkTitleClientLeft - anchorClientLeft
|
||||
barEl.style.top = `${offsetTop}px`
|
||||
barEl.style.height = `${offsetHeight}px`
|
||||
if (slotEl) {
|
||||
slotEl.style.top = `${offsetTop}px`
|
||||
slotEl.style.height = `${offsetHeight}px`
|
||||
slotEl.style.maxWidth = `${offsetWidth + offsetLeft}px`
|
||||
}
|
||||
void barEl.offsetHeight
|
||||
void slotEl.offsetHeight
|
||||
if (slotEl) void slotEl.offsetHeight
|
||||
|
||||
if (!transition) {
|
||||
barEl.style.transition = ''
|
||||
slotEl.style.transition = ''
|
||||
if (slotEl) slotEl.style.transition = ''
|
||||
}
|
||||
}
|
||||
function setActiveHref (href: string, transition = true): void {
|
||||
@ -286,8 +297,14 @@ export default defineComponent({
|
||||
},
|
||||
render () {
|
||||
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" />
|
||||
) : null}
|
||||
{this.showRail ? (
|
||||
<div class="n-anchor-rail">
|
||||
<div
|
||||
ref="barRef"
|
||||
@ -299,6 +316,7 @@ export default defineComponent({
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{renderSlot(this.$slots, 'default')}
|
||||
</div>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { c, cE, cB, cM } from '../../../_utils/cssr'
|
||||
import { c, cE, cB, cM, cNotM } from '../../../_utils/cssr'
|
||||
|
||||
// vars:
|
||||
// --link-color
|
||||
@ -51,15 +51,16 @@ export default cB('anchor', `
|
||||
cM('active', {
|
||||
backgroundColor: 'var(--rail-color-active)'
|
||||
})
|
||||
])
|
||||
]),
|
||||
c('+', [
|
||||
cNotM('show-rail', [
|
||||
c('>', [
|
||||
cB('anchor-link', {
|
||||
marginTop: 0
|
||||
paddingLeft: 0
|
||||
})
|
||||
])
|
||||
]),
|
||||
cB('anchor-link', `
|
||||
margin-top: .5em;
|
||||
padding-left: 16px;
|
||||
position: relative;
|
||||
line-height: 1.5;
|
||||
@ -68,10 +69,10 @@ export default cB('anchor', `
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`, [
|
||||
c('+', [
|
||||
cB('anchor-link', {
|
||||
paddingLeft: '16px'
|
||||
})
|
||||
c('+, >', [
|
||||
cB('anchor-link', `
|
||||
margin-top: .5em;
|
||||
`)
|
||||
]),
|
||||
cE('title', `
|
||||
outline: none;
|
||||
|
Loading…
Reference in New Issue
Block a user