diff --git a/mcal/config-overrides.js b/mcal/config-overrides.js new file mode 100644 index 0000000..5d15766 --- /dev/null +++ b/mcal/config-overrides.js @@ -0,0 +1,9 @@ +const {aliasWebpack, aliasJest} = require('react-app-alias-ex'); + +const options = {}; +module.exports = aliasWebpack(options); +module.exports.jest = function (config) { + const result = aliasJest(options)(config); + result.moduleDirectories.unshift(require('path').resolve(__dirname, '../node_modules')); + return result; +}; diff --git a/mcal/index.html b/mcal/index.html new file mode 100644 index 0000000..acdb0bb --- /dev/null +++ b/mcal/index.html @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + French Republican Calendar (a.k.a. French Revolutionary Calendar) + + + + +
+
+

Explanation

+
+
+

What is this?

+

The French Republican + calendar was a calendar created and implemented during the French Revolution.

+

It is also frequently referred to as the French Revolutionary Calendar, but this is a misnomer: + year 1 of the calendar started on 22 September 1792, the day after the + abolition of the + monarchy and the founding of the French + First Republic.

+
+
+
+
+

How does it work?

+

A year consists of 12 months of 30 days each, divided into three décades of 10 days + each, followed by 5 complementary days (6 in leap years).

+

The year starts on the day of the autumnal equinox at the Paris Observatory (longitude 2°20′14.03″ E). A + leap year follow directly from this definition: a year is a leap year when the next autumnal equinox + happens 366 days later instead of the normal 365. By this definition, the year will never drift + with respect to the seasons.

+

The 12 months are: Vendémiaire, Brumaire, Frimaire, Nivôse, Pluviôse, + Ventôse, Germinal, Floréal, Prairial, Messidor, Thermidor, + Fructidor. Every three months represent a season, and the endings of the names reflect this + fact.

+

The complementary days are: la Fête de la Vertu, la Fête du Génie, la Fête du + Travail, la Fête de l'Opinion, la Fête des Récompenses, and la Fête de la + Révolution (leap years only).

+
+
+
+
+

What's so special about this version?

+

Most versions of the calendar floating around doesn't use the original definition above.

+

Most versions uses the so-called Romme method for leap years, using the same leap year rules as + the Gregorian calendar, i.e. every year divisible by four, except century years not divisible by 400. + This method might make sense, except years 3, 7, and 11 were leap years under the original rules and + were observed as such in real life, but the Romme method instead makes years 4, 8, 12 leap + years instead.

+

This version uses the original rules. The JPL's + DE440 and DE441 ephemerides were used to calculate the exact timings of the autumnal equinoxes + between the Gregorian years 13201 BCE and 17191 CE (corresponding to the French Republican years -14991 + to 15399). The times were then converted to UT1+00:09:21, the exact local time at the Paris Observatory. + UT1 was chosen to keep track of the Earth's rotation without having to worry about the issues posed by + leap seconds in UTC. Note that due to the uncertainty over + ΔT — the difference between UT1 and + Terrestrial Time (TT) used in the ephemerides — it is theoretically possible for there to be + inaccuracies when the equinox occurs very close to midnight.

+

For more details about how I calculated this calendar, please see + my blog + post on the topic. This is the fourth part of a series on time-keeping, and you are highly + encouraged to read the + first + three + parts + for a more complete understanding.

+
+
+
+
+

What are those names above the Gregorian date?

+

Those are the names of the days in the + rural version of the + calendar. This was intended to replace the Catholic Church's calendar of saints, as the French + Revolution wanted to reduce the influence of the church. Every day of the year has a unique name + associated with the rural economy and these names are supposed to correspond with the season.

+

Every quintidi is named after an animal, every décadi is named after an agricultural + tool, and the remaining days are named after various plants or produce. The only exception is the winter + month of Nivôse, which has the remaining days named after minerals.

+
+
+
+
+

What are those numbers below the Gregorian date?

+

