@c-rex/components 0.1.8 → 0.1.10
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/package.json +24 -4
- package/src/autocomplete.tsx +1 -1
- package/src/breadcrumb.tsx +84 -26
- package/src/check-article-lang.tsx +2 -2
- package/src/dialog-filter.tsx +5 -4
- package/src/flag.tsx +15 -0
- package/src/info/info-card.tsx +43 -0
- package/src/info/info-table.tsx +127 -0
- package/src/info/shared.tsx +32 -0
- package/src/{sidebar.tsx → left-sidebar.tsx} +3 -43
- package/src/navbar/language-switcher/content-language-switch.tsx +26 -3
- package/src/navbar/language-switcher/shared.tsx +14 -14
- package/src/navbar/navbar.tsx +6 -7
- package/src/navbar/search-input.tsx +10 -5
- package/src/navbar/settings.tsx +1 -1
- package/src/navbar/sign-in-out-btns.tsx +1 -16
- package/src/page-wrapper.tsx +3 -2
- package/src/pagination.tsx +10 -3
- package/src/result-list.tsx +17 -9
- package/src/result-view/blog.tsx +83 -10
- package/src/result-view/table.tsx +84 -64
- package/src/right-sidebar.tsx +52 -0
- package/src/blog-card.tsx +0 -88
- package/src/stories/blog-card.stories.tsx +0 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c-rex/components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"files": [
|
|
5
5
|
"src"
|
|
6
6
|
],
|
|
@@ -25,6 +25,22 @@
|
|
|
25
25
|
"types": "./src/breadcrumb.tsx",
|
|
26
26
|
"import": "./src/breadcrumb.tsx"
|
|
27
27
|
},
|
|
28
|
+
"./flag": {
|
|
29
|
+
"types": "./src/flag.tsx",
|
|
30
|
+
"import": "./src/flag.tsx"
|
|
31
|
+
},
|
|
32
|
+
"./pagination": {
|
|
33
|
+
"types": "./src/pagination.tsx",
|
|
34
|
+
"import": "./src/pagination.tsx"
|
|
35
|
+
},
|
|
36
|
+
"./info-table": {
|
|
37
|
+
"types": "./src/info/info-table.tsx",
|
|
38
|
+
"import": "./src/info/info-table.tsx"
|
|
39
|
+
},
|
|
40
|
+
"./info-card": {
|
|
41
|
+
"types": "./src/info/info-card.tsx",
|
|
42
|
+
"import": "./src/info/info-card.tsx"
|
|
43
|
+
},
|
|
28
44
|
"./empty": {
|
|
29
45
|
"types": "./src/empty.tsx",
|
|
30
46
|
"import": "./src/empty.tsx"
|
|
@@ -53,9 +69,13 @@
|
|
|
53
69
|
"types": "./src/result-view/dropdown-menu.tsx",
|
|
54
70
|
"import": "./src/result-view/dropdown-menu.tsx"
|
|
55
71
|
},
|
|
56
|
-
"./sidebar": {
|
|
57
|
-
"types": "./src/sidebar.tsx",
|
|
58
|
-
"import": "./src/sidebar.tsx"
|
|
72
|
+
"./left-sidebar": {
|
|
73
|
+
"types": "./src/left-sidebar.tsx",
|
|
74
|
+
"import": "./src/left-sidebar.tsx"
|
|
75
|
+
},
|
|
76
|
+
"./right-sidebar": {
|
|
77
|
+
"types": "./src/right-sidebar.tsx",
|
|
78
|
+
"import": "./src/right-sidebar.tsx"
|
|
59
79
|
},
|
|
60
80
|
"./dialog-filter": {
|
|
61
81
|
"types": "./src/dialog-filter.tsx",
|
package/src/autocomplete.tsx
CHANGED
|
@@ -81,7 +81,7 @@ export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props)
|
|
|
81
81
|
}, [query]);
|
|
82
82
|
|
|
83
83
|
return (
|
|
84
|
-
<div className="relative" ref={containerRef}>
|
|
84
|
+
<div className="relative flex-1" ref={containerRef}>
|
|
85
85
|
<Input
|
|
86
86
|
variant={embedded ? "embedded" : undefined}
|
|
87
87
|
type="text"
|
package/src/breadcrumb.tsx
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import React, { FC, Fragment, ReactNode } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Breadcrumb as BreadcrumbComponent,
|
|
4
|
+
BreadcrumbEllipsis,
|
|
4
5
|
BreadcrumbItem,
|
|
5
6
|
BreadcrumbLink,
|
|
6
7
|
BreadcrumbList,
|
|
7
8
|
BreadcrumbPage,
|
|
8
9
|
BreadcrumbSeparator
|
|
9
10
|
} from "@c-rex/ui/breadcrumb";
|
|
11
|
+
import { Button } from "@c-rex/ui/button";
|
|
12
|
+
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "@c-rex/ui/drawer";
|
|
10
13
|
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
11
14
|
import { TreeOfContent } from "@c-rex/interfaces";
|
|
12
15
|
import { useTranslations } from 'next-intl';
|
|
16
|
+
import Link from "next/link"
|
|
17
|
+
import { useBreakpoint } from "@c-rex/ui/hooks"
|
|
18
|
+
import { DEVICE_OPTIONS } from "@c-rex/constants";
|
|
13
19
|
|
|
14
20
|
interface BreadcrumbProps {
|
|
15
21
|
items: TreeOfContent[];
|
|
@@ -19,9 +25,12 @@ interface BreadcrumbProps {
|
|
|
19
25
|
|
|
20
26
|
export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
|
|
21
27
|
const t = useTranslations("breadcrumbs");
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
const device = useBreakpoint();
|
|
29
|
+
const isMobile = (device === DEVICE_OPTIONS.MOBILE || device === DEVICE_OPTIONS.TABLET);
|
|
30
|
+
|
|
31
|
+
if (!items) return null
|
|
32
|
+
|
|
33
|
+
const lastItem = items[items.length - 1] as TreeOfContent;
|
|
25
34
|
|
|
26
35
|
if (loading) {
|
|
27
36
|
return (
|
|
@@ -43,15 +52,49 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
|
|
|
43
52
|
);
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
const renderDrawer = () => {
|
|
56
|
+
if (items.length < 2) return null
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<>
|
|
60
|
+
<BreadcrumbItem>
|
|
61
|
+
<Drawer>
|
|
62
|
+
<DrawerTrigger asChild>
|
|
63
|
+
<BreadcrumbEllipsis className="h-4 w-4" />
|
|
64
|
+
</DrawerTrigger>
|
|
65
|
+
<DrawerContent>
|
|
66
|
+
<DrawerHeader className="text-left">
|
|
67
|
+
<DrawerTitle>Navigate to</DrawerTitle>
|
|
68
|
+
<DrawerDescription>
|
|
69
|
+
Select a page to navigate to.
|
|
70
|
+
</DrawerDescription>
|
|
71
|
+
</DrawerHeader>
|
|
72
|
+
|
|
73
|
+
<div className="grid gap-1 px-4">
|
|
74
|
+
{items.slice(0, -1).map((item, index) => (
|
|
75
|
+
<Link
|
|
76
|
+
key={index}
|
|
77
|
+
href={item.link}
|
|
78
|
+
className="py-1 text-sm"
|
|
79
|
+
>
|
|
80
|
+
{item.label}
|
|
81
|
+
</Link>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
53
84
|
|
|
54
|
-
|
|
85
|
+
<DrawerFooter className="pt-4">
|
|
86
|
+
<DrawerClose asChild>
|
|
87
|
+
<Button variant="outline">Close</Button>
|
|
88
|
+
</DrawerClose>
|
|
89
|
+
</DrawerFooter>
|
|
90
|
+
</DrawerContent>
|
|
91
|
+
</Drawer>
|
|
92
|
+
</BreadcrumbItem>
|
|
93
|
+
<BreadcrumbSeparator />
|
|
94
|
+
</>
|
|
95
|
+
|
|
96
|
+
);
|
|
97
|
+
};
|
|
55
98
|
|
|
56
99
|
const renderLink = (showLink: boolean, item: TreeOfContent): ReactNode => {
|
|
57
100
|
if (showLink) {
|
|
@@ -62,22 +105,37 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
|
|
|
62
105
|
};
|
|
63
106
|
|
|
64
107
|
return (
|
|
65
|
-
<BreadcrumbComponent lang={lang}>
|
|
108
|
+
<BreadcrumbComponent lang={lang} className="hidden sm:block">
|
|
66
109
|
<BreadcrumbList>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
110
|
+
<BreadcrumbItem>
|
|
111
|
+
<BreadcrumbLink asChild>
|
|
112
|
+
<BreadcrumbLink href="/">{t("home")}</BreadcrumbLink>
|
|
113
|
+
</BreadcrumbLink>
|
|
114
|
+
</BreadcrumbItem>
|
|
115
|
+
<BreadcrumbSeparator />
|
|
116
|
+
|
|
117
|
+
{isMobile ? renderDrawer() : (
|
|
118
|
+
<>
|
|
119
|
+
{items.slice(0, -1).map((item, index) => {
|
|
120
|
+
const isLast = index === items.length - 1;
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Fragment key={`${item.label}-fragment`}>
|
|
124
|
+
<BreadcrumbItem key={`${item.label}-item`} className={index.toString()}>
|
|
125
|
+
{renderLink(!isLast, item)}
|
|
126
|
+
</BreadcrumbItem>
|
|
127
|
+
{!isLast && (
|
|
128
|
+
<BreadcrumbSeparator key={`${item.label}-separator`} />
|
|
129
|
+
)}
|
|
130
|
+
</Fragment>
|
|
131
|
+
);
|
|
132
|
+
})}
|
|
133
|
+
</>
|
|
134
|
+
)}
|
|
135
|
+
|
|
136
|
+
<BreadcrumbPage>
|
|
137
|
+
{renderLink(false, lastItem)}
|
|
138
|
+
</BreadcrumbPage>
|
|
81
139
|
</BreadcrumbList>
|
|
82
140
|
</BreadcrumbComponent>
|
|
83
141
|
);
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { FC, useEffect } from "react";
|
|
4
4
|
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
5
|
-
import {
|
|
5
|
+
import { AvailableVersionsInterface } from "@c-rex/interfaces";
|
|
6
6
|
import { toast } from "sonner"
|
|
7
7
|
import { useTranslations } from "next-intl"
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
|
-
availableVersions:
|
|
10
|
+
availableVersions: AvailableVersionsInterface[]
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
|
package/src/dialog-filter.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { FC,
|
|
1
|
+
import React, { FC, useEffect, useState } from "react"
|
|
2
2
|
import { Button } from "@c-rex/ui/button"
|
|
3
3
|
import {
|
|
4
4
|
Dialog,
|
|
@@ -18,10 +18,10 @@ import { useAppConfig } from "@c-rex/contexts/config-provider"
|
|
|
18
18
|
import { LanguageAndCountries } from "@c-rex/interfaces"
|
|
19
19
|
import { WILD_CARD_OPTIONS, OPERATOR_OPTIONS } from "@c-rex/constants"
|
|
20
20
|
import { Switch } from "@c-rex/ui/switch"
|
|
21
|
+
import { useSearchContext } from "@c-rex/contexts/search"
|
|
21
22
|
|
|
22
23
|
interface DialogFilterProps {
|
|
23
24
|
trigger: React.ReactNode;
|
|
24
|
-
setLoading: (loading: boolean) => void;
|
|
25
25
|
}
|
|
26
26
|
interface Languages {
|
|
27
27
|
lang: string;
|
|
@@ -29,8 +29,9 @@ interface Languages {
|
|
|
29
29
|
checked: boolean;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export const DialogFilter: FC<DialogFilterProps> = ({ trigger
|
|
32
|
+
export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
|
|
33
33
|
const t = useTranslations("filter");
|
|
34
|
+
const { setLoading } = useSearchContext();
|
|
34
35
|
const { availableLanguagesAndCountries } = useAppConfig()
|
|
35
36
|
const [params, setParams] = useQueryStates({
|
|
36
37
|
language: parseAsString,
|
|
@@ -112,7 +113,7 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger, setLoading }) =>
|
|
|
112
113
|
</DialogTrigger>
|
|
113
114
|
<DialogContent>
|
|
114
115
|
<DialogHeader>
|
|
115
|
-
<DialogTitle>{t("
|
|
116
|
+
<DialogTitle>{t("searchSettings")}</DialogTitle>
|
|
116
117
|
</DialogHeader>
|
|
117
118
|
|
|
118
119
|
<div className="grid grid-cols-2 items-center pt-2">
|
package/src/flag.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React, { FC } from "react";
|
|
2
|
+
import * as Flags from 'country-flag-icons/react/3x2';
|
|
3
|
+
|
|
4
|
+
interface FlagProps {
|
|
5
|
+
countryCode: string;
|
|
6
|
+
}
|
|
7
|
+
export const Flag: FC<FlagProps> = ({ countryCode }) => {
|
|
8
|
+
|
|
9
|
+
type CountryCode = keyof typeof Flags;
|
|
10
|
+
const FlagComponent = Flags[countryCode as CountryCode];
|
|
11
|
+
|
|
12
|
+
if (!FlagComponent) return null;
|
|
13
|
+
|
|
14
|
+
return <FlagComponent />;
|
|
15
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
|
|
2
|
+
import { getFromCookieString } from "@c-rex/utils";
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import React, { FC } from "react";
|
|
5
|
+
import { filteredItems, renderValue } from "./shared";
|
|
6
|
+
import { UI_LANG_KEY } from "@c-rex/constants";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
title: string;
|
|
10
|
+
items: {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
|
+
link?: string;
|
|
14
|
+
}[]
|
|
15
|
+
}
|
|
16
|
+
export const InfoCard: FC<Props> = ({ title, items }) => {
|
|
17
|
+
const t = useTranslations();
|
|
18
|
+
const uiLang = getFromCookieString(document.cookie, UI_LANG_KEY)
|
|
19
|
+
const newItems = filteredItems(items)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Card>
|
|
23
|
+
<CardHeader>
|
|
24
|
+
<CardTitle className="text-lg">{title}</CardTitle>
|
|
25
|
+
</CardHeader>
|
|
26
|
+
<CardContent className="space-y-3">
|
|
27
|
+
{newItems.map((item, index) => (
|
|
28
|
+
<div key={index} className="border-b border-blog-divider last:border-0 pb-3 last:pb-0">
|
|
29
|
+
{item.link ? (
|
|
30
|
+
<a href={item.link}>
|
|
31
|
+
<h4 className="text-sm font-medium mb-1">{t(item.label)}</h4>
|
|
32
|
+
</a>
|
|
33
|
+
) : (
|
|
34
|
+
<h4 className="text-sm font-medium mb-1">{t(item.label)}</h4>
|
|
35
|
+
)}
|
|
36
|
+
|
|
37
|
+
<span className="text-xs text-muted-foreground">{renderValue(item, uiLang)}</span>
|
|
38
|
+
</div>
|
|
39
|
+
))}
|
|
40
|
+
</CardContent>
|
|
41
|
+
</Card>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React, { FC } from "react";
|
|
2
|
+
import { articleInfoItemType, DocumentsType } from "@c-rex/types";
|
|
3
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
|
|
4
|
+
import {
|
|
5
|
+
Table,
|
|
6
|
+
TableBody,
|
|
7
|
+
TableCell,
|
|
8
|
+
TableRow,
|
|
9
|
+
} from "@c-rex/ui/table"
|
|
10
|
+
import { useTranslations } from "next-intl";
|
|
11
|
+
import { getFromCookieString } from "@c-rex/utils";
|
|
12
|
+
import { UI_LANG_KEY } from "@c-rex/constants";
|
|
13
|
+
import { filteredItems, renderValue } from "./shared";
|
|
14
|
+
import {
|
|
15
|
+
DropdownMenu,
|
|
16
|
+
DropdownMenuContent,
|
|
17
|
+
DropdownMenuItem,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from "@c-rex/ui/dropdown-menu";
|
|
20
|
+
import { CloudDownload, Eye } from "lucide-react";
|
|
21
|
+
|
|
22
|
+
import { FaFilePdf } from "react-icons/fa6";
|
|
23
|
+
import { AvailableVersionsInterface } from "@c-rex/interfaces";
|
|
24
|
+
import { Flag } from "../flag";
|
|
25
|
+
|
|
26
|
+
type Props = {
|
|
27
|
+
title: string;
|
|
28
|
+
files?: DocumentsType
|
|
29
|
+
items: articleInfoItemType[]
|
|
30
|
+
availableVersions?: AvailableVersionsInterface[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const IconsToFileExtension: Record<string, React.ReactNode> = {
|
|
34
|
+
"application/pdf": <FaFilePdf className="h-6 w-6" />,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const InfoTable: FC<Props> = ({ title, items, files, availableVersions }) => {
|
|
38
|
+
const t = useTranslations();
|
|
39
|
+
const uiLang = getFromCookieString(document.cookie, UI_LANG_KEY)
|
|
40
|
+
const newItems = filteredItems(items)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Card className="p-0 !pt-4">
|
|
44
|
+
<CardHeader>
|
|
45
|
+
<CardTitle className="text-lg">{title}</CardTitle>
|
|
46
|
+
</CardHeader>
|
|
47
|
+
<CardContent className="space-y-3 !p-0">
|
|
48
|
+
<Table>
|
|
49
|
+
<TableBody>
|
|
50
|
+
{newItems.map((item, index) => (
|
|
51
|
+
<TableRow key={index} className="h-12">
|
|
52
|
+
<TableCell className="font-medium w-28 pl-4">
|
|
53
|
+
{item.link ? (
|
|
54
|
+
<a href={item.link} className="underline">
|
|
55
|
+
<h4 className="text-sm font-medium">{t(item.label)}</h4>
|
|
56
|
+
</a>
|
|
57
|
+
) : (
|
|
58
|
+
<h4 className="text-sm font-medium">{t(item.label)}</h4>
|
|
59
|
+
)}
|
|
60
|
+
</TableCell>
|
|
61
|
+
<TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
|
|
62
|
+
{renderValue(item, uiLang)}
|
|
63
|
+
</TableCell>
|
|
64
|
+
</TableRow>
|
|
65
|
+
))}
|
|
66
|
+
|
|
67
|
+
{files && (
|
|
68
|
+
<TableRow className="h-12">
|
|
69
|
+
<TableCell className="font-medium w-28 pl-4">
|
|
70
|
+
<h4 className="text-sm font-medium">{t("files")}</h4>
|
|
71
|
+
</TableCell>
|
|
72
|
+
<TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
|
|
73
|
+
|
|
74
|
+
{Object.keys(files).map((item, index) => {
|
|
75
|
+
if (!files[item]) return null
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<DropdownMenu key={index}>
|
|
79
|
+
<DropdownMenuTrigger className="mx-2">
|
|
80
|
+
{IconsToFileExtension[item]}
|
|
81
|
+
</DropdownMenuTrigger>
|
|
82
|
+
<DropdownMenuContent>
|
|
83
|
+
<DropdownMenuItem>
|
|
84
|
+
<a href={files[item].view} target="_blank" rel="noreferrer" className="flex items-center">
|
|
85
|
+
<Eye className="mr-2 " /> Open
|
|
86
|
+
</a>
|
|
87
|
+
</DropdownMenuItem>
|
|
88
|
+
<DropdownMenuItem>
|
|
89
|
+
<a href={files[item].download} target="_blank" rel="noreferrer" className="flex items-center">
|
|
90
|
+
<CloudDownload className="mr-2 " /> Download
|
|
91
|
+
</a>
|
|
92
|
+
</DropdownMenuItem>
|
|
93
|
+
|
|
94
|
+
</DropdownMenuContent>
|
|
95
|
+
</DropdownMenu>
|
|
96
|
+
)
|
|
97
|
+
})}
|
|
98
|
+
|
|
99
|
+
</TableCell>
|
|
100
|
+
</TableRow>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
{availableVersions && (
|
|
104
|
+
<TableRow className="h-12">
|
|
105
|
+
<TableCell className="font-medium w-28 pl-4">
|
|
106
|
+
<h4 className="text-sm font-medium">{t("availableIn")}</h4>
|
|
107
|
+
</TableCell>
|
|
108
|
+
<TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
|
|
109
|
+
{availableVersions.map((item) => {
|
|
110
|
+
return (
|
|
111
|
+
<span className="w-8 block border" key={item.shortId}>
|
|
112
|
+
<a href={item.link} title={item.lang}>
|
|
113
|
+
<Flag countryCode={item.country} />
|
|
114
|
+
</a>
|
|
115
|
+
</span>
|
|
116
|
+
)
|
|
117
|
+
})}
|
|
118
|
+
</TableCell>
|
|
119
|
+
</TableRow>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
</TableBody>
|
|
123
|
+
</Table>
|
|
124
|
+
</CardContent>
|
|
125
|
+
</Card>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
import { articleInfoItemType } from "@c-rex/types";
|
|
3
|
+
import { formatDateToLocale } from "@c-rex/utils";
|
|
4
|
+
import { Flag } from "../flag";
|
|
5
|
+
|
|
6
|
+
const isDate = (value: string): boolean => {
|
|
7
|
+
return (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value))
|
|
8
|
+
}
|
|
9
|
+
const isLanguage = (label: string): boolean => {
|
|
10
|
+
return label === "language" || label === "languages";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const renderValue = (item: articleInfoItemType, uiLang: string): ReactNode => {
|
|
14
|
+
if (isDate(item.value)) return formatDateToLocale(item.value, uiLang);
|
|
15
|
+
|
|
16
|
+
if (isLanguage(item.label)) {
|
|
17
|
+
const countryCode = item.value.split("-")[1] || item.value;
|
|
18
|
+
return (
|
|
19
|
+
<span className="w-8 block">
|
|
20
|
+
<Flag countryCode={countryCode} />
|
|
21
|
+
</span>
|
|
22
|
+
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return item.value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const filteredItems = (items: articleInfoItemType[]): articleInfoItemType[] => {
|
|
30
|
+
const hasTitle = items.some(item => item.label === "titles");
|
|
31
|
+
return items.filter(item => (hasTitle ? !(item.label == "labels") : true));
|
|
32
|
+
}
|
|
@@ -4,9 +4,7 @@ import React, { ComponentProps, JSX } from "react";
|
|
|
4
4
|
import {
|
|
5
5
|
Sidebar,
|
|
6
6
|
SidebarContent,
|
|
7
|
-
SidebarFooter,
|
|
8
7
|
SidebarGroup,
|
|
9
|
-
SidebarGroupLabel,
|
|
10
8
|
SidebarMenu,
|
|
11
9
|
SidebarMenuButton,
|
|
12
10
|
SidebarMenuItem,
|
|
@@ -15,28 +13,18 @@ import {
|
|
|
15
13
|
SidebarMenuSubItem,
|
|
16
14
|
} from "@c-rex/ui/sidebar";
|
|
17
15
|
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
18
|
-
import {
|
|
19
|
-
import * as Flags from 'country-flag-icons/react/3x2';
|
|
16
|
+
import { TreeOfContent } from "@c-rex/interfaces";
|
|
20
17
|
import { useTranslations } from "next-intl";
|
|
21
18
|
import { cn } from "@c-rex/utils";
|
|
22
19
|
|
|
23
20
|
interface SidebarProps extends ComponentProps<typeof Sidebar> {
|
|
24
21
|
data: TreeOfContent[];
|
|
25
22
|
loading?: boolean;
|
|
26
|
-
availableVersions: SidebarAvailableVersionsInterface[]
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
export function
|
|
25
|
+
export function LeftSidebar({ data, loading, className, lang, ...props }: SidebarProps) {
|
|
30
26
|
const t = useTranslations();
|
|
31
27
|
|
|
32
|
-
const getFlagIcon = (countryCode: string): JSX.Element | null => {
|
|
33
|
-
type CountryCode = keyof typeof Flags;
|
|
34
|
-
const FlagComponent = Flags[countryCode as CountryCode];
|
|
35
|
-
if (!FlagComponent) return null;
|
|
36
|
-
|
|
37
|
-
return <FlagComponent />;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
28
|
const tableOfContentGroup = (): JSX.Element | null => {
|
|
41
29
|
if (loading) {
|
|
42
30
|
return (
|
|
@@ -94,39 +82,11 @@ export function AppSidebar({ data, availableVersions, loading, className, lang,
|
|
|
94
82
|
);
|
|
95
83
|
}
|
|
96
84
|
|
|
97
|
-
const availableVersionsGroup = (): JSX.Element | null => {
|
|
98
|
-
|
|
99
|
-
if (availableVersions.length == 0) return null;
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<>
|
|
103
|
-
<SidebarGroupLabel>{t("availableIn")}:</SidebarGroupLabel>
|
|
104
|
-
<SidebarMenu>
|
|
105
|
-
{availableVersions.map((item) => {
|
|
106
|
-
return (
|
|
107
|
-
<SidebarMenuItem key={item.shortId}>
|
|
108
|
-
<SidebarMenuButton asChild isActive={item.active}>
|
|
109
|
-
<a href={item.link} title={item.lang}>
|
|
110
|
-
{getFlagIcon(item.country)} {item.lang}
|
|
111
|
-
</a>
|
|
112
|
-
</SidebarMenuButton>
|
|
113
|
-
</SidebarMenuItem>
|
|
114
|
-
)
|
|
115
|
-
})}
|
|
116
|
-
</SidebarMenu>
|
|
117
|
-
</>
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
85
|
return (
|
|
123
|
-
<Sidebar className={cn(className
|
|
86
|
+
<Sidebar className={cn(className)} {...props}>
|
|
124
87
|
<SidebarContent>
|
|
125
88
|
{tableOfContentGroup()}
|
|
126
89
|
</SidebarContent>
|
|
127
|
-
<SidebarFooter>
|
|
128
|
-
{availableVersionsGroup()}
|
|
129
|
-
</SidebarFooter>
|
|
130
90
|
</Sidebar>
|
|
131
91
|
);
|
|
132
92
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { startTransition } from "react";
|
|
4
4
|
import { SharedLanguageSwitch } from "./shared";
|
|
5
5
|
import { getFromCookieString, setCookie } from "@c-rex/utils";
|
|
6
|
-
import { CONTENT_LANG_KEY } from "@c-rex/constants";
|
|
6
|
+
import { BLOG_TYPE_AND_LINK, CONTENT_LANG_KEY, DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
|
|
7
7
|
import { useQueryState } from "nuqs"
|
|
8
8
|
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
9
9
|
import { useTranslations } from "next-intl";
|
|
@@ -12,8 +12,22 @@ import { toast } from "sonner"
|
|
|
12
12
|
export const ContentLanguageSwitch = () => {
|
|
13
13
|
const t = useTranslations();
|
|
14
14
|
const contentLang = getFromCookieString(document.cookie, CONTENT_LANG_KEY)
|
|
15
|
-
const { availableLanguagesAndCountries, setContentLang, availableVersions } = useAppConfig()
|
|
15
|
+
const { availableLanguagesAndCountries: aux, setContentLang, availableVersions } = useAppConfig()
|
|
16
16
|
|
|
17
|
+
const availableLanguagesAndCountries = () => {
|
|
18
|
+
let result = aux.map(item => ({ ...item, link: "#" }))
|
|
19
|
+
if (availableVersions == null) return result
|
|
20
|
+
|
|
21
|
+
result = aux.map(item => {
|
|
22
|
+
const availableVersion = availableVersions.find(version => version.lang === item.value)
|
|
23
|
+
return {
|
|
24
|
+
...item,
|
|
25
|
+
link: availableVersion?.link ?? "#"
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
17
31
|
const [queryLanguage, setContentLanguage] = useQueryState('language', {
|
|
18
32
|
history: 'push',
|
|
19
33
|
shallow: false,
|
|
@@ -23,10 +37,19 @@ export const ContentLanguageSwitch = () => {
|
|
|
23
37
|
startTransition(() => {
|
|
24
38
|
setCookie(CONTENT_LANG_KEY, locale)
|
|
25
39
|
setContentLang(locale)
|
|
40
|
+
|
|
26
41
|
if (queryLanguage != null) {
|
|
27
42
|
setContentLanguage(locale)
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
const currentPath = window.location.pathname;
|
|
46
|
+
const isTopicOrBlogOrDocument = currentPath.includes(TOPICS_TYPE_AND_LINK) || currentPath.includes(BLOG_TYPE_AND_LINK) || currentPath.includes(DOCUMENTS_TYPE_AND_LINK)
|
|
47
|
+
|
|
48
|
+
if (!isTopicOrBlogOrDocument) {
|
|
49
|
+
window.location.reload();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
30
53
|
if (availableVersions !== null) {
|
|
31
54
|
const filteredList = availableVersions.filter((item) => item.lang === locale)
|
|
32
55
|
|
|
@@ -48,7 +71,7 @@ export const ContentLanguageSwitch = () => {
|
|
|
48
71
|
|
|
49
72
|
return (
|
|
50
73
|
<SharedLanguageSwitch
|
|
51
|
-
availableLanguagesAndCountries={availableLanguagesAndCountries}
|
|
74
|
+
availableLanguagesAndCountries={availableLanguagesAndCountries()}
|
|
52
75
|
changeLanguage={changeContentLanguage}
|
|
53
76
|
selected={contentLang}
|
|
54
77
|
/>
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import React, { FC
|
|
2
|
-
import * as Flags from 'country-flag-icons/react/3x2';
|
|
1
|
+
import React, { FC } from "react";
|
|
3
2
|
import { LanguageAndCountries } from "@c-rex/interfaces";
|
|
4
3
|
import { DropdownMenuRadioGroup, DropdownMenuRadioItem } from "@c-rex/ui/dropdown-menu";
|
|
4
|
+
import { Flag } from "../../flag";
|
|
5
5
|
|
|
6
6
|
interface SharedLanguageSwitchProps {
|
|
7
|
-
availableLanguagesAndCountries: LanguageAndCountries[];
|
|
7
|
+
availableLanguagesAndCountries: (LanguageAndCountries & { link?: string })[];
|
|
8
8
|
selected: string;
|
|
9
9
|
changeLanguage: (locale: string) => void;
|
|
10
10
|
}
|
|
11
11
|
export const SharedLanguageSwitch: FC<SharedLanguageSwitchProps> = ({ availableLanguagesAndCountries, changeLanguage, selected }) => {
|
|
12
12
|
|
|
13
|
-
const getFlagIcon = (countryCode: string): JSX.Element | null => {
|
|
14
|
-
type CountryCode = keyof typeof Flags;
|
|
15
|
-
const FlagComponent = Flags[countryCode as CountryCode];
|
|
16
|
-
if (!FlagComponent) return null;
|
|
17
|
-
|
|
18
|
-
return <FlagComponent />;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
13
|
return (
|
|
22
|
-
<DropdownMenuRadioGroup value={selected}
|
|
14
|
+
<DropdownMenuRadioGroup value={selected}>
|
|
23
15
|
{availableLanguagesAndCountries.map(item => {
|
|
24
16
|
return (
|
|
25
|
-
<DropdownMenuRadioItem
|
|
26
|
-
{
|
|
17
|
+
<DropdownMenuRadioItem
|
|
18
|
+
key={item.value}
|
|
19
|
+
value={item.value}
|
|
20
|
+
link={item?.link}
|
|
21
|
+
onClick={(e) => {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
changeLanguage(item.value)
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<Flag countryCode={item.country} />
|
|
27
27
|
<span>{item.lang.toUpperCase()}</span>
|
|
28
28
|
</DropdownMenuRadioItem>
|
|
29
29
|
)
|