@c-rex/components 0.1.26 → 0.1.29

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 (60) hide show
  1. package/package.json +9 -1
  2. package/src/article/article-action-bar.stories.tsx +15 -0
  3. package/src/article/article-content.stories.tsx +21 -0
  4. package/src/autocomplete.tsx +8 -5
  5. package/src/carousel/carousel.tsx +344 -0
  6. package/src/check-article-lang.tsx +5 -5
  7. package/src/directoryNodes/tree-of-content.stories.tsx +22 -0
  8. package/src/directoryNodes/tree-of-content.tsx +2 -2
  9. package/src/documents/result-list.stories.tsx +19 -0
  10. package/src/documents/result-list.tsx +24 -33
  11. package/src/favorites/bookmark-button.stories.tsx +19 -0
  12. package/src/favorites/favorite-button.stories.tsx +22 -0
  13. package/src/generated/create-server-request.tsx +2 -2
  14. package/src/icons/file-icon.stories.tsx +19 -0
  15. package/src/icons/flag-icon.stories.tsx +25 -0
  16. package/src/icons/loading.stories.tsx +21 -0
  17. package/src/info/info-table.tsx +2 -69
  18. package/src/info/stories/info-table.stories.tsx +31 -0
  19. package/src/info/stories/shared.stories.tsx +24 -0
  20. package/src/navbar/language-switcher/content-language-switch.tsx +3 -7
  21. package/src/navbar/language-switcher/ui-language-switch.tsx +2 -2
  22. package/src/navbar/navbar.tsx +76 -35
  23. package/src/navbar/stories/navbar.stories.tsx +21 -6
  24. package/src/navbar/stories/settings.stories.tsx +15 -0
  25. package/src/navbar/stories/sign-in-out-btns.stories.tsx +15 -0
  26. package/src/navbar/stories/user-menu.stories.tsx +20 -0
  27. package/src/page-wrapper.tsx +10 -14
  28. package/src/renditions/file-download.stories.tsx +19 -0
  29. package/src/renditions/html.stories.tsx +19 -0
  30. package/src/renditions/html.tsx +12 -9
  31. package/src/renditions/image/container.stories.tsx +19 -0
  32. package/src/renditions/image/container.tsx +6 -3
  33. package/src/renditions/image/rendition.stories.tsx +19 -0
  34. package/src/renditions/image/rendition.tsx +9 -14
  35. package/src/restriction-menu/restriction-menu-item.tsx +89 -0
  36. package/src/restriction-menu/restriction-menu.tsx +157 -0
  37. package/src/results/cards.tsx +1 -1
  38. package/src/results/dialog-filter.tsx +30 -67
  39. package/src/results/filter-navbar.tsx +12 -15
  40. package/src/results/generic/table-result-list.stories.tsx +21 -0
  41. package/src/results/generic/table-result-list.tsx +31 -28
  42. package/src/results/stories/cards.stories.tsx +16 -8
  43. package/src/results/stories/dialog-filter.stories.tsx +20 -0
  44. package/src/results/stories/empty.stories.tsx +12 -1
  45. package/src/results/stories/filter-navbar.stories.tsx +19 -0
  46. package/src/results/stories/filter-sidebar.stories.tsx +20 -0
  47. package/src/results/stories/pagination.stories.tsx +24 -0
  48. package/src/results/stories/table-with-images.stories.tsx +19 -0
  49. package/src/results/stories/table.stories.tsx +32 -9
  50. package/src/results/table-with-images.tsx +2 -16
  51. package/src/search-input.tsx +33 -12
  52. package/src/stores/language-store.ts +33 -16
  53. package/src/stores/search-settings-store.ts +49 -8
  54. package/src/stories/autocomplete.stories.tsx +20 -0
  55. package/src/stories/breadcrumb.stories.tsx +72 -31
  56. package/src/stories/check-article-lang.stories.tsx +22 -0
  57. package/src/stories/render-article.stories.tsx +19 -0
  58. package/src/stories/search-input.stories.tsx +21 -0
  59. package/src/stories/share-button.stories.tsx +15 -0
  60. package/src/results/result-container.tsx +0 -70
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/components",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "files": [
5
5
  "src"
