@equinor/fusion-framework-react-components-bookmark 2.0.0 → 2.0.2

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 (108) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +69 -0
  3. package/dist/esm/components/Bookmark.js +12 -2
  4. package/dist/esm/components/Bookmark.js.map +1 -1
  5. package/dist/esm/components/BookmarkProvider.js +16 -0
  6. package/dist/esm/components/BookmarkProvider.js.map +1 -1
  7. package/dist/esm/components/create-bookmark/{CreateBookmark.js → CreateBookmarkModal.js} +13 -5
  8. package/dist/esm/components/create-bookmark/CreateBookmarkModal.js.map +1 -0
  9. package/dist/esm/components/create-bookmark/index.js +1 -1
  10. package/dist/esm/components/create-bookmark/index.js.map +1 -1
  11. package/dist/esm/components/edit-bookmark/{EditBookmark.js → EditBookmarkModal.js} +16 -4
  12. package/dist/esm/components/edit-bookmark/EditBookmarkModal.js.map +1 -0
  13. package/dist/esm/components/edit-bookmark/index.js +1 -1
  14. package/dist/esm/components/edit-bookmark/index.js.map +1 -1
  15. package/dist/esm/components/filter/{Filter.js → BookmarkFilter.js} +9 -3
  16. package/dist/esm/components/filter/BookmarkFilter.js.map +1 -0
  17. package/dist/esm/components/import-bookmark/{ImportBookmark.js → ImportBookmarkModal.js} +13 -1
  18. package/dist/esm/components/import-bookmark/ImportBookmarkModal.js.map +1 -0
  19. package/dist/esm/components/import-bookmark/index.js +1 -1
  20. package/dist/esm/components/import-bookmark/index.js.map +1 -1
  21. package/dist/esm/components/loading/Loading.js +5 -0
  22. package/dist/esm/components/loading/Loading.js.map +1 -1
  23. package/dist/esm/components/messages/Message.js +7 -0
  24. package/dist/esm/components/messages/Message.js.map +1 -1
  25. package/dist/esm/components/row/MoreMenu.js +15 -7
  26. package/dist/esm/components/row/MoreMenu.js.map +1 -1
  27. package/dist/esm/components/row/Row.js +9 -4
  28. package/dist/esm/components/row/Row.js.map +1 -1
  29. package/dist/esm/components/section/Section.js +6 -0
  30. package/dist/esm/components/section/Section.js.map +1 -1
  31. package/dist/esm/components/sectionList/SectionList.js +16 -5
  32. package/dist/esm/components/sectionList/SectionList.js.map +1 -1
  33. package/dist/esm/components/shared/SharedIcon.js +7 -1
  34. package/dist/esm/components/shared/SharedIcon.js.map +1 -1
  35. package/dist/esm/hooks/useBookmarkGrouping.js +23 -10
  36. package/dist/esm/hooks/useBookmarkGrouping.js.map +1 -1
  37. package/dist/esm/index.js +10 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/utils/{append-bookmark-to-uri.js → append-bookmark-id-to-url.js} +1 -1
  40. package/dist/esm/utils/append-bookmark-id-to-url.js.map +1 -0
  41. package/dist/esm/utils/filter-empty-groups.js +4 -0
  42. package/dist/esm/utils/filter-empty-groups.js.map +1 -0
  43. package/dist/esm/utils/sort-by-name.js +2 -0
  44. package/dist/esm/utils/sort-by-name.js.map +1 -0
  45. package/dist/esm/utils/to-human-readable.js +6 -0
  46. package/dist/esm/utils/to-human-readable.js.map +1 -0
  47. package/dist/esm/version.js +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/dist/types/components/Bookmark.d.ts +6 -1
  50. package/dist/types/components/BookmarkProvider.d.ts +16 -1
  51. package/dist/types/components/create-bookmark/CreateBookmarkModal.d.ts +12 -0
  52. package/dist/types/components/create-bookmark/index.d.ts +1 -1
  53. package/dist/types/components/edit-bookmark/EditBookmarkModal.d.ts +14 -0
  54. package/dist/types/components/edit-bookmark/index.d.ts +1 -1
  55. package/dist/types/components/filter/BookmarkFilter.d.ts +16 -0
  56. package/dist/types/components/import-bookmark/ImportBookmarkModal.d.ts +6 -0
  57. package/dist/types/components/import-bookmark/index.d.ts +1 -1
  58. package/dist/types/components/loading/Loading.d.ts +6 -1
  59. package/dist/types/components/messages/Message.d.ts +7 -1
  60. package/dist/types/components/row/MoreMenu.d.ts +7 -1
  61. package/dist/types/components/row/Row.d.ts +7 -1
  62. package/dist/types/components/section/Section.d.ts +7 -1
  63. package/dist/types/components/sectionList/SectionList.d.ts +7 -1
  64. package/dist/types/components/shared/SharedIcon.d.ts +7 -1
  65. package/dist/types/hooks/useBookmarkGrouping.d.ts +6 -0
  66. package/dist/types/index.d.ts +10 -0
  67. package/dist/types/utils/filter-empty-groups.d.ts +5 -0
  68. package/dist/types/utils/sort-by-name.d.ts +2 -0
  69. package/dist/types/utils/to-human-readable.d.ts +1 -0
  70. package/dist/types/version.d.ts +1 -1
  71. package/package.json +8 -8
  72. package/src/components/Bookmark.tsx +12 -5
  73. package/src/components/BookmarkProvider.tsx +16 -0
  74. package/src/components/create-bookmark/{CreateBookmark.tsx → CreateBookmarkModal.tsx} +12 -4
  75. package/src/components/create-bookmark/index.ts +1 -1
  76. package/src/components/edit-bookmark/{EditBookmark.tsx → EditBookmarkModal.tsx} +16 -4
  77. package/src/components/edit-bookmark/index.ts +1 -1
  78. package/src/components/filter/{Filter.tsx → BookmarkFilter.tsx} +11 -5
  79. package/src/components/import-bookmark/{ImportBookmark.tsx → ImportBookmarkModal.tsx} +12 -0
  80. package/src/components/import-bookmark/index.ts +1 -1
  81. package/src/components/loading/Loading.tsx +5 -1
  82. package/src/components/messages/Message.tsx +7 -0
  83. package/src/components/row/MoreMenu.tsx +24 -17
  84. package/src/components/row/Row.tsx +8 -1
  85. package/src/components/section/Section.tsx +6 -0
  86. package/src/components/sectionList/SectionList.tsx +16 -5
  87. package/src/components/shared/SharedIcon.tsx +6 -0
  88. package/src/hooks/useBookmarkGrouping.ts +29 -18
  89. package/src/index.ts +10 -0
  90. package/src/utils/filter-empty-groups.ts +5 -0
  91. package/src/utils/sort-by-name.ts +4 -0
  92. package/src/utils/to-human-readable.ts +6 -0
  93. package/src/version.ts +1 -1
  94. package/dist/esm/components/create-bookmark/CreateBookmark.js.map +0 -1
  95. package/dist/esm/components/edit-bookmark/EditBookmark.js.map +0 -1
  96. package/dist/esm/components/filter/Filter.js.map +0 -1
  97. package/dist/esm/components/import-bookmark/ImportBookmark.js.map +0 -1
  98. package/dist/esm/utils/append-bookmark-to-uri.js.map +0 -1
  99. package/dist/esm/utils/utils.js +0 -9
  100. package/dist/esm/utils/utils.js.map +0 -1
  101. package/dist/types/components/create-bookmark/CreateBookmark.d.ts +0 -4
  102. package/dist/types/components/edit-bookmark/EditBookmark.d.ts +0 -5
  103. package/dist/types/components/filter/Filter.d.ts +0 -10
  104. package/dist/types/components/import-bookmark/ImportBookmark.d.ts +0 -1
  105. package/dist/types/utils/utils.d.ts +0 -7
  106. package/src/utils/utils.ts +0 -13
  107. /package/dist/types/utils/{append-bookmark-to-uri.d.ts → append-bookmark-id-to-url.d.ts} +0 -0
  108. /package/src/utils/{append-bookmark-to-uri.ts → append-bookmark-id-to-url.ts} +0 -0
