@c-rex/components 0.1.38 → 0.1.39
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 +73 -73
- package/package.json +250 -218
- package/src/article/article-action-bar.tsx +110 -110
- package/src/article/article-content.tsx +18 -46
- package/src/autocomplete.tsx +201 -201
- package/src/breadcrumb.tsx +124 -124
- package/src/carousel/carousel.tsx +353 -353
- package/src/check-article-lang.tsx +47 -47
- package/src/directoryNodes/directory-tree-context.tsx +388 -0
- package/src/directoryNodes/tree-of-content.tsx +68 -67
- package/src/documents/result-list.tsx +124 -127
- package/src/favorites/bookmark-button.tsx +97 -94
- package/src/favorites/favorite-button.tsx +137 -120
- package/src/footer/footer-shell.tsx +52 -0
- package/src/footer/footer.tsx +7 -0
- package/src/footer/legal-links-block.tsx +25 -0
- package/src/footer/organization-contact-block.tsx +94 -0
- package/src/footer/social-links-block.tsx +38 -0
- package/src/footer/types.ts +10 -0
- package/src/footer/vcard-footer.tsx +72 -0
- package/src/generated/client-components.tsx +1366 -1350
- package/src/generated/create-client-request.tsx +116 -113
- package/src/generated/create-server-request.tsx +70 -61
- package/src/generated/create-suggestions-request.tsx +55 -55
- package/src/generated/server-components.tsx +1056 -1056
- package/src/generated/suggestions.tsx +302 -299
- package/src/icons/file-icon.tsx +8 -8
- package/src/icons/flag-icon.tsx +15 -15
- package/src/icons/loading.tsx +11 -11
- package/src/icons/social-icon.tsx +24 -0
- package/src/info/info-card.tsx +43 -0
- package/src/info/{info-table.tsx → information-unit-metadata-grid.tsx} +157 -168
- package/src/info/shared.tsx +49 -25
- package/src/navbar/language-switcher/content-language-switch.tsx +92 -92
- package/src/navbar/language-switcher/shared.tsx +33 -33
- package/src/navbar/language-switcher/ui-language-switch.tsx +37 -37
- package/src/navbar/navbar.tsx +157 -152
- package/src/navbar/settings.tsx +62 -62
- package/src/navbar/sign-in-out-btns.tsx +35 -35
- package/src/navbar/user-menu.tsx +60 -60
- package/src/page-wrapper.tsx +54 -31
- package/src/render-article.module.css +155 -0
- package/src/render-article.tsx +75 -68
- package/src/renditions/file-download.tsx +83 -83
- package/src/renditions/html.tsx +64 -64
- package/src/renditions/image/container.tsx +54 -54
- package/src/renditions/image/rendition.tsx +55 -55
- package/src/restriction-menu/restriction-menu-container.tsx +117 -53
- package/src/restriction-menu/restriction-menu-item.tsx +155 -147
- package/src/restriction-menu/restriction-menu.tsx +341 -156
- package/src/results/dialog-filter.tsx +166 -166
- package/src/results/empty.tsx +15 -15
- package/src/results/filter-navbar.tsx +294 -261
- package/src/results/filter-sidebar/__tests__/utils.test.ts +129 -0
- package/src/results/filter-sidebar/index.tsx +270 -126
- package/src/results/filter-sidebar/utils.ts +196 -164
- package/src/results/generic/table-result-list.tsx +97 -99
- package/src/results/{table-with-images.tsx → information-unit-search-results-card-list.tsx} +125 -127
- package/src/results/{cards.tsx → information-unit-search-results-cards.tsx} +99 -99
- package/src/results/{table.tsx → information-unit-search-results-table.tsx} +104 -104
- package/src/results/pagination.tsx +81 -81
- package/src/results/summary.ts +30 -0
- package/src/results/utils.ts +54 -54
- package/src/search-input.tsx +70 -70
- package/src/share-button.tsx +49 -49
- package/src/stores/favorites-store.ts +88 -88
- package/src/stores/highlight-store.ts +15 -15
- package/src/stores/language-store.ts +14 -14
- package/src/stores/restriction-store.ts +11 -11
- package/src/stores/search-settings-store.ts +68 -64
- package/src/info/set-available-versions.tsx +0 -19
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { FC } from "react";
|
|
2
|
-
import { ImageOff } from "lucide-react";
|
|
3
|
-
import { CommonItemsModel, RenditionModel } from "@c-rex/interfaces";
|
|
4
|
-
import { cn } from "@c-rex/utils";
|
|
5
|
-
interface ImageRenditionProps {
|
|
6
|
-
items: CommonItemsModel[];
|
|
7
|
-
emptyImageStyle?: string;
|
|
8
|
-
imageStyle?: string;
|
|
9
|
-
formats?: string[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ImageRendition: FC<ImageRenditionProps> = ({
|
|
13
|
-
items,
|
|
14
|
-
formats = ["image/svg+xml", "image/gif", "image/png", "image/jpg"],
|
|
15
|
-
emptyImageStyle,
|
|
16
|
-
imageStyle }) => {
|
|
17
|
-
if (!items || items.length === 0) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
18
|
-
|
|
19
|
-
const item = items[0];
|
|
20
|
-
if (!item || item == undefined) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
21
|
-
|
|
22
|
-
const wanted = formats.map((f) => f.toLowerCase());
|
|
23
|
-
let rendition = null
|
|
24
|
-
const renditions = items[0]?.renditions as RenditionModel[] || [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
for (const fmt of wanted) {
|
|
28
|
-
|
|
29
|
-
const found = renditions.find((r) => r.format?.toLowerCase() === fmt);
|
|
30
|
-
|
|
31
|
-
if (found) {
|
|
32
|
-
rendition = found;
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (rendition == null) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
38
|
-
|
|
39
|
-
const src =
|
|
40
|
-
rendition.links?.find((l) => l.rel === "view")?.href ??
|
|
41
|
-
rendition.links?.find((l) => l.rel === "download")?.href ??
|
|
42
|
-
rendition.links?.find((l) => l.rel === "resource")?.href ??
|
|
43
|
-
rendition.source ??
|
|
44
|
-
"";
|
|
45
|
-
|
|
46
|
-
if (!src) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
47
|
-
|
|
48
|
-
const alt =
|
|
49
|
-
item.labels?.[0]?.value ??
|
|
50
|
-
item.titles?.[0]?.value ??
|
|
51
|
-
"Document image";
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<img src={src} alt={alt} loading="eager" className={cn(imageStyle, "w-full")} />
|
|
55
|
-
);
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { ImageOff } from "lucide-react";
|
|
3
|
+
import { CommonItemsModel, RenditionModel } from "@c-rex/interfaces";
|
|
4
|
+
import { cn } from "@c-rex/utils";
|
|
5
|
+
interface ImageRenditionProps {
|
|
6
|
+
items: CommonItemsModel[];
|
|
7
|
+
emptyImageStyle?: string;
|
|
8
|
+
imageStyle?: string;
|
|
9
|
+
formats?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const ImageRendition: FC<ImageRenditionProps> = ({
|
|
13
|
+
items,
|
|
14
|
+
formats = ["image/svg+xml", "image/gif", "image/png", "image/jpg"],
|
|
15
|
+
emptyImageStyle,
|
|
16
|
+
imageStyle }) => {
|
|
17
|
+
if (!items || items.length === 0) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
18
|
+
|
|
19
|
+
const item = items[0];
|
|
20
|
+
if (!item || item == undefined) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
21
|
+
|
|
22
|
+
const wanted = formats.map((f) => f.toLowerCase());
|
|
23
|
+
let rendition = null
|
|
24
|
+
const renditions = items[0]?.renditions as RenditionModel[] || [];
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
for (const fmt of wanted) {
|
|
28
|
+
|
|
29
|
+
const found = renditions.find((r) => r.format?.toLowerCase() === fmt);
|
|
30
|
+
|
|
31
|
+
if (found) {
|
|
32
|
+
rendition = found;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (rendition == null) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
38
|
+
|
|
39
|
+
const src =
|
|
40
|
+
rendition.links?.find((l) => l.rel === "view")?.href ??
|
|
41
|
+
rendition.links?.find((l) => l.rel === "download")?.href ??
|
|
42
|
+
rendition.links?.find((l) => l.rel === "resource")?.href ??
|
|
43
|
+
rendition.source ??
|
|
44
|
+
"";
|
|
45
|
+
|
|
46
|
+
if (!src) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
47
|
+
|
|
48
|
+
const alt =
|
|
49
|
+
item.labels?.[0]?.value ??
|
|
50
|
+
item.titles?.[0]?.value ??
|
|
51
|
+
"Document image";
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<img src={src} alt={alt} loading="eager" className={cn(imageStyle, "w-full")} />
|
|
55
|
+
);
|
|
56
56
|
}
|
|
@@ -1,53 +1,117 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
7
|
-
import { RestrictionMenu } from "./restriction-menu";
|
|
8
|
-
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
queryParams
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { DomainEntityModel } from "@c-rex/interfaces";
|
|
4
|
+
import { FC, ReactNode, useMemo, useState } from "react"
|
|
5
|
+
import * as ComponentOptions from "../generated/client-components";
|
|
6
|
+
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
7
|
+
import { RestrictionMenu } from "./restriction-menu";
|
|
8
|
+
|
|
9
|
+
type GenericRequestData = {
|
|
10
|
+
items?: DomainEntityModel[];
|
|
11
|
+
pageInfo?: {
|
|
12
|
+
totalItemCount?: number;
|
|
13
|
+
};
|
|
14
|
+
} | null | undefined;
|
|
15
|
+
type GenericQueryParams = Record<string, unknown> & { Restrict?: string[]; PageSize?: number; Fields?: string[] };
|
|
16
|
+
type GenericRequestProps = {
|
|
17
|
+
queryParams?: GenericQueryParams;
|
|
18
|
+
children: (props: { data?: GenericRequestData; isLoading?: boolean }) => ReactNode;
|
|
19
|
+
};
|
|
20
|
+
type RestrictionMenuFetchMode = "all" | "deferred";
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
restrictField: string
|
|
24
|
+
navigationMenuListClassName?: string
|
|
25
|
+
itemsToRender?: number
|
|
26
|
+
requestType: keyof typeof ComponentOptions
|
|
27
|
+
onlyUsedEntries?: boolean
|
|
28
|
+
enableHierarchy?: boolean
|
|
29
|
+
fetchMode?: RestrictionMenuFetchMode
|
|
30
|
+
showAllWhenEmpty?: boolean
|
|
31
|
+
queryParams?: GenericQueryParams
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const RestrictionMenuContainer: FC<Props> = ({
|
|
35
|
+
queryParams,
|
|
36
|
+
restrictField,
|
|
37
|
+
itemsToRender = 7,
|
|
38
|
+
requestType,
|
|
39
|
+
onlyUsedEntries = true,
|
|
40
|
+
enableHierarchy = false,
|
|
41
|
+
fetchMode = "deferred",
|
|
42
|
+
showAllWhenEmpty = true,
|
|
43
|
+
navigationMenuListClassName = "items-center justify-between flex-row",
|
|
44
|
+
}) => {
|
|
45
|
+
const [loadAll, setLoadAll] = useState(false);
|
|
46
|
+
const RequestComponent = ComponentOptions[requestType] as unknown as FC<GenericRequestProps>;
|
|
47
|
+
const queryRestrict = queryParams?.Restrict || [];
|
|
48
|
+
const restrict = onlyUsedEntries ? ["hasInformationUnits=true", ...queryRestrict] : queryRestrict;
|
|
49
|
+
const explicitPageSize =
|
|
50
|
+
Number.isFinite(Number(queryParams?.PageSize)) && Number(queryParams?.PageSize) > 0
|
|
51
|
+
? Number(queryParams?.PageSize)
|
|
52
|
+
: undefined;
|
|
53
|
+
const resolvedPageSize = useMemo(() => {
|
|
54
|
+
if (explicitPageSize) return explicitPageSize;
|
|
55
|
+
// TODO(UI/Gabriel): `deferred` currently behaves like "initial subset + load all on first More click".
|
|
56
|
+
// Keep this behavior for now; planned follow-up is incremental loading from inside the opened More dropdown.
|
|
57
|
+
if (fetchMode === "deferred" && !loadAll) return Math.max(itemsToRender, 1);
|
|
58
|
+
return 100;
|
|
59
|
+
}, [explicitPageSize, fetchMode, itemsToRender, loadAll]);
|
|
60
|
+
const requestedFields = Array.isArray(queryParams?.Fields) ? queryParams.Fields : undefined;
|
|
61
|
+
const resolvedFields = useMemo(() => {
|
|
62
|
+
const baseFields = requestedFields && requestedFields.length > 0 ? requestedFields : ["labels"];
|
|
63
|
+
if (!enableHierarchy) return baseFields;
|
|
64
|
+
const withParents = new Set(baseFields);
|
|
65
|
+
withParents.add("parents");
|
|
66
|
+
return Array.from(withParents);
|
|
67
|
+
}, [enableHierarchy, requestedFields]);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<RequestComponent
|
|
71
|
+
queryParams={{
|
|
72
|
+
...queryParams,
|
|
73
|
+
Fields: resolvedFields,
|
|
74
|
+
Links: true,
|
|
75
|
+
Sort: ["labels.value"],
|
|
76
|
+
PageSize: resolvedPageSize,
|
|
77
|
+
Restrict: restrict,
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
{({ data, isLoading }) => {
|
|
81
|
+
|
|
82
|
+
if (isLoading) return (
|
|
83
|
+
<div className="flex justify-between">
|
|
84
|
+
<Skeleton className="w-12 h-9 rounded-full" />
|
|
85
|
+
{Array(itemsToRender).fill(0).map((_, index) => (
|
|
86
|
+
<Skeleton key={`skeleton-${index}`} className="w-28 h-9 rounded-full" />
|
|
87
|
+
))}
|
|
88
|
+
<Skeleton className="w-20 h-9 rounded-full" />
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if (!data) return null;
|
|
93
|
+
|
|
94
|
+
const itemCount = data.items?.length || 0;
|
|
95
|
+
const totalItemCount = data.pageInfo?.totalItemCount;
|
|
96
|
+
const hasMoreFromServer = typeof totalItemCount === "number" ? totalItemCount > itemCount : false;
|
|
97
|
+
const hasMoreItems = hasMoreFromServer || (explicitPageSize ? false : (fetchMode === "deferred" && !loadAll && itemCount >= resolvedPageSize));
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<RestrictionMenu
|
|
101
|
+
restrictField={restrictField}
|
|
102
|
+
items={data.items || []}
|
|
103
|
+
enableHierarchy={enableHierarchy}
|
|
104
|
+
hasMoreItems={hasMoreItems}
|
|
105
|
+
showAllWhenEmpty={showAllWhenEmpty}
|
|
106
|
+
onRequestMore={() => {
|
|
107
|
+
if (fetchMode === "deferred" && !explicitPageSize) {
|
|
108
|
+
setLoadAll(true);
|
|
109
|
+
}
|
|
110
|
+
}}
|
|
111
|
+
navigationMenuListClassName={navigationMenuListClassName}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}}
|
|
115
|
+
</RequestComponent>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
@@ -1,147 +1,155 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { FC } from "react";
|
|
4
|
-
import { Button } from "@c-rex/ui/button";
|
|
5
|
-
import { NavigationMenuItem } from "@c-rex/ui/navigation-menu";
|
|
6
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
|
|
7
|
-
import { useQueryState } from "nuqs";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
shortId?: string;
|
|
12
|
-
label: string;
|
|
13
|
-
restrictField?: string;
|
|
14
|
-
removeRestrictParam?: boolean;
|
|
15
|
-
selected?: boolean;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FC } from "react";
|
|
4
|
+
import { Button } from "@c-rex/ui/button";
|
|
5
|
+
import { NavigationMenuItem } from "@c-rex/ui/navigation-menu";
|
|
6
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
|
|
7
|
+
import { useQueryState } from "nuqs";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
shortId?: string;
|
|
12
|
+
label: string;
|
|
13
|
+
restrictField?: string;
|
|
14
|
+
removeRestrictParam?: boolean;
|
|
15
|
+
selected?: boolean;
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const RestrictionNavigationItem: FC<Props> = ({
|
|
20
|
+
shortId,
|
|
21
|
+
label,
|
|
22
|
+
restrictField,
|
|
23
|
+
removeRestrictParam = false,
|
|
24
|
+
selected = false,
|
|
25
|
+
}) => {
|
|
26
|
+
const [restrict, setRestrict] = useQueryState("restrict", {
|
|
27
|
+
shallow: false,
|
|
28
|
+
history: "push",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const { restrictionValue, shouldRemoveRestrictParam } = getRestrictionValue({
|
|
32
|
+
shortId,
|
|
33
|
+
restrictField,
|
|
34
|
+
removeRestrictParam,
|
|
35
|
+
selected,
|
|
36
|
+
currentRestrict: restrict,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const labelStyle = {
|
|
40
|
+
display: 'inline-block',
|
|
41
|
+
maxWidth: 100,
|
|
42
|
+
overflow: 'hidden',
|
|
43
|
+
textOverflow: 'ellipsis',
|
|
44
|
+
whiteSpace: 'nowrap',
|
|
45
|
+
verticalAlign: 'middle',
|
|
46
|
+
} as React.CSSProperties;
|
|
47
|
+
|
|
48
|
+
const needsTooltip = label.length > 12;
|
|
49
|
+
|
|
50
|
+
const labelNode = needsTooltip ? (
|
|
51
|
+
<TooltipProvider delayDuration={300}>
|
|
52
|
+
<Tooltip>
|
|
53
|
+
<TooltipTrigger asChild>
|
|
54
|
+
<span style={labelStyle}>{label}</span>
|
|
55
|
+
</TooltipTrigger>
|
|
56
|
+
<TooltipContent>{label}</TooltipContent>
|
|
57
|
+
</Tooltip>
|
|
58
|
+
</TooltipProvider>
|
|
59
|
+
) : (
|
|
60
|
+
<span style={labelStyle}>{label}</span>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<NavigationMenuItem key={shortId} asChild>
|
|
65
|
+
<Button
|
|
66
|
+
variant={selected ? "default" : "outline"}
|
|
67
|
+
rounded="full"
|
|
68
|
+
onClick={() => shouldRemoveRestrictParam ? setRestrict(null) : setRestrict(restrictionValue)}
|
|
69
|
+
className="cursor-pointer"
|
|
70
|
+
>
|
|
71
|
+
{labelNode}
|
|
72
|
+
</Button>
|
|
73
|
+
</NavigationMenuItem>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const RestrictionDropdownItem: FC<Props> = ({
|
|
78
|
+
shortId,
|
|
79
|
+
label,
|
|
80
|
+
restrictField,
|
|
81
|
+
selected = false,
|
|
82
|
+
onClick,
|
|
83
|
+
}) => {
|
|
84
|
+
const [restrict, setRestrict] = useQueryState("restrict", {
|
|
85
|
+
shallow: false,
|
|
86
|
+
history: "push",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const { restrictionValue, shouldRemoveRestrictParam } = getRestrictionValue({
|
|
90
|
+
shortId,
|
|
91
|
+
restrictField,
|
|
92
|
+
selected,
|
|
93
|
+
currentRestrict: restrict,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Button
|
|
98
|
+
variant={selected ? "default" : "ghost"}
|
|
99
|
+
onClick={() => {
|
|
100
|
+
if (onClick) {
|
|
101
|
+
onClick();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
shouldRemoveRestrictParam ? setRestrict(null) : setRestrict(restrictionValue)
|
|
105
|
+
}}
|
|
106
|
+
rounded="full"
|
|
107
|
+
className="text-left text-wrap !h-auto min-h-10 w-full !justify-start cursor-pointer"
|
|
108
|
+
>
|
|
109
|
+
{label}
|
|
110
|
+
</Button>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function getRestrictionValue({
|
|
115
|
+
shortId,
|
|
116
|
+
restrictField,
|
|
117
|
+
removeRestrictParam = false,
|
|
118
|
+
selected = false,
|
|
119
|
+
|
|
120
|
+
currentRestrict,
|
|
121
|
+
}: {
|
|
122
|
+
shortId?: string;
|
|
123
|
+
restrictField?: string;
|
|
124
|
+
removeRestrictParam?: boolean;
|
|
125
|
+
selected?: boolean;
|
|
126
|
+
currentRestrict: string | null;
|
|
127
|
+
}): { restrictionValue: string | null; shouldRemoveRestrictParam: boolean } {
|
|
128
|
+
let restrictParam = "";
|
|
129
|
+
let shouldRemoveRestrictParam = removeRestrictParam;
|
|
130
|
+
|
|
131
|
+
if (currentRestrict) {
|
|
132
|
+
if (selected) {
|
|
133
|
+
const restrictionsLength = currentRestrict.split(",").length;
|
|
134
|
+
//if there is only one restriction, we can remove the whole restrict param
|
|
135
|
+
if (restrictionsLength === 1) {
|
|
136
|
+
shouldRemoveRestrictParam = true;
|
|
137
|
+
} else {
|
|
138
|
+
restrictParam = currentRestrict.replace(`${shortId}`, "").replace(",,", ",").replace(/(^,)|(,$)/g, "");
|
|
139
|
+
|
|
140
|
+
// Remove the restrict param if nothing remains after '='
|
|
141
|
+
if (/^[^=]+=$/.test(restrictParam)) {
|
|
142
|
+
shouldRemoveRestrictParam = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
restrictParam = `${currentRestrict},${shortId}`;
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
restrictParam = `${restrictField}=${shortId}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const restrictionValue = shouldRemoveRestrictParam ? null : restrictParam
|
|
153
|
+
|
|
154
|
+
return { restrictionValue, shouldRemoveRestrictParam };
|
|
155
|
+
}
|