@c-rex/components 0.1.22 → 0.1.23

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 (53) hide show
  1. package/package.json +78 -62
  2. package/src/article/article-action-bar.tsx +89 -0
  3. package/src/article/article-content.tsx +55 -0
  4. package/src/autocomplete.tsx +55 -50
  5. package/src/breadcrumb.tsx +3 -1
  6. package/src/directoryNodes/tree-of-content.tsx +49 -0
  7. package/src/{bookmark-button.tsx → favorites/bookmark-button.tsx} +12 -3
  8. package/src/{favorite-button.tsx → favorites/favorite-button.tsx} +1 -1
  9. package/src/generated/client-components.tsx +1350 -0
  10. package/src/generated/create-client-request.tsx +105 -0
  11. package/src/generated/create-server-request.tsx +61 -0
  12. package/src/generated/create-suggestions-request.tsx +56 -0
  13. package/src/generated/server-components.tsx +1056 -0
  14. package/src/generated/suggestions.tsx +299 -0
  15. package/src/info/bookmark.tsx +51 -0
  16. package/src/info/info-table.tsx +127 -60
  17. package/src/info/shared.tsx +1 -1
  18. package/src/navbar/language-switcher/shared.tsx +1 -1
  19. package/src/navbar/navbar.tsx +1 -1
  20. package/src/{stories → navbar/stories}/navbar.stories.tsx +1 -1
  21. package/src/page-wrapper.tsx +1 -1
  22. package/src/renditions/file-download.tsx +84 -0
  23. package/src/renditions/html.tsx +55 -0
  24. package/src/renditions/image/container.tsx +52 -0
  25. package/src/renditions/image/rendition.tsx +61 -0
  26. package/src/{dialog-filter.tsx → results/dialog-filter.tsx} +22 -23
  27. package/src/results/filter-navbar.tsx +241 -0
  28. package/src/results/filter-sidebar/index.tsx +125 -0
  29. package/src/results/filter-sidebar/utils.ts +164 -0
  30. package/src/{pagination.tsx → results/pagination.tsx} +12 -10
  31. package/src/results/result-container.tsx +70 -0
  32. package/src/{stories/blog-view.stories.tsx → results/stories/cards.stories.tsx} +1 -1
  33. package/src/{stories/table-view.stories.tsx → results/stories/table.stories.tsx} +1 -1
  34. package/src/results/table-with-images.tsx +140 -0
  35. package/src/{result-view → results}/table.tsx +1 -2
  36. package/src/results/utils.ts +67 -0
  37. package/src/{navbar/search-input.tsx → search-input.tsx} +9 -6
  38. package/src/share-button.tsx +49 -0
  39. package/src/stores/search-settings-store.ts +1 -1
  40. package/src/blur-image.tsx +0 -23
  41. package/src/left-sidebar.tsx +0 -90
  42. package/src/result-list.tsx +0 -43
  43. package/src/result-view/table-with-images.tsx +0 -199
  44. package/src/right-sidebar.tsx +0 -70
  45. package/src/search-modal.tsx +0 -140
  46. package/src/stories/blur-image.stories.tsx +0 -51
  47. package/src/stories/sidebar.stories.tsx +0 -94
  48. /package/src/{file-icon.tsx → icons/file-icon.tsx} +0 -0
  49. /package/src/{flag.tsx → icons/flag-icon.tsx} +0 -0
  50. /package/src/{loading.tsx → icons/loading.tsx} +0 -0
  51. /package/src/{result-view/blog.tsx → results/cards.tsx} +0 -0
  52. /package/src/{empty.tsx → results/empty.tsx} +0 -0
  53. /package/src/{stories → results/stories}/empty.stories.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/components",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "files": [
5
5
  "src"
6
6
  ],
@@ -9,33 +9,45 @@
9
9
  },
10
10
  "sideEffects": false,
