From 4f2944a9746f9be96149239d187f6c5f31f165db Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:31:56 +0700
Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=94=A7=20chore:=20install=20`date-f?=
=?UTF-8?q?ns`=20and=20`react-calendar`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/frontend/package.json | 3 +-
yarn.lock | 62 ++++++++++++++++++++++++++++++----
2 files changed, 57 insertions(+), 8 deletions(-)
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index a2a77ba..4973668 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -13,13 +13,14 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"assert": "^2.1.0",
- "date-fns": "^3.0.1",
+ "date-fns": "^3.3.1",
"downshift": "^8.2.3",
"eslint-config-react-app": "^7.0.1",
"gql-client": "^1.0.0",
"luxon": "^3.4.4",
"octokit": "^3.1.2",
"react": "^18.2.0",
+ "react-calendar": "^4.8.0",
"react-code-blocks": "^0.1.6",
"react-day-picker": "^8.9.1",
"react-dom": "^18.2.0",
diff --git a/yarn.lock b/yarn.lock
index e471349..8845c97 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3910,6 +3910,18 @@
dependencies:
"@types/node" "*"
+"@types/lodash.memoize@^4.1.7":
+ version "4.1.9"
+ resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b"
+ integrity sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash@*":
+ version "4.14.202"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
+ integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
+
"@types/long@^4.0.0", "@types/long@^4.0.1":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
@@ -4687,6 +4699,11 @@
"@webassemblyjs/ast" "1.11.6"
"@xtuc/long" "4.2.2"
+"@wojtekmaj/date-utils@^1.1.3":
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz#c3cd67177ac781cfa5736219d702a55a2aea5f2b"
+ integrity sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww==
+
"@wry/caches@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52"
@@ -6144,6 +6161,11 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+clsx@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
+ integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
+
cmd-shim@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d"
@@ -6821,10 +6843,10 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
-date-fns@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.0.1.tgz#a95b3e8296e72cf57c99819f37679aa27ca65ec4"
- integrity sha512-cr9igCUa0QSqgAMj7JOrYTY6Nh1rmyGrFDko7ADqfmaQqP/I2N4rlfrLl7AWuzDaoIpz6MNjoEcTPzgZYIrhnA==
+date-fns@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.3.1.tgz#7581daca0892d139736697717a168afbb908cfed"
+ integrity sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==
dateformat@^3.0.3:
version "3.0.3"
@@ -8720,6 +8742,14 @@ get-tsconfig@^4.7.0:
dependencies:
resolve-pkg-maps "^1.0.0"
+get-user-locale@^2.2.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.1.tgz#fc7319429c8a70fac01b3b2a0b08b0c71c1d3fe2"
+ integrity sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ==
+ dependencies:
+ "@types/lodash.memoize" "^4.1.7"
+ lodash.memoize "^4.1.1"
+
git-raw-commits@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-3.0.0.tgz#5432f053a9744f67e8db03dbc48add81252cfdeb"
@@ -11012,7 +11042,7 @@ lodash.isstring@^4.0.1:
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
-lodash.memoize@^4.1.2:
+lodash.memoize@^4.1.1, lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz"
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
@@ -11065,7 +11095,7 @@ long@^5.2.0:
resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1"
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
-loose-envify@^1.1.0, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -13281,7 +13311,7 @@ promzard@^1.0.0:
dependencies:
read "^2.0.0"
-prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@15.8.1, prop-types@^15.6.0, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -13440,6 +13470,17 @@ react-app-polyfill@^3.0.0:
regenerator-runtime "^0.13.9"
whatwg-fetch "^3.6.2"
+react-calendar@^4.8.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/react-calendar/-/react-calendar-4.8.0.tgz#61edbba6d17e7ef8a8012de9143b5e5ff41104c8"
+ integrity sha512-qFgwo+p58sgv1QYMI1oGNaop90eJVKuHTZ3ZgBfrrpUb+9cAexxsKat0sAszgsizPMVo7vOXedV7Lqa0GQGMvA==
+ dependencies:
+ "@wojtekmaj/date-utils" "^1.1.3"
+ clsx "^2.0.0"
+ get-user-locale "^2.2.1"
+ prop-types "^15.6.0"
+ warning "^4.0.0"
+
react-code-blocks@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/react-code-blocks/-/react-code-blocks-0.1.6.tgz#ec64e7899223d3e910eb916465a66d95ce1ae1b2"
@@ -15880,6 +15921,13 @@ walker@^1.0.7:
dependencies:
makeerror "1.0.12"
+warning@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz"
From 0a5a1b8c5550c8bca0acaa8a47673e19c60f54e5 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:32:35 +0700
Subject: [PATCH 02/13] =?UTF-8?q?=F0=9F=94=A7=20chore:=20add=20vscode=20se?=
=?UTF-8?q?ttings=20to=20enable=20tailwind=20variant=20intellisense?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.vscode/settings.json | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 .vscode/settings.json
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..f3e0823
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+ // IntelliSense for taiwind variants
+ "tailwindCSS.experimental.classRegex": [
+ ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
+ ]
+}
From cc97ddff9d828592d38827a981173ffcec6ce83b Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:33:12 +0700
Subject: [PATCH 03/13] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20create=20che?=
=?UTF-8?q?vron=20left,=20right,=20and=20grabbed=20horizontal=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../CustomIcon/ChevronGrabberHorizontal.tsx | 20 ++++++++++++++++++
.../shared/CustomIcon/ChevronLeft.tsx | 21 +++++++++++++++++++
.../shared/CustomIcon/ChevronRight.tsx | 21 +++++++++++++++++++
.../src/components/shared/CustomIcon/index.ts | 3 +++
4 files changed, 65 insertions(+)
create mode 100644 packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx
create mode 100644 packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx
create mode 100644 packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx
diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx
new file mode 100644
index 0000000..b62f79d
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const ChevronGrabberHorizontal = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx
new file mode 100644
index 0000000..f9c4885
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const ChevronLeft = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx
new file mode 100644
index 0000000..75da005
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const ChevronRight = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts
index d50d942..e4b5103 100644
--- a/packages/frontend/src/components/shared/CustomIcon/index.ts
+++ b/packages/frontend/src/components/shared/CustomIcon/index.ts
@@ -1,2 +1,5 @@
export * from './PlusIcon';
export * from './CustomIcon';
+export * from './ChevronGrabberHorizontal';
+export * from './ChevronLeft';
+export * from './ChevronRight';
From ad7dd1920a3a0f74d3f2520a6a284b49da7a7b97 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:33:26 +0700
Subject: [PATCH 04/13] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20create=20cal?=
=?UTF-8?q?endar=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/shared/Calendar/Calendar.css | 128 ++++++++
.../shared/Calendar/Calendar.theme.ts | 49 +++
.../components/shared/Calendar/Calendar.tsx | 292 ++++++++++++++++++
.../src/components/shared/Calendar/index.ts | 1 +
4 files changed, 470 insertions(+)
create mode 100644 packages/frontend/src/components/shared/Calendar/Calendar.css
create mode 100644 packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
create mode 100644 packages/frontend/src/components/shared/Calendar/Calendar.tsx
create mode 100644 packages/frontend/src/components/shared/Calendar/index.ts
diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.css b/packages/frontend/src/components/shared/Calendar/Calendar.css
new file mode 100644
index 0000000..85ce418
--- /dev/null
+++ b/packages/frontend/src/components/shared/Calendar/Calendar.css
@@ -0,0 +1,128 @@
+/* React Calendar */
+.react-calendar {
+ @apply border-none font-sans;
+}
+
+/* Weekdays -- START */
+.react-calendar__month-view__weekdays {
+ @apply p-0 flex items-center justify-center;
+}
+
+.react-calendar__month-view__weekdays__weekday {
+ @apply h-8 w-12 flex items-center justify-center p-0 font-medium text-xs text-elements-disabled mb-2;
+}
+
+abbr[title] {
+ text-decoration: none;
+}
+/* Weekdays -- END */
+
+/* Days -- START */
+.react-calendar__month-view__days {
+ @apply p-0 gap-0;
+}
+
+.react-calendar__month-view__days__day--neighboringMonth {
+ @apply !text-elements-disabled;
+}
+
+.react-calendar__month-view__days__day--neighboringMonth:hover {
+ @apply !text-elements-disabled !bg-transparent;
+}
+
+.react-calendar__month-view__days__day--neighboringMonth {
+ @apply !text-elements-disabled !bg-transparent;
+}
+
+/* For weekend days */
+.react-calendar__month-view__days__day--weekend {
+ /* color: ${colors.grey[950]} !important; */
+}
+
+.react-calendar__tile {
+ @apply h-12 w-12 text-elements-high-em;
+}
+
+.react-calendar__tile:hover {
+ @apply bg-base-bg-emphasized rounded-lg;
+}
+
+.react-calendar__tile:focus-visible {
+ @apply bg-base-bg-emphasized rounded-lg focus-ring z-10;
+}
+
+.react-calendar__tile--now {
+ @apply bg-base-bg-emphasized text-elements-high-em rounded-lg;
+}
+
+.react-calendar__tile--now:hover {
+ @apply bg-base-bg-emphasized text-elements-high-em rounded-lg;
+}
+
+.react-calendar__tile--now:focus-visible {
+ @apply bg-base-bg-emphasized text-elements-high-em rounded-lg focus-ring;
+}
+
+.react-calendar__tile--active {
+ @apply bg-controls-primary text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--active:hover {
+ @apply bg-controls-primary-hovered;
+}
+
+.react-calendar__tile--active:focus-visible {
+ @apply bg-controls-primary-hovered focus-ring;
+}
+
+/* Range -- START */
+.react-calendar__tile--range {
+ @apply bg-controls-secondary text-elements-on-secondary rounded-none;
+}
+
+.react-calendar__tile--range:hover {
+ @apply bg-controls-secondary-hovered text-elements-on-secondary rounded-none;
+}
+
+.react-calendar__tile--range:focus-visible {
+ @apply bg-controls-secondary-hovered text-elements-on-secondary rounded-lg;
+}
+
+.react-calendar__tile--rangeStart {
+ @apply bg-controls-primary text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--rangeStart:hover {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--rangeStart:focus-visible {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring;
+}
+
+.react-calendar__tile--rangeEnd {
+ @apply bg-controls-primary text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--rangeEnd:hover {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--rangeEnd:focus-visible {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring;
+}
+/* Range -- END */
+/* Days -- END */
+
+/* Months -- START */
+.react-calendar__tile--hasActive {
+ @apply bg-controls-primary text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--hasActive:hover {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg;
+}
+
+.react-calendar__tile--hasActive:focus-visible {
+ @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring;
+}
diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
new file mode 100644
index 0000000..e39e592
--- /dev/null
+++ b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
@@ -0,0 +1,49 @@
+import { VariantProps, tv } from 'tailwind-variants';
+
+export const calendarTheme = tv({
+ slots: {
+ wrapper: [
+ 'max-w-[352px]',
+ 'bg-surface-floating',
+ 'shadow-calendar',
+ 'rounded-xl',
+ ],
+ calendar: ['flex', 'flex-col', 'py-2', 'px-2', 'gap-2'],
+ navigation: [
+ 'flex',
+ 'items-center',
+ 'justify-between',
+ 'gap-3',
+ 'py-2.5',
+ 'px-1',
+ ],
+ dropdowns: ['flex', 'items-center', 'justify-center', 'gap-1.5', 'flex-1'],
+ dropdown: [
+ 'flex',
+ 'items-center',
+ 'gap-2',
+ 'px-2',
+ 'py-2',
+ 'rounded-lg',
+ 'border',
+ 'border-border-interactive',
+ 'text-elements-high-em',
+ 'shadow-field',
+ 'bg-white',
+ 'hover:bg-base-bg-alternate',
+ 'focus-visible:bg-base-bg-alternate',
+ ],
+ footer: [
+ 'flex',
+ 'items-center',
+ 'justify-end',
+ 'py-3',
+ 'px-2',
+ 'gap-3',
+ 'border-t',
+ 'border-border-separator',
+ ],
+ },
+});
+
+export type CalendarTheme = VariantProps;
diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.tsx b/packages/frontend/src/components/shared/Calendar/Calendar.tsx
new file mode 100644
index 0000000..737eee7
--- /dev/null
+++ b/packages/frontend/src/components/shared/Calendar/Calendar.tsx
@@ -0,0 +1,292 @@
+import React, {
+ ComponentPropsWithRef,
+ MouseEvent,
+ ReactNode,
+ useCallback,
+ useState,
+} from 'react';
+import {
+ Calendar as ReactCalendar,
+ CalendarProps as ReactCalendarProps,
+} from 'react-calendar';
+import { Value } from 'react-calendar/dist/cjs/shared/types';
+import { CalendarTheme, calendarTheme } from './Calendar.theme';
+import { Button } from 'components/shared/Button';
+import {
+ ChevronGrabberHorizontal,
+ ChevronLeft,
+ ChevronRight,
+} from 'components/shared/CustomIcon';
+
+import './Calendar.css';
+import { format } from 'date-fns';
+
+const CALENDAR_VIEW = ['month', 'year', 'decade', 'century'] as const;
+export type CalendarView = (typeof CALENDAR_VIEW)[number];
+
+/**
+ * Defines a custom set of props for a React calendar component by excluding specific props
+ * from the original ReactCalendarProps type.
+ * @type {CustomReactCalendarProps}
+ */
+type CustomReactCalendarProps = Omit<
+ ReactCalendarProps,
+ 'view' | 'showNavigation' | 'onClickMonth' | 'onClickYear'
+>;
+
+export interface CalendarProps extends CustomReactCalendarProps, CalendarTheme {
+ /**
+ * Optional props for wrapping a component with a div element.
+ */
+ wrapperProps?: ComponentPropsWithRef<'div'>;
+ /**
+ * Props for the calendar wrapper component.
+ */
+ calendarWrapperProps?: ComponentPropsWithRef<'div'>;
+ /**
+ * Optional props for the footer component.
+ */
+ footerProps?: ComponentPropsWithRef<'div'>;
+ /**
+ * Optional custom actions to be rendered.
+ */
+ actions?: ReactNode;
+ /**
+ * Optional callback function that is called when a value is selected.
+ * @param {Value} value - The selected value
+ * @returns None
+ */
+ onSelect?: (value: Value) => void;
+ /**
+ * Optional callback function that is called when a cancel action is triggered.
+ * @returns None
+ */
+ onCancel?: () => void;
+}
+
+/**
+ * Calendar component that allows users to select dates and navigate through months and years.
+ * @param {Object} CalendarProps - Props for the Calendar component.
+ * @returns {JSX.Element} A calendar component with navigation, date selection, and actions.
+ */
+export const Calendar = ({
+ selectRange,
+ activeStartDate: activeStartDateProp,
+ value: valueProp,
+ wrapperProps,
+ calendarWrapperProps,
+ footerProps,
+ actions,
+ onSelect,
+ onCancel,
+ onChange: onChangeProp,
+ ...props
+}: CalendarProps): JSX.Element => {
+ const { wrapper, calendar, navigation, dropdowns, dropdown, footer } =
+ calendarTheme();
+
+ const today = new Date();
+ const currentMonth = format(today, 'MMM');
+ const currentyear = format(today, 'yyyy');
+
+ const [view, setView] = useState('month');
+ const [activeDate, setActiveDate] = useState(
+ activeStartDateProp ?? today,
+ );
+ const [value, setValue] = useState(valueProp as Value);
+ const [month, setMonth] = useState(currentMonth);
+ const [year, setYear] = useState(currentyear);
+
+ /**
+ * Update the navigation label based on the active date
+ */
+ const changeNavigationLabel = useCallback(
+ (date: Date) => {
+ setMonth(format(date, 'MMM'));
+ setYear(format(date, 'yyyy'));
+ },
+ [setMonth, setYear],
+ );
+
+ /**
+ * Change the active date base on the action and range
+ */
+ const handleNavigate = useCallback(
+ (action: 'previous' | 'next', range: 'month' | 'year' | 'decade') => {
+ setActiveDate((date) => {
+ const newDate = new Date(date);
+ switch (range) {
+ case 'month':
+ newDate.setMonth(
+ action === 'previous' ? date.getMonth() - 1 : date.getMonth() + 1,
+ );
+ break;
+ case 'year':
+ newDate.setFullYear(
+ action === 'previous'
+ ? date.getFullYear() - 1
+ : date.getFullYear() + 1,
+ );
+ break;
+ case 'decade':
+ newDate.setFullYear(
+ action === 'previous'
+ ? date.getFullYear() - 10
+ : date.getFullYear() + 10,
+ );
+ break;
+ }
+ changeNavigationLabel(newDate);
+ return newDate;
+ });
+ },
+ [setActiveDate, changeNavigationLabel],
+ );
+
+ /**
+ * Change the view of the calendar
+ */
+ const handleChangeView = useCallback(
+ (view: CalendarView) => {
+ setView(view);
+ },
+ [setView],
+ );
+
+ /**
+ * Change the active date and set the view to the selected type
+ * and also update the navigation label
+ */
+ const handleChangeNavigation = useCallback(
+ (type: 'month' | 'year', date: Date) => {
+ setActiveDate(date);
+ changeNavigationLabel(date);
+ setView(type);
+ },
+ [setActiveDate, changeNavigationLabel, setView],
+ );
+
+ const handlePrevious = useCallback(() => {
+ switch (view) {
+ case 'month':
+ return handleNavigate('previous', 'month');
+ case 'year':
+ return handleNavigate('previous', 'year');
+ case 'decade':
+ return handleNavigate('previous', 'decade');
+ }
+ }, [view]);
+
+ const handleNext = useCallback(() => {
+ switch (view) {
+ case 'month':
+ return handleNavigate('next', 'month');
+ case 'year':
+ return handleNavigate('next', 'year');
+ case 'decade':
+ return handleNavigate('next', 'decade');
+ }
+ }, [view]);
+
+ const handleChange = useCallback(
+ (newValue: Value, event: MouseEvent) => {
+ setValue(newValue);
+
+ // Call the onChange prop if it exists
+ onChangeProp?.(newValue, event);
+
+ /**
+ * Update the active date and navigation label
+ *
+ * NOTE:
+ * For range selection, the active date is not updated
+ * The user only can select multiple dates within the same month
+ */
+ if (!selectRange) {
+ setActiveDate(newValue as Date);
+ changeNavigationLabel(newValue as Date);
+ }
+ },
+ [setValue, setActiveDate, changeNavigationLabel, selectRange],
+ );
+
+ return (
+
+ {/* Calendar wrapper */}
+
+ {/* Navigation */}
+
+
+
+
+ }
+ onClick={() => handleChangeView('year')}
+ >
+ {month}
+
+
+ }
+ onClick={() => handleChangeView('decade')}
+ >
+ {year}
+
+
+
+
+
+ {/* Calendar */}
+
handleChangeNavigation('month', date)}
+ onClickYear={(date) => handleChangeNavigation('year', date)}
+ />
+
+
+ {/* Footer or CTA */}
+
+ {actions ? (
+ actions
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/Calendar/index.ts b/packages/frontend/src/components/shared/Calendar/index.ts
new file mode 100644
index 0000000..a723380
--- /dev/null
+++ b/packages/frontend/src/components/shared/Calendar/index.ts
@@ -0,0 +1 @@
+export * from './Calendar';
From 4f7f9cf9143ddd60a1ab4fd91852d50216153924 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:33:59 +0700
Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20calendar=20c?=
=?UTF-8?q?omponent=20to=20the=20example=20page?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/frontend/src/pages/components/index.tsx | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx
index 8c23f34..4ff2e58 100644
--- a/packages/frontend/src/pages/components/index.tsx
+++ b/packages/frontend/src/pages/components/index.tsx
@@ -1,4 +1,5 @@
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
+import { Calendar } from 'components/shared/Calendar';
import { PlusIcon } from 'components/shared/CustomIcon';
import React from 'react';
@@ -13,6 +14,7 @@ const Page = () => {
packages/frontend/src/pages/components/index.tsx
+
{/* Insert Components here */}
@@ -69,6 +71,15 @@ const Page = () => {
))}
+
+
+
);
From af0d28e6546d0b4f41d4f8a6a178fec04917d015 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:34:15 +0700
Subject: [PATCH 06/13] =?UTF-8?q?=F0=9F=8E=A8=20style:=20add=20new=20shado?=
=?UTF-8?q?ws?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/frontend/tailwind.config.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js
index 92ee77d..00ce21f 100644
--- a/packages/frontend/tailwind.config.js
+++ b/packages/frontend/tailwind.config.js
@@ -144,6 +144,9 @@ export default withMT({
boxShadow: {
button:
'inset 0px 0px 4px rgba(255, 255, 255, 0.25), inset 0px -2px 0px rgba(0, 0, 0, 0.04)',
+ calendar:
+ '0px 3px 20px rgba(8, 47, 86, 0.1), 0px 0px 4px rgba(8, 47, 86, 0.14)',
+ field: '0px 1px 2px rgba(0, 0, 0, 0.04)',
},
spacing: {
2.5: '0.625rem',
From dc6bf3794a143dfc42d1e9dc99ccbc73c6c607f4 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 16:54:32 +0700
Subject: [PATCH 07/13] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20add=20intera?=
=?UTF-8?q?ction=20on=20the=20example=20page?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../frontend/src/pages/components/index.tsx | 28 ++++++++++++++++---
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx
index 4ff2e58..775acbb 100644
--- a/packages/frontend/src/pages/components/index.tsx
+++ b/packages/frontend/src/pages/components/index.tsx
@@ -1,9 +1,13 @@
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
import { Calendar } from 'components/shared/Calendar';
import { PlusIcon } from 'components/shared/CustomIcon';
-import React from 'react';
+import React, { useState } from 'react';
+import { Value } from 'react-calendar/dist/cjs/shared/types';
const Page = () => {
+ const [singleDate, setSingleDate] = useState();
+ const [dateRange, setDateRange] = useState();
+
return (
@@ -75,9 +79,25 @@ const Page = () => {
Calendar
-
-
-
+
+
+
Selected date: {singleDate?.toString()}
+
+
+
+
+ Start date:{' '}
+ {dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '}
+
+ End date:{' '}
+ {dateRange instanceof Array ? dateRange[1]?.toString() : ''}
+
+
+
From 5c5d759c10c8d891e854e0052da115ada6daccfd Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 23:08:04 +0700
Subject: [PATCH 08/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20fix=20ty?=
=?UTF-8?q?po=20and=20change=20to=20readable=20name?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../shared/Calendar/Calendar.theme.ts | 4 +--
.../components/shared/Calendar/Calendar.tsx | 30 +++++++++++--------
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
index e39e592..ce6e93d 100644
--- a/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
+++ b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts
@@ -17,8 +17,8 @@ export const calendarTheme = tv({
'py-2.5',
'px-1',
],
- dropdowns: ['flex', 'items-center', 'justify-center', 'gap-1.5', 'flex-1'],
- dropdown: [
+ actions: ['flex', 'items-center', 'justify-center', 'gap-1.5', 'flex-1'],
+ button: [
'flex',
'items-center',
'gap-2',
diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.tsx b/packages/frontend/src/components/shared/Calendar/Calendar.tsx
index 737eee7..dfc15e3 100644
--- a/packages/frontend/src/components/shared/Calendar/Calendar.tsx
+++ b/packages/frontend/src/components/shared/Calendar/Calendar.tsx
@@ -21,7 +21,7 @@ import {
import './Calendar.css';
import { format } from 'date-fns';
-const CALENDAR_VIEW = ['month', 'year', 'decade', 'century'] as const;
+const CALENDAR_VIEW = ['month', 'year', 'decade'] as const;
export type CalendarView = (typeof CALENDAR_VIEW)[number];
/**
@@ -82,12 +82,18 @@ export const Calendar = ({
onChange: onChangeProp,
...props
}: CalendarProps): JSX.Element => {
- const { wrapper, calendar, navigation, dropdowns, dropdown, footer } =
- calendarTheme();
+ const {
+ wrapper,
+ calendar,
+ navigation,
+ actions: actionsClass,
+ button,
+ footer,
+ } = calendarTheme();
const today = new Date();
const currentMonth = format(today, 'MMM');
- const currentyear = format(today, 'yyyy');
+ const currentYear = format(today, 'yyyy');
const [view, setView] = useState('month');
const [activeDate, setActiveDate] = useState(
@@ -95,7 +101,7 @@ export const Calendar = ({
);
const [value, setValue] = useState(valueProp as Value);
const [month, setMonth] = useState(currentMonth);
- const [year, setYear] = useState(currentyear);
+ const [year, setYear] = useState(currentYear);
/**
* Update the navigation label based on the active date
@@ -112,10 +118,10 @@ export const Calendar = ({
* Change the active date base on the action and range
*/
const handleNavigate = useCallback(
- (action: 'previous' | 'next', range: 'month' | 'year' | 'decade') => {
+ (action: 'previous' | 'next', view: CalendarView) => {
setActiveDate((date) => {
const newDate = new Date(date);
- switch (range) {
+ switch (view) {
case 'month':
newDate.setMonth(
action === 'previous' ? date.getMonth() - 1 : date.getMonth() + 1,
@@ -158,10 +164,10 @@ export const Calendar = ({
* and also update the navigation label
*/
const handleChangeNavigation = useCallback(
- (type: 'month' | 'year', date: Date) => {
+ (view: 'month' | 'year', date: Date) => {
setActiveDate(date);
changeNavigationLabel(date);
- setView(type);
+ setView(view);
},
[setActiveDate, changeNavigationLabel, setView],
);
@@ -225,10 +231,10 @@ export const Calendar = ({
-
+
}
@@ -238,7 +244,7 @@ export const Calendar = ({
}
From f31ee7349ffb943da6cace2a86d0d1a00b99b53d Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 18:24:01 +0700
Subject: [PATCH 09/13] =?UTF-8?q?=F0=9F=94=A7=20chore:=20install=20`@radix?=
=?UTF-8?q?-ui/react-checkbox`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/frontend/package.json | 1 +
yarn.lock | 100 ++++++++++++++++++++++++++++++++-
2 files changed, 100 insertions(+), 1 deletion(-)
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index a2a77ba..3f13455 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"@fontsource/inter": "^5.0.16",
"@material-tailwind/react": "^2.1.7",
+ "@radix-ui/react-checkbox": "^1.0.4",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
diff --git a/yarn.lock b/yarn.lock
index e471349..a0d90e6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1280,7 +1280,7 @@
resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
-"@babel/runtime@^7.10.4", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1":
+"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
@@ -3277,6 +3277,104 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
+"@radix-ui/primitive@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
+ integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-checkbox@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
+ integrity sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-presence" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+ "@radix-ui/react-use-previous" "1.0.1"
+ "@radix-ui/react-use-size" "1.0.1"
+
+"@radix-ui/react-compose-refs@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
+ integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-context@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
+ integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-presence@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
+ integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-use-layout-effect" "1.0.1"
+
+"@radix-ui/react-primitive@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
+ integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-slot" "1.0.2"
+
+"@radix-ui/react-slot@1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
+ integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-compose-refs" "1.0.1"
+
+"@radix-ui/react-use-callback-ref@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
+ integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-use-controllable-state@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
+ integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-use-callback-ref" "1.0.1"
+
+"@radix-ui/react-use-layout-effect@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
+ integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-use-previous@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66"
+ integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@radix-ui/react-use-size@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
+ integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-use-layout-effect" "1.0.1"
+
"@remix-run/router@1.13.1":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.13.1.tgz#07e2a8006f23a3bc898b3f317e0a58cc8076b86e"
From 5e3a6ad2b57ac78d985156c55ec3f353c90db55a Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 18:24:28 +0700
Subject: [PATCH 10/13] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20create=20che?=
=?UTF-8?q?ckbox=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../shared/Checkbox/Checkbox.theme.ts | 68 +++++++++++++++++
.../components/shared/Checkbox/Checkbox.tsx | 76 +++++++++++++++++++
.../src/components/shared/Checkbox/index.ts | 1 +
3 files changed, 145 insertions(+)
create mode 100644 packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
create mode 100644 packages/frontend/src/components/shared/Checkbox/Checkbox.tsx
create mode 100644 packages/frontend/src/components/shared/Checkbox/index.ts
diff --git a/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
new file mode 100644
index 0000000..7122f65
--- /dev/null
+++ b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
@@ -0,0 +1,68 @@
+import { tv, type VariantProps } from 'tailwind-variants';
+
+export const getCheckboxVariant = tv({
+ slots: {
+ wrapper: ['group', 'flex', 'gap-3'],
+ indicator: [
+ 'grid',
+ 'place-content-center',
+ 'text-transparent',
+ 'group-hover:text-controls-disabled',
+ 'focus-visible:text-controls-disabled',
+ 'group-focus-visible:text-controls-disabled',
+ 'data-[state=checked]:text-elements-on-primary',
+ 'data-[state=checked]:group-focus-visible:text-elements-on-primary',
+ 'data-[state=indeterminate]:text-elements-on-primary',
+ 'data-[state=checked]:data-[disabled]:text-elements-on-disabled-active',
+ ],
+ icon: ['w-3', 'h-3', 'stroke-current', 'text-current'],
+ input: [
+ 'h-5',
+ 'w-5',
+ 'group',
+ 'border',
+ 'border-border-interactive/10',
+ 'bg-controls-tertiary',
+ 'rounded-md',
+ 'transition-all',
+ 'duration-150',
+ 'focus-ring',
+ 'shadow-button',
+ 'group-hover:border-border-interactive/[0.14]',
+ 'group-hover:bg-controls-tertiary',
+ 'data-[state=checked]:bg-controls-primary',
+ 'data-[state=checked]:hover:bg-controls-primary-hovered',
+ 'data-[state=checked]:focus-visible:bg-controls-primary-hovered',
+ 'data-[disabled]:bg-controls-disabled',
+ 'data-[disabled]:shadow-none',
+ 'data-[disabled]:hover:border-border-interactive/10',
+ 'data-[disabled]:cursor-not-allowed',
+ 'data-[state=checked]:data-[disabled]:bg-controls-disabled-active',
+ ],
+ label: [
+ 'text-sm',
+ 'tracking-[-0.006em]',
+ 'text-elements-high-em',
+ 'flex',
+ 'flex-col',
+ 'gap-1',
+ 'px-1',
+ ],
+ description: ['text-xs', 'text-elements-low-em'],
+ },
+ variants: {
+ disabled: {
+ true: {
+ wrapper: ['cursor-not-allowed'],
+ indicator: ['group-hover:text-current'],
+ input: [
+ 'group-hover:border-border-interactive/[0.14]',
+ 'group-hover:bg-controls-disabled',
+ ],
+ label: ['cursor-not-allowed'],
+ },
+ },
+ },
+});
+
+export type CheckboxVariants = VariantProps;
diff --git a/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx b/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx
new file mode 100644
index 0000000..0ba9d82
--- /dev/null
+++ b/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import * as CheckboxRadix from '@radix-ui/react-checkbox';
+import { type CheckboxProps as CheckboxRadixProps } from '@radix-ui/react-checkbox';
+
+import { getCheckboxVariant, type CheckboxVariants } from './Checkbox.theme';
+import { CheckIcon } from 'components/shared/CustomIcon';
+
+interface CheckBoxProps extends CheckboxRadixProps, CheckboxVariants {
+ /**
+ * The label of the checkbox.
+ */
+ label?: string;
+ /**
+ * The description of the checkbox.
+ */
+ description?: string;
+}
+
+/**
+ * Checkbox component is used to allow users to select one or more items from a set.
+ * It is a wrapper around the `@radix-ui/react-checkbox` component.
+ *
+ * It accepts all the props from `@radix-ui/react-checkbox` component and the variants from the theme.
+ *
+ * It also accepts `label` and `description` props to display the label and description of the checkbox.
+ *
+ * @example
+ * ```tsx
+ *
+ * ```
+ */
+export const Checkbox = ({
+ id,
+ className,
+ label,
+ description,
+ onCheckedChange,
+ ...props
+}: CheckBoxProps) => {
+ const {
+ wrapper: wrapperStyle,
+ indicator: indicatorStyle,
+ icon: iconStyle,
+ input: inputStyle,
+ label: labelStyle,
+ description: descriptionStyle,
+ } = getCheckboxVariant({
+ disabled: props?.disabled,
+ });
+ return (
+
+
+
+
+
+
+ {label && (
+
+ )}
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/Checkbox/index.ts b/packages/frontend/src/components/shared/Checkbox/index.ts
new file mode 100644
index 0000000..f5c939f
--- /dev/null
+++ b/packages/frontend/src/components/shared/Checkbox/index.ts
@@ -0,0 +1 @@
+export * from './Checkbox';
From 26260976fbfbd20c35e2620bf3c6933258e89948 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 18:24:49 +0700
Subject: [PATCH 11/13] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20feat:=20create=20che?=
=?UTF-8?q?ck=20icon=20for=20checkbox=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../shared/CustomIcon/CheckIcon.tsx | 22 +++++++++++++++++++
.../src/components/shared/CustomIcon/index.ts | 1 +
2 files changed, 23 insertions(+)
create mode 100644 packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx
diff --git a/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx
new file mode 100644
index 0000000..e1096cb
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const CheckIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts
index d50d942..5afd68c 100644
--- a/packages/frontend/src/components/shared/CustomIcon/index.ts
+++ b/packages/frontend/src/components/shared/CustomIcon/index.ts
@@ -1,2 +1,3 @@
export * from './PlusIcon';
export * from './CustomIcon';
+export * from './CheckIcon';
From b2550784316c5057507d9ae5d11685c9447b7dfd Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 18:25:13 +0700
Subject: [PATCH 12/13] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20checkbox=20t?=
=?UTF-8?q?o=20example=20component=20page?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../frontend/src/pages/components/index.tsx | 34 +++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx
index 8c23f34..4d56aa6 100644
--- a/packages/frontend/src/pages/components/index.tsx
+++ b/packages/frontend/src/pages/components/index.tsx
@@ -1,6 +1,7 @@
-import { Button, ButtonOrLinkProps } from 'components/shared/Button';
-import { PlusIcon } from 'components/shared/CustomIcon';
import React from 'react';
+import { Button, ButtonOrLinkProps } from 'components/shared/Button';
+import { Checkbox } from 'components/shared/Checkbox';
+import { PlusIcon } from 'components/shared/CustomIcon';
const Page = () => {
return (
@@ -69,6 +70,35 @@ const Page = () => {
))}
+
+
+
+
+
Checkbox
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+ ))}
+
+
+ {Array.from({ length: 2 }).map((_, index) => (
+
+ ))}
+
+
);
From a5bce0cda24573cd945b9f3f61f3a967fc82a4f4 Mon Sep 17 00:00:00 2001
From: Wahyu Kurniawan
Date: Tue, 20 Feb 2024 23:34:20 +0700
Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=8E=A8=20style:=20adjust=20disabled?=
=?UTF-8?q?=20hover=20text=20color?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../frontend/src/components/shared/Checkbox/Checkbox.theme.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
index 7122f65..e39b66f 100644
--- a/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
+++ b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts
@@ -54,7 +54,7 @@ export const getCheckboxVariant = tv({
disabled: {
true: {
wrapper: ['cursor-not-allowed'],
- indicator: ['group-hover:text-current'],
+ indicator: ['group-hover:text-transparent'],
input: [
'group-hover:border-border-interactive/[0.14]',
'group-hover:bg-controls-disabled',