@koine/react 1.0.3 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/Alert/Alert.js +18 -0
  2. package/Alert/index.js +1 -0
  3. package/Animations/Reveal.js +17 -0
  4. package/Animations/Underline.js +15 -0
  5. package/Animations/index.js +3 -0
  6. package/Animations/useReveal.js +70 -0
  7. package/Autocomplete/AutocompleteDownshift.js +158 -0
  8. package/Autocomplete/AutocompleteDownshiftMultiselect.js +353 -0
  9. package/Autocomplete/AutocompleteMui.js +172 -0
  10. package/Autocomplete/AutocompleteReach.js +112 -0
  11. package/Autocomplete/components.js +89 -0
  12. package/Autocomplete/helpers.js +28 -0
  13. package/Autocomplete/index.js +3 -0
  14. package/Bg/BgColor.js +33 -0
  15. package/Bg/BgPhoto.js +59 -0
  16. package/Bg/BgSvg.js +15 -0
  17. package/Bg/index.js +3 -0
  18. package/Breadcrumbs/Breadcrumbs.js +70 -0
  19. package/Breadcrumbs/index.js +1 -0
  20. package/Buttons/Button.js +79 -0
  21. package/Buttons/ButtonComposite.d.ts +1 -1
  22. package/Buttons/ButtonComposite.js +53 -0
  23. package/Buttons/ButtonFab.js +8 -0
  24. package/Buttons/ButtonLink.js +16 -0
  25. package/Buttons/IconButton.js +19 -0
  26. package/Buttons/index.js +5 -0
  27. package/Calendar/CalendarDaygridCell.js +52 -0
  28. package/Calendar/CalendarDaygridNav.js +23 -0
  29. package/Calendar/CalendarDaygridTable.js +49 -0
  30. package/Calendar/CalendarLegend.js +12 -0
  31. package/Calendar/calendar-api-google.js +97 -0
  32. package/Calendar/index.js +6 -0
  33. package/Calendar/types.js +1 -0
  34. package/Calendar/useCalendar.js +166 -0
  35. package/Calendar/utils.js +197 -0
  36. package/Carousel/Carousel.js +378 -0
  37. package/Carousel/CarouselCss.js +39 -0
  38. package/Carousel/index.js +1 -0
  39. package/Collapsable/Collapsable.js +132 -0
  40. package/Collapsable/index.js +1 -0
  41. package/Debug/Debug.js +21 -0
  42. package/Debug/index.js +1 -0
  43. package/Dialog/Dialog.js +93 -0
  44. package/Dialog/index.js +1 -0
  45. package/Editor/Editor--tiptap.js +21 -0
  46. package/Editor/components.d.ts +1 -2
  47. package/Editor/components.js +28 -0
  48. package/Editor/index.js +1 -0
  49. package/Favicon/FaviconTags.js +14 -0
  50. package/Favicon/index.js +1 -0
  51. package/Forms/Checkbox/Checkbox.js +24 -0
  52. package/Forms/Checkbox/index.js +1 -0
  53. package/Forms/Feedback/Feedback.js +10 -0
  54. package/Forms/Feedback/index.js +1 -0
  55. package/Forms/Field/Field.js +61 -0
  56. package/Forms/Field/FieldControl.js +45 -0
  57. package/Forms/Field/FieldHint.js +6 -0
  58. package/Forms/Field/index.js +2 -0
  59. package/Forms/Form/Form.js +64 -0
  60. package/Forms/Form/index.js +1 -0
  61. package/Forms/Input/Input.js +25 -0
  62. package/Forms/Input/index.js +1 -0
  63. package/Forms/InputGroup/InputGroup.js +42 -0
  64. package/Forms/InputGroup/index.js +1 -0
  65. package/Forms/Label/Label.js +24 -0
  66. package/Forms/Label/index.js +1 -0
  67. package/Forms/Password/Password.js +32 -0
  68. package/Forms/Password/index.js +1 -0
  69. package/Forms/Radio/Radio.js +31 -0
  70. package/Forms/Radio/index.js +1 -0
  71. package/Forms/Switch/Switch.js +50 -0
  72. package/Forms/Switch/index.js +1 -0
  73. package/Forms/Textarea/Textarea.js +15 -0
  74. package/Forms/Textarea/TextareaRich.js +44 -0
  75. package/Forms/Textarea/index.js +2 -0
  76. package/Forms/Toggle/Toggle.js +79 -0
  77. package/Forms/Toggle/index.js +1 -0
  78. package/Forms/Toggle/useToggle.js +143 -0
  79. package/Forms/antispam.js +56 -0
  80. package/Forms/helpers.js +44 -0
  81. package/Forms/index.js +17 -0
  82. package/Forms/styles.js +60 -0
  83. package/Gauge/Gauge.js +102 -0
  84. package/Grid/Grid.js +79 -0
  85. package/Grid/index.js +1 -0
  86. package/Hamburger/Hamburger.js +55 -0
  87. package/Hamburger/index.js +1 -0
  88. package/Header/index.js +1 -0
  89. package/Header/useHeader.js +30 -0
  90. package/Hidden/Hidden.js +14 -0
  91. package/Hidden/index.js +1 -0
  92. package/Img/Img.js +34 -0
  93. package/Img/index.js +1 -0
  94. package/Link/Link.js +2 -0
  95. package/Link/LinkBlank.d.ts +1 -1
  96. package/Link/LinkBlank.js +28 -0
  97. package/Link/index.js +2 -0
  98. package/Menu/Menu.js +11 -0
  99. package/Menu/index.js +1 -0
  100. package/MenuItem/MenuItem.js +20 -0
  101. package/MenuItem/index.js +1 -0
  102. package/Meta/Meta.js +4 -0
  103. package/Meta/index.js +1 -0
  104. package/NoJs/NoJs.js +6 -0
  105. package/NoJs/index.js +1 -0
  106. package/Pagination/PaginationNav.js +87 -0
  107. package/Pagination/PaginationResults.js +10 -0
  108. package/Pagination/index.js +2 -0
  109. package/Pill/Pill.js +37 -0
  110. package/Pill/index.js +1 -0
  111. package/Progress/ProgressCircular.js +38 -0
  112. package/Progress/ProgressLinear.js +34 -0
  113. package/Progress/ProgressOverlay.js +40 -0
  114. package/Progress/index.js +3 -0
  115. package/Rating/Rating.js +93 -0
  116. package/Rating/index.js +45 -0
  117. package/Select/SelectDownshift.js +38 -0
  118. package/Select/components.js +20 -0
  119. package/Select/index.js +3 -0
  120. package/Sidebar/Sidebar.js +48 -0
  121. package/Sidebar/index.js +1 -0
  122. package/Spacing/Spacing.js +47 -0
  123. package/Spacing/index.js +1 -0
  124. package/Sticky/Sticky.js +220 -0
  125. package/Sticky/StickyCss.js +6 -0
  126. package/Sticky/index.js +1 -0
  127. package/Tabs/Tabs.js +67 -0
  128. package/Tabs/index.js +1 -0
  129. package/Typography/CopyPasteVisible.js +6 -0
  130. package/Typography/Native.js +47 -0
  131. package/Typography/ReadMore.js +71 -0
  132. package/Typography/TextLoop.js +45 -0
  133. package/Typography/TypeStairs.js +46 -0
  134. package/Typography/index.js +5 -0
  135. package/helpers/index.js +19 -0
  136. package/hooks/index.js +5 -0
  137. package/hooks/useDateLocale.js +30 -0
  138. package/hooks/useFocus.js +11 -0
  139. package/hooks/usePrevious.js +8 -0
  140. package/hooks/useTraceUpdate.js +20 -0
  141. package/hooks/useWindowSize.js +13 -0
  142. package/index.js +36 -0
  143. package/index.umd.js +234 -257
  144. package/package.json +15 -9
  145. package/styles/Body.js +17 -0
  146. package/styles/Global.js +50 -0
  147. package/styles/classed.js +11 -0
  148. package/styles/index.js +7 -0
  149. package/styles/media.js +150 -0
  150. package/styles/spacing.d.ts +6 -6
  151. package/styles/spacing.js +46 -0
  152. package/styles/styled.d.ts +2 -2
  153. package/styles/styled.js +27 -0
  154. package/styles/theme--vanilla.js +53 -0
  155. package/styles/theme.d.ts +0 -7
  156. package/styles/theme.js +38 -0
  157. package/types.js +1 -0
  158. package/typings.d.ts +21 -0
  159. package/index.esm.js +0 -9472
