@equinor/fusion-framework-react-components-bookmark 0.0.0-context-error-20240131144633

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 (93) hide show
  1. package/CHANGELOG.md +317 -0
  2. package/LICENSE +21 -0
  3. package/dist/esm/components/Bookmark.js +61 -0
  4. package/dist/esm/components/Bookmark.js.map +1 -0
  5. package/dist/esm/components/BookmarkProvider.js +33 -0
  6. package/dist/esm/components/BookmarkProvider.js.map +1 -0
  7. package/dist/esm/components/create-bookmark/CreateBookmark.js +34 -0
  8. package/dist/esm/components/create-bookmark/CreateBookmark.js.map +1 -0
  9. package/dist/esm/components/create-bookmark/index.js +2 -0
  10. package/dist/esm/components/create-bookmark/index.js.map +1 -0
  11. package/dist/esm/components/edit-bookmark/EditBookmark.js +65 -0
  12. package/dist/esm/components/edit-bookmark/EditBookmark.js.map +1 -0
  13. package/dist/esm/components/edit-bookmark/index.js +2 -0
  14. package/dist/esm/components/edit-bookmark/index.js.map +1 -0
  15. package/dist/esm/components/filter/Filter.js +19 -0
  16. package/dist/esm/components/filter/Filter.js.map +1 -0
  17. package/dist/esm/components/import-bookmark/ImportBookmark.js +28 -0
  18. package/dist/esm/components/import-bookmark/ImportBookmark.js.map +1 -0
  19. package/dist/esm/components/import-bookmark/index.js +2 -0
  20. package/dist/esm/components/import-bookmark/index.js.map +1 -0
  21. package/dist/esm/components/loading/Loading.js +10 -0
  22. package/dist/esm/components/loading/Loading.js.map +1 -0
  23. package/dist/esm/components/messages/Message.js +45 -0
  24. package/dist/esm/components/messages/Message.js.map +1 -0
  25. package/dist/esm/components/row/MoreMenu.js +11 -0
  26. package/dist/esm/components/row/MoreMenu.js.map +1 -0
  27. package/dist/esm/components/row/Row.js +47 -0
  28. package/dist/esm/components/row/Row.js.map +1 -0
  29. package/dist/esm/components/section/Section.js +41 -0
  30. package/dist/esm/components/section/Section.js.map +1 -0
  31. package/dist/esm/components/sectionList/SectionList.js +94 -0
  32. package/dist/esm/components/sectionList/SectionList.js.map +1 -0
  33. package/dist/esm/components/shared/SharedIcon.js +28 -0
  34. package/dist/esm/components/shared/SharedIcon.js.map +1 -0
  35. package/dist/esm/hooks/index.js +2 -0
  36. package/dist/esm/hooks/index.js.map +1 -0
  37. package/dist/esm/hooks/useBookmarkGrouping.js +35 -0
  38. package/dist/esm/hooks/useBookmarkGrouping.js.map +1 -0
  39. package/dist/esm/index.js +3 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/utils/append-bookmark-to-uri.js +6 -0
  42. package/dist/esm/utils/append-bookmark-to-uri.js.map +1 -0
  43. package/dist/esm/utils/utils.js +7 -0
  44. package/dist/esm/utils/utils.js.map +1 -0
  45. package/dist/esm/version.js +2 -0
  46. package/dist/esm/version.js.map +1 -0
  47. package/dist/tsconfig.tsbuildinfo +1 -0
  48. package/dist/types/components/Bookmark.d.ts +2 -0
  49. package/dist/types/components/BookmarkProvider.d.ts +14 -0
  50. package/dist/types/components/create-bookmark/CreateBookmark.d.ts +4 -0
  51. package/dist/types/components/create-bookmark/index.d.ts +1 -0
  52. package/dist/types/components/edit-bookmark/EditBookmark.d.ts +5 -0
  53. package/dist/types/components/edit-bookmark/index.d.ts +1 -0
  54. package/dist/types/components/filter/Filter.d.ts +10 -0
  55. package/dist/types/components/import-bookmark/ImportBookmark.d.ts +1 -0
  56. package/dist/types/components/import-bookmark/index.d.ts +1 -0
  57. package/dist/types/components/loading/Loading.d.ts +1 -0
  58. package/dist/types/components/messages/Message.d.ts +10 -0
  59. package/dist/types/components/row/MoreMenu.d.ts +10 -0
  60. package/dist/types/components/row/Row.d.ts +17 -0
  61. package/dist/types/components/section/Section.d.ts +7 -0
  62. package/dist/types/components/sectionList/SectionList.d.ts +6 -0
  63. package/dist/types/components/shared/SharedIcon.d.ts +6 -0
  64. package/dist/types/hooks/index.d.ts +1 -0
  65. package/dist/types/hooks/useBookmarkGrouping.d.ts +14 -0
  66. package/dist/types/index.d.ts +2 -0
  67. package/dist/types/utils/append-bookmark-to-uri.d.ts +1 -0
  68. package/dist/types/utils/utils.d.ts +6 -0
  69. package/dist/types/version.d.ts +1 -0
  70. package/package.json +55 -0
  71. package/src/components/Bookmark.tsx +95 -0
  72. package/src/components/BookmarkProvider.tsx +65 -0
  73. package/src/components/create-bookmark/CreateBookmark.tsx +93 -0
  74. package/src/components/create-bookmark/index.ts +1 -0
  75. package/src/components/edit-bookmark/EditBookmark.tsx +137 -0
  76. package/src/components/edit-bookmark/index.ts +1 -0
  77. package/src/components/filter/Filter.tsx +58 -0
  78. package/src/components/import-bookmark/ImportBookmark.tsx +57 -0
  79. package/src/components/import-bookmark/index.ts +1 -0
  80. package/src/components/loading/Loading.tsx +16 -0
  81. package/src/components/messages/Message.tsx +82 -0
  82. package/src/components/row/MoreMenu.tsx +34 -0
  83. package/src/components/row/Row.tsx +92 -0
  84. package/src/components/section/Section.tsx +57 -0
  85. package/src/components/sectionList/SectionList.tsx +161 -0
  86. package/src/components/shared/SharedIcon.tsx +45 -0
  87. package/src/hooks/index.ts +1 -0
  88. package/src/hooks/useBookmarkGrouping.ts +54 -0
  89. package/src/index.ts +2 -0
  90. package/src/utils/append-bookmark-to-uri.ts +5 -0
  91. package/src/utils/utils.ts +12 -0
  92. package/src/version.ts +2 -0
  93. package/tsconfig.json +23 -0