6
6
  ],
@@ -117,6 +117,10 @@
117
117
  "types": "./src/stores/favorites-store.ts",
118
118
  "import": "./src/stores/favorites-store.ts"
119
119
  },
120
+ "./search-settings-store": {
121
+ "types": "./src/stores/search-settings-store.ts",
122
+ "import": "./src/stores/search-settings-store.ts"
123
+ },
120
124
  "./article-content": {
121
125
  "types": "./src/article/article-content.tsx",
122
126
  "import": "./src/article/article-content.tsx"
@@ -156,6 +160,10 @@
156
160
  "./generic-table-result-list": {
157
161
  "types": "./src/results/generic/table-result-list.tsx",
158
162
  "import": "./src/results/generic/table-result-list.tsx"
163
+ },
164
+ "./carousel": {
165
+ "types": "./src/carousel/carousel.tsx",
166
+ "import": "./src/carousel/carousel.tsx"
159
167
  }
160
168
  },
161
169
  "scripts": {
@@ -0,0 +1,15 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ArticleActionBar } from '../article-action-bar';
3
+
4
+ const meta: Meta<typeof ArticleActionBar> = {
5
+ title: 'Components/Article/ActionBar',
6
+ component: ArticleActionBar,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ type Story = StoryObj<typeof ArticleActionBar>;
14
+
15
+ export const Basic: Story = {};
@@ -0,0 +1,21 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ArticleContent } from '../article-content';
3
+
4
+ const meta: Meta<typeof ArticleContent> = {
5
+ title: 'Components/Article/Content',
6
+ component: ArticleContent,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ type Story = StoryObj<typeof ArticleContent>;
14
+
15
+ export const Basic: Story = {
16
+ args: {
17
+ renditions: [], // Não renderiza conteúdo
18
+ },
19
+ };
20
+
21
+ // Exemplo completo depende de dados reais de RenditionModel e fetch, normalmente mockado em ambiente de Storybook.
@@ -6,9 +6,9 @@ import { useTranslations } from "next-intl";
6
6
  import { SuggestionQueryParams } from "@c-rex/interfaces";
7
7
 
8
8
  import { useSearchSettingsStore } from "./stores/search-settings-store";
9
- import { useLanguageStore } from "./stores/language-store";
10
9
  import { useRouter } from 'next/navigation';
11
10
  import { suggestionRequest } from "./generated/create-suggestions-request";
11
+ import { useQueryState } from "nuqs";
12
12
 
13
13
  export type AutoCompleteProps = {
14
14
  initialValue?: string;
@@ -28,6 +28,8 @@ export const AutoComplete = ({
28
28
  onSelectPath = "/"
29
29
  }: AutoCompleteProps) => {
30
30
  const t = useTranslations();
31
+ const [pkg] = useQueryState("package");
32
+
31
33
  const containerRef = useRef<HTMLDivElement>(null);
32
34
  const router = useRouter();
33
35
  const [open, setOpen] = useState(false);
@@ -36,7 +38,10 @@ export const AutoComplete = ({
36
38
  const [suggestions, setSuggestions] = useState<string[]>([]);
37
39
 
38
40
  const fetchSuggestions = useCallback(async (prefix: string): Promise<string[]> => {
39
- const results = await suggestionRequest({ endpoint, prefix, queryParams });
41
+ const params = { ...queryParams };
42
+ if (pkg != null) params.scopes = pkg as unknown as string[];
43
+
44
+ const results = await suggestionRequest({ endpoint, prefix, queryParams: params });
40
45
 
41
46
  return results.data;
42
47
  }, [endpoint, queryParams]);
@@ -50,13 +55,11 @@ export const AutoComplete = ({
50
55
  const onSelect = (value: string) => {
51
56
  const params = new URLSearchParams();
52
57
  const searchSettings = useSearchSettingsStore.getState();
53
- const languageSettings = useLanguageStore.getState();
54
58
 
55
59
  params.set('search', value);
56
60
  params.set("page", "1")
57
61
  params.set("operator", searchSettings.operator)
58
- params.set("language",
59
- searchSettings.language.length > 0 ? searchSettings.language.join(",") : languageSettings.contentLang)
62
+ params.set("language", searchSettings.language)
60
63
  params.set("wildcard", searchSettings.wildcard)
61
64
  params.set("like", searchSettings.like.toString())
62
65
 
@@ -0,0 +1,344 @@
1
+ "use client"
2
+
3
+ import {
4
+ useRef,
5
+ useState,
6
+ useEffect,
7
+ useCallback,
8
+ FC,
9
+ createContext,
10
+ useContext,
11
+ } from "react";
12
+ import { cn, getLanguage, getTitle, getType } from "@c-rex/utils";
13
+ import { Button } from "@c-rex/ui/button";
14
+ import { ArrowLeft, ArrowRight } from "lucide-react";
15
+ import { Skeleton } from "@c-rex/ui/skeleton";
16
+ import * as ServiceOptions from "@c-rex/services/client-requests";
17
+ import { documentsGetAllClientService } from "@c-rex/services/client-requests";
18
+ import { CommonItemsModel } from "@c-rex/interfaces";
19
+ import { ImageRenditionContainer } from "../renditions/image/container";
20
+ import { Card } from "@c-rex/ui/card";
21
+ import { Badge } from "@c-rex/ui/badge";
22
+ import { Flag } from "../icons/flag-icon"
23
+ import { Empty } from "../results/empty";
24
+
25
+ // Types
26
+
27
+ type ResponsiveSetting = {
28
+ maxWidth: number;
29
+ slidesToShow: number;
30
+ };
31
+
32
+ type CarouselContextProps = {
33
+ next: () => void;
34
+ prev: () => void;
35
+ setCurrent: (idx: number) => void;
36
+ current: number;
37
+ slidesToShow: number;
38
+ slidesLength: number;
39
+ page?: number;
40
+ pageCount?: number;
41
+ setPage?: (page: number) => void;
42
+ hasNextPage?: boolean;
43
+ hasPrevPage?: boolean;
44
+ };
45
+
46
+ const CarouselContext = createContext<CarouselContextProps | null>(null);
47
+
48
+ export function useCarousel() {
49
+ const context = useContext(CarouselContext);
50
+ if (!context) {
51
+ throw new Error("useCarousel must be used within a <Carousel />");
52
+ }
53
+ return context;
54
+ }
55
+
56
+ type PageInfo = {
57
+ hasNextPage: boolean;
58
+ hasPreviousPage: boolean;
59
+ pageCount: number;
60
+ };
61
+ type Props = {
62
+ className?: string;
63
+ arrows?: boolean;
64
+ autoplay?: boolean;
65
+ autoplaySpeed?: number;
66
+ responsive?: ResponsiveSetting[];
67
+ indicators?: boolean;
68
+ showImages?: boolean;
69
+ renderItem?: (item: CommonItemsModel, showImages: boolean) => React.ReactNode;
70
+ serviceType: keyof typeof ServiceOptions;
71
+ queryParams?: Record<string, any>;
72
+ loadByPages?: boolean;
73
+ };
74
+
75
+ export const Carousel: FC<Props> = ({
76
+ className,
77
+ arrows = false,
78
+ autoplay = false,
79
+ responsive = [{ maxWidth: 99999, slidesToShow: 1 }],
80
+ indicators = false,
81
+ autoplaySpeed = 3000,
82
+ showImages = false,
83
+ renderItem,
84
+ serviceType,
85
+ queryParams = {},
86
+ loadByPages = false,
87
+ }) => {
88
+ const service = ServiceOptions[serviceType] as typeof documentsGetAllClientService;
89
+ const renderFunc = renderItem ? renderItem : defaultRenderCarouselItem;
90
+
91
+ // Responsive
92
+ const [slidesToShow, setSlidesToShow] = useState(responsive[0]?.slidesToShow || 1);
93
+ const updateSlidesToShow = useCallback(() => {
94
+ const width = window.innerWidth;
95
+ const sorted = [...responsive].sort((a, b) => a.maxWidth - b.maxWidth);
96
+ let found = sorted[sorted.length - 1]?.slidesToShow || 1;
97
+ for (const r of sorted) {
98
+ if (width <= r.maxWidth) {
99
+ found = r.slidesToShow;
100
+ break;
101
+ }
102
+ }
103
+ setSlidesToShow(found);
104
+ }, [responsive]);
105
+ useEffect(() => {
106
+ updateSlidesToShow();
107
+ window.addEventListener("resize", updateSlidesToShow);
108
+ return () => window.removeEventListener("resize", updateSlidesToShow);
109
+ }, [updateSlidesToShow]);
110
+
111
+ // State
112
+ const [current, setCurrent] = useState(0);
113
+ const [data, setData] = useState<CommonItemsModel[] | null>(null);
114
+ const [loading, setIsLoading] = useState(true);
115
+ const [error, setError] = useState<unknown>(null);
116
+
117
+ // Pagination
118
+ const [page, setPage] = useState(1);
119
+ const [pageInfo, setPageInfo] = useState<PageInfo | null>(null);
120
+ const [hasNextPage, setHasNextPage] = useState(true);
121
+ const [hasPrevPage, setHasPrevPage] = useState(false);
122
+
123
+ // Autoplay
124
+ const timer = useRef<NodeJS.Timeout | null>(null);
125
+
126
+ // Data loading
127
+ useEffect(() => {
128
+ let isMounted = true;
129
+ setIsLoading(true);
130
+ setError(null);
131
+ setData(null);
132
+ if (loadByPages) {
133
+ (async () => {
134
+ try {
135
+ const result = await service({ ...queryParams, PageNumber: page });
136
+ if (!isMounted) return;
137
+ setData((result && 'items' in result) ? result.items as CommonItemsModel[] : []);
138
+ setPageInfo((result && 'pageInfo' in result) ? result.pageInfo as PageInfo : null);
139
+ } catch (err) {
140
+ if (!isMounted) return;
141
+ setError(err);
142
+ } finally {
143
+ if (isMounted) setIsLoading(false);
144
+ }
145
+ })();
146
+ } else {
147
+ (async () => {
148
+ try {
149
+ const result = await service(queryParams);
150
+ if (!isMounted) return;
151
+ setData((result && 'items' in result) ? result.items as CommonItemsModel[] : []);
152
+ } catch (err) {
153
+ if (!isMounted) return;
154
+ setError(err);
155
+ } finally {
156
+ if (isMounted) setIsLoading(false);
157
+ }
158
+ })();
159
+ }
160
+ return () => { isMounted = false; };
161
+ }, [queryParams, page, loadByPages, service]);
162
+
163
+ // Page info
164
+ useEffect(() => {
165
+ if (loadByPages && pageInfo) {
166
+ setHasNextPage(pageInfo.hasNextPage);
167
+ setHasPrevPage(pageInfo.hasPreviousPage);
168
+ }
169
+ }, [pageInfo, loadByPages]);
170
+
171
+ // Autoplay logic (static mode only)
172
+ useEffect(() => {
173
+ if (!autoplay || loading || !data || data.length === 0 || loadByPages) return;
174
+ timer.current = setTimeout(() => {
175
+ if (current < data.length - slidesToShow) {
176
+ setCurrent((prev) => prev + 1);
177
+ } else {
178
+ setCurrent(0);
179
+ }
180
+ }, autoplaySpeed);
181
+ return () => {
182
+ if (timer.current) clearTimeout(timer.current);
183
+ };
184
+ }, [autoplay, autoplaySpeed, current, data, slidesToShow, loading, loadByPages]);
185
+
186
+ // Navigation
187
+ const prev = useCallback(() => {
188
+ if (loadByPages) {
189
+ if (hasPrevPage) setPage((p) => p - 1);
190
+ } else {
191
+ setCurrent((prev) => Math.max(prev - 1, 0));
192
+ }
193
+ }, [loadByPages, hasPrevPage]);
194
+
195
+ const next = useCallback(() => {
196
+ if (loadByPages) {
197
+ if (hasNextPage) setPage((p) => p + 1);
198
+ } else {
199
+ setCurrent((prev) => (prev < (data?.length || 0) - slidesToShow ? prev + 1 : 0));
200
+ }
201
+ }, [loadByPages, hasNextPage, data, slidesToShow]);
202
+
203
+ const contextValue: CarouselContextProps = {
204
+ next,
205
+ prev,
206
+ setCurrent,
207
+ current,
208
+ slidesToShow,
209
+ pageCount: pageInfo?.pageCount || 1,
210
+ slidesLength: data?.length || 0,
211
+ page: loadByPages ? page : undefined,
212
+ setPage: loadByPages ? setPage : undefined,
213
+ hasNextPage: loadByPages ? hasNextPage : undefined,
214
+ hasPrevPage: loadByPages ? hasPrevPage : undefined,
215
+ };
216
+
217
+ return (
218
+ <CarouselContext.Provider value={contextValue}>
219
+ <div className={cn("flex items-center flex-col", className)}>
220
+ <div className={cn("w-full flex items-center")}>
221
+ {(arrows && data && data.length > 0) && <CarouselPrev />}
222
+ <div className={cn("flex-1 overflow-hidden relative flex items-center")}>
223
+ <div
224
+ className="flex will-change-transform transition-all duration-600 ease-[cubic-bezier(0.4,0,0.2,1)] w-full"
225
+ style={{ transform: `translateX(-${(current * 100) / slidesToShow}%)` }}
226
+ >
227
+ {loading ? (
228
+ <Skeleton className="w-full h-80" />
229
+ ) : error ? (
230
+ <div className="w-full h-80 flex items-center justify-center text-red-500">
231
+ {JSON.stringify(error)}
232
+ </div>
233
+ ) : data && data.length == 0 ? (
234
+ <Empty />
235
+ ) : data && data.length > 0 ? (
236
+ data.map((item: CommonItemsModel) => (
237
+ <div
238
+ key={item.shortId}
239
+ className={`flex-shrink-0 flex-grow-0 flex justify-center`}
240
+ style={{ width: `${100 / slidesToShow}%` }}
241
+ >
242
+ {renderFunc(item, showImages)}
243
+ </div>
244
+ ))
245
+ ) : null}
246
+ </div>
247
+ </div>
248
+ {(arrows && data && data.length > 0) && <CarouselNext />}
249
+ </div>
250
+ {indicators && <CarouselIndicators />}
251
+ </div>
252
+ </CarouselContext.Provider>
253
+ );
254
+ };
255
+
256
+ const CarouselNext: FC = () => {
257
+ const { next, current, slidesToShow, slidesLength, hasNextPage } = useCarousel();
258
+ const disabled = hasNextPage === undefined ? current >= slidesLength - slidesToShow : !hasNextPage;
259
+ return (
260
+ <Button className="size-8" rounded="full" onClick={next} variant="ghost" disabled={disabled}>
261
+ <ArrowRight />
262
+ </Button>
263
+ );
264
+ };
265
+
266
+ const CarouselPrev: FC = () => {
267
+ const { prev, current, hasPrevPage } = useCarousel();
268
+ const disabled = hasPrevPage === undefined ? current === 0 : !hasPrevPage;
269
+ return (
270
+ <Button className="size-8" rounded="full" onClick={prev} variant="ghost" disabled={disabled}>
271
+ <ArrowLeft />
272
+ </Button>
273
+ );
274
+ };
275
+
276
+ const CarouselIndicators: FC<{ className?: string }> = ({ className }) => {
277
+ const { current, setCurrent, slidesToShow, slidesLength, page, setPage, pageCount } = useCarousel();
278
+ // Static mode
279
+ if (!setPage) {
280
+ const totalOfIndicators = Math.max(slidesLength - slidesToShow + 1, 1);
281
+ if (totalOfIndicators === 1) return null;
282
+ return (
283
+ <ol className={cn("flex gap-2 z-20 list-none p-0 m-0", className)}>
284
+ {Array.from({ length: totalOfIndicators }).map((_, index) => (
285
+ <li key={index}>
286
+ <Button
287
+ className={`w-3 h-3 rounded-full border-0 cursor-pointer transition-colors ${index === current ? "bg-gray-800" : "bg-gray-300"}`}
288
+ size="xs"
289
+ onClick={() => setCurrent(index)}
290
+ />
291
+ </li>
292
+ ))}
293
+ </ol>
294
+ );
295
+ }
296
+ // Paginated mode
297
+ const totalOfIndicators = pageCount || 1;
298
+ if (totalOfIndicators === 1) return null;
299
+ return (
300
+ <ol className={cn("flex gap-2 z-20 list-none p-0 m-0", className)}>
301
+ {Array.from({ length: totalOfIndicators }).map((_, index) => {
302
+ const pageNumber = index + 1;
303
+ return (
304
+ <li key={pageNumber}>
305
+ <Button
306
+ className={`w-3 h-3 rounded-full border-0 cursor-pointer transition-colors ${pageNumber === page ? "bg-gray-800" : "bg-gray-300"}`}
307
+ size="xs"
308
+ onClick={() => setPage(pageNumber)}
309
+ />
310
+ </li>
311
+ );
312
+ })}
313
+ </ol>
314
+ );
315
+ };
316
+
317
+ const defaultRenderCarouselItem = (item: CommonItemsModel, showImages: boolean) => {
318
+ const title = getTitle(item.titles, item.labels);
319
+ const itemType = getType(item.class);
320
+ const language = getLanguage(item.languages)
321
+ const countryCode = language.split("-")[1] || "";
322
+ return (
323
+ <div className="p-2 flex flex-1">
324
+ <Card className="p-4 flex-1 justify-between">
325
+ {showImages && (
326
+ <ImageRenditionContainer
327
+ itemShortId={item.shortId!}
328
+ emptyImageStyle="h-48 w-full"
329
+ imageStyle="object-cover h-48 w-full"
330
+ />
331
+ )}
332
+
333
+ <span className="text-lg font-semibold">{title}</span>
334
+
335
+ <div className="flex justify-between w-full">
336
+ <Badge>{itemType}</Badge>
337
+ <span className="w-8 block border">
338
+ <Flag countryCode={countryCode} />
339
+ </span>
340
+ </div>
341
+ </Card>
342
+ </div>
343
+ );
344
+ }
@@ -5,7 +5,7 @@ import { useAppConfig } from "@c-rex/contexts/config-provider";
5
5
  import { AvailableVersionsInterface } from "@c-rex/interfaces";
6
6
  import { toast } from "sonner"
7
7
  import { useTranslations } from "next-intl"
8
- import { useLanguageStore } from "./stores/language-store";
8
+ import { useSearchSettingsStore } from "./stores/search-settings-store";
9
9
 
10
10
  interface Props {
11
11
  availableVersions: AvailableVersionsInterface[]
@@ -17,12 +17,12 @@ export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
17
17
 
18
18
  useEffect(() => {
19
19
  setAvailableVersions(availableVersions)
20
- const contentLang = useLanguageStore.getState().contentLang;
21
-
20
+ const searchLanguage = useSearchSettingsStore.getState().language;
22
21
  const activeArticle = availableVersions.filter((item) => item.active)[0]
23
- if (activeArticle == undefined || activeArticle.lang == contentLang) return
24
22
 
25
- const articleAvailable = availableVersions.find((item) => item.lang === contentLang)
23
+ if (activeArticle == undefined || activeArticle.lang == searchLanguage) return
24
+
25
+ const articleAvailable = availableVersions.find((item) => item.lang === searchLanguage)
26
26
  if (articleAvailable != undefined) {
27
27
  articleAvailableInToast(articleAvailable.lang, articleAvailable.link)
28
28
  }
@@ -0,0 +1,22 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { TreeOfContent } from './tree-of-content';
3
+
4
+ const meta: Meta<typeof TreeOfContent> = {
5
+ title: 'Components/DirectoryNodes/TreeOfContent',
6
+ component: TreeOfContent,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ type Story = StoryObj<typeof TreeOfContent>;
14
+
15
+ export const Basic: Story = {
16
+ args: {
17
+ directoryNodes: [
18
+ { id: '1', label: 'Node 1', active: true, children: [] },
19
+ { id: '2', label: 'Node 2', active: false, children: [] },
20
+ ],
21
+ },
22
+ };
@@ -38,7 +38,7 @@ export async function TreeOfContent({ directoryNodes }: SidebarProps) {
38
38
  {tree.map((item) => (
39
39
  <SidebarMenuItem key={item.id}>
40
40
  <SidebarMenuButton asChild isActive={item.active}>
41
- <Link href={`/topics/${item.linkId}/content`} title={item.label}>
41
+ <Link href={`/topics/${item.linkId}/pages`} title={item.label}>
42
42
  {item.label}
43
43
  </Link>
44
44
  </SidebarMenuButton>
@@ -51,7 +51,7 @@ export async function TreeOfContent({ directoryNodes }: SidebarProps) {
51
51
  asChild
52
52
  isActive={item.active}
53
53
  >
54
- <Link href={`/topics/${item.linkId}/content`} title={item.label}>
54
+ <Link href={`/topics/${item.linkId}/pages`} title={item.label}>
55
55
  {item.label}
56
56
  </Link>
57
57
  </SidebarMenuSubButton>
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { DocumentsResultList } from '../result-list';
3
+
4
+ const meta: Meta<typeof DocumentsResultList> = {
5
+ title: 'Components/Documents/ResultList',
6
+ component: DocumentsResultList,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ type Story = StoryObj<typeof DocumentsResultList>;
14
+
15
+ export const Basic: Story = {
16
+ args: {
17
+ items: [],
18
+ },
19
+ };
@@ -1,36 +1,21 @@
1
1
  import { FC } from "react";
2
- import {
3
- DocumentModel,
4
- ExternalProductGraphicModel,
5
- FragmentModel,
6
- InformationUnitModel,
7
- PackageModel,
8
- TopicModel
9
- } from "@c-rex/interfaces";
2
+ import { CommonItemsModel } from "@c-rex/interfaces";
10
3
  import { FileStack } from "lucide-react";
11
- import { cn, getType, getTitle, getVersions } from "@c-rex/utils";
4
+ import { cn, getType, getTitle, getVersions, getLanguage } from "@c-rex/utils";
12
5
  import { Button } from "@c-rex/ui/button";
13
6
  import { Tooltip, TooltipContent, TooltipTrigger } from "@c-rex/ui/tooltip";
14
- import { ResultTypes } from "@c-rex/types";
15
7
  import { RESULT_TYPES } from "@c-rex/constants";
16
8
  import { FileDownloadDropdown } from "@c-rex/components/file-download";
17
9
  import { FavoriteButton } from "@c-rex/components/favorite-button";
18
-
19
10
  import { ImageRenditionContainer } from "../renditions/image/container";
20
11
  import { BookmarkButton } from "../favorites/bookmark-button";
21
12
  import { HtmlRendition } from "../renditions/html";
13
+ import { generateQueryParams } from "../../../utils/src/params";
14
+ import { QueryParams } from "@c-rex/types";
22
15
 
23
16
  interface Props {
24
- items: (
25
- DocumentModel |
26
- ExternalProductGraphicModel |
27
- FragmentModel |
28
- InformationUnitModel |
29
- PackageModel |
30
- TopicModel
31
- )[];
17
+ items: CommonItemsModel[];
32
18
  query?: string;
33
- disabledResults?: ResultTypes[];
34
19
  imageRestrictions?: string[];
35
20
  showActions?: boolean;
36
21
  }
@@ -39,7 +24,6 @@ interface Props {
39
24
  export const DocumentsResultList: FC<Props> = ({
40
25
  items,
41
26
  query = "",
42
- disabledResults = [],
43
27
  imageRestrictions = [],
44
28
  showActions = true,
45
29
  }) => {
@@ -47,26 +31,37 @@ export const DocumentsResultList: FC<Props> = ({
47
31
  <div className="flex-1">
48
32
  {items.map((item, index) => {
49
33
  const title = getTitle(item.titles, item.labels);
34
+ const language = getLanguage(item.languages);
50
35
  const itemType = getType(item.class);
51
- const disabled = disabledResults.includes(itemType);
52
36
  const isDocument = itemType === RESULT_TYPES.DOCUMENT;
53
37
  const multipleVersions = getVersions(item.versionOf);
54
38
  const packageId = item.packages && item.packages.length > 0 ? item.packages[0]?.shortId : null;
55
-
56
- let itemLink = `/documents/${item.shortId}?q=${query}`
39
+ const queryParams: QueryParams[] = []
57
40
 
58
41
  if (packageId) {
59
- itemLink += `&package=${packageId}`
42
+ queryParams.push({
43
+ key: "package",
44
+ value: packageId,
45
+ })
46
+ }
47
+
48
+ if (query.length > 0) {
49
+ queryParams.push({
50
+ key: "q",
51
+ value: query,
52
+ })
60
53
  }
61
54
 
55
+ const params = generateQueryParams(queryParams)
56
+ const itemLink = `/documents/${item.shortId}` + (params.length > 0 ? `?${params}` : "")
57
+
62
58
  return (
63
59
  <div
64
60
  key={index}
65
61
  className={cn(
66
62
  "min-h-12 flex flex-wrap items-center border px-4 py-2 gap-2 rounded",
67
63
  index == items.length - 1 ? "" : "mb-4",
68
- `c-rex-result-item c-rex-result-${itemType}`,
69
- disabled && "c-rex-result-item-disabled",
64
+ `c-rex-result-item c-rex-result-${itemType}`
70
65
  )}
71
66
  >
72
67
  <div className="h-16 w-16 flex items-start">
@@ -79,15 +74,11 @@ export const DocumentsResultList: FC<Props> = ({
79
74
 
80
75
  <div className="flex-1 p-2 flex flex-col">
81
76
  <span className="text-sm text-muted-foreground">
82
- {item.revision}
77
+ {item.revision} - {language}
83
78
  </span>
84
79
 
85
80
  <span className="text-lg font-medium">
86
- {disabled ? (
87
- title
88
- ) : (
89
- <a className="hover:underline" href={itemLink}>{title}</a>
90
- )}
81
+ <a className="hover:underline" href={itemLink}>{title}</a>
91
82
  </span>
92
83
 
93
84
  <span className="text-sm">
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { BookmarkButton } from '../bookmark-button';
3
+
4
+ const meta: Meta<typeof BookmarkButton> = {
5
+ title: 'Components/Favorites/BookmarkButton',
6
+ component: BookmarkButton,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ type Story = StoryObj<typeof BookmarkButton>;
14
+
15
+ export const Basic: Story = {
16
+ args: {
17
+ shortId: 'doc-1',
18
+ },
19
+ };