@itwin/saved-views-react 0.1.0
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.
- package/LICENSE.md +9 -0
- package/README.md +131 -0
- package/lib/LayeredDropdownMenu/LayeredDropdownMenu.css +11 -0
- package/lib/LayeredDropdownMenu/LayeredDropdownMenu.d.ts +39 -0
- package/lib/LayeredDropdownMenu/LayeredDropdownMenu.js +53 -0
- package/lib/SavedView.d.ts +18 -0
- package/lib/SavedView.js +1 -0
- package/lib/SavedViewTile/SavedViewOptions.css +26 -0
- package/lib/SavedViewTile/SavedViewOptions.d.ts +139 -0
- package/lib/SavedViewTile/SavedViewOptions.js +173 -0
- package/lib/SavedViewTile/SavedViewTile.css +89 -0
- package/lib/SavedViewTile/SavedViewTile.d.ts +55 -0
- package/lib/SavedViewTile/SavedViewTile.js +110 -0
- package/lib/SavedViewTile/SavedViewTileContext.d.ts +14 -0
- package/lib/SavedViewTile/SavedViewTileContext.js +20 -0
- package/lib/SavedViewsClient/ITwinSavedViewsClient.d.ts +30 -0
- package/lib/SavedViewsClient/ITwinSavedViewsClient.js +132 -0
- package/lib/SavedViewsClient/SavedViewsClient.d.ts +72 -0
- package/lib/SavedViewsClient/SavedViewsClient.js +1 -0
- package/lib/SavedViewsContext.d.ts +13 -0
- package/lib/SavedViewsContext.js +38 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupOptions.d.ts +9 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupOptions.js +14 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTile.d.ts +14 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTile.js +37 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.d.ts +14 -0
- package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.js +20 -0
- package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.css +50 -0
- package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.d.ts +36 -0
- package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.js +36 -0
- package/lib/SavedViewsWidget/SavedViewsFolderWidget.d.ts +14 -0
- package/lib/SavedViewsWidget/SavedViewsFolderWidget.js +60 -0
- package/lib/StickyExpandableBlock/StickyExpandableBlock.css +20 -0
- package/lib/StickyExpandableBlock/StickyExpandableBlock.d.ts +29 -0
- package/lib/StickyExpandableBlock/StickyExpandableBlock.js +63 -0
- package/lib/TileGrid/TileGrid.css +28 -0
- package/lib/TileGrid/TileGrid.d.ts +48 -0
- package/lib/TileGrid/TileGrid.js +32 -0
- package/lib/api/clients/IModelQueryClient.d.ts +10 -0
- package/lib/api/clients/IModelQueryClient.js +45 -0
- package/lib/api/clients/ISavedViewsClient.d.ts +9 -0
- package/lib/api/clients/ISavedViewsClient.js +16 -0
- package/lib/api/utilities/SavedViewTypes.d.ts +48 -0
- package/lib/api/utilities/SavedViewTypes.js +1 -0
- package/lib/api/utilities/translation/ModelsAndCategoriesHelper.d.ts +3 -0
- package/lib/api/utilities/translation/ModelsAndCategoriesHelper.js +57 -0
- package/lib/api/utilities/translation/RgbColor.d.ts +29 -0
- package/lib/api/utilities/translation/RgbColor.js +1 -0
- package/lib/api/utilities/translation/SavedViewTranslation.d.ts +22 -0
- package/lib/api/utilities/translation/SavedViewTranslation.js +246 -0
- package/lib/api/utilities/translation/SavedViewsExtensionHandlers.d.ts +13 -0
- package/lib/api/utilities/translation/SavedViewsExtensionHandlers.js +42 -0
- package/lib/api/utilities/translation/clipVectorsExtractor.d.ts +5 -0
- package/lib/api/utilities/translation/clipVectorsExtractor.js +56 -0
- package/lib/api/utilities/translation/displayStyleExtractor.d.ts +17 -0
- package/lib/api/utilities/translation/displayStyleExtractor.js +499 -0
- package/lib/api/utilities/translation/extensionExtractor.d.ts +18 -0
- package/lib/api/utilities/translation/extensionExtractor.js +79 -0
- package/lib/api/utilities/translation/extractionUtilities.d.ts +209 -0
- package/lib/api/utilities/translation/extractionUtilities.js +515 -0
- package/lib/api/utilities/translation/urlConverter.d.ts +7 -0
- package/lib/api/utilities/translation/urlConverter.js +42 -0
- package/lib/api/utilities/translation/viewExtractorSavedViewToLegacySavedView.d.ts +35 -0
- package/lib/api/utilities/translation/viewExtractorSavedViewToLegacySavedView.js +298 -0
- package/lib/experimental.d.ts +4 -0
- package/lib/experimental.js +8 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +14 -0
- package/lib/localization.d.ts +52 -0
- package/lib/localization.js +51 -0
- package/lib/ui/viewlist/ModelCategoryOverrideProvider.d.ts +31 -0
- package/lib/ui/viewlist/ModelCategoryOverrideProvider.js +88 -0
- package/lib/useSavedViews.d.ts +52 -0
- package/lib/useSavedViews.js +514 -0
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +7 -0
- package/package.json +75 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2024 Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @itwin/saved-views-react
|
|
2
|
+
|
|
3
|
+
## About
|
|
4
|
+
|
|
5
|
+
A collection of React components for building applications that deal with [Saved Views](https://developer.bentley.com/apis/savedviews/overview/).
|
|
6
|
+
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
### React components
|
|
10
|
+
|
|
11
|
+
* [SavedViewTile](./src/SavedViewTile/SavedViewTile.tsx)
|
|
12
|
+
* [SavedViewOptions](./src/SavedViewTile/SavedViewOptions.tsx)
|
|
13
|
+
* [LayeredDropdownMenu](./src/LayeredDropdownMenu/LayeredDropdownMenu.tsx)
|
|
14
|
+
* [TileGrid](./src/TileGrid/TileGrid.tsx)
|
|
15
|
+
* [StickyExpandableBlock](./src/StickyExpandableBlock/StickyExpandableBlock.tsx)
|
|
16
|
+
|
|
17
|
+
You can use these components however you see fit, however, below is the suggested component arrangement.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { LayeredMenuItem, SavedViewTile, StickyExpandableBlock, TileGrid } from "@itwin/saved-views-react";
|
|
21
|
+
|
|
22
|
+
export function SavedViewsWidget(props) {
|
|
23
|
+
return (
|
|
24
|
+
<StickyExpandableBlock title="Saved Views">
|
|
25
|
+
<TileGrid gridItems={props.savedViews}>
|
|
26
|
+
{
|
|
27
|
+
(savedView) => (
|
|
28
|
+
<SavedViewTile
|
|
29
|
+
savedView={savedView}
|
|
30
|
+
options={[
|
|
31
|
+
<SavedViewOptions.Delete key="delete" deleteSavedView={props.deleteSavedView} />,
|
|
32
|
+
<LayeredMenuItem key="menu-item" content={<MyMenuItemContent />}>
|
|
33
|
+
Custom menu item
|
|
34
|
+
</LayeredMenuItem>,
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
</TileGrid>
|
|
40
|
+
</StickyExpandableBlock>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### useSavedViews
|
|
46
|
+
|
|
47
|
+
[useSavedViews](./src/useSavedViews.tsx) React hook provides basic functionality to jump-start your Saved Views widget. It accepts [`ITwinSavedViewsClient`](./src/SavedViewsClient/ITwinSavedViewsClient.ts) which is used to pull Saved View data and synchronize it back to the [Saved Views service](https://developer.bentley.com/apis/savedviews/overview/).
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { useSavedViews, ITwinSavedViewsClient } from "@itwin/saved-views-react";
|
|
51
|
+
|
|
52
|
+
const client = new ITwinSavedViewsClient({
|
|
53
|
+
// auth_token should have access to savedviews:read and savedviews:modify OIDC scopes
|
|
54
|
+
getAccessToken: async () => "auth_token",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export function SavedViewsWidget(props) {
|
|
58
|
+
const savedViews = useSavedViews({ iTwinId: props.iTwinId, iModelId: props.iModelId, client });
|
|
59
|
+
if (savedViews === undefined) {
|
|
60
|
+
return "loading";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<MyWidgetContent
|
|
65
|
+
savedViews={savedViews.savedViews}
|
|
66
|
+
groups={savedViews.groups}
|
|
67
|
+
tags={savedViews.tags}
|
|
68
|
+
actions={savedViews.actions}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Localization
|
|
75
|
+
|
|
76
|
+
In order to localize text across various provided components, mount [`<SavedViewsContextProvider />`](./src/SavedViewsContext.tsx) and supply `localization` object containing string replacements.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { SavedViewsContextProvider, type LocalizationStrings } from "@itwin/saved-views-react";
|
|
80
|
+
|
|
81
|
+
const localization: LocalizationStrings = { delete: "🗑️" };
|
|
82
|
+
|
|
83
|
+
export function SavedViewsWidget() {
|
|
84
|
+
return (
|
|
85
|
+
<SavedViewsContextProvider localization={localization}>
|
|
86
|
+
<MyWidgetContent />
|
|
87
|
+
</SavedViewsContextProvider>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Custom tile options
|
|
94
|
+
|
|
95
|
+
[`<SavedViewTile />`](./src/SavedViewTile/SavedViewTile.tsx) accepts an array of options to display in its context menu. This package exposes a number of ready-to-use options to use with your tiles, accessible through [`SavedViewOptions`](./src/SavedViewTile//SavedViewOptions.tsx) export, and you are free to create your own ones.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { MenuDivider, MenuItem } from "@itwin/itwinui-react";
|
|
99
|
+
import { SavedViewOptions, SavedViewTile, useSavedViewTileContext } from "@itwin/saved-views-react";
|
|
100
|
+
|
|
101
|
+
export function SavedViewsWidget(props) {
|
|
102
|
+
return (
|
|
103
|
+
<SavedViewTile
|
|
104
|
+
savedView={props.savedView}
|
|
105
|
+
options={(close) => [
|
|
106
|
+
<SavedViewOptions.Rename key="rename" onClick={close} />,
|
|
107
|
+
<MenuDivider key="divider" />,
|
|
108
|
+
<OpenSavedView key="custom" onClick={props.openSavedView} />,
|
|
109
|
+
]}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function OpenSavedView(props) {
|
|
115
|
+
const { savedView } = useSavedViewTileContext();
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<MenuItem onClick={() => props.onClick(savedView)}>
|
|
119
|
+
Open {savedView.displayName}
|
|
120
|
+
</MenuItem>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
We welcome contributions to make this package better. You can submit feature requests or report bugs by creating an [issue](https://github.com/iTwin/saved-views/issues).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
Copyright © Bentley Systems, Incorporated. All rights reserved. See [LICENSE.md](./LICENSE.md) for license terms and full copyright notice.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
.svr-layered-dropdown--back {
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: var(--iui-size-s);
|
|
8
|
+
align-items: center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9MYXllcmVkRHJvcGRvd25NZW51L0xheWVyZWREcm9wZG93bk1lbnUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIiwiZmlsZSI6IkxheWVyZWREcm9wZG93bk1lbnUuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiogQ29weXJpZ2h0IChjKSBCZW50bGV5IFN5c3RlbXMsIEluY29ycG9yYXRlZC4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiogU2VlIExJQ0VOU0UubWQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSB0ZXJtcyBhbmQgZnVsbCBjb3B5cmlnaHQgbm90aWNlLlxuKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi5zdnItbGF5ZXJlZC1kcm9wZG93bi0tYmFjayB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGdhcDogdmFyKC0taXVpLXNpemUtcyk7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG59XG4iXX0= */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import "./LayeredDropdownMenu.css";
|
|
3
|
+
interface LayeredDropdownMenuProps {
|
|
4
|
+
/**
|
|
5
|
+
* Items to pass to iTwinUI `<DropdownMenu />` component. It is preferable for items to include at least one
|
|
6
|
+
* `<LayeredMenuItem />`. Otherwise, it is more efficient to use plain `<DropdownMenu />`.
|
|
7
|
+
*/
|
|
8
|
+
menuItems: ReactElement[] | ((close: () => void) => ReactElement[]);
|
|
9
|
+
/** Dropdown menu trigger to pass to iTwinUI `<DropdownMenu />` component. */
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* An extended version of iTwinUI `<DropdownMenu />` component with support for layered dropdown menu items.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <LayeredDropdownMenu
|
|
17
|
+
* menuItems={[
|
|
18
|
+
* <LayeredMenuItem key="layered" content={<MyLayeredMenu />}>
|
|
19
|
+
* Layered menu
|
|
20
|
+
* </LayeredMenuItem>,
|
|
21
|
+
* <MenuDivider key="divider" />,
|
|
22
|
+
* <MenuItem ley="regural">Regular menu item</MenuItem>,
|
|
23
|
+
* ]}
|
|
24
|
+
* >
|
|
25
|
+
* <Button>Open dropdown menu</Button>
|
|
26
|
+
* </LayeredDropdownMenu>
|
|
27
|
+
*/
|
|
28
|
+
export declare function LayeredDropdownMenu(props: LayeredDropdownMenuProps): ReactElement;
|
|
29
|
+
interface LayeredDropdownMenuItemProps {
|
|
30
|
+
/** Content of the nested menu. */
|
|
31
|
+
content: ReactNode;
|
|
32
|
+
/** Menu item icon. */
|
|
33
|
+
icon?: ReactNode | undefined;
|
|
34
|
+
/** Menu item label. */
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}
|
|
37
|
+
/** Behaves much like iTwinUI `<MenuItem />` but upon activation advances menu to a nested layer. */
|
|
38
|
+
export declare function LayeredMenuItem(props: LayeredDropdownMenuItemProps): ReactElement;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
import { SvgChevronLeft, SvgChevronRight } from "@itwin/itwinui-icons-react";
|
|
7
|
+
import { DropdownMenu, IconButton, ListItem, MenuDivider, MenuExtraContent } from "@itwin/itwinui-react";
|
|
8
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
9
|
+
import "./LayeredDropdownMenu.css";
|
|
10
|
+
/**
|
|
11
|
+
* An extended version of iTwinUI `<DropdownMenu />` component with support for layered dropdown menu items.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <LayeredDropdownMenu
|
|
15
|
+
* menuItems={[
|
|
16
|
+
* <LayeredMenuItem key="layered" content={<MyLayeredMenu />}>
|
|
17
|
+
* Layered menu
|
|
18
|
+
* </LayeredMenuItem>,
|
|
19
|
+
* <MenuDivider key="divider" />,
|
|
20
|
+
* <MenuItem ley="regural">Regular menu item</MenuItem>,
|
|
21
|
+
* ]}
|
|
22
|
+
* >
|
|
23
|
+
* <Button>Open dropdown menu</Button>
|
|
24
|
+
* </LayeredDropdownMenu>
|
|
25
|
+
*/
|
|
26
|
+
export function LayeredDropdownMenu(props) {
|
|
27
|
+
const [activeMenuItem, setActiveMenuItem] = useState(undefined);
|
|
28
|
+
const menuItems = useMemo(() => {
|
|
29
|
+
const menuItemsCallback = activeMenuItem === undefined
|
|
30
|
+
? (menuItems) => menuItems.map((item, i) => (_jsx(layeredMenuItemIdentifierContext.Provider, { value: i, children: item }, item.key ?? i)))
|
|
31
|
+
: (menuItems) => [
|
|
32
|
+
_jsx(layeredMenuItemIdentifierContext.Provider, { value: activeMenuItem, children: menuItems[activeMenuItem] }, menuItems[activeMenuItem].key ?? activeMenuItem),
|
|
33
|
+
];
|
|
34
|
+
return getMenuItems(props.menuItems, menuItemsCallback);
|
|
35
|
+
}, [activeMenuItem, props.menuItems]);
|
|
36
|
+
return (_jsx(layeredDropdownMenuContext.Provider, { value: { activeMenuItem, setActiveMenuItem }, children: _jsx(DropdownMenu, { menuItems: menuItems, onVisibleChange: (visible) => !visible && setActiveMenuItem(undefined), children: props.children }) }));
|
|
37
|
+
}
|
|
38
|
+
function getMenuItems(menuItems, callback) {
|
|
39
|
+
return (close) => callback(typeof menuItems === "function" ? menuItems(close) : menuItems);
|
|
40
|
+
}
|
|
41
|
+
/** Behaves much like iTwinUI `<MenuItem />` but upon activation advances menu to a nested layer. */
|
|
42
|
+
export function LayeredMenuItem(props) {
|
|
43
|
+
const itemIdentifier = useContext(layeredMenuItemIdentifierContext);
|
|
44
|
+
const { activeMenuItem, setActiveMenuItem } = useContext(layeredDropdownMenuContext);
|
|
45
|
+
if (itemIdentifier === activeMenuItem) {
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsx(MenuExtraContent, { children: _jsx("div", { children: _jsx("div", { onClick: () => setActiveMenuItem(undefined), children: _jsxs(IconButton, { styleType: "borderless", iconProps: { className: "svr-layered-dropdown--back" }, children: [_jsx(SvgChevronLeft, {}), props.children] }) }) }) }, "header"), _jsx(MenuDivider, {}, "separator"), _jsx(MenuExtraContent, { children: props.content }, "content")] }));
|
|
47
|
+
}
|
|
48
|
+
return (_jsxs(ListItem, { tabIndex: -1, onClick: () => setActiveMenuItem(itemIdentifier), actionable: true, children: [props.icon && _jsx(ListItem.Icon, { children: props.icon }), _jsx(ListItem.Content, { children: props.children }), _jsx(ListItem.Icon, { children: _jsx(SvgChevronRight, {}) })] }));
|
|
49
|
+
}
|
|
50
|
+
const layeredDropdownMenuContext = createContext({ activeMenuItem: undefined, setActiveMenuItem: () => { } });
|
|
51
|
+
layeredDropdownMenuContext.displayName = "LayeredDropdownMenuContext";
|
|
52
|
+
const layeredMenuItemIdentifierContext = createContext(undefined);
|
|
53
|
+
layeredMenuItemIdentifierContext.displayName = "LayeredDropdownMenuIdentifier";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface SavedView {
|
|
3
|
+
id: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
groupId?: string | undefined;
|
|
6
|
+
tagIds?: string[] | undefined;
|
|
7
|
+
shared?: boolean | undefined;
|
|
8
|
+
thumbnail?: ReactNode | string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface SavedViewTag {
|
|
11
|
+
id: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SavedViewGroup {
|
|
15
|
+
id: string;
|
|
16
|
+
displayName: string;
|
|
17
|
+
shared?: boolean | undefined;
|
|
18
|
+
}
|
package/lib/SavedView.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
.svr-searchable-submenu {
|
|
7
|
+
display: grid;
|
|
8
|
+
grid: auto minmax(0, 1fr) / 1fr;
|
|
9
|
+
gap: var(--iui-size-xs);
|
|
10
|
+
height: calc(var(--iui-menu-max-height) - 2 * var(--iui-component-height) - 1px);
|
|
11
|
+
user-select: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.svr-searchable-submenu > div {
|
|
15
|
+
overflow: auto;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.svr-searchable-submenu-item {
|
|
19
|
+
width: calc(10 * var(--iui-size-l));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.svr-semibold {
|
|
23
|
+
font-weight: var(--iui-font-weight-semibold);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TYXZlZFZpZXdUaWxlL1NhdmVkVmlld09wdGlvbnMuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIiwiZmlsZSI6IlNhdmVkVmlld09wdGlvbnMuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiogQ29weXJpZ2h0IChjKSBCZW50bGV5IFN5c3RlbXMsIEluY29ycG9yYXRlZC4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiogU2VlIExJQ0VOU0UubWQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSB0ZXJtcyBhbmQgZnVsbCBjb3B5cmlnaHQgbm90aWNlLlxuKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuLnN2ci1zZWFyY2hhYmxlLXN1Ym1lbnUge1xuICBkaXNwbGF5OiBncmlkO1xuICBncmlkOiBhdXRvIG1pbm1heCgwLCAxZnIpIC8gMWZyO1xuICBnYXA6IHZhcigtLWl1aS1zaXplLXhzKTtcbiAgaGVpZ2h0OiBjYWxjKHZhcigtLWl1aS1tZW51LW1heC1oZWlnaHQpIC0gMiAqIHZhcigtLWl1aS1jb21wb25lbnQtaGVpZ2h0KSAtIDFweCk7XG4gIHVzZXItc2VsZWN0OiBub25lO1xufVxuXG4uc3ZyLXNlYXJjaGFibGUtc3VibWVudSA+IGRpdiB7XG4gIG92ZXJmbG93OiBhdXRvO1xufVxuXG4uc3ZyLXNlYXJjaGFibGUtc3VibWVudS1pdGVtIHtcbiAgd2lkdGg6IGNhbGMoMTAgKiB2YXIoLS1pdWktc2l6ZS1sKSk7XG59XG5cbi5zdnItc2VtaWJvbGQge1xuICBmb250LXdlaWdodDogdmFyKC0taXVpLWZvbnQtd2VpZ2h0LXNlbWlib2xkKTtcbn1cbiJdfQ== */
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactElement } from "react";
|
|
2
|
+
import type { SavedViewGroup, SavedViewTag } from "../SavedView.js";
|
|
3
|
+
import "./SavedViewOptions.css";
|
|
4
|
+
/**
|
|
5
|
+
* A collection of useful menu items for {@linkcode SavedViewTile}.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <SavedViewTile
|
|
9
|
+
* savedView={savedView}
|
|
10
|
+
* options={(close) => [<SavedViewOptions.Rename key="rename" icon={<SvgBlank />} onClick={close} />]}
|
|
11
|
+
* onRename={handleRename}
|
|
12
|
+
* />
|
|
13
|
+
*/
|
|
14
|
+
export declare const SavedViewOptions: {
|
|
15
|
+
/**
|
|
16
|
+
* When activated, makes Saved View title enter editable state. Has no effect if the {@linkcode SavedViewTile} does
|
|
17
|
+
* not receive `onRename` prop.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <SavedViewTile
|
|
21
|
+
* savedView={savedView}
|
|
22
|
+
* options={(close) => [<SavedViewOptions.Rename key="rename" icon={<SvgBlank />} onClick={close} />]}
|
|
23
|
+
* onRename={handleRename}
|
|
24
|
+
* />
|
|
25
|
+
*/
|
|
26
|
+
Rename: typeof Rename;
|
|
27
|
+
/**
|
|
28
|
+
* Displays a control for moving Saved View to a group.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <SavedViewOptions.MoveToGroup
|
|
32
|
+
* groups={groups}
|
|
33
|
+
* moveToGroup={handleMoveToGroup}
|
|
34
|
+
* moveToNewGroup={handleMoveTo}
|
|
35
|
+
* icon={<SvgBlank />}
|
|
36
|
+
* />
|
|
37
|
+
*/
|
|
38
|
+
MoveToGroup: typeof MoveToGroup;
|
|
39
|
+
/**
|
|
40
|
+
* Displays a control for managing Saved View tags.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* <SavedViewOptions.ManageTags
|
|
44
|
+
* tags={tags}
|
|
45
|
+
* addTag={handleAddTag}
|
|
46
|
+
* addNewTag={handleAddNewTag}
|
|
47
|
+
* removeTag={handleRemoveTag}
|
|
48
|
+
* icon={<SvgBlank />}
|
|
49
|
+
* />
|
|
50
|
+
*/
|
|
51
|
+
ManageTags: typeof ManageTags;
|
|
52
|
+
/**
|
|
53
|
+
* Displays option for Saved View deletion.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <SavedViewOptions.Delete
|
|
57
|
+
* icon={<SvgBlank />}
|
|
58
|
+
* deleteSavedView={handleDeleteSavedView}
|
|
59
|
+
* />
|
|
60
|
+
*/
|
|
61
|
+
Delete: typeof Delete;
|
|
62
|
+
};
|
|
63
|
+
export interface CreateSavedViewOptionsParams {
|
|
64
|
+
/** When `true`, the returned options contain a `Rename` entry. */
|
|
65
|
+
renameSavedView?: boolean | undefined;
|
|
66
|
+
/** When set, the returned options contain a `Move to group` entry. */
|
|
67
|
+
groupActions?: OmitCommonOptionProps<ComponentProps<typeof SavedViewOptions.MoveToGroup>>;
|
|
68
|
+
/** When set, the returned options contain a `Tags` entry. */
|
|
69
|
+
tagActions?: OmitCommonOptionProps<ComponentProps<typeof SavedViewOptions.ManageTags>>;
|
|
70
|
+
/** When set, the returned options contain a `Delete` entry. */
|
|
71
|
+
deleteSavedView?: ComponentProps<typeof SavedViewOptions.Delete>["deleteSavedView"];
|
|
72
|
+
}
|
|
73
|
+
type OmitCommonOptionProps<T> = Omit<T, "icon">;
|
|
74
|
+
/**
|
|
75
|
+
* Convenience function that returns an array populated with options to be used on {@linkcode SavedViewTile}. Available
|
|
76
|
+
* options are determined by what data is present in the {@linkcode args} argument.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* <SavedViewTile
|
|
80
|
+
* savedView={savedView}
|
|
81
|
+
* options={createSavedViewOptions({ tagActions, groupActions })}
|
|
82
|
+
* editable
|
|
83
|
+
* />
|
|
84
|
+
*/
|
|
85
|
+
export declare function createSavedViewOptions(args: CreateSavedViewOptionsParams): (close: () => void) => ReactElement[];
|
|
86
|
+
interface RenameProps extends CommonOptionProps {
|
|
87
|
+
/** Invoked after triggering the rename action. Often used to close the tile dropdown menu. */
|
|
88
|
+
onClick?: (() => void) | undefined;
|
|
89
|
+
}
|
|
90
|
+
declare function Rename(props: RenameProps): ReactElement;
|
|
91
|
+
interface MoveToGroupProps extends CommonOptionProps {
|
|
92
|
+
/** Available Saved View groups to choose from. */
|
|
93
|
+
groups: SavedViewGroup[];
|
|
94
|
+
/**
|
|
95
|
+
* Invoked when user selects a group from the supplied {@linkcode groups} list.
|
|
96
|
+
* @param savedViewId Id of the associated Saved View.
|
|
97
|
+
* @param groupId Id of the user-selected group.
|
|
98
|
+
*/
|
|
99
|
+
moveToGroup: (savedViewId: string, groupId: string) => void;
|
|
100
|
+
/**
|
|
101
|
+
* When set, allows creation of new Saved View groups. Invoked when user submits a name for the new group.
|
|
102
|
+
* @param savedViewId Id of the associated Saved View.
|
|
103
|
+
* @param groupName User-submitted string for the new Saved View group.
|
|
104
|
+
*/
|
|
105
|
+
moveToNewGroup?: ((savedViewId: string, groupName: string) => void) | undefined;
|
|
106
|
+
}
|
|
107
|
+
declare function MoveToGroup(props: MoveToGroupProps): ReactElement;
|
|
108
|
+
interface ManageTagsProps extends CommonOptionProps {
|
|
109
|
+
/** Available Saved View tags to choose from. */
|
|
110
|
+
tags: SavedViewTag[];
|
|
111
|
+
/**
|
|
112
|
+
* Invoked when user selects a tag from the supplied {@linkcode tags} list.
|
|
113
|
+
* @param savedViewId Id of the associated Saved View.
|
|
114
|
+
* @param tagId Id of the user-selected tag.
|
|
115
|
+
*/
|
|
116
|
+
addTag: (savedViewId: string, tagId: string) => void;
|
|
117
|
+
/**
|
|
118
|
+
* When set, allows creation of new Saved View tags. Invoked when user submits a name for the new tag.
|
|
119
|
+
* @param savedViewId Id of the associated Saved View.
|
|
120
|
+
* @param tagName User-submitted string for the new Saved View tag.
|
|
121
|
+
*/
|
|
122
|
+
addNewTag?: ((savedViewId: string, tagName: string) => void) | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* Invoked when user selects a tag from the supplied {@linkcode tags} list.
|
|
125
|
+
* @param savedViewId Id of the associated Saved View.
|
|
126
|
+
* @param tagId Id of the user-selected tag.
|
|
127
|
+
*/
|
|
128
|
+
removeTag: (savedViewId: string, tagId: string) => void;
|
|
129
|
+
}
|
|
130
|
+
declare function ManageTags(props: ManageTagsProps): ReactElement;
|
|
131
|
+
interface DeleteProps extends CommonOptionProps {
|
|
132
|
+
deleteSavedView: (savedViewId: string) => void;
|
|
133
|
+
}
|
|
134
|
+
declare function Delete(props: DeleteProps): ReactElement;
|
|
135
|
+
interface CommonOptionProps {
|
|
136
|
+
/** Icon to the left of this menu item. */
|
|
137
|
+
icon?: ReactElement | undefined;
|
|
138
|
+
}
|
|
139
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
import { SvgAdd, SvgBlank, SvgCheckmarkSmall, SvgRename, SvgShare } from "@itwin/itwinui-icons-react";
|
|
7
|
+
import { Input, MenuItem, Text } from "@itwin/itwinui-react";
|
|
8
|
+
import Fuse from "fuse.js";
|
|
9
|
+
import { useMemo, useState } from "react";
|
|
10
|
+
import { LayeredMenuItem } from "../LayeredDropdownMenu/LayeredDropdownMenu.js";
|
|
11
|
+
import { useSavedViewsContext } from "../SavedViewsContext.js";
|
|
12
|
+
import { trimInputString } from "../utils.js";
|
|
13
|
+
import { useSavedViewTileContext } from "./SavedViewTileContext.js";
|
|
14
|
+
import "./SavedViewOptions.css";
|
|
15
|
+
/**
|
|
16
|
+
* A collection of useful menu items for {@linkcode SavedViewTile}.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <SavedViewTile
|
|
20
|
+
* savedView={savedView}
|
|
21
|
+
* options={(close) => [<SavedViewOptions.Rename key="rename" icon={<SvgBlank />} onClick={close} />]}
|
|
22
|
+
* onRename={handleRename}
|
|
23
|
+
* />
|
|
24
|
+
*/
|
|
25
|
+
export const SavedViewOptions = {
|
|
26
|
+
/**
|
|
27
|
+
* When activated, makes Saved View title enter editable state. Has no effect if the {@linkcode SavedViewTile} does
|
|
28
|
+
* not receive `onRename` prop.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <SavedViewTile
|
|
32
|
+
* savedView={savedView}
|
|
33
|
+
* options={(close) => [<SavedViewOptions.Rename key="rename" icon={<SvgBlank />} onClick={close} />]}
|
|
34
|
+
* onRename={handleRename}
|
|
35
|
+
* />
|
|
36
|
+
*/
|
|
37
|
+
Rename,
|
|
38
|
+
/**
|
|
39
|
+
* Displays a control for moving Saved View to a group.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* <SavedViewOptions.MoveToGroup
|
|
43
|
+
* groups={groups}
|
|
44
|
+
* moveToGroup={handleMoveToGroup}
|
|
45
|
+
* moveToNewGroup={handleMoveTo}
|
|
46
|
+
* icon={<SvgBlank />}
|
|
47
|
+
* />
|
|
48
|
+
*/
|
|
49
|
+
MoveToGroup,
|
|
50
|
+
/**
|
|
51
|
+
* Displays a control for managing Saved View tags.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* <SavedViewOptions.ManageTags
|
|
55
|
+
* tags={tags}
|
|
56
|
+
* addTag={handleAddTag}
|
|
57
|
+
* addNewTag={handleAddNewTag}
|
|
58
|
+
* removeTag={handleRemoveTag}
|
|
59
|
+
* icon={<SvgBlank />}
|
|
60
|
+
* />
|
|
61
|
+
*/
|
|
62
|
+
ManageTags,
|
|
63
|
+
/**
|
|
64
|
+
* Displays option for Saved View deletion.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* <SavedViewOptions.Delete
|
|
68
|
+
* icon={<SvgBlank />}
|
|
69
|
+
* deleteSavedView={handleDeleteSavedView}
|
|
70
|
+
* />
|
|
71
|
+
*/
|
|
72
|
+
Delete,
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Convenience function that returns an array populated with options to be used on {@linkcode SavedViewTile}. Available
|
|
76
|
+
* options are determined by what data is present in the {@linkcode args} argument.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* <SavedViewTile
|
|
80
|
+
* savedView={savedView}
|
|
81
|
+
* options={createSavedViewOptions({ tagActions, groupActions })}
|
|
82
|
+
* editable
|
|
83
|
+
* />
|
|
84
|
+
*/
|
|
85
|
+
export function createSavedViewOptions(args) {
|
|
86
|
+
return (close) => {
|
|
87
|
+
const options = [];
|
|
88
|
+
if (args.renameSavedView) {
|
|
89
|
+
options.push(_jsx(SavedViewOptions.Rename, { icon: _jsx(SvgRename, {}), onClick: close }, "rename"));
|
|
90
|
+
}
|
|
91
|
+
if (args.groupActions && args.groupActions) {
|
|
92
|
+
options.push(_jsx(SavedViewOptions.MoveToGroup, { groups: args.groupActions.groups, moveToGroup: args.groupActions.moveToGroup, moveToNewGroup: args.groupActions.moveToNewGroup, icon: _jsx(SvgBlank, {}) }, "move"));
|
|
93
|
+
}
|
|
94
|
+
if (args.tagActions && args.tagActions) {
|
|
95
|
+
options.push(_jsx(SavedViewOptions.ManageTags, { tags: args.tagActions.tags, addTag: args.tagActions.addTag, addNewTag: args.tagActions.addNewTag, removeTag: args.tagActions.removeTag, icon: _jsx(SvgBlank, {}) }, "tags"));
|
|
96
|
+
}
|
|
97
|
+
if (args.deleteSavedView) {
|
|
98
|
+
options.push(_jsx(SavedViewOptions.Delete, { icon: _jsx(SvgBlank, {}), deleteSavedView: args.deleteSavedView }, "delete"));
|
|
99
|
+
}
|
|
100
|
+
return options;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function Rename(props) {
|
|
104
|
+
const { setEditingName } = useSavedViewTileContext();
|
|
105
|
+
const { localization } = useSavedViewsContext();
|
|
106
|
+
const handleClick = () => {
|
|
107
|
+
setEditingName(true);
|
|
108
|
+
props.onClick?.();
|
|
109
|
+
};
|
|
110
|
+
return (_jsx(MenuItem, { startIcon: props.icon, onClick: handleClick, children: localization.rename }));
|
|
111
|
+
}
|
|
112
|
+
function MoveToGroup(props) {
|
|
113
|
+
const { savedView } = useSavedViewTileContext();
|
|
114
|
+
const { localization } = useSavedViewsContext();
|
|
115
|
+
return (_jsx(LayeredMenuItem, { icon: _jsx(SvgBlank, {}), content: _jsx(MoveToGroupSubmenu, { savedView: savedView, groups: props.groups, moveToGroup: props.moveToGroup, moveToNewGroup: props.moveToNewGroup }, "move"), children: localization.moveToGroupMenu.moveToGroup }));
|
|
116
|
+
}
|
|
117
|
+
function MoveToGroupSubmenu(props) {
|
|
118
|
+
const { localization: { moveToGroupMenu } } = useSavedViewsContext();
|
|
119
|
+
const handleMoveToGroup = (groupId) => {
|
|
120
|
+
props.moveToGroup(props.savedView.id, groupId);
|
|
121
|
+
};
|
|
122
|
+
const { moveToNewGroup } = props;
|
|
123
|
+
const handleCreate = moveToNewGroup && ((groupName) => {
|
|
124
|
+
moveToNewGroup(props.savedView.id, groupName);
|
|
125
|
+
});
|
|
126
|
+
return (_jsx(SearchableSubmenu, { collection: props.groups, placeholder: props.moveToNewGroup ? moveToGroupMenu.findOrCreateGroup : moveToGroupMenu.findGroup, creationLabel: moveToGroupMenu.createGroup, onCreate: handleCreate, children: (searchResults) => searchResults.map((group) => (_jsxs(MenuItem, { className: "svr-searchable-submenu-item", startIcon: group.shared ? _jsx(SvgShare, {}) : _jsx(SvgBlank, {}), onClick: () => handleMoveToGroup(group.id), disabled: group.id === props.savedView.groupId, children: [group.displayName, group.id === props.savedView.groupId &&
|
|
127
|
+
_jsx(Text, { style: { marginLeft: "var(--iui-size-xs)" }, as: "span", children: moveToGroupMenu.current })] }, group.id))) }));
|
|
128
|
+
}
|
|
129
|
+
function ManageTags(props) {
|
|
130
|
+
const { savedView } = useSavedViewTileContext();
|
|
131
|
+
const { localization } = useSavedViewsContext();
|
|
132
|
+
return (_jsx(LayeredMenuItem, { icon: props.icon, content: _jsx(ManageTagsSubmenu, { savedView: savedView, tags: props.tags, addTag: props.addTag, addNewTag: props.addNewTag, removeTag: props.removeTag }, "tags"), children: localization.tagsMenu.tags }));
|
|
133
|
+
}
|
|
134
|
+
function ManageTagsSubmenu(props) {
|
|
135
|
+
const { localization } = useSavedViewsContext();
|
|
136
|
+
const handleTagClick = (tagId) => {
|
|
137
|
+
if (props.savedView.tagIds?.includes(tagId)) {
|
|
138
|
+
props.removeTag(props.savedView.id, tagId);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
props.addTag(props.savedView.id, tagId);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const { addNewTag } = props;
|
|
145
|
+
const handleCreate = addNewTag && ((tagName) => {
|
|
146
|
+
addNewTag(props.savedView.id, tagName);
|
|
147
|
+
});
|
|
148
|
+
return (_jsx(SearchableSubmenu, { collection: props.tags, placeholder: props.addNewTag ? localization.tagsMenu.findOrCreateTag : localization.tagsMenu.findTag, creationLabel: localization.tagsMenu.createTag, onCreate: handleCreate, children: (searchResults) => searchResults.map((tag) => (_jsx(MenuItem, { className: "svr-searchable-submenu-item", startIcon: props.savedView.tagIds?.includes(tag.id) ? _jsx(SvgCheckmarkSmall, {}) : _jsx(SvgBlank, {}), onClick: () => handleTagClick(tag.id), children: tag.displayName }, tag.id))) }));
|
|
149
|
+
}
|
|
150
|
+
function SearchableSubmenu(props) {
|
|
151
|
+
const { localization } = useSavedViewsContext();
|
|
152
|
+
const fuse = useMemo(() => new Fuse(props.collection, { keys: ["displayName"], threshold: 0.5 }), [props.collection]);
|
|
153
|
+
const [inputValue, setInputValue] = useState("");
|
|
154
|
+
const searchResults = useMemo(() => inputValue.length === 0 ? props.collection : fuse.search(inputValue, { limit: 6 }).map(({ item }) => item), [inputValue, props.collection, fuse]);
|
|
155
|
+
const handleSearchChange = (event) => {
|
|
156
|
+
setInputValue(event.target.value);
|
|
157
|
+
};
|
|
158
|
+
const { onCreate } = props;
|
|
159
|
+
const trimmedValue = trimInputString(inputValue);
|
|
160
|
+
const offerCreate = onCreate && trimmedValue && !searchResults.find((result) => result.displayName === trimmedValue);
|
|
161
|
+
const handleCreate = onCreate && (() => {
|
|
162
|
+
setInputValue("");
|
|
163
|
+
onCreate(trimmedValue);
|
|
164
|
+
});
|
|
165
|
+
return (_jsxs("div", { className: "svr-searchable-submenu", children: [_jsx(Input, { className: "svr-searchable-submenu-item", placeholder: props.placeholder, value: inputValue, onChange: handleSearchChange }), _jsxs("div", { children: [offerCreate &&
|
|
166
|
+
_jsxs(MenuItem, { className: "svr-searchable-submenu-item", startIcon: _jsx(SvgAdd, {}), onClick: handleCreate, children: [props.creationLabel, "\u00A0", _jsx(Text, { className: "svr-semibold", as: "span", children: trimmedValue })] }), props.children(searchResults), searchResults.length === 0 &&
|
|
167
|
+
_jsx(MenuItem, { sublabel: localization.searchableMenu.noSearchResults, size: "default", disabled: true })] })] }));
|
|
168
|
+
}
|
|
169
|
+
function Delete(props) {
|
|
170
|
+
const { savedView } = useSavedViewTileContext();
|
|
171
|
+
const { localization } = useSavedViewsContext();
|
|
172
|
+
return (_jsx(MenuItem, { startIcon: props.icon, onClick: () => props.deleteSavedView(savedView.id), children: localization.delete }));
|
|
173
|
+
}
|