@@ -0,0 +1,58 @@
1
+ import { Search, Autocomplete } from '@equinor/eds-core-react';
2
+ import { GroupingKeys } from '../../hooks/useBookmarkGrouping';
3
+
4
+ import styled from 'styled-components';
5
+
6
+ type BookmarkFilterProps = {
7
+ readonly searchText: string;
8
+ readonly setSearchText: (newVal: string | null) => void;
9
+ readonly setGroupBy: (groupBy: GroupingKeys) => void;
10
+ readonly groupingModes: string[];
11
+ readonly groupBy: string;
12
+ };
13
+
14
+ const Styled = {
15
+ Root: styled.div`
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: space-between;
19
+ `,
20
+ };
21
+
22
+ export const BookmarkFilter = ({
23
+ searchText,
24
+ setGroupBy,
25
+ setSearchText,
26
+ groupingModes,
27
+ groupBy,
28
+ }: BookmarkFilterProps) => {
29
+ return (
30
+ <Search
31
+ placeholder="Search in my bookmarks"
32
+ value={searchText ?? ''}
33
+ onChange={(e) => {
34
+ setSearchText(e.currentTarget.value.length ? e.currentTarget.value : null);
35
+ }}
36
+ />
37
+ );
38
+ return (
39
+ <Styled.Root>
40
+ <Search
41
+ placeholder="Search in my bookmarks"
42
+ value={searchText ?? ''}
43
+ onChange={(e) => {
44
+ setSearchText(e.currentTarget.value.length ? e.currentTarget.value : null);
45
+ }}
46
+ />
47
+ <Autocomplete
48
+ options={groupingModes}
49
+ selectedOptions={[groupBy]}
50
+ multiple={false}
51
+ hideClearButton
52
+ autoWidth
53
+ onOptionsChange={(changes) => setGroupBy(changes.selectedItems[0] as GroupingKeys)}
54
+ label={''}
55
+ />
56
+ </Styled.Root>
57
+ );
58
+ };
@@ -0,0 +1,57 @@
1
+ import { Button, Dialog } from '@equinor/eds-core-react';
2
+ import { useBookmark } from '@equinor/fusion-framework-react-module-bookmark';
3
+ import { useEffect, useState } from 'react';
4
+
5
+ import styled from 'styled-components';
6
+
7
+ const Styled = {
8
+ Actions: styled.div`
9
+ display: flex;
10
+ gap: 1rem;
11
+ `,
12
+ };
13
+
14
+ export const ImportBookmarkModal = () => {
15
+ const [isOpen, setIsOpen] = useState(false);
16
+
17
+ const { currentBookmark, bookmarks, addBookmarkFavorite } = useBookmark();
18
+
19
+ useEffect(() => {
20
+ if (!bookmarks || !currentBookmark || bookmarks.length === 0) return;
21
+
22
+ if (bookmarks.findIndex(({ id }) => id === currentBookmark.id) === -1) {
23
+ setIsOpen(true);
24
+ }
25
+ }, [bookmarks, currentBookmark]);
26
+
27
+ return (
28
+ <Dialog style={{ width: '600px' }} open={isOpen}>
29
+ <Dialog.Header>Import bookmark</Dialog.Header>
30
+ <Dialog.Content>
31
+ <p>This bookmark was created by {currentBookmark?.createdBy.name}</p>
32
+ <p>Would you like to import it?</p>
33
+ </Dialog.Content>
34
+ <Dialog.Actions>
35
+ <Styled.Actions>
36
+ <Button
37
+ onClick={() => setIsOpen(false)}
38
+ variant={'ghost'}
39
+ style={{ width: '70px' }}
40
+ >
41
+ No
42
+ </Button>
43
+ <Button
44
+ onClick={() => {
45
+ console.log(currentBookmark?.id);
46
+ currentBookmark && addBookmarkFavorite(currentBookmark.id);
47
+ setIsOpen(false);
48
+ }}
49
+ variant="ghost"
50
+ >
51
+ Import
52
+ </Button>
53
+ </Styled.Actions>
54
+ </Dialog.Actions>
55
+ </Dialog>
56
+ );
57
+ };
@@ -0,0 +1 @@
1
+ export * from './ImportBookmark';
@@ -0,0 +1,16 @@
1
+ import { CircularProgress } from '@equinor/eds-core-react';
2
+ import React from 'react';
3
+
4
+ export const Loading = () => (
5
+ <div
6
+ style={{
7
+ display: 'flex',
8
+ alignItems: 'center',
9
+ justifyContent: 'center',
10
+ height: '100%',
11
+ width: '100%',
12
+ }}
13
+ >
14
+ <CircularProgress />
15
+ </div>
16
+ );
@@ -0,0 +1,82 @@
1
+ import { Icon, Typography } from '@equinor/eds-core-react';
2
+ import { tokens } from '@equinor/eds-tokens';
3
+ import { PropsWithChildren } from 'react';
4
+ import styled from 'styled-components';
5
+
6
+ import { error_outlined, file_description } from '@equinor/eds-icons';
7
+
8
+ export type PortalMessageType = 'Error' | 'Info' | 'Warning' | 'NoContent';
9
+
10
+ const getMessageType = (type?: PortalMessageType) => {
11
+ switch (type) {
12
+ case 'Error':
13
+ return { color: tokens.colors.interactive.danger__resting.hex, icon: error_outlined };
14
+ case 'Info':
15
+ return { color: tokens.colors.interactive.primary__resting.hex, icon: error_outlined };
16
+ case 'Warning':
17
+ return { color: tokens.colors.interactive.warning__resting.hex, icon: error_outlined };
18
+ case 'NoContent':
19
+ return {
20
+ color: tokens.colors.interactive.primary__resting.hex,
21
+ icon: file_description,
22
+ };
23
+ default:
24
+ return undefined;
25
+ }
26
+ };
27
+
28
+ const Styles = {
29
+ Wrapper: styled.div`
30
+ width: 100%;
31
+ height: 100%;
32
+ display: flex;
33
+ flex-direction: column;
34
+ align-items: center;
35
+ gap: 1rem;
36
+ justify-content: center;
37
+ `,
38
+ Content: styled.div`
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ gap: 0.5rem;
43
+ justify-content: center;
44
+ `,
45
+ };
46
+
47
+ type MessageProps = {
48
+ readonly title: string;
49
+ readonly body?: React.FC | string;
50
+ readonly type?: PortalMessageType;
51
+ readonly color?: string;
52
+ };
53
+
54
+ export const Message = ({
55
+ title,
56
+ type = 'Info',
57
+ color,
58
+ children,
59
+ }: PropsWithChildren<MessageProps>) => {
60
+ const currentType = getMessageType(type);
61
+ return (
62
+ <Styles.Wrapper>
63
+ <Icon
64
+ data-testid="icon"
65
+ size={40}
66
+ color={currentType?.color || color || tokens.colors.text.static_icons__tertiary.hex}
67
+ data={currentType?.icon || error_outlined}
68
+ />
69
+ <Styles.Content>
70
+ <Typography
71
+ color={tokens.colors.text.static_icons__default.hex}
72
+ variant={'h3'}
73
+ aria-label={`Title for ${type} message`}
74
+ >
75
+ {title}
76
+ </Typography>
77
+
78
+ <Typography>{children && children}</Typography>
79
+ </Styles.Content>
80
+ </Styles.Wrapper>
81
+ );
82
+ };
@@ -0,0 +1,34 @@
1
+ import { Menu } from '@equinor/eds-core-react';
2
+ import { MutableRefObject } from 'react';
3
+ import { MenuOption } from './Row';
4
+
5
+ type MenuProps = {
6
+ readonly pRef: MutableRefObject<HTMLElement | null>;
7
+ readonly onClose: VoidFunction;
8
+ readonly open: boolean;
9
+ readonly options: MenuOption[];
10
+ };
11
+
12
+ export const MoreMenu = ({ pRef, onClose, open, options }: MenuProps) => {
13
+ return (
14
+ <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
+ ))}
32
+ </Menu>
33
+ );
34
+ };
@@ -0,0 +1,92 @@
1
+ import { Icon, Typography } from '@equinor/eds-core-react';
2
+ import { tokens } from '@equinor/eds-tokens';
3
+ import { useOutsideClick } from '@equinor/eds-utils';
4
+ import { useBookmark } from '@equinor/fusion-framework-react-module-bookmark';
5
+ import { MutableRefObject, ReactNode, useRef } from 'react';
6
+ import { MoreMenu } from './MoreMenu';
7
+
8
+ import styled from 'styled-components';
9
+
10
+ export type MenuOption = {
11
+ name: string;
12
+ disabled: boolean;
13
+ onClick: VoidFunction;
14
+ Icon?: ReactNode;
15
+ };
16
+
17
+ type RowProps = {
18
+ readonly name: string;
19
+ readonly id: string;
20
+ readonly menuOpen: boolean;
21
+ readonly onMenuOpen: (id: string) => void;
22
+ readonly menuOptions: MenuOption[];
23
+ readonly children?: ReactNode;
24
+ };
25
+
26
+ const Styled = {
27
+ Icons: styled.div`
28
+ display: flex;
29
+ flex-direction: row;
30
+ gap: 0.2em;
31
+ align-items: center;
32
+ `,
33
+ ListItem: styled.div`
34
+ cursor: pointer;
35
+ &:hover {
36
+ background-color: ${tokens.colors.ui.background__light.hex};
37
+ }
38
+
39
+ display: flex;
40
+ align-items: center;
41
+ height: 32px;
42
+ min-height: 32px;
43
+ max-height: 32px;
44
+ justify-content: space-between;
45
+ border-bottom: 1px solid ${tokens.colors.ui.background__medium.hex};
46
+ `,
47
+ };
48
+
49
+ export const Row = ({ name, menuOptions, children, id, menuOpen, onMenuOpen }: RowProps) => {
50
+ const pRef = useRef<HTMLElement | null>(null);
51
+ const { setCurrentBookmark } = useBookmark();
52
+
53
+ useOutsideClick(pRef.current, () => onMenuOpen(''));
54
+
55
+ // TODO: @noggling fix this
56
+ return (
57
+ /* eslint-disable-next-line styled-components-a11y/click-events-have-key-events, styled-components-a11y/no-static-element-interactions*/
58
+ <Styled.ListItem
59
+ onClick={(e) => {
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ setCurrentBookmark(id);
63
+ }}
64
+ >
65
+ <Typography>{name}</Typography>
66
+ <Styled.Icons>
67
+ {children}
68
+ {!!menuOptions?.length && (
69
+ <Icon
70
+ name="more_vertical"
71
+ onClick={(e) => {
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ onMenuOpen(id);
75
+ }}
76
+ color={tokens.colors.interactive.primary__resting.hex}
77
+ ref={pRef as unknown as MutableRefObject<SVGSVGElement>}
78
+ />
79
+ )}
80
+
81
+ <MoreMenu
82
+ open={menuOpen}
83
+ options={menuOptions}
84
+ onClose={() => {
85
+ onMenuOpen('');
86
+ }}
87
+ pRef={pRef}
88
+ />
89
+ </Styled.Icons>
90
+ </Styled.ListItem>
91
+ );
92
+ };
@@ -0,0 +1,57 @@
1
+ import { useState } from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { Button, Icon, Typography } from '@equinor/eds-core-react';
4
+ import { chevron_down, chevron_right } from '@equinor/eds-icons';
5
+
6
+ import { tokens } from '@equinor/eds-tokens';
7
+ import styled from 'styled-components';
8
+
9
+ const Styled = {
10
+ Button: styled(Button)`
11
+ width: 100%;
12
+ padding: 0;
13
+ padding-top: 0.5rem;
14
+ margin-bottom: 0.5rem;
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ background: none;
19
+ border: none;
20
+ color: ${tokens.colors.infographic.primary__moss_green_100.hex};
21
+ cursor: pointer;
22
+ :hover {
23
+ background: none;
24
+ }
25
+ `,
26
+ List: styled.ol`
27
+ margin-left: 0.7rem;
28
+ padding-left: 1rem;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: 1rem;
32
+ border-left: 1px solid ${tokens.colors.infographic.primary__moss_green_100.hex};
33
+ `,
34
+ };
35
+
36
+ Icon.add({
37
+ chevron_down,
38
+ chevron_right,
39
+ });
40
+
41
+ type SectionProps = {
42
+ readonly name: string;
43
+ readonly children: ReactNode;
44
+ };
45
+ export const Section = ({ children, name }: SectionProps) => {
46
+ const [isExpanded, setIsExpanded] = useState(true);
47
+
48
+ return (
49
+ <div>
50
+ <Styled.Button variant="ghost" onClick={() => setIsExpanded((s) => !s)}>
51
+ <Icon name={isExpanded ? chevron_down.name : chevron_right.name} />
52
+ <Typography variant="h6">{name}</Typography>
53
+ </Styled.Button>
54
+ {isExpanded && <Styled.List>{children}</Styled.List>}
55
+ </div>
56
+ );
57
+ };
@@ -0,0 +1,161 @@
1
+ import { Icon } from '@equinor/eds-core-react';
2
+ import { useBookmarkGrouping } from '../../hooks';
3
+ import { Row } from '../row/Row';
4
+ import { Section } from '../section/Section';
5
+ import { SharedIcon } from '../shared/SharedIcon';
6
+ // TODO - export from `@equinor/fusion-framework-react-module-bookmark`
7
+ import type { Bookmark } from '@equinor/fusion-framework-module-bookmark';
8
+ import { useBookmark } from '@equinor/fusion-framework-react-module-bookmark';
9
+ import { useCurrentUser } from '@equinor/fusion-framework-react/hooks';
10
+ import { useCallback, useState } from 'react';
11
+ import { delete_to_trash, share, edit, close, update } from '@equinor/eds-icons';
12
+ import { useFramework } from '@equinor/fusion-framework-react';
13
+ import { appendBookmarkIdToUrl } from '../../utils/append-bookmark-to-uri';
14
+ import { filterEmptyGroups, sortByName, toHumanReadable } from '../../utils/utils';
15
+
16
+ Icon.add({
17
+ delete_to_trash,
18
+ edit,
19
+ share,
20
+ close,
21
+ update,
22
+ });
23
+
24
+ type SectionListProps = {
25
+ readonly bookmarkGroups: ReturnType<typeof useBookmarkGrouping>['bookmarkGroups'];
26
+ };
27
+
28
+ export const SectionList = ({ bookmarkGroups }: SectionListProps) => {
29
+ const { deleteBookmarkById, getCurrentAppKey, updateBookmark, removeBookmarkFavorite } =
30
+ useBookmark();
31
+
32
+ const user = useCurrentUser();
33
+ const [isMenuByIdOpen, setIsMenuByIdOpen] = useState('');
34
+
35
+ const { event } = useFramework().modules;
36
+
37
+ const editBookmark = useCallback(
38
+ (bookmarkId: string) => {
39
+ event.dispatchEvent('onBookmarkEdit', { detail: { bookmarkId } });
40
+ },
41
+ [event],
42
+ );
43
+
44
+ const shareBookmark = useCallback(
45
+ (bookmark: Bookmark) => {
46
+ if (!bookmark.isShared) {
47
+ const url = appendBookmarkIdToUrl(bookmark.id);
48
+ navigator.clipboard.writeText(url);
49
+ event.dispatchEvent('onBookmarkUrlCopy', { detail: { url } });
50
+ }
51
+ updateBookmark({ ...bookmark, isShared: !bookmark.isShared });
52
+ },
53
+ [event, updateBookmark],
54
+ );
55
+
56
+ const updateBookmarkWithCurrentView = useCallback(
57
+ (bookmark: Bookmark) => {
58
+ updateBookmark({ ...bookmark }, { updatePayload: true });
59
+ },
60
+ [updateBookmark],
61
+ );
62
+
63
+ const createMenuOptions = (bookmark: Bookmark) =>
64
+ createBookmarkActions(
65
+ bookmark,
66
+ deleteBookmarkById,
67
+ editBookmark,
68
+ shareBookmark,
69
+ removeBookmarkFavorite,
70
+ updateBookmarkWithCurrentView,
71
+ getCurrentAppKey(),
72
+ user?.localAccountId,
73
+ );
74
+
75
+ return (
76
+ <>
77
+ {bookmarkGroups.filter(filterEmptyGroups).map(({ groupingProperty, values }) => (
78
+ <Section key={groupingProperty} name={toHumanReadable(groupingProperty)}>
79
+ {values.sort(sortByName).map((b) => (
80
+ <Row
81
+ menuOptions={createMenuOptions(b)}
82
+ key={b.id}
83
+ name={b.name}
84
+ id={b.id}
85
+ menuOpen={isMenuByIdOpen === b.id}
86
+ onMenuOpen={(id: string) => {
87
+ setIsMenuByIdOpen(id);
88
+ }}
89
+ >
90
+ {b.isShared && (
91
+ <SharedIcon
92
+ createdById={b.createdBy.azureUniqueId}
93
+ createdBy={b.createdBy.name}
94
+ />
95
+ )}
96
+ </Row>
97
+ ))}
98
+ </Section>
99
+ ))}
100
+ </>
101
+ );
102
+ };
103
+
104
+ function createBookmarkActions(
105
+ bookmark: Bookmark,
106
+ deleteBookmarkById: (bookmarkId: string) => Promise<string | undefined>,
107
+ editBookmark: (bookmark: string) => void,
108
+ shareBookmark: (bookmark: Bookmark) => void,
109
+ removeBookmarkFavorite: (bookmarkId: string) => void,
110
+ updateBookmarkWithCurrentView: (bookmark: Bookmark) => void,
111
+ appKey?: string,
112
+ localAccountId?: string,
113
+ ) {
114
+ if (bookmark.createdBy.azureUniqueId === localAccountId) {
115
+ return [
116
+ {
117
+ name: 'Edit',
118
+ disabled: appKey !== bookmark.appKey,
119
+ onClick: () => {
120
+ editBookmark(bookmark.id);
121
+ },
122
+ Icon: <Icon name="edit" />,
123
+ },
124
+ {
125
+ name: 'Update with current view',
126
+ disabled: appKey !== bookmark.appKey,
127
+ onClick: () => {
128
+ updateBookmarkWithCurrentView(bookmark);
129
+ },
130
+ Icon: <Icon name="update" />,
131
+ },
132
+ {
133
+ name: 'Remove',
134
+ disabled: false,
135
+ onClick: () => {
136
+ deleteBookmarkById(bookmark.id);
137
+ },
138
+ Icon: <Icon name="delete_to_trash" />,
139
+ },
140
+ {
141
+ name: bookmark.isShared ? 'Unshare' : 'Share',
142
+ disabled: false,
143
+ onClick: () => {
144
+ shareBookmark(bookmark);
145
+ },
146
+ Icon: <Icon name="share" />,
147
+ },
148
+ ];
149
+ } else {
150
+ return [
151
+ {
152
+ name: 'Unfavourite',
153
+ disabled: false,
154
+ onClick: () => {
155
+ removeBookmarkFavorite(bookmark.id);
156
+ },
157
+ Icon: <Icon name="close" />,
158
+ },
159
+ ];
160
+ }
161
+ }
@@ -0,0 +1,45 @@
1
+ import { Icon } from '@equinor/eds-core-react';
2
+ import { tokens } from '@equinor/eds-tokens';
3
+ import { useFramework } from '@equinor/fusion-framework-react';
4
+
5
+ import styled from 'styled-components';
6
+
7
+ const Styled = {
8
+ Row: styled.div`
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 0.2em;
12
+ `,
13
+ CreatedBy: styled.p`
14
+ font-size: 12px;
15
+ font-weight: 500;
16
+ line-height: 16px;
17
+ letter-spacing: 0em;
18
+ text-align: right;
19
+ `,
20
+ };
21
+
22
+ type SharedIconProps = {
23
+ readonly createdBy: string;
24
+ readonly createdById: string;
25
+ };
26
+
27
+ export const SharedIcon = ({ createdBy, createdById }: SharedIconProps) => {
28
+ const myId = useFramework().modules.auth.defaultAccount?.localAccountId;
29
+
30
+ const isMe = createdById === myId;
31
+
32
+ return (
33
+ <Styled.Row>
34
+ {!isMe && <Styled.CreatedBy>shared by {createdBy}</Styled.CreatedBy>}
35
+ <Icon
36
+ name="share"
37
+ color={
38
+ isMe
39
+ ? tokens.colors.interactive.primary__resting.hex
40
+ : tokens.colors.interactive.disabled__border.hex
41
+ }
42
+ />
43
+ </Styled.Row>
44
+ );
45
+ };
@@ -0,0 +1 @@
1
+ export * from './useBookmarkGrouping';
@@ -0,0 +1,54 @@
1
+ import { Bookmark } from '@equinor/fusion-framework-module-bookmark';
2
+
3
+ import { useState } from 'react';
4
+
5
+ export type GroupingKeys = 'Group by app' | 'Created by' | 'Group by Context';
6
+
7
+ const groupingModes: Record<GroupingKeys, (_item: Bookmark) => string> = {
8
+ 'Group by app': (item: Bookmark) => item.appKey,
9
+ 'Created by': (item: Bookmark) => item.createdBy.name,
10
+ 'Group by Context': (item: Bookmark) => item?.context?.name ?? 'Unknown',
11
+ } as const;
12
+
13
+ export const useBookmarkGrouping = (bookmarks?: Bookmark[]) => {
14
+ const [searchText, setSearchText] = useState<string | null>(null);
15
+
16
+ const [groupByKey, setGroupBy] = useState<keyof typeof groupingModes>('Group by app');
17
+
18
+ const bookmarkGroups = groupBy(bookmarks || [], groupingModes[groupByKey], searchText, 'name');
19
+
20
+ return {
21
+ setGroupBy,
22
+ setSearchText,
23
+ searchText,
24
+ bookmarkGroups,
25
+ groupingModes,
26
+ groupByKey,
27
+ };
28
+ };
29
+
30
+ const groupBy = <T>(
31
+ array: T[],
32
+ getKey: (item: T) => string,
33
+ searchText: string | null,
34
+ field: keyof T,
35
+ ) => {
36
+ return (
37
+ array
38
+ ?.map(getKey)
39
+ .filter((v, i, a) => a.indexOf(v) === i)
40
+ .map((groupingProperty) => ({
41
+ groupingProperty,
42
+ values: array
43
+ .filter((s) => getKey(s) === groupingProperty)
44
+ .filter((s) => {
45
+ const fieldData = s[field];
46
+ if (typeof fieldData === 'string' && searchText) {
47
+ return fieldData.includes(searchText);
48
+ }
49
+
50
+ return true;
51
+ }),
52
+ })) ?? []
53
+ );
54
+ };
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Bookmark } from './components/Bookmark';
2
+ export { BookmarkProvider } from './components/BookmarkProvider';
@@ -0,0 +1,5 @@
1
+ export const appendBookmarkIdToUrl = (id: string): string => {
2
+ const url = new URL(window.location.toString());
3
+ url.searchParams.set('bookmarkId', id);
4
+ return url.toString();
5
+ };
@@ -0,0 +1,12 @@
1
+ import { Bookmark } from '@equinor/fusion-framework-module-bookmark';
2
+
3
+ /** Helpers.. */
4
+ export const filterEmptyGroups = (i: { values: Bookmark[] }) => i.values.length;
5
+ export const sortByName = (a: Bookmark, b: Bookmark) => a.name.localeCompare(b.name);
6
+ // const extractNameFromMail = (mail: string) => mail.split('@')[0].toLowerCase();
7
+
8
+ export const toHumanReadable = (val: string) =>
9
+ val
10
+ .split('-')
11
+ .map((s) => `${s[0].toUpperCase()}${s.slice(1)}`)
12
+ .join(' ');