@elliemae/ds-read-more 3.57.0-next.41 → 3.57.0-next.42
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 +0 -2
- package/dist/cjs/DSReadMore.js.map +2 -2
- package/dist/cjs/MoreLessButton.js +25 -17
- package/dist/cjs/MoreLessButton.js.map +2 -2
- package/dist/cjs/styled.js +9 -28
- package/dist/cjs/styled.js.map +2 -2
- package/dist/cjs/useReadMoreTruncate.js +3 -15
- package/dist/cjs/useReadMoreTruncate.js.map +2 -2
- package/dist/esm/DSReadMore.js +0 -2
- package/dist/esm/DSReadMore.js.map +2 -2
- package/dist/esm/MoreLessButton.js +25 -17
- package/dist/esm/MoreLessButton.js.map +2 -2
- package/dist/esm/styled.js +9 -28
- package/dist/esm/styled.js.map +2 -2
- package/dist/esm/useReadMoreTruncate.js +3 -15
- package/dist/esm/useReadMoreTruncate.js.map +2 -2
- package/dist/types/styled.d.ts +3 -4
- package/package.json +9 -9
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 { ScreenReaderOnly } from '@elliemae/ds-accessibility';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { useCallback } from 'react';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './constants/index.js';\nimport { DSReadMorePropTypesSchema, defaultProps, type DSReadMoreT } from './react-desc-prop-types.js';\nimport { StyledTextContent, StyledTextWrapper, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.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) {\n onMore();\n } else {\n onLess();\n }\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({ expanded }), [expanded]);\n const getReadMoreDescribedByText = useCallback(() => {\n if (withTooltip) {\n return 'shows the full text for sighted users but the full text is already there for screen readers';\n }\n return `${expanded ? 'collapse' : 'expand'} the full text for sighted users but the full text is already there for screen readers`;\n }, [expanded, withTooltip]);\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 {/**\n * Hidden from screen readers unless referenced by `aria-describedby`\n * Ensures the text is only read when associated with a control\n * Prevents it from being focusable or announced independently\n */}\n <ScreenReaderOnly id=\"ds-read_more-text-disclaimer\" aria-hidden>\n {getReadMoreDescribedByText()}\n </ScreenReaderOnly>\n\n <StyledTextContent\n
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADyCnB;AAzCJ,8BAAiC;AACjC,8BAA2E;AAC3E,2BAA4B;AAC5B,mBAA4B;AAC5B,4BAA+B;AAC/B,uBAAsD;AACtD,mCAA0E;AAC1E,oBAA2E;AAC3E,iCAAoC;AAEpC,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,aAAa;AACf,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,oBAAgB,0BAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,6BAAyB,0BAAY,OAAO,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC;AAC3E,QAAM,iCAA6B,0BAAY,MAAM;AACnD,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AACA,WAAO,GAAG,WAAW,aAAa,QAAQ;AAAA,EAC5C,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QAMD,4CAAC,4CAAiB,IAAG,gCAA+B,eAAW,MAC5D,qCAA2B,GAC9B;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC
|
|
4
|
+
"sourcesContent": ["import { ScreenReaderOnly } from '@elliemae/ds-accessibility';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { useCallback } from 'react';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './constants/index.js';\nimport { DSReadMorePropTypesSchema, defaultProps, type DSReadMoreT } from './react-desc-prop-types.js';\nimport { StyledTextContent, StyledTextWrapper, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.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) {\n onMore();\n } else {\n onLess();\n }\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({ expanded }), [expanded]);\n const getReadMoreDescribedByText = useCallback(() => {\n if (withTooltip) {\n return 'shows the full text for sighted users but the full text is already there for screen readers';\n }\n return `${expanded ? 'collapse' : 'expand'} the full text for sighted users but the full text is already there for screen readers`;\n }, [expanded, withTooltip]);\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 {/**\n * Hidden from screen readers unless referenced by `aria-describedby`\n * Ensures the text is only read when associated with a control\n * Prevents it from being focusable or announced independently\n */}\n <ScreenReaderOnly id=\"ds-read_more-text-disclaimer\" aria-hidden>\n {getReadMoreDescribedByText()}\n </ScreenReaderOnly>\n\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\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\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;\nconst DSReadMoreWithSchema = describe(DSReadMore);\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;ADyCnB;AAzCJ,8BAAiC;AACjC,8BAA2E;AAC3E,2BAA4B;AAC5B,mBAA4B;AAC5B,4BAA+B;AAC/B,uBAAsD;AACtD,mCAA0E;AAC1E,oBAA2E;AAC3E,iCAAoC;AAEpC,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,aAAa;AACf,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,oBAAgB,0BAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,6BAAyB,0BAAY,OAAO,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC;AAC3E,QAAM,iCAA6B,0BAAY,MAAM;AACnD,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AACA,WAAO,GAAG,WAAW,aAAa,QAAQ;AAAA,EAC5C,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QAMD,4CAAC,4CAAiB,IAAG,gCAA+B,eAAW,MAC5D,qCAA2B,GAC9B;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC;AAAA,YACA,YAAY,uCAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAGD,eAAe,cACd,4CAAC,sCAAqB,eAA8B,wBAClD,sDAAC,oCAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,YAAY,uCAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AACzB,MAAM,2BAAuB,kCAAS,UAAU;AAChD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -36,22 +36,30 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
36
36
|
var import_styled = require("./styled.js");
|
|
37
37
|
const MoreLessButton = (props) => {
|
|
38
38
|
const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;
|
|
39
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
39
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
40
|
+
import_styled.StyledButtonWrapper,
|
|
41
|
+
{
|
|
42
|
+
expanded,
|
|
43
|
+
getOwnerProps,
|
|
44
|
+
getOwnerPropsArguments,
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: expanded || withTooltip ? " " : ellipsis }),
|
|
47
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
48
|
+
import_styled.StyledButton,
|
|
49
|
+
{
|
|
50
|
+
withTooltip,
|
|
51
|
+
buttonType: "text",
|
|
52
|
+
onClick,
|
|
53
|
+
size: "s",
|
|
54
|
+
"data-testid": dataTestId,
|
|
55
|
+
getOwnerProps,
|
|
56
|
+
getOwnerPropsArguments,
|
|
57
|
+
"aria-describedby": "ds-read_more-text-disclaimer",
|
|
58
|
+
children: label
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
);
|
|
56
64
|
};
|
|
57
65
|
//# sourceMappingURL=MoreLessButton.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/MoreLessButton.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["import React from 'react';\nimport { StyledButton, StyledButtonWrapper } from './styled.js';\n\ninterface MoreLessButtonProps {\n expanded: boolean;\n label: string;\n onClick?: () => void;\n ellipsis: string;\n withTooltip?: boolean;\n dataTestId: string;\n getOwnerProps: () => object;\n getOwnerPropsArguments: () => object;\n}\n\nconst MoreLessButton = (props: MoreLessButtonProps) => {\n const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;\n\n return (\n <StyledButtonWrapper
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADkBnB;AAjBJ,oBAAkD;AAalD,MAAM,iBAAiB,CAAC,UAA+B;AACrD,QAAM,EAAE,UAAU,OAAO,SAAS,UAAU,aAAa,YAAY,eAAe,uBAAuB,IAAI;AAE/G,SACE,
|
|
4
|
+
"sourcesContent": ["import React from 'react';\nimport { StyledButton, StyledButtonWrapper } from './styled.js';\n\ninterface MoreLessButtonProps {\n expanded: boolean;\n label: string;\n onClick?: () => void;\n ellipsis: string;\n withTooltip?: boolean;\n dataTestId: string;\n getOwnerProps: () => object;\n getOwnerPropsArguments: () => object;\n}\n\nconst MoreLessButton = (props: MoreLessButtonProps) => {\n const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;\n\n return (\n <StyledButtonWrapper\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n <span aria-hidden>{expanded || withTooltip ? ' ' : ellipsis}</span>\n\n <StyledButton\n withTooltip={withTooltip}\n buttonType=\"text\"\n onClick={onClick}\n size=\"s\"\n data-testid={dataTestId}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n aria-describedby=\"ds-read_more-text-disclaimer\"\n >\n {label}\n </StyledButton>\n </StyledButtonWrapper>\n );\n};\n\nexport { MoreLessButton };\n", "import * as React from 'react';\nexport { React };\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADkBnB;AAjBJ,oBAAkD;AAalD,MAAM,iBAAiB,CAAC,UAA+B;AACrD,QAAM,EAAE,UAAU,OAAO,SAAS,UAAU,aAAa,YAAY,eAAe,uBAAuB,IAAI;AAE/G,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAEA;AAAA,oDAAC,UAAK,eAAW,MAAE,sBAAY,cAAc,MAAM,UAAS;AAAA,QAE5D;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,YAAW;AAAA,YACX;AAAA,YACA,MAAK;AAAA,YACL,eAAa;AAAA,YACb;AAAA,YACA;AAAA,YACA,oBAAiB;AAAA,YAEhB;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cjs/styled.js
CHANGED
|
@@ -42,26 +42,10 @@ var import_ds_button_v2 = require("@elliemae/ds-button-v2");
|
|
|
42
42
|
var import_constants = require("./constants/index.js");
|
|
43
43
|
const StyledTextWrapper = (0, import_ds_system.styled)("span", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.ROOT })`
|
|
44
44
|
position: relative;
|
|
45
|
-
|
|
46
|
-
|
|
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;
|
|
45
|
+
${({ lines, expanded }) => expanded ? "" : `display: -webkit-box;
|
|
46
|
+
-webkit-line-clamp: ${lines};
|
|
47
|
+
-webkit-box-orient: vertical;
|
|
63
48
|
`}
|
|
64
|
-
|
|
65
49
|
-webkit-hyphens: auto;
|
|
66
50
|
-moz-hyphens: auto;
|
|
67
51
|
-ms-hyphens: auto;
|
|
@@ -70,18 +54,15 @@ const StyledTextWrapper = (0, import_ds_system.styled)("span", { name: import_co
|
|
|
70
54
|
white-space: pre-wrap;
|
|
71
55
|
${import_ds_system.xStyledCommonProps}
|
|
72
56
|
`;
|
|
73
|
-
const StyledTextContent = (0, import_ds_system.styled)("span", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.TEXT_CONTENT })
|
|
74
|
-
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
75
|
-
flex: 1 1 auto;
|
|
76
|
-
overflow: hidden;
|
|
77
|
-
white-space: nowrap;
|
|
78
|
-
text-overflow: clip;
|
|
79
|
-
` : ""}
|
|
80
|
-
`;
|
|
57
|
+
const StyledTextContent = (0, import_ds_system.styled)("span", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.TEXT_CONTENT })``;
|
|
81
58
|
const StyledTooltipWrapper = (0, import_ds_system.styled)("div", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.TOOLTIP_WRAPPER })`
|
|
82
59
|
display: inline-block;
|
|
83
60
|
`;
|
|
84
|
-
const StyledButtonWrapper = (0, import_ds_system.styled)("span", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.BUTTON_WRAPPER })
|
|
61
|
+
const StyledButtonWrapper = (0, import_ds_system.styled)("span", { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.BUTTON_WRAPPER })`
|
|
62
|
+
display: -webkit-inline-box;
|
|
63
|
+
-webkit-line-clamp: 2;
|
|
64
|
+
-webkit-box-orient: vertical;
|
|
65
|
+
`;
|
|
85
66
|
const StyledButton = (0, import_ds_system.styled)(import_ds_button_v2.DSButtonV3, { name: import_constants.DSReadMoreName, slot: import_constants.DSReadMoreSlots.BUTTON })`
|
|
86
67
|
padding: ${({ theme }) => `0 ${theme.space.xxxs}`};
|
|
87
68
|
color: ${({ withTooltip, theme }) => withTooltip ? "inherit" : `${theme.colors.brand["600"]}`};
|
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 { DSButtonV3 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './constants/index.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,uBAAgD;AAEzC,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,KAAK,CAAC;AAAA;AAAA
|
|
4
|
+
"sourcesContent": ["import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV3 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './constants/index.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n ${({ lines, expanded }) =>\n expanded\n ? ''\n : `display: -webkit-box;\n -webkit-line-clamp: ${lines};\n -webkit-box-orient: vertical;\n `}\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n -ms-hyphens: auto;\n hyphens: auto;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n ${xStyledCommonProps}\n`;\n\nexport const StyledTextContent = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })``;\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 expanded: boolean;\n}>`\n display: -webkit-inline-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n`;\n\nexport const StyledButton = styled(DSButtonV3, { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON })<{\n withTooltip?: boolean;\n}>`\n padding: ${({ theme }) => `0 ${theme.space.xxxs}`};\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,uBAAgD;AAEzC,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,KAAK,CAAC;AAAA;AAAA,IAKhG,CAAC,EAAE,OAAO,SAAS,MACnB,WACI,KACA;AAAA,wBACgB,KAAK;AAAA;AAAA,GAE1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOC,mCAAkB;AAAA;AAGf,MAAM,wBAAoB,yBAAO,QAAQ,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,aAAa,CAAC;AAErG,MAAM,2BAAuB,yBAAO,OAAO,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,gBAAgB,CAAC;AAAA;AAAA;AAI1G,MAAM,0BAAsB,yBAAO,QAAQ,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,eAAe,CAAC;AAAA;AAAA;AAAA;AAAA;AAQzG,MAAM,mBAAe,yBAAO,gCAAY,EAAE,MAAM,iCAAgB,MAAM,iCAAgB,OAAO,CAAC;AAAA,aAGxF,CAAC,EAAE,MAAM,MAAM,KAAK,MAAM,MAAM,IAAI,EAAE;AAAA,WACxC,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
|
}
|
|
@@ -65,20 +65,6 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
65
65
|
const wrapperEl = textWrapperRef.current;
|
|
66
66
|
if (!textEl || !wrapperEl) return;
|
|
67
67
|
textEl.innerText = content;
|
|
68
|
-
if (lines === 1) {
|
|
69
|
-
const computed = getComputedStyle(textEl);
|
|
70
|
-
const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;
|
|
71
|
-
const canvas = document.createElement("canvas");
|
|
72
|
-
const ctx = canvas.getContext("2d");
|
|
73
|
-
let truncated = false;
|
|
74
|
-
if (ctx) {
|
|
75
|
-
ctx.font = font;
|
|
76
|
-
const measuredWidth = ctx.measureText(content).width;
|
|
77
|
-
truncated = measuredWidth > wrapperEl.clientWidth;
|
|
78
|
-
}
|
|
79
|
-
setShowButton(expanded || truncated);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
68
|
const isOverflowing = overflows({
|
|
83
69
|
offsetHeight: wrapperEl.offsetHeight,
|
|
84
70
|
scrollHeight: wrapperEl.scrollHeight
|
|
@@ -87,7 +73,9 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
87
73
|
if (!expanded && isOverflowing) {
|
|
88
74
|
const doBinaryTruncate = () => {
|
|
89
75
|
if (!textEl || !wrapperEl) return;
|
|
90
|
-
|
|
76
|
+
requestAnimationFrame(() => {
|
|
77
|
+
binSearchTextOverflow(textEl, wrapperEl, content);
|
|
78
|
+
});
|
|
91
79
|
};
|
|
92
80
|
if ("fonts" in document && document.fonts.ready) {
|
|
93
81
|
document.fonts.ready.then(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/useReadMoreTruncate.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable max-statements */\n/* eslint-disable complexity */\n// useReadMoreTruncate.tsx\nimport 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 + margin/buffer to avoid subpixel errors\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 4 < scrollHeight;\n\n/**\n * binSearchTextOverflow slices the content until there's no overflow.\n * It assumes `element` is the <span> node containing the text, and `parent`\n * is the wrapper with the height/clamp applied.\n */\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n let left = 0;\n let right = content.length;\n let textEnd = 0;\n\n // Place the full text before starting\n element.innerText = content;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n element.innerText = content.slice(0, mid);\n\n if (overflows(parent)) {\n // Still overflowing \u2192 reduce\n right = mid - 1;\n } else {\n // No overflow \u2192 we can increase\n textEnd = mid;\n left = mid + 1;\n }\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 const [showButton, setShowButton] = useState(false);\n\n // Detect container size changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textEl = textRef.current;\n const wrapperEl = textWrapperRef.current;\n if (!textEl || !wrapperEl) return;\n\n // 1) Always reset the text to \"content\"\n textEl.innerText = content;\n\n //
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable max-statements */\n/* eslint-disable complexity */\n// useReadMoreTruncate.tsx\nimport 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 + margin/buffer to avoid subpixel errors\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 4 < scrollHeight;\n\n/**\n * binSearchTextOverflow slices the content until there's no overflow.\n * It assumes `element` is the <span> node containing the text, and `parent`\n * is the wrapper with the height/clamp applied.\n */\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n let left = 0;\n let right = content.length;\n let textEnd = 0;\n\n // Place the full text before starting\n element.innerText = content;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n element.innerText = content.slice(0, mid);\n\n if (overflows(parent)) {\n // Still overflowing \u2192 reduce\n right = mid - 1;\n } else {\n // No overflow \u2192 we can increase\n textEnd = mid;\n left = mid + 1;\n }\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 const [showButton, setShowButton] = useState(false);\n\n // Detect container size changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textEl = textRef.current;\n const wrapperEl = textWrapperRef.current;\n if (!textEl || !wrapperEl) return;\n\n // 1) Always reset the text to \"content\"\n textEl.innerText = content;\n\n // 3) For multiline: first check native overflow\n const isOverflowing = overflows({\n offsetHeight: wrapperEl.offsetHeight,\n scrollHeight: wrapperEl.scrollHeight,\n });\n setShowButton(expanded || isOverflowing);\n\n // 4) If not expanded and there is overflow, adjust the slice with binary search\n if (!expanded && isOverflowing) {\n // Function that executes binSearchTextOverflow\n const doBinaryTruncate = () => {\n if (!textEl || !wrapperEl) return;\n requestAnimationFrame(() => {\n binSearchTextOverflow(textEl, wrapperEl, content);\n });\n };\n\n // If the browser supports document.fonts, wait for them to load.\n if ('fonts' in document && document.fonts.ready) {\n document.fonts.ready\n .then(() => {\n doBinaryTruncate();\n })\n .catch(() => {\n // If fonts.ready fails, fall back to timeout\n setTimeout(doBinaryTruncate, 0);\n });\n } else {\n // Fallback for browsers without the Font Loading API\n setTimeout(doBinaryTruncate, 0);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n // Hidden text for screen readers (always the full string)\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n // Mark the visible content as aria-hidden\n const textProps = useMemo(() => ({ 'aria-hidden': true }), []);\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;ADgGY;AA5FnC,mBAAkE;AAClE,gCAAmC;AAEnC,oBAAiC;AAGjC,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAOrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,MAAI,OAAO;AACX,MAAI,QAAQ,QAAQ;AACpB,MAAI,UAAU;AAGd,UAAQ,YAAY;AAEpB,SAAO,QAAQ,OAAO;AACpB,UAAM,MAAM,KAAK,OAAO,OAAO,SAAS,CAAC;AACzC,YAAQ,YAAY,QAAQ,MAAM,GAAG,GAAG;AAExC,QAAI,UAAU,MAAM,GAAG;AAErB,cAAQ,MAAM;AAAA,IAChB,OAAO;AAEL,gBAAU;AACV,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,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;AAC9C,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,QAAI,8CAAmB,cAAc;AAE3D,oCAAgB,MAAM;AACpB,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,eAAe;AACjC,QAAI,CAAC,UAAU,CAAC,UAAW;AAG3B,WAAO,YAAY;AAGnB,UAAM,gBAAgB,UAAU;AAAA,MAC9B,cAAc,UAAU;AAAA,MACxB,cAAc,UAAU;AAAA,IAC1B,CAAC;AACD,kBAAc,YAAY,aAAa;AAGvC,QAAI,CAAC,YAAY,eAAe;AAE9B,YAAM,mBAAmB,MAAM;AAC7B,YAAI,CAAC,UAAU,CAAC,UAAW;AAC3B,8BAAsB,MAAM;AAC1B,gCAAsB,QAAQ,WAAW,OAAO;AAAA,QAClD,CAAC;AAAA,MACH;AAGA,UAAI,WAAW,YAAY,SAAS,MAAM,OAAO;AAC/C,iBAAS,MAAM,MACZ,KAAK,MAAM;AACV,2BAAiB;AAAA,QACnB,CAAC,EACA,MAAM,MAAM;AAEX,qBAAW,kBAAkB,CAAC;AAAA,QAChC,CAAC;AAAA,MACL,OAAO;AAEL,mBAAW,kBAAkB,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAG5C,QAAM,iBAAa,sBAAQ,MAAM,4CAAC,kCAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAG1F,QAAM,gBAAY,sBAAQ,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC,CAAC;AAE7D,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 { ScreenReaderOnly } from '@elliemae/ds-accessibility';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { useCallback } from 'react';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './constants/index.js';\nimport { DSReadMorePropTypesSchema, defaultProps, type DSReadMoreT } from './react-desc-prop-types.js';\nimport { StyledTextContent, StyledTextWrapper, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.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) {\n onMore();\n } else {\n onLess();\n }\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({ expanded }), [expanded]);\n const getReadMoreDescribedByText = useCallback(() => {\n if (withTooltip) {\n return 'shows the full text for sighted users but the full text is already there for screen readers';\n }\n return `${expanded ? 'collapse' : 'expand'} the full text for sighted users but the full text is already there for screen readers`;\n }, [expanded, withTooltip]);\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 {/**\n * Hidden from screen readers unless referenced by `aria-describedby`\n * Ensures the text is only read when associated with a control\n * Prevents it from being focusable or announced independently\n */}\n <ScreenReaderOnly id=\"ds-read_more-text-disclaimer\" aria-hidden>\n {getReadMoreDescribedByText()}\n </ScreenReaderOnly>\n\n <StyledTextContent\n
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACyCnB,SAcE,KAdF;AAzCJ,SAAS,wBAAwB;AACjC,SAAS,UAAU,oBAAoB,oCAAoC;AAC3E,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB,sBAAsB;AACtD,SAAS,2BAA2B,oBAAsC;AAC1E,SAAS,mBAAmB,mBAAmB,4BAA4B;AAC3E,SAAS,2BAA2B;AAEpC,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,aAAa;AACf,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,gBAAgB,YAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,yBAAyB,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC;AAC3E,QAAM,6BAA6B,YAAY,MAAM;AACnD,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AACA,WAAO,GAAG,WAAW,aAAa,QAAQ;AAAA,EAC5C,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QAMD,oBAAC,oBAAiB,IAAG,gCAA+B,eAAW,MAC5D,qCAA2B,GAC9B;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { ScreenReaderOnly } from '@elliemae/ds-accessibility';\nimport { describe, useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { DSTooltipV3 } from '@elliemae/ds-tooltip-v3';\nimport { useCallback } from 'react';\nimport { MoreLessButton } from './MoreLessButton.js';\nimport { DSReadMoreDataTestIds, DSReadMoreName } from './constants/index.js';\nimport { DSReadMorePropTypesSchema, defaultProps, type DSReadMoreT } from './react-desc-prop-types.js';\nimport { StyledTextContent, StyledTextWrapper, StyledTooltipWrapper } from './styled.js';\nimport { useReadMoreTruncate } from './useReadMoreTruncate.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) {\n onMore();\n } else {\n onLess();\n }\n },\n [onMore, onLess, setExpanded],\n );\n\n const getOwnerProps = useCallback(() => propsWithDefault, [propsWithDefault]);\n const getOwnerPropsArguments = useCallback(() => ({ expanded }), [expanded]);\n const getReadMoreDescribedByText = useCallback(() => {\n if (withTooltip) {\n return 'shows the full text for sighted users but the full text is already there for screen readers';\n }\n return `${expanded ? 'collapse' : 'expand'} the full text for sighted users but the full text is already there for screen readers`;\n }, [expanded, withTooltip]);\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 {/**\n * Hidden from screen readers unless referenced by `aria-describedby`\n * Ensures the text is only read when associated with a control\n * Prevents it from being focusable or announced independently\n */}\n <ScreenReaderOnly id=\"ds-read_more-text-disclaimer\" aria-hidden>\n {getReadMoreDescribedByText()}\n </ScreenReaderOnly>\n\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\n {showButton && !withTooltip && (\n <MoreLessButton\n expanded={expanded}\n label={expanded ? less : more}\n onClick={() => toggleExpanded(!expanded)}\n ellipsis={ellipsis}\n dataTestId={DSReadMoreDataTestIds.READ_MORE_BUTTON}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n />\n )}\n\n {withTooltip && showButton && (\n <StyledTooltipWrapper getOwnerProps={getOwnerProps} getOwnerPropsArguments={getOwnerPropsArguments}>\n <DSTooltipV3 text={content} wrapWords>\n <MoreLessButton\n expanded={expanded}\n label=\"...\"\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;\nconst DSReadMoreWithSchema = describe(DSReadMore);\nDSReadMoreWithSchema.propTypes = DSReadMorePropTypesSchema;\n\nexport { DSReadMore, DSReadMoreWithSchema };\nexport default DSReadMore;\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACyCnB,SAcE,KAdF;AAzCJ,SAAS,wBAAwB;AACjC,SAAS,UAAU,oBAAoB,oCAAoC;AAC3E,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB,sBAAsB;AACtD,SAAS,2BAA2B,oBAAsC;AAC1E,SAAS,mBAAmB,mBAAmB,4BAA4B;AAC3E,SAAS,2BAA2B;AAEpC,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,aAAa;AACf,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,QAAQ,WAAW;AAAA,EAC9B;AAEA,QAAM,gBAAgB,YAAY,MAAM,kBAAkB,CAAC,gBAAgB,CAAC;AAC5E,QAAM,yBAAyB,YAAY,OAAO,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC;AAC3E,QAAM,6BAA6B,YAAY,MAAM;AACnD,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AACA,WAAO,GAAG,WAAW,aAAa,QAAQ;AAAA,EAC5C,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QAMD,oBAAC,oBAAiB,IAAG,gCAA+B,eAAW,MAC5D,qCAA2B,GAC9B;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU;AAAA,YACV,eAAY;AAAA,YACX,GAAG;AAAA,YACJ;AAAA,YACA;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEC,cAAc,CAAC,eACd;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,WAAW,OAAO;AAAA,YACzB,SAAS,MAAM,eAAe,CAAC,QAAQ;AAAA,YACvC;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF;AAAA,QAGD,eAAe,cACd,oBAAC,wBAAqB,eAA8B,wBAClD,8BAAC,eAAY,MAAM,SAAS,WAAS,MACnC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,YAAY,sBAAsB;AAAA,YAClC;AAAA,YACA;AAAA;AAAA,QACF,GACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,WAAW,cAAc;AACzB,MAAM,uBAAuB,SAAS,UAAU;AAChD,qBAAqB,YAAY;AAGjC,IAAO,qBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,23 +3,31 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { StyledButton, StyledButtonWrapper } from "./styled.js";
|
|
4
4
|
const MoreLessButton = (props) => {
|
|
5
5
|
const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;
|
|
6
|
-
return /* @__PURE__ */ jsxs(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
StyledButtonWrapper,
|
|
8
|
+
{
|
|
9
|
+
expanded,
|
|
10
|
+
getOwnerProps,
|
|
11
|
+
getOwnerPropsArguments,
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, children: expanded || withTooltip ? " " : ellipsis }),
|
|
14
|
+
/* @__PURE__ */ jsx(
|
|
15
|
+
StyledButton,
|
|
16
|
+
{
|
|
17
|
+
withTooltip,
|
|
18
|
+
buttonType: "text",
|
|
19
|
+
onClick,
|
|
20
|
+
size: "s",
|
|
21
|
+
"data-testid": dataTestId,
|
|
22
|
+
getOwnerProps,
|
|
23
|
+
getOwnerPropsArguments,
|
|
24
|
+
"aria-describedby": "ds-read_more-text-disclaimer",
|
|
25
|
+
children: label
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
);
|
|
23
31
|
};
|
|
24
32
|
export {
|
|
25
33
|
MoreLessButton
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/MoreLessButton.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { StyledButton, StyledButtonWrapper } from './styled.js';\n\ninterface MoreLessButtonProps {\n expanded: boolean;\n label: string;\n onClick?: () => void;\n ellipsis: string;\n withTooltip?: boolean;\n dataTestId: string;\n getOwnerProps: () => object;\n getOwnerPropsArguments: () => object;\n}\n\nconst MoreLessButton = (props: MoreLessButtonProps) => {\n const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;\n\n return (\n <StyledButtonWrapper
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;ACkBnB,
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { StyledButton, StyledButtonWrapper } from './styled.js';\n\ninterface MoreLessButtonProps {\n expanded: boolean;\n label: string;\n onClick?: () => void;\n ellipsis: string;\n withTooltip?: boolean;\n dataTestId: string;\n getOwnerProps: () => object;\n getOwnerPropsArguments: () => object;\n}\n\nconst MoreLessButton = (props: MoreLessButtonProps) => {\n const { expanded, label, onClick, ellipsis, withTooltip, dataTestId, getOwnerProps, getOwnerPropsArguments } = props;\n\n return (\n <StyledButtonWrapper\n expanded={expanded}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n >\n <span aria-hidden>{expanded || withTooltip ? ' ' : ellipsis}</span>\n\n <StyledButton\n withTooltip={withTooltip}\n buttonType=\"text\"\n onClick={onClick}\n size=\"s\"\n data-testid={dataTestId}\n getOwnerProps={getOwnerProps}\n getOwnerPropsArguments={getOwnerPropsArguments}\n aria-describedby=\"ds-read_more-text-disclaimer\"\n >\n {label}\n </StyledButton>\n </StyledButtonWrapper>\n );\n};\n\nexport { MoreLessButton };\n"],
|
|
5
|
+
"mappings": "AAAA,YAAY,WAAW;ACkBnB,SAKE,KALF;AAjBJ,SAAS,cAAc,2BAA2B;AAalD,MAAM,iBAAiB,CAAC,UAA+B;AACrD,QAAM,EAAE,UAAU,OAAO,SAAS,UAAU,aAAa,YAAY,eAAe,uBAAuB,IAAI;AAE/G,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAEA;AAAA,4BAAC,UAAK,eAAW,MAAE,sBAAY,cAAc,MAAM,UAAS;AAAA,QAE5D;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,YAAW;AAAA,YACX;AAAA,YACA,MAAK;AAAA,YACL,eAAa;AAAA,YACb;AAAA,YACA;AAAA,YACA,oBAAiB;AAAA,YAEhB;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/esm/styled.js
CHANGED
|
@@ -4,26 +4,10 @@ import { DSButtonV3 } from "@elliemae/ds-button-v2";
|
|
|
4
4
|
import { DSReadMoreName, DSReadMoreSlots } from "./constants/index.js";
|
|
5
5
|
const StyledTextWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })`
|
|
6
6
|
position: relative;
|
|
7
|
-
|
|
8
|
-
|
|
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;
|
|
7
|
+
${({ lines, expanded }) => expanded ? "" : `display: -webkit-box;
|
|
8
|
+
-webkit-line-clamp: ${lines};
|
|
9
|
+
-webkit-box-orient: vertical;
|
|
25
10
|
`}
|
|
26
|
-
|
|
27
11
|
-webkit-hyphens: auto;
|
|
28
12
|
-moz-hyphens: auto;
|
|
29
13
|
-ms-hyphens: auto;
|
|
@@ -32,18 +16,15 @@ const StyledTextWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMor
|
|
|
32
16
|
white-space: pre-wrap;
|
|
33
17
|
${xStyledCommonProps}
|
|
34
18
|
`;
|
|
35
|
-
const StyledTextContent = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })
|
|
36
|
-
${({ lines, expanded }) => lines === 1 && !expanded ? `
|
|
37
|
-
flex: 1 1 auto;
|
|
38
|
-
overflow: hidden;
|
|
39
|
-
white-space: nowrap;
|
|
40
|
-
text-overflow: clip;
|
|
41
|
-
` : ""}
|
|
42
|
-
`;
|
|
19
|
+
const StyledTextContent = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })``;
|
|
43
20
|
const StyledTooltipWrapper = styled("div", { name: DSReadMoreName, slot: DSReadMoreSlots.TOOLTIP_WRAPPER })`
|
|
44
21
|
display: inline-block;
|
|
45
22
|
`;
|
|
46
|
-
const StyledButtonWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON_WRAPPER })
|
|
23
|
+
const StyledButtonWrapper = styled("span", { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON_WRAPPER })`
|
|
24
|
+
display: -webkit-inline-box;
|
|
25
|
+
-webkit-line-clamp: 2;
|
|
26
|
+
-webkit-box-orient: vertical;
|
|
27
|
+
`;
|
|
47
28
|
const StyledButton = styled(DSButtonV3, { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON })`
|
|
48
29
|
padding: ${({ theme }) => `0 ${theme.space.xxxs}`};
|
|
49
30
|
color: ${({ withTooltip, theme }) => withTooltip ? "inherit" : `${theme.colors.brand["600"]}`};
|
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 { DSButtonV3 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './constants/index.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
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport { DSButtonV3 } from '@elliemae/ds-button-v2';\nimport { DSReadMoreName, DSReadMoreSlots } from './constants/index.js';\n\nexport const StyledTextWrapper = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.ROOT })<{\n lines: number;\n expanded: boolean;\n}>`\n position: relative;\n ${({ lines, expanded }) =>\n expanded\n ? ''\n : `display: -webkit-box;\n -webkit-line-clamp: ${lines};\n -webkit-box-orient: vertical;\n `}\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n -ms-hyphens: auto;\n hyphens: auto;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n ${xStyledCommonProps}\n`;\n\nexport const StyledTextContent = styled('span', { name: DSReadMoreName, slot: DSReadMoreSlots.TEXT_CONTENT })``;\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 expanded: boolean;\n}>`\n display: -webkit-inline-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n`;\n\nexport const StyledButton = styled(DSButtonV3, { name: DSReadMoreName, slot: DSReadMoreSlots.BUTTON })<{\n withTooltip?: boolean;\n}>`\n padding: ${({ theme }) => `0 ${theme.space.xxxs}`};\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,IAKhG,CAAC,EAAE,OAAO,SAAS,MACnB,WACI,KACA;AAAA,wBACgB,KAAK;AAAA;AAAA,GAE1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOC,kBAAkB;AAAA;AAGf,MAAM,oBAAoB,OAAO,QAAQ,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,aAAa,CAAC;AAErG,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;AAAA;AAAA;AAAA;AAAA;AAQzG,MAAM,eAAe,OAAO,YAAY,EAAE,MAAM,gBAAgB,MAAM,gBAAgB,OAAO,CAAC;AAAA,aAGxF,CAAC,EAAE,MAAM,MAAM,KAAK,MAAM,MAAM,IAAI,EAAE;AAAA,WACxC,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
|
}
|
|
@@ -32,20 +32,6 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
32
32
|
const wrapperEl = textWrapperRef.current;
|
|
33
33
|
if (!textEl || !wrapperEl) return;
|
|
34
34
|
textEl.innerText = content;
|
|
35
|
-
if (lines === 1) {
|
|
36
|
-
const computed = getComputedStyle(textEl);
|
|
37
|
-
const font = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;
|
|
38
|
-
const canvas = document.createElement("canvas");
|
|
39
|
-
const ctx = canvas.getContext("2d");
|
|
40
|
-
let truncated = false;
|
|
41
|
-
if (ctx) {
|
|
42
|
-
ctx.font = font;
|
|
43
|
-
const measuredWidth = ctx.measureText(content).width;
|
|
44
|
-
truncated = measuredWidth > wrapperEl.clientWidth;
|
|
45
|
-
}
|
|
46
|
-
setShowButton(expanded || truncated);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
35
|
const isOverflowing = overflows({
|
|
50
36
|
offsetHeight: wrapperEl.offsetHeight,
|
|
51
37
|
scrollHeight: wrapperEl.scrollHeight
|
|
@@ -54,7 +40,9 @@ const useReadMoreTruncate = ({ lines, content }) => {
|
|
|
54
40
|
if (!expanded && isOverflowing) {
|
|
55
41
|
const doBinaryTruncate = () => {
|
|
56
42
|
if (!textEl || !wrapperEl) return;
|
|
57
|
-
|
|
43
|
+
requestAnimationFrame(() => {
|
|
44
|
+
binSearchTextOverflow(textEl, wrapperEl, content);
|
|
45
|
+
});
|
|
58
46
|
};
|
|
59
47
|
if ("fonts" in document && document.fonts.ready) {
|
|
60
48
|
document.fonts.ready.then(() => {
|
|
@@ -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", "/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable max-statements */\n/* eslint-disable complexity */\n// useReadMoreTruncate.tsx\nimport 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 + margin/buffer to avoid subpixel errors\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 4 < scrollHeight;\n\n/**\n * binSearchTextOverflow slices the content until there's no overflow.\n * It assumes `element` is the <span> node containing the text, and `parent`\n * is the wrapper with the height/clamp applied.\n */\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n let left = 0;\n let right = content.length;\n let textEnd = 0;\n\n // Place the full text before starting\n element.innerText = content;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n element.innerText = content.slice(0, mid);\n\n if (overflows(parent)) {\n // Still overflowing \u2192 reduce\n right = mid - 1;\n } else {\n // No overflow \u2192 we can increase\n textEnd = mid;\n left = mid + 1;\n }\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 const [showButton, setShowButton] = useState(false);\n\n // Detect container size changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textEl = textRef.current;\n const wrapperEl = textWrapperRef.current;\n if (!textEl || !wrapperEl) return;\n\n // 1) Always reset the text to \"content\"\n textEl.innerText = content;\n\n //
|
|
5
|
-
"mappings": "AAAA,YAAY,WAAW;
|
|
4
|
+
"sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable max-statements */\n/* eslint-disable complexity */\n// useReadMoreTruncate.tsx\nimport 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 + margin/buffer to avoid subpixel errors\nconst overflows = ({ offsetHeight, scrollHeight }: { offsetHeight: number; scrollHeight: number }) =>\n offsetHeight + 4 < scrollHeight;\n\n/**\n * binSearchTextOverflow slices the content until there's no overflow.\n * It assumes `element` is the <span> node containing the text, and `parent`\n * is the wrapper with the height/clamp applied.\n */\nconst binSearchTextOverflow = (element: HTMLElement, parent: HTMLElement, content: string) => {\n let left = 0;\n let right = content.length;\n let textEnd = 0;\n\n // Place the full text before starting\n element.innerText = content;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n element.innerText = content.slice(0, mid);\n\n if (overflows(parent)) {\n // Still overflowing \u2192 reduce\n right = mid - 1;\n } else {\n // No overflow \u2192 we can increase\n textEnd = mid;\n left = mid + 1;\n }\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 const [showButton, setShowButton] = useState(false);\n\n // Detect container size changes\n const { width, height } = useOnElementResize(textWrapperRef);\n\n useLayoutEffect(() => {\n const textEl = textRef.current;\n const wrapperEl = textWrapperRef.current;\n if (!textEl || !wrapperEl) return;\n\n // 1) Always reset the text to \"content\"\n textEl.innerText = content;\n\n // 3) For multiline: first check native overflow\n const isOverflowing = overflows({\n offsetHeight: wrapperEl.offsetHeight,\n scrollHeight: wrapperEl.scrollHeight,\n });\n setShowButton(expanded || isOverflowing);\n\n // 4) If not expanded and there is overflow, adjust the slice with binary search\n if (!expanded && isOverflowing) {\n // Function that executes binSearchTextOverflow\n const doBinaryTruncate = () => {\n if (!textEl || !wrapperEl) return;\n requestAnimationFrame(() => {\n binSearchTextOverflow(textEl, wrapperEl, content);\n });\n };\n\n // If the browser supports document.fonts, wait for them to load.\n if ('fonts' in document && document.fonts.ready) {\n document.fonts.ready\n .then(() => {\n doBinaryTruncate();\n })\n .catch(() => {\n // If fonts.ready fails, fall back to timeout\n setTimeout(doBinaryTruncate, 0);\n });\n } else {\n // Fallback for browsers without the Font Loading API\n setTimeout(doBinaryTruncate, 0);\n }\n }\n }, [lines, content, expanded, width, height]);\n\n // Hidden text for screen readers (always the full string)\n const srOnlyText = useMemo(() => <ScreenReaderOnly>{content}</ScreenReaderOnly>, [content]);\n\n // Mark the visible content as aria-hidden\n const textProps = useMemo(() => ({ 'aria-hidden': true }), []);\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;ACgGY;AA5FnC,SAAgB,iBAAiB,SAAS,QAAQ,gBAAgB;AAClE,SAAS,0BAA0B;AAEnC,SAAS,wBAAwB;AAGjC,MAAM,YAAY,CAAC,EAAE,cAAc,aAAa,MAC9C,eAAe,IAAI;AAOrB,MAAM,wBAAwB,CAAC,SAAsB,QAAqB,YAAoB;AAC5F,MAAI,OAAO;AACX,MAAI,QAAQ,QAAQ;AACpB,MAAI,UAAU;AAGd,UAAQ,YAAY;AAEpB,SAAO,QAAQ,OAAO;AACpB,UAAM,MAAM,KAAK,OAAO,OAAO,SAAS,CAAC;AACzC,YAAQ,YAAY,QAAQ,MAAM,GAAG,GAAG;AAExC,QAAI,UAAU,MAAM,GAAG;AAErB,cAAQ,MAAM;AAAA,IAChB,OAAO;AAEL,gBAAU;AACV,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,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;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAGlD,QAAM,EAAE,OAAO,OAAO,IAAI,mBAAmB,cAAc;AAE3D,kBAAgB,MAAM;AACpB,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,eAAe;AACjC,QAAI,CAAC,UAAU,CAAC,UAAW;AAG3B,WAAO,YAAY;AAGnB,UAAM,gBAAgB,UAAU;AAAA,MAC9B,cAAc,UAAU;AAAA,MACxB,cAAc,UAAU;AAAA,IAC1B,CAAC;AACD,kBAAc,YAAY,aAAa;AAGvC,QAAI,CAAC,YAAY,eAAe;AAE9B,YAAM,mBAAmB,MAAM;AAC7B,YAAI,CAAC,UAAU,CAAC,UAAW;AAC3B,8BAAsB,MAAM;AAC1B,gCAAsB,QAAQ,WAAW,OAAO;AAAA,QAClD,CAAC;AAAA,MACH;AAGA,UAAI,WAAW,YAAY,SAAS,MAAM,OAAO;AAC/C,iBAAS,MAAM,MACZ,KAAK,MAAM;AACV,2BAAiB;AAAA,QACnB,CAAC,EACA,MAAM,MAAM;AAEX,qBAAW,kBAAkB,CAAC;AAAA,QAChC,CAAC;AAAA,MACL,OAAO;AAEL,mBAAW,kBAAkB,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,UAAU,OAAO,MAAM,CAAC;AAG5C,QAAM,aAAa,QAAQ,MAAM,oBAAC,oBAAkB,mBAAQ,GAAqB,CAAC,OAAO,CAAC;AAG1F,QAAM,YAAY,QAAQ,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC,CAAC;AAE7D,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
|
@@ -2,12 +2,11 @@ export declare const StyledTextWrapper: import("styled-components").StyledCompon
|
|
|
2
2
|
lines: number;
|
|
3
3
|
expanded: boolean;
|
|
4
4
|
} & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
5
|
-
export declare const StyledTextContent: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme,
|
|
6
|
-
|
|
5
|
+
export declare const StyledTextContent: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
6
|
+
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>;
|
|
7
|
+
export declare const StyledButtonWrapper: import("styled-components").StyledComponent<"span", import("@elliemae/ds-system").Theme, {
|
|
7
8
|
expanded: boolean;
|
|
8
9
|
} & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"span">, never>;
|
|
9
|
-
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>;
|
|
10
|
-
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>;
|
|
11
10
|
export declare const StyledButton: import("styled-components").StyledComponent<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV3T.Props>, import("@elliemae/ds-system").Theme, {
|
|
12
11
|
withTooltip?: boolean;
|
|
13
12
|
} & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<import("react").ComponentType<import("@elliemae/ds-button-v2").DSButtonV3T.Props>>, never>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-read-more",
|
|
3
|
-
"version": "3.57.0-next.
|
|
3
|
+
"version": "3.57.0-next.42",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - Read More",
|
|
6
6
|
"files": [
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
"checkDeps": "npm exec ../../util/ds-codemods -- check-missing-packages --projectFolderPath=\"./\" --ignorePackagesGlobPattern=\"\" --ignoreFilesGlobPattern=\"**/test-ables/*,**/tests/*\""
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@elliemae/ds-accessibility": "3.57.0-next.
|
|
49
|
-
"@elliemae/ds-button-v2": "3.57.0-next.
|
|
50
|
-
"@elliemae/ds-props-helpers": "3.57.0-next.
|
|
51
|
-
"@elliemae/ds-system": "3.57.0-next.
|
|
52
|
-
"@elliemae/ds-tooltip-v3": "3.57.0-next.
|
|
48
|
+
"@elliemae/ds-accessibility": "3.57.0-next.42",
|
|
49
|
+
"@elliemae/ds-button-v2": "3.57.0-next.42",
|
|
50
|
+
"@elliemae/ds-props-helpers": "3.57.0-next.42",
|
|
51
|
+
"@elliemae/ds-system": "3.57.0-next.42",
|
|
52
|
+
"@elliemae/ds-tooltip-v3": "3.57.0-next.42"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@elliemae/ds-monorepo-devops": "3.57.0-next.
|
|
56
|
-
"@elliemae/ds-test-utils": "3.57.0-next.
|
|
55
|
+
"@elliemae/ds-monorepo-devops": "3.57.0-next.42",
|
|
56
|
+
"@elliemae/ds-test-utils": "3.57.0-next.42",
|
|
57
57
|
"@elliemae/pui-cli": "catalog:",
|
|
58
58
|
"jest": "catalog:",
|
|
59
59
|
"styled-components": "catalog:"
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"access": "public",
|
|
69
69
|
"typeSafety": true
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "9137b3564bc7399c85ae12674d2e794f78e71e78"
|
|
72
72
|
}
|