@c-rex/components 0.1.37 → 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.
Files changed (146) hide show
  1. package/README.md +73 -73
  2. package/package.json +250 -235
  3. package/src/article/article-action-bar.tsx +110 -110
  4. package/src/article/article-content.tsx +18 -46
  5. package/src/autocomplete.tsx +201 -201
  6. package/src/breadcrumb.tsx +124 -124
  7. package/src/carousel/carousel.tsx +353 -352
  8. package/src/check-article-lang.tsx +47 -43
  9. package/src/directoryNodes/directory-tree-context.tsx +388 -0
  10. package/src/directoryNodes/tree-of-content.tsx +68 -67
  11. package/src/documents/result-list.tsx +124 -127
  12. package/src/favorites/bookmark-button.tsx +97 -79
  13. package/src/favorites/favorite-button.tsx +137 -74
  14. package/src/footer/footer-shell.tsx +52 -0
  15. package/src/footer/footer.tsx +7 -0
  16. package/src/footer/legal-links-block.tsx +25 -0
  17. package/src/footer/organization-contact-block.tsx +94 -0
  18. package/src/footer/social-links-block.tsx +38 -0
  19. package/src/footer/types.ts +10 -0
  20. package/src/footer/vcard-footer.tsx +72 -0
  21. package/src/generated/client-components.tsx +1366 -1350
  22. package/src/generated/create-client-request.tsx +116 -113
  23. package/src/generated/create-server-request.tsx +70 -61
  24. package/src/generated/create-suggestions-request.tsx +55 -55
  25. package/src/generated/server-components.tsx +1056 -1056
  26. package/src/generated/suggestions.tsx +302 -299
  27. package/src/icons/file-icon.tsx +8 -8
  28. package/src/icons/flag-icon.tsx +15 -15
  29. package/src/icons/loading.tsx +11 -11
  30. package/src/icons/social-icon.tsx +24 -0
  31. package/src/info/info-card.tsx +43 -0
  32. package/src/info/{info-table.tsx → information-unit-metadata-grid.tsx} +157 -146
  33. package/src/info/shared.tsx +49 -25
  34. package/src/navbar/language-switcher/content-language-switch.tsx +92 -92
  35. package/src/navbar/language-switcher/shared.tsx +33 -33
  36. package/src/navbar/language-switcher/ui-language-switch.tsx +37 -38
  37. package/src/navbar/navbar.tsx +157 -148
  38. package/src/navbar/settings.tsx +62 -62
  39. package/src/navbar/sign-in-out-btns.tsx +35 -35
  40. package/src/navbar/user-menu.tsx +60 -60
  41. package/src/page-wrapper.tsx +54 -31
  42. package/src/render-article.module.css +155 -0
  43. package/src/render-article.tsx +75 -68
  44. package/src/renditions/file-download.tsx +83 -83
  45. package/src/renditions/html.tsx +64 -64
  46. package/src/renditions/image/container.tsx +54 -54
  47. package/src/renditions/image/rendition.tsx +55 -55
  48. package/src/restriction-menu/restriction-menu-container.tsx +117 -53
  49. package/src/restriction-menu/restriction-menu-item.tsx +155 -147
  50. package/src/restriction-menu/restriction-menu.tsx +341 -157
  51. package/src/results/dialog-filter.tsx +166 -166
  52. package/src/results/empty.tsx +15 -15
  53. package/src/results/filter-navbar.tsx +294 -261
  54. package/src/results/filter-sidebar/__tests__/utils.test.ts +129 -0
  55. package/src/results/filter-sidebar/index.tsx +270 -126
  56. package/src/results/filter-sidebar/utils.ts +196 -164
  57. package/src/results/generic/table-result-list.tsx +97 -99
  58. package/src/results/{table-with-images.tsx → information-unit-search-results-card-list.tsx} +125 -127
  59. package/src/results/{cards.tsx → information-unit-search-results-cards.tsx} +99 -99
  60. package/src/results/{table.tsx → information-unit-search-results-table.tsx} +104 -104
  61. package/src/results/pagination.tsx +81 -81
  62. package/src/results/summary.ts +30 -0
  63. package/src/results/utils.ts +54 -47
  64. package/src/search-input.tsx +70 -70
  65. package/src/share-button.tsx +49 -49
  66. package/src/stores/favorites-store.ts +88 -88
  67. package/src/stores/highlight-store.ts +15 -15
  68. package/src/stores/language-store.ts +14 -43
  69. package/src/stores/restriction-store.ts +11 -11
  70. package/src/stores/search-settings-store.ts +68 -64
  71. package/src/article/article-action-bar.analysis.md +0 -15
  72. package/src/article/article-action-bar.stories.tsx +0 -15
  73. package/src/article/article-content.analysis.md +0 -15
  74. package/src/article/article-content.stories.tsx +0 -21
  75. package/src/autocomplete.analysis.md +0 -17
  76. package/src/breadcrumb.analysis.md +0 -15
  77. package/src/carousel/carousel.analysis.md +0 -17
  78. package/src/check-article-lang.analysis.md +0 -15
  79. package/src/directoryNodes/tree-of-content.analysis.md +0 -14
  80. package/src/directoryNodes/tree-of-content.stories.tsx +0 -22
  81. package/src/documents/result-list.analysis.md +0 -14
  82. package/src/documents/result-list.stories.tsx +0 -19
  83. package/src/favorites/bookmark-button.analysis.md +0 -17
  84. package/src/favorites/bookmark-button.stories.tsx +0 -19
  85. package/src/favorites/favorite-button.analysis.md +0 -18
  86. package/src/favorites/favorite-button.stories.tsx +0 -22
  87. package/src/icons/file-icon.analysis.md +0 -14
  88. package/src/icons/file-icon.stories.tsx +0 -19
  89. package/src/icons/flag-icon.analysis.md +0 -14
  90. package/src/icons/flag-icon.stories.tsx +0 -25
  91. package/src/icons/loading.analysis.md +0 -14
  92. package/src/icons/loading.stories.tsx +0 -21
  93. package/src/info/info-table.analysis.md +0 -15
  94. package/src/info/shared.analysis.md +0 -14
  95. package/src/info/stories/info-table.stories.tsx +0 -31
  96. package/src/info/stories/shared.stories.tsx +0 -24
  97. package/src/navbar/language-switcher/content-language-switch.analysis.md +0 -15
  98. package/src/navbar/language-switcher/shared.analysis.md +0 -14
  99. package/src/navbar/language-switcher/ui-language-switch.analysis.md +0 -15
  100. package/src/navbar/navbar.analysis.md +0 -14
  101. package/src/navbar/settings.analysis.md +0 -14
  102. package/src/navbar/sign-in-out-btns.analysis.md +0 -14
  103. package/src/navbar/stories/navbar.stories.tsx +0 -31
  104. package/src/navbar/stories/settings.stories.tsx +0 -15
  105. package/src/navbar/stories/sign-in-out-btns.stories.tsx +0 -15
  106. package/src/navbar/stories/user-menu.stories.tsx +0 -20
  107. package/src/navbar/user-menu.analysis.md +0 -14
  108. package/src/page-wrapper.analysis.md +0 -14
  109. package/src/render-article.analysis.md +0 -15
  110. package/src/renditions/file-download.analysis.md +0 -14
  111. package/src/renditions/file-download.stories.tsx +0 -19
  112. package/src/renditions/html.analysis.md +0 -17
  113. package/src/renditions/html.stories.tsx +0 -19
  114. package/src/renditions/image/container.analysis.md +0 -15
  115. package/src/renditions/image/container.stories.tsx +0 -19
  116. package/src/renditions/image/rendition.analysis.md +0 -14
  117. package/src/renditions/image/rendition.stories.tsx +0 -19
  118. package/src/restriction-menu/restriction-menu-container.analysis.md +0 -14
  119. package/src/restriction-menu/restriction-menu-item.analysis.md +0 -14
  120. package/src/restriction-menu/restriction-menu.analysis.md +0 -17
  121. package/src/results/analysis/cards.analysis.md +0 -14
  122. package/src/results/analysis/dialog-filter.analysis.md +0 -17
  123. package/src/results/analysis/empty.analysis.md +0 -14
  124. package/src/results/analysis/filter-navbar.analysis.md +0 -16
  125. package/src/results/analysis/pagination.analysis.md +0 -14
  126. package/src/results/analysis/table-with-images.analysis.md +0 -15
  127. package/src/results/analysis/table.analysis.md +0 -15
  128. package/src/results/filter-sidebar/index.analysis.md +0 -14
  129. package/src/results/generic/table-result-list.analysis.md +0 -15
  130. package/src/results/generic/table-result-list.stories.tsx +0 -21
  131. package/src/results/stories/cards.stories.tsx +0 -66
  132. package/src/results/stories/dialog-filter.stories.tsx +0 -20
  133. package/src/results/stories/empty.stories.tsx +0 -25
  134. package/src/results/stories/filter-navbar.stories.tsx +0 -19
  135. package/src/results/stories/filter-sidebar.stories.tsx +0 -20
  136. package/src/results/stories/pagination.stories.tsx +0 -24
  137. package/src/results/stories/table-with-images.stories.tsx +0 -19
  138. package/src/results/stories/table.stories.tsx +0 -78
  139. package/src/search-input.analysis.md +0 -15
  140. package/src/share-button.analysis.md +0 -19
  141. package/src/stories/autocomplete.stories.tsx +0 -20
  142. package/src/stories/breadcrumb.stories.tsx +0 -93
  143. package/src/stories/check-article-lang.stories.tsx +0 -22
  144. package/src/stories/render-article.stories.tsx +0 -19
  145. package/src/stories/search-input.stories.tsx +0 -21
  146. package/src/stories/share-button.stories.tsx +0 -15
