@tfehotels/tfe-gatsby-library 1.0.0

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 (84) hide show
  1. package/README.md +41 -0
  2. package/lib/index.browser.cjs +2 -0
  3. package/lib/index.browser.cjs.map +1 -0
  4. package/lib/index.browser.d.ts +640 -0
  5. package/lib/index.browser.esm.js +2 -0
  6. package/lib/index.browser.esm.js.map +1 -0
  7. package/lib/index.node.cjs +2 -0
  8. package/lib/index.node.cjs.map +1 -0
  9. package/lib/index.node.d.ts +116 -0
  10. package/lib/index.node.esm.js +2 -0
  11. package/lib/index.node.esm.js.map +1 -0
  12. package/package.json +84 -0
  13. package/src/browser/api/button/index.tsx +16 -0
  14. package/src/browser/api/button/types.ts +7 -0
  15. package/src/browser/api/checkbox/index.tsx +74 -0
  16. package/src/browser/api/checkbox/types.tsx +21 -0
  17. package/src/browser/api/country-prefix/index.tsx +150 -0
  18. package/src/browser/api/country-prefix/types.tsx +17 -0
  19. package/src/browser/api/form/index.tsx +330 -0
  20. package/src/browser/api/form/types.ts +31 -0
  21. package/src/browser/api/google-places/index.tsx +90 -0
  22. package/src/browser/api/google-places/types.ts +24 -0
  23. package/src/browser/api/icon/index.tsx +26 -0
  24. package/src/browser/api/icon/types.tsx +24 -0
  25. package/src/browser/api/image/index.tsx +91 -0
  26. package/src/browser/api/image/types.ts +11 -0
  27. package/src/browser/api/input/index.tsx +44 -0
  28. package/src/browser/api/input/types.ts +10 -0
  29. package/src/browser/api/link/index.tsx +54 -0
  30. package/src/browser/api/link/types.ts +24 -0
  31. package/src/browser/api/linklist/index.tsx +17 -0
  32. package/src/browser/api/linklist/types.ts +6 -0
  33. package/src/browser/api/select/index.tsx +99 -0
  34. package/src/browser/api/select/types.ts +24 -0
  35. package/src/browser/api/svg/index.tsx +8 -0
  36. package/src/browser/api/svg/types.ts +8 -0
  37. package/src/browser/api/text/index.tsx +14 -0
  38. package/src/browser/api/text/types.ts +12 -0
  39. package/src/browser/api/textarea/index.tsx +12 -0
  40. package/src/browser/api/title/index.tsx +19 -0
  41. package/src/browser/api/title/types.ts +10 -0
  42. package/src/browser/api/types.ts +245 -0
  43. package/src/browser/carousel/buttons/index.tsx +81 -0
  44. package/src/browser/carousel/buttons/types.ts +15 -0
  45. package/src/browser/carousel/dots/index.tsx +53 -0
  46. package/src/browser/carousel/dots/types.ts +14 -0
  47. package/src/browser/carousel/index.tsx +131 -0
  48. package/src/browser/carousel/types.ts +21 -0
  49. package/src/browser/markdown/index.tsx +41 -0
  50. package/src/browser/markdown/types.ts +11 -0
  51. package/src/browser/modal/index.tsx +35 -0
  52. package/src/browser/modal/types.ts +9 -0
  53. package/src/browser/spinner/index.tsx +19 -0
  54. package/src/browser/spinner/types.ts +5 -0
  55. package/src/browser/toast/index.tsx +84 -0
  56. package/src/browser/use_viewport/index.tsx +34 -0
  57. package/src/browser/use_viewport/types.ts +5 -0
  58. package/src/browser/utils/animation.ts +12 -0
  59. package/src/browser/utils/booking_engine.ts +180 -0
  60. package/src/browser/utils/eclub.ts +30 -0
  61. package/src/browser/utils/forms.ts +76 -0
  62. package/src/browser/utils/hotel.ts +25 -0
  63. package/src/browser/utils/image.ts +63 -0
  64. package/src/browser/utils/location.ts +213 -0
  65. package/src/browser/utils/notifications.tsx +25 -0
  66. package/src/browser/utils/number.ts +6 -0
  67. package/src/browser/utils/requests.ts +2 -0
  68. package/src/browser/utils/search.ts +25 -0
  69. package/src/browser/utils/string.ts +9 -0
  70. package/src/browser/utils/types.ts +106 -0
  71. package/src/browser/utils/url.ts +116 -0
  72. package/src/browser/utils/viewport.ts +59 -0
  73. package/src/index.browser.ts +103 -0
  74. package/src/index.node.ts +16 -0
  75. package/src/node/api/index.ts +174 -0
  76. package/src/node/api/types.ts +19 -0
  77. package/src/node/build/index.ts +142 -0
  78. package/src/node/build/types.ts +5 -0
  79. package/src/node/config/index.ts +149 -0
  80. package/src/node/form/index.ts +23 -0
  81. package/src/node/form/types.ts +3 -0
  82. package/src/node/property/index.ts +78 -0
  83. package/src/node/property/types.ts +25 -0
  84. package/src/node/url/index.ts +8 -0
