@elliemae/ds-read-more 3.52.0-rc.9 → 3.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/DSReadMore.js +2 -0
- package/dist/cjs/DSReadMore.js.map +2 -2
- package/dist/cjs/DSReadMoreDefinitions.js +2 -2
- package/dist/cjs/DSReadMoreDefinitions.js.map +2 -2
- package/dist/cjs/styled.js +28 -4
- package/dist/cjs/styled.js.map +2 -2
- package/dist/cjs/useReadMoreTruncate.js +17 -2
- package/dist/cjs/useReadMoreTruncate.js.map +2 -2
- package/dist/esm/DSReadMore.js +2 -0
- package/dist/esm/DSReadMore.js.map +2 -2
- package/dist/esm/DSReadMoreDefinitions.js +1 -1
- package/dist/esm/DSReadMoreDefinitions.js.map +1 -1
- package/dist/esm/styled.js +28 -4
- package/dist/esm/styled.js.map +2 -2
- package/dist/esm/useReadMoreTruncate.js +17 -2
- package/dist/esm/useReadMoreTruncate.js.map +2 -2
- package/dist/types/styled.d.ts +4 -1
- package/package.json +8 -8
package/dist/cjs/DSReadMore.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/DSReadMore.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["import React, { useCallback } from 'react';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { type DSReadMoreT, DSReadMorePropTypesSchema, defaultProps } from './react-desc-prop-types.js';\nimport { StyledTextWrapper, StyledTextContent, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './DSReadMoreDefinitions.js';\n\nconst DSReadMore = (props: DSReadMoreT.Props) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSReadMoreT.InternalProps>(props, defaultProps);\n const { lines, more, less, content, withTooltip, ellipsis, onMore, onLess } = propsWithDefault;\n\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n\n const { textWrapperRef, textRef, showButton, expanded, setExpanded, textProps, srOnlyText } =\n useReadMoreTruncate(propsWithDefault);\n\n const toggleExpanded = useCallback(\n (newExpanded: boolean) => {\n setExpanded(newExpanded);\n if (newExpanded) onMore();\n else onLess();\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({}), []);\n\n return (\n <StyledTextWrapper\n innerRef={textWrapperRef}\n lines={lines}\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n {...xstyledProps}\n >\n {srOnlyText}\n <StyledTextContent\n innerRef={textRef}\n data-testid=\"ds-read_more-text\"\n {...textProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n {content}\n </StyledTextContent>\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n withTooltip={withTooltip}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_TOOLTIP_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n </DSTooltipV3>\n </StyledTooltipWrapper>\n )}\n </StyledTextWrapper>\n );\n};\n\nDSReadMore.displayName = DSReadMoreName;\n\nconst DSReadMoreWithSchema = describe(DSReadMore);\n\nDSReadMoreWithSchema.propTypes = DSReadMorePropTypesSchema;\n\nexport { DSReadMore, DSReadMoreWithSchema };\nexport default DSReadMore;\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD+BnB;AA/BJ,mBAAmC;AACnC,8BAA2E;AAC3E,2BAA4B;AAC5B,4BAA+B;AAC/B,mCAA0E;AAC1E,oBAA2E;AAC3E,iCAAoC;AACpC,mCAAsD;AAEtD,MAAM,aAAa,CAAC,UAA6B;AAC/C,QAAM,uBAAmB,sDAAwD,OAAO,yCAAY;AACpG,QAAM,EAAE,OAAO,MAAM,MAAM,SAAS,aAAa,UAAU,QAAQ,OAAO,IAAI;AAE9E,QAAM,mBAAe,4CAAmB,gBAAgB;AAExD,QAAM,EAAE,gBAAgB,SAAS,YAAY,UAAU,aAAa,WAAW,WAAW,QACxF,gDAAoB,gBAAgB;AAEtC,QAAM,qBAAiB;AAAA,IACrB,CAAC,gBAAyB;AACxB,kBAAY,WAAW;AACvB,UAAI,YAAa,QAAO;AAAA,UACnB,QAAO;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,oBAAgB,0BAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,6BAAyB,0BAAY,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA,YAAY,mDAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAED,eAAe,cACd,4CAAC,sCAAqB,eAA8B,wBAClD,sDAAC,oCAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA;AAAA,YACA,YAAY,mDAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AAEzB,MAAM,2BAAuB,kCAAS,UAAU;AAEhD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
4
|
+
"sourcesContent": ["import React, { useCallback } from 'react';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { type DSReadMoreT, DSReadMorePropTypesSchema, defaultProps } from './react-desc-prop-types.js';\nimport { StyledTextWrapper, StyledTextContent, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './DSReadMoreDefinitions.js';\n\nconst DSReadMore = (props: DSReadMoreT.Props) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSReadMoreT.InternalProps>(props, defaultProps);\n const { lines, more, less, content, withTooltip, ellipsis, onMore, onLess } = propsWithDefault;\n\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n\n const { textWrapperRef, textRef, showButton, expanded, setExpanded, textProps, srOnlyText } =\n useReadMoreTruncate(propsWithDefault);\n\n const toggleExpanded = useCallback(\n (newExpanded: boolean) => {\n setExpanded(newExpanded);\n if (newExpanded) onMore();\n else onLess();\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({}), []);\n\n return (\n <StyledTextWrapper\n innerRef={textWrapperRef}\n lines={lines}\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n {...xstyledProps}\n >\n {srOnlyText}\n <StyledTextContent\n lines={lines}\n expanded={expanded}\n innerRef={textRef}\n data-testid=\"ds-read_more-text\"\n {...textProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n {content}\n </StyledTextContent>\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n withTooltip={withTooltip}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_TOOLTIP_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n </DSTooltipV3>\n </StyledTooltipWrapper>\n )}\n </StyledTextWrapper>\n );\n};\n\nDSReadMore.displayName = DSReadMoreName;\n\nconst DSReadMoreWithSchema = describe(DSReadMore);\n\nDSReadMoreWithSchema.propTypes = DSReadMorePropTypesSchema;\n\nexport { DSReadMore, DSReadMoreWithSchema };\nexport default DSReadMore;\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD+BnB;AA/BJ,mBAAmC;AACnC,8BAA2E;AAC3E,2BAA4B;AAC5B,4BAA+B;AAC/B,mCAA0E;AAC1E,oBAA2E;AAC3E,iCAAoC;AACpC,mCAAsD;AAEtD,MAAM,aAAa,CAAC,UAA6B;AAC/C,QAAM,uBAAmB,sDAAwD,OAAO,yCAAY;AACpG,QAAM,EAAE,OAAO,MAAM,MAAM,SAAS,aAAa,UAAU,QAAQ,OAAO,IAAI;AAE9E,QAAM,mBAAe,4CAAmB,gBAAgB;AAExD,QAAM,EAAE,gBAAgB,SAAS,YAAY,UAAU,aAAa,WAAW,WAAW,QACxF,gDAAoB,gBAAgB;AAEtC,QAAM,qBAAiB;AAAA,IACrB,CAAC,gBAAyB;AACxB,kBAAY,WAAW;AACvB,UAAI,YAAa,QAAO;AAAA,UACnB,QAAO;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,oBAAgB,0BAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,6BAAyB,0BAAY,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA,YAAY,mDAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAED,eAAe,cACd,4CAAC,sCAAqB,eAA8B,wBAClD,sDAAC,oCAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA;AAAA,YACA,YAAY,mDAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AAEzB,MAAM,2BAAuB,kCAAS,UAAU;AAEhD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -34,7 +34,7 @@ __export(DSReadMoreDefinitions_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(DSReadMoreDefinitions_exports);
|
|
36
36
|
var React = __toESM(require("react"));
|
|
37
|
-
var
|
|
37
|
+
var import_lodash_es = require("lodash-es");
|
|
38
38
|
var import_ds_system = require("@elliemae/ds-system");
|
|
39
39
|
const DSReadMoreName = "DSReadmore";
|
|
40
40
|
const DSReadMoreSlots = {
|
|
@@ -45,7 +45,7 @@ const DSReadMoreSlots = {
|
|
|
45
45
|
BUTTON: "button"
|
|
46
46
|
};
|
|
47
47
|
const DSReadMoreDataTestIds = {
|
|
48
|
-
...(0,
|
|
48
|
+
...(0, import_lodash_es.omit)((0, import_ds_system.slotObjectToDataTestIds)(DSReadMoreName, DSReadMoreSlots), "BUTTON"),
|
|
49
49
|
READ_MORE_BUTTON: "ds-read_more-button",
|
|
50
50
|
READ_MORE_TOOLTIP_BUTTON: "ds-read_more-tooltip-button"
|
|
51
51
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/DSReadMoreDefinitions.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["import { omit } from 'lodash';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,
|
|
4
|
+
"sourcesContent": ["import { omit } from 'lodash-es';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,uBAAqB;AACrB,uBAAwC;AAEjC,MAAM,iBAAiB;AAEvB,MAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAEO,MAAM,wBAAwB;AAAA,EACnC,OAAG,2BAAK,0CAAwB,gBAAgB,eAAe,GAAG,QAAQ;AAAA,EAC1E,kBAAkB;AAAA,EAClB,0BAA0B;AAC5B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/styled.js
CHANGED
|
@@ -42,9 +42,26 @@ var import_ds_button_v2 = require("@elliemae/ds-button-v2");
|
|
|
42
42
|
var import_DSReadMoreDefinitions = require("./DSReadMoreDefinitions.js");
|
|
43
43
|
const StyledTextWrapper = (0, import_ds_system.styled)("span", { name: import_DSReadMoreDefinitions.DSReadMoreName, slot: import_DSReadMoreDefinitions.DSReadMoreSlots.ROOT })`
|
|
44
44
|
position: relative;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
/**
|
|
46
|
+
* PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia
|
|
47
|
+
* -----------------------------------
|
|
48
|
+
* In Safari 17.4+ (macOS Sequoia), using scrollHeight or offsetHeight with line-clamp
|
|
49
|
+
* can break the layout and, potentially, trigger infinite loops through ResizeObserver.
|
|
50
|
+
*
|
|
51
|
+
* - Avoids -webkit-line-clamp: 1 which breaks layout and can cause infinite ResizeObserver loops.
|
|
52
|
+
* - Instead, uses flex + white-space + overflow hidden for single-line truncation.
|
|
53
|
+
*/
|
|
54
|
+
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: baseline;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
` : `
|
|
60
|
+
display: -webkit-box;
|
|
61
|
+
-webkit-line-clamp: ${expanded ? "unset" : lines};
|
|
62
|
+
-webkit-box-orient: vertical;
|
|
63
|
+
`}
|
|
64
|
+
|
|
48
65
|
-webkit-hyphens: auto;
|
|
49
66
|
-moz-hyphens: auto;
|
|
50
67
|
-ms-hyphens: auto;
|
|
@@ -52,7 +69,14 @@ const StyledTextWrapper = (0, import_ds_system.styled)("span", { name: import_DS
|
|
|
52
69
|
word-break: break-all;
|
|
53
70
|
${import_ds_system.xStyledCommonProps}
|
|
54
71
|
`;
|
|
55
|
-
const StyledTextContent = (0, import_ds_system.styled)("span", { name: import_DSReadMoreDefinitions.DSReadMoreName, slot: import_DSReadMoreDefinitions.DSReadMoreSlots.TEXT_CONTENT })
|
|
72
|
+
const StyledTextContent = (0, import_ds_system.styled)("span", { name: import_DSReadMoreDefinitions.DSReadMoreName, slot: import_DSReadMoreDefinitions.DSReadMoreSlots.TEXT_CONTENT })`
|
|
73
|
+
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
74
|
+
flex: 1 1 auto;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
text-overflow: clip;
|
|
78
|
+
` : ""}
|
|
79
|
+
`;
|
|
56
80
|
const StyledTooltipWrapper = (0, import_ds_system.styled)("div", { name: import_DSReadMoreDefinitions.DSReadMoreName, slot: import_DSReadMoreDefinitions.DSReadMoreSlots.TOOLTIP_WRAPPER })`
|
|
57
81
|
display: inline-block;
|
|
58
82
|
`;
|
package/dist/cjs/styled.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/styled.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV2 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './DSReadMoreDefinitions.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,uBAA2C;AAC3C,0BAA2B;AAC3B,mCAAgD;AAEzC,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,KAAK,CAAC;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV2 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './DSReadMoreDefinitions.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n /**\n * PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia\n * -----------------------------------\n * In Safari 17.4+ (macOS Sequoia), using scrollHeight or offsetHeight with line-clamp\n * can break the layout and, potentially, trigger infinite loops through ResizeObserver.\n *\n * - Avoids -webkit-line-clamp: 1 which breaks layout and can cause infinite ResizeObserver loops.\n * - Instead, uses flex + white-space + overflow hidden for single-line truncation.\n */\n ${({ lines, expanded }) =>\n lines === 1 && !expanded\n ? `\n display: flex;\n align-items: baseline;\n overflow: hidden;\n white-space: nowrap;\n `\n : `\n display: -webkit-box;\n -webkit-line-clamp: ${expanded ? 'unset' : lines};\n -webkit-box-orient: vertical;\n `}\n\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n -ms-hyphens: auto;\n hyphens: auto;\n word-break: break-all;\n ${xStyledCommonProps}\n`;\n\nexport const StyledTextContent = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })<{\n lines: number;\n expanded: boolean;\n}>`\n ${({ lines, expanded }) =>\n lines === 1 && !expanded\n ? `\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: clip;\n `\n : ''}\n`;\n\nexport const StyledTooltipWrapper = styled('div', { name: DSReadMoreName, slot: DSReadMoreSlots.TOOLTIP_WRAPPER })`\n display: inline-block;\n`;\n\nexport const StyledButtonWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON_WRAPPER })``;\n\nexport const StyledButton = styled(DSButtonV2, { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON })<{\n withTooltip?: boolean;\n}>`\n padding: 0;\n color: ${({ withTooltip, theme }) => (withTooltip ? 'inherit' : `${theme.colors.brand['600']}`)};\n border: 0;\n margin: 0;\n font-size: 12px;\n min-width: 0px;\n height: auto;\n text-transform: unset;\n &:hover:not([disabled]) {\n color: ${({ withTooltip, theme }) => (withTooltip ? 'inherit' : `${theme.colors.brand['600']}`)};\n background-color: transparent;\n text-decoration: ${({ withTooltip }) => (withTooltip ? '' : 'underline')};\n }\n`;\n\nexport const ScreenReaderOnly = styled.span`\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 100%;\n overflow: hidden;\n position: absolute;\n width: 100%;\n`;\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,uBAA2C;AAC3C,0BAA2B;AAC3B,mCAAgD;AAEzC,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAchG,CAAC,EAAE,OAAO,SAAS,MACnB,UAAU,KAAK,CAAC,WACZ;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA;AAAA;AAAA,0BAEkB,WAAW,UAAU,KAAK;AAAA;AAAA,GAEjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOC,mCAAkB;AAAA;AAGf,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,aAAa,CAAC;AAAA,IAIxG,CAAC,EAAE,OAAO,SAAS,MACnB,UAAU,KAAK,CAAC,WACZ;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,EAAE;AAAA;AAGH,MAAM,2BAAuB,yBAAO,OAAO,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,gBAAgB,CAAC;AAAA;AAAA;AAI1G,MAAM,0BAAsB,yBAAO,QAAQ,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,eAAe,CAAC;AAEzG,MAAM,mBAAe,yBAAO,gCAAY,EAAE,MAAM,6CAAgB,MAAM,6CAAgB,OAAO,CAAC;AAAA;AAAA,WAI1F,CAAC,EAAE,aAAa,MAAM,MAAO,cAAc,YAAY,GAAG,MAAM,OAAO,MAAM,KAAK,CAAC,EAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAQpF,CAAC,EAAE,aAAa,MAAM,MAAO,cAAc,YAAY,GAAG,MAAM,OAAO,MAAM,KAAK,CAAC,EAAG;AAAA;AAAA,uBAE5E,CAAC,EAAE,YAAY,MAAO,cAAc,KAAK,WAAY;AAAA;AAAA;AAIrE,MAAM,mBAAmB,wBAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -64,8 +64,23 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
64
64
|
const parentElement = textWrapperRef.current;
|
|
65
65
|
if (parentElement && textElement) {
|
|
66
66
|
textElement.innerText = content;
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
if (lines === 1) {
|
|
68
|
+
const computed = getComputedStyle(textElement);
|
|
69
|
+
const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;
|
|
70
|
+
const canvas = document.createElement("canvas");
|
|
71
|
+
const ctx = canvas.getContext("2d");
|
|
72
|
+
let isTruncated = false;
|
|
73
|
+
if (ctx) {
|
|
74
|
+
ctx.font = font;
|
|
75
|
+
const measuredWidth = ctx.measureText(content).width;
|
|
76
|
+
isTruncated = measuredWidth > parentElement.clientWidth;
|
|
77
|
+
}
|
|
78
|
+
setShowButton(expanded || isTruncated);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const isOverflowing = overflows(parentElement);
|
|
82
|
+
setShowButton(expanded || isOverflowing);
|
|
83
|
+
if (!expanded && isOverflowing) {
|
|
69
84
|
binSearchTextOverflow(textElement, parentElement, content);
|
|
70
85
|
}
|
|
71
86
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/useReadMoreTruncate.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\n\n// offsetHeight + 2 takes into consideration any size of the button\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 2 < scrollHeight;\n\n// Searchs the optimum cut on the text to have the button in the same line\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n const text = element.innerText;\n let left = 1;\n let right = text.length;\n let textEnd = 0;\n while (left <= right) {\n const middle = (left + right) / 2;\n element.innerText = content.slice(0, middle);\n if (overflows(parent)) right = middle - 1;\n else {\n left = middle + 1;\n textEnd = middle;\n }\n }\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n\n const [showButton, setShowButton] = useState(false);\n\n // We need to re-run the effect when the size of the container changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textElement = textRef.current;\n const parentElement = textWrapperRef.current;\n if (parentElement && textElement) {\n textElement.innerText = content;\n setShowButton(expanded || overflows(parentElement));\n if (!expanded &&
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;
|
|
4
|
+
"sourcesContent": ["import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\n\n// offsetHeight + 2 takes into consideration any size of the button\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 2 < scrollHeight;\n\n// Searchs the optimum cut on the text to have the button in the same line\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n const text = element.innerText;\n let left = 1;\n let right = text.length;\n let textEnd = 0;\n while (left <= right) {\n const middle = (left + right) / 2;\n element.innerText = content.slice(0, middle);\n if (overflows(parent)) right = middle - 1;\n else {\n left = middle + 1;\n textEnd = middle;\n }\n }\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n\n const [showButton, setShowButton] = useState(false);\n\n // We need to re-run the effect when the size of the container changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textElement = textRef.current;\n const parentElement = textWrapperRef.current;\n if (parentElement && textElement) {\n textElement.innerText = content;\n /**\n * PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia\n * -----------------------------------\n * In Safari 17.4+ (macOS Sequoia), using `scrollHeight` or `offsetHeight` with line-clamp\n * can break the layout and, potentially, trigger infinite loops through ResizeObserver.\n *\n * Instead of relying on overflow-based measurements, we calculate the actual pixel width\n * of the text using a canvas and compare it with the visible width of the wrapper.\n *\n * This method is layout-safe, does not force reflow, and works reliably across browsers.\n */\n if (lines === 1) {\n const computed = getComputedStyle(textElement);\n const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n let isTruncated = false;\n if (ctx) {\n ctx.font = font;\n const measuredWidth = ctx.measureText(content).width;\n isTruncated = measuredWidth > parentElement.clientWidth;\n }\n setShowButton(expanded || isTruncated);\n return;\n }\n\n const isOverflowing = overflows(parentElement);\n setShowButton(expanded || isOverflowing);\n if (!expanded && isOverflowing) {\n binSearchTextOverflow(textElement, parentElement, content);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n const textProps = useMemo(\n () => ({\n 'aria-hidden': true,\n }),\n [],\n );\n\n return {\n textWrapperRef,\n textRef,\n showButton,\n expanded,\n setExpanded,\n textProps,\n srOnlyText,\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;AD6EY;AA7EnC,mBAAkE;AAClE,gCAAmC;AAEnC,oBAAiC;AAGjC,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAGrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,QAAM,OAAO,QAAQ;AACrB,MAAI,OAAO;AACX,MAAI,QAAQ,KAAK;AACjB,MAAI,UAAU;AACd,SAAO,QAAQ,OAAO;AACpB,UAAM,UAAU,OAAO,SAAS;AAChC,YAAQ,YAAY,QAAQ,MAAM,GAAG,MAAM;AAC3C,QAAI,UAAU,MAAM,EAAG,SAAQ,SAAS;AAAA,SACnC;AACH,aAAO,SAAS;AAChB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,UAAQ,YAAY,QAAQ,MAAM,GAAG,OAAO;AAC9C;AAEO,MAAM,sBAAsB,CAAC,EAAE,OAAO,QAAQ,MAA4D;AAC/G,QAAM,qBAAiB,qBAAwB,IAAI;AACnD,QAAM,cAAU,qBAAwB,IAAI;AAE5C,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAE9C,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,QAAI,8CAAmB,cAAc;AAE3D,oCAAgB,MAAM;AACpB,UAAM,cAAc,QAAQ;AAC5B,UAAM,gBAAgB,eAAe;AACrC,QAAI,iBAAiB,aAAa;AAChC,kBAAY,YAAY;AAYxB,UAAI,UAAU,GAAG;AACf,cAAM,WAAW,iBAAiB,WAAW;AAC7C,cAAM,OAAO,GAAG,SAAS,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS,UAAU;AAC/E,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,cAAM,MAAM,OAAO,WAAW,IAAI;AAClC,YAAI,cAAc;AAClB,YAAI,KAAK;AACP,cAAI,OAAO;AACX,gBAAM,gBAAgB,IAAI,YAAY,OAAO,EAAE;AAC/C,wBAAc,gBAAgB,cAAc;AAAA,QAC9C;AACA,sBAAc,YAAY,WAAW;AACrC;AAAA,MACF;AAEA,YAAM,gBAAgB,UAAU,aAAa;AAC7C,oBAAc,YAAY,aAAa;AACvC,UAAI,CAAC,YAAY,eAAe;AAC9B,8BAAsB,aAAa,eAAe,OAAO;AAAA,MAC3D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAE5C,QAAM,iBAAa,sBAAQ,MAAM,4CAAC,kCAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAE1F,QAAM,gBAAY;AAAA,IAChB,OAAO;AAAA,MACL,eAAe;AAAA,IACjB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/esm/DSReadMore.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSReadMore.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { type DSReadMoreT, DSReadMorePropTypesSchema, defaultProps } from './react-desc-prop-types.js';\nimport { StyledTextWrapper, StyledTextContent, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './DSReadMoreDefinitions.js';\n\nconst DSReadMore = (props: DSReadMoreT.Props) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSReadMoreT.InternalProps>(props, defaultProps);\n const { lines, more, less, content, withTooltip, ellipsis, onMore, onLess } = propsWithDefault;\n\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n\n const { textWrapperRef, textRef, showButton, expanded, setExpanded, textProps, srOnlyText } =\n useReadMoreTruncate(propsWithDefault);\n\n const toggleExpanded = useCallback(\n (newExpanded: boolean) => {\n setExpanded(newExpanded);\n if (newExpanded) onMore();\n else onLess();\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({}), []);\n\n return (\n <StyledTextWrapper\n innerRef={textWrapperRef}\n lines={lines}\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n {...xstyledProps}\n >\n {srOnlyText}\n <StyledTextContent\n innerRef={textRef}\n data-testid=\"ds-read_more-text\"\n {...textProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n {content}\n </StyledTextContent>\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n withTooltip={withTooltip}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_TOOLTIP_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n </DSTooltipV3>\n </StyledTooltipWrapper>\n )}\n </StyledTextWrapper>\n );\n};\n\nDSReadMore.displayName = DSReadMoreName;\n\nconst DSReadMoreWithSchema = describe(DSReadMore);\n\nDSReadMoreWithSchema.propTypes = DSReadMorePropTypesSchema;\n\nexport { DSReadMore, DSReadMoreWithSchema };\nexport default DSReadMore;\n"],
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;AC+BnB,SASE,KATF;AA/BJ,SAAgB,mBAAmB;AACnC,SAAS,UAAU,oBAAoB,oCAAoC;AAC3E,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAA2B,2BAA2B,oBAAoB;AAC1E,SAAS,mBAAmB,mBAAmB,4BAA4B;AAC3E,SAAS,2BAA2B;AACpC,SAAS,uBAAuB,sBAAsB;AAEtD,MAAM,aAAa,CAAC,UAA6B;AAC/C,QAAM,mBAAmB,6BAAwD,OAAO,YAAY;AACpG,QAAM,EAAE,OAAO,MAAM,MAAM,SAAS,aAAa,UAAU,QAAQ,OAAO,IAAI;AAE9E,QAAM,eAAe,mBAAmB,gBAAgB;AAExD,QAAM,EAAE,gBAAgB,SAAS,YAAY,UAAU,aAAa,WAAW,WAAW,IACxF,oBAAoB,gBAAgB;AAEtC,QAAM,iBAAiB;AAAA,IACrB,CAAC,gBAAyB;AACxB,kBAAY,WAAW;AACvB,UAAI,YAAa,QAAO;AAAA,UACnB,QAAO;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,gBAAgB,YAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,yBAAyB,YAAY,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAED,eAAe,cACd,oBAAC,wBAAqB,eAA8B,wBAClD,8BAAC,eAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AAEzB,MAAM,uBAAuB,SAAS,UAAU;AAEhD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useCallback } from 'react';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { type DSReadMoreT, DSReadMorePropTypesSchema, defaultProps } from './react-desc-prop-types.js';\nimport { StyledTextWrapper, StyledTextContent, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './DSReadMoreDefinitions.js';\n\nconst DSReadMore = (props: DSReadMoreT.Props) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSReadMoreT.InternalProps>(props, defaultProps);\n const { lines, more, less, content, withTooltip, ellipsis, onMore, onLess } = propsWithDefault;\n\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n\n const { textWrapperRef, textRef, showButton, expanded, setExpanded, textProps, srOnlyText } =\n useReadMoreTruncate(propsWithDefault);\n\n const toggleExpanded = useCallback(\n (newExpanded: boolean) => {\n setExpanded(newExpanded);\n if (newExpanded) onMore();\n else onLess();\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({}), []);\n\n return (\n <StyledTextWrapper\n innerRef={textWrapperRef}\n lines={lines}\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n {...xstyledProps}\n >\n {srOnlyText}\n <StyledTextContent\n lines={lines}\n expanded={expanded}\n innerRef={textRef}\n data-testid=\"ds-read_more-text\"\n {...textProps}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n {content}\n </StyledTextContent>\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\n ariaLabel={expanded ? 'Read less' : 'Read more'}\n ellipsis={ellipsis}\n withTooltip={withTooltip}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_TOOLTIP_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n </DSTooltipV3>\n </StyledTooltipWrapper>\n )}\n </StyledTextWrapper>\n );\n};\n\nDSReadMore.displayName = DSReadMoreName;\n\nconst DSReadMoreWithSchema = describe(DSReadMore);\n\nDSReadMoreWithSchema.propTypes = DSReadMorePropTypesSchema;\n\nexport { DSReadMore, DSReadMoreWithSchema };\nexport default DSReadMore;\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;AC+BnB,SASE,KATF;AA/BJ,SAAgB,mBAAmB;AACnC,SAAS,UAAU,oBAAoB,oCAAoC;AAC3E,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAA2B,2BAA2B,oBAAoB;AAC1E,SAAS,mBAAmB,mBAAmB,4BAA4B;AAC3E,SAAS,2BAA2B;AACpC,SAAS,uBAAuB,sBAAsB;AAEtD,MAAM,aAAa,CAAC,UAA6B;AAC/C,QAAM,mBAAmB,6BAAwD,OAAO,YAAY;AACpG,QAAM,EAAE,OAAO,MAAM,MAAM,SAAS,aAAa,UAAU,QAAQ,OAAO,IAAI;AAE9E,QAAM,eAAe,mBAAmB,gBAAgB;AAExD,QAAM,EAAE,gBAAgB,SAAS,YAAY,UAAU,aAAa,WAAW,WAAW,IACxF,oBAAoB,gBAAgB;AAEtC,QAAM,iBAAiB;AAAA,IACrB,CAAC,gBAAyB;AACxB,kBAAY,WAAW;AACvB,UAAI,YAAa,QAAO;AAAA,UACnB,QAAO;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,gBAAgB,YAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,yBAAyB,YAAY,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAED,eAAe,cACd,oBAAC,wBAAqB,eAA8B,wBAClD,8BAAC,eAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN,WAAW,WAAW,cAAc;AAAA,YACpC;AAAA,YACA;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AAEzB,MAAM,uBAAuB,SAAS,UAAU;AAEhD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSReadMoreDefinitions.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { omit } from 'lodash';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n"],
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { omit } from 'lodash-es';\nimport { slotObjectToDataTestIds } from '@elliemae/ds-system';\n\nexport const DSReadMoreName = 'DSReadmore';\n\nexport const DSReadMoreSlots = {\n ROOT: 'root',\n TEXT_CONTENT: 'text-content',\n TOOLTIP_WRAPPER: 'tooltip-wrapper',\n BUTTON_WRAPPER: 'button-wrapper',\n BUTTON: 'button',\n};\n\nexport const DSReadMoreDataTestIds = {\n ...omit(slotObjectToDataTestIds(DSReadMoreName, DSReadMoreSlots), 'BUTTON'),\n READ_MORE_BUTTON: 'ds-read_more-button',\n READ_MORE_TOOLTIP_BUTTON: 'ds-read_more-tooltip-button',\n};\n"],
|
|
5
5
|
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,YAAY;AACrB,SAAS,+BAA+B;AAEjC,MAAM,iBAAiB;AAEvB,MAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAEO,MAAM,wBAAwB;AAAA,EACnC,GAAG,KAAK,wBAAwB,gBAAgB,eAAe,GAAG,QAAQ;AAAA,EAC1E,kBAAkB;AAAA,EAClB,0BAA0B;AAC5B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/esm/styled.js
CHANGED
|
@@ -4,9 +4,26 @@ import { DSButtonV2 } from "@elliemae/ds-button-v2";
|
|
|
4
4
|
import { DSReadMoreName, DSReadMoreSlots } from "./DSReadMoreDefinitions.js";
|
|
5
5
|
const StyledTextWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })`
|
|
6
6
|
position: relative;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
/**
|
|
8
|
+
* PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia
|
|
9
|
+
* -----------------------------------
|
|
10
|
+
* In Safari 17.4+ (macOS Sequoia), using scrollHeight or offsetHeight with line-clamp
|
|
11
|
+
* can break the layout and, potentially, trigger infinite loops through ResizeObserver.
|
|
12
|
+
*
|
|
13
|
+
* - Avoids -webkit-line-clamp: 1 which breaks layout and can cause infinite ResizeObserver loops.
|
|
14
|
+
* - Instead, uses flex + white-space + overflow hidden for single-line truncation.
|
|
15
|
+
*/
|
|
16
|
+
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: baseline;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
` : `
|
|
22
|
+
display: -webkit-box;
|
|
23
|
+
-webkit-line-clamp: ${expanded ? "unset" : lines};
|
|
24
|
+
-webkit-box-orient: vertical;
|
|
25
|
+
`}
|
|
26
|
+
|
|
10
27
|
-webkit-hyphens: auto;
|
|
11
28
|
-moz-hyphens: auto;
|
|
12
29
|
-ms-hyphens: auto;
|
|
@@ -14,7 +31,14 @@ const StyledTextWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMor
|
|
|
14
31
|
word-break: break-all;
|
|
15
32
|
${xStyledCommonProps}
|
|
16
33
|
`;
|
|
17
|
-
const StyledTextContent = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })
|
|
34
|
+
const StyledTextContent = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })`
|
|
35
|
+
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
36
|
+
flex: 1 1 auto;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
text-overflow: clip;
|
|
40
|
+
` : ""}
|
|
41
|
+
`;
|
|
18
42
|
const StyledTooltipWrapper = styled("div", { name: DSReadMoreName, slot: DSReadMoreSlots.TOOLTIP_WRAPPER })`
|
|
19
43
|
display: inline-block;
|
|
20
44
|
`;
|
package/dist/esm/styled.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/styled.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV2 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './DSReadMoreDefinitions.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,QAAQ,0BAA0B;AAC3C,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB,uBAAuB;AAEzC,MAAM,oBAAoB,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,KAAK,CAAC;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV2 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './DSReadMoreDefinitions.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n /**\n * PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia\n * -----------------------------------\n * In Safari 17.4+ (macOS Sequoia), using scrollHeight or offsetHeight with line-clamp\n * can break the layout and, potentially, trigger infinite loops through ResizeObserver.\n *\n * - Avoids -webkit-line-clamp: 1 which breaks layout and can cause infinite ResizeObserver loops.\n * - Instead, uses flex + white-space + overflow hidden for single-line truncation.\n */\n ${({ lines, expanded }) =>\n lines === 1 && !expanded\n ? `\n display: flex;\n align-items: baseline;\n overflow: hidden;\n white-space: nowrap;\n `\n : `\n display: -webkit-box;\n -webkit-line-clamp: ${expanded ? 'unset' : lines};\n -webkit-box-orient: vertical;\n `}\n\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n -ms-hyphens: auto;\n hyphens: auto;\n word-break: break-all;\n ${xStyledCommonProps}\n`;\n\nexport const StyledTextContent = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })<{\n lines: number;\n expanded: boolean;\n}>`\n ${({ lines, expanded }) =>\n lines === 1 && !expanded\n ? `\n flex: 1 1 auto;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: clip;\n `\n : ''}\n`;\n\nexport const StyledTooltipWrapper = styled('div', { name: DSReadMoreName, slot: DSReadMoreSlots.TOOLTIP_WRAPPER })`\n display: inline-block;\n`;\n\nexport const StyledButtonWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON_WRAPPER })``;\n\nexport const StyledButton = styled(DSButtonV2, { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON })<{\n withTooltip?: boolean;\n}>`\n padding: 0;\n color: ${({ withTooltip, theme }) => (withTooltip ? 'inherit' : `${theme.colors.brand['600']}`)};\n border: 0;\n margin: 0;\n font-size: 12px;\n min-width: 0px;\n height: auto;\n text-transform: unset;\n &:hover:not([disabled]) {\n color: ${({ withTooltip, theme }) => (withTooltip ? 'inherit' : `${theme.colors.brand['600']}`)};\n background-color: transparent;\n text-decoration: ${({ withTooltip }) => (withTooltip ? '' : 'underline')};\n }\n`;\n\nexport const ScreenReaderOnly = styled.span`\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n height: 100%;\n overflow: hidden;\n position: absolute;\n width: 100%;\n`;\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,QAAQ,0BAA0B;AAC3C,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB,uBAAuB;AAEzC,MAAM,oBAAoB,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAchG,CAAC,EAAE,OAAO,SAAS,MACnB,UAAU,KAAK,CAAC,WACZ;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA;AAAA;AAAA,0BAEkB,WAAW,UAAU,KAAK;AAAA;AAAA,GAEjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOC,kBAAkB;AAAA;AAGf,MAAM,oBAAoB,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,aAAa,CAAC;AAAA,IAIxG,CAAC,EAAE,OAAO,SAAS,MACnB,UAAU,KAAK,CAAC,WACZ;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,EAAE;AAAA;AAGH,MAAM,uBAAuB,OAAO,OAAO,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,gBAAgB,CAAC;AAAA;AAAA;AAI1G,MAAM,sBAAsB,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,eAAe,CAAC;AAEzG,MAAM,eAAe,OAAO,YAAY,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,OAAO,CAAC;AAAA;AAAA,WAI1F,CAAC,EAAE,aAAa,MAAM,MAAO,cAAc,YAAY,GAAG,MAAM,OAAO,MAAM,KAAK,CAAC,EAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAQpF,CAAC,EAAE,aAAa,MAAM,MAAO,cAAc,YAAY,GAAG,MAAM,OAAO,MAAM,KAAK,CAAC,EAAG;AAAA;AAAA,uBAE5E,CAAC,EAAE,YAAY,MAAO,cAAc,KAAK,WAAY;AAAA;AAAA;AAIrE,MAAM,mBAAmB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -31,8 +31,23 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
31
31
|
const parentElement = textWrapperRef.current;
|
|
32
32
|
if (parentElement && textElement) {
|
|
33
33
|
textElement.innerText = content;
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
if (lines === 1) {
|
|
35
|
+
const computed = getComputedStyle(textElement);
|
|
36
|
+
const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;
|
|
37
|
+
const canvas = document.createElement("canvas");
|
|
38
|
+
const ctx = canvas.getContext("2d");
|
|
39
|
+
let isTruncated = false;
|
|
40
|
+
if (ctx) {
|
|
41
|
+
ctx.font = font;
|
|
42
|
+
const measuredWidth = ctx.measureText(content).width;
|
|
43
|
+
isTruncated = measuredWidth > parentElement.clientWidth;
|
|
44
|
+
}
|
|
45
|
+
setShowButton(expanded || isTruncated);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const isOverflowing = overflows(parentElement);
|
|
49
|
+
setShowButton(expanded || isOverflowing);
|
|
50
|
+
if (!expanded && isOverflowing) {
|
|
36
51
|
binSearchTextOverflow(textElement, parentElement, content);
|
|
37
52
|
}
|
|
38
53
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/useReadMoreTruncate.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\n\n// offsetHeight + 2 takes into consideration any size of the button\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 2 < scrollHeight;\n\n// Searchs the optimum cut on the text to have the button in the same line\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n const text = element.innerText;\n let left = 1;\n let right = text.length;\n let textEnd = 0;\n while (left <= right) {\n const middle = (left + right) / 2;\n element.innerText = content.slice(0, middle);\n if (overflows(parent)) right = middle - 1;\n else {\n left = middle + 1;\n textEnd = middle;\n }\n }\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n\n const [showButton, setShowButton] = useState(false);\n\n // We need to re-run the effect when the size of the container changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textElement = textRef.current;\n const parentElement = textWrapperRef.current;\n if (parentElement && textElement) {\n textElement.innerText = content;\n setShowButton(expanded || overflows(parentElement));\n if (!expanded &&
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { useOnElementResize } from './useOnElementResize.js';\nimport type { DSReadMoreT } from './react-desc-prop-types.js';\nimport { ScreenReaderOnly } from './styled.js';\n\n// offsetHeight + 2 takes into consideration any size of the button\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 2 < scrollHeight;\n\n// Searchs the optimum cut on the text to have the button in the same line\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n const text = element.innerText;\n let left = 1;\n let right = text.length;\n let textEnd = 0;\n while (left <= right) {\n const middle = (left + right) / 2;\n element.innerText = content.slice(0, middle);\n if (overflows(parent)) right = middle - 1;\n else {\n left = middle + 1;\n textEnd = middle;\n }\n }\n element.innerText = content.slice(0, textEnd);\n};\n\nexport const useReadMoreTruncate = ({ lines, content }: Pick<DSReadMoreT.InternalProps, 'lines' | 'content'>) => {\n const textWrapperRef = useRef<HTMLSpanElement>(null);\n const textRef = useRef<HTMLSpanElement>(null);\n\n const [expanded, setExpanded] = useState(false);\n\n const [showButton, setShowButton] = useState(false);\n\n // We need to re-run the effect when the size of the container changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textElement = textRef.current;\n const parentElement = textWrapperRef.current;\n if (parentElement && textElement) {\n textElement.innerText = content;\n /**\n * PUI-16110 - [ReadMore] Autotriggered-Visually broken in safari + Sequoia\n * -----------------------------------\n * In Safari 17.4+ (macOS Sequoia), using `scrollHeight` or `offsetHeight` with line-clamp\n * can break the layout and, potentially, trigger infinite loops through ResizeObserver.\n *\n * Instead of relying on overflow-based measurements, we calculate the actual pixel width\n * of the text using a canvas and compare it with the visible width of the wrapper.\n *\n * This method is layout-safe, does not force reflow, and works reliably across browsers.\n */\n if (lines === 1) {\n const computed = getComputedStyle(textElement);\n const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n let isTruncated = false;\n if (ctx) {\n ctx.font = font;\n const measuredWidth = ctx.measureText(content).width;\n isTruncated = measuredWidth > parentElement.clientWidth;\n }\n setShowButton(expanded || isTruncated);\n return;\n }\n\n const isOverflowing = overflows(parentElement);\n setShowButton(expanded || isOverflowing);\n if (!expanded && isOverflowing) {\n binSearchTextOverflow(textElement, parentElement, content);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n const textProps = useMemo(\n () => ({\n 'aria-hidden': true,\n }),\n [],\n );\n\n return {\n textWrapperRef,\n textRef,\n showButton,\n expanded,\n setExpanded,\n textProps,\n srOnlyText,\n };\n};\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;AC6EY;AA7EnC,SAAgB,iBAAiB,SAAS,QAAQ,gBAAgB;AAClE,SAAS,0BAA0B;AAEnC,SAAS,wBAAwB;AAGjC,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAGrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,QAAM,OAAO,QAAQ;AACrB,MAAI,OAAO;AACX,MAAI,QAAQ,KAAK;AACjB,MAAI,UAAU;AACd,SAAO,QAAQ,OAAO;AACpB,UAAM,UAAU,OAAO,SAAS;AAChC,YAAQ,YAAY,QAAQ,MAAM,GAAG,MAAM;AAC3C,QAAI,UAAU,MAAM,EAAG,SAAQ,SAAS;AAAA,SACnC;AACH,aAAO,SAAS;AAChB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,UAAQ,YAAY,QAAQ,MAAM,GAAG,OAAO;AAC9C;AAEO,MAAM,sBAAsB,CAAC,EAAE,OAAO,QAAQ,MAA4D;AAC/G,QAAM,iBAAiB,OAAwB,IAAI;AACnD,QAAM,UAAU,OAAwB,IAAI;AAE5C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,cAAc;AAE3D,kBAAgB,MAAM;AACpB,UAAM,cAAc,QAAQ;AAC5B,UAAM,gBAAgB,eAAe;AACrC,QAAI,iBAAiB,aAAa;AAChC,kBAAY,YAAY;AAYxB,UAAI,UAAU,GAAG;AACf,cAAM,WAAW,iBAAiB,WAAW;AAC7C,cAAM,OAAO,GAAG,SAAS,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS,UAAU;AAC/E,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,cAAM,MAAM,OAAO,WAAW,IAAI;AAClC,YAAI,cAAc;AAClB,YAAI,KAAK;AACP,cAAI,OAAO;AACX,gBAAM,gBAAgB,IAAI,YAAY,OAAO,EAAE;AAC/C,wBAAc,gBAAgB,cAAc;AAAA,QAC9C;AACA,sBAAc,YAAY,WAAW;AACrC;AAAA,MACF;AAEA,YAAM,gBAAgB,UAAU,aAAa;AAC7C,oBAAc,YAAY,aAAa;AACvC,UAAI,CAAC,YAAY,eAAe;AAC9B,8BAAsB,aAAa,eAAe,OAAO;AAAA,MAC3D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAE5C,QAAM,aAAa,QAAQ,MAAM,oBAAC,oBAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAE1F,QAAM,YAAY;AAAA,IAChB,OAAO;AAAA,MACL,eAAe;AAAA,IACjB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types/styled.d.ts
CHANGED
|
@@ -3,7 +3,10 @@ export declare const StyledTextWrapper: import("styled-components").StyledCompon
|
|
|
3
3
|
lines: number;
|
|
4
4
|
expanded: boolean;
|
|
5
5
|
} & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
6
|
-
export declare const StyledTextContent: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme,
|
|
6
|
+
export declare const StyledTextContent: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, {
|
|
7
|
+
lines: number;
|
|
8
|
+
expanded: boolean;
|
|
9
|
+
} & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
7
10
|
export declare const StyledTooltipWrapper: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"div">, never>;
|
|
8
11
|
export declare const StyledButtonWrapper: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
9
12
|
export declare const StyledButton: import("styled-components").StyledComponent<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV2T.Props>, import("@elliemae/ds-system").Theme, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-read-more",
|
|
3
|
-
"version": "3.52.0
|
|
3
|
+
"version": "3.52.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - Read More",
|
|
6
6
|
"files": [
|
|
@@ -36,19 +36,19 @@
|
|
|
36
36
|
"indent": 4
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@elliemae/ds-button-v2": "3.52.0
|
|
40
|
-
"@elliemae/ds-
|
|
41
|
-
"@elliemae/ds-system": "3.52.0
|
|
42
|
-
"@elliemae/ds-
|
|
39
|
+
"@elliemae/ds-button-v2": "3.52.0",
|
|
40
|
+
"@elliemae/ds-tooltip-v3": "3.52.0",
|
|
41
|
+
"@elliemae/ds-system": "3.52.0",
|
|
42
|
+
"@elliemae/ds-props-helpers": "3.52.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@elliemae/pui-cli": "9.0.0-next.
|
|
45
|
+
"@elliemae/pui-cli": "9.0.0-next.63",
|
|
46
46
|
"jest": "~29.7.0",
|
|
47
47
|
"styled-components": "~5.3.9",
|
|
48
|
-
"@elliemae/ds-monorepo-devops": "3.52.0
|
|
48
|
+
"@elliemae/ds-monorepo-devops": "3.52.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"lodash": "^4.17.21",
|
|
51
|
+
"lodash-es": "^4.17.21",
|
|
52
52
|
"react": "^18.3.1",
|
|
53
53
|
"react-dom": "^18.3.1",
|
|
54
54
|
"styled-components": "~5.3.9"
|