mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-18 03:14:07 +08:00
chore: feature merge master
This commit is contained in:
commit
620e168cfc
@ -25,7 +25,7 @@ import { isTwoCNChar, isUnBorderedButtonType, spaceChildren } from './buttonHelp
|
||||
import IconWrapper from './IconWrapper';
|
||||
import LoadingIcon from './LoadingIcon';
|
||||
import useStyle from './style';
|
||||
import CompactStyle from './style/compactCmp';
|
||||
import CompactCmp from './style/compactCmp';
|
||||
|
||||
export type LegacyButtonType = ButtonType | 'danger';
|
||||
|
||||
@ -289,7 +289,7 @@ const InternalButton: React.ForwardRefRenderFunction<
|
||||
{kids}
|
||||
|
||||
{/* Styles: compact */}
|
||||
{compactItemClassnames && <CompactStyle prefixCls={prefixCls} />}
|
||||
{compactItemClassnames && <CompactCmp key="compact" prefixCls={prefixCls} />}
|
||||
</button>
|
||||
);
|
||||
|
||||
|
@ -2434,6 +2434,142 @@ exports[`renders components/form/demo/customized-form-controls.tsx extend contex
|
||||
|
||||
exports[`renders components/form/demo/customized-form-controls.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/form/demo/dependencies.tsx extend context correctly 1`] = `
|
||||
<form
|
||||
autocomplete="off"
|
||||
class="ant-form ant-form-vertical"
|
||||
id="dependencies"
|
||||
style="max-width: 600px;"
|
||||
>
|
||||
<div
|
||||
class="ant-alert ant-alert-info"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
aria-label="info-circle"
|
||||
class="anticon anticon-info-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="info-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Try modify \`Password2\` and then modify \`Password\`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dependencies_password"
|
||||
title="Password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dependencies_password"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dependencies_password2"
|
||||
title="Confirm Password"
|
||||
>
|
||||
Confirm Password
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dependencies_password2"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<article
|
||||
class="ant-typography"
|
||||
>
|
||||
<p>
|
||||
Only Update when
|
||||
<code>
|
||||
password2
|
||||
</code>
|
||||
updated:
|
||||
</p>
|
||||
<pre>
|
||||
{}
|
||||
</pre>
|
||||
</article>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders components/form/demo/dependencies.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/form/demo/disabled.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<label
|
||||
@ -6236,258 +6372,153 @@ exports[`renders components/form/demo/dynamic-form-items-complex.tsx extend cont
|
||||
style="max-width: 600px;"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
style="display: flex; row-gap: 16px; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
class="ant-card ant-card-bordered ant-card-small"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dynamic_form_complex_area"
|
||||
title="Area"
|
||||
>
|
||||
Area
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
class="ant-card-head"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
class="ant-card-head-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
class="ant-card-head-title"
|
||||
>
|
||||
Item 1
|
||||
</div>
|
||||
<div
|
||||
class="ant-card-extra"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-card-body"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
class="ant-col ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="dynamic_form_complex_items_0_name"
|
||||
title="Name"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-18 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="dynamic_form_complex_area_list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="Search"
|
||||
aria-owns="dynamic_form_complex_area_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="dynamic_form_complex_area"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity: 0;"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
class="ant-input"
|
||||
id="dynamic_form_complex_items_0_name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up ant-select-dropdown-placement-bottomLeft"
|
||||
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
title="List"
|
||||
>
|
||||
<div>
|
||||
List
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-18 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
id="dynamic_form_complex_area_list"
|
||||
role="listbox"
|
||||
style="height: 0px; width: 0px; overflow: hidden;"
|
||||
style="display: flex; flex-direction: column; row-gap: 16px;"
|
||||
>
|
||||
<div
|
||||
aria-label="Beijing"
|
||||
aria-selected="false"
|
||||
id="dynamic_form_complex_area_list_0"
|
||||
role="option"
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
Beijing
|
||||
</div>
|
||||
<div
|
||||
aria-label="Shanghai"
|
||||
aria-selected="false"
|
||||
id="dynamic_form_complex_area_list_1"
|
||||
role="option"
|
||||
>
|
||||
Shanghai
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="rc-virtual-list"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
class="rc-virtual-list-holder"
|
||||
style="max-height: 256px; overflow-y: auto;"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="rc-virtual-list-holder-inner"
|
||||
style="display: flex; flex-direction: column;"
|
||||
>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-select-item ant-select-item-option ant-select-item-option-active"
|
||||
title="Beijing"
|
||||
>
|
||||
<div
|
||||
class="ant-select-item-option-content"
|
||||
>
|
||||
Beijing
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-item-option-state"
|
||||
style="user-select: none;"
|
||||
unselectable="on"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-selected="false"
|
||||
class="ant-select-item ant-select-item-option"
|
||||
title="Shanghai"
|
||||
>
|
||||
<div
|
||||
class="ant-select-item-option-content"
|
||||
>
|
||||
Shanghai
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-item-option-state"
|
||||
style="user-select: none;"
|
||||
unselectable="on"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
+ Add Sub Item
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select: none;"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="plus"
|
||||
class="anticon anticon-plus"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="plus"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
|
||||
/>
|
||||
<path
|
||||
d="M192 474h672q8 0 8 8v60q0 8-8 8H160q-8 0-8-8v-60q0-8 8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
Add sights
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
+ Add Item
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
<article
|
||||
class="ant-typography"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre>
|
||||
{
|
||||
"items": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</article>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
@ -1777,6 +1777,140 @@ exports[`renders components/form/demo/customized-form-controls.tsx correctly 1`]
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders components/form/demo/dependencies.tsx correctly 1`] = `
|
||||
<form
|
||||
autocomplete="off"
|
||||
class="ant-form ant-form-vertical"
|
||||
id="dependencies"
|
||||
style="max-width:600px"
|
||||
>
|
||||
<div
|
||||
class="ant-alert ant-alert-info"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
aria-label="info-circle"
|
||||
class="anticon anticon-info-circle ant-alert-icon"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="info-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm32 664c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V456c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272zm-32-344a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Try modify \`Password2\` and then modify \`Password\`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dependencies_password"
|
||||
title="Password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dependencies_password"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dependencies_password2"
|
||||
title="Confirm Password"
|
||||
>
|
||||
Confirm Password
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-required="true"
|
||||
class="ant-input"
|
||||
id="dependencies_password2"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<article
|
||||
class="ant-typography"
|
||||
>
|
||||
<p>
|
||||
Only Update when
|
||||
<code>
|
||||
password2
|
||||
</code>
|
||||
updated:
|
||||
</p>
|
||||
<pre>
|
||||
{}
|
||||
</pre>
|
||||
</article>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders components/form/demo/disabled.tsx correctly 1`] = `
|
||||
Array [
|
||||
<label
|
||||
@ -3514,178 +3648,149 @@ exports[`renders components/form/demo/dynamic-form-items-complex.tsx correctly 1
|
||||
style="max-width:600px"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
style="display:flex;row-gap:16px;flex-direction:column"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
class="ant-card ant-card-bordered ant-card-small"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class="ant-form-item-required"
|
||||
for="dynamic_form_complex_area"
|
||||
title="Area"
|
||||
>
|
||||
Area
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
class="ant-card-head"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
class="ant-card-head-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
class="ant-card-head-title"
|
||||
>
|
||||
Item 1
|
||||
</div>
|
||||
<div
|
||||
class="ant-card-extra"
|
||||
>
|
||||
<span
|
||||
aria-label="close"
|
||||
class="anticon anticon-close"
|
||||
role="img"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M799.86 166.31c.02 0 .04.02.08.06l57.69 57.7c.04.03.05.05.06.08a.12.12 0 010 .06c0 .03-.02.05-.06.09L569.93 512l287.7 287.7c.04.04.05.06.06.09a.12.12 0 010 .07c0 .02-.02.04-.06.08l-57.7 57.69c-.03.04-.05.05-.07.06a.12.12 0 01-.07 0c-.03 0-.05-.02-.09-.06L512 569.93l-287.7 287.7c-.04.04-.06.05-.09.06a.12.12 0 01-.07 0c-.02 0-.04-.02-.08-.06l-57.69-57.7c-.04-.03-.05-.05-.06-.07a.12.12 0 010-.07c0-.03.02-.05.06-.09L454.07 512l-287.7-287.7c-.04-.04-.05-.06-.06-.09a.12.12 0 010-.07c0-.02.02-.04.06-.08l57.7-57.69c.03-.04.05-.05.07-.06a.12.12 0 01.07 0c.03 0 .05.02.09.06L512 454.07l287.7-287.7c.04-.04.06-.05.09-.06a.12.12 0 01.07 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-card-body"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
aria-required="true"
|
||||
class="ant-select ant-select-in-form-item ant-select-single ant-select-show-arrow"
|
||||
class="ant-col ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="dynamic_form_complex_items_0_name"
|
||||
title="Name"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-18 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-select-selector"
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<span
|
||||
class="ant-select-selection-search"
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<input
|
||||
aria-autocomplete="list"
|
||||
aria-controls="dynamic_form_complex_area_list"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="Search"
|
||||
aria-owns="dynamic_form_complex_area_list"
|
||||
aria-required="true"
|
||||
autocomplete="off"
|
||||
class="ant-select-selection-search-input"
|
||||
id="dynamic_form_complex_area"
|
||||
readonly=""
|
||||
role="combobox"
|
||||
style="opacity:0"
|
||||
type="search"
|
||||
unselectable="on"
|
||||
class="ant-input"
|
||||
id="dynamic_form_complex_items_0_name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="ant-select-selection-placeholder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="ant-select-arrow"
|
||||
style="user-select:none;-webkit-user-select:none"
|
||||
unselectable="on"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
title="List"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-select-suffix"
|
||||
role="img"
|
||||
List
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-col-18 ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
<div
|
||||
style="display:flex;flex-direction:column;row-gap:16px"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
+ Add Sub Item
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="ant-btn-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="plus"
|
||||
class="anticon anticon-plus"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="plus"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
|
||||
/>
|
||||
<path
|
||||
d="M192 474h672q8 0 8 8v60q0 8-8 8H160q-8 0-8-8v-60q0-8 8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
Add sights
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
+ Add Item
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="ant-form-item"
|
||||
<article
|
||||
class="ant-typography"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre>
|
||||
{}
|
||||
</pre>
|
||||
</article>
|
||||
</form>
|
||||
`;
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
## zh-CN
|
||||
|
||||
Buggy!
|
||||
|
||||
## en-US
|
||||
|
||||
Buggy!
|
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Form, Input } from 'antd';
|
||||
|
||||
let acc = 0;
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
name="debug"
|
||||
initialValues={{ debug1: 'debug1', debug2: 'debug2' }}
|
||||
style={{ maxWidth: 600 }}
|
||||
>
|
||||
<Form.Item noStyle dependencies={['debug1']}>
|
||||
{
|
||||
() => acc++
|
||||
// return <pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>;
|
||||
}
|
||||
</Form.Item>
|
||||
<Form.Item label="debug1" name="debug1">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label="debug2" name="debug2">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
7
components/form/demo/dependencies.md
Normal file
7
components/form/demo/dependencies.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
Form.Item 可以通过 `dependencies` 属性,设置关联字段。当关联字段的值发生变化时,会触发校验与更新。
|
||||
|
||||
## en-US
|
||||
|
||||
Form.Item can set the associated field through the `dependencies` property. When the value of the associated field changes, the validation and update will be triggered.
|
57
components/form/demo/dependencies.tsx
Normal file
57
components/form/demo/dependencies.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import { Alert, Form, Input, Typography } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
name="dependencies"
|
||||
autoComplete="off"
|
||||
style={{ maxWidth: 600 }}
|
||||
layout="vertical"
|
||||
>
|
||||
<Alert message=" Try modify `Password2` and then modify `Password`" type="info" showIcon />
|
||||
|
||||
<Form.Item label="Password" name="password" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
{/* Field */}
|
||||
<Form.Item
|
||||
label="Confirm Password"
|
||||
name="password2"
|
||||
dependencies={['password']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('password') === value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(new Error('The new password that you entered do not match!'));
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
{/* Render Props */}
|
||||
<Form.Item noStyle dependencies={['password2']}>
|
||||
{() => (
|
||||
<Typography>
|
||||
<p>
|
||||
Only Update when <code>password2</code> updated:
|
||||
</p>
|
||||
<pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
|
||||
</Typography>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -1,7 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
这个例子演示了一个表单中包含多个表单控件的情况。
|
||||
多个 Form.List 嵌套的使用场景。
|
||||
|
||||
## en-US
|
||||
|
||||
This example demonstrates the case that a form contains multiple form controls.
|
||||
Multiple Form.List nested usage scenarios.
|
||||
|
@ -1,96 +1,83 @@
|
||||
import React from 'react';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Form, Input, Select, Space } from 'antd';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const areas = [
|
||||
{ label: 'Beijing', value: 'Beijing' },
|
||||
{ label: 'Shanghai', value: 'Shanghai' },
|
||||
];
|
||||
|
||||
const sights = {
|
||||
Beijing: ['Tiananmen', 'Great Wall'],
|
||||
Shanghai: ['Oriental Pearl', 'The Bund'],
|
||||
};
|
||||
|
||||
type SightsKeys = keyof typeof sights;
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Form, Input, Space, Typography } from 'antd';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
console.log('Received values of form:', values);
|
||||
};
|
||||
|
||||
const handleChange = () => {
|
||||
form.setFieldsValue({ sights: [] });
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 18 }}
|
||||
form={form}
|
||||
name="dynamic_form_complex"
|
||||
onFinish={onFinish}
|
||||
style={{ maxWidth: 600 }}
|
||||
autoComplete="off"
|
||||
initialValues={{ items: [{}] }}
|
||||
>
|
||||
<Form.Item name="area" label="Area" rules={[{ required: true, message: 'Missing area' }]}>
|
||||
<Select options={areas} onChange={handleChange} />
|
||||
</Form.Item>
|
||||
<Form.List name="sights">
|
||||
<Form.List name="items">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
|
||||
{fields.map((field) => (
|
||||
<Space key={field.key} align="baseline">
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.area !== curValues.area || prevValues.sights !== curValues.sights
|
||||
}
|
||||
>
|
||||
{() => (
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="Sight"
|
||||
name={[field.name, 'sight']}
|
||||
rules={[{ required: true, message: 'Missing sight' }]}
|
||||
>
|
||||
<Select disabled={!form.getFieldValue('area')} style={{ width: 130 }}>
|
||||
{(sights[form.getFieldValue('area') as SightsKeys] || []).map((item) => (
|
||||
<Option key={item} value={item}>
|
||||
{item}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...field}
|
||||
label="Price"
|
||||
name={[field.name, 'price']}
|
||||
rules={[{ required: true, message: 'Missing price' }]}
|
||||
>
|
||||
<Card
|
||||
size="small"
|
||||
title={`Item ${field.name + 1}`}
|
||||
key={field.key}
|
||||
extra={
|
||||
<CloseOutlined
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Form.Item label="Name" name={[field.name, 'name']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<MinusCircleOutlined onClick={() => remove(field.name)} />
|
||||
</Space>
|
||||
{/* Nest Form.List */}
|
||||
<Form.Item label="List">
|
||||
<Form.List name={[field.name, 'list']}>
|
||||
{(subFields, subOpt) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}>
|
||||
{subFields.map((subField) => (
|
||||
<Space key={subField.key}>
|
||||
<Form.Item noStyle name={[subField.name, 'first']}>
|
||||
<Input placeholder="first" />
|
||||
</Form.Item>
|
||||
<Form.Item noStyle name={[subField.name, 'second']}>
|
||||
<Input placeholder="second" />
|
||||
</Form.Item>
|
||||
<CloseOutlined
|
||||
onClick={() => {
|
||||
subOpt.remove(subField.name);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
<Button type="dashed" onClick={() => subOpt.add()} block>
|
||||
+ Add Sub Item
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
<Form.Item>
|
||||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||||
Add sights
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
<Button type="dashed" onClick={() => add()} block>
|
||||
+ Add Item
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{() => (
|
||||
<Typography>
|
||||
<pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
|
||||
</Typography>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
|
@ -46,9 +46,9 @@ High performance Form component with data scope management. Including data colle
|
||||
<code src="./demo/without-form-create.tsx">Handle Form Data Manually</code>
|
||||
<code src="./demo/validate-static.tsx">Customized Validation</code>
|
||||
<code src="./demo/dynamic-rule.tsx">Dynamic Rules</code>
|
||||
<code src="./demo/dependencies.tsx">Dependencies</code>
|
||||
<code src="./demo/validate-other.tsx">Other Form Controls</code>
|
||||
<code src="./demo/disabled-input-debug.tsx" debug>Disabled Input Debug</code>
|
||||
<code src="./demo/dep-debug.tsx" debug>Dep Debug</code>
|
||||
<code src="./demo/label-debug.tsx" debug>label ellipsis</code>
|
||||
<code src="./demo/col-24-debug.tsx" debug>Test col 24 usage</code>
|
||||
<code src="./demo/ref-item.tsx" debug>Ref item</code>
|
||||
@ -154,12 +154,10 @@ After wrapped by `Form.Item` with `name` property, `value`(or other property def
|
||||
|
||||
### dependencies
|
||||
|
||||
Used when there are dependencies between fields. If a field has the `dependencies` prop, this field will automatically trigger updates and validations when upstream is updated. A common scenario is a user registration form with "password" and "confirm password" fields. The "Confirm Password" validation depends on the "Password" field. After setting `dependencies`, the "Password" field update will re-trigger the validation of "Check Password". You can refer [examples](#components-form-demo-register).
|
||||
Used when there are dependencies between fields. If a field has the `dependencies` prop, this field will automatically trigger updates and validations when upstream is updated. A common scenario is a user registration form with "password" and "confirm password" fields. The "Confirm Password" validation depends on the "Password" field. After setting `dependencies`, the "Password" field update will re-trigger the validation of "Check Password". You can refer [examples](#components-form-demo-dependencies).
|
||||
|
||||
`dependencies` shouldn't be used together with `shouldUpdate`, since it may result in conflicting update logic.
|
||||
|
||||
`dependencies` supports `Form.Item` with render props children since `4.5.0`.
|
||||
|
||||
### shouldUpdate
|
||||
|
||||
Form updates only the modified field-related components for performance optimization purposes by incremental update. In most cases, you only need to write code or do validation with the [`dependencies`](#dependencies) property. In some specific cases, such as when a new field option appears with a field value changed, or you just want to keep some area updating by form update, you can modify the update logic of Form.Item via the `shouldUpdate`.
|
||||
|
@ -47,9 +47,9 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*ylFATY6w-ygAAA
|
||||
<code src="./demo/without-form-create.tsx">自行处理表单数据</code>
|
||||
<code src="./demo/validate-static.tsx">自定义校验</code>
|
||||
<code src="./demo/dynamic-rule.tsx">动态校验规则</code>
|
||||
<code src="./demo/dependencies.tsx">校验与更新依赖</code>
|
||||
<code src="./demo/validate-other.tsx">校验其他组件</code>
|
||||
<code src="./demo/disabled-input-debug.tsx" debug>Disabled Input Debug</code>
|
||||
<code src="./demo/dep-debug.tsx" debug>Dep Debug</code>
|
||||
<code src="./demo/label-debug.tsx" debug>测试 label 省略</code>
|
||||
<code src="./demo/col-24-debug.tsx" debug>测试特殊 col 24 用法</code>
|
||||
<code src="./demo/ref-item.tsx" debug>引用字段</code>
|
||||
@ -155,12 +155,10 @@ const validateMessages = {
|
||||
|
||||
### dependencies
|
||||
|
||||
当字段间存在依赖关系时使用。如果一个字段设置了 `dependencies` 属性。那么它所依赖的字段更新时,该字段将自动触发更新与校验。一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段,设置 `dependencies` 后,“密码”字段更新会重新触发“校验密码”的校验逻辑。你可以参考[具体例子](#components-form-demo-register)。
|
||||
当字段间存在依赖关系时使用。如果一个字段设置了 `dependencies` 属性。那么它所依赖的字段更新时,该字段将自动触发更新与校验。一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段,设置 `dependencies` 后,“密码”字段更新会重新触发“校验密码”的校验逻辑。你可以参考[具体例子](#components-form-demo-dependencies)。
|
||||
|
||||
`dependencies` 不应和 `shouldUpdate` 一起使用,因为这可能带来更新逻辑的混乱。
|
||||
|
||||
从 `4.5.0` 版本开始,`dependencies` 支持使用 render props 类型 children 的 `Form.Item`。
|
||||
|
||||
### shouldUpdate
|
||||
|
||||
Form 通过增量更新方式,只更新被修改的字段相关组件以达到性能优化目的。大部分场景下,你只需要编写代码或者与 [`dependencies`](#dependencies) 属性配合校验即可。而在某些特定场景,例如修改某个字段值后出现新的字段选项、或者纯粹希望表单任意变化都对某一个区域进行渲染。你可以通过 `shouldUpdate` 修改 Form.Item 的更新逻辑。
|
||||
|
@ -201,7 +201,4 @@ export default genComponentStyleHook(
|
||||
token.paddingSM
|
||||
}px`,
|
||||
}),
|
||||
{
|
||||
clientOnly: true,
|
||||
},
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import { useNotification as useRcNotification } from 'rc-notification';
|
||||
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
|
||||
import type { NotificationAPI } from 'rc-notification/lib';
|
||||
import * as React from 'react';
|
||||
import warning from '../_util/warning';
|
||||
@ -17,6 +17,8 @@ import type {
|
||||
} from './interface';
|
||||
import useStyle from './style';
|
||||
import { getMotion, wrapPromiseFn } from './util';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import type { NotificationConfig as RcNotificationConfig } from 'rc-notification/lib/useNotification';
|
||||
|
||||
const DEFAULT_OFFSET = 8;
|
||||
const DEFAULT_DURATION = 3;
|
||||
@ -30,10 +32,27 @@ type HolderProps = ConfigOptions & {
|
||||
|
||||
interface HolderRef extends NotificationAPI {
|
||||
prefixCls: string;
|
||||
hashId: string;
|
||||
message?: ComponentStyleConfig;
|
||||
}
|
||||
|
||||
const Wrapper: FC<PropsWithChildren<{ prefixCls: string }>> = ({ children, prefixCls }) => {
|
||||
const [, hashId] = useStyle(prefixCls);
|
||||
return (
|
||||
<NotificationProvider classNames={{ list: hashId, notice: hashId }}>
|
||||
{children}
|
||||
</NotificationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const renderNotifications: RcNotificationConfig['renderNotifications'] = (
|
||||
node,
|
||||
{ prefixCls, key },
|
||||
) => (
|
||||
<Wrapper prefixCls={prefixCls} key={key}>
|
||||
{node}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
const {
|
||||
top,
|
||||
@ -49,8 +68,6 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
|
||||
const prefixCls = staticPrefixCls || getPrefixCls('message');
|
||||
|
||||
const [, hashId] = useStyle(prefixCls);
|
||||
|
||||
// =============================== Style ===============================
|
||||
const getStyle = (): React.CSSProperties => ({
|
||||
left: '50%',
|
||||
@ -58,7 +75,7 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
top: top ?? DEFAULT_OFFSET,
|
||||
});
|
||||
|
||||
const getClassName = () => classNames(hashId, { [`${prefixCls}-rtl`]: rtl });
|
||||
const getClassName = () => classNames({ [`${prefixCls}-rtl`]: rtl });
|
||||
|
||||
// ============================== Motion ===============================
|
||||
const getNotificationMotion = () => getMotion(prefixCls, transitionName);
|
||||
@ -82,13 +99,13 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
getContainer: () => staticGetContainer?.() || getPopupContainer?.() || document.body,
|
||||
maxCount,
|
||||
onAllRemoved,
|
||||
renderNotifications,
|
||||
});
|
||||
|
||||
// ================================ Ref ================================
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...api,
|
||||
prefixCls,
|
||||
hashId,
|
||||
message,
|
||||
}));
|
||||
|
||||
@ -128,7 +145,7 @@ export function useInternalMessage(
|
||||
return fakeResult;
|
||||
}
|
||||
|
||||
const { open: originOpen, prefixCls, hashId, message } = holderRef.current;
|
||||
const { open: originOpen, prefixCls, message } = holderRef.current;
|
||||
const noticePrefixCls = `${prefixCls}-notice`;
|
||||
|
||||
const { content, icon, type, key, className, style, onClose, ...restConfig } = config;
|
||||
@ -151,7 +168,6 @@ export function useInternalMessage(
|
||||
placement: 'top',
|
||||
className: classNames(
|
||||
type && `${noticePrefixCls}-${type}`,
|
||||
hashId,
|
||||
className,
|
||||
message?.className,
|
||||
),
|
||||
|
@ -300,7 +300,4 @@ export default genComponentStyleHook(
|
||||
zIndexPopup: token.zIndexPopupBase + 50,
|
||||
width: 384,
|
||||
}),
|
||||
{
|
||||
clientOnly: true,
|
||||
},
|
||||
);
|
||||
|
@ -1,7 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useNotification as useRcNotification } from 'rc-notification';
|
||||
import type { NotificationAPI } from 'rc-notification/lib';
|
||||
import { NotificationProvider, useNotification as useRcNotification } from 'rc-notification';
|
||||
import type {
|
||||
NotificationAPI,
|
||||
NotificationConfig as RcNotificationConfig,
|
||||
} from 'rc-notification/lib';
|
||||
import warning from '../_util/warning';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import type { ComponentStyleConfig } from '../config-provider/context';
|
||||
@ -14,6 +17,7 @@ import type {
|
||||
import { getCloseIcon, PureContent } from './PurePanel';
|
||||
import useStyle from './style';
|
||||
import { getMotion, getPlacementStyle } from './util';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
|
||||
const DEFAULT_OFFSET = 24;
|
||||
const DEFAULT_DURATION = 4.5;
|
||||
@ -28,10 +32,27 @@ type HolderProps = NotificationConfig & {
|
||||
|
||||
interface HolderRef extends NotificationAPI {
|
||||
prefixCls: string;
|
||||
hashId: string;
|
||||
notification?: ComponentStyleConfig;
|
||||
}
|
||||
|
||||
const Wrapper: FC<PropsWithChildren<{ prefixCls: string }>> = ({ children, prefixCls }) => {
|
||||
const [, hashId] = useStyle(prefixCls);
|
||||
return (
|
||||
<NotificationProvider classNames={{ list: hashId, notice: hashId }}>
|
||||
{children}
|
||||
</NotificationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const renderNotifications: RcNotificationConfig['renderNotifications'] = (
|
||||
node,
|
||||
{ prefixCls, key },
|
||||
) => (
|
||||
<Wrapper prefixCls={prefixCls} key={key}>
|
||||
{node}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
const {
|
||||
top,
|
||||
@ -50,10 +71,7 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
const getStyle = (placement: NotificationPlacement): React.CSSProperties =>
|
||||
getPlacementStyle(placement, top ?? DEFAULT_OFFSET, bottom ?? DEFAULT_OFFSET);
|
||||
|
||||
// Style
|
||||
const [, hashId] = useStyle(prefixCls);
|
||||
|
||||
const getClassName = () => classNames(hashId, { [`${prefixCls}-rtl`]: rtl });
|
||||
const getClassName = () => classNames({ [`${prefixCls}-rtl`]: rtl });
|
||||
|
||||
// ============================== Motion ===============================
|
||||
const getNotificationMotion = () => getMotion(prefixCls);
|
||||
@ -70,13 +88,13 @@ const Holder = React.forwardRef<HolderRef, HolderProps>((props, ref) => {
|
||||
getContainer: () => staticGetContainer?.() || getPopupContainer?.() || document.body,
|
||||
maxCount,
|
||||
onAllRemoved,
|
||||
renderNotifications,
|
||||
});
|
||||
|
||||
// ================================ Ref ================================
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
...api,
|
||||
prefixCls,
|
||||
hashId,
|
||||
notification,
|
||||
}));
|
||||
|
||||
@ -106,7 +124,7 @@ export function useInternalNotification(
|
||||
return;
|
||||
}
|
||||
|
||||
const { open: originOpen, prefixCls, hashId, notification } = holderRef.current;
|
||||
const { open: originOpen, prefixCls, notification } = holderRef.current;
|
||||
|
||||
const noticePrefixCls = `${prefixCls}-notice`;
|
||||
|
||||
@ -142,7 +160,6 @@ export function useInternalNotification(
|
||||
),
|
||||
className: classNames(
|
||||
type && `${noticePrefixCls}-${type}`,
|
||||
hashId,
|
||||
className,
|
||||
notification?.className,
|
||||
),
|
||||
|
@ -9,6 +9,10 @@ const onSearch = (value: string) => {
|
||||
console.log('search:', value);
|
||||
};
|
||||
|
||||
// Filter `option.label` match the user type `input`
|
||||
const filterOption = (input: string, option: { label: string; value: string }) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase());
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Select
|
||||
showSearch
|
||||
@ -16,9 +20,7 @@ const App: React.FC = () => (
|
||||
optionFilterProp="children"
|
||||
onChange={onChange}
|
||||
onSearch={onSearch}
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
filterOption={filterOption}
|
||||
options={[
|
||||
{
|
||||
value: 'jack',
|
||||
|
@ -70,7 +70,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| dropdownRender | Customize dropdown content | (originNode: ReactNode) => ReactNode | - | |
|
||||
| dropdownStyle | The style of dropdown menu | CSSProperties | - | |
|
||||
| fieldNames | Customize node label, value, options,groupLabel field name | object | { label: `label`, value: `value`, options: `options`, groupLabel: `label` } | 4.17.0 (`groupLabel` added in 5.6.0) |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. [Example](#select-demo-search) | boolean \| function(inputValue, option) | true | |
|
||||
| filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | Whether to embed label in value, turn the format of value from `string` to { value: string, label: ReactNode } | boolean | false | |
|
||||
|
@ -71,7 +71,7 @@ demo:
|
||||
| dropdownRender | 自定义下拉框内容 | (originNode: ReactNode) => ReactNode | - | |
|
||||
| dropdownStyle | 下拉菜单的 style 属性 | CSSProperties | - | |
|
||||
| fieldNames | 自定义节点 label、value、options、groupLabel 的字段 | object | { label: `label`, value: `value`, options: `options`, groupLabel: `label` } | 4.17.0(`groupLabel` 在 5.6.0 新增) |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | boolean \| function(inputValue, option) | true | |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false。[示例](#select-demo-search) | boolean \| function(inputValue, option) | true | |
|
||||
| filterSort | 搜索时对筛选结果项的排序函数, 类似[Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)里的 compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 `string` 变为 { value: string, label: ReactNode } 的格式 | boolean | false | |
|
||||
|
@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { isPresetColor, isPresetStatusColor } from '../_util/colors';
|
||||
import useClosable from '../_util/hooks/useClosable';
|
||||
@ -12,6 +13,8 @@ import Wave from '../_util/wave';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import CheckableTag from './CheckableTag';
|
||||
import useStyle from './style';
|
||||
import PresetCmp from './style/presetCmp';
|
||||
import StatusCmp from './style/statusCmp';
|
||||
|
||||
export type { CheckableTagProps } from './CheckableTag';
|
||||
|
||||
@ -69,7 +72,9 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
const isInternalColor = isPresetColor(color) || isPresetStatusColor(color);
|
||||
const isPreset = isPresetColor(color);
|
||||
const isStatus = isPresetStatusColor(color);
|
||||
const isInternalColor = isPreset || isStatus;
|
||||
|
||||
const tagStyle: React.CSSProperties = {
|
||||
backgroundColor: color && !isInternalColor ? color : undefined,
|
||||
@ -140,6 +145,8 @@ const InternalTag: React.ForwardRefRenderFunction<HTMLSpanElement, TagProps> = (
|
||||
<span {...props} ref={ref} className={tagClassName} style={tagStyle}>
|
||||
{kids}
|
||||
{mergedCloseIcon}
|
||||
{isPreset && <PresetCmp key="preset" prefixCls={prefixCls} />}
|
||||
{isStatus && <StatusCmp key="status" prefixCls={prefixCls} />}
|
||||
</span>
|
||||
);
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type { CSSInterpolation } from '@ant-design/cssinjs';
|
||||
import type React from 'react';
|
||||
import capitalize from '../../_util/capitalize';
|
||||
import type { CSSInterpolation } from '@ant-design/cssinjs';
|
||||
|
||||
import { resetComponent } from '../../style';
|
||||
import type { GlobalToken } from '../../theme';
|
||||
import type { FullToken } from '../../theme/internal';
|
||||
import { genComponentStyleHook, genPresetColor, mergeToken } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
import type { GenStyleFn } from '../../theme/util/genComponentStyleHook';
|
||||
|
||||
export interface ComponentToken {
|
||||
/**
|
||||
@ -18,7 +20,7 @@ export interface ComponentToken {
|
||||
defaultColor: string;
|
||||
}
|
||||
|
||||
interface TagToken extends FullToken<'Tag'> {
|
||||
export interface TagToken extends FullToken<'Tag'> {
|
||||
tagFontSize: number;
|
||||
tagLineHeight: React.CSSProperties['lineHeight'];
|
||||
tagIconSize: number;
|
||||
@ -28,44 +30,6 @@ interface TagToken extends FullToken<'Tag'> {
|
||||
|
||||
// ============================== Styles ==============================
|
||||
|
||||
type CssVariableType = 'Success' | 'Info' | 'Error' | 'Warning';
|
||||
|
||||
const genTagStatusStyle = (
|
||||
token: TagToken,
|
||||
status: 'success' | 'processing' | 'error' | 'warning',
|
||||
cssVariableType: CssVariableType,
|
||||
): CSSInterpolation => {
|
||||
const capitalizedCssVariableType = capitalize<CssVariableType>(cssVariableType);
|
||||
return {
|
||||
[`${token.componentCls}-${status}`]: {
|
||||
color: token[`color${cssVariableType}`],
|
||||
background: token[`color${capitalizedCssVariableType}Bg`],
|
||||
borderColor: token[`color${capitalizedCssVariableType}Border`],
|
||||
[`&${token.componentCls}-borderless`]: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genPresetStyle = (token: TagToken) =>
|
||||
genPresetColor(token, (colorKey, { textColor, lightBorderColor, lightColor, darkColor }) => ({
|
||||
[`${token.componentCls}-${colorKey}`]: {
|
||||
color: textColor,
|
||||
background: lightColor,
|
||||
borderColor: lightBorderColor,
|
||||
// Inverse color
|
||||
'&-inverse': {
|
||||
color: token.colorTextLightSolid,
|
||||
background: darkColor,
|
||||
borderColor: darkColor,
|
||||
},
|
||||
[`&${token.componentCls}-borderless`]: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const genBaseStyle = (token: TagToken): CSSInterpolation => {
|
||||
const { paddingXXS, lineWidth, tagPaddingHorizontal, componentCls } = token;
|
||||
const paddingInline = tagPaddingHorizontal - lineWidth;
|
||||
@ -162,33 +126,33 @@ const genBaseStyle = (token: TagToken): CSSInterpolation => {
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export const prepareToken: (token: Parameters<GenStyleFn<'Tag'>>[0]) => TagToken = (token) => {
|
||||
const { lineWidth, fontSizeIcon } = token;
|
||||
|
||||
const tagFontSize = token.fontSizeSM;
|
||||
const tagLineHeight = `${token.lineHeightSM * tagFontSize}px`;
|
||||
|
||||
const tagToken = mergeToken<TagToken>(token, {
|
||||
tagFontSize,
|
||||
tagLineHeight,
|
||||
tagIconSize: fontSizeIcon - 2 * lineWidth, // Tag icon is much smaller
|
||||
tagPaddingHorizontal: 8, // Fixed padding.
|
||||
tagBorderlessBg: token.colorFillTertiary,
|
||||
});
|
||||
return tagToken;
|
||||
};
|
||||
|
||||
export const prepareCommonToken: (token: GlobalToken) => ComponentToken = (token) => ({
|
||||
defaultBg: token.colorFillQuaternary,
|
||||
defaultColor: token.colorText,
|
||||
});
|
||||
|
||||
export default genComponentStyleHook(
|
||||
'Tag',
|
||||
(token) => {
|
||||
const { lineWidth, fontSizeIcon } = token;
|
||||
const tagToken = prepareToken(token);
|
||||
|
||||
const tagFontSize = token.fontSizeSM;
|
||||
const tagLineHeight = `${token.lineHeightSM * tagFontSize}px`;
|
||||
|
||||
const tagToken = mergeToken<TagToken>(token, {
|
||||
tagFontSize,
|
||||
tagLineHeight,
|
||||
tagIconSize: fontSizeIcon - 2 * lineWidth, // Tag icon is much smaller
|
||||
tagPaddingHorizontal: 8, // Fixed padding.
|
||||
tagBorderlessBg: token.colorFillTertiary,
|
||||
});
|
||||
|
||||
return [
|
||||
genBaseStyle(tagToken),
|
||||
genPresetStyle(tagToken),
|
||||
genTagStatusStyle(tagToken, 'success', 'Success'),
|
||||
genTagStatusStyle(tagToken, 'processing', 'Info'),
|
||||
genTagStatusStyle(tagToken, 'error', 'Error'),
|
||||
genTagStatusStyle(tagToken, 'warning', 'Warning'),
|
||||
];
|
||||
return genBaseStyle(tagToken);
|
||||
},
|
||||
(token) => ({
|
||||
defaultBg: token.colorFillQuaternary,
|
||||
defaultColor: token.colorText,
|
||||
}),
|
||||
prepareCommonToken,
|
||||
);
|
||||
|
33
components/tag/style/presetCmp.ts
Normal file
33
components/tag/style/presetCmp.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// Style as status component
|
||||
import { prepareCommonToken, prepareToken, type TagToken } from '.';
|
||||
import { genPresetColor, genSubStyleComponent } from '../../theme/internal';
|
||||
|
||||
// ============================== Preset ==============================
|
||||
const genPresetStyle = (token: TagToken) =>
|
||||
genPresetColor(token, (colorKey, { textColor, lightBorderColor, lightColor, darkColor }) => ({
|
||||
[`${token.componentCls}-${colorKey}`]: {
|
||||
color: textColor,
|
||||
background: lightColor,
|
||||
borderColor: lightBorderColor,
|
||||
// Inverse color
|
||||
'&-inverse': {
|
||||
color: token.colorTextLightSolid,
|
||||
background: darkColor,
|
||||
borderColor: darkColor,
|
||||
},
|
||||
[`&${token.componentCls}-borderless`]: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genSubStyleComponent(
|
||||
['Tag', 'preset'],
|
||||
(token) => {
|
||||
const tagToken = prepareToken(token);
|
||||
|
||||
return genPresetStyle(tagToken);
|
||||
},
|
||||
prepareCommonToken,
|
||||
);
|
43
components/tag/style/statusCmp.ts
Normal file
43
components/tag/style/statusCmp.ts
Normal file
@ -0,0 +1,43 @@
|
||||
// Style as status component
|
||||
import type { CSSInterpolation } from '@ant-design/cssinjs';
|
||||
|
||||
import { prepareCommonToken, prepareToken, type TagToken } from '.';
|
||||
import capitalize from '../../_util/capitalize';
|
||||
import { genSubStyleComponent } from '../../theme/internal';
|
||||
|
||||
// ============================== Status ==============================
|
||||
type CssVariableType = 'Success' | 'Info' | 'Error' | 'Warning';
|
||||
|
||||
const genTagStatusStyle = (
|
||||
token: TagToken,
|
||||
status: 'success' | 'processing' | 'error' | 'warning',
|
||||
cssVariableType: CssVariableType,
|
||||
): CSSInterpolation => {
|
||||
const capitalizedCssVariableType = capitalize<CssVariableType>(cssVariableType);
|
||||
return {
|
||||
[`${token.componentCls}-${status}`]: {
|
||||
color: token[`color${cssVariableType}`],
|
||||
background: token[`color${capitalizedCssVariableType}Bg`],
|
||||
borderColor: token[`color${capitalizedCssVariableType}Border`],
|
||||
[`&${token.componentCls}-borderless`]: {
|
||||
borderColor: 'transparent',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genSubStyleComponent(
|
||||
['Tag', 'status'],
|
||||
(token) => {
|
||||
const tagToken = prepareToken(token);
|
||||
|
||||
return [
|
||||
genTagStatusStyle(tagToken, 'success', 'Success'),
|
||||
genTagStatusStyle(tagToken, 'processing', 'Info'),
|
||||
genTagStatusStyle(tagToken, 'error', 'Error'),
|
||||
genTagStatusStyle(tagToken, 'warning', 'Warning'),
|
||||
];
|
||||
},
|
||||
prepareCommonToken,
|
||||
);
|
@ -184,13 +184,19 @@ export interface SubStyleComponentProps {
|
||||
prefixCls: string;
|
||||
}
|
||||
|
||||
export function genSubStyleComponent<ComponentName extends OverrideComponent>(
|
||||
export const genSubStyleComponent: <ComponentName extends OverrideComponent>(
|
||||
...args: Parameters<typeof genComponentStyleHook<ComponentName>>
|
||||
): ComponentType<SubStyleComponentProps> {
|
||||
const useStyle = genComponentStyleHook(...args);
|
||||
) => ComponentType<SubStyleComponentProps> = (componentName, styleFn, getDefaultToken, options) => {
|
||||
const useStyle = genComponentStyleHook(componentName, styleFn, getDefaultToken, {
|
||||
resetStyle: false,
|
||||
|
||||
// Sub Style should default after root one
|
||||
order: -998,
|
||||
...options,
|
||||
});
|
||||
|
||||
return ({ prefixCls }: SubStyleComponentProps) => {
|
||||
useStyle(prefixCls);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -305,6 +305,11 @@ const genTransferListStyle: GenerateStyle<TransferToken> = (token: TransferToken
|
||||
'&-footer': {
|
||||
borderTop: `${lineWidth}px ${lineType} ${colorSplit}`,
|
||||
},
|
||||
|
||||
// fix: https://github.com/ant-design/ant-design/issues/44489
|
||||
'&-checkbox': {
|
||||
lineHeight: 1,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -72,7 +72,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| progress | Custom progress bar | [ProgressProps](/components/progress/#api) (support `type="line"` only) | { strokeWidth: 2, showInfo: false } | 4.3.0 |
|
||||
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon`, `showRemoveIcon`, `showDownloadIcon`, `removeIcon` and `downloadIcon` individually | boolean \| { showPreviewIcon?: boolean, showDownloadIcon?: boolean, showRemoveIcon?: boolean, previewIcon?: ReactNode \| (file: UploadFile) => ReactNode, removeIcon?: ReactNode \| (file: UploadFile) => ReactNode, downloadIcon?: ReactNode \| (file: UploadFile) => ReactNode } | true | function: 4.7.0 |
|
||||
| withCredentials | The ajax upload with cookie sent | boolean | false | |
|
||||
| onChange | A callback function, can be executed when uploading state is changing, see [onChange](#onchange) | function | - | |
|
||||
| onChange | A callback function, can be executed when uploading state is changing. It will trigger by every uploading phase. see [onChange](#onchange) | function | - | |
|
||||
| onDrop | A callback function executed when files are dragged and dropped into the upload area | (event: React.DragEvent) => void | - | 4.16.0 |
|
||||
| onDownload | Click the method to download the file, pass the method to perform the method logic, and do not pass the default jump to the new TAB | function(file): void | (Jump to new TAB) | |
|
||||
| onPreview | A callback function, will be executed when the file link or preview icon is clicked | function(file) | - | |
|
||||
@ -94,7 +94,7 @@ Extends File with additional props.
|
||||
|
||||
### onChange
|
||||
|
||||
> The function will be called when uploading is in progress, completed, or failed.
|
||||
> 💡 The function will be called when uploading is in progress, completed, or failed.
|
||||
|
||||
When uploading state change, it returns:
|
||||
|
||||
|
@ -73,7 +73,7 @@ demo:
|
||||
| progress | 自定义进度条样式 | [ProgressProps](/components/progress-cn#api)(仅支持 `type="line"`) | { strokeWidth: 2, showInfo: false } | 4.3.0 |
|
||||
| showUploadList | 是否展示文件列表, 可设为一个对象,用于单独设定 `showPreviewIcon`, `showRemoveIcon`, `showDownloadIcon`, `removeIcon` 和 `downloadIcon` | boolean \| { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean, previewIcon?: ReactNode \| (file: UploadFile) => ReactNode, removeIcon?: ReactNode \| (file: UploadFile) => ReactNode, downloadIcon?: ReactNode \| (file: UploadFile) => ReactNode } | true | function: 4.7.0 |
|
||||
| withCredentials | 上传请求时是否携带 cookie | boolean | false | |
|
||||
| onChange | 上传文件改变时的回调,详见 [onChange](#onchange) | function | - | |
|
||||
| onChange | 上传文件改变时的回调,上传每个阶段都会触发该事件。详见 [onChange](#onchange) | function | - | |
|
||||
| onDrop | 当文件被拖入上传区域时执行的回调功能 | (event: React.DragEvent) => void | - | 4.16.0 |
|
||||
| onDownload | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页 | function(file): void | (跳转新标签页) | |
|
||||
| onPreview | 点击文件链接或预览图标时的回调 | function(file) | - | |
|
||||
@ -95,7 +95,7 @@ demo:
|
||||
|
||||
### onChange
|
||||
|
||||
> 上传中、完成、失败都会调用这个函数。
|
||||
> 💡 上传中、完成、失败都会调用这个函数。
|
||||
|
||||
文件状态改变的回调,返回为:
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import Watermark from '..';
|
||||
import Modal from '../../modal';
|
||||
import Drawer from '../../drawer';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, waitFakeTimer, waitFor } from '../../../tests/utils';
|
||||
import Drawer from '../../drawer';
|
||||
import Modal from '../../modal';
|
||||
|
||||
describe('Watermark', () => {
|
||||
mountTest(Watermark);
|
||||
@ -18,10 +19,18 @@ describe('Watermark', () => {
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockSrcSet.mockRestore();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('The watermark should render successfully', () => {
|
||||
const { container } = render(<Watermark className="watermark" content="Ant Design" />);
|
||||
expect(container.querySelector('.watermark div')).toBeTruthy();
|
||||
@ -126,4 +135,15 @@ describe('Watermark', () => {
|
||||
() => document.body.querySelector('.ant-drawer-content')!.lastChild!,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not crash if content is empty string', async () => {
|
||||
const spy = jest.spyOn(CanvasRenderingContext2D.prototype, 'drawImage');
|
||||
render(<Watermark content="" className="watermark" />);
|
||||
await waitFakeTimer();
|
||||
expect(spy).not.toHaveBeenCalledWith(expect.anything(), 0, 0);
|
||||
expect(spy).not.toHaveBeenCalledWith(expect.anything(), -0, 0);
|
||||
expect(spy).not.toHaveBeenCalledWith(expect.anything(), -0, -0);
|
||||
expect(spy).not.toHaveBeenCalledWith(expect.anything(), 0, -0);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -69,7 +69,9 @@ export default function useClips() {
|
||||
// Copy from `ctx` and rotate
|
||||
rCtx.translate(realMaxSize / 2, realMaxSize / 2);
|
||||
rCtx.rotate(angle);
|
||||
rCtx.drawImage(canvas, -contentWidth / 2, -contentHeight / 2);
|
||||
if (contentWidth > 0 && contentHeight > 0) {
|
||||
rCtx.drawImage(canvas, -contentWidth / 2, -contentHeight / 2);
|
||||
}
|
||||
|
||||
// Get boundary of rotated text
|
||||
function getRotatePos(x: number, y: number) {
|
||||
|
@ -136,7 +136,7 @@
|
||||
"rc-mentions": "~2.6.0",
|
||||
"rc-menu": "~9.11.0",
|
||||
"rc-motion": "^2.7.3",
|
||||
"rc-notification": "~5.0.4",
|
||||
"rc-notification": "~5.1.1",
|
||||
"rc-pagination": "~3.6.0",
|
||||
"rc-picker": "~3.13.0",
|
||||
"rc-progress": "~3.5.1",
|
||||
|
Loading…
Reference in New Issue
Block a user