@@ -0,0 +1,53 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { memo } from "react";
4
+ import styled from "styled-components";
5
+ const Root = styled.span `
6
+ ${(p) => p.$icon ? `display: flex;` : `display: inline-block; text-align: left;`}
7
+ padding: 0;
8
+ min-width: 0;
9
+
10
+ & svg {
11
+ font-size: 2em !important;
12
+ margin: 0 0.33em 0 0 !important;
13
+ }
14
+ `;
15
+ const ButtonCompositeIcon = styled.svg `
16
+ float: left;
17
+ `;
18
+ const BesidesIcon = styled.span `
19
+ text-align: left;
20
+ line-height: 1.2;
21
+ `;
22
+ const Main = styled.span `
23
+ display: block;
24
+ font-size: 0.9em;
25
+
26
+ &:last-child {
27
+ margin-top: ${(p) => (p.$reverse && !p.$icon ? "0.5em" : "0")};
28
+ }
29
+ `;
30
+ const Sub = styled.span `
31
+ display: block;
32
+ text-transform: none;
33
+ font-size: 0.7em;
34
+ font-weight: 500;
35
+
36
+ ${Main} + & {
37
+ margin-top: ${(p) => (p.$reverse && !p.$icon ? "0.5em" : "0")};
38
+ }
39
+ `;
40
+ const Text = styled.span ``;
41
+ const Inner = memo(({ textMain, textSub, text, $reverse, $icon, }) => (_jsxs(_Fragment, { children: [$reverse ? (_jsxs(_Fragment, { children: [textSub && (_jsx(Sub, Object.assign({ "$icon": $icon, "$reverse": $reverse }, { children: textSub }))), textMain && (_jsx(Main, Object.assign({ "$icon": $icon, "$reverse": $reverse }, { children: textMain })))] })) : (_jsxs(_Fragment, { children: [textMain && (_jsx(Main, Object.assign({ "$icon": $icon, "$reverse": $reverse }, { children: textMain }))), textSub && (_jsx(Sub, Object.assign({ "$icon": $icon, "$reverse": $reverse }, { children: textSub })))] })), text && _jsx(Text, { children: text })] })));
42
+ export const KoineButtonComposite = (_a) => {
43
+ var { Icon, iconProps = {}, textMain, textSub, textReverse, text, Koine = { Button: "button", ButtonLink: "a" } } = _a, props = __rest(_a, ["Icon", "iconProps", "textMain", "textSub", "textReverse", "text", "Koine"]);
44
+ const { ButtonLink, Button } = Koine;
45
+ const Btn = props.href ? ButtonLink : Button;
46
+ const styledProps = {
47
+ $icon: !!Icon,
48
+ $reverse: textReverse,
49
+ $twoLines: !!(textMain && textSub && !Icon),
50
+ };
51
+ const innerProps = Object.assign({ textMain, textSub, text }, styledProps);
52
+ return (_jsxs(Root, Object.assign({ as: Btn }, props, styledProps, { children: [Icon && _jsx(ButtonCompositeIcon, Object.assign({ as: Icon }, iconProps)), Icon ? (_jsx(BesidesIcon, Object.assign({}, styledProps, { children: _jsx(Inner, Object.assign({}, innerProps)) }))) : (_jsx(Inner, Object.assign({}, innerProps)))] })));
53
+ };
@@ -0,0 +1,8 @@
1
+ import styled, { css } from "styled-components";
2
+ import { IconButton } from "./IconButton";
3
+ export const buttonFab = css `
4
+ box-shadow: 3px 3px 6px -4px rgb(0, 0, 0, 0.5);
5
+ `;
6
+ export const IconButtonFab = styled(IconButton) `
7
+ ${buttonFab}
8
+ `;
@@ -0,0 +1,16 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import styled from "styled-components";
4
+ import { KoineButton } from "./Button";
5
+ const Root = styled(KoineButton) `
6
+ text-decoration: none;
7
+ `;
8
+ // export const KoineButtonLink: FC<ButtonLinkProps> = ({ Koine, ...props }) => {
9
+ // return <Root {...props} as={Koine.Link} />;
10
+ // }
11
+ export const KoineButtonLink = (_a) => {
12
+ var { href, Koine = { Link: "a" } } = _a, props = __rest(_a, ["href", "Koine"]);
13
+ const { Link } = Koine;
14
+ const isRelative = href && href.startsWith("/");
15
+ return isRelative ? (_jsx(Root, Object.assign({ href: href }, props, { as: Link }))) : (_jsx(Root, Object.assign({ href: href }, props, { as: "a" })));
16
+ };
@@ -0,0 +1,19 @@
1
+ import styled, { css } from "styled-components";
2
+ import { btnStyleReset, btnStyleOutlined, btnStyleContained, } from "./Button";
3
+ export const iconBtnStyleReset = css `
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ width: 44px;
8
+ height: 44px;
9
+ border-radius: 100%;
10
+ ${btnStyleReset}
11
+ `;
12
+ export const iconBtnStyleBase = css `
13
+ ${iconBtnStyleReset}
14
+ `;
15
+ export const IconButton = styled.button `
16
+ ${iconBtnStyleBase}
17
+ ${(p) => p.$variant === "outlined" && btnStyleOutlined}
18
+ ${(p) => p.$variant === "contained" && btnStyleContained}
19
+ `;
@@ -0,0 +1,5 @@
1
+ export * from "./Button";
2
+ export * from "./ButtonComposite";
3
+ export * from "./ButtonFab";
4
+ export * from "./ButtonLink";
5
+ export * from "./IconButton";
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Fragment, useState } from "react";
3
+ import { MdAdd as IconExpand } from "react-icons/md";
4
+ import { getDisplayTime } from "./utils";
5
+ /**
6
+ * Style for button within a event cell
7
+ *
8
+ * Here we might differentiate week/month view where the first does not get
9
+ * ellipsed btn texts, with `Start` as block element and underneath the `Title`
10
+ * on multiple lines, but that would mean that we loose the ability to interweave
11
+ * single-day events among the spaces left by wider multi-days events.
12
+ */
13
+ const styleBtn = {
14
+ overflow: "hidden",
15
+ whiteSpace: "nowrap",
16
+ textOverflow: "ellipsis",
17
+ };
18
+ export const CalendarDaygridCell = ({ eventClicked, setEventClicked,
19
+ // eventHovered,
20
+ setEventHovered, view, maxEvents, events, calendarsMap, Koine, }) => {
21
+ const [isExpanded, expand] = useState(false);
22
+ const { Cell = "div", CellOverflow = "div", CellEvent = "div", CellEventBtn = "div", CellEventTitle = "span", CellEventStart = "span", } = Koine;
23
+ const eventsWithoutPlaceholders = events.filter((event) => !event.placeholder);
24
+ return (_jsx(Cell, { children: events.map((event, i) => {
25
+ if (i === maxEvents && !isExpanded) {
26
+ return (_jsxs(CellOverflow, Object.assign({ onClick: () => expand(true) }, { children: [_jsx(IconExpand, {}), eventsWithoutPlaceholders.length - maxEvents] }), "overflowMessage" + i));
27
+ }
28
+ if (i > maxEvents && !isExpanded)
29
+ return null;
30
+ if (event.placeholder) {
31
+ return (_jsx(Fragment, { children: _jsx(CellEvent, Object.assign({ "$placeholder": true }, { children: _jsx(CellEventBtn, Object.assign({ "aria-hidden": "true", style: { visibility: "hidden" }, "$placeholder": true }, { children: _jsx(CellEventTitle, { children: "\u00A0" }) })) })) }, event.key));
32
+ }
33
+ const styleEvent = {
34
+ zIndex: event.firstOfMulti ? 1 : 0,
35
+ position: "relative",
36
+ width: event.firstOfMulti ? `${100 * event.width}%` : "100%",
37
+ };
38
+ if (!calendarsMap[event.calendar.id].on) {
39
+ // @ts-expect-error nevermind
40
+ styleBtn.display = "none";
41
+ }
42
+ const styledProps = {
43
+ $view: view,
44
+ $selected: (eventClicked === null || eventClicked === void 0 ? void 0 : eventClicked.uid) === event.uid,
45
+ $past: event.isPast,
46
+ $color: event.color,
47
+ $isOutOfRange: event.$isOutOfRange,
48
+ $isToday: event.$isToday,
49
+ };
50
+ return (_jsx(Fragment, { children: _jsx(CellEvent, Object.assign({ style: styleEvent }, styledProps, { children: _jsx(CellEventBtn, Object.assign({ role: "button", style: styleBtn }, styledProps, { onClick: () => setEventClicked((prev) => (prev === null || prev === void 0 ? void 0 : prev.uid) === event.uid ? null : event), onMouseEnter: () => setEventHovered(event), onMouseLeave: () => setEventHovered(null) }, { children: event.allDay ? (_jsx(CellEventTitle, { children: event.title })) : (_jsxs(_Fragment, { children: [_jsx(CellEventStart, { children: getDisplayTime(event.start) }), _jsx(CellEventTitle, { children: event.title })] })) })) })) }, event.key));
51
+ }) }));
52
+ };
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import format from "date-fns/format";
3
+ import { useDateLocale } from "../hooks/useDateLocale";
4
+ export const KoineCalendarDaygridNav = ({ range, view, todayInView, handlePrev, handleNext, handleToday, handleView, locale: localeCode, Koine, }) => {
5
+ const [start, end] = range;
6
+ const locale = useDateLocale(localeCode);
7
+ const { NavRoot = "nav", NavTitle = "div", NavBtns = "div", NavBtnPrev = "button", NavBtnNext = "button", NavBtnToday = "button", NavBtnViewMonth = "button", NavBtnViewWeek = "button", } = Koine;
8
+ const opts = { locale };
9
+ let formatted = "";
10
+ if (view === "month") {
11
+ formatted = format(start, "MMMM yyyy", opts);
12
+ }
13
+ if (view === "week") {
14
+ const inSameMonth = start.getMonth() === end.getMonth();
15
+ if (inSameMonth) {
16
+ formatted = format(start, "# MMMM yyyy", opts).replace("#", `${start.getDate()}-${end.getDate()}`);
17
+ }
18
+ else {
19
+ formatted = `${format(start, "d MMMM", opts)} - ${format(end, "d MMMM yyyy", opts)}`;
20
+ }
21
+ }
22
+ return (_jsxs(NavRoot, { children: [_jsxs(NavBtns, { children: [_jsx(NavBtnPrev, { onClick: handlePrev }), _jsx(NavBtnNext, { onClick: handleNext }), _jsx(NavBtnToday, { onClick: handleToday, disabled: todayInView }), _jsx(NavBtnViewMonth, { onClick: () => handleView("month"), disabled: view === "month" }), _jsx(NavBtnViewWeek, { onClick: () => handleView("week"), disabled: view === "week" })] }), _jsx(NavTitle, { range: range, formatted: formatted })] }));
23
+ };
@@ -0,0 +1,49 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from "react";
4
+ import eachWeekOfInterval from "date-fns/eachWeekOfInterval";
5
+ import { useSwipeable } from "react-swipeable";
6
+ import { useDateLocale } from "../hooks";
7
+ import { processEventsInView } from "./utils";
8
+ import { CalendarDaygridCell, } from "./CalendarDaygridCell";
9
+ function getView(range) {
10
+ const [start, end] = range;
11
+ const weeks = eachWeekOfInterval({ start, end }, { weekStartsOn: 1 });
12
+ return {
13
+ month: start.getMonth(),
14
+ weeks,
15
+ };
16
+ }
17
+ export const KoineCalendarDaygridTable = ({ locale: localeCode, handlePrev, handleNext, events, dayLabels, view, range, eventClicked, setEventClicked, eventHovered, setEventHovered, calendarsMap = {}, maxEvents = 5, Koine, }) => {
18
+ const { Table = "table", TableHead = "thead", TableHeadCell = "th", TableBody = "tbody", TableBodyRow = "tr", TableBodyCell = "td", TableBodyCellDate = "div" } = Koine, restKoine = __rest(Koine, ["Table", "TableHead", "TableHeadCell", "TableBody", "TableBodyRow", "TableBodyCell", "TableBodyCellDate"]);
19
+ const [days, setDays] = useState(dayLabels || [0, 1, 2, 3, 4, 5, 6]);
20
+ const [weeksEvents, setWeeksEvents] = useState([]);
21
+ // const [days, setDays] = useState(dayLabels || [...Array(7).keys()]);
22
+ const locale = useDateLocale(localeCode);
23
+ const { month, weeks } = useMemo(() => getView(range), [range]);
24
+ const swipeableHandlers = useSwipeable({
25
+ onSwipedLeft: handleNext,
26
+ onSwipedRight: handlePrev,
27
+ });
28
+ useEffect(() => {
29
+ setWeeksEvents(processEventsInView(events, view, month, weeks));
30
+ }, [events, view, month, weeks]);
31
+ useEffect(() => {
32
+ if (locale && locale.localize && !dayLabels) {
33
+ setDays([1, 2, 3, 4, 5, 6, 0].map(
34
+ // @ts-expect-error nevermind
35
+ (i) => locale.localize.day(i, { width: "abbreviated" })));
36
+ }
37
+ }, [locale, dayLabels]);
38
+ return (_jsxs(Table, Object.assign({}, swipeableHandlers, { children: [_jsx(TableHead, { children: _jsx("tr", { children: days.map((day) => (_jsx(TableHeadCell, Object.assign({ scope: "column" }, { children: day }), day))) }) }), _jsx(TableBody, { children: weeksEvents.map((week, i) => (_jsx(TableBodyRow, Object.assign({}, week.props, { children: week.days.map((day) => (_jsxs(TableBodyCell, Object.assign({}, day.props, { children: [_jsx(TableBodyCellDate, Object.assign({}, day.props, { children: day.label })), day.events.length > 0 && (_jsx(CalendarDaygridCell, Object.assign({}, {
39
+ eventClicked,
40
+ setEventClicked,
41
+ eventHovered,
42
+ setEventHovered,
43
+ view,
44
+ maxEvents,
45
+ events: day.events,
46
+ timestamp: day.timestamp,
47
+ calendarsMap,
48
+ }, { Koine: restKoine })))] })))) })))) })] })));
49
+ };
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ export const KoineCalendarLegend = ({ calendarsMap = {}, toggleCalendarVisibility, Koine, }) => {
3
+ const { LegendItem = "div", LegendItemStatus = "span", LegendItemLabel = "span", LegendItemEvents = "span", } = Koine;
4
+ // const sorted = Object.entries(calendarsMap).sort((a, b) => {
5
+ // const { name: nameA } = a[1];
6
+ // const { name: nameB } = b[1];
7
+ // if (nameA < nameB) return -1;
8
+ // else if (nameA > nameB) return 1;
9
+ // else return 0;
10
+ // });
11
+ return (_jsx(_Fragment, { children: Object.entries(calendarsMap).map(([id, calendar]) => (_jsxs(LegendItem, Object.assign({ onClick: () => toggleCalendarVisibility(id), "$color": calendar.color, "$empty": calendar.events === 0, disabled: calendar.events === 0 }, { children: [_jsx(LegendItemStatus, { children: calendar.on ? "\u2b24" : "\u2b58" }), _jsx(LegendItemLabel, { children: calendar.name }), _jsx(LegendItemEvents, { children: calendar.events })] }), "CalendarLegend." + id))) }));
12
+ };
@@ -0,0 +1,97 @@
1
+ import { __awaiter, __rest } from "tslib";
2
+ import { arrayToLookup, isString, isUndefined } from "@koine/utils";
3
+ import differenceInDays from "date-fns/differenceInDays";
4
+ import subDays from "date-fns/subDays";
5
+ import { getEventTimestamp, addCalendarEvents } from "./utils";
6
+ const baseURL = "https://www.googleapis.com/calendar/v3/calendars/";
7
+ export function getCalendarsEventsFromGoogle(_a) {
8
+ var { calendars } = _a, options = __rest(_a, ["calendars"]);
9
+ return __awaiter(this, void 0, void 0, function* () {
10
+ const allEvents = {};
11
+ yield Promise.all(calendars.map((calendar) => __awaiter(this, void 0, void 0, function* () {
12
+ const events = yield getCalendarEventsFromGoogle(Object.assign({ calendar }, options));
13
+ addCalendarEvents(events, allEvents);
14
+ })));
15
+ return allEvents;
16
+ });
17
+ }
18
+ function getCalendarEventsFromGoogle({ apiKey, calendar, timeZone = "", start, end, }) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const events = {};
21
+ const params = new URLSearchParams({
22
+ calendarId: calendar.id,
23
+ timeZone,
24
+ singleEvents: "true",
25
+ maxAttendees: "1",
26
+ maxResults: "9999",
27
+ sanitizeHtml: "true",
28
+ timeMin: start.toISOString(),
29
+ timeMax: end.toISOString(),
30
+ key: apiKey || process.env["GOOGLE_CALENDAR_API_KEY"] || "",
31
+ }).toString();
32
+ const url = baseURL + calendar.id + "/events?" + params;
33
+ try {
34
+ const response = yield fetch(url, { method: "GET" });
35
+ const data = (yield response.json());
36
+ calendar.name = calendar.name || data.summary;
37
+ data.items.forEach((googleEvent) => {
38
+ const event = transformCalendarEventFromGoogle(googleEvent, calendar);
39
+ events[event.uid] = event;
40
+ });
41
+ }
42
+ catch (e) {
43
+ // if (onError) onError(e);
44
+ }
45
+ return events;
46
+ });
47
+ }
48
+ function transformCalendarEventFromGoogle(event, calendar) {
49
+ const created = new Date(event.created);
50
+ const link = event.htmlLink;
51
+ const title = event.summary;
52
+ const status = event.status;
53
+ const start = new Date(event.start.date || event.start.dateTime);
54
+ let end = new Date(event.end.date || event.end.dateTime);
55
+ const color = calendar.color;
56
+ const allDay = isUndefined(event.end.dateTime) && isString(event.end.date);
57
+ const location = event.location || "";
58
+ const description = event.description || ""; // FIXME: he.decode(event.description || '');
59
+ const uid = created.getTime() + "" + start.getTime();
60
+ // multi-days all day events has as end date the date after to what we actually
61
+ // mean, hence we subtract one day. @see https://support.google.com/calendar/thread/10074544/google-calendar-all-day-events-are-showing-up-as-a-24-hr-event-across-time-zones?hl=en
62
+ if (allDay && end > start) {
63
+ end = subDays(end, 1);
64
+ end.setHours(23, 59, 59);
65
+ }
66
+ const days = getDays();
67
+ const daysMap = arrayToLookup(days);
68
+ const multi = days.length > 1;
69
+ function getDays() {
70
+ const from = new Date(start);
71
+ const to = new Date(end);
72
+ const days = [getEventTimestamp(from)];
73
+ while (differenceInDays(to, from)) {
74
+ // console.log(title, differenceInDays(to, from))
75
+ from.setDate(from.getDate() + 1);
76
+ days.push(getEventTimestamp(from));
77
+ }
78
+ return days;
79
+ }
80
+ return {
81
+ calendar,
82
+ created,
83
+ link,
84
+ title,
85
+ status,
86
+ start,
87
+ end,
88
+ days,
89
+ daysMap,
90
+ multi,
91
+ color,
92
+ allDay,
93
+ location,
94
+ description,
95
+ uid,
96
+ };
97
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./CalendarDaygridCell";
2
+ export * from "./CalendarDaygridNav";
3
+ export * from "./CalendarDaygridTable";
4
+ export * from "./CalendarLegend";
5
+ export * from "./useCalendar";
6
+ export * from "./types";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,166 @@
1
+ import { __awaiter } from "tslib";
2
+ import { useCallback, useEffect, useReducer, useState } from "react";
3
+ import { getCalendarsEventsFromGoogle } from "./calendar-api-google";
4
+ import { getStartDate, getEndDate, getPrevDate, getNextDate, isTodayInView, } from "./utils";
5
+ export function useCalendar({ locale, apiKey, calendars, events: initialEvents, start: initialStart, end: initialEnd, view: initialView = "month", timeZone = "", onError, }) {
6
+ const [view, setView] = useState(initialView);
7
+ const start = initialStart || getStartDate(new Date(), view);
8
+ const end = initialEnd || getEndDate(start, view);
9
+ const [range, setRange] = useState([start, end]);
10
+ const [todayInView, setTodayInView] = useState(isTodayInView(start, end));
11
+ const [events, setEvents] = useState(initialEvents || {});
12
+ const [eventHovered, setEventHovered] = useState(null);
13
+ const [eventClicked, setEventClicked] = useState(null);
14
+ const [calendarsMap, updateCalendars] = useReducer((state, action) => {
15
+ const { type } = action;
16
+ switch (type) {
17
+ case "events": {
18
+ const events = action.payload;
19
+ return Object.entries(state).reduce((map, [id, calendar]) => {
20
+ map[id] = Object.assign(Object.assign({}, calendar), { events: events[id] || 0 });
21
+ return map;
22
+ }, {});
23
+ }
24
+ case "visibility": {
25
+ const visible = action.payload;
26
+ if (typeof visible === "string") {
27
+ return Object.assign(Object.assign({}, state), { [visible]: Object.assign(Object.assign({}, state[visible]), { on: !state[visible].on }) });
28
+ }
29
+ else {
30
+ return Object.entries(state).reduce((map, [id, calendar]) => {
31
+ map[id] = Object.assign(Object.assign({}, calendar), { on: visible.indexOf(id) > -1 });
32
+ return map;
33
+ }, {});
34
+ }
35
+ }
36
+ default:
37
+ return state;
38
+ }
39
+ },
40
+ // initial state
41
+ calendars.reduce((map, calendar) => {
42
+ map[calendar.id] = Object.assign(Object.assign({}, calendar), { name: calendar.name || "", on: true, events: 0 });
43
+ return map;
44
+ }, {}));
45
+ const toggleCalendarVisibility = useCallback((idOrIds) => {
46
+ updateCalendars({ type: "visibility", payload: idOrIds });
47
+ }, [updateCalendars]);
48
+ const updateCalendarsBasedOnEvents = useCallback((events) => {
49
+ const payload = {};
50
+ for (const uid in events) {
51
+ const { id } = events[uid].calendar;
52
+ payload[id] = payload[id] || 0;
53
+ payload[id]++;
54
+ }
55
+ updateCalendars({ type: "events", payload });
56
+ }, []);
57
+ const loadCalendars = useCallback((calendars, start, end) => __awaiter(this, void 0, void 0, function* () {
58
+ try {
59
+ const newEvents = yield getCalendarsEventsFromGoogle({
60
+ apiKey,
61
+ calendars,
62
+ timeZone,
63
+ start,
64
+ end,
65
+ });
66
+ // setEvents(mergeCalendarEvents(events, newEvents));
67
+ setEvents(newEvents);
68
+ }
69
+ catch (e) {
70
+ if (onError)
71
+ onError(e);
72
+ }
73
+ }), [setEvents, apiKey, timeZone, onError]);
74
+ const handleToday = useCallback(() => {
75
+ const [start, end] = range;
76
+ const newStart = getStartDate(new Date(), view);
77
+ const newEnd = getEndDate(newStart, view);
78
+ setRange([newStart, newEnd]);
79
+ // reset event only if we are not on the current view already
80
+ if (start.getTime() !== newStart.getTime() ||
81
+ end.getTime() !== newEnd.getTime()) {
82
+ setEventClicked(null);
83
+ setEventHovered(null);
84
+ }
85
+ }, [view, range]);
86
+ const handlePrev = useCallback(() => {
87
+ setRange(([start]) => {
88
+ const newStart = getPrevDate(start, view);
89
+ const newEnd = getEndDate(newStart, view);
90
+ return [newStart, newEnd];
91
+ });
92
+ setEventClicked(null);
93
+ setEventHovered(null);
94
+ }, [view]);
95
+ const handleNext = useCallback(() => {
96
+ setRange(([start]) => {
97
+ const newStart = getNextDate(start, view);
98
+ const newEnd = getEndDate(newStart, view);
99
+ return [newStart, newEnd];
100
+ });
101
+ setEventClicked(null);
102
+ setEventHovered(null);
103
+ }, [view]);
104
+ const handleView = useCallback((newView) => {
105
+ const newStart = getStartDate(start, newView);
106
+ const newEnd = getEndDate(newStart, newView);
107
+ setRange([newStart, newEnd]);
108
+ setView(newView);
109
+ setEventClicked(null);
110
+ setEventHovered(null);
111
+ }, [start]);
112
+ useEffect(() => {
113
+ const [start, end] = range;
114
+ loadCalendars(calendars, start, end);
115
+ setTodayInView(isTodayInView(start, end));
116
+ // eslint-disable-next-line react-hooks/exhaustive-deps
117
+ }, [range]);
118
+ useEffect(() => {
119
+ if (events) {
120
+ updateCalendarsBasedOnEvents(events);
121
+ }
122
+ }, [events, updateCalendarsBasedOnEvents]);
123
+ // when toggling a calendar we also remove the clicked event if that belongs
124
+ // to a now hidden calendar
125
+ useEffect(() => {
126
+ if (eventClicked) {
127
+ if (!calendarsMap[eventClicked.calendar.id].on) {
128
+ setEventClicked(null);
129
+ }
130
+ }
131
+ }, [calendarsMap, eventClicked, setEventClicked]);
132
+ return {
133
+ view,
134
+ eventClicked,
135
+ setEventClicked,
136
+ eventHovered,
137
+ setEventHovered,
138
+ getDaygridNavProps: () => ({
139
+ locale,
140
+ handlePrev,
141
+ handleNext,
142
+ handleToday,
143
+ handleView,
144
+ todayInView,
145
+ range,
146
+ view,
147
+ }),
148
+ getDaygridTableProps: () => ({
149
+ locale,
150
+ events,
151
+ eventClicked,
152
+ setEventClicked,
153
+ eventHovered,
154
+ setEventHovered,
155
+ handlePrev,
156
+ handleNext,
157
+ calendarsMap,
158
+ range,
159
+ view,
160
+ }),
161
+ getLegendProps: () => ({
162
+ calendarsMap,
163
+ toggleCalendarVisibility,
164
+ }),
165
+ };
166
+ }