Major reshuffle post demodularization
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
|
||||
# Production
|
||||
build
|
||||
|
||||
# Miscellaneous
|
||||
*.local
|
||||
.DS_Store
|
||||
|
||||
# Component Modules built for local dev using lerna should be ignored
|
||||
Calendar
|
||||
DatePicker
|
||||
DateRangePicker
|
||||
EventForm
|
||||
EventList
|
||||
EventManager
|
||||
JustCalendar
|
||||
MonthNavigator
|
||||
MonthSelector
|
||||
6
Jenkinsfile
vendored
Normal file
6
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
@Library('jenkins-shared') _
|
||||
kanikoPipeline(
|
||||
repoName: 'calendar',
|
||||
branch: env.BRANCH_NAME ?: 'main',
|
||||
isNpmLib: true
|
||||
)
|
||||
1
README.md
Normal file
1
README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Armco Template for the tech stack: React, TS, Dart Sass, Redux Tookkit, react-redux, react browser routing, TS based plop generator
|
||||
36
build-tools/build.sh
Executable file
36
build-tools/build.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory of the current script
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Default values
|
||||
DEV_FLAG=""
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"
|
||||
do
|
||||
case $arg in
|
||||
--dev)
|
||||
DEV_FLAG="--dev"
|
||||
shift # Remove --dev from processing
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "[BUILD:SH] Dev flag is: $DEV_FLAG"
|
||||
echo "[BUILD:SH] Removing build if exists"
|
||||
rm -rf build
|
||||
echo "[BUILD:SH] Checking TS Types"
|
||||
npx tsc
|
||||
echo "[BUILD:SH] Initiating build..."
|
||||
# Conditionally use vite-dev.config.ts if --dev flag is present
|
||||
if [ "$DEV_FLAG" == "--dev" ]; then
|
||||
vite build --config vite-dev.config.ts
|
||||
else
|
||||
vite build
|
||||
fi
|
||||
|
||||
echo "[BUILD:SH] Running post processor scripts..."
|
||||
# Run Post processors: Update style imports in .js files, create component modules
|
||||
node "$SCRIPT_DIR/post-processor.js" build/cjs $DEV_FLAG
|
||||
node "$SCRIPT_DIR/post-processor.js" build/es $DEV_FLAG
|
||||
35
build-tools/generate-module.js
Normal file
35
build-tools/generate-module.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { promises as fs } from "fs"
|
||||
import { dirname, resolve } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
async function generateModule(fileName, isDev) {
|
||||
if (
|
||||
fileName.endsWith(".js") &&
|
||||
fileName.indexOf("-chunk") === -1 &&
|
||||
fileName !== "index.js"
|
||||
) {
|
||||
const dir = fileName.slice(0, -3)
|
||||
const name = `@armco/calendar/${dir}`
|
||||
const packageJsonContent = {
|
||||
name,
|
||||
main: `../${isDev ? "build/" : ""}cjs/${dir}.js`,
|
||||
module: `../${isDev ? "build/" : ""}es/${dir}.js`,
|
||||
types: `../${isDev ? "build/" : ""}types/${dir}.d.ts`,
|
||||
}
|
||||
const dirPath = resolve(__dirname, `../${isDev ? "" : "build/"}${dir}`)
|
||||
try {
|
||||
await fs.mkdir(dirPath, { recursive: true })
|
||||
await fs.writeFile(
|
||||
resolve(dirPath, "package.json"),
|
||||
JSON.stringify(packageJsonContent, null, 2),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Error processing directory ${dirPath}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default generateModule
|
||||
24
build-tools/post-processor.js
Normal file
24
build-tools/post-processor.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { readdir } from "fs/promises"
|
||||
import generateModule from "./generate-module.js"
|
||||
|
||||
async function postProcessor(dir, isDev) {
|
||||
try {
|
||||
const files = await readdir(dir)
|
||||
await Promise.all(
|
||||
files.map(async (file) => {
|
||||
await generateModule(file, isDev)
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Error processing directory ${dir}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
const targetDir = process.argv[2]
|
||||
|
||||
if (targetDir) {
|
||||
postProcessor(targetDir, process.argv.includes("--dev"))
|
||||
} else {
|
||||
console.error("Please provide the build directory to run post processor on.")
|
||||
process.exit(1)
|
||||
}
|
||||
8032
package-lock.json
generated
Normal file
8032
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
65
package.json
Normal file
65
package.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@armco/calendar",
|
||||
"version": "0.0.7",
|
||||
"type": "module",
|
||||
"main": "build/cjs/index.js",
|
||||
"module": "build/es/index.js",
|
||||
"types": "build/types/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "./build-tools/build.sh",
|
||||
"build:sm": "./build-tools/build.sh --dev",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint .",
|
||||
"publish:sh": "./publish.sh",
|
||||
"publish:local": "./publish-local.sh"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
],
|
||||
"plugins": [
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/jsx-no-target-blank": "off"
|
||||
}
|
||||
},
|
||||
"prettier": "prettier-config-nick",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ReStruct-Corporate-Advantage/calendar.git"
|
||||
},
|
||||
"keywords": [
|
||||
"components",
|
||||
"atomic",
|
||||
"building-blocks",
|
||||
"foundation"
|
||||
],
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ReStruct-Corporate-Advantage/calendar/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ReStruct-Corporate-Advantage/calendar#readme",
|
||||
"dependencies": {
|
||||
"@armco/configs": "^0.0.15",
|
||||
"@armco/icon": "^0.0.13",
|
||||
"@armco/shared-components": "^0.0.59",
|
||||
"@armco/utils": "^0.0.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@armco/types": "^0.0.22",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"glob": "^11.0.3",
|
||||
"sass-embedded": "^1.93.3",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-externalize-deps": "^0.10.0",
|
||||
"vite-plugin-lib-inject-css": "^2.2.2",
|
||||
"vitest": "^4.0.8"
|
||||
}
|
||||
}
|
||||
16
publish-local.sh
Executable file
16
publish-local.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
semver=${1:-patch}
|
||||
|
||||
set -e
|
||||
|
||||
npm run build
|
||||
cp package.json build/
|
||||
sed -i '' -E 's/"build"/"*"/' build/package.json
|
||||
|
||||
sed -i '' 's#"build/cjs/Icon.js"#"cjs/Icon.js"#' build/package.json
|
||||
sed -i '' 's#"build/es/Icon.js"#"es/Icon.js"#' build/package.json
|
||||
sed -i '' 's#"build/types/Icon.d.ts"#"types/Icon.d.ts"#' build/package.json
|
||||
|
||||
cd build
|
||||
npm pack --pack-destination ~/__Projects__/Common
|
||||
28
publish.sh
Executable file
28
publish.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
semver=${1:-patch}
|
||||
|
||||
set -e
|
||||
npm --no-git-tag-version version ${semver}
|
||||
npm run build
|
||||
cp package.json build/
|
||||
|
||||
# Use Node.js for portable package.json normalization
|
||||
# Pass the target path via env var to avoid Node treating it as a module/script argument
|
||||
PKG_PATH="$(pwd)/build/package.json" node - <<'EOF'
|
||||
const fs = require('fs');
|
||||
const path = process.env.PKG_PATH;
|
||||
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
|
||||
pkg.private = false;
|
||||
delete pkg.scripts;
|
||||
delete pkg.devDependencies;
|
||||
if (!pkg.files) pkg.files = ['*'];
|
||||
else pkg.files = pkg.files.map(x => x === 'build' ? '*' : x);
|
||||
['main','module','types'].forEach(k => {
|
||||
if (pkg[k]) pkg[k] = pkg[k].replace(/^build\//, '');
|
||||
});
|
||||
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
|
||||
EOF
|
||||
|
||||
cd build
|
||||
npm publish --access public --loglevel verbose
|
||||
54
src/Calendar.component.scss
Executable file
54
src/Calendar.component.scss
Executable file
@@ -0,0 +1,54 @@
|
||||
@use "./MonthSelector.component.scss";
|
||||
@use "./JustCalendar.component.scss";
|
||||
@use "./MonthNavigator.component.scss";
|
||||
@use "./EventManager.component.scss";
|
||||
|
||||
|
||||
.ar-Calendar {
|
||||
--ar-footer-height: 3rem;
|
||||
&.is-mini {
|
||||
--ar-footer-height: 2rem;
|
||||
width: calc(2.5rem * 7 + 1rem) !important;
|
||||
height: calc(3rem + (2.5rem * 6) + 3rem + 1px) !important;
|
||||
}
|
||||
|
||||
&__cal-event-decade-month-year-selector {
|
||||
|
||||
& > * {
|
||||
transition: left 0.3s;
|
||||
}
|
||||
|
||||
.ar-MonthSelector, .ar-EventForm {
|
||||
left: 100%;
|
||||
}
|
||||
.ar-JustCalendar {
|
||||
left: 0;
|
||||
}
|
||||
&.to-month-year, &.to-event-form {
|
||||
.ar-JustCalendar {
|
||||
left: -100%
|
||||
}
|
||||
}
|
||||
&.to-month-year {
|
||||
.ar-MonthSelector {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
&.to-event-form {
|
||||
.ar-EventForm {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ar-Calendar__cal-event-decade-month-year-selector {
|
||||
height: calc(100% - var(--ar-footer-height));
|
||||
}
|
||||
.ar-Calendar__footer {
|
||||
transition: height 0.3s;
|
||||
height: var(--ar-footer-height);
|
||||
|
||||
.ar-Calendar__status {
|
||||
color: var(--ar-color-obscure);
|
||||
}
|
||||
}
|
||||
}
|
||||
288
src/Calendar.tsx
Executable file
288
src/Calendar.tsx
Executable file
@@ -0,0 +1,288 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { ArButtonVariants, ArSizes } from "@armco/shared-components/enums"
|
||||
import Button from "@armco/shared-components/Button"
|
||||
import { ArCalViews, ArMonthSelectorViews } from "./enums"
|
||||
import { CalendarDate, CalendarProps, Event } from "./types"
|
||||
import { CalHelper, isWithinSchedule } from "./dateHelper"
|
||||
import MonthSelector from "./MonthSelector"
|
||||
import JustCalendar from "./JustCalendar"
|
||||
import EventManager from "./EventManager"
|
||||
import MonthNavigator from "./MonthNavigator"
|
||||
import "./Calendar.component.scss"
|
||||
|
||||
const calHelper = new CalHelper({ siblingMonths: true, weekStart: 1 })
|
||||
|
||||
const today = new Date()
|
||||
|
||||
const Calendar = (props: CalendarProps): ReactNode => {
|
||||
const {
|
||||
allowEventSetting,
|
||||
classes,
|
||||
events,
|
||||
customDayEventSetter,
|
||||
hasTimeControls,
|
||||
isSingleSelect,
|
||||
maxDate,
|
||||
minDate,
|
||||
miniMode,
|
||||
onDateSelected,
|
||||
startDate,
|
||||
endDate,
|
||||
theme,
|
||||
} = props
|
||||
const [calendar, setCalendar] = useState<Array<CalendarDate | false>>()
|
||||
const [refDate, setRefDate] = useState<Date>(today)
|
||||
const [startDateLocal, setStartDate] = useState<CalendarDate | null>()
|
||||
const [endDateLocal, setEndDate] = useState<CalendarDate | null>()
|
||||
const [eventDate, setEventDate] = useState<CalendarDate | null>()
|
||||
const [hovered, setHovered] = useState<CalendarDate | null>()
|
||||
const [currentView, setCurrentView] = useState<ArCalViews>(
|
||||
ArCalViews.CALENDAR,
|
||||
)
|
||||
const [localEvents, setLocalEvents] = useState<Array<Event>>()
|
||||
const [currentMonthNavView, setCurrentMonthNavView] =
|
||||
useState<ArMonthSelectorViews | null>()
|
||||
|
||||
const currentDate = (startDateLocal || {
|
||||
day: today.getDate(),
|
||||
month: today.getMonth(),
|
||||
year: today.getFullYear(),
|
||||
}) as CalendarDate
|
||||
|
||||
const [currentDecade, setCurrentDecade] = useState<number | undefined>(
|
||||
currentDate?.year && Math.floor(currentDate.year / 10) * 10,
|
||||
)
|
||||
const [currentYear, setCurrentYear] = useState<number | undefined>(
|
||||
currentDate?.year,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setLocalEvents(events)
|
||||
}, [events])
|
||||
|
||||
useEffect(() => {
|
||||
if (startDate) {
|
||||
setStartDate({
|
||||
year: (startDate as Date).getFullYear(),
|
||||
month: (startDate as Date).getMonth(),
|
||||
day: (startDate as Date).getDate(),
|
||||
})
|
||||
}
|
||||
}, [startDate])
|
||||
|
||||
useEffect(() => {
|
||||
if (endDate) {
|
||||
setEndDate({
|
||||
year: (endDate as Date).getFullYear(),
|
||||
month: (endDate as Date).getMonth(),
|
||||
day: (endDate as Date).getDate(),
|
||||
})
|
||||
}
|
||||
}, [endDate])
|
||||
|
||||
useEffect(() => {
|
||||
if (refDate) {
|
||||
const year = (refDate as Date).getFullYear()
|
||||
const month = (refDate as Date).getMonth()
|
||||
const calendar = calHelper.getCalendar(year, month)
|
||||
setCalendar(calendar)
|
||||
}
|
||||
}, [refDate])
|
||||
|
||||
const onDateSelectedLocal = (date: CalendarDate, isSpecialOp?: boolean) => {
|
||||
if (!startDateLocal || isSingleSelect) {
|
||||
setStartDate(date)
|
||||
calHelper.setStartDate(date)
|
||||
onDateSelected &&
|
||||
onDateSelected(isSingleSelect ? date : { startDate: date })
|
||||
if (isSpecialOp && allowEventSetting) {
|
||||
setEventDate(date)
|
||||
setCurrentView(ArCalViews.EVENT_FORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (startDateLocal && endDateLocal) {
|
||||
if (
|
||||
CalHelper.compare(startDateLocal, date) === -1 &&
|
||||
CalHelper.compare(date, endDateLocal) === -1
|
||||
) {
|
||||
calHelper.setEndDate(date)
|
||||
setEndDate(date)
|
||||
onDateSelected &&
|
||||
onDateSelected(
|
||||
isSingleSelect
|
||||
? date
|
||||
: { startDate: startDateLocal, endDate: date },
|
||||
)
|
||||
} else {
|
||||
calHelper.setEndDate(null)
|
||||
setEndDate(null)
|
||||
calHelper.setStartDate(date)
|
||||
setStartDate(date)
|
||||
onDateSelected &&
|
||||
onDateSelected(
|
||||
isSingleSelect ? date : { startDate: date, endDate: null },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (CalHelper.compare(date, startDateLocal) === -1) {
|
||||
setEndDate(startDateLocal)
|
||||
calHelper.setEndDate(startDateLocal)
|
||||
setStartDate(date)
|
||||
calHelper.setStartDate(date)
|
||||
onDateSelected &&
|
||||
onDateSelected(
|
||||
isSingleSelect
|
||||
? date
|
||||
: { startDate: date, endDate: startDateLocal },
|
||||
)
|
||||
} else {
|
||||
setEndDate(date)
|
||||
calHelper.setEndDate(date)
|
||||
onDateSelected &&
|
||||
onDateSelected(
|
||||
isSingleSelect
|
||||
? date
|
||||
: { startDate: startDateLocal, endDate: date },
|
||||
)
|
||||
}
|
||||
setHovered(null)
|
||||
}
|
||||
if (isSpecialOp && allowEventSetting) {
|
||||
setEventDate(date)
|
||||
setCurrentView(ArCalViews.EVENT_FORM)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ar-Calendar w-100 h-100 d-flex flex-column${hasTimeControls ? " has-time-controls" : ""
|
||||
}${classes ? " " + classes : ""}${miniMode ? " is-mini" : ""}`}
|
||||
>
|
||||
{refDate && currentView !== ArCalViews.EVENT_FORM && (
|
||||
<MonthNavigator
|
||||
currentDecade={currentDecade}
|
||||
currentMonthNavView={currentMonthNavView}
|
||||
currentView={currentView}
|
||||
currentYear={currentYear}
|
||||
miniMode={miniMode}
|
||||
refDate={refDate}
|
||||
setCurrentDecade={setCurrentDecade}
|
||||
setCurrentMonthNavView={setCurrentMonthNavView}
|
||||
setCurrentView={setCurrentView}
|
||||
setCurrentYear={setCurrentYear}
|
||||
setRefDate={setRefDate}
|
||||
theme={theme}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`ar-Calendar__cal-event-decade-month-year-selector d-flex flex-1 position-relative overflow-hidden border-bottom${currentView === ArCalViews.MONTH_YEAR_SELECTOR ? " to-month-year" : ""
|
||||
}${currentView === ArCalViews.EVENT_FORM ? " to-event-form" : ""}${miniMode ? " is-mini" : ""
|
||||
}`}
|
||||
>
|
||||
<JustCalendar
|
||||
allowEventSetting={allowEventSetting}
|
||||
calHelper={calHelper}
|
||||
calendar={calendar}
|
||||
endDate={endDateLocal as CalendarDate}
|
||||
hovered={hovered as CalendarDate}
|
||||
isSingleSelect={isSingleSelect}
|
||||
miniMode={miniMode}
|
||||
onDateSelected={onDateSelectedLocal}
|
||||
setHovered={setHovered}
|
||||
startDate={startDateLocal as CalendarDate}
|
||||
/>
|
||||
<MonthSelector
|
||||
currentDecade={currentDecade}
|
||||
currentMonthNavView={currentMonthNavView}
|
||||
currentView={currentView}
|
||||
currentYear={currentYear}
|
||||
miniMode={miniMode}
|
||||
onMonthSelect={(date: Date) => {
|
||||
setCurrentView(ArCalViews.CALENDAR)
|
||||
setRefDate(date)
|
||||
}}
|
||||
setCurrentDecade={setCurrentDecade}
|
||||
setCurrentMonthNavView={setCurrentMonthNavView}
|
||||
setCurrentView={setCurrentView}
|
||||
setCurrentYear={setCurrentYear}
|
||||
/>
|
||||
{eventDate && currentView === ArCalViews.EVENT_FORM && (
|
||||
<EventManager
|
||||
events={localEvents?.filter((event) =>
|
||||
isWithinSchedule(eventDate, event),
|
||||
)}
|
||||
miniMode={miniMode}
|
||||
onEventSubmit={(event: Event) => {
|
||||
const localEventsClone = localEvents ? [...localEvents] : []
|
||||
localEventsClone.push(event)
|
||||
setLocalEvents(localEventsClone)
|
||||
setEventDate(null)
|
||||
setCurrentView(ArCalViews.CALENDAR)
|
||||
}}
|
||||
selectedDate={eventDate as CalendarDate}
|
||||
setCurrentView={setCurrentView}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`ar-Calendar__footer flex-v-center${miniMode ? " p-2" : " px-3 py-2"
|
||||
}${currentView === ArCalViews.EVENT_FORM ? " h-0 overflow-hidden" : ""
|
||||
}`}
|
||||
>
|
||||
{startDateLocal && !miniMode && (
|
||||
<div className="ar-Calendar__status justify-self-start">
|
||||
<span className="fw-bold">
|
||||
{CalHelper.toString(startDateLocal)}
|
||||
{endDateLocal && " - " + CalHelper.toString(endDateLocal)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="ar-Calendar__controls ms-auto d-flex">
|
||||
<Button
|
||||
content="Clear"
|
||||
classes={miniMode ? "me-2" : "me-3"}
|
||||
size={miniMode ? ArSizes.XSMALL : ArSizes.SMALL}
|
||||
variant={ArButtonVariants.SECONDARY}
|
||||
onClick={() => {
|
||||
setStartDate(null)
|
||||
setEndDate(null)
|
||||
calHelper.setStartDate(null)
|
||||
calHelper.setEndDate(null)
|
||||
}}
|
||||
disabled={!startDateLocal}
|
||||
/>
|
||||
<Button
|
||||
content="Today"
|
||||
size={miniMode ? ArSizes.XSMALL : ArSizes.SMALL}
|
||||
onClick={() => {
|
||||
const date = today
|
||||
const calDate = {
|
||||
day: date.getDate(),
|
||||
month: date.getMonth(),
|
||||
year: date.getFullYear(),
|
||||
}
|
||||
setStartDate(calDate)
|
||||
setEndDate(null)
|
||||
calHelper.setStartDate(calDate)
|
||||
calHelper.setEndDate(null)
|
||||
setRefDate(date)
|
||||
setCurrentDecade(Math.floor(calDate.year / 10) * 10)
|
||||
setCurrentYear(calDate.year)
|
||||
setCurrentMonthNavView(null)
|
||||
setCurrentView(ArCalViews.CALENDAR)
|
||||
onDateSelected &&
|
||||
onDateSelected(
|
||||
isSingleSelect
|
||||
? calDate
|
||||
: { startDate: calDate, endDate: null },
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Calendar
|
||||
17
src/DatePicker.component.scss
Executable file
17
src/DatePicker.component.scss
Executable file
@@ -0,0 +1,17 @@
|
||||
.ar-DatePicker {
|
||||
border-top-left-radius: 0.125rem;
|
||||
border-bottom-left-radius: 0.125rem;
|
||||
.ar-DatePicker__picker {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ar-DatePicker__clear-button {
|
||||
visibility: hidden;
|
||||
padding: 0 0.75rem;
|
||||
|
||||
&.has-value {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
src/DatePicker.tsx
Executable file
72
src/DatePicker.tsx
Executable file
@@ -0,0 +1,72 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { FunctionType } from "@armco/types"
|
||||
import { pad } from "@armco/utils/helper"
|
||||
import { ArPopoverPositions, ArPopoverSlots } from "@armco/shared-components/enums"
|
||||
import Popover from "@armco/shared-components/Popover"
|
||||
import Icon from "@armco/icon"
|
||||
import { CalendarDate, DatePickerProps } from "./types"
|
||||
import Calendar from "./Calendar"
|
||||
import "./DatePicker.component.scss"
|
||||
|
||||
const DatePicker = (props: DatePickerProps): ReactNode => {
|
||||
const { allowEventSetting, classes, id, label, onChange } = props
|
||||
const [date, setDate] = useState<CalendarDate>()
|
||||
const [pickerDisplayed, showPicker] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
!allowEventSetting && date && showPicker(false)
|
||||
}, [allowEventSetting, date])
|
||||
|
||||
const localId = id || "ar-DatePicker__input"
|
||||
|
||||
const onLocalChange = (date: CalendarDate) => {
|
||||
setDate(date)
|
||||
onChange && (onChange as FunctionType)(date)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`ar-DatePicker${classes ? " " + classes : ""}`}>
|
||||
<Popover
|
||||
contentClasses="high-shadow"
|
||||
position={ArPopoverPositions.BOTTOMRIGHT}
|
||||
version="v1"
|
||||
isOpen={pickerDisplayed}
|
||||
>
|
||||
<div
|
||||
className="position-relative cursor-pointer"
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
onClick={() => !allowEventSetting && showPicker(true)}
|
||||
>
|
||||
{label && (
|
||||
<label className="fw-bold me-3" htmlFor={localId}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
id={localId}
|
||||
className="ar-DatePicker__input px-3 py-2"
|
||||
type="text"
|
||||
placeholder="dd/mm/yyyy"
|
||||
value={
|
||||
date &&
|
||||
pad(date.day) + "/" + pad(date.month + 1) + "/" + date.year
|
||||
}
|
||||
/>
|
||||
<Icon
|
||||
attributes={{ classes: "position-absolute top-point5 end-point5" }}
|
||||
icon="fa.FaRegCalendarAlt"
|
||||
/>
|
||||
</div>
|
||||
<Calendar
|
||||
allowEventSetting={allowEventSetting}
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
onDateSelected={onLocalChange}
|
||||
miniMode
|
||||
isSingleSelect
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DatePicker
|
||||
5
src/DateRangePicker.component.scss
Executable file
5
src/DateRangePicker.component.scss
Executable file
@@ -0,0 +1,5 @@
|
||||
.ar-DateRangePicker {
|
||||
.ar-DateRangePicker__input {
|
||||
min-width: 15rem;
|
||||
}
|
||||
}
|
||||
95
src/DateRangePicker.tsx
Executable file
95
src/DateRangePicker.tsx
Executable file
@@ -0,0 +1,95 @@
|
||||
import { ReactNode, useEffect, useState } from "react"
|
||||
import { ArPopoverPositions, ArPopoverSlots } from "@armco/shared-components/enums"
|
||||
import Icon from "@armco/icon"
|
||||
import { pad } from "@armco/utils/helper"
|
||||
import Popover from "@armco/shared-components/Popover"
|
||||
import { DateRangePickerProps, CalendarDate } from "./types"
|
||||
import { toDate } from "./dateHelper"
|
||||
import { DateFormatter } from "./dateformat"
|
||||
import Calendar from "./Calendar"
|
||||
import "./DateRangePicker.component.scss"
|
||||
|
||||
const DateRangePicker = (props: DateRangePickerProps): ReactNode => {
|
||||
const { closeOnEndSelect, id, label, mask } = props
|
||||
const [startDate, setStartDate] = useState<CalendarDate>()
|
||||
const [endDate, setEndDate] = useState<CalendarDate>()
|
||||
const [pickerDisplayed, showPicker] = useState<boolean>()
|
||||
let value
|
||||
const localId = id || "ar-DateRangePicker__input"
|
||||
|
||||
useEffect(() => {
|
||||
if (endDate !== null && closeOnEndSelect) {
|
||||
showPicker(false)
|
||||
}
|
||||
}, [closeOnEndSelect, endDate])
|
||||
|
||||
if (startDate && endDate) {
|
||||
if (mask) {
|
||||
value =
|
||||
DateFormatter(toDate(startDate), mask) +
|
||||
" - " +
|
||||
DateFormatter(toDate(endDate), mask)
|
||||
} else {
|
||||
value =
|
||||
pad(startDate.day) +
|
||||
"/" +
|
||||
pad(startDate.month + 1) +
|
||||
"/" +
|
||||
startDate.year
|
||||
value += " - "
|
||||
value +=
|
||||
pad(endDate.day) + "/" + pad(endDate.month + 1) + "/" + endDate.year
|
||||
}
|
||||
}
|
||||
|
||||
const setDate = (dates: {
|
||||
startDate: CalendarDate
|
||||
endDate?: CalendarDate
|
||||
}) => {
|
||||
setStartDate(dates.startDate)
|
||||
if (dates.endDate) {
|
||||
setEndDate(dates.endDate)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ar-DateRangePicker">
|
||||
<Popover
|
||||
contentClasses="high-shadow"
|
||||
position={ArPopoverPositions.BOTTOMRIGHT}
|
||||
version="v1"
|
||||
isOpen={pickerDisplayed}
|
||||
>
|
||||
<div
|
||||
className="position-relative cursor-pointer"
|
||||
slot={ArPopoverSlots.ANCHOR}
|
||||
onClick={() => showPicker(true)}
|
||||
>
|
||||
{label && (
|
||||
<label className="fw-bold me-3" htmlFor={localId}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
id={localId}
|
||||
className="ar-DateRangePicker__input px-3 py-2"
|
||||
type="text"
|
||||
placeholder="dd/mm/yyyy - dd/mm/yyyy"
|
||||
value={value}
|
||||
/>
|
||||
<Icon
|
||||
icon="fa.FaRegCalendarAlt"
|
||||
attributes={{ classes: "position-absolute top-point5 end-point5" }}
|
||||
/>
|
||||
</div>
|
||||
<Calendar
|
||||
slot={ArPopoverSlots.POPOVER}
|
||||
onDateSelected={setDate}
|
||||
miniMode
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DateRangePicker
|
||||
273
src/EventForm.tsx
Normal file
273
src/EventForm.tsx
Normal file
@@ -0,0 +1,273 @@
|
||||
import { useState } from "react"
|
||||
import { FunctionType, ObjectType } from "@armco/types"
|
||||
import { ArButtonVariants, ArSizes } from "@armco/shared-components/enums"
|
||||
import Button from "@armco/shared-components/Button"
|
||||
import SegmentedControl from "@armco/shared-components/SegmentedControl"
|
||||
import Select from "@armco/shared-components/Select"
|
||||
import TextArea from "@armco/shared-components/TextArea"
|
||||
import TextInput from "@armco/shared-components/TextInput"
|
||||
import TimeEntry from "@armco/shared-components/TimeEntry"
|
||||
import Toggle from "@armco/shared-components/Toggle"
|
||||
import { ArEventStates, ArEventTypes, ArSchedules } from "./enums"
|
||||
import { Event, EventFormProps } from "./types"
|
||||
import { toDate } from "./dateHelper"
|
||||
import { EventListItem } from "./EventList"
|
||||
|
||||
const durationOptions = [
|
||||
{
|
||||
name: "15min",
|
||||
label: "15 minutes",
|
||||
value: "15min",
|
||||
data: { minutes: 15 },
|
||||
},
|
||||
{
|
||||
name: "30min",
|
||||
label: "30 minutes",
|
||||
value: "30min",
|
||||
data: { minutes: 30 },
|
||||
},
|
||||
{
|
||||
name: "45min",
|
||||
label: "45 minutes",
|
||||
value: "45min",
|
||||
data: { minutes: 45 },
|
||||
},
|
||||
{
|
||||
name: "1hour",
|
||||
label: "1 hour",
|
||||
value: "1hour",
|
||||
data: { hours: 1 },
|
||||
},
|
||||
{
|
||||
name: "2hours",
|
||||
label: "2 hours",
|
||||
value: "2hours",
|
||||
data: { hours: 2 },
|
||||
},
|
||||
{ name: "custom", label: "Custom", value: "custom" },
|
||||
]
|
||||
|
||||
export const segments = [
|
||||
{ name: "task", label: "Task", icon: "md.MdAddTask" },
|
||||
{
|
||||
name: "event",
|
||||
label: "Event",
|
||||
icon: "md.MdOutlineEventAvailable",
|
||||
},
|
||||
{
|
||||
name: "birthday",
|
||||
label: "Birthday",
|
||||
icon: "fa.FaBirthdayCake",
|
||||
},
|
||||
{
|
||||
name: "meeting",
|
||||
label: "Meeting",
|
||||
icon: "md.MdOutlineMeetingRoom",
|
||||
},
|
||||
]
|
||||
|
||||
const validateEvent = (event: Event) => {
|
||||
return (
|
||||
event &&
|
||||
event.eventType &&
|
||||
event.title &&
|
||||
event.schedule &&
|
||||
event.schedule.starts &&
|
||||
event.schedule.ends
|
||||
)
|
||||
}
|
||||
|
||||
export const EventForm = (props: EventFormProps) => {
|
||||
const { miniMode, onSubmit, selectedDate } = props
|
||||
const [localEvent, setLocalEvent] = useState<Event>({
|
||||
title: "",
|
||||
state: ArEventStates.DRAFT,
|
||||
schedule: {},
|
||||
})
|
||||
|
||||
const onChange = (
|
||||
key: string,
|
||||
value: string | boolean | ArSchedules | ObjectType,
|
||||
) => {
|
||||
const eventClone = { ...localEvent }
|
||||
if (key === "isWholeDay" || key === "starts" || key === "ends") {
|
||||
if (!eventClone.schedule) {
|
||||
eventClone.schedule = {}
|
||||
}
|
||||
} else if (key !== "duration") {
|
||||
; (eventClone[key as keyof Event] as string | boolean | ArSchedules) =
|
||||
value as string
|
||||
}
|
||||
if (key === "isWholeDay" && eventClone.schedule) {
|
||||
eventClone.schedule.isWholeDay = value as boolean
|
||||
if (value) {
|
||||
eventClone.schedule.starts = toDate(selectedDate)
|
||||
eventClone.schedule.ends = toDate({
|
||||
...selectedDate,
|
||||
day: selectedDate.day + 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
if ((key === "starts" || key === "ends") && eventClone.schedule) {
|
||||
const timeDetails = (value as ObjectType).raw as ObjectType
|
||||
let date
|
||||
try {
|
||||
date =
|
||||
timeDetails &&
|
||||
timeDetails.hour &&
|
||||
toDate(
|
||||
selectedDate,
|
||||
+(timeDetails.hour || 0),
|
||||
+(timeDetails.minute || 0),
|
||||
timeDetails.ampm as string,
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn("Failed attempt to convert string to number")
|
||||
}
|
||||
date && (eventClone.schedule[key] = date)
|
||||
}
|
||||
if (key === "eventType" && value === "birthday") {
|
||||
if (!eventClone.schedule) {
|
||||
eventClone.schedule = {}
|
||||
}
|
||||
eventClone.schedule.isWholeDay = true
|
||||
eventClone.schedule.starts = toDate(selectedDate)
|
||||
eventClone.schedule.ends = toDate({
|
||||
...selectedDate,
|
||||
day: selectedDate.day + 1,
|
||||
})
|
||||
}
|
||||
if (key === "duration" && eventClone.schedule) {
|
||||
const durationData = ((value as ObjectType).data as ObjectType)?.data
|
||||
const currentHours = (eventClone.schedule.starts as Date)?.getHours()
|
||||
const currentMinutes = (eventClone.schedule.starts as Date)?.getMinutes()
|
||||
const currentAmPm = (eventClone.schedule.starts as Date)
|
||||
?.toLocaleString("en-US", { hour12: true })
|
||||
.split(" ")[1]
|
||||
const addHours = (durationData as ObjectType)?.hours
|
||||
const addMinutes = (durationData as ObjectType)?.minutes
|
||||
let endHours = currentHours,
|
||||
endMinutes = currentMinutes,
|
||||
ampm = currentAmPm,
|
||||
endsNextDay = false
|
||||
if (
|
||||
addMinutes &&
|
||||
currentMinutes &&
|
||||
currentMinutes + (addMinutes as number) > 59
|
||||
) {
|
||||
endMinutes = currentMinutes + (addMinutes as number) - 60
|
||||
endHours += 1
|
||||
}
|
||||
if (addHours && endHours + (addHours as number) >= 12) {
|
||||
endHours = endHours + (addHours as number) - 12
|
||||
if (ampm === "PM") {
|
||||
endsNextDay = true
|
||||
}
|
||||
ampm = ampm === "AM" ? "PM" : "AM"
|
||||
}
|
||||
eventClone.schedule.ends = toDate(
|
||||
endsNextDay
|
||||
? { ...selectedDate, day: selectedDate.day + 1 }
|
||||
: selectedDate,
|
||||
endHours,
|
||||
endMinutes,
|
||||
)
|
||||
}
|
||||
setLocalEvent(eventClone)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ar-EventForm p-2 mb-3 overflow-auto ${miniMode ? "w-100" : "border-right"
|
||||
}`}
|
||||
>
|
||||
<SegmentedControl
|
||||
classes="mb-3"
|
||||
segments={segments}
|
||||
onChange={(segment) => {
|
||||
onChange("eventType", segment.name)
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
classes="mb-3"
|
||||
label="Title"
|
||||
size={ArSizes.SMALL}
|
||||
onChange={(e) =>
|
||||
onChange("title", (e.target as HTMLInputElement).value)
|
||||
}
|
||||
required
|
||||
/>
|
||||
<TextArea
|
||||
classes="mb-3"
|
||||
label="Details"
|
||||
size={ArSizes.SMALL}
|
||||
onChange={(e) =>
|
||||
onChange("description", (e.target as HTMLTextAreaElement).value)
|
||||
}
|
||||
/>
|
||||
<Toggle
|
||||
classes="mb-3"
|
||||
label="Whole Day?"
|
||||
onChange={(checked: boolean) => onChange("isWholeDay", checked)}
|
||||
size={ArSizes.SMALL}
|
||||
isOn={localEvent?.schedule?.isWholeDay as boolean}
|
||||
hideStatus
|
||||
/>
|
||||
<TimeEntry
|
||||
classes="mb-3"
|
||||
label="Starts"
|
||||
size={ArSizes.SMALL}
|
||||
isDisabled={localEvent?.schedule?.isWholeDay as boolean}
|
||||
onChange={
|
||||
((value: ObjectType) => onChange("starts", value)) as FunctionType
|
||||
}
|
||||
required
|
||||
/>
|
||||
{localEvent?.eventType &&
|
||||
localEvent?.eventType !== ArEventTypes.MEETING && (
|
||||
<TimeEntry
|
||||
classes="mb-4"
|
||||
label="Ends"
|
||||
size={ArSizes.SMALL}
|
||||
isDisabled={localEvent?.schedule?.isWholeDay as boolean}
|
||||
onChange={
|
||||
((value: ObjectType) => onChange("ends", value)) as FunctionType
|
||||
}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
{localEvent?.eventType === ArEventTypes.MEETING && (
|
||||
<Select
|
||||
classes="mb-3"
|
||||
options={durationOptions}
|
||||
onSelectionChanged={(obj) => onChange("duration", obj)}
|
||||
size={ArSizes.SMALL}
|
||||
isDisabled={!localEvent.schedule || !localEvent.schedule.starts}
|
||||
/>
|
||||
)}
|
||||
<EventListItem
|
||||
classes="mb-4"
|
||||
event={localEvent}
|
||||
selectedEventType={segments.find(
|
||||
(segment) => segment.name === localEvent.eventType,
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
size={ArSizes.SMALL}
|
||||
content="OK"
|
||||
variant={ArButtonVariants.SUCCESS}
|
||||
onClick={() => {
|
||||
if (localEvent && validateEvent(localEvent)) {
|
||||
localEvent.state = ArEventStates.SCHEDULED
|
||||
onSubmit && onSubmit(localEvent)
|
||||
setLocalEvent({
|
||||
title: "",
|
||||
state: ArEventStates.DRAFT,
|
||||
schedule: {},
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
105
src/EventList.tsx
Normal file
105
src/EventList.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ReactNode } from "react"
|
||||
import { SegmentData } from "@armco/shared-components/entity"
|
||||
import Icon from "@armco/icon"
|
||||
import { EventListProps, Event } from "./types"
|
||||
import { segments } from "./EventForm"
|
||||
|
||||
const noEventsMessage = (
|
||||
<div className="ar-EventManager__no-events fw-bold flex-center h-100 flex-column">
|
||||
<span>No events on this date yet.</span>
|
||||
<span>Use the form on the left to select type and add an event.</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
export const EventListItem = ({
|
||||
classes,
|
||||
event,
|
||||
selectedEventType,
|
||||
}: {
|
||||
classes?: string
|
||||
event: Event
|
||||
selectedEventType?: SegmentData
|
||||
}) => {
|
||||
const titleJSX = event && <span>{event.title}</span>
|
||||
const startAtJSX = event?.schedule?.starts && (
|
||||
<span>
|
||||
. Starts:{" "}
|
||||
{event.schedule.starts?.toLocaleString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
const endsAtJSX = event?.schedule?.ends && (
|
||||
<span>
|
||||
, Ends:{" "}
|
||||
{event.schedule.ends?.toLocaleString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
|
||||
let preview: ReactNode = event?.title ? (
|
||||
<>
|
||||
{titleJSX}
|
||||
{event.schedule?.starts && (
|
||||
<>
|
||||
{startAtJSX}
|
||||
{event.schedule?.ends && endsAtJSX}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : null
|
||||
|
||||
if (event?.eventType) {
|
||||
preview = selectedEventType?.icon ? (
|
||||
<span>
|
||||
<Icon attributes={{ classes: "me-2" }} icon={selectedEventType.icon} />
|
||||
{preview}
|
||||
</span>
|
||||
) : (
|
||||
preview
|
||||
)
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className={`ar-EventListItem small d-inline-block fw-bold${classes ? " " + classes : ""
|
||||
}`}
|
||||
>
|
||||
{preview}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export const EventList = (props: EventListProps) => {
|
||||
const { events } = props
|
||||
return (
|
||||
<div className="ar-EventList flex-grow-1 p-3">
|
||||
{events && events.length > 0 ? (
|
||||
<ul className="list-unstyled">
|
||||
{events.map((event) => (
|
||||
<li>
|
||||
<EventListItem
|
||||
event={event}
|
||||
selectedEventType={segments.find(
|
||||
(segment) => segment.name === event.eventType,
|
||||
)}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
noEventsMessage
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
26
src/EventManager.component.scss
Normal file
26
src/EventManager.component.scss
Normal file
@@ -0,0 +1,26 @@
|
||||
.ar-EventManager {
|
||||
.ar-EventManager__body {
|
||||
height: calc(100% - 2rem - 1px);
|
||||
&:not(.is-mini) .ar-EventForm {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
.ar-EventList {
|
||||
line-height: 1.3rem;
|
||||
|
||||
.ar-EventListItem {
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.super-small {
|
||||
width: 3rem !important;
|
||||
}
|
||||
|
||||
.ar-EventManager__no-events {
|
||||
font-style: italic;
|
||||
color: var(--ar-color-disabled);
|
||||
}
|
||||
}
|
||||
57
src/EventManager.tsx
Normal file
57
src/EventManager.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { pad } from "@armco/utils/helper"
|
||||
import { ArButtonVariants, ArSizes } from "@armco/shared-components/enums"
|
||||
import Button from "@armco/shared-components/Button"
|
||||
import { EventManagerProps, Event } from "./types"
|
||||
import { ArCalViews } from "./enums"
|
||||
import { EventForm } from "./EventForm"
|
||||
import { EventList } from "./EventList"
|
||||
import "./EventManager.component.scss"
|
||||
|
||||
const EventManager = (props: EventManagerProps) => {
|
||||
const { events, miniMode, selectedDate, setCurrentView, onEventSubmit } =
|
||||
props
|
||||
const [localEvents, setLocalEvents] = useState<Array<Event>>()
|
||||
|
||||
useEffect(() => setLocalEvents(events), [events])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ar-EventManager position-absolute h-100 w-100${miniMode ? " is-mini" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="ar-EventManager__header border-bottom py-2 px-3 flex-v-center">
|
||||
<Button
|
||||
classes="me-3 super-small"
|
||||
preIcon="io5/IoArrowBackCircleOutline"
|
||||
content=""
|
||||
variant={ArButtonVariants.SECONDARY}
|
||||
size={ArSizes.XSMALL}
|
||||
onClick={() => setCurrentView(ArCalViews.CALENDAR)}
|
||||
/>
|
||||
<span className="fw-bold">
|
||||
{pad(selectedDate.day) +
|
||||
" - " +
|
||||
pad(selectedDate.month + 1) +
|
||||
" - " +
|
||||
selectedDate.year}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ar-EventManager__body d-flex">
|
||||
<EventForm
|
||||
selectedDate={selectedDate}
|
||||
miniMode={miniMode}
|
||||
onSubmit={(newEvent: Event) => {
|
||||
const localEventsClone = localEvents ? [...localEvents] : []
|
||||
localEventsClone.push(newEvent)
|
||||
setLocalEvents(localEventsClone)
|
||||
onEventSubmit && onEventSubmit(newEvent)
|
||||
}}
|
||||
/>
|
||||
{!miniMode && <EventList events={localEvents} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EventManager
|
||||
63
src/JustCalendar.component.scss
Normal file
63
src/JustCalendar.component.scss
Normal file
@@ -0,0 +1,63 @@
|
||||
.ar-JustCalendar {
|
||||
.ar-JustCalendar__day {
|
||||
width: calc(100% / 7);
|
||||
.ar-JustCalendar__day__date {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.ar-JustCalendar__day__event-btn {
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
transition: max-width 0.3s;
|
||||
left: calc(50% + 1rem);
|
||||
&.show {
|
||||
max-width: 2rem;
|
||||
}
|
||||
}
|
||||
&:not(.ar-JustCalendar__weekday):hover {
|
||||
background-color: var(--ar-bg-hover);
|
||||
.ar-JustCalendar__day__date {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
&.ar-JustCalendar__sibling-month {
|
||||
color: var(--ar-color-disabled);
|
||||
&.selected {
|
||||
.ar-JustCalendar__day__date {
|
||||
color: var(--ar-color-disabled-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.selected {
|
||||
.ar-JustCalendar__day__date {
|
||||
background-color: var(--ar-bg-selected-4-dark);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
&.selectable {
|
||||
background-color: var(--ar-bg-hover);
|
||||
}
|
||||
&__badge {
|
||||
top: 0.1rem;
|
||||
right: 0;
|
||||
}
|
||||
// &.today {
|
||||
// background-color: var(--ar-bg-selected-3-faded);
|
||||
// }
|
||||
}
|
||||
&.is-mini {
|
||||
width: 18rem;
|
||||
.ar-JustCalendar__day {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
.ar-JustCalendar__day__event-btn {
|
||||
left: calc(50% + 0.3rem);
|
||||
top: calc(50% - 0.8rem);
|
||||
&.show {
|
||||
max-width: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/JustCalendar.tsx
Normal file
169
src/JustCalendar.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import { useState } from "react"
|
||||
import { ArBadgeType, ArSizes } from "@armco/shared-components/enums"
|
||||
import Badge from "@armco/shared-components/Badge"
|
||||
import Icon from "@armco/icon"
|
||||
import { CalendarDate, DateItemProps, JustCalendarProps } from "./types"
|
||||
|
||||
const weekDays = [
|
||||
{
|
||||
name: "Monday",
|
||||
short: "Mo",
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
name: "Tuesday",
|
||||
short: "Tu",
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
name: "Wednesday",
|
||||
short: "We",
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
name: "Thursday",
|
||||
short: "Th",
|
||||
index: 3,
|
||||
},
|
||||
{
|
||||
name: "Friday",
|
||||
short: "Fr",
|
||||
index: 4,
|
||||
},
|
||||
{
|
||||
name: "Saturday",
|
||||
short: "Sa",
|
||||
index: 5,
|
||||
},
|
||||
{
|
||||
name: "Sunday",
|
||||
short: "Su",
|
||||
index: 6,
|
||||
},
|
||||
]
|
||||
const areSame = (date: Date | CalendarDate, calDate: CalendarDate) => {
|
||||
if ("day" in date) {
|
||||
date = date as CalendarDate
|
||||
return (
|
||||
calDate.day === date.day &&
|
||||
calDate.month === date.month &&
|
||||
calDate.year === date.year
|
||||
)
|
||||
} else {
|
||||
date = date as Date
|
||||
return (
|
||||
calDate.day === date.getDate() &&
|
||||
calDate.month === date.getMonth() &&
|
||||
calDate.year === date.getFullYear()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const DateItem = (props: DateItemProps) => {
|
||||
const {
|
||||
allowEventSetting,
|
||||
calHelper,
|
||||
date,
|
||||
hovered: conditionalHovered,
|
||||
isSingleSelect,
|
||||
isToday,
|
||||
miniMode,
|
||||
onDateSelected,
|
||||
setHovered: setConditionalHovered,
|
||||
} = props
|
||||
const [hovered, setHovered] = useState<boolean>()
|
||||
return (
|
||||
<li
|
||||
className={`ar-JustCalendar__day flex-center cursor-pointer position-relative${date.siblingMonth ? " ar-JustCalendar__sibling-month" : ""
|
||||
}${calHelper?.isDateSelected(date) ? " selected" : ""}${isToday ? " today" : ""
|
||||
}${calHelper?.isDateSelected(date, conditionalHovered) ? " selectable" : ""
|
||||
}`}
|
||||
onClick={() => onDateSelected(date)}
|
||||
onMouseOver={() => {
|
||||
setHovered(true)
|
||||
if (!!calHelper?.startDate && !calHelper.endDate && !isSingleSelect) {
|
||||
setConditionalHovered(date)
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
<span className="ar-JustCalendar__day__date flex-center position-relative">
|
||||
{isToday && (
|
||||
<Badge
|
||||
type={ArBadgeType.COMPLETE}
|
||||
size={ArSizes.SMALL}
|
||||
classes="ar-JustCalendar__day__badge position-absolute"
|
||||
/>
|
||||
)}
|
||||
{date.day}
|
||||
</span>
|
||||
{allowEventSetting && (
|
||||
<span
|
||||
className={`ar-JustCalendar__day__event-btn position-absolute${hovered ? " show" : ""
|
||||
}`}
|
||||
onClick={() => onDateSelected(date, allowEventSetting && hovered)}
|
||||
>
|
||||
<Icon
|
||||
icon="io.IoIosAddCircle"
|
||||
attributes={{
|
||||
colors: {
|
||||
fillColor: "#5cb85cb3",
|
||||
hoverFillColor: "#5cb85c",
|
||||
},
|
||||
size: miniMode ? "0.8rem" : "1.5rem",
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
const JustCalendar = (props: JustCalendarProps) => {
|
||||
const {
|
||||
allowEventSetting,
|
||||
calendar,
|
||||
calHelper,
|
||||
endDate,
|
||||
hovered,
|
||||
isSingleSelect,
|
||||
miniMode,
|
||||
setHovered,
|
||||
onDateSelected,
|
||||
startDate,
|
||||
} = props
|
||||
return (
|
||||
<ul
|
||||
className={`ar-JustCalendar list-unstyled d-flex flex-wrap flex-1 position-relative mb-0 ${miniMode ? "is-mini p-1" : "p-3"
|
||||
}`}
|
||||
onMouseLeave={() => setHovered(null)}
|
||||
>
|
||||
{weekDays.map((weekday) => (
|
||||
<li className="ar-JustCalendar__day ar-Calendar__weekday fw-bold flex-center">
|
||||
<span className="ar-JustCalendar__day__short">{weekday.short}</span>
|
||||
</li>
|
||||
))}
|
||||
{calendar?.map((date) => {
|
||||
const isToday = date && areSame(new Date(), date)
|
||||
const liElement = date ? (
|
||||
<DateItem
|
||||
allowEventSetting={allowEventSetting}
|
||||
calHelper={calHelper}
|
||||
date={date}
|
||||
hovered={hovered}
|
||||
isSingleSelect={isSingleSelect}
|
||||
isToday={isToday}
|
||||
miniMode={miniMode}
|
||||
onDateSelected={onDateSelected}
|
||||
setHovered={setHovered}
|
||||
/>
|
||||
) : (
|
||||
<li />
|
||||
)
|
||||
return liElement
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default JustCalendar
|
||||
72
src/MonthNavigator.component.scss
Normal file
72
src/MonthNavigator.component.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
.ar-MonthNavigator {
|
||||
&__month {
|
||||
font-weight: bold;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
&__nav-button {
|
||||
transition: width 0.3s;
|
||||
width: calc(100% / 5);
|
||||
padding: 0.5rem 0;
|
||||
&:hover {
|
||||
background-color: var(--ar-bg-hover);
|
||||
}
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
background-color: var(--ar-color-disabled-3);
|
||||
color: var(--ar-color-disabled-2);
|
||||
}
|
||||
}
|
||||
&.is-mini {
|
||||
.ar-MonthNavigator {
|
||||
&__month {
|
||||
width: 48%;
|
||||
}
|
||||
&__nav-button:not(.ar-MonthNavigator__month) {
|
||||
width: 13%;
|
||||
}
|
||||
}
|
||||
&.to-month-year {
|
||||
.ar-MonthNavigator {
|
||||
&__month {
|
||||
width: 70%;
|
||||
}
|
||||
&__nav-button:first-child, &__nav-button:last-child {
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
// &.to-month-selector {
|
||||
// .ar-MonthNavigator {
|
||||
// &__nav-button {
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
&.to-month-year {
|
||||
.ar-MonthNavigator {
|
||||
&__nav-button {
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
&__nav-button:first-child, &__nav-button:last-child {
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
// &.to-month-selector {
|
||||
// .ar-MonthNavigator {
|
||||
// &__nav-button {
|
||||
// width: 100%;
|
||||
// }
|
||||
// &__nav-button:nth-child(2), &__nav-button:nth-child(4) {
|
||||
// width: 0;
|
||||
// overflow: hidden;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
174
src/MonthNavigator.tsx
Normal file
174
src/MonthNavigator.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import { ArThemes } from "@armco/utils"
|
||||
import Icon from "@armco/icon"
|
||||
import { MonthNavigatorProps } from "./types"
|
||||
import { ArCalViews, ArMonthSelectorViews } from "./enums"
|
||||
import { CalHelper } from "./dateHelper"
|
||||
import { MONTH_INDEX } from "./configs"
|
||||
|
||||
const getSelectedDecade = (refDate: Date, selectedDecade?: number) => {
|
||||
const useYear = selectedDecade || refDate.getFullYear()
|
||||
const start = Math.floor(useYear / 10) * 10
|
||||
const end = start + 10
|
||||
|
||||
return start + 1 + " - " + end
|
||||
}
|
||||
|
||||
const getSelectedCentury = (refDate: Date, selectedDecade?: number) => {
|
||||
const useDecade = selectedDecade || refDate.getFullYear()
|
||||
const start = Math.floor(useDecade / 100) * 100
|
||||
const end = start + 100
|
||||
|
||||
return start + 1 + " - " + end
|
||||
}
|
||||
|
||||
const MonthNavigator = (props: MonthNavigatorProps) => {
|
||||
const {
|
||||
currentDecade,
|
||||
currentMonthNavView,
|
||||
currentView,
|
||||
currentYear,
|
||||
miniMode,
|
||||
refDate,
|
||||
setCurrentDecade,
|
||||
setCurrentMonthNavView,
|
||||
setCurrentView,
|
||||
setCurrentYear,
|
||||
setRefDate,
|
||||
theme,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`ar-MonthNavigator border-bottom d-flex${miniMode ? " is-mini px-0 py-1" : " px-3 py-2"
|
||||
}${currentView === ArCalViews.MONTH_YEAR_SELECTOR ? " to-month-year" : ""
|
||||
}${ArMonthSelectorViews.MONTH === currentMonthNavView
|
||||
? " to-month-selector"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div className="d-flex w-100">
|
||||
<div
|
||||
className="ar-MonthNavigator__nav-button flex-center cursor-pointer"
|
||||
onClick={() => setRefDate(CalHelper.getPreviousYear(refDate))}
|
||||
>
|
||||
<Icon
|
||||
icon="ai/AiOutlineDoubleLeft"
|
||||
attributes={{
|
||||
colors: {
|
||||
fillColor: theme === ArThemes.DARK1 ? "white" : "black",
|
||||
},
|
||||
size: "1rem",
|
||||
strokeWidth: miniMode ? "30" : "100",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ar-MonthNavigator__nav-button flex-center cursor-pointer"
|
||||
onClick={() => {
|
||||
if (currentView === ArCalViews.CALENDAR) {
|
||||
setRefDate(CalHelper.getPreviousMonth(refDate))
|
||||
} else {
|
||||
currentMonthNavView === ArMonthSelectorViews.MONTH
|
||||
? setCurrentYear((currentYear || refDate.getFullYear()) - 1)
|
||||
: setCurrentDecade(
|
||||
(currentDecade || 2000) -
|
||||
(currentMonthNavView === ArMonthSelectorViews.DECADE
|
||||
? 100
|
||||
: 10),
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="ai/AiOutlineLeft"
|
||||
attributes={{
|
||||
colors: {
|
||||
fillColor: theme === ArThemes.DARK1 ? "white" : "black",
|
||||
},
|
||||
size: "1rem",
|
||||
strokeWidth: miniMode ? "30" : "100",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`ar-MonthNavigator__nav-button ar-MonthNavigator__month d-inline-flex flex-center cursor-pointer overflow-auto${currentMonthNavView === ArMonthSelectorViews.DECADE
|
||||
? " disabled"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (currentView === ArCalViews.CALENDAR) {
|
||||
setCurrentView(ArCalViews.MONTH_YEAR_SELECTOR)
|
||||
}
|
||||
if (currentMonthNavView === ArMonthSelectorViews.MONTH) {
|
||||
setCurrentMonthNavView(ArMonthSelectorViews.YEAR)
|
||||
} else if (currentMonthNavView === ArMonthSelectorViews.YEAR) {
|
||||
setCurrentMonthNavView(ArMonthSelectorViews.DECADE)
|
||||
} else {
|
||||
setCurrentMonthNavView(ArMonthSelectorViews.MONTH)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentView === ArCalViews.MONTH_YEAR_SELECTOR ? (
|
||||
currentMonthNavView === ArMonthSelectorViews.MONTH ? (
|
||||
currentYear || refDate.getFullYear()
|
||||
) : currentMonthNavView === ArMonthSelectorViews.YEAR ? (
|
||||
getSelectedDecade(refDate, currentDecade)
|
||||
) : (
|
||||
getSelectedCentury(refDate, currentDecade)
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<span className="me-1">{MONTH_INDEX[refDate.getMonth()]}</span>
|
||||
<span>{refDate.getFullYear()}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="ar-MonthNavigator__nav-button flex-center cursor-pointer"
|
||||
onClick={() => {
|
||||
if (currentView === ArCalViews.CALENDAR) {
|
||||
setRefDate(CalHelper.getNextMonth(refDate))
|
||||
} else {
|
||||
currentMonthNavView === ArMonthSelectorViews.MONTH
|
||||
? setCurrentYear((currentYear || refDate.getFullYear()) + 1)
|
||||
: setCurrentDecade(
|
||||
(currentDecade || 2000) +
|
||||
(currentMonthNavView === ArMonthSelectorViews.DECADE
|
||||
? 100
|
||||
: 10),
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="ai.AiOutlineRight"
|
||||
attributes={{
|
||||
colors: {
|
||||
fillColor: theme === ArThemes.DARK1 ? "white" : "black",
|
||||
},
|
||||
size: "1rem",
|
||||
strokeWidth: miniMode ? "30" : "100",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="ar-MonthNavigator__nav-button flex-center cursor-pointer"
|
||||
onClick={() => setRefDate(CalHelper.getNextYear(refDate))}
|
||||
>
|
||||
<Icon
|
||||
icon="ai.AiOutlineDoubleRight"
|
||||
attributes={{
|
||||
colors: {
|
||||
fillColor: theme === ArThemes.DARK1 ? "white" : "black",
|
||||
},
|
||||
size: "1rem",
|
||||
strokeWidth: miniMode ? "30" : "100",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MonthNavigator
|
||||
18
src/MonthSelector.component.scss
Executable file
18
src/MonthSelector.component.scss
Executable file
@@ -0,0 +1,18 @@
|
||||
.ar-MonthSelector {
|
||||
& > * {
|
||||
transition: left 0.3s;
|
||||
}
|
||||
&__decade-selector, &__year-selector, &__month-selector {
|
||||
&__decade-padder, &__year-padder, &__month-padder {
|
||||
width: calc(100% / 4);
|
||||
}
|
||||
&__decade:hover, &__year:hover, &__month:hover {
|
||||
background-color: var(--ar-bg-hover);
|
||||
}
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
&__decade-selector__decade-padder, &__year-selector__year-padder, &__month-selector__month-padder {
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
132
src/MonthSelector.tsx
Normal file
132
src/MonthSelector.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { MonthSelectorProps } from "./types"
|
||||
import { ArCalViews, ArMonthSelectorViews } from "./enums"
|
||||
import { MONTH_INDEX } from "./configs"
|
||||
import { sub } from "./dateHelper"
|
||||
import "./MonthSelector.component.scss"
|
||||
|
||||
const generateDecades = (currentDecade: number) => {
|
||||
let startDecade = +(("" + currentDecade).substring(0, 2) + "00")
|
||||
const tillDecade = startDecade + 100
|
||||
const decades = []
|
||||
while (startDecade < tillDecade) {
|
||||
decades.push({ start: startDecade + 1, end: startDecade + 10 })
|
||||
startDecade += 10
|
||||
}
|
||||
return decades
|
||||
}
|
||||
|
||||
const generateYears = (currentYear: number) => {
|
||||
let startYear = currentYear + 1
|
||||
const tillYear = startYear + 10
|
||||
const years = []
|
||||
while (startYear < tillYear) {
|
||||
years.push(startYear)
|
||||
startYear++
|
||||
}
|
||||
return years
|
||||
}
|
||||
|
||||
const MonthSelector = (props: MonthSelectorProps) => {
|
||||
const {
|
||||
currentDecade,
|
||||
currentMonthNavView,
|
||||
currentYear,
|
||||
miniMode,
|
||||
onMonthSelect,
|
||||
setCurrentDecade,
|
||||
setCurrentMonthNavView,
|
||||
setCurrentView,
|
||||
setCurrentYear,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className="ar-MonthSelector position-absolute h-100 d-flex w-100 overflow-hidden">
|
||||
<div
|
||||
className={`ar-MonthSelector__decade-selector position-absolute h-100 w-100 d-flex flex-wrap${currentMonthNavView === ArMonthSelectorViews.DECADE
|
||||
? " start-0"
|
||||
: " start-100"
|
||||
}`}
|
||||
>
|
||||
{currentDecade &&
|
||||
generateDecades(currentDecade).map((decade) => {
|
||||
return (
|
||||
<div
|
||||
className={`ar-MonthSelector__decade-selector__decade-padder d-flex ${miniMode ? "p-0" : "p-3"
|
||||
}`}
|
||||
onClick={() => {
|
||||
setCurrentDecade(decade.start - 1)
|
||||
setCurrentMonthNavView(ArMonthSelectorViews.YEAR)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`ar-MonthSelector__decade-selector__decade flex-center flex-1 cursor-pointer${!miniMode ? " border border-radius" : ""
|
||||
}`}
|
||||
>
|
||||
{decade.start + " - " + sub(decade.end)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`ar-MonthSelector__year-selector position-absolute w-100 h-100 d-flex flex-wrap${currentMonthNavView === ArMonthSelectorViews.YEAR
|
||||
? " start-0"
|
||||
: currentMonthNavView === ArMonthSelectorViews.MONTH
|
||||
? " start-100"
|
||||
: " start-n100"
|
||||
}`}
|
||||
>
|
||||
{currentDecade &&
|
||||
generateYears(currentDecade).map((year) => {
|
||||
return (
|
||||
<div
|
||||
className={`ar-MonthSelector__year-selector__year-padder d-flex ${miniMode ? "p-0" : "p-3"
|
||||
}`}
|
||||
onClick={() => {
|
||||
setCurrentYear(year)
|
||||
setCurrentMonthNavView(ArMonthSelectorViews.MONTH)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`ar-MonthSelector__year-selector__year flex-center flex-1 cursor-pointer${!miniMode ? " border border-radius" : ""
|
||||
}`}
|
||||
>
|
||||
{year}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`ar-MonthSelector__month-selector position-absolute w-100 h-100 d-flex flex-wrap${currentMonthNavView === ArMonthSelectorViews.MONTH
|
||||
? " start-0"
|
||||
: " start-n100"
|
||||
}`}
|
||||
>
|
||||
{MONTH_INDEX.map((month, index) => (
|
||||
<div
|
||||
className={`ar-MonthSelector__month-selector__month-padder d-flex ${miniMode ? "p-0" : "p-3"
|
||||
}`}
|
||||
onClick={() => {
|
||||
const date = new Date()
|
||||
date.setMonth(index)
|
||||
date.setFullYear(+(currentYear || date.getFullYear()))
|
||||
onMonthSelect(date)
|
||||
setCurrentMonthNavView(null)
|
||||
setCurrentView(ArCalViews.CALENDAR)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`ar-MonthSelector__month-selector__month flex-center flex-1 cursor-pointer${!miniMode ? " border border-radius" : ""
|
||||
}`}
|
||||
>
|
||||
{month}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MonthSelector
|
||||
14
src/configs.ts
Normal file
14
src/configs.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const MONTH_INDEX = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
]
|
||||
547
src/dateHelper.ts
Normal file
547
src/dateHelper.ts
Normal file
@@ -0,0 +1,547 @@
|
||||
import { pad } from "@armco/utils/helper"
|
||||
import { ArDateFormats } from "./enums"
|
||||
import { CalendarDate, Event } from "./types"
|
||||
import { MONTH_INDEX } from "./configs"
|
||||
|
||||
export interface CalendarOptions {
|
||||
/**
|
||||
* Date object indicating the selected start date
|
||||
*/
|
||||
startDate?: CalendarDate | null
|
||||
|
||||
/**
|
||||
* Date object indicating the selected end date
|
||||
*/
|
||||
endDate?: CalendarDate | null
|
||||
|
||||
/**
|
||||
* Calculate dates from sibling months (before and after the current month, based on weekStart)
|
||||
*/
|
||||
siblingMonths?: boolean
|
||||
|
||||
/**
|
||||
* Calculate the week days
|
||||
*/
|
||||
weekNumbers?: boolean
|
||||
|
||||
/**
|
||||
* Day of the week to start the calendar, respects `Date.prototype.getDay` (defaults to `0`, Sunday)
|
||||
*/
|
||||
weekStart?: number
|
||||
}
|
||||
|
||||
export const sub = (num: number, separator?: string) => {
|
||||
return ("" + num).substring(2) + (separator ? separator : "")
|
||||
}
|
||||
|
||||
export const str = (num: number, trim?: boolean, separator?: string) => {
|
||||
const month = MONTH_INDEX[num]
|
||||
return (trim ? month.substring(0, 4) : month) + (separator ? separator : "")
|
||||
}
|
||||
|
||||
export type CalendarInstance = Calendar
|
||||
/**
|
||||
* Calendar object
|
||||
*/
|
||||
class Calendar {
|
||||
startDate: CalendarDate | null
|
||||
endDate: CalendarDate | null
|
||||
siblingMonths: boolean
|
||||
weekNumbers: boolean
|
||||
weekStart: number
|
||||
|
||||
/**
|
||||
* Calendar constructor
|
||||
*
|
||||
* @param options Calendar options
|
||||
*/
|
||||
constructor({
|
||||
startDate = null,
|
||||
endDate = null,
|
||||
siblingMonths = false,
|
||||
weekNumbers = false,
|
||||
weekStart = 0,
|
||||
}: CalendarOptions = {}) {
|
||||
this.startDate = startDate
|
||||
this.endDate = endDate
|
||||
this.siblingMonths = siblingMonths
|
||||
this.weekNumbers = weekNumbers
|
||||
this.weekStart = weekStart
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a calendar month
|
||||
*
|
||||
* @param year Year
|
||||
* @param month Month [0-11]
|
||||
* @return Calendar days
|
||||
*/
|
||||
getCalendar(year: number, month: number) {
|
||||
const date = new Date(Date.UTC(year, month, 1, 0, 0, 0, 0))
|
||||
|
||||
year = date.getFullYear()
|
||||
month = date.getMonth()
|
||||
|
||||
const calendar: (CalendarDate | false)[] = []
|
||||
const firstDay = date.getDay()
|
||||
const firstDate = -((7 - this.weekStart + firstDay) % 7)
|
||||
const lastDate = Calendar.daysInMonth(year, month)
|
||||
const lastDay = (lastDate - firstDate) % 7
|
||||
const lastDatePreviousMonth = Calendar.daysInMonth(year, month - 1)
|
||||
|
||||
let i = firstDate
|
||||
let currentDay
|
||||
let currentDate
|
||||
let currentDateObject: CalendarDate | false = false
|
||||
let currentWeekNumber = null
|
||||
let otherMonth
|
||||
let otherYear
|
||||
|
||||
const max = lastDate - i + (lastDay !== 0 ? 7 - lastDay : 0) + firstDate
|
||||
|
||||
while (i < max) {
|
||||
currentDate = i + 1
|
||||
currentDay = ((i < 1 ? 7 + i : i) + firstDay) % 7
|
||||
if (currentDate < 1 || currentDate > lastDate) {
|
||||
if (this.siblingMonths) {
|
||||
if (currentDate < 1) {
|
||||
otherMonth = month - 1
|
||||
otherYear = year
|
||||
if (otherMonth < 0) {
|
||||
otherMonth = 11
|
||||
otherYear--
|
||||
}
|
||||
currentDate = lastDatePreviousMonth + currentDate
|
||||
} else if (currentDate > lastDate) {
|
||||
otherMonth = month + 1
|
||||
otherYear = year
|
||||
if (otherMonth > 11) {
|
||||
otherMonth = 0
|
||||
otherYear++
|
||||
}
|
||||
currentDate = i - lastDate + 1
|
||||
}
|
||||
|
||||
if (otherMonth !== undefined && otherYear !== undefined) {
|
||||
currentDateObject = {
|
||||
day: currentDate,
|
||||
weekDay: currentDay,
|
||||
month: otherMonth,
|
||||
year: otherYear,
|
||||
siblingMonth: true,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentDateObject = false
|
||||
}
|
||||
} else {
|
||||
currentDateObject = {
|
||||
day: currentDate,
|
||||
weekDay: currentDay,
|
||||
month: month,
|
||||
year: year,
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDateObject && this.weekNumbers) {
|
||||
if (currentWeekNumber === null) {
|
||||
currentWeekNumber = Calendar.calculateWeekNumber(currentDateObject)
|
||||
} else if (currentDay === 1 && currentWeekNumber === 52) {
|
||||
currentWeekNumber = 1
|
||||
} else if (currentDay === 1) {
|
||||
currentWeekNumber++
|
||||
}
|
||||
currentDateObject.weekNumber = currentWeekNumber
|
||||
}
|
||||
|
||||
if (currentDateObject && this.startDate) {
|
||||
currentDateObject.selected = this.isDateSelected(currentDateObject)
|
||||
}
|
||||
|
||||
calendar.push(currentDateObject)
|
||||
i++
|
||||
}
|
||||
|
||||
return calendar
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a date is selected
|
||||
*
|
||||
* @param date Date object
|
||||
* @return Selected status of the date
|
||||
*/
|
||||
isDateSelected(date: CalendarDate, endDate?: CalendarDate | null) {
|
||||
// Hack to disregard this.endDate in further calculations for finding selectable candidates (hovered)
|
||||
// by explicitly sending a "null" endDate, if endDate is not sent at all (undefined) then only function behaves as
|
||||
// initially intended and considers endDate for calculations
|
||||
if (!this.startDate || endDate === null) {
|
||||
return false
|
||||
}
|
||||
let startDate = this.startDate
|
||||
if (endDate) {
|
||||
if (Calendar.compare(this.startDate, endDate) === 1) {
|
||||
startDate = endDate
|
||||
endDate = this.startDate
|
||||
}
|
||||
} else {
|
||||
endDate = this.endDate
|
||||
}
|
||||
if (
|
||||
date.year === startDate.year &&
|
||||
date.month === startDate.month &&
|
||||
date.day === startDate.day
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!endDate) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
date.year === startDate.year &&
|
||||
date.month === startDate.month &&
|
||||
date.day < startDate.day
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
date.year === endDate.year &&
|
||||
date.month === endDate.month &&
|
||||
date.day > endDate.day
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (date.year === startDate.year && date.month < startDate.month) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (date.year === endDate.year && date.month > endDate.month) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (date.year < startDate.year) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (date.year > endDate.year) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected period start
|
||||
*
|
||||
* @param date Date object
|
||||
*/
|
||||
setStartDate(date: CalendarDate | null) {
|
||||
this.startDate = date
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected period end
|
||||
*
|
||||
* @param date Date object
|
||||
*/
|
||||
setEndDate(date: CalendarDate | null) {
|
||||
this.endDate = date
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets one selected date
|
||||
*
|
||||
* @param date Date object
|
||||
*/
|
||||
setDate(date: CalendarDate) {
|
||||
return this.setStartDate(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the difference between two dates (date1 - date2), in days
|
||||
*
|
||||
* @param dateLeft Date object
|
||||
* @param dateRight Date object
|
||||
* @return Days between the dates
|
||||
*/
|
||||
static diff(dateLeft: CalendarDate, dateRight: CalendarDate) {
|
||||
const dateLeftDate = new Date(
|
||||
Date.UTC(dateLeft.year, dateLeft.month, dateLeft.day, 0, 0, 0, 0),
|
||||
)
|
||||
const dateRightDate = new Date(
|
||||
Date.UTC(dateRight.year, dateRight.month, dateRight.day, 0, 0, 0, 0),
|
||||
)
|
||||
return Math.ceil(
|
||||
(dateLeftDate.getTime() - dateRightDate.getTime()) / 86400000,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the interval between two dates
|
||||
*
|
||||
* @param dateLeft Date object
|
||||
* @param dateRight Date object
|
||||
* @return Number of days between dates
|
||||
*/
|
||||
static interval(dateLeft: CalendarDate, dateRight: CalendarDate) {
|
||||
return Math.abs(Calendar.diff(dateLeft, dateRight)) + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly compare two dates
|
||||
*
|
||||
* @param dateLeft Left `CalendarDate` object
|
||||
* @param dateRight Right `CalendarDate` object
|
||||
* @return Comparison result: -1 (left < right), 0 (equal) or 1 (left > right)
|
||||
*/
|
||||
static compare(dateLeft: CalendarDate, dateRight: CalendarDate) {
|
||||
if (
|
||||
typeof dateLeft !== "object" ||
|
||||
typeof dateRight !== "object" ||
|
||||
dateLeft === null ||
|
||||
dateRight === null
|
||||
) {
|
||||
throw new TypeError("dates must be objects")
|
||||
}
|
||||
|
||||
if (dateLeft.year < dateRight.year) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (dateLeft.year > dateRight.year) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (dateLeft.month < dateRight.month) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (dateLeft.month > dateRight.month) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (dateLeft.day < dateRight.day) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if (dateLeft.day > dateRight.day) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of days in a month
|
||||
*
|
||||
* @param year Year
|
||||
* @param month Month [0-11]
|
||||
* @return Length of the month
|
||||
*/
|
||||
static daysInMonth(year: number, month: number) {
|
||||
return new Date(year, month + 1, 0).getDate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates if a given year is a leap year
|
||||
*
|
||||
* @param year Year
|
||||
* @return Leap year or not
|
||||
*/
|
||||
static isLeapYear(year: number) {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the week number for a given date
|
||||
*
|
||||
* @param date Date object
|
||||
* @return Week number
|
||||
*/
|
||||
// Adapted from http://techblog.procurios.nl/k/news/view/33796/14863/calculate-iso-8601-week-and-year-in-javascript.html
|
||||
static calculateWeekNumber(date: CalendarDate) {
|
||||
// Creates the requested date
|
||||
const current = new Date(
|
||||
Date.UTC(date.year, date.month, date.day, 0, 0, 0, 0),
|
||||
)
|
||||
|
||||
// Create a copy of the object
|
||||
const target = new Date(current.valueOf())
|
||||
|
||||
// ISO week date weeks start on monday so correct the day number
|
||||
const dayNr = (current.getUTCDay() + 6) % 7
|
||||
|
||||
// ISO 8601 states that week 1 is the week with the first thursday of that
|
||||
// year. Set the target date to the thursday in the target week.
|
||||
target.setUTCDate(target.getUTCDate() - dayNr + 3)
|
||||
|
||||
// Store the millisecond value of the target date
|
||||
const firstThursday = target.valueOf()
|
||||
|
||||
// Set the target to the first thursday of the year
|
||||
|
||||
// First set the target to january first
|
||||
target.setUTCMonth(0, 1)
|
||||
|
||||
// Not a thursday? Correct the date to the next thursday
|
||||
if (target.getUTCDay() !== 4) {
|
||||
target.setUTCMonth(0, 1 + ((4 - target.getUTCDay() + 7) % 7))
|
||||
}
|
||||
|
||||
// The week number is the number of weeks between the first thursday of the
|
||||
// year and the thursday in the target week.
|
||||
// 604800000 = 7 * 24 * 3600 * 1000
|
||||
return 1 + Math.ceil((firstThursday - target.getTime()) / 604800000)
|
||||
}
|
||||
|
||||
static toString(
|
||||
date: CalendarDate,
|
||||
separator: string = "/",
|
||||
format: string = "DDMMYYYY",
|
||||
) {
|
||||
const day = pad(date.day, separator)
|
||||
const month = pad(date.month + 1, separator)
|
||||
const year = date.year
|
||||
|
||||
switch (format) {
|
||||
case ArDateFormats.MMDDYY:
|
||||
return month + day + sub(year)
|
||||
case ArDateFormats.DDMMYYYY:
|
||||
return day + month + year
|
||||
case ArDateFormats.MMDDYYYY:
|
||||
return month + day + year
|
||||
case ArDateFormats.DDMMMYY:
|
||||
return day + str(date.month + 1, true, separator) + sub(year)
|
||||
case ArDateFormats.DDMMMYYYY:
|
||||
return day + str(date.month + 1, true, separator) + year
|
||||
case ArDateFormats.MMMDDYY:
|
||||
return str(date.month + 1, true, separator) + day + sub(year)
|
||||
case ArDateFormats.MMMDDYYYY:
|
||||
return str(date.month + 1, true, separator) + day + year
|
||||
case ArDateFormats.YYMMDD:
|
||||
return sub(year) + month + day
|
||||
case ArDateFormats.YYMMMDD:
|
||||
return sub(year) + str(date.month + 1, true, separator) + day
|
||||
case ArDateFormats.YYYYMMDD:
|
||||
return year + month + day
|
||||
case ArDateFormats.YYYYMMMDD:
|
||||
return year + str(date.month + 1, true, separator) + day
|
||||
case ArDateFormats.DDMMYY:
|
||||
default:
|
||||
return day + month + sub(year)
|
||||
}
|
||||
}
|
||||
|
||||
static getPreviousMonth(date: Date) {
|
||||
const month = date.getMonth()
|
||||
const year = date.getFullYear()
|
||||
|
||||
const previousMonth = month === 0 ? 11 : month - 1
|
||||
const previousYear = month === 0 ? year - 1 : year
|
||||
|
||||
const returnDate = new Date()
|
||||
returnDate.setDate(date.getDate())
|
||||
returnDate.setMonth(previousMonth)
|
||||
returnDate.setFullYear(previousYear)
|
||||
|
||||
return returnDate
|
||||
}
|
||||
|
||||
static getNextMonth(date: Date) {
|
||||
const month = date.getMonth()
|
||||
const year = date.getFullYear()
|
||||
|
||||
const previousMonth = month === 11 ? 0 : month + 1
|
||||
const previousYear = month === 11 ? year + 1 : year
|
||||
|
||||
const returnDate = new Date()
|
||||
returnDate.setDate(date.getDate())
|
||||
returnDate.setMonth(previousMonth)
|
||||
returnDate.setFullYear(previousYear)
|
||||
|
||||
return returnDate
|
||||
}
|
||||
|
||||
static getPreviousYear(date: Date) {
|
||||
const year = date.getFullYear()
|
||||
const returnDate = new Date()
|
||||
returnDate.setDate(date.getDate())
|
||||
returnDate.setMonth(date.getMonth())
|
||||
returnDate.setFullYear(year === 1 ? 1 : year - 1)
|
||||
return returnDate
|
||||
}
|
||||
|
||||
static getNextYear(date: Date) {
|
||||
const year = date.getFullYear()
|
||||
const returnDate = new Date()
|
||||
returnDate.setDate(date.getDate())
|
||||
returnDate.setMonth(date.getMonth())
|
||||
returnDate.setFullYear(year + 1)
|
||||
return returnDate
|
||||
}
|
||||
}
|
||||
|
||||
export const toDate = (
|
||||
calDate: CalendarDate,
|
||||
hour?: number,
|
||||
minute?: number,
|
||||
ampm: string = "AM",
|
||||
) => {
|
||||
if (hour) {
|
||||
let adjustedHour = hour
|
||||
if (ampm === "PM" && hour !== 12) {
|
||||
adjustedHour += 12 // Add 12 hours for PM time, except when it's 12 PM
|
||||
} else if (ampm === "AM" && hour === 12) {
|
||||
adjustedHour = 0 // 12 AM is equivalent to 0 hours
|
||||
}
|
||||
|
||||
return new Date(
|
||||
calDate.year,
|
||||
calDate.month,
|
||||
calDate.day,
|
||||
adjustedHour,
|
||||
minute,
|
||||
)
|
||||
} else return new Date(calDate.year, calDate.month, calDate.day)
|
||||
}
|
||||
|
||||
export const isWithinSchedule = (date: CalendarDate, event: Event) => {
|
||||
const startDate = event.schedule?.starts
|
||||
const endDate = event.schedule?.ends
|
||||
|
||||
const startCalDate = startDate && {
|
||||
day: (startDate as Date).getDate(),
|
||||
month: (startDate as Date).getMonth(),
|
||||
year: (startDate as Date).getFullYear(),
|
||||
}
|
||||
|
||||
const endCalDate = endDate && {
|
||||
day: (endDate as Date).getDate(),
|
||||
month: (endDate as Date).getMonth(),
|
||||
year: (endDate as Date).getFullYear(),
|
||||
}
|
||||
|
||||
if (startCalDate && endCalDate) {
|
||||
const checkStart = Calendar.compare(date, startCalDate)
|
||||
const checkEnd = Calendar.compare(date, endCalDate)
|
||||
const amPm = (endDate as Date)
|
||||
?.toLocaleString("en-US", { hour12: true })
|
||||
.split(" ")[2]
|
||||
const shouldExcludeEnd =
|
||||
(endDate as Date).getHours() === 0 &&
|
||||
amPm === "AM" &&
|
||||
date.day === endCalDate.day
|
||||
|
||||
return checkStart >= 0 && checkEnd <= 0 && !shouldExcludeEnd
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the Calendar
|
||||
*/
|
||||
export { Calendar as CalHelper }
|
||||
338
src/dateformat.ts
Normal file
338
src/dateformat.ts
Normal file
@@ -0,0 +1,338 @@
|
||||
import { FunctionType } from "@armco/types"
|
||||
import { ArDateMasks } from "./enums"
|
||||
|
||||
const token =
|
||||
/d{1,4}|D{3,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|W{1,2}|[LlopSZN]|"[^"]*"|'[^']*'/g
|
||||
const timezone =
|
||||
/\b(?:[A-Z]{1,3}[A-Z][TC])(?:[-+]\d{4})?|((?:Australian )?(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time)\b/g
|
||||
const timezoneClip = /[^-+\dA-Z]/g
|
||||
|
||||
/**
|
||||
* @param {string | number | Date} date
|
||||
* @param {string} mask
|
||||
* @param {boolean} utc
|
||||
* @param {boolean} gmt
|
||||
*/
|
||||
export function dateFormat(
|
||||
date?: number | Date,
|
||||
mask?: ArDateMasks | string,
|
||||
utc?: boolean,
|
||||
gmt?: boolean,
|
||||
) {
|
||||
// You can't provide utc if you skip other args (use the 'UTC:' mask prefix)
|
||||
if (arguments.length === 1 && typeof date === "string" && !/\d/.test(date)) {
|
||||
mask = date
|
||||
date = undefined
|
||||
}
|
||||
|
||||
date = date || date === 0 ? date : new Date()
|
||||
|
||||
if (isNaN(date as number)) {
|
||||
throw TypeError("Invalid date")
|
||||
}
|
||||
|
||||
if (!(date instanceof Date)) {
|
||||
date = new Date(date)
|
||||
}
|
||||
|
||||
mask = String(
|
||||
ArDateMasksRecord[mask as string] || mask || ArDateMasksRecord["DEFAULT"],
|
||||
)
|
||||
|
||||
// Allow setting the utc/gmt argument via the mask
|
||||
const maskSlice = mask.slice(0, 4)
|
||||
if (maskSlice === "UTC:" || maskSlice === "GMT:") {
|
||||
mask = mask.slice(4)
|
||||
utc = true
|
||||
if (maskSlice === "GMT:") {
|
||||
gmt = true
|
||||
}
|
||||
}
|
||||
|
||||
const _ = () => (utc ? "getUTC" : "get")
|
||||
const d = () => (date as Date)[(_() + "Date") as "getDate" | "getUTCDate"]()
|
||||
const D = () => (date as Date)[(_() + "Day") as "getDay" | "getUTCDay"]()
|
||||
const m = () =>
|
||||
(date as Date)[(_() + "Month") as "getMonth" | "getUTCMonth"]()
|
||||
const y = () =>
|
||||
(date as Date)[(_() + "FullYear") as "getFullYear" | "getUTCFullYear"]()
|
||||
const H = () =>
|
||||
(date as Date)[(_() + "Hours") as "getHours" | "getUTCHours"]()
|
||||
const M = () =>
|
||||
(date as Date)[(_() + "Minutes") as "getMinutes" | "getUTCMinutes"]()
|
||||
const s = () =>
|
||||
(date as Date)[(_() + "Seconds") as "getSeconds" | "getUTCSeconds"]()
|
||||
const L = () =>
|
||||
(date as Date)[
|
||||
(_() + "Milliseconds") as "getMilliseconds" | "getUTCMilliseconds"
|
||||
]()
|
||||
const o = () => (utc ? 0 : (date as Date).getTimezoneOffset())
|
||||
const W = () => date instanceof Date && getWeek(date)
|
||||
const N = () => date instanceof Date && getDayOfWeek(date)
|
||||
|
||||
const flags: { [key: string]: FunctionType } = {
|
||||
d: () => d(),
|
||||
dd: () => pad(d()),
|
||||
ddd: () => i18n.dayNames[D()],
|
||||
DDD: () =>
|
||||
getDayName({
|
||||
y: y(),
|
||||
m: m(),
|
||||
d: d(),
|
||||
_: _(),
|
||||
dayName: i18n.dayNames[D()],
|
||||
short: true,
|
||||
}),
|
||||
dddd: () => i18n.dayNames[D() + 7],
|
||||
DDDD: () =>
|
||||
getDayName({
|
||||
y: y(),
|
||||
m: m(),
|
||||
d: d(),
|
||||
_: _(),
|
||||
dayName: i18n.dayNames[D() + 7],
|
||||
}),
|
||||
m: () => m() + 1,
|
||||
mm: () => pad(m() + 1),
|
||||
mmm: () => i18n.monthNames[m()],
|
||||
mmmm: () => i18n.monthNames[m() + 12],
|
||||
yy: () => String(y()).slice(2),
|
||||
yyyy: () => pad(y(), 4),
|
||||
h: () => H() % 12 || 12,
|
||||
hh: () => pad(H() % 12 || 12),
|
||||
H: () => H(),
|
||||
HH: () => pad(H()),
|
||||
M: () => M(),
|
||||
MM: () => pad(M()),
|
||||
s: () => s(),
|
||||
ss: () => pad(s()),
|
||||
l: () => pad(L(), 3),
|
||||
L: () => pad(Math.floor(L() / 10)),
|
||||
t: () => (H() < 12 ? i18n.timeNames[0] : i18n.timeNames[1]),
|
||||
tt: () => (H() < 12 ? i18n.timeNames[2] : i18n.timeNames[3]),
|
||||
T: () => (H() < 12 ? i18n.timeNames[4] : i18n.timeNames[5]),
|
||||
TT: () => (H() < 12 ? i18n.timeNames[6] : i18n.timeNames[7]),
|
||||
Z: () =>
|
||||
gmt ? "GMT" : utc ? "UTC" : date instanceof Date && formatTimezone(date),
|
||||
o: () =>
|
||||
(o() > 0 ? "-" : "+") +
|
||||
pad(Math.floor(Math.abs(o()) / 60) * 100 + (Math.abs(o()) % 60), 4),
|
||||
p: () =>
|
||||
(o() > 0 ? "-" : "+") +
|
||||
pad(Math.floor(Math.abs(o()) / 60), 2) +
|
||||
":" +
|
||||
pad(Math.floor(Math.abs(o()) % 60), 2),
|
||||
S: () =>
|
||||
["th", "st", "nd", "rd"][
|
||||
d() % 10 > 3 ? 0 : (+((d() % 100) - (d() % 10) !== 10) * d()) % 10
|
||||
],
|
||||
W: () => W(),
|
||||
WW: () => {
|
||||
const date = W()
|
||||
date && pad(date)
|
||||
},
|
||||
N: () => N(),
|
||||
}
|
||||
|
||||
return mask.replace(token, (match) => {
|
||||
if (match in flags) {
|
||||
return flags[match]()
|
||||
}
|
||||
return match.slice(1, match.length - 1)
|
||||
})
|
||||
}
|
||||
|
||||
const ArDateMasksRecord: Record<string, string> = {
|
||||
DEFAULT: ArDateMasks.DEFAULT,
|
||||
SHORTDATE: ArDateMasks.SHORTDATE,
|
||||
PADDEDSHORTDATE: ArDateMasks.PADDEDSHORTDATE,
|
||||
MEDIUMDATE: ArDateMasks.MEDIUMDATE,
|
||||
LONGDATE: ArDateMasks.LONGDATE,
|
||||
FULLDATE: ArDateMasks.FULLDATE,
|
||||
SHORTTIME: ArDateMasks.SHORTTIME,
|
||||
MEDIUMTIME: ArDateMasks.MEDIUMTIME,
|
||||
LONGTIME: ArDateMasks.LONGTIME,
|
||||
ISODATE: ArDateMasks.ISODATE,
|
||||
ISOTIME: ArDateMasks.ISOTIME,
|
||||
ISODATETIME: ArDateMasks.ISODATETIME,
|
||||
ISOUTCDATETIME: ArDateMasks.ISOUTCDATETIME,
|
||||
EXPIRESHEADERFORMAT: ArDateMasks.EXPIRESHEADERFORMAT,
|
||||
}
|
||||
|
||||
// Internationalization strings
|
||||
export let i18n = {
|
||||
dayNames: [
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
],
|
||||
monthNames: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
],
|
||||
timeNames: ["a", "p", "am", "pm", "A", "P", "AM", "PM"],
|
||||
}
|
||||
|
||||
const pad = (val: number, len = 2) => String(val).padStart(len, "0")
|
||||
|
||||
/**
|
||||
* Get day name
|
||||
* Yesterday, Today, Tomorrow if the date lies within, else fallback to Monday - Sunday
|
||||
* @param {Object}
|
||||
* @return {String}
|
||||
*/
|
||||
const getDayName = ({
|
||||
y,
|
||||
m,
|
||||
d,
|
||||
_,
|
||||
dayName,
|
||||
short = false,
|
||||
}: {
|
||||
y: number
|
||||
m: number
|
||||
d: number
|
||||
_: string
|
||||
dayName: string
|
||||
short?: boolean
|
||||
}) => {
|
||||
const today = new Date()
|
||||
const yesterday = new Date()
|
||||
const tomorrow = new Date()
|
||||
const dateAccessor = (_ + "Date") as "getDate" | "getUTCDate"
|
||||
const monthAccessor = (_ + "Month") as "getMonth" | "getUTCMonth"
|
||||
const yearAccessor = (_ + "FullYear") as "getFullYear" | "getUTCFullYear"
|
||||
yesterday.setDate(yesterday[dateAccessor]() - 1)
|
||||
tomorrow.setDate(tomorrow[dateAccessor]() + 1)
|
||||
const today_d = () => today[dateAccessor]()
|
||||
const today_m = () => today[monthAccessor]()
|
||||
const today_y = () => today[yearAccessor]()
|
||||
const yesterday_d = () => yesterday[dateAccessor]()
|
||||
const yesterday_m = () => yesterday[monthAccessor]()
|
||||
const yesterday_y = () => yesterday[yearAccessor]()
|
||||
const tomorrow_d = () => tomorrow[dateAccessor]()
|
||||
const tomorrow_m = () => tomorrow[monthAccessor]()
|
||||
const tomorrow_y = () => tomorrow[yearAccessor]()
|
||||
|
||||
if (today_y() === y && today_m() === m && today_d() === d) {
|
||||
return short ? "Tdy" : "Today"
|
||||
} else if (
|
||||
yesterday_y() === y &&
|
||||
yesterday_m() === m &&
|
||||
yesterday_d() === d
|
||||
) {
|
||||
return short ? "Ysd" : "Yesterday"
|
||||
} else if (tomorrow_y() === y && tomorrow_m() === m && tomorrow_d() === d) {
|
||||
return short ? "Tmw" : "Tomorrow"
|
||||
}
|
||||
return dayName
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ISO 8601 week number
|
||||
* Based on comments from
|
||||
* http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html
|
||||
*
|
||||
* @param {Date} `date`
|
||||
* @return {Number}
|
||||
*/
|
||||
const getWeek = (date: Date) => {
|
||||
// Remove time components of date
|
||||
const targetThursday = new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate(),
|
||||
)
|
||||
|
||||
// Change date to Thursday same week
|
||||
targetThursday.setDate(
|
||||
targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3,
|
||||
)
|
||||
|
||||
// Take January 4th as it is always in week 1 (see ISO 8601)
|
||||
const firstThursday = new Date(targetThursday.getFullYear(), 0, 4)
|
||||
|
||||
// Change date to Thursday same week
|
||||
firstThursday.setDate(
|
||||
firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3,
|
||||
)
|
||||
|
||||
// Check if daylight-saving-time-switch occurred and correct for it
|
||||
const ds =
|
||||
targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset()
|
||||
targetThursday.setHours(targetThursday.getHours() - ds)
|
||||
|
||||
// Number of weeks between target Thursday and first Thursday
|
||||
const weekDiff =
|
||||
(targetThursday.getTime() - firstThursday.getTime()) / (86400000 * 7)
|
||||
return 1 + Math.floor(weekDiff)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ISO-8601 numeric representation of the day of the week
|
||||
* 1 (for Monday) through 7 (for Sunday)
|
||||
*
|
||||
* @param {Date} `date`
|
||||
* @return {Number}
|
||||
*/
|
||||
const getDayOfWeek = (date: Date) => {
|
||||
let dow = date.getDay()
|
||||
if (dow === 0) {
|
||||
dow = 7
|
||||
}
|
||||
return dow
|
||||
}
|
||||
|
||||
/**
|
||||
* Get proper timezone abbreviation or timezone offset.
|
||||
*
|
||||
* This will fall back to `GMT+xxxx` if it does not recognize the
|
||||
* timezone within the `timezone` RegEx above. Currently only common
|
||||
* American and Australian timezone abbreviations are supported.
|
||||
*
|
||||
* @param {String | Date} date
|
||||
* @return {String}
|
||||
*/
|
||||
export const formatTimezone = (date: Date) => {
|
||||
const strDate = String(date)
|
||||
const match = strDate.match(timezone) || [""]
|
||||
return match
|
||||
.pop()
|
||||
?.replace(timezoneClip, "")
|
||||
.replace(/GMT\+0000/g, "UTC")
|
||||
}
|
||||
|
||||
export { dateFormat as DateFormatter }
|
||||
67
src/enums.ts
Normal file
67
src/enums.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
export enum ArDateFormats {
|
||||
DDMMYYYY = "DDMMYYYY",
|
||||
DDMMYY = "DDMMYY",
|
||||
MMDDYYYY = "MMDDYYYY",
|
||||
MMDDYY = "MMDDYY",
|
||||
YYMMDD = "YYMMDD",
|
||||
YYYYMMDD = "YYYYMMDD",
|
||||
DDMMMYY = "DDMMMYY",
|
||||
DDMMMYYYY = "DDMMMYYYY",
|
||||
MMMDDYY = "MMMDDYY",
|
||||
MMMDDYYYY = "MMMDDYYYY",
|
||||
YYYYMMMDD = "YYYYMMMDD",
|
||||
YYMMMDD = "YYMMMDD",
|
||||
}
|
||||
|
||||
export enum ArDateMasks {
|
||||
DEFAULT = "ddd mmm dd yyyy HH:MM:ss",
|
||||
SHORTDATE = "m/d/yy",
|
||||
PADDEDSHORTDATE = "mm/dd/yyyy",
|
||||
MEDIUMDATE = "mmm d, yyyy",
|
||||
LONGDATE = "mmmm d, yyyy",
|
||||
FULLDATE = "dddd, mmmm d, yyyy",
|
||||
SHORTTIME = "h:MM TT",
|
||||
MEDIUMTIME = "h:MM:ss TT",
|
||||
LONGTIME = "h:MM:ss TT Z",
|
||||
ISODATE = "yyyy-mm-dd",
|
||||
ISOTIME = "HH:MM:ss",
|
||||
ISODATETIME = "yyyy-mm-dd'T'HH:MM:sso",
|
||||
ISOUTCDATETIME = "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
|
||||
EXPIRESHEADERFORMAT = "ddd, dd mmm yyyy HH:MM:ss Z",
|
||||
}
|
||||
|
||||
export enum ArCalViews {
|
||||
CALENDAR = "calendar",
|
||||
MONTH_YEAR_SELECTOR = "month-year-selector",
|
||||
EVENT_FORM = "event-form",
|
||||
}
|
||||
|
||||
export enum ArMonthSelectorViews {
|
||||
DECADE = "decade",
|
||||
YEAR = "year",
|
||||
MONTH = "month",
|
||||
}
|
||||
|
||||
export enum ArEventStates {
|
||||
DRAFT = "draft",
|
||||
SCHEDULED = "scheduled",
|
||||
EXPIRED = "expired",
|
||||
CANCELLED = "cancelled",
|
||||
DEFERRED = "deferred",
|
||||
}
|
||||
|
||||
export enum ArEventTypes {
|
||||
TASK = "task",
|
||||
EVENT = "event",
|
||||
BIRTHDAY = "birthday",
|
||||
MEETING = "meeting",
|
||||
}
|
||||
|
||||
export enum ArSchedules {
|
||||
TODAY = "today",
|
||||
TOMORROW = "tomorrow",
|
||||
NEXTWEEK = "next-week",
|
||||
NEXTMONTH = "next-month",
|
||||
CUSTOM = "custom",
|
||||
}
|
||||
9
src/index.ts
Executable file
9
src/index.ts
Executable file
@@ -0,0 +1,9 @@
|
||||
export { default } from "./Calendar"
|
||||
export { default as DatePicker } from "./DatePicker"
|
||||
export { default as DateRangePicker } from "./DateRangePicker"
|
||||
export { default as JustCalendar } from "./JustCalendar"
|
||||
export { EventList } from "./EventList"
|
||||
export { EventForm } from "./EventForm"
|
||||
export { default as EventManager } from "./EventManager"
|
||||
export { default as MonthNavigator } from "./MonthNavigator"
|
||||
export { default as MonthSelector } from "./MonthSelector"
|
||||
71
src/react-app-env.d.ts
vendored
Normal file
71
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="react" />
|
||||
/// <reference types="react-dom" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
readonly NODE_ENV: "development" | "production"
|
||||
readonly PUBLIC_URL: string
|
||||
}
|
||||
}
|
||||
|
||||
declare module "*.avif" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.bmp" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.gif" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.jpg" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.jpeg" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.png" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.webp" {
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.svg" {
|
||||
import * as React from "react"
|
||||
|
||||
export const ReactComponent: React.FunctionComponent<
|
||||
React.SVGProps<SVGSVGElement> & { title?: string }
|
||||
>
|
||||
|
||||
const src: string
|
||||
export default src
|
||||
}
|
||||
|
||||
declare module "*.module.css" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module "*.module.scss" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module "*.module.sass" {
|
||||
const classes: { readonly [key: string]: string }
|
||||
export default classes
|
||||
}
|
||||
115
src/types.ts
Normal file
115
src/types.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { BaseProps, DateFormat, FunctionType } from "@armco/types"
|
||||
import { FormInputProps } from "@armco/shared-components/types"
|
||||
import { ArCalViews, ArDateFormats, ArDateMasks, ArEventStates, ArEventTypes, ArMonthSelectorViews, ArSchedules } from "./enums"
|
||||
|
||||
export interface CalendarProps extends BaseProps {
|
||||
allowEventSetting?: boolean
|
||||
events?: Array<Event>
|
||||
customDayEventSetter?: FunctionType
|
||||
endDate?: Date | ArDateFormats | CalendarDate
|
||||
hasTimeControls?: boolean
|
||||
isSingleSelect?: boolean
|
||||
maxDate?: Date | DateFormat | CalendarDate
|
||||
minDate?: Date | DateFormat | CalendarDate
|
||||
miniMode?: boolean
|
||||
onDateSelected?: FunctionType
|
||||
startDate?: Date | DateFormat | CalendarDate
|
||||
}
|
||||
|
||||
export interface CalendarDate {
|
||||
day: number
|
||||
month: number
|
||||
selected?: boolean
|
||||
siblingMonth?: boolean
|
||||
weekDay?: number
|
||||
weekNumber?: number
|
||||
year: number
|
||||
}
|
||||
|
||||
export interface Event {
|
||||
title: string
|
||||
eventType?: ArEventTypes
|
||||
description?: string
|
||||
state: ArEventStates
|
||||
schedule?: Schedule
|
||||
}
|
||||
|
||||
export interface Schedule {
|
||||
isWholeDay?: boolean
|
||||
starts?: Date | ArSchedules
|
||||
ends?: Date | ArSchedules
|
||||
}
|
||||
|
||||
export interface DatePickerProps extends FormInputProps {
|
||||
allowEventSetting?: boolean
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export interface DateRangePickerProps extends FormInputProps {
|
||||
closeOnEndSelect?: boolean
|
||||
mask?: ArDateMasks | string
|
||||
}
|
||||
|
||||
export interface EventFormProps extends BaseProps {
|
||||
miniMode?: boolean
|
||||
onSubmit: FunctionType
|
||||
selectedDate: CalendarDate
|
||||
}
|
||||
|
||||
export interface EventListProps extends BaseProps {
|
||||
events?: Array<Event>
|
||||
}
|
||||
|
||||
export interface EventManagerProps extends BaseProps {
|
||||
events?: Array<Event>
|
||||
miniMode?: boolean
|
||||
onEventSubmit: FunctionType
|
||||
selectedDate: CalendarDate
|
||||
setCurrentView: FunctionType
|
||||
}
|
||||
|
||||
export interface JustCalendarProps extends BaseProps {
|
||||
allowEventSetting?: boolean
|
||||
calHelper?: any
|
||||
calendar?: Array<CalendarDate | false>
|
||||
endDate?: CalendarDate
|
||||
hovered?: CalendarDate
|
||||
isSingleSelect?: boolean
|
||||
miniMode?: boolean
|
||||
onDateSelected: FunctionType
|
||||
setHovered: FunctionType
|
||||
startDate?: CalendarDate
|
||||
}
|
||||
|
||||
export interface DateItemProps extends JustCalendarProps {
|
||||
date: CalendarDate
|
||||
isToday: boolean
|
||||
}
|
||||
|
||||
|
||||
export interface MonthNavigatorProps extends BaseProps {
|
||||
currentDecade?: number
|
||||
currentMonthNavView?: ArMonthSelectorViews | null
|
||||
currentView: ArCalViews
|
||||
currentYear?: number
|
||||
miniMode?: boolean
|
||||
refDate: Date
|
||||
setCurrentDecade: FunctionType
|
||||
setCurrentMonthNavView: FunctionType
|
||||
setCurrentView: FunctionType
|
||||
setCurrentYear: FunctionType
|
||||
setRefDate: FunctionType
|
||||
}
|
||||
|
||||
export interface MonthSelectorProps extends BaseProps {
|
||||
currentDecade?: number
|
||||
currentMonthNavView?: ArMonthSelectorViews | null
|
||||
currentView: ArCalViews
|
||||
currentYear?: number
|
||||
miniMode?: boolean
|
||||
onMonthSelect: FunctionType
|
||||
setCurrentDecade: FunctionType
|
||||
setCurrentMonthNavView: FunctionType
|
||||
setCurrentView: FunctionType
|
||||
setCurrentYear: FunctionType
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"outDir": "build",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
41
vite-dev.config.ts
Normal file
41
vite-dev.config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { resolve } from "path"
|
||||
import { glob } from "glob"
|
||||
import { defineConfig } from "vitest/config"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import dts from "vite-plugin-dts"
|
||||
import { libInjectCss } from "vite-plugin-lib-inject-css"
|
||||
import { externalizeDeps } from "vite-plugin-externalize-deps"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
libInjectCss(),
|
||||
dts({ outDir: "build/types" }),
|
||||
externalizeDeps(),
|
||||
],
|
||||
build: {
|
||||
outDir: "build",
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry: glob.sync(resolve(__dirname, "src/**/!(*.d).{ts,tsx}")),
|
||||
},
|
||||
rollupOptions: {
|
||||
treeshake: true,
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "build/es",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
{
|
||||
format: "cjs",
|
||||
dir: "build/cjs",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
40
vite.config.ts
Normal file
40
vite.config.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { resolve } from "path"
|
||||
import { glob } from "glob"
|
||||
import { defineConfig } from "vitest/config"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import dts from "vite-plugin-dts"
|
||||
import { libInjectCss } from "vite-plugin-lib-inject-css"
|
||||
import { externalizeDeps } from "vite-plugin-externalize-deps"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
libInjectCss(),
|
||||
dts({ outDir: "build/types" }),
|
||||
externalizeDeps(),
|
||||
],
|
||||
build: {
|
||||
outDir: "build",
|
||||
lib: {
|
||||
entry: glob.sync(resolve(__dirname, "src/**/!(*.d).{ts,tsx}")),
|
||||
},
|
||||
rollupOptions: {
|
||||
treeshake: true,
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: "build/es",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
{
|
||||
format: "cjs",
|
||||
dir: "build/cjs",
|
||||
entryFileNames: "[name].js",
|
||||
chunkFileNames: "[name]-chunk.js",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user