@chayns-components/core 5.5.12 → 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.
Files changed (53) hide show
  1. package/AGENTS.md +21 -0
  2. package/lib/cjs/components/copyable-content/CopyableContent.js +27 -12
  3. package/lib/cjs/components/copyable-content/CopyableContent.js.map +1 -1
  4. package/lib/cjs/components/copyable-content/CopyableContent.styles.js +14 -8
  5. package/lib/cjs/components/copyable-content/CopyableContent.styles.js.map +1 -1
  6. package/lib/cjs/components/copyable-content/CopyableContent.test.js +26 -17
  7. package/lib/cjs/components/copyable-content/CopyableContent.test.js.map +1 -1
  8. package/lib/cjs/components/copyable-content/CopyableContent.types.js +15 -0
  9. package/lib/cjs/components/copyable-content/CopyableContent.types.js.map +1 -0
  10. package/lib/cjs/components/copyable-content/copyableContentClipboard.js +45 -28
  11. package/lib/cjs/components/copyable-content/copyableContentClipboard.js.map +1 -1
  12. package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js +13 -11
  13. package/lib/cjs/components/copyable-content/copyableContentClipboard.test.js.map +1 -1
  14. package/lib/cjs/components/sharing-context-menu/SharingContextMenu.js +11 -12
  15. package/lib/cjs/components/sharing-context-menu/SharingContextMenu.js.map +1 -1
  16. package/lib/cjs/components/sharing-context-menu/SharingContextMenu.test.js +48 -0
  17. package/lib/cjs/components/sharing-context-menu/SharingContextMenu.test.js.map +1 -0
  18. package/lib/cjs/constants/textStrings.js +0 -4
  19. package/lib/cjs/constants/textStrings.js.map +1 -1
  20. package/lib/cjs/index.js +7 -1
  21. package/lib/cjs/index.js.map +1 -1
  22. package/lib/cjs/utils/sharingBar.js +14 -17
  23. package/lib/cjs/utils/sharingBar.js.map +1 -1
  24. package/lib/esm/components/copyable-content/CopyableContent.js +23 -13
  25. package/lib/esm/components/copyable-content/CopyableContent.js.map +1 -1
  26. package/lib/esm/components/copyable-content/CopyableContent.styles.js +14 -8
  27. package/lib/esm/components/copyable-content/CopyableContent.styles.js.map +1 -1
  28. package/lib/esm/components/copyable-content/CopyableContent.test.js +24 -16
  29. package/lib/esm/components/copyable-content/CopyableContent.test.js.map +1 -1
  30. package/lib/esm/components/copyable-content/CopyableContent.types.js +9 -0
  31. package/lib/esm/components/copyable-content/CopyableContent.types.js.map +1 -0
  32. package/lib/esm/components/copyable-content/copyableContentClipboard.js +41 -28
  33. package/lib/esm/components/copyable-content/copyableContentClipboard.js.map +1 -1
  34. package/lib/esm/components/copyable-content/copyableContentClipboard.test.js +13 -11
  35. package/lib/esm/components/copyable-content/copyableContentClipboard.test.js.map +1 -1
  36. package/lib/esm/components/sharing-context-menu/SharingContextMenu.js +11 -12
  37. package/lib/esm/components/sharing-context-menu/SharingContextMenu.js.map +1 -1
  38. package/lib/esm/components/sharing-context-menu/SharingContextMenu.test.js +45 -0
  39. package/lib/esm/components/sharing-context-menu/SharingContextMenu.test.js.map +1 -0
  40. package/lib/esm/constants/textStrings.js +0 -4
  41. package/lib/esm/constants/textStrings.js.map +1 -1
  42. package/lib/esm/index.js +1 -1
  43. package/lib/esm/index.js.map +1 -1
  44. package/lib/esm/utils/sharingBar.js +15 -18
  45. package/lib/esm/utils/sharingBar.js.map +1 -1
  46. package/lib/types/components/copyable-content/CopyableContent.d.ts +6 -1
  47. package/lib/types/components/copyable-content/CopyableContent.styles.d.ts +7 -2
  48. package/lib/types/components/copyable-content/CopyableContent.types.d.ts +7 -0
  49. package/lib/types/components/sharing-context-menu/SharingContextMenu.d.ts +16 -0
  50. package/lib/types/constants/textStrings.d.ts +0 -4
  51. package/lib/types/index.d.ts +1 -1
  52. package/package.json +5 -7
  53. package/LICENSE +0 -21