The five (or more) numbers separated by dots is the corresponding + Mesoamerican Long Count + calendar date. This is commonly known as the “Mayan calendar.” This calendar is not + available for dates before August 11, 3114 BCE (25 Thermidor -4905).

+
+
+
+
+

What is decimal time?

+

Decimal time is a time system used during the French Revolution that divided the day into 10 + hours, each with 100 minutes, which contained 100 seconds each.

+

The result is 100,000 seconds in one day, compared to the 86,400 seconds with the normal 24-hour + system. This makes it very easy to denote time as a decimal fraction of a day. For example, decimal time + 5:67:72 (around 13:37:31) on January 1, 2000 can be represented as 2000-01-01.56772.

+

Also note that each decimal hour is 2.4 normal hours, each decimal minute is 1.44 normal minutes, and + each decimal second is 0.864 normal seconds.

+
+
+
+ + + + diff --git a/mcal/manifest.json b/mcal/manifest.json new file mode 100644 index 0000000..9ac9eb1 --- /dev/null +++ b/mcal/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "French Republican Calendar", + "name": "French Republican Calendar", + "icons": [ + { + "src": "favicon.ico", + "sizes": "128x128 64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#ff0000", + "background_color": "#ffffff" +} diff --git a/mcal/package.json b/mcal/package.json new file mode 100644 index 0000000..d36f6a0 --- /dev/null +++ b/mcal/package.json @@ -0,0 +1,51 @@ +{ + "name": "mcal", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.2", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", + "@types/jest": "^27.4.0", + "@types/node": "^16.11.24", + "@types/react": "^17.0.39", + "@types/react-dom": "^17.0.11", + "bootstrap": "~5.1.3", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-scripts": "5.0.0", + "sass": "^1.49.7", + "sass-loader": "^12.4.0", + "typescript": "^4.5.5", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-app-rewired start", + "build": "react-app-rewired build", + "test": "react-app-rewired test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@types/bootstrap": "^5.1.9", + "react-app-alias-ex": "^2.1.0", + "react-app-rewired": "^2.2.1" + } +} diff --git a/mcal/public/favicon.ico b/mcal/public/favicon.ico new file mode 100644 index 0000000..222a1b0 Binary files /dev/null and b/mcal/public/favicon.ico differ diff --git a/mcal/public/index.html b/mcal/public/index.html new file mode 100644 index 0000000..acdb0bb --- /dev/null +++ b/mcal/public/index.html @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + French Republican Calendar (a.k.a. French Revolutionary Calendar) + + + + +
+
+

Explanation

+
+
+

What is this?

+

The French Republican + calendar was a calendar created and implemented during the French Revolution.

+

It is also frequently referred to as the French Revolutionary Calendar, but this is a misnomer: + year 1 of the calendar started on 22 September 1792, the day after the + abolition of the + monarchy and the founding of the French + First Republic.

+
+
+
+
+

How does it work?

+

A year consists of 12 months of 30 days each, divided into three décades of 10 days + each, followed by 5 complementary days (6 in leap years).

+

The year starts on the day of the autumnal equinox at the Paris Observatory (longitude 2°20′14.03″ E). A + leap year follow directly from this definition: a year is a leap year when the next autumnal equinox + happens 366 days later instead of the normal 365. By this definition, the year will never drift + with respect to the seasons.

+

The 12 months are: Vendémiaire, Brumaire, Frimaire, Nivôse, Pluviôse, + Ventôse, Germinal, Floréal, Prairial, Messidor, Thermidor, + Fructidor. Every three months represent a season, and the endings of the names reflect this + fact.

+

The complementary days are: la Fête de la Vertu, la Fête du Génie, la Fête du + Travail, la Fête de l'Opinion, la Fête des Récompenses, and la Fête de la + Révolution (leap years only).

+
+
+
+
+

What's so special about this version?

+

Most versions of the calendar floating around doesn't use the original definition above.

+

