refactor: anchor
parent
1877d66cc5
commit
fbe3a48ac2
|
@ -14,6 +14,7 @@ export default (
|
|||
prefixCls: ComputedRef<string>;
|
||||
direction: ComputedRef<Direction>;
|
||||
size: ComputedRef<SizeType>;
|
||||
getTargetContainer: ComputedRef<() => HTMLElement>;
|
||||
} => {
|
||||
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
||||
'configProvider',
|
||||
|
@ -22,5 +23,6 @@ export default (
|
|||
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
|
||||
const direction = computed(() => configProvider.direction);
|
||||
const size = computed(() => props.size || configProvider.componentSize);
|
||||
return { configProvider, prefixCls, direction, size };
|
||||
const getTargetContainer = computed(() => props.getTargetContainer);
|
||||
return { configProvider, prefixCls, direction, size, getTargetContainer };
|
||||
};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
defineComponent,
|
||||
inject,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
|
@ -9,6 +8,8 @@ import {
|
|||
reactive,
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
ExtractPropTypes,
|
||||
computed,
|
||||
} from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from '../_util/classNames';
|
||||
|
@ -16,17 +17,13 @@ import addEventListener from '../vc-util/Dom/addEventListener';
|
|||
import Affix from '../affix';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
function getDefaultContainer() {
|
||||
return window;
|
||||
}
|
||||
|
||||
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
|
||||
if (!element) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!element.getClientRects().length) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -35,7 +32,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
|
|||
|
||||
if (rect.width || rect.height) {
|
||||
if (container === window) {
|
||||
container = element.ownerDocument.documentElement;
|
||||
container = element.ownerDocument!.documentElement!;
|
||||
return rect.top - container.clientTop;
|
||||
}
|
||||
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
|
||||
|
@ -44,7 +41,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
|
|||
return rect.top;
|
||||
}
|
||||
|
||||
const sharpMatcherRegx = /#([^#]+)$/;
|
||||
const sharpMatcherRegx = /#(\S+)$/;
|
||||
|
||||
type Section = {
|
||||
link: string;
|
||||
|
@ -53,7 +50,7 @@ type Section = {
|
|||
|
||||
export type AnchorContainer = HTMLElement | Window;
|
||||
|
||||
const AnchorProps = {
|
||||
const anchorProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
offsetTop: PropTypes.number,
|
||||
bounds: PropTypes.number,
|
||||
|
@ -68,6 +65,8 @@ const AnchorProps = {
|
|||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export type AnchorProps = Partial<ExtractPropTypes<typeof anchorProps>>;
|
||||
|
||||
export interface AntAnchor {
|
||||
registerLink: (link: string) => void;
|
||||
unregisterLink: (link: string) => void;
|
||||
|
@ -81,28 +80,32 @@ export interface AnchorState {
|
|||
links: string[];
|
||||
scrollEvent: any;
|
||||
animating: boolean;
|
||||
sPrefixCls?: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AAnchor',
|
||||
inheritAttrs: false,
|
||||
props: AnchorProps,
|
||||
props: anchorProps,
|
||||
emits: ['change', 'click'],
|
||||
setup(props, { emit, attrs, slots }) {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props);
|
||||
const instance = getCurrentInstance();
|
||||
const inkNodeRef = ref();
|
||||
const anchorRef = ref();
|
||||
const state = reactive<AnchorState>({
|
||||
activeLink: null,
|
||||
links: [],
|
||||
sPrefixCls: '',
|
||||
scrollContainer: null,
|
||||
scrollEvent: null,
|
||||
animating: false,
|
||||
});
|
||||
const getContainer = computed(() => {
|
||||
const { getContainer } = props;
|
||||
|
||||
const getFunc = getContainer || getTargetContainer.value || getDefaultContainer;
|
||||
|
||||
return getFunc();
|
||||
});
|
||||
// func...
|
||||
const getCurrentActiveLink = (offsetTop = 0, bounds = 5) => {
|
||||
const { getCurrentAnchor } = props;
|
||||
|
@ -110,14 +113,9 @@ export default defineComponent({
|
|||
if (typeof getCurrentAnchor === 'function') {
|
||||
return getCurrentAnchor();
|
||||
}
|
||||
const activeLink = '';
|
||||
if (typeof document === 'undefined') {
|
||||
return activeLink;
|
||||
}
|
||||
|
||||
const linkSections: Array<Section> = [];
|
||||
const { getContainer } = props;
|
||||
const container = getContainer();
|
||||
const container = getContainer.value();
|
||||
state.links.forEach(link => {
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
|
||||
if (!sharpLinkMatch) {
|
||||
|
@ -188,11 +186,9 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const updateInk = () => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const { sPrefixCls } = state;
|
||||
const linkNode = anchorRef.value.getElementsByClassName(`${sPrefixCls}-link-title-active`)[0];
|
||||
const linkNode = anchorRef.value.getElementsByClassName(
|
||||
`${prefixCls.value}-link-title-active`,
|
||||
)[0];
|
||||
if (linkNode) {
|
||||
(inkNodeRef.value as HTMLElement).style.top = `${linkNode.offsetTop +
|
||||
linkNode.clientHeight / 2 -
|
||||
|
@ -220,8 +216,8 @@ export default defineComponent({
|
|||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const { getContainer } = props;
|
||||
state.scrollContainer = getContainer();
|
||||
const container = getContainer.value();
|
||||
state.scrollContainer = container;
|
||||
state.scrollEvent = addEventListener(state.scrollContainer, 'scroll', handleScroll);
|
||||
handleScroll();
|
||||
});
|
||||
|
@ -233,8 +229,7 @@ export default defineComponent({
|
|||
});
|
||||
onUpdated(() => {
|
||||
if (state.scrollEvent) {
|
||||
const { getContainer } = props;
|
||||
const currentContainer = getContainer();
|
||||
const currentContainer = getContainer.value();
|
||||
if (state.scrollContainer !== currentContainer) {
|
||||
state.scrollContainer = currentContainer;
|
||||
state.scrollEvent.remove();
|
||||
|
@ -246,24 +241,17 @@ export default defineComponent({
|
|||
});
|
||||
|
||||
return () => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
offsetTop,
|
||||
affix,
|
||||
showInkInFixed,
|
||||
getContainer,
|
||||
} = props;
|
||||
const getPrefixCls = configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
state.sPrefixCls = prefixCls;
|
||||
|
||||
const inkClass = classNames(`${prefixCls}-ink-ball`, {
|
||||
const { offsetTop, affix, showInkInFixed } = props;
|
||||
const pre = prefixCls.value;
|
||||
const inkClass = classNames(`${pre}-ink-ball`, {
|
||||
visible: state.activeLink,
|
||||
});
|
||||
|
||||
const wrapperClass = classNames(props.wrapperClass, `${prefixCls}-wrapper`);
|
||||
const wrapperClass = classNames(props.wrapperClass, `${pre}-wrapper`, {
|
||||
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||
});
|
||||
|
||||
const anchorClass = classNames(prefixCls, {
|
||||
const anchorClass = classNames(pre, {
|
||||
fixed: !affix && !showInkInFixed,
|
||||
});
|
||||
|
||||
|
@ -274,7 +262,7 @@ export default defineComponent({
|
|||
const anchorContent = (
|
||||
<div class={wrapperClass} style={wrapperStyle} ref={anchorRef}>
|
||||
<div class={anchorClass}>
|
||||
<div class={`${prefixCls}-ink`}>
|
||||
<div class={`${pre}-ink`}>
|
||||
<span class={inkClass} ref={inkNodeRef} />
|
||||
</div>
|
||||
{slots.default?.()}
|
||||
|
@ -285,7 +273,7 @@ export default defineComponent({
|
|||
return !affix ? (
|
||||
anchorContent
|
||||
) : (
|
||||
<Affix {...attrs} offsetTop={offsetTop} target={getContainer}>
|
||||
<Affix {...attrs} offsetTop={offsetTop} target={getContainer.value}>
|
||||
{anchorContent}
|
||||
</Affix>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ComponentInternalInstance,
|
||||
defineComponent,
|
||||
ExtractPropTypes,
|
||||
inject,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
|
@ -10,22 +11,24 @@ import {
|
|||
import PropTypes from '../_util/vue-types';
|
||||
import { getPropsSlot } from '../_util/props-util';
|
||||
import classNames from '../_util/classNames';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { AntAnchor } from './Anchor';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function noop(..._any: any[]): any {}
|
||||
|
||||
const AnchorLinkProps = {
|
||||
const anchorLinkProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
href: PropTypes.string.def('#'),
|
||||
title: PropTypes.VNodeChild,
|
||||
target: PropTypes.string,
|
||||
};
|
||||
|
||||
export type AnchorLinkProps = Partial<ExtractPropTypes<typeof anchorLinkProps>>;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AAnchorLink',
|
||||
props: AnchorLinkProps,
|
||||
props: anchorLinkProps,
|
||||
setup(props, { slots }) {
|
||||
const antAnchor = inject('antAnchor', {
|
||||
registerLink: noop,
|
||||
|
@ -34,7 +37,7 @@ export default defineComponent({
|
|||
$data: {},
|
||||
} as AntAnchor);
|
||||
const antAnchorContext = inject('antAnchorContext', {}) as ComponentInternalInstance;
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
const { prefixCls } = useConfigInject('anchor', props);
|
||||
|
||||
const handleClick = (e: Event) => {
|
||||
// antAnchor.scrollTo(props.href);
|
||||
|
@ -65,18 +68,15 @@ export default defineComponent({
|
|||
});
|
||||
|
||||
return () => {
|
||||
const { prefixCls: customizePrefixCls, href, target } = props;
|
||||
|
||||
const getPrefixCls = configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
|
||||
const { href, target } = props;
|
||||
const pre = prefixCls.value;
|
||||
const title = getPropsSlot(slots, props, 'title');
|
||||
const active = antAnchor.$data.activeLink === href;
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
const wrapperClassName = classNames(`${pre}-link`, {
|
||||
[`${pre}-link-active`]: active,
|
||||
});
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: active,
|
||||
const titleClassName = classNames(`${pre}-link-title`, {
|
||||
[`${pre}-link-title-active`]: active,
|
||||
});
|
||||
return (
|
||||
<div class={wrapperClassName}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { App, Plugin } from 'vue';
|
||||
import Anchor from './Anchor';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import Anchor, { AnchorProps } from './Anchor';
|
||||
import AnchorLink, { AnchorLinkProps } from './AnchorLink';
|
||||
|
||||
Anchor.Link = AnchorLink;
|
||||
|
||||
|
@ -11,6 +11,8 @@ Anchor.install = function(app: App) {
|
|||
return app;
|
||||
};
|
||||
|
||||
export { AnchorLinkProps, AnchorProps, AnchorLink, AnchorLink as Link };
|
||||
|
||||
export default Anchor as typeof Anchor &
|
||||
Plugin & {
|
||||
readonly Link: typeof AnchorLink;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
margin-left: -4px;
|
||||
padding-left: 4px;
|
||||
overflow: auto;
|
||||
background-color: @component-background;
|
||||
background-color: @anchor-bg;
|
||||
}
|
||||
|
||||
&-ink {
|
||||
|
@ -52,7 +52,7 @@
|
|||
}
|
||||
|
||||
&-link {
|
||||
padding: 7px 0 7px 16px;
|
||||
padding: @anchor-link-padding;
|
||||
line-height: 1.143;
|
||||
|
||||
&-title {
|
||||
|
@ -80,3 +80,5 @@
|
|||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
.@{ant-prefix}-anchor {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&-wrapper {
|
||||
.@{ant-prefix}-anchor-rtl& {
|
||||
margin-right: -4px;
|
||||
margin-left: 0;
|
||||
padding-right: 4px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-ink {
|
||||
.@{ant-prefix}-anchor-rtl & {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
&-ball {
|
||||
.@{ant-prefix}-anchor-rtl & {
|
||||
right: 50%;
|
||||
left: 0;
|
||||
transform: translateX(50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-link {
|
||||
.@{ant-prefix}-anchor-rtl & {
|
||||
padding: @anchor-link-top @anchor-link-left @anchor-link-top 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue