diff --git a/components/_util/__tests__/unreachableException.test.js b/components/_util/__tests__/unreachableException.test.js new file mode 100644 index 000000000..4019e4fed --- /dev/null +++ b/components/_util/__tests__/unreachableException.test.js @@ -0,0 +1,8 @@ +import UnreachableException from '../unreachableException'; + +describe('UnreachableException', () => { + it('error thrown matches snapshot', () => { + const exception = new UnreachableException('some value'); + expect(exception.error.message).toMatchInlineSnapshot(`"unreachable case: \\"some value\\""`); + }); +}); diff --git a/components/_util/styleChecker.ts b/components/_util/styleChecker.ts index ae845c67e..59244441a 100644 --- a/components/_util/styleChecker.ts +++ b/components/_util/styleChecker.ts @@ -2,8 +2,8 @@ import canUseDom from './canUseDom'; export const canUseDocElement = () => canUseDom() && window.document.documentElement; -export const isStyleSupport = (styleName: string | Array): boolean => { - if (canUseDocElement()) { +const isStyleNameSupport = (styleName: string | string[]): boolean => { + if (canUseDom() && window.document.documentElement) { const styleNameList = Array.isArray(styleName) ? styleName : [styleName]; const { documentElement } = window.document; @@ -12,6 +12,25 @@ export const isStyleSupport = (styleName: string | Array): boolean => { return false; }; +const isStyleValueSupport = (styleName: string, value: any) => { + if (!isStyleNameSupport(styleName)) { + return false; + } + + const ele = document.createElement('div'); + const origin = ele.style[styleName]; + ele.style[styleName] = value; + return ele.style[styleName] !== origin; +}; + +export function isStyleSupport(styleName: string | string[], styleValue?: any) { + if (!Array.isArray(styleName) && styleValue !== undefined) { + return isStyleValueSupport(styleName, styleValue); + } + + return isStyleNameSupport(styleName); +} + let flexGapSupported: boolean | undefined; export const detectFlexGapSupported = () => { if (!canUseDocElement()) { diff --git a/components/_util/unreachableException.ts b/components/_util/unreachableException.ts new file mode 100644 index 000000000..424a2e5f6 --- /dev/null +++ b/components/_util/unreachableException.ts @@ -0,0 +1,7 @@ +export default class UnreachableException { + error: Error; + + constructor(value: never) { + this.error = new Error(`unreachable case: ${JSON.stringify(value)}`); + } +} diff --git a/components/_util/wave.tsx b/components/_util/wave.tsx index 4fd70e193..a733fcbc5 100644 --- a/components/_util/wave.tsx +++ b/components/_util/wave.tsx @@ -24,6 +24,7 @@ export default defineComponent({ name: 'Wave', props: { insertExtraNode: Boolean, + disabled: Boolean, }, setup(props, { slots, expose }) { const instance = getCurrentInstance(); @@ -60,10 +61,11 @@ export default defineComponent({ return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node'; }; const onClick = (node: HTMLElement, waveColor: string) => { - if (!node || isHidden(node) || node.className.indexOf('-leave') >= 0) { + const { insertExtraNode, disabled } = props; + if (disabled || !node || isHidden(node) || node.className.indexOf('-leave') >= 0) { return; } - const { insertExtraNode } = props; + extraNode = document.createElement('div'); extraNode.className = 'ant-click-animating-node'; const attributeName = getAttributeName(); diff --git a/components/affix/index.en-US.md b/components/affix/index.en-US.md index 75d27a721..910c536c5 100644 --- a/components/affix/index.en-US.md +++ b/components/affix/index.en-US.md @@ -23,9 +23,9 @@ Please note that Affix should not cover other content on the page, especially wh ### events -| Events Name | Description | Arguments | Version | -| ----------- | ---------------------------------------- | ----------------- | ------- | -| change | Callback for when Affix state is changed | Function(affixed) | +| Events Name | Description | Arguments | Version | +| ----------- | ---------------------------------------- | --------------------------- | ------- | +| change | Callback for when Affix state is changed | (affixed?: boolean) => void | | **Note:** Children of `Affix` must not have the property `position: absolute`, but you can set `position: absolute` on `Affix` itself: @@ -35,8 +35,12 @@ Please note that Affix should not cover other content on the page, especially wh ## FAQ -### Affix bind container with `target`, sometime move out of container. +### When binding container with `target` in Affix, elements sometimes move out of the container. -We don't listen window scroll for performance consideration. +We only listen to container scroll events for performance consideration. You can add custom listeners if you still want to, like react demo Related issues:[#3938](https://github.com/ant-design/ant-design/issues/3938) [#5642](https://github.com/ant-design/ant-design/issues/5642) [#16120](https://github.com/ant-design/ant-design/issues/16120) + +### When Affix is ​​used in a horizontal scroll container, the position of the element `left` is incorrect. + +Affix is ​​generally only applicable to areas with one-way scrolling, and only supports usage in vertical scrolling containers. If you want to use it in a horizontal container, you can consider implementing with the native `position: sticky` property. diff --git a/components/affix/index.tsx b/components/affix/index.tsx index e403f96e6..aa451f51f 100644 --- a/components/affix/index.tsx +++ b/components/affix/index.tsx @@ -179,10 +179,7 @@ const Affix = defineComponent({ watch( () => props.target, val => { - let newTarget = null; - if (val) { - newTarget = val() || null; - } + const newTarget = val?.() || null; if (state.prevTarget !== newTarget) { removeObserveTarget(currentInstance); if (newTarget) { diff --git a/components/affix/index.zh-CN.md b/components/affix/index.zh-CN.md index 7128d19a0..1dad5ed64 100644 --- a/components/affix/index.zh-CN.md +++ b/components/affix/index.zh-CN.md @@ -19,14 +19,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg | 成员 | 说明 | 类型 | 默认值 | 版本 | | --- | --- | --- | --- | --- | | offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | | -| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | | +| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | 0 | | | target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window | | ### 事件 -| 事件名称 | 说明 | 回调参数 | 版本 | -| -------- | ---------------------------- | ----------------- | ---- | --- | -| change | 固定状态改变时触发的回调函数 | Function(affixed) | 无 | | +| 事件名称 | 说明 | 回调参数 | 版本 | +| -------- | ---------------------------- | --------------------------- | ---- | --- | +| change | 固定状态改变时触发的回调函数 | (affixed?: boolean) => void | - | | **注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位: @@ -38,6 +38,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/tX6-md4H6/Affix.svg ### Affix 使用 `target` 绑定容器时,元素会跑到容器外。 -从性能角度考虑,我们只监听容器滚动事件。 +从性能角度考虑,我们只监听容器滚动事件。如果希望任意滚动,你可以在窗体添加滚动监听, 参考 react 版本示例 相关 issue:[#3938](https://github.com/ant-design/ant-design/issues/3938) [#5642](https://github.com/ant-design/ant-design/issues/5642) [#16120](https://github.com/ant-design/ant-design/issues/16120) + +### Affix 在水平滚动容器中使用时, 元素 `left` 位置不正确。 + +Affix 一般只适用于单向滚动的区域,只支持在垂直滚动容器中使用。如果希望在水平容器中使用,你可以考虑使用 原生 `position: sticky` 实现。 + +相关 issue: [#29108](https://github.com/ant-design/ant-design/issues/29108) diff --git a/components/affix/utils.ts b/components/affix/utils.ts index 20543df10..4ce8c11bb 100644 --- a/components/affix/utils.ts +++ b/components/affix/utils.ts @@ -3,19 +3,14 @@ import type { ComponentPublicInstance } from 'vue'; import supportsPassive from '../_util/supportsPassive'; export type BindElement = HTMLElement | Window | null | undefined; -export type Rect = ClientRect | DOMRect; -export function getTargetRect(target: BindElement): ClientRect { +export function getTargetRect(target: BindElement): DOMRect { return target !== window ? (target as HTMLElement).getBoundingClientRect() - : ({ top: 0, bottom: window.innerHeight } as ClientRect); + : ({ top: 0, bottom: window.innerHeight } as DOMRect); } -export function getFixedTop( - placeholderReact: Rect, - targetRect: Rect, - offsetTop: number | undefined, -) { +export function getFixedTop(placeholderReact: DOMRect, targetRect: DOMRect, offsetTop: number) { if (offsetTop !== undefined && targetRect.top > placeholderReact.top - offsetTop) { return `${offsetTop + targetRect.top}px`; } @@ -23,9 +18,9 @@ export function getFixedTop( } export function getFixedBottom( - placeholderReact: Rect, - targetRect: Rect, - offsetBottom: number | undefined, + placeholderReact: DOMRect, + targetRect: DOMRect, + offsetBottom: number, ) { if (offsetBottom !== undefined && targetRect.bottom < placeholderReact.bottom + offsetBottom) { const targetBottomOffset = window.innerHeight - targetRect.bottom;