Most versions uses the so-called Romme method for leap years, using the same leap year rules as + the Gregorian calendar, i.e. every year divisible by four, except century years not divisible by 400. + This method might make sense, except years 3, 7, and 11 were leap years under the original rules and + were observed as such in real life, but the Romme method instead makes years 4, 8, 12 leap + years instead.

+

This version uses the original rules. The JPL's + DE440 and DE441 ephemerides were used to calculate the exact timings of the autumnal equinoxes + between the Gregorian years 13201 BCE and 17191 CE (corresponding to the French Republican years -14991 + to 15399). The times were then converted to UT1+00:09:21, the exact local time at the Paris Observatory. + UT1 was chosen to keep track of the Earth's rotation without having to worry about the issues posed by + leap seconds in UTC. Note that due to the uncertainty over + ΔT — the difference between UT1 and + Terrestrial Time (TT) used in the ephemerides — it is theoretically possible for there to be + inaccuracies when the equinox occurs very close to midnight.

+

For more details about how I calculated this calendar, please see + my blog + post on the topic. This is the fourth part of a series on time-keeping, and you are highly + encouraged to read the + first + three + parts + for a more complete understanding.

+
+
+
+
+

What are those names above the Gregorian date?

+

Those are the names of the days in the + rural version of the + calendar. This was intended to replace the Catholic Church's calendar of saints, as the French + Revolution wanted to reduce the influence of the church. Every day of the year has a unique name + associated with the rural economy and these names are supposed to correspond with the season.

+

Every quintidi is named after an animal, every décadi is named after an agricultural + tool, and the remaining days are named after various plants or produce. The only exception is the winter + month of Nivôse, which has the remaining days named after minerals.

+
+
+
+
+

What are those numbers below the Gregorian date?

+

The five (or more) numbers separated by dots is the corresponding + Mesoamerican Long Count + calendar date. This is commonly known as the “Mayan calendar.” This calendar is not + available for dates before August 11, 3114 BCE (25 Thermidor -4905).

+
+
+
+
+

What is decimal time?

+

Decimal time is a time system used during the French Revolution that divided the day into 10 + hours, each with 100 minutes, which contained 100 seconds each.

+

The result is 100,000 seconds in one day, compared to the 86,400 seconds with the normal 24-hour + system. This makes it very easy to denote time as a decimal fraction of a day. For example, decimal time + 5:67:72 (around 13:37:31) on January 1, 2000 can be represented as 2000-01-01.56772.

+

Also note that each decimal hour is 2.4 normal hours, each decimal minute is 1.44 normal minutes, and + each decimal second is 0.864 normal seconds.

