diff --git a/packages/ui-universal/lib/components/Calendar/index.scss b/packages/ui-universal/lib/components/Calendar/index.scss
new file mode 100644
index 0000000..756973a
--- /dev/null
+++ b/packages/ui-universal/lib/components/Calendar/index.scss
@@ -0,0 +1,87 @@
+@use "../../variables" as *;
+@use "../../_variables/color.scss" as *;
+@use "../../_variables/radius.scss" as *;
+@use "../../_variables/border.scss" as *;
+@use "../../_variables/fontSize.scss" as *;
+@use "../../_variables/duration.scss" as *;
+@use "../../_variables/animation.scss" as *;
+@use "../../_variables/disabled.scss" as *;
+$animation-duration: $duration-300;
+.base {
+ height: fit-content;
+ width: fit-content;
+ padding: 10px;
+ border-radius: $radius-10;
+ background-color: var(--white-color);
+ color: var(--black-color);
+ box-shadow: $shadow;
+ min-width: 19.6875rem;
+ .calendarTitle {
+ font-size: 18px;
+ font-weight: 1000;
+ color: var(--shadow-color);
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 10px;
+ .buttonContainer {
+ display: flex;
+ gap: 3px;
+ }
+ }
+ .calendarItems {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ grid-template-rows: 1.5fr auto;
+ column-gap: 5px;
+ row-gap: 5px;
+ padding: 5px 10px;
+ box-sizing: border-box;
+ justify-items: center;
+ .calendarItem {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 35px;
+ height: 35px;
+ cursor: pointer;
+ &.otherMonth {
+ opacity: 0.4;
+ }
+ &:hover:not(&.today):not(&.select) {
+ background-color: var(--background-blue);
+ color: var(--primary-color);
+ border-radius: $radius-5;
+ transition: all $animation-duration $cubic-bezier;
+ }
+ &.select {
+ background-color: var(--primary-color);
+ border-radius: 5px;
+ color: var(--white-color);
+ transition: all $animation-duration $cubic-bezier;
+ }
+ &.today {
+ background-color: var(--background-blue);
+ color: var(--primary-color);
+ border-radius: $radius-5;
+ }
+ }
+ }
+ .weekdays {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ background-color: var(--title-shadow);
+ box-sizing: border-box;
+ gap: 5px;
+ padding: 10px;
+ border-radius: 5px;
+ font-weight: 600;
+ color: var(--shadow-color);
+ .weekday {
+ font-size: $font-size-14;
+ width: 35px;
+ text-align: center;
+ }
+ }
+}
diff --git a/packages/ui-universal/lib/components/Calendar/index.ts b/packages/ui-universal/lib/components/Calendar/index.ts
new file mode 100644
index 0000000..347eaa2
--- /dev/null
+++ b/packages/ui-universal/lib/components/Calendar/index.ts
@@ -0,0 +1,234 @@
+import { LitElement, html } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import { createComponent } from "@lit/react";
+import React from "react";
+import styles from "./index.scss?inline";
+import { classMap } from "lit/directives/class-map.js";
+
+export interface CalendarProps {
+ /**
+ * the onChange of the calendar
+ */
+ onchange?: (value: Date) => void;
+ /**
+ * the selected of the date
+ */
+ selected?: Date;
+}
+
+@customElement("a-calendar")
+export class ACalendar extends LitElement {
+ static styles = styles;
+ @property() onSelect?: (value: Date) => void;
+ @property({ type: Object }) selected?: Date;
+
+ @state() private selectDate: Date | undefined;
+ @state() private currentDate: Date = new Date();
+ @state() private selectMonth: number = new Date().getMonth();
+ @state() private numberOfDaysFromPrevMonth: number = 0;
+ @state() private numberOfDaysInLastMonth: number = 0;
+ @state() private numberOfDaysInMonth: number = 0;
+ @state() private numberOfDaysFromAfterMonth: number = 0;
+
+ updated(changedProperties: Map
) {
+ if (changedProperties.has("selected")) {
+ this.selectDate = this.selected;
+ }
+ }
+
+ firstUpdated() {
+ this.updateCalendar();
+ }
+
+ // update number of days in month
+ private updateNumberOfDaysInMonth() {
+ this.numberOfDaysInMonth = new Date(
+ this.currentDate.getFullYear(),
+ this.selectMonth + 1,
+ 0
+ ).getDate();
+ }
+
+ // update number of days from prev month
+ private updateNumberOfDaysFromPrevMonth() {
+ const firstDay = new Date(
+ this.currentDate.getFullYear(),
+ this.selectMonth,
+ 1
+ ).getDay();
+ this.numberOfDaysFromPrevMonth = firstDay === 0 ? 6 : firstDay - 1;
+ }
+
+ // update number of days from after month
+ private updateNumberOfDaysFromAfterMonth() {
+ const lastDay = new Date(
+ this.currentDate.getFullYear(),
+ this.selectMonth + 1,
+ 0
+ ).getDay();
+ this.numberOfDaysFromAfterMonth = lastDay === 0 ? 0 : 7 - lastDay;
+ }
+
+ // update number of days in last month
+ private updateNumberOfDaysInLastMonth() {
+ this.numberOfDaysInLastMonth = new Date(
+ this.currentDate.getFullYear(),
+ this.selectMonth,
+ 0
+ ).getDate();
+ }
+
+ private updateCalendar() {
+ this.updateNumberOfDaysInMonth();
+ this.updateNumberOfDaysFromPrevMonth();
+ this.updateNumberOfDaysFromAfterMonth();
+ this.updateNumberOfDaysInLastMonth();
+ }
+
+ private changeMonth(isBack: boolean) {
+ this.selectMonth = isBack ? this.selectMonth - 1 : this.selectMonth + 1;
+ this.updateCalendar();
+ }
+
+ private generateCalendarGrid() {
+ const dayItems = [];
+ for (let index = 0; index < this.numberOfDaysFromPrevMonth; index++) {
+ dayItems.push(
+ html`
+ ${this.numberOfDaysInLastMonth -
+ this.numberOfDaysFromPrevMonth +
+ index +
+ 1}
+
`
+ );
+ }
+ for (let index = 0; index < this.numberOfDaysInMonth; index++) {
+ dayItems.push(
+ html` {
+ this.selected = new Date(
+ this.currentDate.getFullYear(),
+ this.selectMonth,
+ index + 1
+ );
+ this.onSelect && this.onSelect(this.selected);
+ }}
+ >
+ ${index + 1}
+
`
+ );
+ }
+ for (let index = 0; index < this.numberOfDaysFromAfterMonth; index++) {
+ dayItems.push(
+ html`${index + 1}
`
+ );
+ }
+ return dayItems;
+ }
+
+ render() {
+ return html`
+
+
+
+
${new Date(this.currentDate.getFullYear(), this.selectMonth + 1, 0)
+ .toDateString()
+ .split(" ")[1]},
+ ${this.currentDate.getFullYear()}
+
+
+
Mo
+
Tu
+
We
+
Th
+
Fr
+
Sa
+
Su
+
+
+ ${this.generateCalendarGrid().map((value) => {
+ return value;
+ })}
+
+
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "a-calendar": ACalendar;
+ }
+}
+
+export const Calendar = createComponent({
+ tagName: "a-calendar",
+ elementClass: ACalendar,
+ react: React,
+});
diff --git a/packages/ui-universal/lib/components/Input/index.scss b/packages/ui-universal/lib/components/Input/index.scss
index c3ff70c..4fbf8e7 100644
--- a/packages/ui-universal/lib/components/Input/index.scss
+++ b/packages/ui-universal/lib/components/Input/index.scss
@@ -1,5 +1,11 @@
@use "../../variables" as *;
@use "../../_variables/color.scss" as *;
+@use "../../_variables/radius.scss" as *;
+@use "../../_variables/border.scss" as *;
+@use "../../_variables/fontSize.scss" as *;
+@use "../../_variables/duration.scss" as *;
+@use "../../_variables/animation.scss" as *;
+@use "../../_variables/disabled.scss" as *;
$border-radius: $radius-8;
$border-width: $border-1;
diff --git a/packages/ui-universal/lib/index.ts b/packages/ui-universal/lib/index.ts
index 909bd38..1ee1301 100644
--- a/packages/ui-universal/lib/index.ts
+++ b/packages/ui-universal/lib/index.ts
@@ -2,3 +2,4 @@ export * from "./components/Button";
export * from "./components/Accordion";
export * from "./components/Input";
export * from "./components/Badge";
+export * from "./components/Calendar";
diff --git a/packages/ui-universal/vite.config.js b/packages/ui-universal/vite.config.js
index 14914b7..4e772c0 100644
--- a/packages/ui-universal/vite.config.js
+++ b/packages/ui-universal/vite.config.js
@@ -22,7 +22,6 @@ export default {
css: {
preprocessorOptions: {
scss: {
- additionalData: `@use "./lib/variables" as *;`, // Import global scss variables
includePaths: [path.resolve(__dirname, "lib")], // Include paths for scss
},
},