Skip to content

Commit

Permalink
Typography support collapse (ant-design#47264)
Browse files Browse the repository at this point in the history
* feat: typograohy support collapse

* feat: snap

* feat: test

* feat: 单测不符合预期

* feat: test

* feat: 恢复

* feat: test

* feat: test

* feat: 修改命名

* feat: 代码优化

* feat: 添加控制台提示

* feat: snap

* feat: symbol support function

* feat: snap

* fix: text

* feat: snap

* feat: api 修改

* feat: key 修改

* feat: 去掉参数

* feat: lint

* feat: snap

* feat: test

* feat: use 2

* feat: review

* feat: test

* chore: part of it

* chore: fix auto collapse logic

* feat: 修改 doc 单测

* feat: doc

* test: update testcase

* docs: add more

---------

Co-authored-by: 二货机器人 <smith3816@gmail.com>
  • Loading branch information
2 people authored and CooperHash committed Mar 22, 2024
1 parent b67ba63 commit 9e379f7
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 61 deletions.
2 changes: 1 addition & 1 deletion components/_util/getAllowClear.tsx
Expand Up @@ -13,7 +13,7 @@ const getAllowClear = (allowClear: AllowClear): AllowClear => {
clearIcon: <CloseCircleFilled />,
};
}

return mergedAllowClear;
};