+
+
+
+ + + + diff --git a/mcal/public/logo.svg b/mcal/public/logo.svg new file mode 100644 index 0000000..35fbd1c --- /dev/null +++ b/mcal/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/mcal/public/logo192.png b/mcal/public/logo192.png new file mode 100644 index 0000000..ac33d6c Binary files /dev/null and b/mcal/public/logo192.png differ diff --git a/mcal/public/logo512.png b/mcal/public/logo512.png new file mode 100644 index 0000000..e6b3502 Binary files /dev/null and b/mcal/public/logo512.png differ diff --git a/mcal/public/manifest.json b/mcal/public/manifest.json new file mode 100644 index 0000000..9ac9eb1 --- /dev/null +++ b/mcal/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "French Republican Calendar", + "name": "French Republican Calendar", + "icons": [ + { + "src": "favicon.ico", + "sizes": "128x128 64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#ff0000", + "background_color": "#ffffff" +} diff --git a/mcal/public/robots.txt b/mcal/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/mcal/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/mcal/robots.txt b/mcal/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/mcal/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/mcal/src/App.tsx b/mcal/src/App.tsx new file mode 100644 index 0000000..c07c773 --- /dev/null +++ b/mcal/src/App.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {Calendar} from './Calendar'; +import {FrenchMonth, frEndJD, frStartJD, frSupportedYear, jdnFrench} from '@common/french'; +import {JulianMonth} from '@common/gregorian'; +import {GregorianJumper} from '@common/dateJump'; +import MonthBasedApp from '@common/ui/MonthBasedApp'; +import Export from './Export'; + +export default class App extends MonthBasedApp { + override parseYearMonth(year: string, month: string) { + if (!frSupportedYear(+year) || +month < 1 || +month > 13) + return null; + return {year: +year, month: +month as JulianMonth}; + } + + override defaultSelector(todayJDN: number) { + const {year, month} = jdnFrench(todayJDN); + return {year, month}; + } + + goToJDN = (jdn: number) => { + const {year, month} = jdnFrench(Math.min(Math.max(frStartJD, jdn), frEndJD)); + this.setState({selector: {year, month}}); + }; + + render() { + const {selector: {year, month}, todayJDN} = this.state; + return <> + this.setState({selector: {year, month}})}/> + +
+

Go to a date

+ +
+ +
+

Export calendar

+ +
+ ; + } +} diff --git a/mcal/src/Calendar.scss b/mcal/src/Calendar.scss new file mode 100644 index 0000000..f90c2c3 --- /dev/null +++ b/mcal/src/Calendar.scss @@ -0,0 +1,71 @@ +@import 'bootstrap/scss/functions'; +@import 'bootstrap/scss/variables'; +@import 'bootstrap/scss/mixins'; +@import 'bootstrap/scss/forms'; +@import 'bootstrap/scss/grid'; +@import 'bootstrap/scss/buttons'; +@import '@common/ui/consts.scss'; +@import '@common/ui/MonthBasedCalendar.scss'; + +@include media-breakpoint-up(xs) { + .DayOuter.NormalDay { + @include make-col($size: 1, $columns: 2); + } + + .DayOuter.ComplementaryDay { + @include make-col($size: 1, $columns: 1); + } +} + +@include media-breakpoint-up(sm) { + .DayOuter.NormalDay { + @include make-col($size: 1, $columns: 3); + } +} + +@include media-breakpoint-up(md) { + .DayOuter.NormalDay { + @include make-col($size: 1, $columns: 4); + } +} + +@include media-breakpoint-up(lg) { + .DayOuter.NormalDay { + @include make-col($size: 1, $columns: 6); + } + + .DayOuter.ComplementaryDay { + @include make-col($size: 1, $columns: 2); + } +} + +@include media-breakpoint-up(xl) { + .DayOuter.NormalDay { + @include make-col($size: 1, $columns: 8); + } +} + +@include media-breakpoint-up(xxl) { + .Month-weekdayHead { + display: block; + @include make-row($gutter: 0); + position: sticky; + top: 0; + background: white; + margin-top: -$spacer * 7.5; + padding-top: $spacer * 7.5; + z-index: 19; + } + + .Month-days { + margin-top: $spacer; + } + + .DayOuter.NormalDay, .WeekdayName { + @include make-col($size: 1, $columns: 10); + } + + .Day-weekday { + display: none; + } +} diff --git a/mcal/src/Calendar.tsx b/mcal/src/Calendar.tsx new file mode 100644 index 0000000..245bc8c --- /dev/null +++ b/mcal/src/Calendar.tsx @@ -0,0 +1,167 @@ +import React from 'react'; +import './Calendar.scss'; +import { + dateName, + dateRuralName, + decadeNames, + FrenchDay, + FrenchMonth, + frEndYear, + frIsLeap, + frJDN, + frStartYear, + jdnFrench, + monthName, +} from '@common/french'; +import {jdnDate} from '@common/gregorian'; +import {jdnLongCount} from '@common/longCount'; +import {useMobileTooltipProps} from '@common/ui/MobileTooltip'; +import {MonthBasedCalendar} from '@common/ui/MonthBasedCalendar'; + +type FrenchYear = number; + +type MonthProps = { + year: FrenchYear; + month: FrenchMonth; +}; + +type DateProps = MonthProps & { + day: FrenchDay; +}; + +function DecadeName({name}: { name: string }): JSX.Element { + return
{name}
; +} + +function DayDetail({jdn}: { jdn: number }): JSX.Element { + return
+
{jdnDate(jdn).toDateString()}
+
{jdnLongCount(jdn)?.join('.')}
+
; +} + +function NormalDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element { + const jdn = frJDN(year, month, day); + const rural = dateRuralName(month, day)!; + const mobile = useMobileTooltipProps(); + return
+
{day}
+
{decadeNames[(day - 1) % 10]}
+
{rural.name}
+ +
; +} + +function NormalMonth({year, month, todayJDN}: MonthProps & { todayJDN: number }): JSX.Element { + const decadeHeads = decadeNames.map((name, i) => ); + return
+
{decadeHeads}
+
{ + Array.from(Array(30).keys()).map(i =>
+ +
) + }
+
; +} + +function ComplementaryDay({year, month, day, todayJDN}: DateProps & { todayJDN: number }): JSX.Element { + const jdn = frJDN(year, month, day); + return
+
{dateName(month, day)}
+ +
; +} + +function ComplementaryDays({year, todayJDN}: { year: FrenchYear, todayJDN: number }): JSX.Element { + const leap = frIsLeap(year); + return
+
{ + Array.from(Array(leap ? 6 : 5).keys()).map(i =>
+ +
) + }
+
; +} + +export class Calendar extends MonthBasedCalendar { + override parseYear(year: string): FrenchYear { + return +year; + } + + override parseMonth(month: string): FrenchMonth { + return +month as FrenchMonth; + } + + override yearToString(year: FrenchYear): string { + return year.toString(); + } + + override monthToString(month: FrenchMonth): string { + return month.toString(); + } + + private goToNormalized(year: number, month: number) { + while (month < 1) { + --year; + month += 13; + } + + while (month > 13) { + ++year; + month -= 13; + } + + if (year < frStartYear) { + year = frStartYear; + month = 1; + } else if (year > frEndYear) { + year = frEndYear; + month = 13; + } + + this.goTo(year, month as FrenchMonth); + } + + override prevYear = () => { + this.goToNormalized(this.props.year - 1, this.props.month); + }; + + override prevMonth = () => { + this.goToNormalized(this.props.year, this.props.month - 1); + }; + + override nextYear = () => { + this.goToNormalized(this.props.year + 1, this.props.month); + }; + + override nextMonth = () => { + this.goToNormalized(this.props.year, this.props.month + 1); + }; + + override isValidYear(year: string): boolean { + return /^-?\d+/.test(year); + } + + override jdnLookup(jdn: number): { year: FrenchYear; month: FrenchMonth } { + return jdnFrench(jdn); + } + + override monthName(year: FrenchYear, month: FrenchMonth): string { + return month === 13 ? year.toString() : `${monthName(month)} ${year}`; + } + + override renderMonthOptions(): JSX.Element[] { + return Array.from(Array(13).keys()).map(i => { + const month = i + 1 as FrenchMonth; + return ; + }); + } + + override renderBody(): JSX.Element { + if (this.props.month < 13) { + return ; + } else { + return ; + } + } +} diff --git a/mcal/src/Export.tsx b/mcal/src/Export.tsx new file mode 100644 index 0000000..1af7286 --- /dev/null +++ b/mcal/src/Export.tsx @@ -0,0 +1,92 @@ +import {dateName, jdnFrench} from '@common/french'; +import {jdnGregorian} from '@common/gregorian'; +import React from 'react'; +import GregorianSelector from '@common/dateJump/GregorianSelector'; +import {BaseDateProps} from '@common/dateJump/base'; + +function zeroPad(item: unknown, width: number) { + const n = item + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n; +} + + +export function* iCalStream(startJDN: number, endJDN: number): Generator { + yield `BEGIN:VCALENDAR\r +VERSION:2.0\r +PRODID:-//hacksw/handcal//NONSGML v1.0//EN\r +`; + + for (let jdn = startJDN; jdn <= endJDN; ++jdn) { + const [gy, gm, gd] = jdnGregorian(jdn); + const {year: fy, month: fm, day: fd} = jdnFrench(jdn); + yield `BEGIN:VEVENT\r +UID:jdn-${jdn}@frcal.qt.ax\r +DTSTAMP:${new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '')}\r +DTSTART;VALUE=DATE:${zeroPad(gy, 4)}${zeroPad(gm, 2)}${zeroPad(gd, 2)}\r +SUMMARY:${dateName(fm, fd)} ${fy}\r +TRANSP:TRANSPARENT\r +END:VEVENT\r +`; + } + + yield 'END:VCALENDAR\r\n'; +} + +function iCalBlob(startJDN: number, endJDN: number): Blob { + return new Blob(iCalStream(startJDN, endJDN) as any, {type: 'text/calendar'}); +} + +function downloadAs(url: string, name: string) { + const a = document.createElement('a'); + a.href = url; + a.download = name; + a.style.display = 'none'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); +} + +type Props = BaseDateProps; + +export default function Export(props: Props): JSX.Element { + const [rangeStart, setRangeStart] = React.useState(); + const [rangeEnd, setRangeEnd] = React.useState(); + + function doExport(event: React.FormEvent) { + event.preventDefault(); + + if (rangeStart === undefined || rangeEnd === undefined) { + alert('Date out of range!'); + return; + } + + const days = rangeEnd - rangeStart; + if (days > 36500 && !window.confirm( + `You are exporting ${days} days of calendar data. This may crash your computer. Do you want to continue?`, + )) + return; + + const blob = iCalBlob(rangeStart, rangeEnd); + const [sy, sm, sd] = jdnGregorian(rangeStart); + const [ey, em, ed] = jdnGregorian(rangeEnd); + const startDate = `${zeroPad(sy, 4)}-${zeroPad(sm, 2)}-${zeroPad(sd, 2)}`; + const endDate = `${zeroPad(ey, 4)}-${zeroPad(em, 2)}-${zeroPad(ed, 2)}`; + downloadAs(URL.createObjectURL(blob), `frcal-${startDate}-${endDate}.ics`); + } + + return
+
+ Start date + +
+ +
+ End date + +
+ + +
; +} diff --git a/mcal/src/index.scss b/mcal/src/index.scss new file mode 100644 index 0000000..40cbd86 --- /dev/null +++ b/mcal/src/index.scss @@ -0,0 +1,30 @@ +@import '@common/ui/index.scss'; + +.download { + max-width: $calendar-width; + margin-top: $spacer; + @include make-container(); + + .input-group-text { + width: 6em; + } + + .btn-primary { + max-width: 5em; + } +} + +@include media-breakpoint-up(md) { + .download form { + display: flex; + + .input-group { + width: fit-content; + margin-right: $spacer / 2; + } + + .input-group-text { + width: auto; + } + } +} diff --git a/mcal/src/index.tsx b/mcal/src/index.tsx new file mode 100644 index 0000000..11c07b2 --- /dev/null +++ b/mcal/src/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import 'bootstrap/js/dist/collapse'; +import './index.scss'; +import App from './App'; +import reportWebVitals from '@common/ui/reportWebVitals'; +import {MobileTooltipProvider} from '@common/ui/MobileTooltip'; + +ReactDOM.render( + + + + + , + document.getElementById('root'), +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/mcal/src/react-app-env.d.ts b/mcal/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/mcal/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/mcal/src/setupTests.ts b/mcal/src/setupTests.ts new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/mcal/src/setupTests.ts @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/mcal/tsconfig.json b/mcal/tsconfig.json new file mode 100644 index 0000000..9d6c7c6 --- /dev/null +++ b/mcal/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.paths.json", + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "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" + ] +} diff --git a/mcal/tsconfig.paths.json b/mcal/tsconfig.paths.json new file mode 100644 index 0000000..1cacae9 --- /dev/null +++ b/mcal/tsconfig.paths.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@common/*": ["../common/src/*"] + } + } +} \ No newline at end of file