package/AGENTS.md CHANGED
@@ -1533,6 +1533,27 @@ import { CopyableContent } from '@chayns-components/core';
1533
1533
  />
1534
1534
  ```
1535
1535
 
1536
+ #### Chat Appearance
1537
+
1538
+ ```tsx
1539
+ <CopyableContent
1540
+ appearance={CopyableContentAppearance.Chat}
1541
+ content={`# Projekt-Update: Sommerfest
1542
+
1543
+ Die Vorbereitungen für das Sommerfest gehen in die letzte Runde. Das Organisationsteam hat die Rückmeldungen aus den einzelnen Gruppen zusammengeführt und den Ablauf für den Nachmittag angepasst.
1544
+
1545
+ ## Was bereits feststeht
1546
+
1547
+ - Der Aufbau beginnt am Freitag um 16:30 Uhr am Bürgerhaus.
1548
+ - Für Kinder gibt es eine Kreativstation, eine kleine Rallye und einen ruhigen Rückzugsbereich.
1549
+ - Getränke und vegetarische Speisen werden vor Ort angeboten.
1550
+
1551
+ > Bitte gebt Rückmeldung, falls ihr beim Aufbau helfen könnt oder besondere Anforderungen an die Verpflegung habt.
1552
+
1553
+ Weitere Informationen stehen im [gemeinsamen Ablaufplan](https://example.com/veranstaltungen/sommerfest-2026/ablauf-und-helferinnen).`}
1554
+ />
1555
+ ```
1556
+
1536
1557
  #### Nested Scroll Container
1537
1558
 
1538
1559
  ```tsx
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "CopyableContentAppearance", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _CopyableContent2.CopyableContentAppearance;
10
+ }
11
+ });
6
12
  exports.default = void 0;
7
13
  var _format = require("@chayns-components/format");
8
14
  var _textstring = require("@chayns-components/textstring");
@@ -13,24 +19,25 @@ var _useStickyActionState = require("../../hooks/useStickyActionState");
13
19
  var _SharingContextMenu = _interopRequireDefault(require("../sharing-context-menu/SharingContextMenu"));
14
20
  var _Icon = _interopRequireDefault(require("../icon/Icon"));
15
21
  var _CopyableContent = require("./CopyableContent.styles");
22
+ var _CopyableContent2 = require("./CopyableContent.types");
16
23
  var _copyableContentClipboard = require("./copyableContentClipboard");
17
24
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
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;
19
27
  const CopyableContent = ({
28
+ appearance = _CopyableContent2.CopyableContentAppearance.Default,
20
29
  content,
21
30
  children,
22
- copiedMessage,
23
31
  copyFailedMessage
24
32
  }) => {
25
33
  const rootRef = (0, _react.useRef)(null);
26
34
  const actionGroupRef = (0, _react.useRef)(null);
35
+ const copyFeedbackTimeoutRef = (0, _react.useRef)();
27
36
  const isActionGroupSticky = (0, _useStickyActionState.useStickyActionState)(rootRef, actionGroupRef);
37
+ const [hasCopied, setHasCopied] = (0, _react.useState)(false);
28
38
  const defaultCopyButtonText = (0, _textstring.useTextstringValue)({
29
39
  textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copy)
30
40
  });