Expand Down
1 change: 1 addition & 0 deletions components/locale/en_US.ts
Expand Up @@ -80,6 +80,7 @@ const localeValues: Locale = {
copy: 'Copy',
copied: 'Copied',
expand: 'Expand',
collapse: 'Collapse',
},
Form: {
optional: '(optional)',
Expand Down
1 change: 1 addition & 0 deletions components/locale/index.tsx
Expand Up @@ -40,6 +40,7 @@ export interface Locale {
copy?: any;
copied?: any;
expand?: any;
collapse?: any;
};
Form?: {
optional?: string;
Expand Down
1 change: 1 addition & 0 deletions components/locale/zh_CN.ts
Expand Up @@ -80,6 +80,7 @@ const localeValues: Locale = {
copy: '复制',
copied: '复制成功',
expand: '展开',
collapse: '收起',
},
Form: {
optional: '(可选)',
Expand Down
20 changes: 11 additions & 9 deletions components/typography/Base/Ellipsis.tsx
Expand Up @@ -105,12 +105,11 @@ export interface EllipsisProps {
rows: number;
children: (
cutChildren: React.ReactNode[],
/** Tell current `cutChildren` is in ellipsis */
inEllipsis: boolean,
/** Tell current `text` is exceed the `rows` which can be ellipsis */
canEllipsis: boolean,
) => React.ReactNode;
onEllipsis: (isEllipsis: boolean) => void;
expanded: boolean;
/**
* Mark for misc update. Which will not affect ellipsis content length.
* e.g. tooltip content update.
Expand All @@ -131,13 +130,14 @@ const lineClipStyle: React.CSSProperties = {
};

export default function EllipsisMeasure(props: EllipsisProps) {
const { enableMeasure, width, text, children, rows, miscDeps, onEllipsis } = props;
const { enableMeasure, width, text, children, rows, expanded, miscDeps, onEllipsis } = props;

const nodeList = React.useMemo(() => toArray(text), [text]);
const nodeLen = React.useMemo(() => getNodesLen(nodeList), [text]);

// ========================= Full Content =========================
const fullContent = React.useMemo(() => children(nodeList, false, false), [text]);
// Used for measure only, which means it's always render as no need ellipsis
const fullContent = React.useMemo(() => children(nodeList, false), [text]);

// ========================= Cut Content ==========================
const [ellipsisCutIndex, setEllipsisCutIndex] = React.useState<[number, number] | null>(null);
Expand All @@ -150,6 +150,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {
const descRowsEllipsisRef = React.useRef<MeasureTextRef>(null);
const symbolRowEllipsisRef = React.useRef<MeasureTextRef>(null);

const [canEllipsis, setCanEllipsis] = React.useState(false);
const [needEllipsis, setNeedEllipsis] = React.useState(STATUS_MEASURE_NONE);
const [ellipsisHeight, setEllipsisHeight] = React.useState(0);

Expand All @@ -169,6 +170,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {

setNeedEllipsis(isOverflow ? STATUS_MEASURE_NEED_ELLIPSIS : STATUS_MEASURE_NO_NEED_ELLIPSIS);
setEllipsisCutIndex(isOverflow ? [0, nodeLen] : null);
setCanEllipsis(isOverflow);

// Get the basic height of ellipsis rows
const baseRowsEllipsisHeight = needEllipsisRef.current?.getHeight() || 0;
Expand Down Expand Up @@ -218,7 +220,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {
!ellipsisCutIndex ||
ellipsisCutIndex[0] !== ellipsisCutIndex[1]
) {
const content = children(nodeList, false, false);
const content = children(nodeList, false);

// Limit the max line count to avoid scrollbar blink
// https://github.com/ant-design/ant-design/issues/42958
Expand All @@ -241,8 +243,8 @@ export default function EllipsisMeasure(props: EllipsisProps) {
return content;
}

return children(sliceNodes(nodeList, ellipsisCutIndex[0]), true, true);
}, [needEllipsis, ellipsisCutIndex, nodeList, ...miscDeps]);
return children(expanded ? nodeList : sliceNodes(nodeList, ellipsisCutIndex[0]), canEllipsis);
}, [expanded, needEllipsis, ellipsisCutIndex, nodeList, ...miscDeps]);

// ============================ Render ============================
const measureStyle: React.CSSProperties = {
Expand Down Expand Up @@ -293,7 +295,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {
}}
ref={symbolRowEllipsisRef}
>
{children([], true, true)}
{children([], true)}
</MeasureText>
</>
)}
Expand All @@ -309,7 +311,7 @@ export default function EllipsisMeasure(props: EllipsisProps) {
}}
ref={cutMidRef}
>
{children(sliceNodes(nodeList, cutMidIndex), true, true)}
{children(sliceNodes(nodeList, cutMidIndex), true)}
</MeasureText>
)}
</>
Expand Down
56 changes: 29 additions & 27 deletions components/typography/Base/index.tsx
Expand Up @@ -52,10 +52,12 @@ interface EditConfig {

export interface EllipsisConfig {
rows?: number;
expandable?: boolean;
expandable?: boolean | 'collapsible';
suffix?: string;
symbol?: React.ReactNode;
onExpand?: React.MouseEventHandler<HTMLElement>;
symbol?: React.ReactNode | ((expanded: boolean) => React.ReactNode);
defaultExpanded?: boolean;
expanded?: boolean;
onExpand?: (e: React.MouseEvent<HTMLElement, MouseEvent>, info: { expanded: boolean }) => void;
onEllipsis?: (ellipsis: boolean) => void;
tooltip?: React.ReactNode | TooltipProps;
}
Expand Down Expand Up @@ -215,15 +217,19 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
const [isLineClampSupport, setIsLineClampSupport] = React.useState(false);
const [isTextOverflowSupport, setIsTextOverflowSupport] = React.useState(false);

const [expanded, setExpanded] = React.useState(false);
const [isJsEllipsis, setIsJsEllipsis] = React.useState(false);
const [isNativeEllipsis, setIsNativeEllipsis] = React.useState(false);
const [isNativeVisible, setIsNativeVisible] = React.useState(true);
const [enableEllipsis, ellipsisConfig] = useMergedConfig<EllipsisConfig>(ellipsis, {
expandable: false,
symbol: (isExpanded) => (isExpanded ? textLocale?.collapse : textLocale?.expand),
});
const [expanded, setExpanded] = useMergedState(ellipsisConfig.defaultExpanded || false, {
value: ellipsisConfig.expanded,
});

const mergedEnableEllipsis = enableEllipsis && !expanded;
const mergedEnableEllipsis =
enableEllipsis && (!expanded || ellipsisConfig.expandable === 'collapsible');

// Shared prop to reduce bundle size
const { rows = 1 } = ellipsisConfig;
Expand Down Expand Up @@ -267,9 +273,9 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
const cssLineClamp = mergedEnableEllipsis && rows > 1 && cssEllipsis;

// >>>>> Expand
const onExpandClick: React.MouseEventHandler<HTMLElement> = (e) => {
setExpanded(true);
ellipsisConfig.onExpand?.(e);
const onExpandClick: EllipsisConfig['onExpand'] = (e, info) => {
setExpanded(info.expanded);
ellipsisConfig.onExpand?.(e, info);
};

const [ellipsisWidth, setEllipsisWidth] = React.useState(0);
Expand Down Expand Up @@ -389,22 +395,16 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
const { expandable, symbol } = ellipsisConfig;

if (!expandable) return null;

let expandContent: React.ReactNode;
if (symbol) {
expandContent = symbol;
} else {
expandContent = textLocale?.expand;
}
if (expanded && expandable !== 'collapsible') return null;

return (
<a
key="expand"
className={`${prefixCls}-expand`}
onClick={onExpandClick}
aria-label={textLocale?.expand}
className={`${prefixCls}-${expanded ? 'collapse' : 'expand'}`}
onClick={(e) => onExpandClick(e, { expanded: !expanded })}
aria-label={expanded ? textLocale.collapse : textLocale?.expand}
>
{expandContent}
{typeof symbol === 'function' ? symbol(expanded) : symbol}
</a>
);
};
Expand Down Expand Up @@ -451,20 +451,21 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
);
};

const renderOperations = (renderExpanded: boolean) => [
renderExpanded && renderExpand(),
const renderOperations = (canEllipsis: boolean) => [
// (renderExpanded || ellipsisConfig.collapsible) && renderExpand(),
canEllipsis && renderExpand(),
renderEdit(),
renderCopy(),
];

const renderEllipsis = (needEllipsis: boolean) => [
needEllipsis && (
const renderEllipsis = (canEllipsis: boolean) => [
canEllipsis && !expanded && (
<span aria-hidden key="ellipsis">
{ELLIPSIS_STR}
</span>
),
ellipsisConfig.suffix,
renderOperations(needEllipsis),
renderOperations(canEllipsis),
];

return (
Expand Down Expand Up @@ -506,11 +507,12 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
rows={rows}
width={ellipsisWidth}
onEllipsis={onJsEllipsis}
expanded={expanded}
miscDeps={[copied, expanded]}
>
{(node, needEllipsis) => {
{(node, canEllipsis) => {
let renderNode: React.ReactNode = node;
if (node.length && needEllipsis && topAriaLabel) {
if (node.length && canEllipsis && !expanded && topAriaLabel) {
renderNode = (
<span key="show-content" aria-hidden>
{renderNode}
Expand All @@ -522,7 +524,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
props,
<>
{renderNode}
{renderEllipsis(needEllipsis)}
{renderEllipsis(canEllipsis)}
</>,
);

Expand Down
133 changes: 133 additions & 0 deletions components/typography/__tests__/__snapshots__/demo-extend.test.ts.snap
Expand Up @@ -1520,6 +1520,139 @@ Array [

exports[`renders components/typography/demo/ellipsis.tsx extend context correctly 2`] = `[]`;

exports[`renders components/typography/demo/ellipsis-controlled.tsx extend context correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-vertical"
style="gap: 16px;"
>
<div
class="ant-flex ant-flex-align-center"
style="gap: 16px;"
>
<button
aria-checked="false"
class="ant-switch"
role="switch"
style="flex: 0 0 auto;"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
<div
class="ant-slider ant-slider-horizontal"
style="flex-basis: auto;"
>
<div
class="ant-slider-rail"
/>
<div
class="ant-slider-track"
style="left: 0%; width: 5.263157894736842%;"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-orientation="horizontal"
aria-valuemax="20"
aria-valuemin="1"
aria-valuenow="2"
class="ant-slider-handle"
role="slider"
style="left: 5.263157894736842%; transform: translateX(-50%);"
tabindex="0"
/>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-slider-tooltip ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
>
2
</div>
</div>
</div>
</div>
</div>
<div
aria-label="Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team."
class="ant-typography ant-typography-ellipsis"
>
Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.Ant Design, a design language for background applications, is refined by Ant UED Team.
<div
aria-label="Copy"
class="ant-typography-copy"
role="button"
style="border: 0px; background: transparent; padding: 0px; line-height: inherit; display: inline-block;"
tabindex="0"
>
<span
aria-label="copy"
class="anticon anticon-copy"
role="img"
>
<svg
aria-hidden="true"
data-icon="copy"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
/>
</svg>
</span>
</div>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
role="tooltip"
>
Copy
</div>
</div>
</div>
</div>
</div>
`;

exports[`renders components/typography/demo/ellipsis-controlled.tsx extend context correctly 2`] = `[]`;

exports[`renders components/typography/demo/ellipsis-debug.tsx extend context correctly 1`] = `
Array [
<button
Expand Down

0 comments on commit 9e379f7

Please sign in to comment.