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
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-only

import { ArrowLeftIcon } from "@radix-ui/react-icons";
import {
	Box,
	type BoxProps,
	Card,
	Flex,
	Grid,
	Heading,
	IconButton,
	Inset,
	ScrollArea,
	Text,
	Theme,
} from "@radix-ui/themes";
import { type FC, type ReactNode } from "react";

import * as CopyrightNotice from "../../components/CopyrightNotice.ts";
import { Logo } from "../../components/Logo.tsx";

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

export interface LayoutProps extends Pick<BoxProps, "className" | "children" | "style"> {
	title: ReactNode;

	onBack?(): void;
}

export const Layout: FC<LayoutProps> = ({
	className,
	children,
	title,
	onBack,
	...rest
}) => {
	return (
		<Grid
			className={`${css.bgGrid} ${className || ""}`}
			inset="0"
			height="100%"
			p={{ initial: "2", md: "4" }}
			pb="2"
			columns={{ initial: "1", md: "minmax(0, 1fr) 30rem" }}
			rows={{
				initial: "max-content minmax(0, 1fr) max-content",
				md: "minmax(0, 1fr) max-content",
			}}
			gap="2"
			{...rest}
		>
			<Text asChild size="6">
				<Flex justify="center" align="center" py="1">
					<Logo />
				</Flex>
			</Text>
			<Box asChild gridColumn="-2 / -1">
				<Card>
					<Theme asChild panelBackground="solid">
						<Flex height="100%" direction="column" gap="5" p={{ initial: "0", md: "1" }}>
							<Flex align="center">
								{onBack && (
									<IconButton
										className={css.back}
										variant="ghost"
										onClick={() => void onBack()}
									>
										<ArrowLeftIcon />
									</IconButton>
								)}
								<Box className={css.title} asChild flexGrow="1" flexShrink="1">
									<Heading size="4" align="center">
										{title}
									</Heading>
								</Box>
							</Flex>
							<Inset className={css.body} data-root={onBack ? "" : undefined}>
								<ScrollArea>
									<Box px={{ initial: "2", xs: "3" }} pb="3" pt="0">
										{children}
									</Box>
								</ScrollArea>
							</Inset>
						</Flex>
					</Theme>
				</Card>
			</Box>
			<Flex gridColumn="-2 / -1" asChild align="center" justify="end" gap="4">
				<Text size="1" color="gray">
					<CopyrightNotice.ThirdParty />
					<CopyrightNotice.FirstParty />
				</Text>
			</Flex>
		</Grid>
	);
};