@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.
- package/README.md +41 -0
- package/lib/index.browser.cjs +2 -0
- package/lib/index.browser.cjs.map +1 -0
- package/lib/index.browser.d.ts +640 -0
- package/lib/index.browser.esm.js +2 -0
- package/lib/index.browser.esm.js.map +1 -0
- package/lib/index.node.cjs +2 -0
- package/lib/index.node.cjs.map +1 -0
- package/lib/index.node.d.ts +116 -0
- package/lib/index.node.esm.js +2 -0
- package/lib/index.node.esm.js.map +1 -0
- package/package.json +84 -0
- package/src/browser/api/button/index.tsx +16 -0
- package/src/browser/api/button/types.ts +7 -0
- package/src/browser/api/checkbox/index.tsx +74 -0
- package/src/browser/api/checkbox/types.tsx +21 -0
- package/src/browser/api/country-prefix/index.tsx +150 -0
- package/src/browser/api/country-prefix/types.tsx +17 -0
- package/src/browser/api/form/index.tsx +330 -0
- package/src/browser/api/form/types.ts +31 -0
- package/src/browser/api/google-places/index.tsx +90 -0
- package/src/browser/api/google-places/types.ts +24 -0
- package/src/browser/api/icon/index.tsx +26 -0
- package/src/browser/api/icon/types.tsx +24 -0
- package/src/browser/api/image/index.tsx +91 -0
- package/src/browser/api/image/types.ts +11 -0
- package/src/browser/api/input/index.tsx +44 -0
- package/src/browser/api/input/types.ts +10 -0
- package/src/browser/api/link/index.tsx +54 -0
- package/src/browser/api/link/types.ts +24 -0
- package/src/browser/api/linklist/index.tsx +17 -0
- package/src/browser/api/linklist/types.ts +6 -0
- package/src/browser/api/select/index.tsx +99 -0
- package/src/browser/api/select/types.ts +24 -0
- package/src/browser/api/svg/index.tsx +8 -0
- package/src/browser/api/svg/types.ts +8 -0
- package/src/browser/api/text/index.tsx +14 -0
- package/src/browser/api/text/types.ts +12 -0
- package/src/browser/api/textarea/index.tsx +12 -0
- package/src/browser/api/title/index.tsx +19 -0
- package/src/browser/api/title/types.ts +10 -0
- package/src/browser/api/types.ts +245 -0
- package/src/browser/carousel/buttons/index.tsx +81 -0
- package/src/browser/carousel/buttons/types.ts +15 -0
- package/src/browser/carousel/dots/index.tsx +53 -0
- package/src/browser/carousel/dots/types.ts +14 -0
- package/src/browser/carousel/index.tsx +131 -0
- package/src/browser/carousel/types.ts +21 -0
- package/src/browser/markdown/index.tsx +41 -0
- package/src/browser/markdown/types.ts +11 -0
- package/src/browser/modal/index.tsx +35 -0
- package/src/browser/modal/types.ts +9 -0
- package/src/browser/spinner/index.tsx +19 -0
- package/src/browser/spinner/types.ts +5 -0
- package/src/browser/toast/index.tsx +84 -0
- package/src/browser/use_viewport/index.tsx +34 -0
- package/src/browser/use_viewport/types.ts +5 -0
- package/src/browser/utils/animation.ts +12 -0
- package/src/browser/utils/booking_engine.ts +180 -0
- package/src/browser/utils/eclub.ts +30 -0
- package/src/browser/utils/forms.ts +76 -0
- package/src/browser/utils/hotel.ts +25 -0
- package/src/browser/utils/image.ts +63 -0
- package/src/browser/utils/location.ts +213 -0
- package/src/browser/utils/notifications.tsx +25 -0
- package/src/browser/utils/number.ts +6 -0
- package/src/browser/utils/requests.ts +2 -0
- package/src/browser/utils/search.ts +25 -0
- package/src/browser/utils/string.ts +9 -0
- package/src/browser/utils/types.ts +106 -0
- package/src/browser/utils/url.ts +116 -0
- package/src/browser/utils/viewport.ts +59 -0
- package/src/index.browser.ts +103 -0
- package/src/index.node.ts +16 -0
- package/src/node/api/index.ts +174 -0
- package/src/node/api/types.ts +19 -0
- package/src/node/build/index.ts +142 -0
- package/src/node/build/types.ts +5 -0
- package/src/node/config/index.ts +149 -0
- package/src/node/form/index.ts +23 -0
- package/src/node/form/types.ts +3 -0
- package/src/node/property/index.ts +78 -0
- package/src/node/property/types.ts +25 -0
- 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,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
|
+
}
|