feat(zhst/meta): 全量迁移antd5.17.4
This commit is contained in:
parent
e5b176c148
commit
357a4c677a
@ -16,7 +16,7 @@
|
||||
|
||||
#root .dumi-default-sidebar {
|
||||
width: 188px;
|
||||
overflow-y: hidden;
|
||||
overflow-y: scroll;
|
||||
/* stylelint-disable-next-line rule-empty-line-before */
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
|
@ -40,5 +40,5 @@ export default defineConfig({
|
||||
monorepoRedirect: {
|
||||
srcDir: ['packages', 'src'],
|
||||
peerDeps: true,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
@ -82,5 +82,12 @@
|
||||
},
|
||||
"authors": [
|
||||
"dev<710328466@qq.com>"
|
||||
]
|
||||
],
|
||||
"dependencies": {
|
||||
"@ant-design/happy-work-theme": "^1.0.0",
|
||||
"@zhst/meta": "workspace:^",
|
||||
"rc-rate": "~2.12.0",
|
||||
"react-fast-marquee": "^1.6.4",
|
||||
"react-infinite-scroll-component": "^6.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import BoxPanel from './components/boxPanel';
|
||||
import type { BoxPanelProps } from './components/boxPanel';
|
||||
import './index.less'
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface BoxSelectTreeProps extends BoxPanelProps {
|
||||
onTabChange?: (e: any) => void
|
||||
tabsProps?: TabsProps
|
||||
|
@ -17,7 +17,6 @@ const demo = () => {
|
||||
}
|
||||
|
||||
const onBoxBatchDelete = () => {
|
||||
console.log('盒子批量删除', checkedKeys)
|
||||
modal.warning({
|
||||
content: (
|
||||
<div>
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,15 +1,6 @@
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
||||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
||||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
||||
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
||||
import { cloneDeep, dataURLToBlob, get, isNull } from "@zhst/func";
|
||||
var proto = {
|
||||
Common: {
|
||||
|
@ -1,9 +1,4 @@
|
||||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
||||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
||||
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
||||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
||||
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
||||
import React, { useRef, useState, useEffect, forwardRef, useImperativeHandle, useContext } from 'react';
|
||||
// @ts-ignore
|
||||
import { generateImg, get, addEventListenerWrapper } from '@zhst/func';
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,9 +1,4 @@
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
//@ts-nocheck
|
||||
import { isObject, isNumber, assign, isFunction } from '@zhst/func';
|
||||
import { hasClass, addClass, removeClass } from 'rc-util/lib/Dom/class';
|
||||
|
@ -1,9 +1,4 @@
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
//@ts-nocheck
|
||||
import { isNumber, get, addEventListenerWrapper } from '@zhst/func';
|
||||
import { addClass, removeClass } from 'rc-util/lib/Dom/class.js';
|
||||
|
@ -1,17 +1,11 @@
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
||||
var _excluded = ["x", "y"],
|
||||
_excluded2 = ["x", "y"],
|
||||
_excluded3 = ["x", "y", "w", "h", "image"],
|
||||
_excluded4 = ["x", "y"],
|
||||
_excluded5 = ["x", "y"],
|
||||
_excluded6 = ["x", "y", "w", "h"];
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
||||
//@ts-nocheck
|
||||
import * as turf from '@turf/turf';
|
||||
import { AXIS_TYPE_ORIGIN, AXIS_TYPE_CANVAS, AXIS_TYPE_IMAGE } from "./constants";
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,15 +1,5 @@
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
||||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
||||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
||||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
||||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
||||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
// @ts-nocheck
|
||||
import { isNil, isArray, isFunction } from '@zhst/func';
|
||||
import * as turf from '@turf/turf';
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,22 +1,12 @@
|
||||
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
||||
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/esm/createClass";
|
||||
import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized";
|
||||
import _inherits from "@babel/runtime/helpers/esm/inherits";
|
||||
import _createSuper from "@babel/runtime/helpers/esm/createSuper";
|
||||
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
||||
var _excluded = ["className", "autoPlay", "config", "onCreate", "playId"];
|
||||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
||||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||||
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||||
import React, { Component } from 'react';
|
||||
import flvjs from 'flv.js';
|
||||
import { isEqual } from '@zhst/func';
|
||||
|
@ -1,6 +1,5 @@
|
||||
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
||||
var _excluded = ["className", "style", "showSlider"];
|
||||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
||||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Slider, ConfigProvider } from 'antd';
|
||||
|
@ -6,6 +6,7 @@ export { default as CropperImage } from "./cropperImage";
|
||||
export { default as AttachImage } from "./attachImage";
|
||||
export { default as RelatedImage } from "./relatedImage";
|
||||
export { default as VideoPlayer } from "./VideoPlayer";
|
||||
// antd
|
||||
export { default as Tabs } from "./tabs";
|
||||
export { default as Tree } from "./tree";
|
||||
export { default as message } from "./message";
|
||||
|
@ -61,8 +61,6 @@
|
||||
"@types/react": "^18.2.46",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react-highlight-words": "^0.16.7",
|
||||
"@types/react-resizable": "^3.0.7",
|
||||
"@types/semver": "^7.5.6",
|
||||
"@types/tar": "^6.1.10",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
@ -77,12 +75,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@ant-design/cssinjs": "^1.18.2",
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ctrl/tinycolor": "^4.0.2",
|
||||
"@ant-design/cssinjs": "^1.19.1",
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"@ant-design/react-slick": "~1.1.2",
|
||||
"@babel/runtime": "^7.24.5",
|
||||
"@ctrl/tinycolor": "^3.6.1",
|
||||
"@rc-component/color-picker": "~1.5.3",
|
||||
"@rc-component/mutate-observer": "^1.1.0",
|
||||
"@rc-component/tour": "^1.12.3",
|
||||
"@rc-component/trigger": "^1.18.2",
|
||||
"@rc-component/tour": "~1.15.0",
|
||||
"@rc-component/trigger": "^2.2.0",
|
||||
"@turf/boolean-point-in-polygon": "^6.5.0",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/downloadjs": "^1.4.6",
|
||||
@ -90,7 +91,6 @@
|
||||
"@zhst/func": "workspace:^",
|
||||
"@zhst/hooks": "workspace:^",
|
||||
"@zhst/icon": "workspace:^",
|
||||
"@zhst/meta": "workspace:^",
|
||||
"antd": "^5.12.5",
|
||||
"antd-img-crop": "^4.21.0",
|
||||
"antd-style": "^3.6.1",
|
||||
@ -101,44 +101,47 @@
|
||||
"fabric": "^5.3.0",
|
||||
"flv.js": "^1.6.2",
|
||||
"lunar-typescript": "^1.7.3",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"rc-align": "^4.0.15",
|
||||
"rc-cascader": "~3.20.0",
|
||||
"rc-checkbox": "~3.1.0",
|
||||
"rc-collapse": "~3.7.2",
|
||||
"rc-dialog": "~9.3.4",
|
||||
"rc-drawer": "~6.5.2",
|
||||
"rc-dropdown": "~4.1.0",
|
||||
"rc-field-form": "~1.41.0",
|
||||
"rc-image": "~7.5.1",
|
||||
"rc-input": "~1.3.11",
|
||||
"rc-input-number": "~8.4.0",
|
||||
"rc-mentions": "~2.9.1",
|
||||
"rc-menu": "~9.12.4",
|
||||
"rc-motion": "^2.9.0",
|
||||
"rc-notification": "~5.3.0",
|
||||
"rc-cascader": "~3.26.0",
|
||||
"rc-checkbox": "~3.3.0",
|
||||
"rc-collapse": "~3.7.3",
|
||||
"rc-dialog": "~9.4.0",
|
||||
"rc-drawer": "~7.1.0",
|
||||
"rc-dropdown": "~4.2.0",
|
||||
"rc-field-form": "~2.0.1",
|
||||
"rc-image": "~7.6.0",
|
||||
"rc-input": "~1.5.1",
|
||||
"rc-input-number": "~9.1.0",
|
||||
"rc-mentions": "~2.13.1",
|
||||
"rc-menu": "~9.14.0",
|
||||
"rc-motion": "^2.9.1",
|
||||
"rc-notification": "~5.4.0",
|
||||
"rc-pagination": "~4.0.4",
|
||||
"rc-picker": "~3.14.6",
|
||||
"rc-progress": "~3.5.1",
|
||||
"rc-picker": "~4.5.0",
|
||||
"rc-progress": "~4.0.0",
|
||||
"rc-rate": "~2.12.0",
|
||||
"rc-resize-observer": "^1.4.0",
|
||||
"rc-segmented": "~2.2.2",
|
||||
"rc-select": "~14.10.0",
|
||||
"rc-slider": "~10.5.0",
|
||||
"rc-segmented": "~2.3.0",
|
||||
"rc-select": "~14.14.0",
|
||||
"rc-slider": "~10.6.2",
|
||||
"rc-steps": "~6.0.1",
|
||||
"rc-switch": "~4.1.0",
|
||||
"rc-table": "~7.36.1",
|
||||
"rc-tabs": "~12.14.1",
|
||||
"rc-textarea": "~1.5.3",
|
||||
"rc-tooltip": "~6.1.3",
|
||||
"rc-tree": "~5.8.2",
|
||||
"rc-tree-select": "~5.15.0",
|
||||
"rc-table": "~7.45.7",
|
||||
"rc-tabs": "~15.1.0",
|
||||
"rc-textarea": "~1.7.0",
|
||||
"rc-tooltip": "~6.2.0",
|
||||
"rc-tree": "~5.8.7",
|
||||
"rc-tree-select": "~5.21.0",
|
||||
"rc-upload": "~4.5.2",
|
||||
"rc-util": "^5.38.1",
|
||||
"rc-util": "^5.41.0",
|
||||
"rc-virtual-list": "^3.14.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"swiper": "^11.1.1"
|
||||
"swiper": "^11.1.1",
|
||||
"throttle-debounce": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { CompareImage, Space } from '@zhst/meta'
|
||||
import { CompareImage } from '@zhst/meta'
|
||||
|
||||
export default () => {
|
||||
const ref = useRef(null)
|
||||
@ -10,26 +10,24 @@ export default () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<CompareImage
|
||||
label="目标图"
|
||||
url={data.url}
|
||||
score={data.score}
|
||||
openRoll
|
||||
ref={ref}
|
||||
onPre={() => {
|
||||
setData({
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
score: '0.8'
|
||||
})
|
||||
}}
|
||||
onNext={() => {
|
||||
setData({
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
score: '0.4'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
<CompareImage
|
||||
label="目标图"
|
||||
url={data.url}
|
||||
score={data.score}
|
||||
openRoll
|
||||
ref={ref}
|
||||
onPre={() => {
|
||||
setData({
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
score: '0.8'
|
||||
})
|
||||
}}
|
||||
onNext={() => {
|
||||
setData({
|
||||
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
score: '0.4'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
78
packages/meta/src/__tests__/__snapshots__/index.test.ts.snap
Normal file
78
packages/meta/src/__tests__/__snapshots__/index.test.ts.snap
Normal file
@ -0,0 +1,78 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`antd exports modules correctly 1`] = `
|
||||
[
|
||||
"Affix",
|
||||
"Alert",
|
||||
"Anchor",
|
||||
"App",
|
||||
"AutoComplete",
|
||||
"Avatar",
|
||||
"BackTop",
|
||||
"Badge",
|
||||
"Breadcrumb",
|
||||
"Button",
|
||||
"Calendar",
|
||||
"Card",
|
||||
"Carousel",
|
||||
"Cascader",
|
||||
"Checkbox",
|
||||
"Col",
|
||||
"Collapse",
|
||||
"ColorPicker",
|
||||
"ConfigProvider",
|
||||
"DatePicker",
|
||||
"Descriptions",
|
||||
"Divider",
|
||||
"Drawer",
|
||||
"Dropdown",
|
||||
"Empty",
|
||||
"Flex",
|
||||
"FloatButton",
|
||||
"Form",
|
||||
"Grid",
|
||||
"Image",
|
||||
"Input",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"Mentions",
|
||||
"Menu",
|
||||
"Modal",
|
||||
"Pagination",
|
||||
"Popconfirm",
|
||||
"Popover",
|
||||
"Progress",
|
||||
"QRCode",
|
||||
"Radio",
|
||||
"Rate",
|
||||
"Result",
|
||||
"Row",
|
||||
"Segmented",
|
||||
"Select",
|
||||
"Skeleton",
|
||||
"Slider",
|
||||
"Space",
|
||||
"Spin",
|
||||
"Statistic",
|
||||
"Steps",
|
||||
"Switch",
|
||||
"Table",
|
||||
"Tabs",
|
||||
"Tag",
|
||||
"TimePicker",
|
||||
"Timeline",
|
||||
"Tooltip",
|
||||
"Tour",
|
||||
"Transfer",
|
||||
"Tree",
|
||||
"TreeSelect",
|
||||
"Typography",
|
||||
"Upload",
|
||||
"Watermark",
|
||||
"message",
|
||||
"notification",
|
||||
"theme",
|
||||
"version",
|
||||
]
|
||||
`;
|
@ -0,0 +1,11 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SetUp.Test diff of React 18 & React 17 1`] = `
|
||||
NodeList [
|
||||
<div>
|
||||
bamboo
|
||||
little
|
||||
</div>,
|
||||
<div />,
|
||||
]
|
||||
`;
|
35
packages/meta/src/__tests__/blog.test.ts
Normal file
35
packages/meta/src/__tests__/blog.test.ts
Normal file
@ -0,0 +1,35 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const blogList = [
|
||||
'check-conduct',
|
||||
'contributor-development-maintenance-guide',
|
||||
'css-in-js',
|
||||
'extract-ssr',
|
||||
'getContainer',
|
||||
'github-actions-workflow',
|
||||
'issue-helper',
|
||||
'mock-project-build',
|
||||
'modal-hook-order',
|
||||
'testing-migrate',
|
||||
'render-times',
|
||||
'to-be-collaborator',
|
||||
'tooltip-align',
|
||||
'tree-shaking',
|
||||
'why-not-static',
|
||||
].map((blogName) => path.join(__dirname, `../../docs/blog/${blogName}.en-US.md`));
|
||||
|
||||
describe('blog', () => {
|
||||
it('should not include Chinese in en-US blog', () => {
|
||||
blogList.forEach((blog) => {
|
||||
fs.readFile(blog, (err: NodeJS.ErrnoException | null, data: Buffer) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
const includeChinese = /[\u4E00-\u9FA5]/.test(data.toString());
|
||||
expect(includeChinese).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
13
packages/meta/src/__tests__/index.test.ts
Normal file
13
packages/meta/src/__tests__/index.test.ts
Normal file
@ -0,0 +1,13 @@
|
||||
const OLD_NODE_ENV = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = 'development';
|
||||
const antd = require('..');
|
||||
|
||||
describe('antd', () => {
|
||||
afterAll(() => {
|
||||
process.env.NODE_ENV = OLD_NODE_ENV;
|
||||
});
|
||||
|
||||
it('exports modules correctly', () => {
|
||||
expect(Object.keys(antd)).toMatchSnapshot();
|
||||
});
|
||||
});
|
53
packages/meta/src/__tests__/node.test.tsx
Normal file
53
packages/meta/src/__tests__/node.test.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import * as React from 'react';
|
||||
import { globSync } from 'glob';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
|
||||
import type { Options } from '../../tests/shared/demoTest';
|
||||
|
||||
(global as any).testConfig = {};
|
||||
|
||||
jest.mock('../../tests/shared/demoTest', () => {
|
||||
function fakeDemoTest(name: string, option: Options = {}) {
|
||||
(global as any).testConfig[name] = option;
|
||||
}
|
||||
|
||||
fakeDemoTest.rootPropsTest = () => {};
|
||||
|
||||
return fakeDemoTest;
|
||||
});
|
||||
|
||||
describe('node', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2016-11-22'));
|
||||
});
|
||||
|
||||
// Find the component exist demo test file
|
||||
const files = globSync(`./components/*/__tests__/demo.test.@(j|t)s?(x)`);
|
||||
|
||||
files.forEach((componentTestFile) => {
|
||||
const componentName = componentTestFile.match(/components\/([^/]*)\//)![1];
|
||||
|
||||
// Test for ssr
|
||||
describe(componentName, () => {
|
||||
const demoList = globSync(`./components/${componentName}/demo/*.tsx`).filter(
|
||||
(file) => !file.includes('_semantic'),
|
||||
);
|
||||
|
||||
// Use mock to get config
|
||||
require(`../../${componentTestFile}`); // eslint-disable-line global-require, import/no-dynamic-require
|
||||
const option = (global as any).testConfig?.[componentName];
|
||||
|
||||
demoList.forEach((demoFile) => {
|
||||
const skip: string[] = option?.skip || [];
|
||||
const test = skip.some((skipMarkdown) => demoFile.includes(skipMarkdown)) ? it.skip : it;
|
||||
|
||||
test(demoFile, () => {
|
||||
const Demo = require(`../../${demoFile}`).default; // eslint-disable-line global-require, import/no-dynamic-require
|
||||
expect(() => {
|
||||
renderToString(<Demo />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
15
packages/meta/src/__tests__/setup.test.tsx
Normal file
15
packages/meta/src/__tests__/setup.test.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { render } from '../../tests/utils';
|
||||
|
||||
describe('SetUp.Test', () => {
|
||||
it('diff of React 18 & React 17', () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
<div>{['bamboo', '', 'little']}</div>
|
||||
<div>{['', '']}</div>
|
||||
</>,
|
||||
);
|
||||
expect(container.childNodes).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,5 +1,6 @@
|
||||
---
|
||||
category: Components
|
||||
toc: content
|
||||
title: Util 工具类
|
||||
subtitle: 工具类
|
||||
description: 辅助开发,提供一些常用的工具方法。
|
||||
|
197
packages/meta/src/affix/__tests__/Affix.test.tsx
Normal file
197
packages/meta/src/affix/__tests__/Affix.test.tsx
Normal file
@ -0,0 +1,197 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
import Affix from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
|
||||
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
|
||||
|
||||
interface AffixProps {
|
||||
offsetTop?: number;
|
||||
offsetBottom?: number;
|
||||
style?: React.CSSProperties;
|
||||
onChange?: () => void;
|
||||
onTestUpdatePosition?: () => void;
|
||||
}
|
||||
|
||||
const AffixMounter: React.FC<AffixProps> = (props) => {
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (container.current) {
|
||||
container.current.addEventListener = jest
|
||||
.fn()
|
||||
.mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Event) => void) => {
|
||||
(events as any)[event] = cb;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div ref={container} className="container">
|
||||
<Affix className="fixed" target={() => container.current} {...props}>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Affix Render', () => {
|
||||
rtlTest(Affix as any);
|
||||
accessibilityTest(Affix as any);
|
||||
|
||||
const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect');
|
||||
|
||||
const classRect: Record<string, DOMRect> = { container: { top: 0, bottom: 100 } as DOMRect };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
domMock.mockImplementation(function fn(this: HTMLElement) {
|
||||
return classRect[this.className] || { top: 0, bottom: 0 };
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
domMock.mockRestore();
|
||||
});
|
||||
|
||||
const movePlaceholder = async (top: number) => {
|
||||
classRect.fixed = { top, bottom: top } as DOMRect;
|
||||
if (events.scroll == null) {
|
||||
throw new Error('scroll should be set');
|
||||
}
|
||||
events.scroll({ type: 'scroll' });
|
||||
await waitFakeTimer();
|
||||
};
|
||||
|
||||
it('Anchor render perfectly', async () => {
|
||||
const { container } = render(<AffixMounter />);
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(container.querySelector('.ant-affix')).toBeFalsy();
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(container.querySelector('.ant-affix')).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(container.querySelector('.ant-affix')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Anchor correct render when target is null', async () => {
|
||||
render(<Affix target={() => null}>test</Affix>);
|
||||
await waitFakeTimer();
|
||||
});
|
||||
|
||||
it('support offsetBottom', async () => {
|
||||
const { container } = render(<AffixMounter offsetBottom={0} />);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(container.querySelector('.ant-affix')).toBeTruthy();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(container.querySelector('.ant-affix')).toBeFalsy();
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(container.querySelector('.ant-affix')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container, rerender } = render(<AffixMounter offsetTop={0} onChange={onChange} />);
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(onChange).toHaveBeenLastCalledWith(true);
|
||||
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: 0 });
|
||||
|
||||
rerender(<AffixMounter offsetTop={10} onChange={onChange} />);
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: `10px` });
|
||||
});
|
||||
|
||||
describe('updatePosition when target changed', () => {
|
||||
it('function change', () => {
|
||||
document.body.innerHTML = `<div id="mounter" />`;
|
||||
const target = document.getElementById('mounter');
|
||||
const getTarget = () => target;
|
||||
const { container, rerender } = render(<Affix target={getTarget}>{null}</Affix>);
|
||||
rerender(<Affix target={() => null}>{null}</Affix>);
|
||||
expect(container.querySelector(`div[aria-hidden="true"]`)).toBeNull();
|
||||
expect(container.querySelector('.ant-affix')?.getAttribute('style')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('check position change before measure', async () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
<Affix offsetTop={10}>
|
||||
<Button>top</Button>
|
||||
</Affix>
|
||||
<Affix offsetBottom={10}>
|
||||
<Button>bottom</Button>
|
||||
</Affix>
|
||||
</>,
|
||||
);
|
||||
await waitFakeTimer();
|
||||
await movePlaceholder(1000);
|
||||
expect(container.querySelector<HTMLDivElement>('.ant-affix')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('do not measure when hidden', async () => {
|
||||
const { container, rerender } = render(<AffixMounter offsetBottom={0} />);
|
||||
await waitFakeTimer();
|
||||
const affixStyleEle = container.querySelector('.ant-affix');
|
||||
const firstAffixStyle = affixStyleEle ? affixStyleEle.getAttribute('style') : null;
|
||||
|
||||
rerender(<AffixMounter offsetBottom={0} style={{ display: 'none' }} />);
|
||||
await waitFakeTimer();
|
||||
const secondAffixStyle = affixStyleEle ? affixStyleEle.getAttribute('style') : null;
|
||||
|
||||
expect(firstAffixStyle).toEqual(secondAffixStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePosition when size changed', () => {
|
||||
it('add class automatically', async () => {
|
||||
document.body.innerHTML = '<div id="mounter" />';
|
||||
|
||||
const { container } = render(<AffixMounter offsetBottom={0} />, {
|
||||
container: document.getElementById('mounter')!,
|
||||
});
|
||||
|
||||
await waitFakeTimer();
|
||||
await movePlaceholder(300);
|
||||
expect(container.querySelector(`div[aria-hidden="true"]`)).toBeTruthy();
|
||||
expect(container.querySelector('.ant-affix')?.getAttribute('style')).toBeTruthy();
|
||||
});
|
||||
|
||||
// Trigger inner and outer element for the two <ResizeObserver>s.
|
||||
['.ant-btn', '.fixed'].forEach((selector) => {
|
||||
it(`trigger listener when size change: ${selector}`, async () => {
|
||||
const updateCalled = jest.fn();
|
||||
const { container } = render(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
{ container: document.getElementById('mounter')! },
|
||||
);
|
||||
|
||||
updateCalled.mockReset();
|
||||
triggerResize(container.querySelector(selector)!);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Affix Render rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
/>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,116 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/affix/demo/basic.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/basic.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/affix/demo/debug.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
style="height: 10000px;"
|
||||
>
|
||||
<div>
|
||||
Top
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
style="background: red;"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Bottom
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/debug.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/affix/demo/on-change.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
120px to affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/on-change.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/affix/demo/target.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
style="width: 100%; height: 100px; overflow: auto; border: 1px solid #40a9ff;"
|
||||
>
|
||||
<div
|
||||
style="width: 100%; height: 1000px;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Fixed at the top of container
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/target.tsx extend context correctly 2`] = `[]`;
|
@ -0,0 +1,108 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/affix/demo/basic.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix bottom
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/debug.tsx correctly 1`] = `
|
||||
<div
|
||||
style="height:10000px"
|
||||
>
|
||||
<div>
|
||||
Top
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
style="background:red"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Bottom
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/on-change.tsx correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-default"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
120px to affix top
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/affix/demo/target.tsx correctly 1`] = `
|
||||
<div
|
||||
style="width:100%;height:100px;overflow:auto;border:1px solid #40a9ff"
|
||||
>
|
||||
<div
|
||||
style="width:100%;height:1000px"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Fixed at the top of container
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
3
packages/meta/src/affix/__tests__/demo-extend.test.ts
Normal file
3
packages/meta/src/affix/__tests__/demo-extend.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('affix');
|
35
packages/meta/src/affix/__tests__/demo.test.tsx
Normal file
35
packages/meta/src/affix/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import * as React from 'react';
|
||||
import { spyElementPrototype } from 'rc-util/lib/test/domHook';
|
||||
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('affix', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
'affix',
|
||||
(Affix, props) => (
|
||||
<Affix {...props} className="fixed" target={() => document.querySelector('#holder')}>
|
||||
Bamboo
|
||||
</Affix>
|
||||
),
|
||||
{
|
||||
beforeRender: () => {
|
||||
spyElementPrototype(HTMLElement, 'getBoundingClientRect', function getBoundingClientRect() {
|
||||
// @ts-ignore
|
||||
if (this.id === 'holder') {
|
||||
return { top: 0, bottom: 100 };
|
||||
}
|
||||
// @ts-ignore
|
||||
if (this.className === 'fixed') {
|
||||
return { top: -100, bottom: -100 };
|
||||
}
|
||||
|
||||
return { top: 0, bottom: 0 };
|
||||
});
|
||||
},
|
||||
findRootElements: () => document.querySelectorAll('.ant-affix'),
|
||||
expectCount: 1,
|
||||
},
|
||||
);
|
5
packages/meta/src/affix/__tests__/image.test.ts
Normal file
5
packages/meta/src/affix/__tests__/image.test.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Affix image', () => {
|
||||
imageDemoTest('affix');
|
||||
});
|
7
packages/meta/src/affix/demo/basic.md
Normal file
7
packages/meta/src/affix/demo/basic.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
最简单的用法。
|
||||
|
||||
## en-US
|
||||
|
||||
The simplest usage.
|
24
packages/meta/src/affix/demo/basic.tsx
Normal file
24
packages/meta/src/affix/demo/basic.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [top, setTop] = React.useState<number>(100);
|
||||
const [bottom, setBottom] = React.useState<number>(100);
|
||||
return (
|
||||
<>
|
||||
<Affix offsetTop={top}>
|
||||
<Button type="primary" onClick={() => setTop(top + 10)}>
|
||||
Affix top
|
||||
</Button>
|
||||
</Affix>
|
||||
<br />
|
||||
<Affix offsetBottom={bottom}>
|
||||
<Button type="primary" onClick={() => setBottom(bottom + 10)}>
|
||||
Affix bottom
|
||||
</Button>
|
||||
</Affix>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/affix/demo/debug.md
Normal file
7
packages/meta/src/affix/demo/debug.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
DEBUG
|
||||
|
||||
## en-US
|
||||
|
||||
DEBUG
|
22
packages/meta/src/affix/demo/debug.tsx
Normal file
22
packages/meta/src/affix/demo/debug.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [top, setTop] = useState(10);
|
||||
|
||||
return (
|
||||
<div style={{ height: 10000 }}>
|
||||
<div>Top</div>
|
||||
<Affix offsetTop={top}>
|
||||
<div style={{ background: 'red' }}>
|
||||
<Button type="primary" onClick={() => setTop(top + 10)}>
|
||||
Affix top
|
||||
</Button>
|
||||
</div>
|
||||
</Affix>
|
||||
<div>Bottom</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
packages/meta/src/affix/demo/on-change.md
Normal file
7
packages/meta/src/affix/demo/on-change.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
可以获得是否固定的状态。
|
||||
|
||||
## en-US
|
||||
|
||||
Callback with affixed state.
|
10
packages/meta/src/affix/demo/on-change.tsx
Normal file
10
packages/meta/src/affix/demo/on-change.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Affix offsetTop={120} onChange={(affixed) => console.log(affixed)}>
|
||||
<Button>120px to affix top</Button>
|
||||
</Affix>
|
||||
);
|
||||
|
||||
export default App;
|
19
packages/meta/src/affix/demo/target.md
Normal file
19
packages/meta/src/affix/demo/target.md
Normal file
@ -0,0 +1,19 @@
|
||||
## zh-CN
|
||||
|
||||
用 `target` 设置 `Affix` 需要监听其滚动事件的元素,默认为 `window`。
|
||||
|
||||
## en-US
|
||||
|
||||
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
|
||||
|
||||
<style>
|
||||
#components-affix-demo-target .scrollable-container {
|
||||
height: 100px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#components-affix-demo-target .background {
|
||||
padding-top: 60px;
|
||||
height: 300px;
|
||||
background-image: url('https://zos.alipayobjects.com/rmsportal/RmjwQiJorKyobvI.jpg');
|
||||
}
|
||||
</style>
|
29
packages/meta/src/affix/demo/target.tsx
Normal file
29
packages/meta/src/affix/demo/target.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Affix, Button } from 'antd';
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
width: '100%',
|
||||
height: 100,
|
||||
overflow: 'auto',
|
||||
border: '1px solid #40a9ff',
|
||||
};
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
width: '100%',
|
||||
height: 1000,
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [container, setContainer] = React.useState<HTMLDivElement | null>(null);
|
||||
return (
|
||||
<div style={containerStyle} ref={setContainer}>
|
||||
<div style={style}>
|
||||
<Affix target={() => container}>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
280
packages/meta/src/affix/index.tsx
Normal file
280
packages/meta/src/affix/index.tsx
Normal file
@ -0,0 +1,280 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
|
||||
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
import { getFixedBottom, getFixedTop, getTargetRect } from './utils';
|
||||
|
||||
const TRIGGER_EVENTS = [
|
||||
'resize',
|
||||
'scroll',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend',
|
||||
'pageshow',
|
||||
'load',
|
||||
] as const;
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ? window : null;
|
||||
}
|
||||
|
||||
// Affix
|
||||
export interface AffixProps {
|
||||
/** Triggered when the specified offset is reached from the top of the window */
|
||||
offsetTop?: number;
|
||||
/** Triggered when the specified offset is reached from the bottom of the window */
|
||||
offsetBottom?: number;
|
||||
style?: React.CSSProperties;
|
||||
/** Callback function triggered when fixed state changes */
|
||||
onChange?: (affixed?: boolean) => void;
|
||||
/** Set the element that Affix needs to listen to its scroll event, the value is a function that returns the corresponding DOM element */
|
||||
target?: () => Window | HTMLElement | null;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
const AFFIX_STATUS_NONE = 0;
|
||||
const AFFIX_STATUS_PREPARE = 1;
|
||||
|
||||
type AffixStatus = typeof AFFIX_STATUS_NONE | typeof AFFIX_STATUS_PREPARE;
|
||||
|
||||
interface AffixState {
|
||||
affixStyle?: React.CSSProperties;
|
||||
placeholderStyle?: React.CSSProperties;
|
||||
status: AffixStatus;
|
||||
lastAffix: boolean;
|
||||
prevTarget: Window | HTMLElement | null;
|
||||
}
|
||||
|
||||
export interface AffixRef {
|
||||
updatePosition: ReturnType<typeof throttleByAnimationFrame>;
|
||||
}
|
||||
|
||||
const Affix = React.forwardRef<AffixRef, AffixProps>((props, ref) => {
|
||||
const {
|
||||
style,
|
||||
offsetTop,
|
||||
offsetBottom,
|
||||
prefixCls,
|
||||
className,
|
||||
rootClassName,
|
||||
children,
|
||||
target,
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const { getPrefixCls, getTargetContainer } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const affixPrefixCls = getPrefixCls('affix', prefixCls);
|
||||
|
||||
const [lastAffix, setLastAffix] = React.useState(false);
|
||||
const [affixStyle, setAffixStyle] = React.useState<React.CSSProperties>();
|
||||
const [placeholderStyle, setPlaceholderStyle] = React.useState<React.CSSProperties>();
|
||||
|
||||
const status = React.useRef<AffixStatus>(AFFIX_STATUS_NONE);
|
||||
|
||||
const prevTarget = React.useRef<Window | HTMLElement | null>(null);
|
||||
const prevListener = React.useRef<EventListener>();
|
||||
|
||||
const placeholderNodeRef = React.useRef<HTMLDivElement>(null);
|
||||
const fixedNodeRef = React.useRef<HTMLDivElement>(null);
|
||||
const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const targetFunc = target ?? getTargetContainer ?? getDefaultTarget;
|
||||
|
||||
const internalOffsetTop = offsetBottom === undefined && offsetTop === undefined ? 0 : offsetTop;
|
||||
|
||||
// =================== Measure ===================
|
||||
const measure = () => {
|
||||
if (
|
||||
status.current !== AFFIX_STATUS_PREPARE ||
|
||||
!fixedNodeRef.current ||
|
||||
!placeholderNodeRef.current ||
|
||||
!targetFunc
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetNode = targetFunc();
|
||||
if (targetNode) {
|
||||
const newState: Partial<AffixState> = {
|
||||
status: AFFIX_STATUS_NONE,
|
||||
};
|
||||
const placeholderRect = getTargetRect(placeholderNodeRef.current);
|
||||
|
||||
if (
|
||||
placeholderRect.top === 0 &&
|
||||
placeholderRect.left === 0 &&
|
||||
placeholderRect.width === 0 &&
|
||||
placeholderRect.height === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const fixedTop = getFixedTop(placeholderRect, targetRect, internalOffsetTop);
|
||||
const fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom);
|
||||
|
||||
if (fixedTop !== undefined) {
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
top: fixedTop,
|
||||
width: placeholderRect.width,
|
||||
height: placeholderRect.height,
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderRect.width,
|
||||
height: placeholderRect.height,
|
||||
};
|
||||
} else if (fixedBottom !== undefined) {
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
bottom: fixedBottom,
|
||||
width: placeholderRect.width,
|
||||
height: placeholderRect.height,
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderRect.width,
|
||||
height: placeholderRect.height,
|
||||
};
|
||||
}
|
||||
|
||||
newState.lastAffix = !!newState.affixStyle;
|
||||
|
||||
if (lastAffix !== newState.lastAffix) {
|
||||
onChange?.(newState.lastAffix);
|
||||
}
|
||||
|
||||
status.current = newState.status!;
|
||||
setAffixStyle(newState.affixStyle);
|
||||
setPlaceholderStyle(newState.placeholderStyle);
|
||||
setLastAffix(newState.lastAffix);
|
||||
}
|
||||
};
|
||||
|
||||
const prepareMeasure = () => {
|
||||
status.current = AFFIX_STATUS_PREPARE;
|
||||
measure();
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
(props as any)?.onTestUpdatePosition?.();
|
||||
}
|
||||
};
|
||||
|
||||
const updatePosition = throttleByAnimationFrame(() => {
|
||||
prepareMeasure();
|
||||
});
|
||||
|
||||
const lazyUpdatePosition = throttleByAnimationFrame(() => {
|
||||
// Check position change before measure to make Safari smooth
|
||||
if (targetFunc && affixStyle) {
|
||||
const targetNode = targetFunc();
|
||||
if (targetNode && placeholderNodeRef.current) {
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const placeholderRect = getTargetRect(placeholderNodeRef.current);
|
||||
const fixedTop = getFixedTop(placeholderRect, targetRect, internalOffsetTop);
|
||||
const fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom);
|
||||
|
||||
if (
|
||||
(fixedTop !== undefined && affixStyle.top === fixedTop) ||
|
||||
(fixedBottom !== undefined && affixStyle.bottom === fixedBottom)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Directly call prepare measure since it's already throttled.
|
||||
prepareMeasure();
|
||||
});
|
||||
|
||||
const addListeners = () => {
|
||||
const listenerTarget = targetFunc?.();
|
||||
if (!listenerTarget) {
|
||||
return;
|
||||
}
|
||||
TRIGGER_EVENTS.forEach((eventName) => {
|
||||
if (prevListener.current) {
|
||||
prevTarget.current?.removeEventListener(eventName, prevListener.current);
|
||||
}
|
||||
listenerTarget?.addEventListener(eventName, lazyUpdatePosition);
|
||||
});
|
||||
prevTarget.current = listenerTarget;
|
||||
prevListener.current = lazyUpdatePosition;
|
||||
};
|
||||
|
||||
const removeListeners = () => {
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = null;
|
||||
}
|
||||
const newTarget = targetFunc?.();
|
||||
TRIGGER_EVENTS.forEach((eventName) => {
|
||||
newTarget?.removeEventListener(eventName, lazyUpdatePosition);
|
||||
if (prevListener.current) {
|
||||
prevTarget.current?.removeEventListener(eventName, prevListener.current);
|
||||
}
|
||||
});
|
||||
updatePosition.cancel();
|
||||
lazyUpdatePosition.cancel();
|
||||
};
|
||||
|
||||
React.useImperativeHandle(ref, () => ({ updatePosition }));
|
||||
|
||||
// mount & unmount
|
||||
React.useEffect(() => {
|
||||
// [Legacy] Wait for parent component ref has its value.
|
||||
// We should use target as directly element instead of function which makes element check hard.
|
||||
timer.current = setTimeout(addListeners);
|
||||
return () => removeListeners();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
addListeners();
|
||||
}, [target, affixStyle]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updatePosition();
|
||||
}, [target, offsetTop, offsetBottom]);
|
||||
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(affixPrefixCls);
|
||||
|
||||
const rootCls = classNames(rootClassName, hashId, affixPrefixCls, cssVarCls);
|
||||
|
||||
const mergedCls = classNames({ [rootCls]: affixStyle });
|
||||
|
||||
let otherProps = omit(props, [
|
||||
'prefixCls',
|
||||
'offsetTop',
|
||||
'offsetBottom',
|
||||
'target',
|
||||
'onChange',
|
||||
'rootClassName',
|
||||
]);
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
otherProps = omit(otherProps, ['onTestUpdatePosition' as any]);
|
||||
}
|
||||
|
||||
return wrapCSSVar(
|
||||
<ResizeObserver onResize={updatePosition}>
|
||||
<div style={style} className={className} ref={placeholderNodeRef} {...otherProps}>
|
||||
{affixStyle && <div style={placeholderStyle} aria-hidden="true" />}
|
||||
<div className={mergedCls} ref={fixedNodeRef} style={affixStyle}>
|
||||
<ResizeObserver onResize={updatePosition}>{children}</ResizeObserver>
|
||||
</div>
|
||||
</div>
|
||||
</ResizeObserver>,
|
||||
);
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Affix.displayName = 'Affix';
|
||||
}
|
||||
|
||||
export default Affix;
|
63
packages/meta/src/affix/index.zh-CN.md
Normal file
63
packages/meta/src/affix/index.zh-CN.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
category: Components
|
||||
toc: content
|
||||
title: Affix
|
||||
subtitle: 固钉
|
||||
description: 将页面元素钉在可视范围。
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*YSm4RI3iOJ8AAAAAAAAAAAAADrJ8AQ/original
|
||||
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*03dxS64LxeQAAAAAAAAAAAAADrJ8AQ/original
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
title: 其他
|
||||
order: 7
|
||||
---
|
||||
|
||||
## 何时使用
|
||||
|
||||
当内容区域比较长,需要滚动页面时,这部分内容对应的操作或者导航需要在滚动范围内始终展现。常用于侧边菜单和按钮组合。
|
||||
|
||||
页面可视范围过小时,慎用此功能以免出现遮挡页面内容的情况。
|
||||
|
||||
> 开发者注意事项:
|
||||
>
|
||||
> 自 `5.10.0` 起,由于 Affix 组件由 class 重构为 FC,之前获取 `ref` 并调用内部实例方法的写法都会失效。
|
||||
|
||||
## 代码演示
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx">基本</code>
|
||||
<code src="./demo/on-change.tsx">固定状态改变的回调</code>
|
||||
<code src="./demo/target.tsx">滚动容器</code>
|
||||
<code src="./demo/debug.tsx" debug>调整浏览器大小,观察 Affix 容器是否发生变化。跟随变化为正常。#17678</code>
|
||||
|
||||
## API
|
||||
|
||||
通用属性参考:[通用属性](/docs/react/common-props)
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | - |
|
||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | 0 |
|
||||
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window |
|
||||
| onChange | 固定状态改变时触发的回调函数 | (affixed?: boolean) => void | - |
|
||||
|
||||
**注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位:
|
||||
|
||||
```jsx
|
||||
<Affix style={{ position: 'absolute', top: y, left: x }}>...</Affix>
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### Affix 使用 `target` 绑定容器时,元素会跑到容器外。
|
||||
|
||||
从性能角度考虑,我们只监听容器滚动事件。如果希望任意滚动,你可以在窗体添加滚动监听:<https://codesandbox.io/s/stupefied-maxwell-ophqnm?file=/index.js>
|
||||
|
||||
相关 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)
|
30
packages/meta/src/affix/style/index.ts
Normal file
30
packages/meta/src/affix/style/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
|
||||
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
|
||||
import { genStyleHooks } from '../../theme/internal';
|
||||
|
||||
export interface ComponentToken {
|
||||
zIndexPopup: number;
|
||||
}
|
||||
|
||||
interface AffixToken extends FullToken<'Affix'> {
|
||||
//
|
||||
}
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedAffixStyle: GenerateStyle<AffixToken> = (token): CSSObject => {
|
||||
const { componentCls } = token;
|
||||
return {
|
||||
[componentCls]: {
|
||||
position: 'fixed',
|
||||
zIndex: token.zIndexPopup,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const prepareComponentToken: GetDefaultToken<'Affix'> = (token) => ({
|
||||
zIndexPopup: token.zIndexBase + 10,
|
||||
});
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genStyleHooks('Affix', genSharedAffixStyle, prepareComponentToken);
|
32
packages/meta/src/affix/utils.ts
Normal file
32
packages/meta/src/affix/utils.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export type BindElement = HTMLElement | Window | null | undefined;
|
||||
|
||||
export function getTargetRect(target: BindElement): DOMRect {
|
||||
return target !== window
|
||||
? (target as HTMLElement).getBoundingClientRect()
|
||||
: ({ top: 0, bottom: window.innerHeight } as DOMRect);
|
||||
}
|
||||
|
||||
export function getFixedTop(placeholderRect: DOMRect, targetRect: DOMRect, offsetTop?: number) {
|
||||
if (
|
||||
offsetTop !== undefined &&
|
||||
Math.round(targetRect.top) > Math.round(placeholderRect.top) - offsetTop
|
||||
) {
|
||||
return offsetTop + targetRect.top;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getFixedBottom(
|
||||
placeholderRect: DOMRect,
|
||||
targetRect: DOMRect,
|
||||
offsetBottom?: number,
|
||||
) {
|
||||
if (
|
||||
offsetBottom !== undefined &&
|
||||
Math.round(targetRect.bottom) < Math.round(placeholderRect.bottom) + offsetBottom
|
||||
) {
|
||||
const targetBottomOffset = window.innerHeight - targetRect.bottom;
|
||||
return offsetBottom + targetBottomOffset;
|
||||
}
|
||||
return undefined;
|
||||
}
|
@ -7,20 +7,26 @@ import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
|
||||
import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled';
|
||||
import classNames from 'classnames';
|
||||
import CSSMotion from 'rc-motion';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||
|
||||
import type { ClosableType } from '../_util/hooks/useClosable';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AlertRef {
|
||||
nativeElement: HTMLDivElement;
|
||||
}
|
||||
|
||||
export interface AlertProps {
|
||||
/** Type of Alert styles, options:`success`, `info`, `warning`, `error` */
|
||||
type?: 'success' | 'info' | 'warning' | 'error';
|
||||
/** Whether Alert can be closed */
|
||||
closable?: boolean;
|
||||
closable?: ClosableType;
|
||||
/**
|
||||
* @deprecated please use `closeIcon` instead.
|
||||
* @deprecated please use `closable.closeIcon` instead.
|
||||
* Close text to show
|
||||
*/
|
||||
closeText?: React.ReactNode;
|
||||
@ -42,12 +48,13 @@ export interface AlertProps {
|
||||
rootClassName?: string;
|
||||
banner?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
/** Custom closeIcon */
|
||||
closeIcon?: boolean | React.ReactNode;
|
||||
closeIcon?: React.ReactNode;
|
||||
action?: React.ReactNode;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const iconMapFilled = {
|
||||
@ -77,25 +84,32 @@ const IconNode: React.FC<IconNodeProps> = (props) => {
|
||||
return React.createElement(iconType, { className: `${prefixCls}-icon` });
|
||||
};
|
||||
|
||||
interface CloseIconProps {
|
||||
type CloseIconProps = {
|
||||
isClosable: boolean;
|
||||
prefixCls: AlertProps['prefixCls'];
|
||||
closeIcon: AlertProps['closeIcon'];
|
||||
handleClose: AlertProps['onClose'];
|
||||
}
|
||||
ariaProps: React.AriaAttributes;
|
||||
};
|
||||
|
||||
const CloseIcon: React.FC<CloseIconProps> = (props) => {
|
||||
const { isClosable, prefixCls, closeIcon, handleClose } = props;
|
||||
const CloseIconNode: React.FC<CloseIconProps> = (props) => {
|
||||
const { isClosable, prefixCls, closeIcon, handleClose, ariaProps } = props;
|
||||
const mergedCloseIcon =
|
||||
closeIcon === true || closeIcon === undefined ? <CloseOutlined /> : closeIcon;
|
||||
return isClosable ? (
|
||||
<button type="button" onClick={handleClose} className={`${prefixCls}-close-icon`} tabIndex={0}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClose}
|
||||
className={`${prefixCls}-close-icon`}
|
||||
tabIndex={0}
|
||||
{...ariaProps}
|
||||
>
|
||||
{mergedCloseIcon}
|
||||
</button>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const Alert: React.FC<AlertProps> = (props) => {
|
||||
const Alert = React.forwardRef<AlertRef, AlertProps>((props, ref) => {
|
||||
const {
|
||||
description,
|
||||
prefixCls: customizePrefixCls,
|
||||
@ -113,6 +127,7 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
closeText,
|
||||
closeIcon,
|
||||
action,
|
||||
id,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
@ -120,10 +135,15 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Alert');
|
||||
warning.deprecated(!closeText, 'closeText', 'closeIcon');
|
||||
warning.deprecated(!closeText, 'closeText', 'closable.closeIcon');
|
||||
}
|
||||
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const internalRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
nativeElement: internalRef.current!,
|
||||
}));
|
||||
|
||||
const { getPrefixCls, direction, alert } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
|
||||
@ -143,7 +163,8 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
}, [props.type, banner]);
|
||||
|
||||
// closeable when closeText or closeIcon is assigned
|
||||
const isClosable = React.useMemo(() => {
|
||||
const isClosable = React.useMemo<boolean>(() => {
|
||||
if (typeof closable === 'object' && closable.closeIcon) return true;
|
||||
if (closeText) {
|
||||
return true;
|
||||
}
|
||||
@ -151,8 +172,12 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
return closable;
|
||||
}
|
||||
// should be true when closeIcon is 0 or ''
|
||||
return closeIcon !== false && closeIcon !== null && closeIcon !== undefined;
|
||||
}, [closeText, closeIcon, closable]);
|
||||
if (closeIcon !== false && closeIcon !== null && closeIcon !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !!alert?.closable;
|
||||
}, [closeText, closeIcon, closable, alert?.closable]);
|
||||
|
||||
// banner mode defaults to Icon
|
||||
const isShowIcon = banner && showIcon === undefined ? true : showIcon;
|
||||
@ -175,6 +200,32 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
|
||||
const restProps = pickAttrs(otherProps, { aria: true, data: true });
|
||||
|
||||
const mergedCloseIcon = React.useMemo(() => {
|
||||
if (typeof closable === 'object' && closable.closeIcon) {
|
||||
return closable.closeIcon;
|
||||
}
|
||||
if (closeText) {
|
||||
return closeText;
|
||||
}
|
||||
if (closeIcon !== undefined) {
|
||||
return closeIcon;
|
||||
}
|
||||
if (typeof alert?.closable === 'object' && alert?.closable?.closeIcon) {
|
||||
return alert?.closable?.closeIcon;
|
||||
}
|
||||
return alert?.closeIcon;
|
||||
}, [closeIcon, closable, closeText, alert?.closeIcon]);
|
||||
|
||||
const mergedAriaProps = React.useMemo<React.AriaAttributes>(() => {
|
||||
const merged = closable ?? alert?.closable;
|
||||
if (typeof merged === 'object') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { closeIcon: _, ...ariaProps } = merged;
|
||||
return ariaProps;
|
||||
}
|
||||
return {};
|
||||
}, [closable, alert?.closable]);
|
||||
|
||||
return wrapCSSVar(
|
||||
<CSSMotion
|
||||
visible={!closed}
|
||||
@ -184,9 +235,10 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
onLeaveStart={(node) => ({ maxHeight: node.offsetHeight })}
|
||||
onLeaveEnd={afterClose}
|
||||
>
|
||||
{({ className: motionClassName, style: motionStyle }) => (
|
||||
{({ className: motionClassName, style: motionStyle }, setRef) => (
|
||||
<div
|
||||
ref={ref}
|
||||
id={id}
|
||||
ref={composeRef(internalRef, setRef)}
|
||||
data-show={!closed}
|
||||
className={classNames(alertCls, motionClassName)}
|
||||
style={{ ...alert?.style, ...style, ...motionStyle }}
|
||||
@ -209,17 +261,18 @@ const Alert: React.FC<AlertProps> = (props) => {
|
||||
{description ? <div className={`${prefixCls}-description`}>{description}</div> : null}
|
||||
</div>
|
||||
{action ? <div className={`${prefixCls}-action`}>{action}</div> : null}
|
||||
<CloseIcon
|
||||
<CloseIconNode
|
||||
isClosable={isClosable}
|
||||
prefixCls={prefixCls}
|
||||
closeIcon={closeText || closeIcon}
|
||||
closeIcon={mergedCloseIcon}
|
||||
handleClose={handleClose}
|
||||
ariaProps={mergedAriaProps}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CSSMotion>,
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Alert.displayName = 'Alert';
|
||||
|
@ -1,10 +1,12 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Alert from './Alert';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
message?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryStates {
|
||||
@ -27,7 +29,7 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
|
||||
}
|
||||
|
||||
render() {
|
||||
const { message, description, children } = this.props;
|
||||
const { message, description, id, children } = this.props;
|
||||
const { error, info } = this.state;
|
||||
const componentStack = info && info.componentStack ? info.componentStack : null;
|
||||
const errorMessage = typeof message === 'undefined' ? (error || '').toString() : message;
|
||||
@ -35,6 +37,7 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
|
||||
if (error) {
|
||||
return (
|
||||
<Alert
|
||||
id={id}
|
||||
type="error"
|
||||
message={errorMessage}
|
||||
description={
|
||||
|
@ -583,6 +583,57 @@ exports[`renders components/alert/demo/closable.tsx extend context correctly 1`]
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Error Text
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert-description"
|
||||
>
|
||||
Error Description Error Description Error Description Error Description Error Description Error Description
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="close"
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close-square"
|
||||
class="anticon anticon-close-square"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-square"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 112c17.7 0 32 14.3 32 32v736c0 17.7-14.3 32-32 32H144c-17.7 0-32-14.3-32-32V144c0-17.7 14.3-32 32-32zM639.98 338.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -573,6 +573,57 @@ exports[`renders components/alert/demo/closable.tsx correctly 1`] = `
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Error Text
|
||||
</div>
|
||||
<div
|
||||
class="ant-alert-description"
|
||||
>
|
||||
Error Description Error Description Error Description Error Description Error Description Error Description
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
aria-label="close"
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="close-square"
|
||||
class="anticon anticon-close-square"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-square"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M880 112c17.7 0 32 14.3 32 32v736c0 17.7-14.3 32-32 32H144c-17.7 0-32-14.3-32-32V144c0-17.7 14.3-32 32-32zM639.98 338.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import React from 'react';
|
||||
|
||||
import Alert from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { act, render, screen } from '../../../tests/utils';
|
||||
import { act, render, screen, waitFakeTimer } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Popconfirm from '../../popconfirm';
|
||||
import Tooltip from '../../tooltip';
|
||||
import type { AlertRef } from '../Alert';
|
||||
|
||||
const { ErrorBoundary } = Alert;
|
||||
|
||||
@ -24,6 +26,7 @@ describe('Alert', () => {
|
||||
});
|
||||
|
||||
it('should show close button and could be closed', async () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const onClose = jest.fn();
|
||||
render(
|
||||
<Alert
|
||||
@ -34,13 +37,14 @@ describe('Alert', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /close/i }));
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
await userEvent.click(screen.getByRole('button', { name: /close/i }));
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
expect(errSpy).not.toHaveBeenCalled();
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('custom action', () => {
|
||||
@ -106,11 +110,9 @@ describe('Alert', () => {
|
||||
|
||||
await userEvent.hover(screen.getByRole('alert'));
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(screen.getByRole('tooltip')).toBeInTheDocument();
|
||||
expect(document.querySelector<HTMLDivElement>('.ant-tooltip')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('could be used with Popconfirm', async () => {
|
||||
@ -154,6 +156,31 @@ describe('Alert', () => {
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('close button should be support aria-* by closable', () => {
|
||||
const { container, rerender } = render(<Alert />);
|
||||
expect(container.querySelector('*[aria-label]')).toBeFalsy();
|
||||
rerender(<Alert closable={{ 'aria-label': 'Close' }} closeIcon="CloseIcon" />);
|
||||
expect(container.querySelector('[aria-label="Close"]')).toBeTruthy();
|
||||
rerender(<Alert closable={{ 'aria-label': 'Close' }} closeText="CloseText" />);
|
||||
expect(container.querySelector('[aria-label="Close"]')).toBeTruthy();
|
||||
rerender(<Alert closable={{ 'aria-label': 'Close', closeIcon: 'CloseIconProp' }} />);
|
||||
expect(container.querySelector('[aria-label="Close"]')).toBeTruthy();
|
||||
});
|
||||
it('close button should be support custom icon by closable', () => {
|
||||
const { container, rerender } = render(<Alert />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')).toBeFalsy();
|
||||
rerender(<Alert closable={{ closeIcon: 'CloseBtn' }} />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('CloseBtn');
|
||||
rerender(<Alert closable={{ closeIcon: 'CloseBtn' }} closeIcon="CloseBtn2" />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('CloseBtn');
|
||||
rerender(<Alert closable={{ closeIcon: 'CloseBtn' }} closeText="CloseBtn3" />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('CloseBtn');
|
||||
rerender(<Alert closeText="CloseBtn2" />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('CloseBtn2');
|
||||
rerender(<Alert closeIcon="CloseBtn3" />);
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('CloseBtn3');
|
||||
});
|
||||
|
||||
it('should warning when using closeText', () => {
|
||||
resetWarned();
|
||||
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
@ -161,11 +188,20 @@ describe('Alert', () => {
|
||||
const { container } = render(<Alert closeText="close" />);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
`Warning: [antd: Alert] \`closeText\` is deprecated. Please use \`closeIcon\` instead.`,
|
||||
`Warning: [antd: Alert] \`closeText\` is deprecated. Please use \`closable.closeIcon\` instead.`,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-alert-close-icon')?.textContent).toBe('close');
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should support id and ref', () => {
|
||||
const alertRef = React.createRef<AlertRef>();
|
||||
const { container } = render(<Alert id="test-id" ref={alertRef} />);
|
||||
const element = container.querySelector<HTMLDivElement>('#test-id');
|
||||
expect(element).toBeTruthy();
|
||||
expect(alertRef.current?.nativeElement).toBeTruthy();
|
||||
expect(alertRef.current?.nativeElement).toBe(element);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Button, Space } from '@zhst/meta';
|
||||
import { Alert, Button, Space } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert } from '@zhst/meta';
|
||||
import { Alert } from 'antd';
|
||||
|
||||
const App: React.FC = () => <Alert message="Success Text" type="success" />;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { CloseSquareFilled } from '@ant-design/icons';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const onClose = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
console.log(e, 'I was closed.');
|
||||
@ -20,6 +21,16 @@ const App: React.FC = () => (
|
||||
closable
|
||||
onClose={onClose}
|
||||
/>
|
||||
<Alert
|
||||
message="Error Text"
|
||||
description="Error Description Error Description Error Description Error Description Error Description Error Description"
|
||||
type="error"
|
||||
closable={{
|
||||
'aria-label': 'close',
|
||||
closeIcon: <CloseSquareFilled />,
|
||||
}}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { Alert, ConfigProvider } from '@zhst/meta';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import { Alert, ConfigProvider } from 'antd';
|
||||
|
||||
const icon = <SmileOutlined />;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { SmileOutlined } from '@ant-design/icons';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const icon = <SmileOutlined />;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Alert, Button } from '@zhst/meta';
|
||||
import { Alert, Button } from 'antd';
|
||||
|
||||
const { ErrorBoundary } = Alert;
|
||||
const ThrowError: React.FC = () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Alert } from 'antd';
|
||||
import Marquee from 'react-fast-marquee';
|
||||
import { Alert } from '@zhst/meta';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Alert
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Alert, Switch, Space } from '@zhst/meta';
|
||||
import { Alert, Space, Switch } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Alert, Space } from '@zhst/meta';
|
||||
import { Alert, Space } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
|
39
packages/meta/src/alert/design/behavior-pattern.tsx
Normal file
39
packages/meta/src/alert/design/behavior-pattern.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
import BehaviorMap from '../../../.dumi/theme/common/BehaviorMap';
|
||||
|
||||
const BehaviorPattern: React.FC = () => (
|
||||
<BehaviorMap
|
||||
data={{
|
||||
id: '200000004',
|
||||
label: '了解页面/模块内需要关注的提示',
|
||||
children: [
|
||||
{
|
||||
id: '500000061',
|
||||
label: '了解提示信息',
|
||||
targetType: 'mvp',
|
||||
children: [
|
||||
{
|
||||
id: '707000085',
|
||||
label: '了解提示内容',
|
||||
link: 'components-alert-index-tab-design-demo-content',
|
||||
},
|
||||
{
|
||||
id: '707000086',
|
||||
label: '了解提示类型',
|
||||
link: 'components-alert-index-tab-design-demo-type',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '200000005',
|
||||
label: '针对提示进行操作',
|
||||
targetType: 'extension',
|
||||
link: 'components-alert-index-tab-design-demo-action',
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default BehaviorPattern;
|
102
packages/meta/src/alert/design/demo/action.tsx
Normal file
102
packages/meta/src/alert/design/demo/action.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { Alert, Flex, Typography } from 'antd';
|
||||
|
||||
const Demo = () => {
|
||||
const [expandA, setExpandA] = React.useState(false);
|
||||
const [expandB, setExpandB] = React.useState(true);
|
||||
|
||||
return (
|
||||
<Flex gap="large" vertical style={{ maxWidth: 600 }}>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>关闭提示</div>
|
||||
<Alert showIcon closable message="你好!欢迎使用专业版,你可以根据自身需求添加业务模块。" />
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message="帮助信息"
|
||||
description="你好,由于你的良好信用,我们决定赠送你三个月产品会员,欲了解会员特权与活动请进首页会员专区查看。"
|
||||
/>
|
||||
</Flex>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>展开/收起提示</div>
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message={
|
||||
<div>
|
||||
<Typography.Paragraph ellipsis={!expandA && { rows: 2 }} style={{ marginBottom: 8 }}>
|
||||
提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。
|
||||
</Typography.Paragraph>
|
||||
<Typography.Link onClick={() => setExpandA((prev) => !prev)}>
|
||||
{expandA ? '收起' : '展开更多'}
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
style={{ alignItems: 'baseline' }}
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message={
|
||||
<div>
|
||||
<Typography.Paragraph ellipsis={!expandB && { rows: 2 }} style={{ marginBottom: 8 }}>
|
||||
提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。提示信息超过2行时,可以使用将部分信息折叠,以减少空间占用。
|
||||
</Typography.Paragraph>
|
||||
<Typography.Link onClick={() => setExpandB((prev) => !prev)}>
|
||||
{expandB ? '收起' : '展开更多'}
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
style={{ alignItems: 'baseline' }}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>执行其他操作</div>
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message="提示信息不超过一行时,按钮放在信息右侧。"
|
||||
action={<Typography.Link>相关操作</Typography.Link>}
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message={
|
||||
<div>
|
||||
<Typography.Paragraph style={{ marginBottom: 8 }}>
|
||||
提示信息超过一行,此时按钮按照从上至下的视觉流,放置在信息区下方,这样浏览起来更流畅,即先阅读提示信息,再根据信息判断执行什么操作。
|
||||
</Typography.Paragraph>
|
||||
<Flex gap={8}>
|
||||
<Typography.Link>相关操作1</Typography.Link>
|
||||
<Typography.Link>相关操作2</Typography.Link>
|
||||
</Flex>
|
||||
</div>
|
||||
}
|
||||
style={{ alignItems: 'baseline' }}
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
closable
|
||||
message="提示标题"
|
||||
description={
|
||||
<div>
|
||||
<Typography.Paragraph style={{ marginBottom: 8 }}>
|
||||
提示信息超过一行,此时按钮按照从上至下的视觉流,放置在信息区下方,这样浏览起来更流畅,即先阅读提示信息,再根据信息判断执行什么操作。
|
||||
</Typography.Paragraph>
|
||||
<Flex gap={8}>
|
||||
<Typography.Link>相关操作1</Typography.Link>
|
||||
<Typography.Link>相关操作2</Typography.Link>
|
||||
</Flex>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Typography.Paragraph type="secondary">
|
||||
建议统一使用Link
|
||||
Button,明确可点击的同时,整体视觉也更和谐;当提示信息不超一行时,按钮放在信息右侧;当提示信息超过一行,按钮放置在信息区下方;这样能够确保用户的浏览动线一致,即先阅读提示信息,再根据信息判断执行什么操作。
|
||||
</Typography.Paragraph>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default Demo;
|
14
packages/meta/src/alert/design/demo/content.tsx
Normal file
14
packages/meta/src/alert/design/demo/content.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Alert, Flex } from 'antd';
|
||||
|
||||
const Demo = () => (
|
||||
<Flex gap="middle" vertical style={{ maxWidth: 600 }}>
|
||||
<Alert message="你好!欢迎使用专业版,你可以根据自身需求添加业务模块。" />
|
||||
<Alert
|
||||
message="帮助信息"
|
||||
description="你好,由于你的良好信用,我们决定赠送你三个月产品会员,欲了解会员特权与活动请进首页会员专区查看。"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
export default Demo;
|
61
packages/meta/src/alert/design/demo/type.tsx
Normal file
61
packages/meta/src/alert/design/demo/type.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import { Alert, Flex } from 'antd';
|
||||
|
||||
const Demo = () => (
|
||||
<Flex gap="large" vertical style={{ maxWidth: 600 }}>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>成功提示</div>
|
||||
<Alert
|
||||
showIcon
|
||||
type="success"
|
||||
message="恭喜!你所提交的信息已经审核通过,如有问题请联系客服。"
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
type="success"
|
||||
message="已成功!"
|
||||
description="你所提交的信息已经审核通过,请及时跟进申请状况。如有问题,请联系审核人员或在线客服。"
|
||||
/>
|
||||
</Flex>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>信息提示</div>
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="你好!欢迎使用专业版,你可以根据自身需求添加业务模块。"
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="帮助信息"
|
||||
description="你好,由于你的良好信用,我们决定赠送你三个月产品会员,欲了解会员特权与活动请进首页会员专区查看。"
|
||||
/>
|
||||
</Flex>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>警告提示</div>
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="系统将于 15 : 00 - 17 : 00 进行升级,请及时保存你的资料!"
|
||||
/>
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="请注意"
|
||||
description="你所提交的信息已经审核失败,可以进入个人信箱查看原因,如有疑问,请联系客服人员。"
|
||||
/>
|
||||
</Flex>
|
||||
<Flex gap="middle" vertical>
|
||||
<div>错误提示</div>
|
||||
<Alert showIcon type="error" message="系统错误,请稍后重试。" />
|
||||
<Alert
|
||||
showIcon
|
||||
type="error"
|
||||
message="出错了!"
|
||||
description="你的账户会员使用权限将在3天后到期,请及时跟进申请状况。如有问题,请联系审核人员。"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
export default Demo;
|
@ -1,11 +1,9 @@
|
||||
import type React from 'react';
|
||||
import type { AlertProps } from './Alert';
|
||||
import InternalAlert from './Alert';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
|
||||
export type { AlertProps } from './Alert';
|
||||
|
||||
type CompoundedComponent = React.FC<AlertProps> & {
|
||||
type CompoundedComponent = typeof InternalAlert & {
|
||||
ErrorBoundary: typeof ErrorBoundary;
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
---
|
||||
category: Components
|
||||
subtitle: 警告提示
|
||||
toc: content
|
||||
title: Alert 警告提示
|
||||
subtitle: 警告提示
|
||||
description: 警告提示,展现需要关注的信息。
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
@ -9,8 +11,6 @@ group:
|
||||
order: 6
|
||||
---
|
||||
|
||||
警告提示,展现需要关注的信息。
|
||||
|
||||
## 何时使用
|
||||
|
||||
- 当某个页面需要向用户显示警告的信息时。
|
||||
@ -25,7 +25,7 @@ group:
|
||||
<code src="./demo/description.tsx">含有辅助性文字介绍</code>
|
||||
<code src="./demo/icon.tsx">图标</code>
|
||||
<code src="./demo/banner.tsx" iframe="250">顶部公告</code>
|
||||
<!-- <code src="./demo/loop-banner.tsx">轮播的公告</code> -->
|
||||
<code src="./demo/loop-banner.tsx">轮播的公告</code>
|
||||
<code src="./demo/smooth-closed.tsx">平滑地卸载</code>
|
||||
<code src="./demo/error-boundary.tsx">React 错误处理</code>
|
||||
<code src="./demo/custom-icon.tsx" debug>自定义图标</code>
|
||||
@ -41,7 +41,7 @@ group:
|
||||
| action | 自定义操作项 | ReactNode | - | 4.9.0 |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | |
|
||||
| banner | 是否用作顶部公告 | boolean | false | |
|
||||
| closeIcon | 自定义关闭 Icon,>=5.7.0: 设置为 `null` 或 `false` 时隐藏关闭按钮 | boolean \| ReactNode | `<CloseOutlined />` | |
|
||||
| closable | 可关闭配置,>=5.15.0: 支持 `aria-*` | boolean \| ({ closeIcon?: React.ReactNode } & React.AriaAttributes) | `false` | |
|
||||
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
|
||||
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
|
||||
| message | 警告提示内容 | ReactNode | - | |
|
||||
|
@ -25,7 +25,6 @@ export interface ComponentToken {
|
||||
withDescriptionIconSize: number;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
type AlertToken = FullToken<'Alert'> & {
|
||||
// Custom token here
|
||||
};
|
||||
@ -55,13 +54,10 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
|
||||
lineHeight,
|
||||
borderRadiusLG: borderRadius,
|
||||
motionEaseInOutCirc,
|
||||
// @ts-ignore
|
||||
withDescriptionIconSize,
|
||||
colorText,
|
||||
colorTextHeading,
|
||||
// @ts-ignore
|
||||
withDescriptionPadding,
|
||||
// @ts-ignore
|
||||
defaultPadding,
|
||||
} = token;
|
||||
|
||||
@ -242,7 +238,7 @@ export const genActionStyle: GenerateStyle<AlertToken> = (token: AlertToken): CS
|
||||
},
|
||||
};
|
||||
};
|
||||
// @ts-ignore
|
||||
|
||||
export const prepareComponentToken: GetDefaultToken<'Alert'> = (token) => {
|
||||
const paddingHorizontal = 12; // Fixed value here.
|
||||
return {
|
||||
@ -253,7 +249,6 @@ export const prepareComponentToken: GetDefaultToken<'Alert'> = (token) => {
|
||||
};
|
||||
|
||||
export default genStyleHooks(
|
||||
// @ts-ignore
|
||||
'Alert',
|
||||
(token) => [genBaseStyle(token), genTypeStyle(token), genActionStyle(token)],
|
||||
prepareComponentToken,
|
||||
|
368
packages/meta/src/anchor/Anchor.tsx
Normal file
368
packages/meta/src/anchor/Anchor.tsx
Normal file
@ -0,0 +1,368 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useEvent } from 'rc-util';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import getScroll from '../_util/getScroll';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import Affix from '../affix';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||
import type { AnchorLinkBaseProps } from './AnchorLink';
|
||||
import AnchorLink from './AnchorLink';
|
||||
import AnchorContext from './context';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AnchorLinkItemProps extends AnchorLinkBaseProps {
|
||||
key: React.Key;
|
||||
children?: AnchorLinkItemProps[];
|
||||
}
|
||||
|
||||
export type AnchorContainer = HTMLElement | Window;
|
||||
|
||||
function getDefaultContainer() {
|
||||
return window;
|
||||
}
|
||||
|
||||
function getOffsetTop(element: HTMLElement, container: AnchorContainer): number {
|
||||
if (!element.getClientRects().length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
if (rect.width || rect.height) {
|
||||
if (container === window) {
|
||||
container = element.ownerDocument!.documentElement!;
|
||||
return rect.top - container.clientTop;
|
||||
}
|
||||
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
return rect.top;
|
||||
}
|
||||
|
||||
const sharpMatcherRegex = /#([\S ]+)$/;
|
||||
|
||||
interface Section {
|
||||
link: string;
|
||||
top: number;
|
||||
}
|
||||
|
||||
export interface AnchorProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
rootClassName?: string;
|
||||
style?: React.CSSProperties;
|
||||
/**
|
||||
* @deprecated Please use `items` instead.
|
||||
*/
|
||||
children?: React.ReactNode;
|
||||
offsetTop?: number;
|
||||
bounds?: number;
|
||||
affix?: boolean;
|
||||
showInkInFixed?: boolean;
|
||||
getContainer?: () => AnchorContainer;
|
||||
/** Return customize highlight anchor */
|
||||
getCurrentAnchor?: (activeLink: string) => string;
|
||||
onClick?: (
|
||||
e: React.MouseEvent<HTMLElement>,
|
||||
link: { title: React.ReactNode; href: string },
|
||||
) => void;
|
||||
/** Scroll to target offset value, if none, it's offsetTop prop value or 0. */
|
||||
targetOffset?: number;
|
||||
/** Listening event when scrolling change active link */
|
||||
onChange?: (currentActiveLink: string) => void;
|
||||
items?: AnchorLinkItemProps[];
|
||||
direction?: AnchorDirection;
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
export interface AnchorState {
|
||||
activeLink: null | string;
|
||||
}
|
||||
|
||||
export interface AnchorDefaultProps extends AnchorProps {
|
||||
prefixCls: string;
|
||||
affix: boolean;
|
||||
showInkInFixed: boolean;
|
||||
getContainer: () => AnchorContainer;
|
||||
}
|
||||
|
||||
export type AnchorDirection = 'vertical' | 'horizontal';
|
||||
|
||||
export interface AntAnchor {
|
||||
registerLink: (link: string) => void;
|
||||
unregisterLink: (link: string) => void;
|
||||
activeLink: string | null;
|
||||
scrollTo: (link: string) => void;
|
||||
onClick?: (
|
||||
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,
|
||||
link: { title: React.ReactNode; href: string },
|
||||
) => void;
|
||||
direction: AnchorDirection;
|
||||
}
|
||||
|
||||
const Anchor: React.FC<AnchorProps> = (props) => {
|
||||
const {
|
||||
rootClassName,
|
||||
prefixCls: customPrefixCls,
|
||||
className,
|
||||
style,
|
||||
offsetTop,
|
||||
affix = true,
|
||||
showInkInFixed = false,
|
||||
children,
|
||||
items,
|
||||
direction: anchorDirection = 'vertical',
|
||||
bounds,
|
||||
targetOffset,
|
||||
onClick,
|
||||
onChange,
|
||||
getContainer,
|
||||
getCurrentAnchor,
|
||||
replace,
|
||||
} = props;
|
||||
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Anchor');
|
||||
|
||||
warning.deprecated(!children, 'Anchor children', 'items');
|
||||
|
||||
warning(
|
||||
!(anchorDirection === 'horizontal' && items?.some((n) => 'children' in n)),
|
||||
'usage',
|
||||
'`Anchor items#children` is not supported when `Anchor` direction is horizontal.',
|
||||
);
|
||||
}
|
||||
|
||||
const [links, setLinks] = React.useState<string[]>([]);
|
||||
const [activeLink, setActiveLink] = React.useState<string | null>(null);
|
||||
const activeLinkRef = React.useRef<string | null>(activeLink);
|
||||
|
||||
const wrapperRef = React.useRef<HTMLDivElement>(null);
|
||||
const spanLinkNode = React.useRef<HTMLSpanElement>(null);
|
||||
const animating = React.useRef<boolean>(false);
|
||||
|
||||
const { direction, anchor, getTargetContainer, getPrefixCls } =
|
||||
React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('anchor', customPrefixCls);
|
||||
|
||||
const rootCls = useCSSVarCls(prefixCls);
|
||||
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
|
||||
|
||||
const getCurrentContainer = getContainer ?? getTargetContainer ?? getDefaultContainer;
|
||||
|
||||
const dependencyListItem: React.DependencyList[number] = JSON.stringify(links);
|
||||
|
||||
const registerLink = useEvent<AntAnchor['registerLink']>((link) => {
|
||||
if (!links.includes(link)) {
|
||||
setLinks((prev) => [...prev, link]);
|
||||
}
|
||||
});
|
||||
|
||||
const unregisterLink = useEvent<AntAnchor['unregisterLink']>((link) => {
|
||||
if (links.includes(link)) {
|
||||
setLinks((prev) => prev.filter((i) => i !== link));
|
||||
}
|
||||
});
|
||||
|
||||
const updateInk = () => {
|
||||
const linkNode = wrapperRef.current?.querySelector<HTMLElement>(
|
||||
`.${prefixCls}-link-title-active`,
|
||||
);
|
||||
if (linkNode && spanLinkNode.current) {
|
||||
const { style: inkStyle } = spanLinkNode.current;
|
||||
const horizontalAnchor = anchorDirection === 'horizontal';
|
||||
inkStyle.top = horizontalAnchor ? '' : `${linkNode.offsetTop + linkNode.clientHeight / 2}px`;
|
||||
inkStyle.height = horizontalAnchor ? '' : `${linkNode.clientHeight}px`;
|
||||
inkStyle.left = horizontalAnchor ? `${linkNode.offsetLeft}px` : '';
|
||||
inkStyle.width = horizontalAnchor ? `${linkNode.clientWidth}px` : '';
|
||||
if (horizontalAnchor) {
|
||||
scrollIntoView(linkNode, { scrollMode: 'if-needed', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getInternalCurrentAnchor = (_links: string[], _offsetTop = 0, _bounds = 5): string => {
|
||||
const linkSections: Section[] = [];
|
||||
const container = getCurrentContainer();
|
||||
_links.forEach((link) => {
|
||||
const sharpLinkMatch = sharpMatcherRegex.exec(link?.toString());
|
||||
if (!sharpLinkMatch) {
|
||||
return;
|
||||
}
|
||||
const target = document.getElementById(sharpLinkMatch[1]);
|
||||
if (target) {
|
||||
const top = getOffsetTop(target, container);
|
||||
if (top <= _offsetTop + _bounds) {
|
||||
linkSections.push({ link, top });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (linkSections.length) {
|
||||
const maxSection = linkSections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev));
|
||||
return maxSection.link;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const setCurrentActiveLink = useEvent((link: string) => {
|
||||
// FIXME: Seems a bug since this compare is not equals
|
||||
// `activeLinkRef` is parsed value which will always trigger `onChange` event.
|
||||
if (activeLinkRef.current === link) {
|
||||
return;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/30584
|
||||
const newLink = typeof getCurrentAnchor === 'function' ? getCurrentAnchor(link) : link;
|
||||
setActiveLink(newLink);
|
||||
activeLinkRef.current = newLink;
|
||||
|
||||
// onChange should respect the original link (which may caused by
|
||||
// window scroll or user click), not the new link
|
||||
onChange?.(link);
|
||||
});
|
||||
|
||||
const handleScroll = React.useCallback(() => {
|
||||
if (animating.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentActiveLink = getInternalCurrentAnchor(
|
||||
links,
|
||||
targetOffset !== undefined ? targetOffset : offsetTop || 0,
|
||||
bounds,
|
||||
);
|
||||
|
||||
setCurrentActiveLink(currentActiveLink);
|
||||
}, [dependencyListItem, targetOffset, offsetTop]);
|
||||
|
||||
const handleScrollTo = React.useCallback<(link: string) => void>(
|
||||
(link) => {
|
||||
setCurrentActiveLink(link);
|
||||
const sharpLinkMatch = sharpMatcherRegex.exec(link);
|
||||
if (!sharpLinkMatch) {
|
||||
return;
|
||||
}
|
||||
const targetElement = document.getElementById(sharpLinkMatch[1]);
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = getCurrentContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||
let y = scrollTop + eleOffsetTop;
|
||||
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
|
||||
animating.current = true;
|
||||
scrollTo(y, {
|
||||
getContainer: getCurrentContainer,
|
||||
callback() {
|
||||
animating.current = false;
|
||||
},
|
||||
});
|
||||
},
|
||||
[targetOffset, offsetTop],
|
||||
);
|
||||
|
||||
const wrapperClass = classNames(
|
||||
hashId,
|
||||
cssVarCls,
|
||||
rootCls,
|
||||
rootClassName,
|
||||
`${prefixCls}-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-wrapper-horizontal`]: anchorDirection === 'horizontal',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
anchor?.className,
|
||||
);
|
||||
|
||||
const anchorClass = classNames(prefixCls, {
|
||||
[`${prefixCls}-fixed`]: !affix && !showInkInFixed,
|
||||
});
|
||||
|
||||
const inkClass = classNames(`${prefixCls}-ink`, {
|
||||
[`${prefixCls}-ink-visible`]: activeLink,
|
||||
});
|
||||
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
maxHeight: offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh',
|
||||
...anchor?.style,
|
||||
...style,
|
||||
};
|
||||
|
||||
const createNestedLink = (options?: AnchorLinkItemProps[]) =>
|
||||
Array.isArray(options)
|
||||
? options.map((item) => (
|
||||
<AnchorLink replace={replace} {...item} key={item.key}>
|
||||
{anchorDirection === 'vertical' && createNestedLink(item.children)}
|
||||
</AnchorLink>
|
||||
))
|
||||
: null;
|
||||
|
||||
const anchorContent = (
|
||||
<div ref={wrapperRef} className={wrapperClass} style={wrapperStyle}>
|
||||
<div className={anchorClass}>
|
||||
<span className={inkClass} ref={spanLinkNode} />
|
||||
{'items' in props ? createNestedLink(items) : children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const scrollContainer = getCurrentContainer();
|
||||
handleScroll();
|
||||
scrollContainer?.addEventListener('scroll', handleScroll);
|
||||
return () => {
|
||||
scrollContainer?.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [dependencyListItem]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof getCurrentAnchor === 'function') {
|
||||
setCurrentActiveLink(getCurrentAnchor(activeLinkRef.current || ''));
|
||||
}
|
||||
}, [getCurrentAnchor]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updateInk();
|
||||
}, [anchorDirection, getCurrentAnchor, dependencyListItem, activeLink]);
|
||||
|
||||
const memoizedContextValue = React.useMemo<AntAnchor>(
|
||||
() => ({
|
||||
registerLink,
|
||||
unregisterLink,
|
||||
scrollTo: handleScrollTo,
|
||||
activeLink,
|
||||
onClick,
|
||||
direction: anchorDirection,
|
||||
}),
|
||||
[activeLink, onClick, handleScrollTo, anchorDirection],
|
||||
);
|
||||
|
||||
return wrapCSSVar(
|
||||
<AnchorContext.Provider value={memoizedContextValue}>
|
||||
{affix ? (
|
||||
<Affix offsetTop={offsetTop} target={getCurrentContainer}>
|
||||
{anchorContent}
|
||||
</Affix>
|
||||
) : (
|
||||
anchorContent
|
||||
)}
|
||||
</AnchorContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Anchor.displayName = 'Anchor';
|
||||
}
|
||||
|
||||
export default Anchor;
|
94
packages/meta/src/anchor/AnchorLink.tsx
Normal file
94
packages/meta/src/anchor/AnchorLink.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { devUseWarning } from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { AntAnchor } from './Anchor';
|
||||
import AnchorContext from './context';
|
||||
|
||||
export interface AnchorLinkBaseProps {
|
||||
prefixCls?: string;
|
||||
href: string;
|
||||
target?: string;
|
||||
title: React.ReactNode;
|
||||
className?: string;
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
export interface AnchorLinkProps extends AnchorLinkBaseProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
const {
|
||||
href,
|
||||
title,
|
||||
prefixCls: customizePrefixCls,
|
||||
children,
|
||||
className,
|
||||
target,
|
||||
replace,
|
||||
} = props;
|
||||
|
||||
const context = React.useContext<AntAnchor | undefined>(AnchorContext);
|
||||
|
||||
const { registerLink, unregisterLink, scrollTo, onClick, activeLink, direction } = context || {};
|
||||
|
||||
React.useEffect(() => {
|
||||
registerLink?.(href);
|
||||
return () => {
|
||||
unregisterLink?.(href);
|
||||
};
|
||||
}, [href]);
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
||||
onClick?.(e, { title, href });
|
||||
scrollTo?.(href);
|
||||
if (replace) {
|
||||
e.preventDefault();
|
||||
window.location.replace(href);
|
||||
}
|
||||
};
|
||||
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('Anchor.Link');
|
||||
|
||||
warning(
|
||||
!children || direction !== 'horizontal',
|
||||
'usage',
|
||||
'`Anchor.Link children` is not supported when `Anchor` direction is horizontal',
|
||||
);
|
||||
}
|
||||
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
|
||||
const active = activeLink === href;
|
||||
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, className, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
});
|
||||
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: active,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<a
|
||||
className={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
{direction !== 'horizontal' ? children : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnchorLink;
|
1024
packages/meta/src/anchor/__tests__/Anchor.test.tsx
Normal file
1024
packages/meta/src/anchor/__tests__/Anchor.test.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,163 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Anchor Render render items and ignore jsx children 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly#horizontal 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,782 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/anchor/demo/basic.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height: 100vh; background: rgba(255, 0, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/basic.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/component-token.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height: 100vh; background: rgba(255, 0, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/component-token.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/customizeHighlight.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/customizeHighlight.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/horizontal.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
style="padding: 20px;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="left: 0px; width: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width: 100vw; height: 100vh; text-align: center; background: rgba(0, 255, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width: 100vw; height: 100vh; text-align: center; background: rgba(0, 0, 255, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width: 100vw; height: 100vh; text-align: center; background: rgb(255, 251, 233);"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/horizontal.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/legacy-anchor.tsx extend context correctly 2`] = `
|
||||
[
|
||||
"Warning: [antd: Anchor] \`Anchor children\` is deprecated. Please use \`items\` instead.",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/onChange.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/onChange.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/onClick.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/onClick.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/replace.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height: 100vh; background: rgba(255, 0, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/replace.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/static.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/static.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/anchor/demo/targetOffset.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-18"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height: 100vh; background: rgba(255, 0, 0, 0.02); margin-top: 30vh;"
|
||||
>
|
||||
Part 1
|
||||
</div>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height: 100vh; background: rgba(0, 255, 0, 0.02);"
|
||||
>
|
||||
Part 2
|
||||
</div>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height: 100vh; background: rgba(0, 0, 255, 0.02);"
|
||||
>
|
||||
Part 3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink ant-anchor-ink-visible"
|
||||
style="top: 0px; height: 0px;"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link ant-anchor-link-active"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title ant-anchor-link-title-active"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="height: 30vh; background: rgba(0, 0, 0, 0.85); position: fixed; top: 0px; left: 0px; width: 75%; color: rgb(255, 255, 255);"
|
||||
>
|
||||
<div>
|
||||
Fixed Top Block
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/targetOffset.tsx extend context correctly 2`] = `[]`;
|
@ -0,0 +1,752 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders components/anchor/demo/basic.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height:100vh;background:rgba(255,0,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height:100vh;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height:100vh;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/component-token.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height:100vh;background:rgba(255,0,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height:100vh;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height:100vh;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/customizeHighlight.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/horizontal.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
style="padding:20px"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#FFFBE9"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/legacy-anchor.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/onChange.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/onClick.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/replace.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-16"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height:100vh;background:rgba(255,0,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height:100vh;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height:100vh;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-8"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/static.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/anchor/demo/targetOffset.tsx correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-18"
|
||||
>
|
||||
<div
|
||||
id="part-1"
|
||||
style="height:100vh;background:rgba(255,0,0,0.02);margin-top:30vh"
|
||||
>
|
||||
Part 1
|
||||
</div>
|
||||
<div
|
||||
id="part-2"
|
||||
style="height:100vh;background:rgba(0,255,0,0.02)"
|
||||
>
|
||||
Part 2
|
||||
</div>
|
||||
<div
|
||||
id="part-3"
|
||||
style="height:100vh;background:rgba(0,0,255,0.02)"
|
||||
>
|
||||
Part 3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-6"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="height:30vh;background:rgba(0,0,0,0.85);position:fixed;top:0;left:0;width:75%;color:#FFF"
|
||||
>
|
||||
<div>
|
||||
Fixed Top Block
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
41
packages/meta/src/anchor/__tests__/cached-context.test.tsx
Normal file
41
packages/meta/src/anchor/__tests__/cached-context.test.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { memo, useContext } from 'react';
|
||||
|
||||
import { fireEvent, pureRender } from '../../../tests/utils';
|
||||
import Anchor from '../Anchor';
|
||||
import AnchorContext from '../context';
|
||||
|
||||
let innerCount = 0;
|
||||
let outerCount = 0;
|
||||
|
||||
const handleClick = () => {
|
||||
outerCount++;
|
||||
};
|
||||
|
||||
// we use'memo' here in order to only render inner component while context changed.
|
||||
const CacheInner: React.FC = memo(() => {
|
||||
innerCount++;
|
||||
// subscribe locale context
|
||||
useContext(AnchorContext);
|
||||
return null;
|
||||
});
|
||||
|
||||
const CacheOuter: React.FC = memo(() => (
|
||||
<>
|
||||
<button type="button" onClick={handleClick} id="parent_btn">
|
||||
Click
|
||||
</button>
|
||||
<Anchor affix={false}>
|
||||
<CacheInner />
|
||||
</Anchor>
|
||||
</>
|
||||
));
|
||||
|
||||
it("Rendering on Anchor without changed won't trigger rendering on child component.", () => {
|
||||
const { container, unmount } = pureRender(<CacheOuter />);
|
||||
expect(outerCount).toBe(0);
|
||||
expect(innerCount).toBe(2);
|
||||
fireEvent.click(container.querySelector('#parent_btn')!);
|
||||
expect(outerCount).toBe(1);
|
||||
expect(innerCount).toBe(2);
|
||||
unmount();
|
||||
});
|
3
packages/meta/src/anchor/__tests__/demo-extend.test.ts
Normal file
3
packages/meta/src/anchor/__tests__/demo-extend.test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('anchor');
|
26
packages/meta/src/anchor/__tests__/demo.test.tsx
Normal file
26
packages/meta/src/anchor/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('anchor', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest(
|
||||
'anchor',
|
||||
(Anchor, props) => (
|
||||
<Anchor
|
||||
{...props}
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
{
|
||||
findRootElements: () => document.querySelector('.ant-anchor-wrapper')!,
|
||||
},
|
||||
);
|
5
packages/meta/src/anchor/__tests__/image.test.ts
Normal file
5
packages/meta/src/anchor/__tests__/image.test.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { imageDemoTest } from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Anchor image', () => {
|
||||
imageDemoTest('anchor', { onlyViewport: true });
|
||||
});
|
7
packages/meta/src/anchor/context.ts
Normal file
7
packages/meta/src/anchor/context.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { AntAnchor } from './Anchor';
|
||||
|
||||
const AnchorContext = React.createContext<AntAnchor | undefined>(undefined);
|
||||
|
||||
export default AnchorContext;
|
7
packages/meta/src/anchor/demo/basic.md
Normal file
7
packages/meta/src/anchor/demo/basic.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
最简单的用法。
|
||||
|
||||
## en-US
|
||||
|
||||
The simplest usage.
|
35
packages/meta/src/anchor/demo/basic.tsx
Normal file
35
packages/meta/src/anchor/demo/basic.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { Anchor, Col, Row } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<div id="part-1" style={{ height: '100vh', background: 'rgba(255,0,0,0.02)' }} />
|
||||
<div id="part-2" style={{ height: '100vh', background: 'rgba(0,255,0,0.02)' }} />
|
||||
<div id="part-3" style={{ height: '100vh', background: 'rgba(0,0,255,0.02)' }} />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/component-token.md
Normal file
7
packages/meta/src/anchor/demo/component-token.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
Component Token Debug.
|
||||
|
||||
## en-US
|
||||
|
||||
Component Token Debug
|
46
packages/meta/src/anchor/demo/component-token.tsx
Normal file
46
packages/meta/src/anchor/demo/component-token.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import { Anchor, Col, ConfigProvider, Row } from 'antd';
|
||||
|
||||
/** Test usage. Do not use in your production. */
|
||||
|
||||
export default () => (
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
components: {
|
||||
Anchor: {
|
||||
linkPaddingBlock: 100,
|
||||
linkPaddingInlineStart: 50,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<div id="part-1" style={{ height: '100vh', background: 'rgba(255,0,0,0.02)' }} />
|
||||
<div id="part-2" style={{ height: '100vh', background: 'rgba(0,255,0,0.02)' }} />
|
||||
<div id="part-3" style={{ height: '100vh', background: 'rgba(0,0,255,0.02)' }} />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</ConfigProvider>
|
||||
);
|
7
packages/meta/src/anchor/demo/customizeHighlight.md
Normal file
7
packages/meta/src/anchor/demo/customizeHighlight.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
自定义锚点高亮。
|
||||
|
||||
## en-US
|
||||
|
||||
Customize the anchor highlight.
|
42
packages/meta/src/anchor/demo/customizeHighlight.tsx
Normal file
42
packages/meta/src/anchor/demo/customizeHighlight.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const getCurrentAnchor = () => '#components-anchor-demo-static';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor
|
||||
affix={false}
|
||||
getCurrentAnchor={getCurrentAnchor}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Basic demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/horizontal.md
Normal file
7
packages/meta/src/anchor/demo/horizontal.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
横向 Anchor。
|
||||
|
||||
## en-US
|
||||
|
||||
Horizontally aligned anchors
|
55
packages/meta/src/anchor/demo/horizontal.tsx
Normal file
55
packages/meta/src/anchor/demo/horizontal.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,255,0,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,0,255,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#FFFBE9' }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/legacy-anchor.md
Normal file
7
packages/meta/src/anchor/demo/legacy-anchor.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
调试使用
|
||||
|
||||
## en-US
|
||||
|
||||
Debug usage
|
17
packages/meta/src/anchor/demo/legacy-anchor.tsx
Normal file
17
packages/meta/src/anchor/demo/legacy-anchor.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const { Link } = Anchor;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor affix={false}>
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<Link href="#api" title="API">
|
||||
<Link href="#anchor-props" title="Anchor Props" />
|
||||
<Link href="#link-props" title="Link Props" />
|
||||
</Link>
|
||||
</Anchor>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/onChange.md
Normal file
7
packages/meta/src/anchor/demo/onChange.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
监听锚点链接改变
|
||||
|
||||
## en-US
|
||||
|
||||
Listening for anchor link change.
|
44
packages/meta/src/anchor/demo/onChange.tsx
Normal file
44
packages/meta/src/anchor/demo/onChange.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const onChange = (link: string) => {
|
||||
console.log('Anchor:OnChange', link);
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor
|
||||
affix={false}
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Basic demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/onClick.md
Normal file
7
packages/meta/src/anchor/demo/onClick.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
点击锚点不记录历史。
|
||||
|
||||
## en-US
|
||||
|
||||
Clicking on an anchor does not record history.
|
51
packages/meta/src/anchor/demo/onClick.tsx
Normal file
51
packages/meta/src/anchor/demo/onClick.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const handleClick = (
|
||||
e: React.MouseEvent<HTMLElement>,
|
||||
link: {
|
||||
title: React.ReactNode;
|
||||
href: string;
|
||||
},
|
||||
) => {
|
||||
e.preventDefault();
|
||||
console.log(link);
|
||||
};
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor
|
||||
affix={false}
|
||||
onClick={handleClick}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Basic demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/replace.md
Normal file
7
packages/meta/src/anchor/demo/replace.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
替换浏览器历史记录中的路径,后退按钮将返回到上一页而不是上一个锚点。
|
||||
|
||||
## en-US
|
||||
|
||||
Replace path in browser history, so back button returns to previous page instead of previous anchor item.
|
36
packages/meta/src/anchor/demo/replace.tsx
Normal file
36
packages/meta/src/anchor/demo/replace.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { Anchor, Col, Row } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<div id="part-1" style={{ height: '100vh', background: 'rgba(255,0,0,0.02)' }} />
|
||||
<div id="part-2" style={{ height: '100vh', background: 'rgba(0,255,0,0.02)' }} />
|
||||
<div id="part-3" style={{ height: '100vh', background: 'rgba(0,0,255,0.02)' }} />
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Anchor
|
||||
replace
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
export default App;
|
7
packages/meta/src/anchor/demo/static.md
Normal file
7
packages/meta/src/anchor/demo/static.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
不浮动,状态不随页面滚动变化。
|
||||
|
||||
## en-US
|
||||
|
||||
Do not change state when page is scrolling.
|
39
packages/meta/src/anchor/demo/static.tsx
Normal file
39
packages/meta/src/anchor/demo/static.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor
|
||||
affix={false}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Basic demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
export default App;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user