11
11
  "exports": {
12
- "./autocomplete": {
13
- "types": "./src/autocomplete.tsx",
14
- "import": "./src/autocomplete.tsx"
12
+ "./server-components": {
13
+ "types": "./src/generated/server-components.tsx",
14
+ "import": "./src/generated/server-components.tsx"
15
15
  },
16
- "./blog-card": {
17
- "types": "./src/blog-card.tsx",
18
- "import": "./src/blog-card.tsx"
16
+ "./client-components": {
17
+ "types": "./src/generated/client-components.tsx",
18
+ "import": "./src/generated/client-components.tsx"
19
19
  },
20
- "./blur-image": {
21
- "types": "./src/blur-image.tsx",
22
- "import": "./src/blur-image.tsx"
20
+ "./suggestion-components": {
21
+ "types": "./src/generated/suggestions.tsx",
22
+ "import": "./src/generated/suggestions.tsx"
23
23
  },
24
24
  "./breadcrumb": {
25
25
  "types": "./src/breadcrumb.tsx",
26
26
  "import": "./src/breadcrumb.tsx"
27
27
  },
28
+ "./navbar": {
29
+ "types": "./src/navbar/navbar.tsx",
30
+ "import": "./src/navbar/navbar.tsx"
31
+ },
32
+ "./page-wrapper": {
33
+ "types": "./src/page-wrapper.tsx",
34
+ "import": "./src/page-wrapper.tsx"
35
+ },
28
36
  "./settings-menu": {
29
37
  "types": "./src/navbar/settings.tsx",
30
38
  "import": "./src/navbar/settings.tsx"
31
39
  },
40
+ "./favorite-button": {
41
+ "types": "./src/favorites/favorite-button.tsx",
42
+ "import": "./src/favorites/favorite-button.tsx"
43
+ },
32
44
  "./flag": {
33
45
  "types": "./src/flag.tsx",
34
46
  "import": "./src/flag.tsx"
35
47
  },
36
- "./pagination": {
37
- "types": "./src/pagination.tsx",
38
- "import": "./src/pagination.tsx"
48
+ "./loading": {
49
+ "types": "./src/loading.tsx",
50
+ "import": "./src/loading.tsx"
39
51
  },
40
52
  "./info-table": {
41
53
  "types": "./src/info/info-table.tsx",
@@ -45,69 +57,61 @@
45
57
  "types": "./src/info/info-card.tsx",
46
58
  "import": "./src/info/info-card.tsx"
47
59
  },
48
- "./empty": {
49
- "types": "./src/empty.tsx",
50
- "import": "./src/empty.tsx"
51
- },
52
- "./navbar": {
53
- "types": "./src/navbar/navbar.tsx",
54
- "import": "./src/navbar/navbar.tsx"
60
+ "./check-article-lang": {
61
+ "types": "./src/check-article-lang.tsx",
62
+ "import": "./src/check-article-lang.tsx"
55
63
  },
56
- "./providers/search-state-wrapper": {
57
- "types": "./src/providers/search-state-wrapper.tsx",
58
- "import": "./src/providers/search-state-wrapper.tsx"
64
+ "./render-article": {
65
+ "types": "./src/render-article.tsx",
66
+ "import": "./src/render-article.tsx"
59
67
  },
60
- "./result-list": {
61
- "types": "./src/result-list.tsx",
62
- "import": "./src/result-list.tsx"
68
+ "./html-rendition": {
69
+ "types": "./src/renditions/html.tsx",
70
+ "import": "./src/renditions/html.tsx"
63
71
  },
64
- "./result-view/blog": {
65
- "types": "./src/result-view/blog.tsx",
66
- "import": "./src/result-view/blog.tsx"
72
+ "./image-rendition-container": {
73
+ "types": "./src/renditions/container.ts",
74
+ "import": "./src/renditions/container.ts"
67
75
  },
68
- "./result-view/table": {
69
- "types": "./src/result-view/table.tsx",
70
- "import": "./src/result-view/table.tsx"
76
+ "./autocomplete": {
77
+ "types": "./src/autocomplete.tsx",
78
+ "import": "./src/autocomplete.tsx"
71
79
  },
72
- "./dropdown-menu": {
73
- "types": "./src/result-view/dropdown-menu.tsx",
74
- "import": "./src/result-view/dropdown-menu.tsx"
80
+ "./result-container": {
81
+ "types": "./src/result-container.tsx",
82
+ "import": "./src/result-container.tsx"
75
83
  },
76
- "./left-sidebar": {
77
- "types": "./src/left-sidebar.tsx",
78
- "import": "./src/left-sidebar.tsx"
84
+ "./table": {
85
+ "types": "./src/results/table.tsx",
86
+ "import": "./src/results/table.tsx"
79
87
  },
80
- "./right-sidebar": {
81
- "types": "./src/right-sidebar.tsx",
82
- "import": "./src/right-sidebar.tsx"
88
+ "./table-with-images": {
89
+ "types": "./src/results/table-with-images.tsx",
90
+ "import": "./src/results/table-with-images.tsx"
83
91
  },
84
92
  "./dialog-filter": {
85
- "types": "./src/dialog-filter.tsx",
86
- "import": "./src/dialog-filter.tsx"
93
+ "types": "./src/results/dialog-filter.tsx",
94
+ "import": "./src/results/dialog-filter.tsx"
87
95
  },
88
- "./check-article-lang": {
89
- "types": "./src/check-article-lang.tsx",
90
- "import": "./src/check-article-lang.tsx"
91
- },
92
- "./render-article": {
93
- "types": "./src/render-article.tsx",
94
- "import": "./src/render-article.tsx"
96
+ "./pagination": {
97
+ "types": "./src/results/pagination.tsx",
98
+ "import": "./src/results/pagination.tsx"
95
99
  },
96
- "./page-wrapper": {
97
- "types": "./src/page-wrapper.tsx",
98
- "import": "./src/page-wrapper.tsx"
100
+ "./cards": {
101
+ "types": "./src/results/cards.tsx",
102
+ "import": "./src/results/cards.tsx"
99
103
  },
100
- "./search-modal": {
101
- "types": "./src/search-modal.tsx",
102
- "import": "./src/search-modal.tsx"
104
+ "./empty": {
105
+ "types": "./src/results/empty.tsx",
106
+ "import": "./src/results/empty.tsx"
103
107
  },
104
- "./loading": {
105
- "types": "./src/loading.tsx",
106
- "import": "./src/loading.tsx"
108
+ "./filter-sidebar": {
109
+ "types": "./src/results/filter-sidebar/index.tsx",
110
+ "import": "./src/results/filter-sidebar/index.tsx"
107
111
  },
108
- "./favorite-button": {
109
- "types": "./src/favorite-button.tsx",
110
- "import": "./src/favorite-button.tsx"
112
+ "./filter-navbar": {
113
+ "types": "./src/results/filter-navbar.tsx",
114
+ "import": "./src/results/filter-navbar.tsx"
111
115
  },
112
116
  "./language-store": {
113
117
  "types": "./src/stores/language-store.ts",
@@ -120,6 +124,18 @@
120
124
  "./favorites-store": {
121
125
  "types": "./src/stores/favorites-store.ts",
122
126
  "import": "./src/stores/favorites-store.ts"
127
+ },
128
+ "./article-content": {
129
+ "types": "./src/article/article-content.tsx",
130
+ "import": "./src/article/article-content.tsx"
131
+ },
132
+ "./tree-of-content": {
133
+ "types": "./src/directoryNodes/tree-of-content.tsx",
134
+ "import": "./src/directoryNodes/tree-of-content.tsx"
135
+ },
136
+ "./share-button": {
137
+ "types": "./src/share-button.tsx",
138
+ "import": "./src/share-button.tsx"
123
139
  }
124
140
  },
125
141
  "scripts": {
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { FC, useEffect, useRef, useState } from "react";
3
+ import { useHighlightStore } from "../stores/highlight-store";
4
+ import { Button } from "@c-rex/ui/button";
5
+ import { ArrowBigLeft, ArrowBigRight, FileSearchIcon, PanelRight, Search, X } from "lucide-react";
6
+ import { useQueryState } from "nuqs";
7
+ import { useHighlight } from "@c-rex/contexts/highlight-provider";
8
+ import { useMultiSidebar } from "@c-rex/ui/sidebar";
9
+ import { cn } from "@c-rex/utils";
10
+ import { useTranslations } from "next-intl";
11
+
12
+ export const ArticleActionBar: FC = () => {
13
+ const t = useTranslations();
14
+ const inputRef = useRef<HTMLInputElement>(null);
15
+ const { next, prev } = useHighlight();
16
+ const [open, setOpen] = useState(false);
17
+ const enableHighlight = useHighlightStore((state) => state.enable);
18
+ const toggleHighlight = useHighlightStore((state) => state.toggleHighlight);
19
+ const { rightSidebar } = useMultiSidebar()
20
+ const [query, setQuery] = useQueryState("q");
21
+
22
+ useEffect(() => {
23
+ if (open && inputRef.current) {
24
+ inputRef.current.focus();
25
+ }
26
+ }, [open]);
27
+
28
+ return (
29
+ <>
30
+ <div className="w-full sm:w-auto justify-between bg-primary text-primary-foreground rounded-full shadow-lg flex sticky bottom-4 p-2 float-right gap-2 transition-all duration-300">
31
+ {enableHighlight && (
32
+
33
+ <>
34
+ <div className="flex items-center">
35
+ <input
36
+ type="text"
37
+ value={query as string || ""}
38
+ ref={inputRef}
39
+ onKeyDown={(e) => {
40
+ if (e.key === "Enter") {
41
+ next();
42
+ }
43
+ if (e.key === "Escape") {
44
+ setOpen(false);
45
+ }
46
+ }}
47
+ onChange={(e) => setQuery(e.target.value || null)}
48
+ placeholder={t("search")}
49
+ className={cn(
50
+ "border border-gray-300 left-12 transition-all duration-300 rounded-full h-9 bg-secondary text-secondary-foreground focus:outline-none focus:ring-2 focus:ring-blue-500",
51
+ open ? "w-48 opacity-100 px-2 mr-2" : "w-0 opacity-0 px-0"
52
+ )}
53
+ />
54
+
55
+ <Button
56
+ variant="ghost" size="icon" rounded="full" onClick={() => setOpen(!open)}
57
+ >
58
+ {open ? <X className="w-5 h-5" /> : <Search className="w-5 h-5" />}
59
+ </Button>
60
+
61
+ </div>
62
+
63
+ <Button variant="ghost" size="icon" rounded="full" onClick={prev}>
64
+ <ArrowBigLeft />
65
+ </Button>
66
+ <Button variant="ghost" size="icon" rounded="full" onClick={next}>
67
+
68
+ <ArrowBigRight />
69
+ </Button>
70
+ </>
71
+ )}
72
+
73
+ <Button variant="ghost" size="icon" rounded="full" onClick={() => toggleHighlight(!enableHighlight)} className="group">
74
+ {enableHighlight ?
75
+ <FileSearchIcon /> :
76
+ <div className="relative inline-block">
77
+ <FileSearchIcon />
78
+ <span className="absolute inset-0 w-[2px] bg-primary-foreground rotate-45 translate-x-2 group-hover:bg-accent-foreground" />
79
+ </div>
80
+ }
81
+ </Button>
82
+
83
+ <Button variant="ghost" size="icon" rounded="full" onClick={rightSidebar.toggleSidebar}>
84
+ <PanelRight />
85
+ </Button>
86
+ </div>
87
+ </>
88
+ )
89
+ }
@@ -0,0 +1,55 @@
1
+ import { FC } from "react";
2
+ import { RenditionModel } from "@c-rex/interfaces";
3
+ import { RenderArticle } from "../render-article";
4
+ import { ArticleActionBar } from "./article-action-bar";
5
+ import * as cheerio from "cheerio"
6
+
7
+ interface Props {
8
+ renditions: RenditionModel[] | null | undefined;
9
+ }
10
+
11
+ export const ArticleContent: FC<Props> = async ({ renditions }) => {
12
+ const empty = (
13
+ <div>No HTML Rendition Available</div>
14
+ )
15
+
16
+ if (renditions == null || renditions.length == 0) return empty;
17
+
18
+ const filteredRenditions = renditions.filter((item) => item.format == "application/xhtml+xml");
19
+
20
+ if (filteredRenditions.length == 0 || filteredRenditions[0] == undefined || filteredRenditions[0].links == undefined) return empty;
21
+
22
+ const filteredLinks = filteredRenditions[0].links.filter((item) => item.rel == "view");
23
+
24
+ if (filteredLinks.length == 0 || filteredLinks[0] == undefined || filteredLinks[0].href == undefined) return empty;
25
+
26
+ const url = filteredLinks[0].href;
27
+ const html = await fetch(url, {
28
+ method: "GET",
29
+ headers: {
30
+ Accept: "application/xhtml+xml",
31
+ },
32
+ }).then(res => res.text());
33
+
34
+ const $ = cheerio.load(html)
35
+
36
+ const metaTags = $("meta").map((_, el) => {
37
+ const name = $(el).attr("name")
38
+ const content = $(el).attr("content")
39
+ return name && content ? { name, content } : null
40
+ }).get().filter(Boolean)
41
+
42
+ const articleHtml = $("main").html() || ""
43
+
44
+ return (
45
+ <>
46
+ {metaTags.map((tag, index) => (
47
+ <meta key={index} name={tag.name} content={tag.content} />
48
+ ))}
49
+ <div className="pr-4 relative">
50
+ <RenderArticle htmlContent={articleHtml} contentLang="" />
51
+ </div>
52
+ <ArticleActionBar />
53
+ </>
54
+ )
55
+ }
@@ -1,55 +1,68 @@
1
1
  "use client"
2
2
 
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { Input } from "@c-rex/ui/input";
5
- import { call, generateQueryParams } from "@c-rex/utils";
6
5
  import { useTranslations } from "next-intl";
7
- import { useAppConfig } from "@c-rex/contexts/config-provider";
8
- import { QueryParams } from "@c-rex/types";
6
+ import { SuggestionQueryParams } from "@c-rex/interfaces";
7
+
9
8
  import { useSearchSettingsStore } from "./stores/search-settings-store";
10
9
  import { useLanguageStore } from "./stores/language-store";
11
-
12
- type Props = {
13
- initialValue: string;
14
- embedded: boolean
15
- searchByPackage: boolean
10
+ import { useRouter } from 'next/navigation';
11
+ import { suggestionRequest } from "./generated/create-suggestions-request";
12
+
13
+ export type AutoCompleteProps = {
14
+ initialValue?: string;
15
+ embedded?: boolean;
16
+ endpoint: string;
17
+ queryParams?: SuggestionQueryParams
18
+ onSelectParams?: { key: string, value: string }[];
16
19
  };
17
20
 
18
- export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props) => {
21
+ export const AutoComplete = ({
22
+ initialValue = "",
23
+ embedded = true,
24
+ endpoint,
25
+ queryParams,
26
+ onSelectParams,
27
+ }: AutoCompleteProps) => {
19
28
  const t = useTranslations();
20
29
  const containerRef = useRef<HTMLDivElement>(null);
21
- const contentLang = useLanguageStore.getState().contentLang;
22
-
23
- const { articleLang, packageID } = useAppConfig()
24
-
30
+ const router = useRouter();
25
31
  const [open, setOpen] = useState(false);
26
32
  const [query, setQuery] = useState(initialValue);
27
33
  const [loading, setLoading] = useState(false);
28
34
  const [suggestions, setSuggestions] = useState<string[]>([]);
29
35
 
30
- const onSearch = (value: string): Promise<string[]> => {
31
- return call<string[]>("InformationUnitsService.getSuggestions", { query: value, language: articleLang || contentLang });
32
- }
33
-
34
- const onSelect = (value: string) => {
35
- const params: QueryParams[] = [
36
- { key: "search", value: value },
37
- { key: "page", value: "1" },
38
- { key: "operator", value: useSearchSettingsStore.getState().operator },
36
+ const fetchSuggestions = useCallback(async (prefix: string): Promise<string[]> => {
37
+ const results = await suggestionRequest({ endpoint, prefix, queryParams });
39
38
 
40
- { key: "language", value: useLanguageStore.getState().contentLang },
39
+ return results.data;
40
+ }, [endpoint, queryParams]);
41
41
 
42
- { key: "wildcard", value: useSearchSettingsStore.getState().wildcard },
43
- { key: "like", value: useSearchSettingsStore.getState().like.toString() },
44
- ]
45
-
46
- if (searchByPackage && packageID !== null) {
47
- params.push({ key: "packages", value: packageID })
48
- }
49
-
50
- const aux = generateQueryParams(params)
42
+ const handleSelect = (value: string) => {
43
+ setQuery(value);
44
+ setOpen(false);
45
+ onSelect(value);
46
+ };
51
47
 
52
- window.location.href = `${window.location.origin}/?${aux}`
48
+ const onSelect = (value: string) => {
49
+ const params = new URLSearchParams();
50
+ const searchSettings = useSearchSettingsStore.getState();
51
+ const languageSettings = useLanguageStore.getState();
52
+
53
+ params.set('search', value);
54
+ params.set("page", "1")
55
+ params.set("operator", searchSettings.operator)
56
+ params.set("language",
57
+ searchSettings.language.length > 0 ? searchSettings.language.join(",") : languageSettings.contentLang)
58
+ params.set("wildcard", searchSettings.wildcard)
59
+ params.set("like", searchSettings.like.toString())
60
+
61
+ onSelectParams?.forEach(param => {
62
+ params.set(param.key, param.value);
63
+ });
64
+
65
+ router.push(`/?${params.toString()}`);
53
66
  };
54
67
 
55
68
  useEffect(() => {
@@ -73,18 +86,15 @@ export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props)
73
86
  }
74
87
 
75
88
  const debounceFetch = setTimeout(() => {
76
- setLoading(true)
77
- onSearch(query).then(suggestions => {
78
- setSuggestions(suggestions)
79
- setLoading(false)
80
- }).catch(() => {
81
- setLoading(false)
82
- setSuggestions([])
83
- });
89
+ setLoading(true);
90
+ fetchSuggestions(query)
91
+ .then(setSuggestions)
92
+ .catch(() => setSuggestions([]))
93
+ .finally(() => setLoading(false));
84
94
  }, 300);
85
95
 
86
96
  return () => clearTimeout(debounceFetch);
87
- }, [query]);
97
+ }, [query, fetchSuggestions]);
88
98
 
89
99
  return (
90
100
  <div className="relative flex-1" ref={containerRef}>
@@ -100,8 +110,7 @@ export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props)
100
110
  onKeyDown={(e) => {
101
111
  if (e.key === "Enter") {
102
112
  e.preventDefault();
103
- onSelect(query);
104
- setOpen(false)
113
+ handleSelect(query);
105
114
  }
106
115
  }}
107
116
  />
@@ -122,11 +131,7 @@ export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props)
122
131
  <li
123
132
  key={index}
124
133
  className="px-4 py-2 hover:bg-accent cursor-pointer text-sm"
125
- onClick={() => {
126
- setQuery(option);
127
- setOpen(false);
128
- onSelect(`"${option}"`);
129
- }}
134
+ onClick={() => handleSelect(`"${option}"`)}
130
135
  >
131
136
  {option}
132
137
  </li>
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import React, { FC, Fragment, ReactNode } from "react";
2
4
  import {
3
5
  Breadcrumb as BreadcrumbComponent,
@@ -74,7 +76,7 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
74
76
  {items.slice(0, -1).map((item, index) => (
75
77
  <Link
76
78
  key={index}
77
- href={item.link}
79
+ href={item.link!}
78
80
  className="py-1 text-sm"
79
81
  >
80
82
  {item.label}
@@ -0,0 +1,49 @@
1
+ import {
2
+ SidebarMenu,
3
+ SidebarMenuButton,
4
+ SidebarMenuItem,
5
+ SidebarMenuSub,
6
+ SidebarMenuSubButton,
7
+ SidebarMenuSubItem,
8
+ } from "@c-rex/ui/sidebar";
9
+ import { DirectoryNodeModel } from "@c-rex/interfaces";
10
+ import { generateTreeOfContent } from "@c-rex/utils/directoryNodes";
11
+
12
+ interface SidebarProps {
13
+ directoryNodes: DirectoryNodeModel[];
14
+ }
15
+
16
+ export async function TreeOfContent({ directoryNodes }: SidebarProps) {
17
+ const tree = await generateTreeOfContent(directoryNodes);
18
+
19
+ return (
20
+ <SidebarMenu>
21
+ {tree.map((item) => (
22
+ <SidebarMenuItem key={item.id}>
23
+ <SidebarMenuButton asChild isActive={item.active}>
24
+ <a href={item.link} title={item.label}>
25
+ {item.label}
26
+ </a>
27
+ </SidebarMenuButton>
28
+
29
+ {item.children?.length ? (
30
+ <SidebarMenuSub>
31
+ {item.children.map((item) => (
32
+ <SidebarMenuSubItem key={item.label}>
33
+ <SidebarMenuSubButton
34
+ asChild
35
+ isActive={item.active}
36
+ >
37
+ <a href={item.link} title={item.label}>
38
+ {item.label}
39
+ </a>
40
+ </SidebarMenuSubButton>
41
+ </SidebarMenuSubItem>
42
+ ))}
43
+ </SidebarMenuSub>
44
+ ) : null}
45
+ </SidebarMenuItem>
46
+ ))}
47
+ </SidebarMenu>
48
+ );
49
+ }
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
 
3
- import { FC, ReactNode } from "react";
3
+ import { FC, ReactNode, useEffect, useState } from "react";
4
4
  import { Button } from "@c-rex/ui/button";
5
5
  import { Trash } from "lucide-react";
6
6
  import { cn } from "@c-rex/utils";
7
7
  import Link from "next/link";
8
- import { Favorite } from "@c-rex/types";
8
+ import { useFavoritesStore } from "../stores/favorites-store";
9
9
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@c-rex/ui/dialog";
10
10
  import {
11
11
  Table,
@@ -15,7 +15,16 @@ import {
15
15
  } from "@c-rex/ui/table"
16
16
  import { FaRegBookmark } from "react-icons/fa6";
17
17
 
18
- export const BookmarkButton: FC<{ markersList: Favorite[], trigger?: ReactNode }> = ({ markersList, trigger }) => {
18
+ export const BookmarkButton: FC<{ shortId: string, trigger?: ReactNode }> = ({ shortId, trigger }) => {
19
+ const [markersList, setMarkersList] = useState<Array<{ id: string; label: string; color: string }>>([]);
20
+
21
+ useEffect(() => {
22
+ setMarkersList(useFavoritesStore.getState().documents[shortId]?.topics || []);
23
+ }, [shortId]);
24
+
25
+ useEffect(() => {
26
+ console.log(shortId, markersList);
27
+ }, [markersList]);
19
28
 
20
29
  return (
21
30
  <Dialog>
@@ -3,7 +3,7 @@
3
3
  import { FC, useEffect, useState } from "react";
4
4
  import { Button } from "@c-rex/ui/button";
5
5
  import { FaStar, FaRegStar } from "react-icons/fa";
6
- import { useFavoritesStore } from "./stores/favorites-store";
6
+ import { useFavoritesStore } from "../stores/favorites-store";
7
7
  import { MARKER_COLORS, RESULT_TYPES } from "@c-rex/constants";
8
8
  import { ResultTypes } from "@c-rex/types";
9
9