@@ -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 { ComponentProps, FC } from "react"
4
- import * as ComponentOptions from "../generated/client-components";
5
- import { InformationSubjectsGetAllClient } from "../generated/client-components";
6
- import { Skeleton } from "@c-rex/ui/skeleton";
7
- import { RestrictionMenu } from "./restriction-menu";
8
-
9
- type Props = {
10
- restrictField?: string
11
- navigationMenuListClassName?: string
12
- itemsToRender?: number
13
- requestType?: keyof typeof ComponentOptions
14
- } & Partial<Omit<ComponentProps<typeof InformationSubjectsGetAllClient>, ("render" | "children" | "pathParams")>>;
15
-
16
- export const RestrictionMenuContainer: FC<Props> = ({
17
- queryParams,
18
- restrictField = "informationSubjects",
19
- itemsToRender = 7,
20
- requestType = "InformationSubjectsGetAllClient",
21
- navigationMenuListClassName = "items-center justify-between flex-row",
22
- }) => {
23
- const RequestComponent = ComponentOptions[requestType] as unknown as FC<ComponentProps<typeof InformationSubjectsGetAllClient>>;
24
-
25
- return (
26
- <RequestComponent
27
- queryParams={{
28
- Fields: ["labels"],
29
- PageSize: 100,
30
- Links: true,
31
- Sort: ["labels.value"],
32
- ...queryParams
33
- }}
34
- >
35
- {({ data, isLoading }) => {
36
-
37
- if (isLoading) return (
38
- <div className="flex justify-between">
39
- <Skeleton className="w-12 h-9 rounded-full" />
40
- {Array(itemsToRender).fill(0).map((_, index) => (
41
- <Skeleton key={`skeleton-${index}`} className="w-28 h-9 rounded-full" />
42
- ))}
43
- <Skeleton className="w-20 h-9 rounded-full" />
44
- </div>
45
- )
46
-
47
- if (!data) return null;
48
-
49
- return <RestrictionMenu restrictField={restrictField} items={data.items} navigationMenuListClassName={navigationMenuListClassName} />
50
- }}
51
- </RequestComponent>
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
- export const RestrictionNavigationItem: FC<Props> = ({
19
- shortId,
20
- label,
21
- restrictField,
22
- removeRestrictParam = false,
23
- selected = false,
24
- }) => {
25
- const [restrict, setRestrict] = useQueryState("restrict", {
26
- shallow: false,
27
- history: "push",
28
- });
29
-
30
- const { restrictionValue, shouldRemoveRestrictParam } = getRestrictionValue({
31
- shortId,
32
- restrictField,
33
- removeRestrictParam,
34
- selected,
35
- currentRestrict: restrict,
36
- });
37
-
38
- const labelStyle = {
39
- display: 'inline-block',
40
- maxWidth: 100,
41
- overflow: 'hidden',
42
- textOverflow: 'ellipsis',
43
- whiteSpace: 'nowrap',
44
- verticalAlign: 'middle',
45
- } as React.CSSProperties;
46
-
47
- const needsTooltip = label.length > 12;
48
-
49
- const labelNode = needsTooltip ? (
50
- <TooltipProvider delayDuration={300}>
51
- <Tooltip>
52
- <TooltipTrigger asChild>
53
- <span style={labelStyle}>{label}</span>
54
- </TooltipTrigger>
55
- <TooltipContent>{label}</TooltipContent>
56
- </Tooltip>
57
- </TooltipProvider>
58
- ) : (
59
- <span style={labelStyle}>{label}</span>
60
- );
61
-
62
- return (
63
- <NavigationMenuItem key={shortId} asChild>
64
- <Button
65
- variant={selected ? "default" : "outline"}
66
- rounded="full"
67
- onClick={() => shouldRemoveRestrictParam ? setRestrict(null) : setRestrict(restrictionValue)}
68
- className="cursor-pointer"
69
- >
70
- {labelNode}
71
- </Button>
72
- </NavigationMenuItem>
73
- );
74
- };
75
-
76
- export const RestrictionDropdownItem: FC<Props> = ({
77
- shortId,
78
- label,
79
- restrictField,
80
- selected = false,
81
- }) => {
82
- const [restrict, setRestrict] = useQueryState("restrict", {
83
- shallow: false,
84
- history: "push",
85
- });
86
-
87
- const { restrictionValue, shouldRemoveRestrictParam } = getRestrictionValue({
88
- shortId,
89
- restrictField,
90
- selected,
91
- currentRestrict: restrict,
92
- });
93
-
94
- return (
95
- <Button
96
- variant={selected ? "default" : "ghost"}
97
- onClick={() => shouldRemoveRestrictParam ? setRestrict(null) : setRestrict(restrictionValue)}
98
- rounded="full"
99
- className="text-left text-wrap !h-auto min-h-10 w-full !justify-start cursor-pointer"
100
- >
101
- {label}
102
- </Button>
103
- );
104
- };
105
-
106
- function getRestrictionValue({
107
- shortId,
108
- restrictField,
109
- removeRestrictParam = false,
110
- selected = false,
111
-
112
- currentRestrict,
113
- }: {
114
- shortId?: string;
115
- restrictField?: string;
116
- removeRestrictParam?: boolean;
117
- selected?: boolean;
118
- currentRestrict: string | null;
119
- }): { restrictionValue: string | null; shouldRemoveRestrictParam: boolean } {
120
- let restrictParam = "";
121
- let shouldRemoveRestrictParam = removeRestrictParam;
122
-
123
- if (currentRestrict) {
124
- if (selected) {
125
- const restrictionsLength = currentRestrict.split(",").length;
126
- //if there is only one restriction, we can remove the whole restrict param
127
- if (restrictionsLength === 1) {
128
- shouldRemoveRestrictParam = true;
129
- } else {
130
- restrictParam = currentRestrict.replace(`${shortId}`, "").replace(",,", ",").replace(/(^,)|(,$)/g, "");
131
-
132
- // Remove the restrict param if nothing remains after '='
133
- if (/^[^=]+=$/.test(restrictParam)) {
134
- shouldRemoveRestrictParam = true;
135
- }
136
- }
137
- } else {
138
- restrictParam = `${currentRestrict},${shortId}`;
139
- }
140
- } else {
141
- restrictParam = `${restrictField}=${shortId}`;
142
- }
143
-
144
- const restrictionValue = shouldRemoveRestrictParam ? null : restrictParam
145
-
146
- return { restrictionValue, shouldRemoveRestrictParam };
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
+ }