@chayns-components/core 5.5.14 → 5.5.20
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/lib/cjs/components/copyable-content/CopyableContent.js +18 -12
- package/lib/cjs/components/copyable-content/CopyableContent.js.map +1 -1
- package/lib/cjs/components/copyable-content/CopyableContent.styles.js +2 -1
- package/lib/cjs/components/copyable-content/CopyableContent.styles.js.map +1 -1
- package/lib/cjs/components/copyable-content/CopyableContent.test.js +26 -17
- package/lib/cjs/components/copyable-content/CopyableContent.test.js.map +1 -1
- package/lib/cjs/components/copyable-content/copyableContentClipboard.js +45 -28
- package/lib/cjs/components/copyable-content/copyableContentClipboard.js.map +1 -1
- package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js +13 -11
- package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js.map +1 -1
- package/lib/cjs/components/sharing-context-menu/SharingContextMenu.js +11 -12
- package/lib/cjs/components/sharing-context-menu/SharingContextMenu.js.map +1 -1
- package/lib/cjs/components/sharing-context-menu/SharingContextMenu.test.js +48 -0
- package/lib/cjs/components/sharing-context-menu/SharingContextMenu.test.js.map +1 -0
- package/lib/cjs/constants/textStrings.js +0 -4
- package/lib/cjs/constants/textStrings.js.map +1 -1
- package/lib/cjs/utils/sharingBar.js +14 -17
- package/lib/cjs/utils/sharingBar.js.map +1 -1
- package/lib/esm/components/copyable-content/CopyableContent.js +19 -13
- package/lib/esm/components/copyable-content/CopyableContent.js.map +1 -1
- package/lib/esm/components/copyable-content/CopyableContent.styles.js +2 -1
- package/lib/esm/components/copyable-content/CopyableContent.styles.js.map +1 -1
- package/lib/esm/components/copyable-content/CopyableContent.test.js +24 -16
- package/lib/esm/components/copyable-content/CopyableContent.test.js.map +1 -1
- package/lib/esm/components/copyable-content/copyableContentClipboard.js +41 -28
- package/lib/esm/components/copyable-content/copyableContentClipboard.js.map +1 -1
- package/lib/esm/components/copyable-content/copyableContentClipboard.test.js +13 -11
- package/lib/esm/components/copyable-content/copyableContentClipboard.test.js.map +1 -1
- package/lib/esm/components/sharing-context-menu/SharingContextMenu.js +11 -12
- package/lib/esm/components/sharing-context-menu/SharingContextMenu.js.map +1 -1
- package/lib/esm/components/sharing-context-menu/SharingContextMenu.test.js +45 -0
- package/lib/esm/components/sharing-context-menu/SharingContextMenu.test.js.map +1 -0
- package/lib/esm/constants/textStrings.js +0 -4
- package/lib/esm/constants/textStrings.js.map +1 -1
- package/lib/esm/utils/sharingBar.js +15 -18
- package/lib/esm/utils/sharingBar.js.map +1 -1
- package/lib/types/components/copyable-content/CopyableContent.d.ts +0 -1
- package/lib/types/components/sharing-context-menu/SharingContextMenu.d.ts +16 -0
- package/lib/types/constants/textStrings.d.ts +0 -4
- package/package.json +5 -7
- package/LICENSE +0 -21
|
@@ -23,22 +23,21 @@ var _CopyableContent2 = require("./CopyableContent.types");
|
|
|
23
23
|
var _copyableContentClipboard = require("./copyableContentClipboard");
|
|
24
24
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
25
25
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
26
|
+
const COPY_FEEDBACK_DURATION = 1500;
|
|
26
27
|
const CopyableContent = ({
|
|
27
28
|
appearance = _CopyableContent2.CopyableContentAppearance.Default,
|
|
28
29
|
content,
|
|
29
30
|
children,
|
|
30
|
-
copiedMessage,
|
|
31
31
|
copyFailedMessage
|
|
32
32
|
}) => {
|
|
33
33
|
const rootRef = (0, _react.useRef)(null);
|
|
34
34
|
const actionGroupRef = (0, _react.useRef)(null);
|
|
35
|
+
const copyFeedbackTimeoutRef = (0, _react.useRef)();
|
|
35
36
|
const isActionGroupSticky = (0, _useStickyActionState.useStickyActionState)(rootRef, actionGroupRef);
|
|
37
|
+
const [hasCopied, setHasCopied] = (0, _react.useState)(false);
|
|
36
38
|
const defaultCopyButtonText = (0, _textstring.useTextstringValue)({
|
|
37
39
|
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copy)
|
|
38
40
|
});
|
|
39
|
-
const defaultCopiedMessage = (0, _textstring.useTextstringValue)({
|
|
40
|
-
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copied)
|
|
41
|
-
});
|
|
42
41
|
const defaultCopyFailedMessage = (0, _textstring.useTextstringValue)({
|
|
43
42
|
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copyFailed)
|
|
44
43
|
});
|
|
@@ -46,15 +45,20 @@ const CopyableContent = ({
|
|
|
46
45
|
textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.share)
|
|
47
46
|
});
|
|
48
47
|
const html = (0, _react.useMemo)(() => (0, _format.formatStringToHtml)(content).html, [content]);
|
|
48
|
+
(0, _react.useEffect)(() => () => {
|
|
49
|
+
window.clearTimeout(copyFeedbackTimeoutRef.current);
|
|
50
|
+
}, []);
|
|
51
|
+
const showCopyFeedback = (0, _react.useCallback)(() => {
|
|
52
|
+
window.clearTimeout(copyFeedbackTimeoutRef.current);
|
|
53
|
+
setHasCopied(true);
|
|
54
|
+
copyFeedbackTimeoutRef.current = window.setTimeout(() => {
|
|
55
|
+
setHasCopied(false);
|
|
56
|
+
}, COPY_FEEDBACK_DURATION);
|
|
57
|
+
}, []);
|
|
49
58
|
const handleCopy = (0, _react.useCallback)(async () => {
|
|
50
59
|
try {
|
|
51
60
|
await (0, _copyableContentClipboard.copyableContentToClipboard)(content);
|
|
52
|
-
|
|
53
|
-
showCloseIcon: true,
|
|
54
|
-
text: copiedMessage ?? defaultCopiedMessage,
|
|
55
|
-
toastType: _chaynsApi.ToastType.SUCCESS,
|
|
56
|
-
type: _chaynsApi.DialogType.TOAST
|
|
57
|
-
}).open();
|
|
61
|
+
showCopyFeedback();
|
|
58
62
|
} catch {
|
|
59
63
|
void (0, _chaynsApi.createDialog)({
|
|
60
64
|
showCloseIcon: true,
|
|
@@ -63,7 +67,7 @@ const CopyableContent = ({
|
|
|
63
67
|
type: _chaynsApi.DialogType.TOAST
|
|
64
68
|
}).open();
|
|
65
69
|
}
|
|
66
|
-
}, [content,
|
|
70
|
+
}, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);
|
|
67
71
|
return /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContent, {
|
|
68
72
|
$appearance: appearance,
|
|
69
73
|
className: "copyable-content",
|
|
@@ -78,9 +82,11 @@ const CopyableContent = ({
|
|
|
78
82
|
},
|
|
79
83
|
type: "button"
|
|
80
84
|
}, /*#__PURE__*/_react.default.createElement(_Icon.default, {
|
|
81
|
-
icons: ['fa-light fa-copy']
|
|
85
|
+
icons: hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']
|
|
82
86
|
})), /*#__PURE__*/_react.default.createElement(_SharingContextMenu.default, {
|
|
83
87
|
link: content,
|
|
88
|
+
shouldShowCallingCodeAction: false,
|
|
89
|
+
shouldShowCopyAction: false,
|
|
84
90
|
shouldUseDefaultTriggerStyles: false
|
|
85
91
|
}, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentButton, {
|
|
86
92
|
$isSticky: isActionGroupSticky,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_useStickyActionState","_SharingContextMenu","_Icon","_CopyableContent","_CopyableContent2","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","CopyableContent","appearance","CopyableContentAppearance","Default","content","children","copiedMessage","copyFailedMessage","rootRef","useRef","actionGroupRef","isActionGroupSticky","useStickyActionState","defaultCopyButtonText","useTextstringValue","textstring","ttsToITextString","textStrings","components","copyableContent","copy","defaultCopiedMessage","copied","defaultCopyFailedMessage","copyFailed","shareText","share","html","useMemo","formatStringToHtml","handleCopy","useCallback","copyableContentToClipboard","createDialog","showCloseIcon","text","toastType","ToastType","SUCCESS","type","DialogType","TOAST","open","ERROR","createElement","StyledCopyableContent","$appearance","className","ref","StyledCopyableContentActions","StyledCopyableContentActionGroup","StyledCopyableContentButton","$isSticky","onClick","icons","link","shouldUseDefaultTriggerStyles","StyledCopyableContentBody","dangerouslySetInnerHTML","__html","displayName","_default","exports"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useMemo, useRef } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport {\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n content: string;\n children?: ReactNode;\n copiedMessage?: string;\n copyFailedMessage?: string;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copiedMessage,\n copyFailedMessage,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopiedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copied),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content);\n void createDialog({\n showCloseIcon: true,\n text: copiedMessage ?? defaultCopiedMessage,\n toastType: ToastType.SUCCESS,\n type: DialogType.TOAST,\n }).open();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [content, copiedMessage, defaultCopiedMessage, defaultCopyFailedMessage, copyFailedMessage]);\n\n return (\n <StyledCopyableContent $appearance={appearance} className=\"copyable-content\" ref={rootRef}>\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu link={content} shouldUseDefaultTriggerStyles={false}>\n <StyledCopyableContentButton\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n <StyledCopyableContentBody>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_useStickyActionState","_SharingContextMenu","_Icon","_CopyableContent","_CopyableContent2","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","COPY_FEEDBACK_DURATION","CopyableContent","appearance","CopyableContentAppearance","Default","content","children","copyFailedMessage","rootRef","useRef","actionGroupRef","copyFeedbackTimeoutRef","isActionGroupSticky","useStickyActionState","hasCopied","setHasCopied","useState","defaultCopyButtonText","useTextstringValue","textstring","ttsToITextString","textStrings","components","copyableContent","copy","defaultCopyFailedMessage","copyFailed","shareText","share","html","useMemo","formatStringToHtml","useEffect","window","clearTimeout","current","showCopyFeedback","useCallback","setTimeout","handleCopy","copyableContentToClipboard","createDialog","showCloseIcon","text","toastType","ToastType","ERROR","type","DialogType","TOAST","open","createElement","StyledCopyableContent","$appearance","className","ref","StyledCopyableContentActions","StyledCopyableContentActionGroup","StyledCopyableContentButton","$isSticky","onClick","icons","link","shouldShowCallingCodeAction","shouldShowCopyAction","shouldUseDefaultTriggerStyles","StyledCopyableContentBody","dangerouslySetInnerHTML","__html","displayName","_default","exports"],"sources":["../../../../src/components/copyable-content/CopyableContent.tsx"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { ttsToITextString, useTextstringValue } from '@chayns-components/textstring';\nimport { createDialog, DialogType, ToastType } from 'chayns-api';\nimport React, { FC, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport textStrings from '../../constants/textStrings';\nimport { useStickyActionState } from '../../hooks/useStickyActionState';\nimport SharingContextMenu from '../sharing-context-menu/SharingContextMenu';\nimport Icon from '../icon/Icon';\nimport {\n StyledCopyableContent,\n StyledCopyableContentActionGroup,\n StyledCopyableContentActions,\n StyledCopyableContentBody,\n StyledCopyableContentButton,\n} from './CopyableContent.styles';\nimport { CopyableContentAppearance } from './CopyableContent.types';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst COPY_FEEDBACK_DURATION = 1500;\n\nexport type CopyableContentProps = {\n /**\n * Controls the visual surface of the content block.\n */\n appearance?: CopyableContentAppearance;\n content: string;\n children?: ReactNode;\n copyFailedMessage?: string;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\n appearance = CopyableContentAppearance.Default,\n content,\n children,\n copyFailedMessage,\n}) => {\n const rootRef = useRef<HTMLElement>(null);\n const actionGroupRef = useRef<HTMLDivElement>(null);\n const copyFeedbackTimeoutRef = useRef<number>();\n const isActionGroupSticky = useStickyActionState(rootRef, actionGroupRef);\n const [hasCopied, setHasCopied] = useState(false);\n\n const defaultCopyButtonText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copy),\n });\n const defaultCopyFailedMessage = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.copyFailed),\n });\n const shareText = useTextstringValue({\n textstring: ttsToITextString(textStrings.components.copyableContent.share),\n });\n\n const html = useMemo(() => formatStringToHtml(content).html, [content]);\n\n useEffect(\n () => () => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n },\n [],\n );\n\n const showCopyFeedback = useCallback(() => {\n window.clearTimeout(copyFeedbackTimeoutRef.current);\n setHasCopied(true);\n\n copyFeedbackTimeoutRef.current = window.setTimeout(() => {\n setHasCopied(false);\n }, COPY_FEEDBACK_DURATION);\n }, []);\n\n const handleCopy = useCallback(async () => {\n try {\n await copyableContentToClipboard(content);\n showCopyFeedback();\n } catch {\n void createDialog({\n showCloseIcon: true,\n text: copyFailedMessage ?? defaultCopyFailedMessage,\n toastType: ToastType.ERROR,\n type: DialogType.TOAST,\n }).open();\n }\n }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);\n\n return (\n <StyledCopyableContent $appearance={appearance} className=\"copyable-content\" ref={rootRef}>\n <StyledCopyableContentActions ref={actionGroupRef}>\n <StyledCopyableContentActionGroup>\n <StyledCopyableContentButton\n $isSticky={isActionGroupSticky}\n aria-label={defaultCopyButtonText}\n onClick={() => {\n void handleCopy();\n }}\n type=\"button\"\n >\n <Icon icons={hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']} />\n </StyledCopyableContentButton>\n <SharingContextMenu\n link={content}\n shouldShowCallingCodeAction={false}\n shouldShowCopyAction={false}\n shouldUseDefaultTriggerStyles={false}\n >\n <StyledCopyableContentButton\n $isSticky={isActionGroupSticky}\n aria-label={shareText}\n type=\"button\"\n >\n <Icon icons={['fa fa-share-nodes']} />\n </StyledCopyableContentButton>\n </SharingContextMenu>\n </StyledCopyableContentActionGroup>\n </StyledCopyableContentActions>\n <StyledCopyableContentBody>\n {children ?? <div dangerouslySetInnerHTML={{ __html: html }} />}\n </StyledCopyableContentBody>\n </StyledCopyableContent>\n );\n};\n\nCopyableContent.displayName = 'CopyableContent';\n\nexport default CopyableContent;\n\nexport { CopyableContentAppearance } from './CopyableContent.types';\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -12,7 +12,7 @@ const getBackgroundColor = ({
|
|
|
12
12
|
theme
|
|
13
13
|
}) => {
|
|
14
14
|
if ($appearance === _CopyableContent.CopyableContentAppearance.Chat) {
|
|
15
|
-
return
|
|
15
|
+
return 'rgba(0, 0, 0, 0.2)';
|
|
16
16
|
}
|
|
17
17
|
const secondaryColor = theme['secondary-100-rgb'] ?? '255, 255, 255';
|
|
18
18
|
const opacity = theme.cardBackgroundOpacity ?? 1;
|
|
@@ -22,6 +22,7 @@ const StyledCopyableContent = exports.StyledCopyableContent = _styledComponents.
|
|
|
22
22
|
--copyable-content-action-size: 32px;
|
|
23
23
|
--copyable-content-action-inset: 8px;
|
|
24
24
|
|
|
25
|
+
margin: 4px 0;
|
|
25
26
|
min-width: 0;
|
|
26
27
|
max-width: 100%;
|
|
27
28
|
overflow-x: clip;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopyableContent.styles.js","names":["_styledComponents","_interopRequireDefault","require","_CopyableContent","e","__esModule","default","getBackgroundColor","$appearance","theme","CopyableContentAppearance","Chat","
|
|
1
|
+
{"version":3,"file":"CopyableContent.styles.js","names":["_styledComponents","_interopRequireDefault","require","_CopyableContent","e","__esModule","default","getBackgroundColor","$appearance","theme","CopyableContentAppearance","Chat","secondaryColor","opacity","cardBackgroundOpacity","StyledCopyableContent","exports","styled","section","cardBorderRadius","text","StyledCopyableContentActions","div","StyledCopyableContentActionGroup","StyledCopyableContentButton","button","$isSticky","buttonBackgroundColor","StyledCopyableContentBody","headline","primary"],"sources":["../../../../src/components/copyable-content/CopyableContent.styles.ts"],"sourcesContent":["import type { WithTheme } from '../color-scheme-provider/ColorSchemeProvider';\nimport styled from 'styled-components';\nimport { CopyableContentAppearance } from './CopyableContent.types';\n\ntype StyledCopyableContentProps = WithTheme<{\n $appearance: CopyableContentAppearance;\n}>;\n\nconst getBackgroundColor = ({ $appearance, theme }: StyledCopyableContentProps) => {\n if ($appearance === CopyableContentAppearance.Chat) {\n return 'rgba(0, 0, 0, 0.2)';\n }\n\n const secondaryColor = theme['secondary-100-rgb'] ?? '255, 255, 255';\n const opacity = theme.cardBackgroundOpacity ?? 1;\n\n return `rgba(${secondaryColor}, ${opacity})`;\n};\n\nexport const StyledCopyableContent = styled.section<StyledCopyableContentProps>`\n --copyable-content-action-size: 32px;\n --copyable-content-action-inset: 8px;\n\n margin: 4px 0;\n min-width: 0;\n max-width: 100%;\n overflow-x: clip;\n overflow-wrap: anywhere;\n padding-top: calc(\n var(--copyable-content-action-size) + var(--copyable-content-action-inset) * 2\n );\n border-radius: ${({ theme }) => theme.cardBorderRadius}px;\n background-color: ${getBackgroundColor};\n color: ${({ theme }) => theme.text};\n`;\n\ntype StyledCopyableContentButtonProps = WithTheme<{\n $isSticky: boolean;\n}>;\n\nexport const StyledCopyableContentActions = styled.div`\n position: sticky;\n top: var(--copyable-content-action-inset);\n z-index: 1;\n display: flex;\n justify-content: flex-end;\n height: calc(var(--copyable-content-action-size) + var(--copyable-content-action-inset));\n margin-top: calc(\n (var(--copyable-content-action-size) + var(--copyable-content-action-inset)) * -1\n );\n padding-right: var(--copyable-content-action-inset);\n`;\n\nexport const StyledCopyableContentActionGroup = styled.div`\n display: flex;\n gap: 4px;\n height: var(--copyable-content-action-size);\n`;\n\nexport const StyledCopyableContentButton = styled.button<StyledCopyableContentButtonProps>`\n box-sizing: border-box;\n border: 1px solid ${({ $isSticky, theme }) => ($isSticky ? theme['202'] : 'transparent')};\n border-radius: 4px;\n width: var(--copyable-content-action-size);\n height: var(--copyable-content-action-size);\n padding: 0;\n cursor: pointer;\n background-color: ${({ $isSticky, theme }) => ($isSticky ? theme['100'] : 'transparent')};\n color: ${({ theme }) => theme.text};\n box-shadow: ${({ $isSticky }) => ($isSticky ? '0 2px 8px rgba(0, 0, 0, 0.16)' : 'none')};\n transition:\n background-color 0.15s ease,\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n transform 0.15s ease;\n\n &:hover {\n background-color: rgba(${({ theme }) => theme['text-rgb']}, 0.1);\n box-shadow: inset 0 0 0 1px rgba(${({ theme }) => theme['text-rgb']}, 0.06);\n }\n\n &:active {\n transform: scale(0.9);\n }\n\n &:focus-visible {\n outline: 2px solid ${({ theme }) => theme.buttonBackgroundColor};\n outline-offset: 2px;\n }\n`;\n\nexport const StyledCopyableContentBody = styled.div<WithTheme<unknown>>`\n min-width: 0;\n max-width: 100%;\n overflow-wrap: anywhere;\n padding: 0 12px 12px;\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n p,\n ul,\n ol,\n blockquote {\n margin: 0 0 12px;\n }\n\n h1,\n h2,\n h3,\n h4,\n h5,\n h6 {\n color: ${({ theme }) => theme.headline};\n }\n\n ul,\n ol {\n padding-left: 24px;\n }\n\n blockquote {\n padding-left: 12px;\n border-left: 3px solid ${({ theme }) => theme['202']};\n }\n\n a {\n color: ${({ theme }) => theme.primary};\n overflow-wrap: anywhere;\n }\n\n > :last-child {\n margin-bottom: 0;\n }\n`;\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,14 +5,24 @@ var _react2 = _interopRequireDefault(require("react"));
|
|
|
5
5
|
var _vitest = require("vitest");
|
|
6
6
|
var _CopyableContent = _interopRequireDefault(require("./CopyableContent"));
|
|
7
7
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
-
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
9
|
+
|
|
9
10
|
_vitest.vi.mock('../icon/Icon', () => ({
|
|
10
|
-
default: (
|
|
11
|
+
default: ({
|
|
12
|
+
icons
|
|
13
|
+
}) => /*#__PURE__*/_react2.default.createElement("span", {
|
|
14
|
+
"data-icons": icons.join(' ')
|
|
15
|
+
})
|
|
11
16
|
}));
|
|
12
17
|
_vitest.vi.mock('../sharing-context-menu/SharingContextMenu', () => ({
|
|
13
18
|
default: ({
|
|
14
|
-
children
|
|
15
|
-
|
|
19
|
+
children,
|
|
20
|
+
shouldShowCallingCodeAction,
|
|
21
|
+
shouldShowCopyAction
|
|
22
|
+
}) => /*#__PURE__*/_react2.default.createElement("div", {
|
|
23
|
+
"data-calling-code-action": shouldShowCallingCodeAction,
|
|
24
|
+
"data-copy-action": shouldShowCopyAction
|
|
25
|
+
}, children)
|
|
16
26
|
}));
|
|
17
27
|
_vitest.vi.mock('chayns-api', async importOriginal => ({
|
|
18
28
|
...(await importOriginal()),
|
|
@@ -52,32 +62,31 @@ const readBlob = blob => new Promise((resolve, reject) => {
|
|
|
52
62
|
const item = write.mock.calls[0][0][0];
|
|
53
63
|
await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');
|
|
54
64
|
});
|
|
55
|
-
(0, _vitest.it)('shows a
|
|
65
|
+
(0, _vitest.it)('shows a checkmark after a successful copy', async () => {
|
|
56
66
|
_vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
const {
|
|
68
|
+
container
|
|
69
|
+
} = (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
70
|
+
content: "source"
|
|
60
71
|
}));
|
|
61
72
|
_react.fireEvent.click(_react.screen.getByRole('button', {
|
|
62
73
|
name: 'Kopieren'
|
|
63
74
|
}));
|
|
64
|
-
await (0, _react.waitFor)(
|
|
65
|
-
|
|
66
|
-
createDialog
|
|
67
|
-
} = await Promise.resolve().then(() => _interopRequireWildcard(require('chayns-api')));
|
|
68
|
-
(0, _vitest.expect)(createDialog).toHaveBeenCalledWith(_vitest.expect.objectContaining({
|
|
69
|
-
text: 'Done'
|
|
70
|
-
}));
|
|
75
|
+
await (0, _react.waitFor)(() => {
|
|
76
|
+
(0, _vitest.expect)(container.querySelector('[data-icons="fa fa-check"]')).toBeInTheDocument();
|
|
71
77
|
});
|
|
72
|
-
(0, _vitest.expect)(_react.screen.queryByRole('status')).not.toBeInTheDocument();
|
|
73
78
|
});
|
|
74
79
|
(0, _vitest.it)('offers sharing actions', () => {
|
|
75
|
-
|
|
80
|
+
const {
|
|
81
|
+
container
|
|
82
|
+
} = (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
|
|
76
83
|
content: "source"
|
|
77
84
|
}));
|
|
78
85
|
(0, _vitest.expect)(_react.screen.getByRole('button', {
|
|
79
86
|
name: 'Teilen'
|
|
80
87
|
})).toBeInTheDocument();
|
|
88
|
+
(0, _vitest.expect)(container.querySelector('[data-copy-action="false"]')).toBeInTheDocument();
|
|
89
|
+
(0, _vitest.expect)(container.querySelector('[data-calling-code-action="false"]')).toBeInTheDocument();
|
|
81
90
|
});
|
|
82
91
|
});
|
|
83
92
|
//# sourceMappingURL=CopyableContent.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopyableContent.test.js","names":["_react","require","_react2","_interopRequireDefault","_vitest","_CopyableContent","e","__esModule","default","
|
|
1
|
+
{"version":3,"file":"CopyableContent.test.js","names":["_react","require","_react2","_interopRequireDefault","_vitest","_CopyableContent","e","__esModule","default","vi","mock","icons","createElement","join","children","shouldShowCallingCodeAction","shouldShowCopyAction","importOriginal","createDialog","fn","open","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","it","render","content","expect","screen","getByRole","name","toBeInTheDocument","toHaveAttribute","document","querySelector","not","write","spyOn","navigator","clipboard","mockResolvedValue","fireEvent","click","waitFor","toHaveBeenCalled","item","calls","getType","resolves","toBe","container"],"sources":["../../../../src/components/copyable-content/CopyableContent.test.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-call */\nimport { fireEvent, render, screen, waitFor } from '@testing-library/react';\nimport React from 'react';\nimport { describe, expect, it, vi } from 'vitest';\nimport CopyableContent from './CopyableContent';\n\nvi.mock('../icon/Icon', () => ({\n default: ({ icons }: { icons: string[] }) => <span data-icons={icons.join(' ')} />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({\n children,\n shouldShowCallingCodeAction,\n shouldShowCopyAction,\n }: {\n children: React.ReactNode;\n shouldShowCallingCodeAction?: boolean;\n shouldShowCopyAction?: boolean;\n }) => (\n <div\n data-calling-code-action={shouldShowCallingCodeAction}\n data-copy-action={shouldShowCopyAction}\n >\n {children}\n </div>\n ),\n}));\n\nvi.mock('chayns-api', async (importOriginal) => ({\n ...(await importOriginal<typeof import('chayns-api')>()),\n createDialog: vi.fn(() => ({ open: vi.fn() })),\n}));\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('CopyableContent', () => {\n it('renders formatted markdown without active input HTML', () => {\n render(\n <CopyableContent\n content={\n '# Heading\\n\\n- Entry\\n\\n[Link](https://example.com)\\n<script onload=\"x\">bad</script>'\n }\n />,\n );\n\n expect(screen.getByRole('heading', { name: 'Heading' })).toBeInTheDocument();\n expect(screen.getByRole('link', { name: 'Link' })).toHaveAttribute(\n 'href',\n 'https://example.com',\n );\n expect(document.querySelector('script')).not.toBeInTheDocument();\n expect(document.querySelector('[onload]')).not.toBeInTheDocument();\n });\n\n it('copies content instead of custom children', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source markdown\">Visible replacement</CopyableContent>);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => expect(write).toHaveBeenCalled());\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem;\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('source markdown');\n });\n\n it('shows a checkmark after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n const { container } = render(<CopyableContent content=\"source\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(() => {\n expect(container.querySelector('[data-icons=\"fa fa-check\"]')).toBeInTheDocument();\n });\n });\n\n it('offers sharing actions', () => {\n const { container } = render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n expect(container.querySelector('[data-copy-action=\"false\"]')).toBeInTheDocument();\n expect(container.querySelector('[data-calling-code-action=\"false\"]')).toBeInTheDocument();\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -5,19 +5,47 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.copyableContentToClipboard = void 0;
|
|
7
7
|
var _format = require("@chayns-components/format");
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const getPlainTextFromHtml = html => {
|
|
9
|
+
const document = new DOMParser().parseFromString(html, 'text/html');
|
|
10
|
+
return Array.from(document.body.children).map(element => {
|
|
11
|
+
var _element$textContent;
|
|
12
|
+
if (element.tagName === 'UL' || element.tagName === 'OL') {
|
|
13
|
+
return Array.from(element.children).map(item => {
|
|
14
|
+
var _item$textContent;
|
|
15
|
+
return `• ${((_item$textContent = item.textContent) === null || _item$textContent === void 0 ? void 0 : _item$textContent.trim()) ?? ''}`;
|
|
16
|
+
}).join('\n');
|
|
17
|
+
}
|
|
18
|
+
return ((_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim()) ?? '';
|
|
19
|
+
}).filter(Boolean).join('\n\n');
|
|
20
|
+
};
|
|
21
|
+
const createClipboardContent = source => {
|
|
22
|
+
const {
|
|
23
|
+
html
|
|
24
|
+
} = (0, _format.formatStringToHtml)(source);
|
|
25
|
+
return {
|
|
26
|
+
html,
|
|
27
|
+
plainText: getPlainTextFromHtml(html)
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const copyWithClipboardItem = async ({
|
|
31
|
+
html,
|
|
32
|
+
plainText
|
|
33
|
+
}) => {
|
|
34
|
+
const clipboardData = new ClipboardItem({
|
|
35
|
+
'text/plain': new Blob([plainText], {
|
|
36
|
+
type: 'text/plain'
|
|
37
|
+
}),
|
|
38
|
+
'text/html': new Blob([html], {
|
|
39
|
+
type: 'text/html'
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
await navigator.clipboard.write([clipboardData]);
|
|
15
43
|
};
|
|
16
|
-
const copyPlainText = async
|
|
44
|
+
const copyPlainText = async plainText => {
|
|
17
45
|
if (typeof ClipboardItem !== 'undefined') {
|
|
18
46
|
try {
|
|
19
|
-
await navigator.clipboard.write([
|
|
20
|
-
'text/plain': new Blob([
|
|
47
|
+
await navigator.clipboard.write([new ClipboardItem({
|
|
48
|
+
'text/plain': new Blob([plainText], {
|
|
21
49
|
type: 'text/plain'
|
|
22
50
|
})
|
|
23
51
|
})]);
|
|
@@ -26,33 +54,22 @@ const copyPlainText = async source => {
|
|
|
26
54
|
// Use writeText as the final browser-compatible fallback.
|
|
27
55
|
}
|
|
28
56
|
}
|
|
29
|
-
await navigator.clipboard.writeText(
|
|
57
|
+
await navigator.clipboard.writeText(plainText);
|
|
30
58
|
};
|
|
31
59
|
const copyableContentToClipboard = async source => {
|
|
60
|
+
const clipboardContent = createClipboardContent(source);
|
|
32
61
|
if (!navigator.clipboard) {
|
|
33
62
|
throw new Error('Clipboard API is not available.');
|
|
34
63
|
}
|
|
35
|
-
if (typeof ClipboardItem === 'undefined'
|
|
36
|
-
await copyPlainText(
|
|
64
|
+
if (typeof ClipboardItem === 'undefined') {
|
|
65
|
+
await copyPlainText(clipboardContent.plainText);
|
|
37
66
|
return;
|
|
38
67
|
}
|
|
39
|
-
const {
|
|
40
|
-
html
|
|
41
|
-
} = (0, _format.formatStringToHtml)(source);
|
|
42
68
|
try {
|
|
43
|
-
await
|
|
44
|
-
'text/plain': new Blob([source], {
|
|
45
|
-
type: 'text/plain'
|
|
46
|
-
}),
|
|
47
|
-
'text/markdown': new Blob([source], {
|
|
48
|
-
type: 'text/markdown'
|
|
49
|
-
}),
|
|
50
|
-
'text/html': new Blob([html], {
|
|
51
|
-
type: 'text/html'
|
|
52
|
-
})
|
|
53
|
-
})]);
|
|
69
|
+
await copyWithClipboardItem(clipboardContent);
|
|
54
70
|
} catch {
|
|
55
|
-
|
|
71
|
+
// Use the plain-text fallback when the browser rejects rich MIME types.
|
|
72
|
+
await copyPlainText(clipboardContent.plainText);
|
|
56
73
|
}
|
|
57
74
|
};
|
|
58
75
|
exports.copyableContentToClipboard = copyableContentToClipboard;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyableContentClipboard.js","names":["_format","require","
|
|
1
|
+
{"version":3,"file":"copyableContentClipboard.js","names":["_format","require","getPlainTextFromHtml","html","document","DOMParser","parseFromString","Array","from","body","children","map","element","_element$textContent","tagName","item","_item$textContent","textContent","trim","join","filter","Boolean","createClipboardContent","source","formatStringToHtml","plainText","copyWithClipboardItem","clipboardData","ClipboardItem","Blob","type","navigator","clipboard","write","copyPlainText","writeText","copyableContentToClipboard","clipboardContent","Error","exports"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\ninterface ClipboardContent {\n html: string;\n plainText: string;\n}\n\nconst getPlainTextFromHtml = (html: string) => {\n const document = new DOMParser().parseFromString(html, 'text/html');\n\n return Array.from(document.body.children)\n .map((element) => {\n if (element.tagName === 'UL' || element.tagName === 'OL') {\n return Array.from(element.children)\n .map((item) => `• ${item.textContent?.trim() ?? ''}`)\n .join('\\n');\n }\n\n return element.textContent?.trim() ?? '';\n })\n .filter(Boolean)\n .join('\\n\\n');\n};\n\nconst createClipboardContent = (source: string): ClipboardContent => {\n const { html } = formatStringToHtml(source);\n return { html, plainText: getPlainTextFromHtml(html) };\n};\n\nconst copyWithClipboardItem = async ({ html, plainText }: ClipboardContent) => {\n const clipboardData = new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n });\n\n await navigator.clipboard.write([clipboardData]);\n};\n\nconst copyPlainText = async (plainText: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/plain': new Blob([plainText], { type: 'text/plain' }),\n }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(plainText);\n};\n\nexport const copyableContentToClipboard = async (source: string) => {\n const clipboardContent = createClipboardContent(source);\n\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined') {\n await copyPlainText(clipboardContent.plainText);\n return;\n }\n\n try {\n await copyWithClipboardItem(clipboardContent);\n } catch {\n // Use the plain-text fallback when the browser rejects rich MIME types.\n await copyPlainText(clipboardContent.plainText);\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -14,27 +14,29 @@ const readBlob = blob => new Promise((resolve, reject) => {
|
|
|
14
14
|
(0, _vitest.afterEach)(() => {
|
|
15
15
|
_vitest.vi.restoreAllMocks();
|
|
16
16
|
});
|
|
17
|
-
(0, _vitest.it)('writes
|
|
17
|
+
(0, _vitest.it)('writes readable plain text first and safe HTML second', async () => {
|
|
18
18
|
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
19
19
|
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
20
20
|
const item = write.mock.calls[0][0][0];
|
|
21
|
-
|
|
22
|
-
await (0, _vitest.expect)(readBlob(await item.getType('text/
|
|
21
|
+
(0, _vitest.expect)(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);
|
|
22
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\n\nLink');
|
|
23
23
|
await (0, _vitest.expect)(readBlob(await item.getType('text/html'))).resolves.toBe((0, _format.formatStringToHtml)(source).html);
|
|
24
24
|
});
|
|
25
|
-
(0, _vitest.it)('uses a plain ClipboardItem
|
|
26
|
-
_vitest.vi.
|
|
27
|
-
|
|
28
|
-
}));
|
|
29
|
-
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
|
|
25
|
+
(0, _vitest.it)('uses a plain ClipboardItem after a rejected rich write', async () => {
|
|
26
|
+
const write = _vitest.vi.spyOn(navigator.clipboard, 'write').mockRejectedValueOnce(new Error('denied')).mockResolvedValueOnce();
|
|
27
|
+
const writeText = _vitest.vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
|
|
30
28
|
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
31
|
-
(0, _vitest.expect)(write).toHaveBeenCalledTimes(
|
|
29
|
+
(0, _vitest.expect)(write).toHaveBeenCalledTimes(2);
|
|
30
|
+
(0, _vitest.expect)(writeText).not.toHaveBeenCalled();
|
|
31
|
+
const item = write.mock.calls[1][0][0];
|
|
32
|
+
(0, _vitest.expect)(Object.keys(item.items)).toEqual(['text/plain']);
|
|
33
|
+
await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\n\nLink');
|
|
32
34
|
});
|
|
33
|
-
(0, _vitest.it)('uses writeText after ClipboardItem writes fail', async () => {
|
|
35
|
+
(0, _vitest.it)('uses writeText after all ClipboardItem writes fail', async () => {
|
|
34
36
|
_vitest.vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));
|
|
35
37
|
const writeText = _vitest.vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
|
|
36
38
|
await (0, _copyableContentClipboard.copyableContentToClipboard)(source);
|
|
37
|
-
(0, _vitest.expect)(writeText).toHaveBeenCalledWith(
|
|
39
|
+
(0, _vitest.expect)(writeText).toHaveBeenCalledWith('Heading\n\nLink');
|
|
38
40
|
});
|
|
39
41
|
});
|
|
40
42
|
//# sourceMappingURL=copyableContentClipboard.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyableContentClipboard.test.js","names":["_format","require","_vitest","_copyableContentClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","afterEach","vi","restoreAllMocks","it","write","spyOn","navigator","clipboard","mockResolvedValue","copyableContentToClipboard","item","mock","calls","expect","getType","resolves","toBe","formatStringToHtml","html","
|
|
1
|
+
{"version":3,"file":"copyableContentClipboard.test.js","names":["_format","require","_vitest","_copyableContentClipboard","source","readBlob","blob","Promise","resolve","reject","reader","FileReader","onerror","error","onload","result","readAsText","describe","afterEach","vi","restoreAllMocks","it","write","spyOn","navigator","clipboard","mockResolvedValue","copyableContentToClipboard","item","mock","calls","expect","Object","keys","items","toEqual","getType","resolves","toBe","formatStringToHtml","html","mockRejectedValueOnce","Error","mockResolvedValueOnce","writeText","toHaveBeenCalledTimes","not","toHaveBeenCalled","mockRejectedValue","toHaveBeenCalledWith"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.test.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport { copyableContentToClipboard } from './copyableContentClipboard';\n\nconst source = '# Heading\\n\\n[Link](https://example.com)';\n\nconst readBlob = (blob: Blob) =>\n new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error);\n reader.onload = () => resolve(reader.result as string);\n reader.readAsText(blob);\n });\n\ndescribe('copyableContentToClipboard', () => {\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n it('writes readable plain text first and safe HTML second', async () => {\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n const item = write.mock.calls[0][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain', 'text/html']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem after a rejected rich write', async () => {\n const write = vi\n .spyOn(navigator.clipboard, 'write')\n .mockRejectedValueOnce(new Error('denied'))\n .mockResolvedValueOnce();\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(2);\n expect(writeText).not.toHaveBeenCalled();\n const item = write.mock.calls[1][0][0] as unknown as ClipboardItem & {\n items: Record<string, Blob>;\n };\n expect(Object.keys(item.items)).toEqual(['text/plain']);\n await expect(readBlob(await item.getType('text/plain'))).resolves.toBe('Heading\\n\\nLink');\n });\n\n it('uses writeText after all ClipboardItem writes fail', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockRejectedValue(new Error('denied'));\n const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(writeText).toHaveBeenCalledWith('Heading\\n\\nLink');\n });\n});\n"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,yBAAA,GAAAF,OAAA;AAEA,MAAMG,MAAM,GAAG,0CAA0C;AAEzD,MAAMC,QAAQ,GAAIC,IAAU,IACxB,IAAIC,OAAO,CAAS,CAACC,OAAO,EAAEC,MAAM,KAAK;EACrC,MAAMC,MAAM,GAAG,IAAIC,UAAU,CAAC,CAAC;EAC/BD,MAAM,CAACE,OAAO,GAAG,MAAMH,MAAM,CAACC,MAAM,CAACG,KAAK,CAAC;EAC3CH,MAAM,CAACI,MAAM,GAAG,MAAMN,OAAO,CAACE,MAAM,CAACK,MAAgB,CAAC;EACtDL,MAAM,CAACM,UAAU,CAACV,IAAI,CAAC;AAC3B,CAAC,CAAC;AAEN,IAAAW,gBAAQ,EAAC,4BAA4B,EAAE,MAAM;EACzC,IAAAC,iBAAS,EAAC,MAAM;IACZC,UAAE,CAACC,eAAe,CAAC,CAAC;EACxB,CAAC,CAAC;EAEF,IAAAC,UAAE,EAAC,uDAAuD,EAAE,YAAY;IACpE,MAAMC,KAAK,GAAGH,UAAE,CAACI,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,OAAO,CAAC,CAACC,iBAAiB,CAAC,CAAC;IAExE,MAAM,IAAAC,oDAA0B,EAACvB,MAAM,CAAC;IAExC,MAAMwB,IAAI,GAAGN,KAAK,CAACO,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAEpC;IACD,IAAAC,cAAM,EAACC,MAAM,CAACC,IAAI,CAACL,IAAI,CAACM,KAAK,CAAC,CAAC,CAACC,OAAO,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACpE,MAAM,IAAAJ,cAAM,EAAC1B,QAAQ,CAAC,MAAMuB,IAAI,CAACQ,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAACC,QAAQ,CAACC,IAAI,CAAC,iBAAiB,CAAC;IACzF,MAAM,IAAAP,cAAM,EAAC1B,QAAQ,CAAC,MAAMuB,IAAI,CAACQ,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAACC,QAAQ,CAACC,IAAI,CACjE,IAAAC,0BAAkB,EAACnC,MAAM,CAAC,CAACoC,IAC/B,CAAC;EACL,CAAC,CAAC;EAEF,IAAAnB,UAAE,EAAC,wDAAwD,EAAE,YAAY;IACrE,MAAMC,KAAK,GAAGH,UAAE,CACXI,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,OAAO,CAAC,CACnCgB,qBAAqB,CAAC,IAAIC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAC1CC,qBAAqB,CAAC,CAAC;IAC5B,MAAMC,SAAS,GAAGzB,UAAE,CAACI,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,WAAW,CAAC,CAACC,iBAAiB,CAAC,CAAC;IAEhF,MAAM,IAAAC,oDAA0B,EAACvB,MAAM,CAAC;IAExC,IAAA2B,cAAM,EAACT,KAAK,CAAC,CAACuB,qBAAqB,CAAC,CAAC,CAAC;IACtC,IAAAd,cAAM,EAACa,SAAS,CAAC,CAACE,GAAG,CAACC,gBAAgB,CAAC,CAAC;IACxC,MAAMnB,IAAI,GAAGN,KAAK,CAACO,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAEpC;IACD,IAAAC,cAAM,EAACC,MAAM,CAACC,IAAI,CAACL,IAAI,CAACM,KAAK,CAAC,CAAC,CAACC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;IACvD,MAAM,IAAAJ,cAAM,EAAC1B,QAAQ,CAAC,MAAMuB,IAAI,CAACQ,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAACC,QAAQ,CAACC,IAAI,CAAC,iBAAiB,CAAC;EAC7F,CAAC,CAAC;EAEF,IAAAjB,UAAE,EAAC,oDAAoD,EAAE,YAAY;IACjEF,UAAE,CAACI,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,OAAO,CAAC,CAACuB,iBAAiB,CAAC,IAAIN,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7E,MAAME,SAAS,GAAGzB,UAAE,CAACI,KAAK,CAACC,SAAS,CAACC,SAAS,EAAE,WAAW,CAAC,CAACC,iBAAiB,CAAC,CAAC;IAEhF,MAAM,IAAAC,oDAA0B,EAACvB,MAAM,CAAC;IAExC,IAAA2B,cAAM,EAACa,SAAS,CAAC,CAACK,oBAAoB,CAAC,iBAAiB,CAAC;EAC7D,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
|
|
@@ -17,6 +17,8 @@ const SharingContextMenu = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
17
17
|
link,
|
|
18
18
|
children,
|
|
19
19
|
shouldEnableKeyboardHighlighting,
|
|
20
|
+
shouldShowCallingCodeAction = true,
|
|
21
|
+
shouldShowCopyAction = true,
|
|
20
22
|
...contextMenuProps
|
|
21
23
|
}, ref) => {
|
|
22
24
|
const isTouch = (0, _environment.useIsTouch)();
|
|
@@ -63,12 +65,12 @@ const SharingContextMenu = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
63
65
|
break;
|
|
64
66
|
}
|
|
65
67
|
}, [link, isTouch]);
|
|
66
|
-
const contextMenuItems = [{
|
|
68
|
+
const contextMenuItems = [...(shouldShowCopyAction ? [{
|
|
67
69
|
icons: ['fa fa-copy'],
|
|
68
70
|
key: 'copy',
|
|
69
71
|
onClick: () => handleShare('copy'),
|
|
70
72
|
text: 'Zwischenablage'
|
|
71
|
-
}, {
|
|
73
|
+
}] : []), {
|
|
72
74
|
icons: ['fa-solid fa-brands fa-whatsapp'],
|
|
73
75
|
key: 'whatsapp',
|
|
74
76
|
onClick: () => handleShare('whatsapp'),
|
|
@@ -88,21 +90,18 @@ const SharingContextMenu = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
88
90
|
key: 'mail',
|
|
89
91
|
onClick: () => handleShare('mail'),
|
|
90
92
|
text: 'Mail'
|
|
91
|
-
}, {
|
|
93
|
+
}, ...(shouldShowCallingCodeAction ? [{
|
|
92
94
|
icons: ['fa fa-qrcode'],
|
|
93
95
|
key: 'callingCode',
|
|
94
96
|
onClick: handleImageDownload,
|
|
95
97
|
text: 'Calling Code herunterladen'
|
|
96
|
-
}];
|
|
97
|
-
return (
|
|
98
|
-
|
|
98
|
+
}] : [])];
|
|
99
|
+
return /*#__PURE__*/_react.default.createElement(_ContextMenu.default, _extends({
|
|
100
|
+
items: contextMenuItems,
|
|
101
|
+
ref: ref,
|
|
102
|
+
shouldEnableKeyboardHighlighting: shouldEnableKeyboardHighlighting
|
|
99
103
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
100
|
-
|
|
101
|
-
items: contextMenuItems,
|
|
102
|
-
ref: ref,
|
|
103
|
-
shouldEnableKeyboardHighlighting: shouldEnableKeyboardHighlighting
|
|
104
|
-
}, contextMenuProps), children)
|
|
105
|
-
);
|
|
104
|
+
}, contextMenuProps), children);
|
|
106
105
|
});
|
|
107
106
|
SharingContextMenu.displayName = 'SharingContextMenu';
|
|
108
107
|
var _default = exports.default = SharingContextMenu;
|