@@ -1,2 +1,7 @@
1
- export declare const Bookmark: () => import("react/jsx-runtime").JSX.Element;
1
+ /**
2
+ * Renders the list of bookmark sections and the current bookmark's loading/message state.
3
+ *
4
+ * @returns The bookmark list UI
5
+ */
6
+ export declare const Bookmark: () => import("react").JSX.Element;
2
7
  export default Bookmark;
@@ -22,8 +22,23 @@ type BookmarkProviderProps = {
22
22
  readonly currentApp?: BookmarkApp;
23
23
  readonly currentUser?: BookmarkUser;
24
24
  };
25
+ /**
26
+ * Hook for accessing the bookmark component context provided by `BookmarkProvider`.
27
+ *
28
+ * @returns The current bookmark provider state
29
+ */
25
30
  export declare const useBookmarkComponentContext: () => ProviderState;
26
- export declare const BookmarkProvider: (props: PropsWithChildren<BookmarkProviderProps>) => import("react/jsx-runtime").JSX.Element;
31
+ /**
32
+ * Provides bookmark state and modals (create/edit/import) to descendant bookmark components.
33
+ *
34
+ * @param props - The provider's props
35
+ * @param props.provider - The bookmark provider used to fetch/manage bookmarks
36
+ * @param props.currentApp - The app the bookmarks belong to
37
+ * @param props.currentUser - The current user, used to determine bookmark ownership
38
+ * @param props.children - The descendant components that can access the bookmark context
39
+ * @returns The provider with its context and modals
40
+ */
41
+ export declare const BookmarkProvider: (props: PropsWithChildren<BookmarkProviderProps>) => import("react").JSX.Element;
27
42
  declare module '@equinor/fusion-framework-module-event' {
28
43
  interface FrameworkEventMap {
29
44
  onBookmarkEdit: FrameworkEvent<FrameworkEventInit<{
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Modal for creating a new bookmark.
3
+ *
4
+ * @param props - The component's props
5
+ * @param props.isOpen - Whether the modal is open
6
+ * @param props.onClose - Callback invoked to close the modal
7
+ * @returns The create bookmark modal
8
+ */
9
+ export declare const CreateBookmarkModal: ({ isOpen, onClose, }: {
10
+ readonly isOpen: boolean;
11
+ readonly onClose: (b: boolean) => void;
12
+ }) => import("react").JSX.Element;
@@ -1 +1 @@
1
- export { CreateBookmarkModal } from './CreateBookmark';
1
+ export { CreateBookmarkModal } from './CreateBookmarkModal';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Modal for editing an existing bookmark.
3
+ *
4
+ * @param props - The component's props
5
+ * @param props.isOpen - Whether the modal is open
6
+ * @param props.onClose - Callback invoked to close the modal
7
+ * @param props.bookmarkId - The id of the bookmark to edit
8
+ * @returns The edit bookmark modal
9
+ */
10
+ export declare const EditBookmarkModal: ({ isOpen, onClose, bookmarkId, }: {
11
+ readonly isOpen: boolean;
12
+ readonly onClose: (b: boolean) => void;
13
+ readonly bookmarkId: string;
14
+ }) => import("react").JSX.Element;
@@ -1 +1 @@
1
- export * from './EditBookmark';
1
+ export * from './EditBookmarkModal';
@@ -0,0 +1,16 @@
1
+ import type { GroupingKeys } from '../../hooks/useBookmarkGrouping';
2
+ type BookmarkFilterProps = {
3
+ readonly searchText: string;
4
+ readonly setSearchText: (newVal: string | null) => void;
5
+ readonly setGroupBy: (groupBy: GroupingKeys) => void;
6
+ readonly groupingModes: string[];
7
+ readonly groupBy: string;
8
+ };
9
+ /**
10
+ * Search and grouping filter controls for the bookmark list.
11
+ *
12
+ * @param props - The component's props
13
+ * @returns The filter controls
14
+ */
15
+ export declare const BookmarkFilter: ({ searchText, setGroupBy: _setGroupBy, setSearchText, groupingModes: _groupingModes, groupBy: _groupBy, }: BookmarkFilterProps) => import("react").JSX.Element;
16
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Modal that prompts the user to import a bookmark shared via a `bookmarkId` URL parameter.
3
+ *
4
+ * @returns The import bookmark modal
5
+ */
6
+ export declare const ImportBookmarkModal: () => import("react").JSX.Element | null;
@@ -1 +1 @@
1
- export * from './ImportBookmark';
1
+ export * from './ImportBookmarkModal';
@@ -1 +1,6 @@
1
- export declare const Loading: () => import("react/jsx-runtime").JSX.Element;
1
+ /**
2
+ * A centered loading spinner.
3
+ *
4
+ * @returns The loading indicator
5
+ */
6
+ export declare const Loading: () => import("react").JSX.Element;
@@ -6,5 +6,11 @@ type MessageProps = {
6
6
  readonly type?: PortalMessageType;
7
7
  readonly color?: string;
8
8
  };
9
- export declare const Message: ({ title, type, color, children, }: PropsWithChildren<MessageProps>) => import("react/jsx-runtime").JSX.Element;
9
+ /**
10
+ * Displays a titled message with an icon matching its type.
11
+ *
12
+ * @param props - The component's props
13
+ * @returns The message
14
+ */
15
+ export declare const Message: ({ title, type, color, children, }: PropsWithChildren<MessageProps>) => import("react").JSX.Element;
10
16
  export {};
@@ -6,5 +6,11 @@ type MenuProps = {
6
6
  readonly open: boolean;
7
7
  readonly options: MenuOption[];
8
8
  };
9
- export declare const MoreMenu: ({ pRef, onClose, open, options }: MenuProps) => import("react/jsx-runtime").JSX.Element;
9
+ /**
10
+ * A dropdown menu of row actions anchored to a reference element.
11
+ *
12
+ * @param props - The component's props
13
+ * @returns The menu
14
+ */
15
+ export declare const MoreMenu: ({ pRef, onClose, open, options }: MenuProps) => import("react").JSX.Element;
10
16
  export {};
@@ -13,5 +13,11 @@ type RowProps = {
13
13
  readonly menuOptions: MenuOption[];
14
14
  readonly children?: ReactNode;
15
15
  };
16
- export declare const Row: ({ name, menuOptions, children, id, menuOpen, onMenuOpen }: RowProps) => import("react/jsx-runtime").JSX.Element;
16
+ /**
17
+ * A single bookmark row, with actions and click-to-set-current behavior.
18
+ *
19
+ * @param props - The component's props
20
+ * @returns The row
21
+ */
22
+ export declare const Row: ({ name, menuOptions, children, id, menuOpen, onMenuOpen }: RowProps) => import("react").JSX.Element;
17
23
  export {};
@@ -3,5 +3,11 @@ type SectionProps = {
3
3
  readonly name: string;
4
4
  readonly children: ReactNode;
5
5
  };
6
- export declare const Section: ({ children, name }: SectionProps) => import("react/jsx-runtime").JSX.Element;
6
+ /**
7
+ * A collapsible section with a named header and its contents.
8
+ *
9
+ * @param props - The component's props
10
+ * @returns The section
11
+ */
12
+ export declare const Section: ({ children, name }: SectionProps) => import("react").JSX.Element;
7
13
  export {};
@@ -2,5 +2,11 @@ import type { useBookmarkGrouping } from '../../hooks';
2
2
  type SectionListProps = {
3
3
  readonly bookmarkGroups: ReturnType<typeof useBookmarkGrouping>['bookmarkGroups'];
4
4
  };
5
- export declare const SectionList: ({ bookmarkGroups }: SectionListProps) => import("react/jsx-runtime").JSX.Element;
5
+ /**
6
+ * Renders bookmark groups as sections of rows with edit/share/delete actions.
7
+ *
8
+ * @param props - The component's props
9
+ * @returns The section list
10
+ */
11
+ export declare const SectionList: ({ bookmarkGroups }: SectionListProps) => import("react").JSX.Element;
6
12
  export {};
@@ -2,5 +2,11 @@ type SharedIconProps = {
2
2
  readonly createdBy: string;
3
3
  readonly createdById: string;
4
4
  };
5
- export declare const SharedIcon: ({ createdBy, createdById }: SharedIconProps) => import("react/jsx-runtime").JSX.Element;
5
+ /**
6
+ * Displays a "shared by" indicator when a bookmark was created by another user.
7
+ *
8
+ * @param props - The component's props
9
+ * @returns The shared icon, or nothing when the current user created the bookmark
10
+ */
11
+ export declare const SharedIcon: ({ createdBy, createdById }: SharedIconProps) => import("react").JSX.Element;
6
12
  export {};
@@ -1,5 +1,11 @@
1
1
  import type { Bookmarks, BookmarkWithoutData } from '@equinor/fusion-framework-module-bookmark';
2
2
  export type GroupingKeys = 'Group by app' | 'Created by' | 'Group by Context';
3
+ /**
4
+ * Hook for grouping and searching bookmarks by app, creator, or context.
5
+ *
6
+ * @param bookmarks - The bookmarks to group and search
7
+ * @returns The grouped bookmarks along with the search text and grouping key state/setters
8
+ */
3
9
  export declare const useBookmarkGrouping: (bookmarks?: Bookmarks) => {
4
10
  setGroupBy: import("react").Dispatch<import("react").SetStateAction<GroupingKeys>>;
5
11
  setSearchText: import("react").Dispatch<import("react").SetStateAction<string | null>>;
@@ -1,2 +1,12 @@
1
+ /**
2
+ * Pre-built React UI components for bookmark management.
3
+ *
4
+ * Provides the {@link Bookmark} list component and {@link BookmarkProvider}
5
+ * context wrapper with built-in create, edit, and import modals plus filtering controls.
6
+ *
7
+ * For bookmark hooks without UI, see `@equinor/fusion-framework-react-app/bookmark`.
8
+ *
9
+ * @packageDocumentation
10
+ */
1
11
  export { Bookmark } from './components/Bookmark';
2
12
  export { BookmarkProvider, useBookmarkComponentContext } from './components/BookmarkProvider';
@@ -0,0 +1,5 @@
1
+ import type { Bookmarks } from '@equinor/fusion-framework-module-bookmark';
2
+ /** Helpers.. */
3
+ export declare const filterEmptyGroups: (i: {
4
+ values: Bookmarks;
5
+ }) => number;
@@ -0,0 +1,2 @@
1
+ import type { BookmarkWithoutData } from '@equinor/fusion-framework-module-bookmark';
2
+ export declare const sortByName: (a: BookmarkWithoutData, b: BookmarkWithoutData) => number;
@@ -0,0 +1 @@
1
+ export declare const toHumanReadable: (val: string) => string;
@@ -1 +1 @@
1
- export declare const version = "2.0.0";
1
+ export declare const version = "2.0.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-react-components-bookmark",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "",
5
5
  "main": "dist/esm/index.js",
6
6
  "exports": {
@@ -23,9 +23,9 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "rxjs": "^7.8.1",
26
- "@equinor/fusion-framework-react": "^8.0.0",
27
- "@equinor/fusion-observable": "^9.0.0",
28
- "@equinor/fusion-framework-react-module-bookmark": "^6.0.0"
26
+ "@equinor/fusion-framework-react-module-bookmark": "^6.0.1",
27
+ "@equinor/fusion-observable": "^9.1.1",
28
+ "@equinor/fusion-framework-react": "^8.0.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@equinor/eds-core-react": "^2.3.7",
@@ -35,10 +35,10 @@
35
35
  "@types/react": "^19.2.7",
36
36
  "react": "^19.2.1",
37
37
  "styled-components": "^6.3.11",
38
- "typescript": "^5.9.3",
39
- "@equinor/fusion-framework-module-app": "^8.0.0",
40
- "@equinor/fusion-framework-module-event": "^6.0.0",
41
- "@equinor/fusion-framework-module-bookmark": "^4.0.0"
38
+ "typescript": "^7.0.2",
39
+ "@equinor/fusion-framework-module-bookmark": "^4.0.3",
40
+ "@equinor/fusion-framework-module-event": "^6.0.1",
41
+ "@equinor/fusion-framework-module-app": "^8.0.3"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@equinor/eds-core-react": "^2.0.0",
@@ -1,6 +1,6 @@
1
1
  import { EMPTY, map } from 'rxjs';
2
2
  import { useBookmarkGrouping } from '../hooks';
3
- import { BookmarkFilter } from './filter/Filter';
3
+ import { BookmarkFilter } from './filter/BookmarkFilter';
4
4
  import { SectionList } from './sectionList/SectionList';
5
5
  import { useMemo } from 'react';
6
6
 
@@ -42,6 +42,11 @@ const Styled = {
42
42
  `,
43
43
  };
44
44
 
45
+ /**
46
+ * Renders the list of bookmark sections and the current bookmark's loading/message state.
47
+ *
48
+ * @returns The bookmark list UI
49
+ */
45
50
  export const Bookmark = () => {
46
51
  const { provider } = useBookmarkComponentContext();
47
52
 
@@ -50,10 +55,10 @@ export const Bookmark = () => {
50
55
  );
51
56
 
52
57
  const { value: isLoading } = useObservableState(
53
- useMemo(
54
- () => (provider?.status$ || EMPTY).pipe(map((status) => !!status.has('fetch_bookmarks'))),
55
- [provider],
56
- ),
58
+ useMemo(() => {
59
+ // Derive a boolean loading flag from whether 'fetch_bookmarks' is an active status
60
+ return (provider?.status$ || EMPTY).pipe(map((status) => !!status.has('fetch_bookmarks')));
61
+ }, [provider]),
57
62
  { initial: true },
58
63
  );
59
64
 
@@ -61,9 +66,11 @@ export const Bookmark = () => {
61
66
  useBookmarkGrouping(bookmarks);
62
67
 
63
68
  const content = useMemo(() => {
69
+ // Show the loading placeholder while bookmarks are still being fetched
64
70
  if (isLoading) {
65
71
  return <Loading />;
66
72
  }
73
+ // Show an empty-state message when there are no bookmark groups to display
67
74
  if (bookmarkGroups.length === 0) {
68
75
  return (
69
76
  <Message title="No Bookmarks" type="NoContent">
@@ -33,9 +33,24 @@ type BookmarkProviderProps = {
33
33
  readonly currentUser?: BookmarkUser;
34
34
  };
35
35
 
36
+ /**
37
+ * Hook for accessing the bookmark component context provided by `BookmarkProvider`.
38
+ *
39
+ * @returns The current bookmark provider state
40
+ */
36
41
  export const useBookmarkComponentContext = () =>
37
42
  useContext(bookmarkProviderContext) as ProviderState;
38
43
 
44
+ /**
45
+ * Provides bookmark state and modals (create/edit/import) to descendant bookmark components.
46
+ *
47
+ * @param props - The provider's props
48
+ * @param props.provider - The bookmark provider used to fetch/manage bookmarks
49
+ * @param props.currentApp - The app the bookmarks belong to
50
+ * @param props.currentUser - The current user, used to determine bookmark ownership
51
+ * @param props.children - The descendant components that can access the bookmark context
52
+ * @returns The provider with its context and modals
53
+ */
39
54
  export const BookmarkProvider = (props: PropsWithChildren<BookmarkProviderProps>) => {
40
55
  const { provider, currentApp, currentUser, children } = props;
41
56
 
@@ -55,6 +70,7 @@ export const BookmarkProvider = (props: PropsWithChildren<BookmarkProviderProps>
55
70
  setSnackbarContent('Bookmark url copied to clipboard');
56
71
  }, []);
57
72
 
73
+ // Render children without a live provider context when no provider is configured
58
74
  if (!provider) {
59
75
  return (
60
76
  <bookmarkProviderContext.Provider
@@ -7,13 +7,20 @@ import { Button, Checkbox, Dialog, Input, Label, Textarea } from '@equinor/eds-c
7
7
  import styled from 'styled-components';
8
8
  import { from } from 'rxjs';
9
9
 
10
- // TODO
11
10
  const StyledContent = styled(Dialog.Content)`
12
11
  display: flex;
13
12
  flex-direction: column;
14
13
  gap: 1rem;
15
14
  `;
16
15
 
16
+ /**
17
+ * Modal for creating a new bookmark.
18
+ *
19
+ * @param props - The component's props
20
+ * @param props.isOpen - Whether the modal is open
21
+ * @param props.onClose - Callback invoked to close the modal
22
+ * @returns The create bookmark modal
23
+ */
17
24
  export const CreateBookmarkModal = ({
18
25
  isOpen,
19
26
  onClose,
@@ -38,13 +45,14 @@ export const CreateBookmarkModal = ({
38
45
 
39
46
  const createBookmark = useCallback(
40
47
  async (args: BookmarkCreateArgs<never>) => {
48
+ // Cannot create a bookmark without a provider to persist it
41
49
  if (!provider) {
42
50
  console.error('Provider not available');
43
51
  return;
44
52
  }
45
- // TODO: Show success message
46
- // TODO: should this call onCreated, with the new bookmark?
47
- // TODO: should current bookmark be updated?
53
+ // TODO(#5089): Show success message
54
+ // TODO(#5090): should this call onCreated, with the new bookmark?
55
+ // TODO(#5090): should current bookmark be updated?
48
56
  const sub = from(provider.createBookmark(args)).subscribe({
49
57
  next: (bookmark) => {
50
58
  console.debug('Bookmark created', bookmark);
@@ -1 +1 @@
1
- export { CreateBookmarkModal } from './CreateBookmark';
1
+ export { CreateBookmarkModal } from './CreateBookmarkModal';
@@ -32,6 +32,15 @@ const Styled = {
32
32
  `,
33
33
  };
34
34
 
35
+ /**
36
+ * Modal for editing an existing bookmark.
37
+ *
38
+ * @param props - The component's props
39
+ * @param props.isOpen - Whether the modal is open
40
+ * @param props.onClose - Callback invoked to close the modal
41
+ * @param props.bookmarkId - The id of the bookmark to edit
42
+ * @returns The edit bookmark modal
43
+ */
35
44
  export const EditBookmarkModal = ({
36
45
  isOpen,
37
46
  onClose,
@@ -63,13 +72,14 @@ export const EditBookmarkModal = ({
63
72
 
64
73
  // set the state when the bookmark is loaded
65
74
  useEffect(() => {
75
+ // Only populate local state once the bookmark has actually loaded
66
76
  if (bookmark) {
67
77
  const { name, description, isShared } = bookmark;
68
78
  setState({ name, description, isShared });
69
79
  }
70
80
  }, [bookmark]);
71
81
 
72
- // TODO - this should be on the bookmark object
82
+ // TODO(#5091): this should be on the bookmark object
73
83
  const appProvider = useFrameworkModule<AppModule>('app');
74
84
  const { value: appName } = useObservableState(
75
85
  useMemo(
@@ -80,6 +90,7 @@ export const EditBookmarkModal = ({
80
90
 
81
91
  const updateBookmark = useCallback(
82
92
  async (updates: BookmarkUpdate) => {
93
+ // Cannot update a bookmark without a provider to persist the change
83
94
  if (!provider) {
84
95
  console.error('Provider not available');
85
96
  return;
@@ -99,8 +110,8 @@ export const EditBookmarkModal = ({
99
110
  onClose(false);
100
111
  },
101
112
  });
102
- // TODO: Show success message
103
- // TODO: should this call onUpdated, with the updated bookmark?
113
+ // TODO(#5089): Show success message
114
+ // TODO(#5090): should this call onUpdated, with the updated bookmark?
104
115
  onClose(false);
105
116
  },
106
117
  [onClose, provider, bookmarkId, updatePayload],
@@ -135,7 +146,7 @@ export const EditBookmarkModal = ({
135
146
  </div>
136
147
  <div>
137
148
  <Label htmlFor="app" label="App" />
138
- {/** TODO - show ghost while loading app name */}
149
+ {/** TODO(#5092): show ghost while loading app name */}
139
150
  <Input readOnly={true} value={appName?.displayName || ''} />
140
151
  </div>
141
152
 
@@ -145,6 +156,7 @@ export const EditBookmarkModal = ({
145
156
  checked={state.isShared}
146
157
  onChange={(changeEvent: ChangeEvent<HTMLInputElement>) => {
147
158
  const isShared = changeEvent.target.checked;
159
+ // Copy the bookmark URL when the user shares it so it's easy to send along
148
160
  if (isShared) {
149
161
  addBookmarkToClipboard(bookmarkId);
150
162
  }
@@ -1 +1 @@
1
- export * from './EditBookmark';
1
+ export * from './EditBookmarkModal';
@@ -1,4 +1,4 @@
1
- import { Search, Autocomplete } from '@equinor/eds-core-react';
1
+ import { Search } from '@equinor/eds-core-react';
2
2
  import type { GroupingKeys } from '../../hooks/useBookmarkGrouping';
3
3
 
4
4
  import styled from 'styled-components';
@@ -11,7 +11,7 @@ type BookmarkFilterProps = {
11
11
  readonly groupBy: string;
12
12
  };
13
13
 
14
- const Styled = {
14
+ const _Styled = {
15
15
  Root: styled.div`
16
16
  display: flex;
17
17
  align-items: center;
@@ -19,12 +19,18 @@ const Styled = {
19
19
  `,
20
20
  };
21
21
 
22
+ /**
23
+ * Search and grouping filter controls for the bookmark list.
24
+ *
25
+ * @param props - The component's props
26
+ * @returns The filter controls
27
+ */
22
28
  export const BookmarkFilter = ({
23
29
  searchText,
24
- setGroupBy,
30
+ setGroupBy: _setGroupBy,
25
31
  setSearchText,
26
- groupingModes,
27
- groupBy,
32
+ groupingModes: _groupingModes,
33
+ groupBy: _groupBy,
28
34
  }: BookmarkFilterProps) => {
29
35
  return (
30
36
  <Search
@@ -14,6 +14,11 @@ const Styled = {
14
14
  `,
15
15
  };
16
16
 
17
+ /**
18
+ * Modal that prompts the user to import a bookmark shared via a `bookmarkId` URL parameter.
19
+ *
20
+ * @returns The import bookmark modal
21
+ */
17
22
  export const ImportBookmarkModal = () => {
18
23
  const [isOpen, setIsOpen] = useState(false);
19
24
 
@@ -21,17 +26,21 @@ export const ImportBookmarkModal = () => {
21
26
 
22
27
  // Observe changes to current bookmark and check if it is already in bookmarks or favorites
23
28
  const newBookmark$ = useMemo(() => {
29
+ // Without a provider there is no bookmark data source to observe
24
30
  if (!provider) {
25
31
  return of(null);
26
32
  }
33
+ // Watch the current bookmark and resolve whether it should prompt an import
27
34
  return provider.currentBookmark$.pipe(
28
35
  // filter out null values, not interested in those
29
36
  filter((bookmark) => !!bookmark),
30
37
  switchMap((bookmark) => {
31
38
  const checkBookmark = bookmark && bookmark.createdBy.id !== currentUser?.id;
39
+ // Only prompt to import bookmarks created by someone else
32
40
  if (!checkBookmark) {
33
41
  return of(null);
34
42
  }
43
+ // Resolve to null once the bookmark is already a favorite, otherwise pass it through
35
44
  return from(provider.isBookmarkInFavorites(bookmark.id)).pipe(
36
45
  map((isFavorite) => (isFavorite ? null : bookmark)),
37
46
  );
@@ -42,6 +51,7 @@ export const ImportBookmarkModal = () => {
42
51
  const { value: newBookmark } = useObservableState(newBookmark$);
43
52
 
44
53
  useEffect(() => {
54
+ // Open the import dialog whenever a new importable bookmark is detected
45
55
  if (newBookmark) {
46
56
  setIsOpen(true);
47
57
  }
@@ -49,6 +59,7 @@ export const ImportBookmarkModal = () => {
49
59
 
50
60
  const onAddBookmarkFavorite = useCallback(
51
61
  (e: React.MouseEvent<HTMLButtonElement>) => {
62
+ // Without a provider we cannot persist the favorite, so bail out
52
63
  if (!provider) {
53
64
  console.error('Provider not available');
54
65
  return;
@@ -62,6 +73,7 @@ export const ImportBookmarkModal = () => {
62
73
  [provider],
63
74
  );
64
75
 
76
+ // Nothing to import when there is no candidate bookmark
65
77
  if (!newBookmark) {
66
78
  return null;
67
79
  }
@@ -1 +1 @@
1
- export * from './ImportBookmark';
1
+ export * from './ImportBookmarkModal';
@@ -1,6 +1,10 @@
1
1
  import { CircularProgress } from '@equinor/eds-core-react';
2
- import React from 'react';
3
2
 
3
+ /**
4
+ * A centered loading spinner.
5
+ *
6
+ * @returns The loading indicator
7
+ */
4
8
  export const Loading = () => (
5
9
  <div
6
10
  style={{
@@ -8,6 +8,7 @@ import { error_outlined, file_description } from '@equinor/eds-icons';
8
8
  export type PortalMessageType = 'Error' | 'Info' | 'Warning' | 'NoContent';
9
9
 
10
10
  const getMessageType = (type?: PortalMessageType) => {
11
+ // Map each message type to its icon and color; unknown types render nothing
11
12
  switch (type) {
12
13
  case 'Error':
13
14
  return { color: tokens.colors.interactive.danger__resting.hex, icon: error_outlined };
@@ -51,6 +52,12 @@ type MessageProps = {
51
52
  readonly color?: string;
52
53
  };
53
54
 
55
+ /**
56
+ * Displays a titled message with an icon matching its type.
57
+ *
58
+ * @param props - The component's props
59
+ * @returns The message
60
+ */
54
61
  export const Message = ({
55
62
  title,
56
63
  type = 'Info',
@@ -9,26 +9,33 @@ type MenuProps = {
9
9
  readonly options: MenuOption[];
10
10
  };
11
11
 
12
+ /**
13
+ * A dropdown menu of row actions anchored to a reference element.
14
+ *
15
+ * @param props - The component's props
16
+ * @returns The menu
17
+ */
12
18
  export const MoreMenu = ({ pRef, onClose, open, options }: MenuProps) => {
19
+ // Render one menu item per configured row action
20
+ const items = options.map(({ onClick, name, disabled, Icon }) => (
21
+ <Menu.Item
22
+ disabled={disabled}
23
+ onClick={(e) => {
24
+ e.stopPropagation();
25
+ e.preventDefault();
26
+ onClick();
27
+ onClose();
28
+ }}
29
+ key={name}
30
+ >
31
+ {Icon && Icon}
32
+ {name}
33
+ </Menu.Item>
34
+ ));
35
+
13
36
  return (
14
37
  <Menu open={open} anchorEl={pRef.current}>
15
- {options.map(({ onClick, name, disabled, Icon }) => (
16
- <Menu.Item
17
- disabled={disabled}
18
- onClick={(e) => {
19
- e.stopPropagation();
20
- e.preventDefault();
21
- onClick();
22
- onClose();
23
- }}
24
- key={name}
25
- >
26
- <>
27
- {Icon && Icon}
28
- {name}
29
- </>
30
- </Menu.Item>
31
- ))}
38
+ {items}
32
39
  </Menu>
33
40
  );
34
41
  };