@@ -0,0 +1,245 @@
1
+ import type { IGatsbyImageData } from "gatsby-plugin-image";
2
+
3
+ export interface BuildContext {
4
+ slug: string;
5
+ }
6
+
7
+ export interface DataAttributeType {
8
+ [key: string]: any;
9
+ }
10
+ export interface IconType {
11
+ icon: string;
12
+ group: string;
13
+ }
14
+ export interface ElementType {
15
+ name?: string;
16
+ data: DataAttributeType;
17
+ icon?: IconType;
18
+ }
19
+
20
+ export interface TitleType extends ElementType {
21
+ type: "title";
22
+ title: string;
23
+ }
24
+
25
+ export interface UserType {
26
+ loyaltyprofileId: string;
27
+ profile_id: string;
28
+ title: string;
29
+ first_name: string;
30
+ last_name: string;
31
+ email: string;
32
+ zip: string;
33
+ country_code: string;
34
+ MobilePhoneCountryCode: string;
35
+ mobile: string;
36
+ date_of_birth: string;
37
+ language: string;
38
+ }
39
+
40
+ export interface ImageType {
41
+ type: "image";
42
+ url: string;
43
+ optimized: boolean;
44
+ name: string;
45
+ alt: string | null;
46
+ caption: string | null;
47
+ width: number;
48
+ height: number;
49
+ extension: string;
50
+ }
51
+ export interface FileType {
52
+ url: string;
53
+ size: number;
54
+ label: string;
55
+ extension: string;
56
+ }
57
+ export interface MediaType extends ElementType {
58
+ type: "file" | "instagram" | "image" | "video";
59
+ title: string;
60
+ loading: "eager" | "lazy";
61
+ image?: ImageType;
62
+ mobile_image?: ImageType;
63
+ file?: FileType;
64
+ mobile_file?: FileType;
65
+ alt?: string;
66
+ external_link?: string;
67
+ page_slug?: string;
68
+ page?: string;
69
+ }
70
+
71
+ export interface ImageMediaType extends MediaType {
72
+ type: "image";
73
+ imageData?: IGatsbyImageData;
74
+ }
75
+
76
+ export interface FileMediaType extends MediaType {
77
+ type: "file";
78
+ file: FileType;
79
+ }
80
+
81
+ export interface VideoMediaType extends MediaType {
82
+ type: "video";
83
+ }
84
+
85
+ export interface MediaListType {
86
+ pk: number;
87
+ type: "medialist";
88
+ files: MediaType[];
89
+ }
90
+ export interface TextType extends ElementType {
91
+ type: "text";
92
+ text: string;
93
+ }
94
+
95
+ export interface LinkType extends ElementType {
96
+ type: "link";
97
+ tag: "a";
98
+ target?: "_blank" | "_parent" | "_top";
99
+ text?: string;
100
+ group?: string;
101
+ page?: string;
102
+ page_slug?: string;
103
+ external_link?: string;
104
+ }
105
+
106
+ export interface LinkListType extends ElementType {
107
+ pk: number;
108
+ type: "linklist";
109
+ links: LinkType[];
110
+ }
111
+
112
+ export interface InputType extends ElementType {
113
+ type: "input";
114
+ input_name: string;
115
+ input_type:
116
+ | "text"
117
+ | "textarea"
118
+ | "button"
119
+ | "checkbox"
120
+ | "color"
121
+ | "date"
122
+ | "datetime-local"
123
+ | "email"
124
+ | "file"
125
+ | "hidden"
126
+ | "image"
127
+ | "month"
128
+ | "number"
129
+ | "password"
130
+ | "radio"
131
+ | "range"
132
+ | "reset"
133
+ | "recaptcha2"
134
+ | "search"
135
+ | "submit"
136
+ | "tel"
137
+ | "time"
138
+ | "url"
139
+ | "week"
140
+ | "google-places";
141
+ label?: string;
142
+ min?: string;
143
+ placeholder?: string;
144
+ required?: boolean;
145
+ disabled?: boolean;
146
+ value?: string;
147
+ iconOnClick?: Function;
148
+ description?: string;
149
+ }
150
+
151
+ export interface SelectType extends Omit<InputType, "type" | "input_type"> {
152
+ type: "select";
153
+ input_type: "select";
154
+ input_name: string;
155
+ options: { [value: string]: string };
156
+ }
157
+
158
+ export interface CheckboxType extends Omit<InputType, "type" | "input_type"> {
159
+ type: "checkbox";
160
+ input_type: "checkbox";
161
+ }
162
+
163
+ export interface RecaptchaType extends Omit<InputType, "type" | "input_type"> {
164
+ type: "recaptcha2";
165
+ input_type: "recaptcha2";
166
+ }
167
+
168
+ export interface GoogleSitesType
169
+ extends Omit<InputType, "type" | "input_type"> {
170
+ type: "google-places";
171
+ input_type: "google-places";
172
+ }
173
+
174
+ export interface ButtonType extends Omit<InputType, "type"> {
175
+ type: "button";
176
+ input_type: "button";
177
+ }
178
+
179
+ export interface SectionType extends ElementType {
180
+ type: "section";
181
+ tag?: "section" | "form" | "header" | "footer" | "div" | "nav";
182
+ pk: number;
183
+ children?: (
184
+ | TitleType
185
+ | TextType
186
+ | LinkType
187
+ | LinkListType
188
+ | InputType
189
+ | ButtonType
190
+ | SectionType
191
+ | SelectType
192
+ | MediaType
193
+ )[];
194
+ }
195
+
196
+ export interface ElementDataType extends ElementType {
197
+ type: "data";
198
+ children: any[];
199
+ }
200
+
201
+ export type AnyElementType =
202
+ | LinkType
203
+ | LinkListType
204
+ | InputType
205
+ | ButtonType
206
+ | SectionType
207
+ | MediaType;
208
+
209
+ export interface FormSectionType extends Omit<SectionType, "children"> {
210
+ name: string;
211
+ recaptcha_v3?: boolean;
212
+ form_method?: "post" | "get";
213
+ form_ajax_request?: boolean;
214
+ form_submit?: string;
215
+ form_success_msg?: string;
216
+ form_recipient?: number;
217
+ children: (InputType | SelectType)[];
218
+ }
219
+
220
+ export interface PageParamsType {
221
+ data: any;
222
+ hotel: null | string;
223
+ tags: { [k: string]: string };
224
+ }
225
+
226
+ export interface LocationType {
227
+ hash: string;
228
+ host: string;
229
+ hostname: string;
230
+ href: string;
231
+ key: string;
232
+ origin: string;
233
+ pathname: string;
234
+ port: string;
235
+ protocol: string;
236
+ search: string;
237
+ }
238
+
239
+ export interface DefaultPropsType extends Omit<SectionType, "children"> {
240
+ className: string;
241
+ children: AnyElementType[];
242
+ pageParams: PageParamsType;
243
+ location: LocationType;
244
+ user: null | UserType;
245
+ }
@@ -0,0 +1,81 @@
1
+ import React, { useCallback, useEffect, useState } from "react"
2
+ import { EmblaCarouselType } from "embla-carousel"
3
+ import { PropType, UseButtonsType } from "./types"
4
+
5
+ export const useButtons = (
6
+ emblaApi: EmblaCarouselType | undefined,
7
+ showButtons: boolean | undefined
8
+ ): UseButtonsType => {
9
+ const [prevBtnDisabled, setPrevBtnDisabled] = useState(true)
10
+ const [nextBtnDisabled, setNextBtnDisabled] = useState(true)
11
+
12
+ const onPrevButtonClick = useCallback(() => {
13
+ if (!emblaApi) return
14
+ emblaApi.scrollPrev()
15
+ }, [emblaApi])
16
+
17
+ const onNextButtonClick = useCallback(() => {
18
+ if (!emblaApi) return
19
+ emblaApi.scrollNext()
20
+ }, [emblaApi])
21
+
22
+ const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
23
+ setPrevBtnDisabled(!emblaApi.canScrollPrev())
24
+ setNextBtnDisabled(!emblaApi.canScrollNext())
25
+ }, [])
26
+
27
+ useEffect(() => {
28
+ if (!emblaApi || !showButtons) return
29
+
30
+ onSelect(emblaApi)
31
+ emblaApi.on("reInit", onSelect)
32
+ emblaApi.on("select", onSelect)
33
+ }, [emblaApi, onSelect])
34
+
35
+ return {
36
+ prevBtnDisabled,
37
+ nextBtnDisabled,
38
+ onPrevButtonClick,
39
+ onNextButtonClick,
40
+ }
41
+ }
42
+
43
+ export const PrevButton: React.FC<PropType> = props => {
44
+ const { children, ...restProps } = props
45
+
46
+ return (
47
+ <button
48
+ className="embla__button embla__button--prev"
49
+ type="button"
50
+ {...restProps}
51
+ >
52
+ <svg className="embla__button__svg" viewBox="0 0 532 532">
53
+ <path
54
+ fill="currentColor"
55
+ d="M355.66 11.354c13.793-13.805 36.208-13.805 50.001 0 13.785 13.804 13.785 36.238 0 50.034L201.22 266l204.442 204.61c13.785 13.805 13.785 36.239 0 50.044-13.793 13.796-36.208 13.796-50.002 0a5994246.277 5994246.277 0 0 0-229.332-229.454 35.065 35.065 0 0 1-10.326-25.126c0-9.2 3.393-18.26 10.326-25.2C172.192 194.973 332.731 34.31 355.66 11.354Z"
56
+ />
57
+ </svg>
58
+ {children}
59
+ </button>
60
+ )
61
+ }
62
+
63
+ export const NextButton: React.FC<PropType> = props => {
64
+ const { children, ...restProps } = props
65
+
66
+ return (
67
+ <button
68
+ className="embla__button embla__button--next"
69
+ type="button"
70
+ {...restProps}
71
+ >
72
+ <svg className="embla__button__svg" viewBox="0 0 532 532">
73
+ <path
74
+ fill="currentColor"
75
+ d="M176.34 520.646c-13.793 13.805-36.208 13.805-50.001 0-13.785-13.804-13.785-36.238 0-50.034L330.78 266 126.34 61.391c-13.785-13.805-13.785-36.239 0-50.044 13.793-13.796 36.208-13.796 50.002 0 22.928 22.947 206.395 206.507 229.332 229.454a35.065 35.065 0 0 1 10.326 25.126c0 9.2-3.393 18.26-10.326 25.2-45.865 45.901-206.404 206.564-229.332 229.52Z"
76
+ />
77
+ </svg>
78
+ {children}
79
+ </button>
80
+ )
81
+ }
@@ -0,0 +1,15 @@
1
+ import { PropsWithChildren } from "react"
2
+
3
+ export type UseButtonsType = {
4
+ prevBtnDisabled: boolean
5
+ nextBtnDisabled: boolean
6
+ onPrevButtonClick: () => void
7
+ onNextButtonClick: () => void
8
+ }
9
+
10
+ export type PropType = PropsWithChildren<
11
+ React.DetailedHTMLProps<
12
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
13
+ HTMLButtonElement
14
+ >
15
+ >
@@ -0,0 +1,53 @@
1
+ import React, { useCallback, useEffect, useState } from "react"
2
+ import { EmblaCarouselType } from "embla-carousel"
3
+ import { PropType, UseDotsType } from "./types"
4
+
5
+ export const useDots = (
6
+ emblaApi: EmblaCarouselType | undefined,
7
+ showDots: boolean | undefined
8
+ ): UseDotsType => {
9
+ const [selectedIndex, setSelectedIndex] = useState(0)
10
+ const [scrollSnaps, setScrollSnaps] = useState<number[]>([])
11
+
12
+ const onDotButtonClick = useCallback(
13
+ (index: number) => {
14
+ if (!emblaApi) return
15
+ emblaApi.scrollTo(index)
16
+ },
17
+ [emblaApi]
18
+ )
19
+
20
+ const onInit = useCallback((emblaApi: EmblaCarouselType) => {
21
+ setScrollSnaps(emblaApi.scrollSnapList())
22
+ }, [])
23
+
24
+ const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
25
+ setSelectedIndex(emblaApi.selectedScrollSnap())
26
+ }, [])
27
+
28
+ useEffect(() => {
29
+ if (!emblaApi || !showDots) return
30
+
31
+ onInit(emblaApi)
32
+ onSelect(emblaApi)
33
+ emblaApi.on("reInit", onInit)
34
+ emblaApi.on("reInit", onSelect)
35
+ emblaApi.on("select", onSelect)
36
+ }, [emblaApi, onInit, onSelect])
37
+
38
+ return {
39
+ selectedIndex,
40
+ scrollSnaps,
41
+ onDotButtonClick,
42
+ }
43
+ }
44
+
45
+ export const DotButton: React.FC<PropType> = props => {
46
+ const { children, ...restProps } = props
47
+
48
+ return (
49
+ <button type="button" {...restProps}>
50
+ {children}
51
+ </button>
52
+ )
53
+ }
@@ -0,0 +1,14 @@
1
+ import { PropsWithChildren } from "react"
2
+
3
+ export type UseDotsType = {
4
+ selectedIndex: number
5
+ scrollSnaps: number[]
6
+ onDotButtonClick: (index: number) => void
7
+ }
8
+
9
+ export type PropType = PropsWithChildren<
10
+ React.DetailedHTMLProps<
11
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
12
+ HTMLButtonElement
13
+ >
14
+ >
@@ -0,0 +1,131 @@
1
+ import React, { useCallback, useEffect, useState } from "react"
2
+ import { EmblaCarouselType } from "embla-carousel"
3
+ import { CarouselPropsType } from "./types"
4
+ import { useButtons } from "./buttons"
5
+ import { useDots,DotButton } from "./dots"
6
+ import { NextButton, PrevButton } from "./buttons"
7
+
8
+
9
+
10
+ const Carousel: React.FC<CarouselPropsType> = ({
11
+ id,
12
+ emblaRef,
13
+ emblaApi,
14
+ slidesPerView,
15
+ slidesPerViewport,
16
+ slides,
17
+ gap,
18
+ showButtons,
19
+ showDots,
20
+ showProgress,
21
+ }) => {
22
+ const [scrollProgress, setScrollProgress] = useState(0)
23
+ const { selectedIndex, scrollSnaps, onDotButtonClick } = useDots(
24
+ emblaApi,
25
+ showDots
26
+ )
27
+
28
+ // Calculate Carousel progress
29
+ const onScroll = useCallback((emblaApi: EmblaCarouselType) => {
30
+ const progress = Math.max(0, Math.min(1, emblaApi.scrollProgress()))
31
+ setScrollProgress(progress * 100)
32
+ }, [])
33
+
34
+ const {
35
+ prevBtnDisabled,
36
+ nextBtnDisabled,
37
+ onPrevButtonClick,
38
+ onNextButtonClick,
39
+ } = useButtons(emblaApi, showButtons)
40
+
41
+ useEffect(() => {
42
+ if (!emblaApi || !showProgress) return
43
+
44
+ onScroll(emblaApi)
45
+ emblaApi.on("reInit", onScroll)
46
+ emblaApi.on("scroll", onScroll)
47
+ }, [emblaApi, onScroll])
48
+
49
+ return (
50
+ <div id={id} className={`embla`}>
51
+ <div className="embla__viewport" ref={emblaRef}>
52
+ <div className="embla__container">
53
+ {slides.map((slide, k) => (
54
+ <div className="embla__slide" key={`embla-${k}`}>
55
+ {slide}
56
+ </div>
57
+ ))}
58
+ </div>
59
+ </div>
60
+ {showButtons && (
61
+ <>
62
+ <PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
63
+ <NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
64
+ </>
65
+ )}
66
+ {showDots && (
67
+ <div className="embla__dots">
68
+ {scrollSnaps.map((_: any, index: number) => (
69
+ <DotButton
70
+ key={index}
71
+ onClick={() => onDotButtonClick(index)}
72
+ className={"embla__dot".concat(
73
+ index === selectedIndex ? " embla__dot--selected" : ""
74
+ )}
75
+ />
76
+ ))}
77
+ </div>
78
+ )}
79
+ {!!showProgress && (
80
+ <div className="embla__progress">
81
+ <div
82
+ className="embla__progress__bar"
83
+ style={{ transform: `translate3d(${scrollProgress}%,0px,0px)` }}
84
+ />
85
+ </div>
86
+ )}
87
+ <style>
88
+ {`
89
+ #${id} .embla__container{
90
+ margin-left: calc(${gap ? gap : 0}px * -1);
91
+ }
92
+ #${id} .embla__slide{
93
+ flex: 0 0 ${(100 / slidesPerView).toFixed(2)}%;
94
+ padding-left: ${gap ? gap : 0}px;
95
+ }
96
+ ${
97
+ slidesPerViewport
98
+ ? Object.entries(slidesPerViewport).map(([screenSize, n]) => {
99
+ let media = "max-width"
100
+ let gap: number | null = null
101
+ let slides = n as number
102
+ if (typeof n === "object") {
103
+ slides = n.slides
104
+ if (n.media) {
105
+ media = n.media
106
+ }
107
+ if (n.gap) {
108
+ gap = n.gap
109
+ }
110
+ }
111
+ return `@media (${media}: ${screenSize}px) {
112
+ ${
113
+ gap !== null
114
+ ? `#${id} .embla__container{margin-left: calc(${gap}px * -1);}`
115
+ : ""
116
+ }
117
+ #${id} .embla__slide {
118
+ flex: 0 0 ${(100 / slides).toFixed(2)}%;
119
+ ${gap !== null ? `padding-left: ${gap}px;` : ""}
120
+ }
121
+ }`
122
+ })
123
+ : ""
124
+ }
125
+ `}
126
+ </style>
127
+ </div>
128
+ )
129
+ }
130
+
131
+ export default Carousel
@@ -0,0 +1,21 @@
1
+ import { EmblaCarouselType, EmblaOptionsType } from "embla-carousel";
2
+ export interface viewPortType {
3
+ slides: number;
4
+ media?: "max-width" | "min-width";
5
+ gap?: number;
6
+ }
7
+ export interface CarouselPropsType {
8
+ id: string;
9
+ emblaRef: any;
10
+ emblaApi?: EmblaCarouselType;
11
+ slidesPerView: number;
12
+ slidesPerViewport?: {
13
+ [screenSize: number]: number | viewPortType;
14
+ };
15
+ slides: React.ReactNode[];
16
+ options?: EmblaOptionsType;
17
+ showButtons?: boolean;
18
+ showDots?: boolean;
19
+ gap?: number;
20
+ showProgress?: boolean;
21
+ }
@@ -0,0 +1,41 @@
1
+ import React from "react"
2
+ import { marked } from "marked"
3
+ import parse from "html-react-parser"
4
+ import { MarkdownPropsType } from "./types"
5
+
6
+ export default function Markdown({
7
+ children,
8
+ className,
9
+ tagAttrs,
10
+ onClick,
11
+ }: MarkdownPropsType) {
12
+ const renderer = new marked.Renderer()
13
+ renderer.link = (href, title, text) => {
14
+ let attrs = {
15
+ href: href,
16
+ title: title ?? "",
17
+ target: "_blank",
18
+ }
19
+ if (tagAttrs?.a) {
20
+ attrs = { ...attrs, ...tagAttrs?.a }
21
+ }
22
+ return `<a ${Object.entries(attrs)
23
+ .map(([k, v]) => `${k}="${v}"`)
24
+ .join(" ")}>${text}</a>`
25
+ }
26
+ marked.setOptions({ renderer })
27
+
28
+ const createMarkup = () => parse(children ? (marked(children) as string) : "")
29
+
30
+ return (
31
+ <>
32
+ {className || onClick ? (
33
+ <div className={className} onClick={onClick}>
34
+ {createMarkup()}
35
+ </div>
36
+ ) : (
37
+ <>{createMarkup()}</>
38
+ )}
39
+ </>
40
+ )
41
+ }
@@ -0,0 +1,11 @@
1
+ import { MouseEventHandler } from "react"
2
+
3
+ export interface TagAttributesType {
4
+ [tag: string]: { [attr: string]: string }
5
+ }
6
+ export interface MarkdownPropsType {
7
+ className?: string
8
+ children: string
9
+ tagAttrs?: TagAttributesType
10
+ onClick?: MouseEventHandler<HTMLDivElement>
11
+ }
@@ -0,0 +1,35 @@
1
+ import React, { useEffect } from "react"
2
+ import { ModalPropsType } from "./types"
3
+ import Icon from "../api/icon"
4
+
5
+ const Modal = ({
6
+ modalId,
7
+ show,
8
+ className,
9
+ setShowModal,
10
+ children,
11
+ }: ModalPropsType) => {
12
+ useEffect(() => {
13
+ document.body.style.overflow = show ? "hidden" : "unset"
14
+ }, [show])
15
+ return (
16
+ <div
17
+ className={`modal-layout${show ? " opened" : ""}`}
18
+ onClick={e => {
19
+ // Close Modal when clicking outside:
20
+ if (e.target === e.currentTarget) {
21
+ setShowModal(modalId, false)
22
+ }
23
+ }}
24
+ >
25
+ <div className={`modal${className ? ` ${className}` : ""}`}>
26
+ <div className="modal-body">{children}</div>
27
+ <button className="close" onClick={() => setShowModal(modalId, false)}>
28
+ <Icon icon={{ icon: "times", group: "FAS" }} />
29
+ </button>
30
+ </div>
31
+ </div>
32
+ )
33
+ }
34
+
35
+ export default Modal
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from "react"
2
+
3
+ export interface ModalPropsType {
4
+ modalId: string
5
+ className?: string
6
+ show: boolean
7
+ setShowModal: Function
8
+ children: ReactNode
9
+ }
@@ -0,0 +1,19 @@
1
+ import React from "react"
2
+ import { SpinnerPropsType } from "./types"
3
+
4
+ export default function Spinner({ color, size, className }: SpinnerPropsType) {
5
+ return (
6
+ <div
7
+ className={`spinner-box${className ? ` ${className}` : ""}`}
8
+ >
9
+ <div
10
+ className="spinner"
11
+ style={{
12
+ border: `5px solid ${color ? color : "white"}`,
13
+ width: size ? size : 45,
14
+ height: size ? size : 45,
15
+ }}
16
+ ></div>
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,5 @@
1
+ export interface SpinnerPropsType {
2
+ className?: string
3
+ color?: string
4
+ size?: number | string
5
+ }