yamori

有給休暇計算を主目的とした簡易勤怠管理システム

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-only

import { CopyIcon } from "@radix-ui/react-icons";
import { Box, type BoxProps, IconButton, Text, Tooltip } from "@radix-ui/themes";
import { type FC, useMemo, useRef, useState } from "react";

import css from "./CopyableText.module.css";

export interface CopyableTextProps extends Omit<BoxProps, "children"> {
	children: string;

	mono?: boolean;
}

export const CopyableText: FC<CopyableTextProps> = ({
	children,
	mono = false,
	...rest
}) => {
	const ref = useRef<HTMLTextAreaElement>(null);

	// TODO: 結果をユーザに通知する
	const [_clipboardWriteResult, setClipboardWriteResult] = useState<
		{ ok: false; error: unknown } | { ok: true } | null
	>();

	const rows = useMemo(() => {
		const lines = children.split("\n");

		return Math.min(10, Math.max(3, lines.length));
	}, [children]);

	return (
		<Box position="relative" {...rest}>
			<Box asChild p="2">
				<Text asChild size="2">
					<textarea
						ref={ref}
						className={css.textarea}
						value={children}
						readOnly
						rows={rows}
						style={{
							fontFamily: mono ? "var(--code-font-family)" : "var(--default-font-family)",
						}}
					/>
				</Text>
			</Box>
			<Tooltip content="クリップボードにコピー">
				<Box asChild position="absolute" bottom="2" right="2">
					<IconButton
						size="1"
						variant="surface"
						color="gray"
						onClick={async (event) => {
							event.preventDefault();
							event.stopPropagation();

							try {
								await navigator.clipboard.writeText(children);

								setClipboardWriteResult({ ok: true });
							} catch (error) {
								setClipboardWriteResult({ ok: false, error });
							}
						}}
					>
						<CopyIcon />
					</IconButton>
				</Box>
			</Tooltip>
		</Box>
	);
};