31
- const defaultCopiedMessage = (0, _textstring.useTextstringValue)({
32
- textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copied)
33
- });
34
41
  const defaultCopyFailedMessage = (0, _textstring.useTextstringValue)({
35
42
  textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.copyFailed)
36
43
  });
@@ -38,15 +45,20 @@ const CopyableContent = ({
38
45
  textstring: (0, _textstring.ttsToITextString)(_textStrings.default.components.copyableContent.share)
39
46
  });
40
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
+ }, []);
41
58
  const handleCopy = (0, _react.useCallback)(async () => {
42
59
  try {
43
60
  await (0, _copyableContentClipboard.copyableContentToClipboard)(content);
44
- void (0, _chaynsApi.createDialog)({
45
- showCloseIcon: true,
46
- text: copiedMessage ?? defaultCopiedMessage,
47
- toastType: _chaynsApi.ToastType.SUCCESS,
48
- type: _chaynsApi.DialogType.TOAST
49
- }).open();
61
+ showCopyFeedback();
50
62
  } catch {
51
63
  void (0, _chaynsApi.createDialog)({
52
64
  showCloseIcon: true,
@@ -55,8 +67,9 @@ const CopyableContent = ({
55
67
  type: _chaynsApi.DialogType.TOAST
56
68
  }).open();
57
69
  }
58
- }, [content, copiedMessage, defaultCopiedMessage, defaultCopyFailedMessage, copyFailedMessage]);
70
+ }, [content, defaultCopyFailedMessage, copyFailedMessage, showCopyFeedback]);
59
71
  return /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContent, {
72
+ $appearance: appearance,
60
73
  className: "copyable-content",
61
74
  ref: rootRef
62
75
  }, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentActions, {
@@ -69,9 +82,11 @@ const CopyableContent = ({
69
82
  },
70
83
  type: "button"
71
84
  }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
72
- icons: ['fa-light fa-copy']
85
+ icons: hasCopied ? ['fa fa-check'] : ['fa-light fa-copy']
73
86
  })), /*#__PURE__*/_react.default.createElement(_SharingContextMenu.default, {
74
87
  link: content,
88
+ shouldShowCallingCodeAction: false,
89
+ shouldShowCopyAction: false,
75
90
  shouldUseDefaultTriggerStyles: false
76
91
  }, /*#__PURE__*/_react.default.createElement(_CopyableContent.StyledCopyableContentButton, {
77
92
  $isSticky: isActionGroupSticky,
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.js","names":["_format","require","_textstring","_chaynsApi","_react","_interopRequireWildcard","_textStrings","_interopRequireDefault","_useStickyActionState","_SharingContextMenu","_Icon","_CopyableContent","_copyableContentClipboard","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","CopyableContent","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","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 { copyableContentToClipboard } from './copyableContentClipboard';\n\nexport type CopyableContentProps = {\n content: string;\n children?: ReactNode;\n copiedMessage?: string;\n copyFailedMessage?: string;\n};\n\nconst CopyableContent: FC<CopyableContentProps> = ({\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 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"],"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":[]}
@@ -5,11 +5,24 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.StyledCopyableContentButton = exports.StyledCopyableContentBody = exports.StyledCopyableContentActions = exports.StyledCopyableContentActionGroup = exports.StyledCopyableContent = void 0;
7
7
  var _styledComponents = _interopRequireDefault(require("styled-components"));
8
+ var _CopyableContent = require("./CopyableContent.types");
8
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const getBackgroundColor = ({
11
+ $appearance,
12
+ theme
13
+ }) => {
14
+ if ($appearance === _CopyableContent.CopyableContentAppearance.Chat) {
15
+ return 'rgba(0, 0, 0, 0.2)';
16
+ }
17
+ const secondaryColor = theme['secondary-100-rgb'] ?? '255, 255, 255';
18
+ const opacity = theme.cardBackgroundOpacity ?? 1;
19
+ return `rgba(${secondaryColor}, ${opacity})`;
20
+ };
9
21
  const StyledCopyableContent = exports.StyledCopyableContent = _styledComponents.default.section`
10
22
  --copyable-content-action-size: 32px;
11
23
  --copyable-content-action-inset: 8px;
12
24
 
25
+ margin: 4px 0;
13
26
  min-width: 0;
14
27
  max-width: 100%;
15
28
  overflow-x: clip;
@@ -20,14 +33,7 @@ const StyledCopyableContent = exports.StyledCopyableContent = _styledComponents.
20
33
  border-radius: ${({
21
34
  theme
22
35
  }) => theme.cardBorderRadius}px;
23
- background-color: rgba(
24
- ${({
25
- theme
26
- }) => theme['secondary-100-rgb']},
27
- ${({
28
- theme
29
- }) => theme.cardBackgroundOpacity}
30
- );
36
+ background-color: ${getBackgroundColor};
31
37
  color: ${({
32
38
  theme
33
39
  }) => theme.text};
@@ -1 +1 @@
1
- {"version":3,"file":"CopyableContent.styles.js","names":["_styledComponents","_interopRequireDefault","require","e","__esModule","default","StyledCopyableContent","exports","styled","section","theme","cardBorderRadius","cardBackgroundOpacity","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';\n\ntype StyledCopyableContentProps = WithTheme<unknown>;\n\nexport const StyledCopyableContent = styled.section<StyledCopyableContentProps>`\n --copyable-content-action-size: 32px;\n --copyable-content-action-inset: 8px;\n\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: rgba(\n ${({ theme }) => theme['secondary-100-rgb']},\n ${({ theme }) => theme.cardBackgroundOpacity}\n );\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<StyledCopyableContentProps>`\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":";;;;;;AACA,IAAAA,iBAAA,GAAAC,sBAAA,CAAAC,OAAA;AAAuC,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAIhC,MAAMG,qBAAqB,GAAAC,OAAA,CAAAD,qBAAA,GAAGE,yBAAM,CAACC,OAAmC;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,CAAC;EAAEC;AAAM,CAAC,KAAKA,KAAK,CAACC,gBAAgB;AAC1D;AACA,UAAU,CAAC;EAAED;AAAM,CAAC,KAAKA,KAAK,CAAC,mBAAmB,CAAC;AACnD,UAAU,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACE,qBAAqB;AACpD;AACA,aAAa,CAAC;EAAEF;AAAM,CAAC,KAAKA,KAAK,CAACG,IAAI;AACtC,CAAC;AAMM,MAAMC,4BAA4B,GAAAP,OAAA,CAAAO,4BAAA,GAAGN,yBAAM,CAACO,GAAG;AACtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAEM,MAAMC,gCAAgC,GAAAT,OAAA,CAAAS,gCAAA,GAAGR,yBAAM,CAACO,GAAG;AAC1D;AACA;AACA;AACA,CAAC;AAEM,MAAME,2BAA2B,GAAAV,OAAA,CAAAU,2BAAA,GAAGT,yBAAM,CAACU,MAAwC;AAC1F;AACA,wBAAwB,CAAC;EAAEC,SAAS;EAAET;AAAM,CAAC,KAAMS,SAAS,GAAGT,KAAK,CAAC,KAAK,CAAC,GAAG,aAAc;AAC5F;AACA;AACA;AACA;AACA;AACA,wBAAwB,CAAC;EAAES,SAAS;EAAET;AAAM,CAAC,KAAMS,SAAS,GAAGT,KAAK,CAAC,KAAK,CAAC,GAAG,aAAc;AAC5F,aAAa,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACG,IAAI;AACtC,kBAAkB,CAAC;EAAEM;AAAU,CAAC,KAAMA,SAAS,GAAG,+BAA+B,GAAG,MAAO;AAC3F;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,CAAC;EAAET;AAAM,CAAC,KAAKA,KAAK,CAAC,UAAU,CAAC;AACjE,2CAA2C,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAAC,UAAU,CAAC;AAC3E;AACA;AACA;AACA;AACA;AACA;AACA;AACA,6BAA6B,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACU,qBAAqB;AACvE;AACA;AACA,CAAC;AAEM,MAAMC,yBAAyB,GAAAd,OAAA,CAAAc,yBAAA,GAAGb,yBAAM,CAACO,GAA+B;AAC/E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iBAAiB,CAAC;EAAEL;AAAM,CAAC,KAAKA,KAAK,CAACY,QAAQ;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iCAAiC,CAAC;EAAEZ;AAAM,CAAC,KAAKA,KAAK,CAAC,KAAK,CAAC;AAC5D;AACA;AACA;AACA,iBAAiB,CAAC;EAAEA;AAAM,CAAC,KAAKA,KAAK,CAACa,OAAO;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA,CAAC","ignoreList":[]}
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
- 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); } /* eslint-disable @typescript-eslint/no-unsafe-call */
8
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
9
+
9
10
  _vitest.vi.mock('../icon/Icon', () => ({
10
- default: () => /*#__PURE__*/_react2.default.createElement("span", null)
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
- }) => children
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 toast after a successful copy', async () => {
65
+ (0, _vitest.it)('shows a checkmark after a successful copy', async () => {
56
66
  _vitest.vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();
57
- (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
58
- content: "source",
59
- copiedMessage: "Done"
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)(async () => {
65
- const {
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
- (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_CopyableContent.default, {
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","_interopRequireWildcard","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","vi","mock","createElement","children","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","copiedMessage","then","toHaveBeenCalledWith","objectContaining","text","queryByRole"],"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: () => <span />,\n}));\n\nvi.mock('../sharing-context-menu/SharingContextMenu', () => ({\n default: ({ children }: { children: React.ReactNode }) => children,\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 toast after a successful copy', async () => {\n vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n render(<CopyableContent content=\"source\" copiedMessage=\"Done\" />);\n\n fireEvent.click(screen.getByRole('button', { name: 'Kopieren' }));\n\n await waitFor(async () => {\n const { createDialog } = await import('chayns-api');\n expect(createDialog).toHaveBeenCalledWith(expect.objectContaining({ text: 'Done' }));\n });\n expect(screen.queryByRole('status')).not.toBeInTheDocument();\n });\n\n it('offers sharing actions', () => {\n render(<CopyableContent content=\"source\" />);\n\n expect(screen.getByRole('button', { name: 'Teilen' })).toBeInTheDocument();\n });\n});\n"],"mappings":"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","ignoreList":[]}
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":[]}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CopyableContentAppearance = void 0;
7
+ /**
8
+ * Defines the visual surface used by CopyableContent.
9
+ */
10
+ let CopyableContentAppearance = exports.CopyableContentAppearance = /*#__PURE__*/function (CopyableContentAppearance) {
11
+ CopyableContentAppearance["Default"] = "default";
12
+ CopyableContentAppearance["Chat"] = "chat";
13
+ return CopyableContentAppearance;
14
+ }({});
15
+ //# sourceMappingURL=CopyableContent.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyableContent.types.js","names":["CopyableContentAppearance","exports"],"sources":["../../../../src/components/copyable-content/CopyableContent.types.ts"],"sourcesContent":["/**\n * Defines the visual surface used by CopyableContent.\n */\nexport enum CopyableContentAppearance {\n Default = 'default',\n Chat = 'chat',\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AAFA,IAGYA,yBAAyB,GAAAC,OAAA,CAAAD,yBAAA,0BAAzBA,yBAAyB;EAAzBA,yBAAyB;EAAzBA,yBAAyB;EAAA,OAAzBA,yBAAyB;AAAA","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 RICH_MIME_TYPES = ['text/plain', 'text/markdown', 'text/html'];
9
- const createClipboardItem = data => new ClipboardItem(data);
10
- const supportsRichMimeTypes = () => {
11
- if (typeof ClipboardItem === 'undefined' || typeof ClipboardItem.supports !== 'function') {
12
- return true;
13
- }
14
- return RICH_MIME_TYPES.every(mimeType => ClipboardItem.supports(mimeType));
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 source => {
44
+ const copyPlainText = async plainText => {
17
45
  if (typeof ClipboardItem !== 'undefined') {
18
46
  try {
19
- await navigator.clipboard.write([createClipboardItem({
20
- 'text/plain': new Blob([source], {
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(source);
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' || !supportsRichMimeTypes()) {
36
- await copyPlainText(source);
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 navigator.clipboard.write([createClipboardItem({
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
- await copyPlainText(source);
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","RICH_MIME_TYPES","createClipboardItem","data","ClipboardItem","supportsRichMimeTypes","supports","every","mimeType","copyPlainText","source","navigator","clipboard","write","Blob","type","writeText","copyableContentToClipboard","Error","html","formatStringToHtml","exports"],"sources":["../../../../src/components/copyable-content/copyableContentClipboard.ts"],"sourcesContent":["import { formatStringToHtml } from '@chayns-components/format';\n\nconst RICH_MIME_TYPES = ['text/plain', 'text/markdown', 'text/html'] as const;\n\nconst createClipboardItem = (data: Record<string, Blob>) => new ClipboardItem(data);\n\nconst supportsRichMimeTypes = () => {\n if (typeof ClipboardItem === 'undefined' || typeof ClipboardItem.supports !== 'function') {\n return true;\n }\n\n return RICH_MIME_TYPES.every((mimeType) => ClipboardItem.supports(mimeType));\n};\n\nconst copyPlainText = async (source: string) => {\n if (typeof ClipboardItem !== 'undefined') {\n try {\n await navigator.clipboard.write([\n createClipboardItem({ 'text/plain': new Blob([source], { type: 'text/plain' }) }),\n ]);\n return;\n } catch {\n // Use writeText as the final browser-compatible fallback.\n }\n }\n\n await navigator.clipboard.writeText(source);\n};\n\nexport const copyableContentToClipboard = async (source: string) => {\n if (!navigator.clipboard) {\n throw new Error('Clipboard API is not available.');\n }\n\n if (typeof ClipboardItem === 'undefined' || !supportsRichMimeTypes()) {\n await copyPlainText(source);\n return;\n }\n\n const { html } = formatStringToHtml(source);\n\n try {\n await navigator.clipboard.write([\n createClipboardItem({\n 'text/plain': new Blob([source], { type: 'text/plain' }),\n 'text/markdown': new Blob([source], { type: 'text/markdown' }),\n 'text/html': new Blob([html], { type: 'text/html' }),\n }),\n ]);\n } catch {\n await copyPlainText(source);\n }\n};\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA,MAAMC,eAAe,GAAG,CAAC,YAAY,EAAE,eAAe,EAAE,WAAW,CAAU;AAE7E,MAAMC,mBAAmB,GAAIC,IAA0B,IAAK,IAAIC,aAAa,CAACD,IAAI,CAAC;AAEnF,MAAME,qBAAqB,GAAGA,CAAA,KAAM;EAChC,IAAI,OAAOD,aAAa,KAAK,WAAW,IAAI,OAAOA,aAAa,CAACE,QAAQ,KAAK,UAAU,EAAE;IACtF,OAAO,IAAI;EACf;EAEA,OAAOL,eAAe,CAACM,KAAK,CAAEC,QAAQ,IAAKJ,aAAa,CAACE,QAAQ,CAACE,QAAQ,CAAC,CAAC;AAChF,CAAC;AAED,MAAMC,aAAa,GAAG,MAAOC,MAAc,IAAK;EAC5C,IAAI,OAAON,aAAa,KAAK,WAAW,EAAE;IACtC,IAAI;MACA,MAAMO,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAC5BX,mBAAmB,CAAC;QAAE,YAAY,EAAE,IAAIY,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;UAAEK,IAAI,EAAE;QAAa,CAAC;MAAE,CAAC,CAAC,CACpF,CAAC;MACF;IACJ,CAAC,CAAC,MAAM;MACJ;IAAA;EAER;EAEA,MAAMJ,SAAS,CAACC,SAAS,CAACI,SAAS,CAACN,MAAM,CAAC;AAC/C,CAAC;AAEM,MAAMO,0BAA0B,GAAG,MAAOP,MAAc,IAAK;EAChE,IAAI,CAACC,SAAS,CAACC,SAAS,EAAE;IACtB,MAAM,IAAIM,KAAK,CAAC,iCAAiC,CAAC;EACtD;EAEA,IAAI,OAAOd,aAAa,KAAK,WAAW,IAAI,CAACC,qBAAqB,CAAC,CAAC,EAAE;IAClE,MAAMI,aAAa,CAACC,MAAM,CAAC;IAC3B;EACJ;EAEA,MAAM;IAAES;EAAK,CAAC,GAAG,IAAAC,0BAAkB,EAACV,MAAM,CAAC;EAE3C,IAAI;IACA,MAAMC,SAAS,CAACC,SAAS,CAACC,KAAK,CAAC,CAC5BX,mBAAmB,CAAC;MAChB,YAAY,EAAE,IAAIY,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;QAAEK,IAAI,EAAE;MAAa,CAAC,CAAC;MACxD,eAAe,EAAE,IAAID,IAAI,CAAC,CAACJ,MAAM,CAAC,EAAE;QAAEK,IAAI,EAAE;MAAgB,CAAC,CAAC;MAC9D,WAAW,EAAE,IAAID,IAAI,CAAC,CAACK,IAAI,CAAC,EAAE;QAAEJ,IAAI,EAAE;MAAY,CAAC;IACvD,CAAC,CAAC,CACL,CAAC;EACN,CAAC,CAAC,MAAM;IACJ,MAAMN,aAAa,CAACC,MAAM,CAAC;EAC/B;AACJ,CAAC;AAACW,OAAA,CAAAJ,0BAAA,GAAAA,0BAAA","ignoreList":[]}
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 exact source as plain text and markdown with safe HTML', async () => {
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
- await (0, _vitest.expect)(readBlob(await item.getType('text/plain'))).resolves.toBe(source);
22
- await (0, _vitest.expect)(readBlob(await item.getType('text/markdown'))).resolves.toBe(source);
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 when rich MIME types are unsupported', async () => {
26
- _vitest.vi.stubGlobal('ClipboardItem', Object.assign(class {}, {
27
- supports: () => false
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(1);
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(source);
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","stubGlobal","Object","assign","supports","toHaveBeenCalledTimes","mockRejectedValue","Error","writeText","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 exact source as plain text and markdown with safe HTML', 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 await expect(readBlob(await item.getType('text/plain'))).resolves.toBe(source);\n await expect(readBlob(await item.getType('text/markdown'))).resolves.toBe(source);\n await expect(readBlob(await item.getType('text/html'))).resolves.toBe(\n formatStringToHtml(source).html,\n );\n });\n\n it('uses a plain ClipboardItem when rich MIME types are unsupported', async () => {\n vi.stubGlobal('ClipboardItem', Object.assign(class {}, { supports: () => false }));\n const write = vi.spyOn(navigator.clipboard, 'write').mockResolvedValue();\n\n await copyableContentToClipboard(source);\n\n expect(write).toHaveBeenCalledTimes(1);\n });\n\n it('uses writeText after 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(source);\n });\n});\n"],"mappings":"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","ignoreList":[]}
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":"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","ignoreList":[]}