@cccsaurora/howler-ui 2.17.2-patch.640 → 2.17.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.
- package/components/app/App.js +2 -1
- package/components/app/providers/AppBarProvider.d.ts +14 -0
- package/components/app/providers/AppBarProvider.js +22 -0
- package/components/hooks/useMyPreferences.js +8 -5
- package/components/routes/home/HomeSettings.d.ts +13 -0
- package/components/routes/home/HomeSettings.js +23 -0
- package/components/routes/home/ViewRefresh.js +2 -2
- package/components/routes/home/index.js +15 -20
- package/package.json +1 -1
package/components/app/App.js
CHANGED
|
@@ -66,6 +66,7 @@ import useMySearch from '../hooks/useMySearch';
|
|
|
66
66
|
import AppContainer from './AppContainer';
|
|
67
67
|
import AnalyticProvider from './providers/AnalyticProvider';
|
|
68
68
|
import ApiConfigProvider, { ApiConfigContext } from './providers/ApiConfigProvider';
|
|
69
|
+
import AppBarProvider from './providers/AppBarProvider';
|
|
69
70
|
import AvatarProvider from './providers/AvatarProvider';
|
|
70
71
|
import CustomPluginProvider from './providers/CustomPluginProvider';
|
|
71
72
|
import FavouriteProvider from './providers/FavouritesProvider';
|
|
@@ -150,7 +151,7 @@ const MyAppProvider = ({ children }) => {
|
|
|
150
151
|
return (_jsx(ErrorBoundary, { children: _jsx(AppProvider, { preferences: myPreferences, theme: myTheme, sitemap: mySitemap, user: myUser, search: mySearch, children: _jsx(CustomPluginProvider, { children: _jsx(ErrorBoundary, { children: _jsx(ErrorBoundary, { children: _jsx(ViewProvider, { children: _jsx(AvatarProvider, { children: _jsx(ModalProvider, { children: _jsx(FieldProvider, { children: _jsx(LocalStorageProvider, { children: _jsx(SocketProvider, { children: _jsx(HitProvider, { children: _jsx(OverviewProvider, { children: _jsx(AnalyticProvider, { children: _jsx(FavouriteProvider, { children: _jsx(UserListProvider, { children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
|
|
151
152
|
};
|
|
152
153
|
const AppProviderWrapper = () => {
|
|
153
|
-
return (_jsx(I18nextProvider, { i18n: i18n, defaultNS: "translation", children: _jsx(ApiConfigProvider, { children: _jsx(PluginProvider, { pluginStore: howlerPluginStore.pluginStore, children: _jsxs(MyAppProvider, { children: [_jsx(MyApp, {}), _jsx(Modal, {})] }) }) }) }));
|
|
154
|
+
return (_jsx(I18nextProvider, { i18n: i18n, defaultNS: "translation", children: _jsx(ApiConfigProvider, { children: _jsx(PluginProvider, { pluginStore: howlerPluginStore.pluginStore, children: _jsx(AppBarProvider, { children: _jsxs(MyAppProvider, { children: [_jsx(MyApp, {}), _jsx(Modal, {})] }) }) }) }) }));
|
|
154
155
|
};
|
|
155
156
|
const router = createBrowserRouter([
|
|
156
157
|
{
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FC, PropsWithChildren, ReactNode } from 'react';
|
|
2
|
+
interface AppBarItem {
|
|
3
|
+
id: string;
|
|
4
|
+
component: ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export interface AppBarContextType {
|
|
7
|
+
leftItems: AppBarItem[];
|
|
8
|
+
rightItems: AppBarItem[];
|
|
9
|
+
addToAppBar: (alignment: 'left' | 'right', id: string, component: ReactNode) => void;
|
|
10
|
+
removeFromAppBar: (id: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const AppBarContext: import("react").Context<AppBarContextType>;
|
|
13
|
+
declare const AppBarProvider: FC<PropsWithChildren>;
|
|
14
|
+
export default AppBarProvider;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useState } from 'react';
|
|
3
|
+
export const AppBarContext = createContext(null);
|
|
4
|
+
const AppBarProvider = ({ children }) => {
|
|
5
|
+
const [leftItems, setLeftItems] = useState([]);
|
|
6
|
+
const [rightItems, setRightItems] = useState([]);
|
|
7
|
+
const addToAppBar = useCallback((alignment, id, component) => {
|
|
8
|
+
const setter = alignment === 'left' ? setLeftItems : setRightItems;
|
|
9
|
+
setter(prev => {
|
|
10
|
+
if (prev.some(item => item.id === id)) {
|
|
11
|
+
return prev;
|
|
12
|
+
}
|
|
13
|
+
return [...prev, { id, component }];
|
|
14
|
+
});
|
|
15
|
+
}, []);
|
|
16
|
+
const removeFromAppBar = useCallback((id) => {
|
|
17
|
+
setLeftItems(prev => prev.filter(item => item.id !== id));
|
|
18
|
+
setRightItems(prev => prev.filter(item => item.id !== id));
|
|
19
|
+
}, []);
|
|
20
|
+
return (_jsx(AppBarContext.Provider, { value: { leftItems, rightItems, addToAppBar, removeFromAppBar }, children: children }));
|
|
21
|
+
};
|
|
22
|
+
export default AppBarProvider;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Api, Article, Book, Code, Dashboard, Description, ExitToApp, FormatListBulleted, Help, HelpCenter, Key, ManageSearch, QueryStats, SavedSearch, Search, Settings, SettingsSuggest, Shield, Storage, SupervisorAccount, Terminal, Topic } from '@mui/icons-material';
|
|
3
|
+
import { Stack } from '@mui/material';
|
|
3
4
|
import { AppBrand } from '@cccsaurora/howler-ui/branding/AppBrand';
|
|
5
|
+
import { AppBarContext } from '@cccsaurora/howler-ui/components/app/providers/AppBarProvider';
|
|
4
6
|
import Classification from '@cccsaurora/howler-ui/components/elements/display/Classification';
|
|
5
7
|
import DocumentationButton from '@cccsaurora/howler-ui/components/elements/display/DocumentationButton';
|
|
6
8
|
import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
|
|
7
|
-
import { useMemo } from 'react';
|
|
9
|
+
import { Fragment, useContext, useMemo } from 'react';
|
|
8
10
|
import AppMenuBuilder from '@cccsaurora/howler-ui/utils/menuUtils';
|
|
9
11
|
// This is your App Name that will be displayed in the left drawer and the top navbar
|
|
10
12
|
const APP_NAME = 'howler';
|
|
11
13
|
const useMyPreferences = () => {
|
|
14
|
+
const { leftItems, rightItems } = useContext(AppBarContext);
|
|
12
15
|
// The following menu items will show up in the Left Navigation Drawer
|
|
13
16
|
const MENU_ITEMS = useMemo(() => {
|
|
14
17
|
let defaultMenu = [
|
|
@@ -269,12 +272,12 @@ const useMyPreferences = () => {
|
|
|
269
272
|
adminMenuI18nKey: 'adminmenu',
|
|
270
273
|
quickSearchParam: 'query',
|
|
271
274
|
quickSearchURI: '/hits',
|
|
272
|
-
leftAfterBreadcrumbs: _jsx(DocumentationButton, {}),
|
|
273
|
-
rightBeforeSearch: _jsx(Classification, {})
|
|
275
|
+
leftAfterBreadcrumbs: (_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [_jsx(DocumentationButton, {}), leftItems.map(item => (_jsx(Fragment, { children: item.component }, item.id)))] })),
|
|
276
|
+
rightBeforeSearch: (_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", pr: 1, children: [rightItems.map(item => (_jsx(Fragment, { children: item.component }, item.id))), _jsx(Classification, {})] }))
|
|
274
277
|
},
|
|
275
278
|
leftnav: {
|
|
276
279
|
elements: MENU_ITEMS
|
|
277
280
|
}
|
|
278
|
-
}), [USER_MENU_ITEMS, ADMIN_MENU_ITEMS, MENU_ITEMS]);
|
|
281
|
+
}), [USER_MENU_ITEMS, ADMIN_MENU_ITEMS, MENU_ITEMS, leftItems, rightItems]);
|
|
279
282
|
};
|
|
280
283
|
export default useMyPreferences;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FC } from 'react';
|
|
2
|
+
interface HomeSettingsProps {
|
|
3
|
+
/** Whether the dashboard is currently in edit mode. Disables the "Edit" menu item. */
|
|
4
|
+
isEditing: boolean;
|
|
5
|
+
/** Current auto-refresh interval in seconds. */
|
|
6
|
+
refreshRate: number;
|
|
7
|
+
/** Called when the user selects a new refresh rate. */
|
|
8
|
+
onRefreshRateChange: (rate: number) => void;
|
|
9
|
+
/** Called when the user clicks the "Edit" menu item. */
|
|
10
|
+
onEdit: () => void;
|
|
11
|
+
}
|
|
12
|
+
declare const HomeSettings: FC<HomeSettingsProps>;
|
|
13
|
+
export default HomeSettings;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Edit, Settings } from '@mui/icons-material';
|
|
3
|
+
import { FormControl, FormLabel, IconButton, ListItemIcon, Menu, MenuItem, Slider, Tooltip } from '@mui/material';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
const REFRESH_RATES = [15, 30, 60, 300];
|
|
7
|
+
const HomeSettings = ({ isEditing, refreshRate, onRefreshRateChange, onEdit }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: t('page.dashboard.settings.edit'), children: _jsx(IconButton, { onClick: e => setAnchorEl(e.currentTarget), size: "small", children: _jsx(Settings, { color: "primary" }) }) }), _jsxs(Menu, { id: "settings-menu", anchorEl: anchorEl, open: !!anchorEl, onClose: () => setAnchorEl(null), children: [_jsxs(MenuItem, { disabled: isEditing, onClick: () => {
|
|
11
|
+
setAnchorEl(null);
|
|
12
|
+
onEdit();
|
|
13
|
+
}, children: [_jsx(ListItemIcon, { children: _jsx(Edit, {}) }), t('page.dashboard.settings.edit')] }), _jsx(MenuItem, { disableRipple: true, disableTouchRipple: true, sx: { '&:hover': { bgcolor: 'transparent' }, cursor: 'default' }, children: _jsxs(FormControl, { sx: { px: 2, py: 1, minWidth: 250, pointerEvents: 'auto' }, children: [_jsx(FormLabel, { id: "refresh-rate-label", sx: { mb: 2 }, children: t('page.dashboard.settings.refreshRate') }), _jsx(Slider, { "aria-labelledby": "refresh-rate-label", value: REFRESH_RATES.indexOf(refreshRate), onChange: (_, value) => onRefreshRateChange(REFRESH_RATES[value]), step: 1, marks: [
|
|
14
|
+
{ value: 0, label: '15s' },
|
|
15
|
+
{ value: 1, label: '30s' },
|
|
16
|
+
{ value: 2, label: '1m' },
|
|
17
|
+
{ value: 3, label: '5m' }
|
|
18
|
+
], min: 0, max: 3, valueLabelDisplay: "auto", valueLabelFormat: value => {
|
|
19
|
+
const rates = ['15s', '30s', '1m', '5m'];
|
|
20
|
+
return rates[value] || '';
|
|
21
|
+
} })] }) })] })] }));
|
|
22
|
+
};
|
|
23
|
+
export default HomeSettings;
|
|
@@ -52,7 +52,7 @@ const ViewRefresh = forwardRef(({ refreshRate, viewCardCount, onRefresh }, ref)
|
|
|
52
52
|
clearTimeout(timerRef.current);
|
|
53
53
|
};
|
|
54
54
|
}, [progress, isRefreshing, refreshRate, triggerRefresh]);
|
|
55
|
-
return (_jsxs(Box, { sx: { position: 'relative', display: 'inline-flex' }, children: [isRefreshing ? (_jsx(CircularProgress, { variant: "indeterminate" })) : (_jsx(CircularProgress, { variant: "determinate", value: progress })), _jsx(Box, { sx: {
|
|
55
|
+
return (_jsxs(Box, { sx: { position: 'relative', display: 'inline-flex' }, children: [isRefreshing ? (_jsx(CircularProgress, { variant: "indeterminate", size: 32 })) : (_jsx(CircularProgress, { variant: "determinate", value: progress, size: 32 })), _jsx(Box, { sx: {
|
|
56
56
|
top: 0,
|
|
57
57
|
left: 0,
|
|
58
58
|
bottom: 0,
|
|
@@ -61,7 +61,7 @@ const ViewRefresh = forwardRef(({ refreshRate, viewCardCount, onRefresh }, ref)
|
|
|
61
61
|
display: 'flex',
|
|
62
62
|
alignItems: 'center',
|
|
63
63
|
justifyContent: 'center'
|
|
64
|
-
}, children: _jsx(Tooltip, { title: t('refresh'), children: _jsx(IconButton, { onClick: triggerRefresh, disabled: isRefreshing, color: "primary", children: _jsx(Refresh, {}) }) }) })] }));
|
|
64
|
+
}, children: _jsx(Tooltip, { title: t('refresh'), children: _jsx(IconButton, { onClick: triggerRefresh, disabled: isRefreshing, color: "primary", size: "small", children: _jsx(Refresh, {}) }) }) })] }));
|
|
65
65
|
});
|
|
66
66
|
ViewRefresh.displayName = 'ViewRefresh';
|
|
67
67
|
export default ViewRefresh;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core';
|
|
3
3
|
import { SortableContext, arrayMove, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
|
|
4
|
-
import { Cancel, Check, Close,
|
|
5
|
-
import { Alert, AlertTitle, CircularProgress,
|
|
4
|
+
import { Cancel, Check, Close, OpenInNew } from '@mui/icons-material';
|
|
5
|
+
import { Alert, AlertTitle, CircularProgress, Grid, IconButton, Stack, Typography } from '@mui/material';
|
|
6
6
|
import api from '@cccsaurora/howler-ui/api';
|
|
7
7
|
import { AppBrand } from '@cccsaurora/howler-ui/branding/AppBrand';
|
|
8
8
|
import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
|
|
9
9
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
10
|
+
import { AppBarContext } from '@cccsaurora/howler-ui/components/app/providers/AppBarProvider';
|
|
10
11
|
import CustomButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomButton';
|
|
11
12
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
12
13
|
import useMyUserFunctions from '@cccsaurora/howler-ui/components/hooks/useMyUserFunctions';
|
|
13
14
|
import dayjs from 'dayjs';
|
|
14
15
|
import isEqual from 'lodash-es/isEqual';
|
|
15
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
17
|
import { useTranslation } from 'react-i18next';
|
|
17
18
|
import { Link } from 'react-router-dom';
|
|
18
19
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
@@ -20,13 +21,14 @@ import ErrorBoundary from '../ErrorBoundary';
|
|
|
20
21
|
import AddNewCard from './AddNewCard';
|
|
21
22
|
import AnalyticCard, {} from './AnalyticCard';
|
|
22
23
|
import EntryWrapper from './EntryWrapper';
|
|
24
|
+
import HomeSettings from './HomeSettings';
|
|
23
25
|
import ViewCard, {} from './ViewCard';
|
|
24
26
|
import ViewRefresh, {} from './ViewRefresh';
|
|
25
27
|
const LUCENE_DATE_FMT = 'YYYY-MM-DD[T]HH:mm:ss';
|
|
26
|
-
const REFRESH_RATES = [15, 30, 60, 300];
|
|
27
28
|
const Home = () => {
|
|
28
29
|
const { t } = useTranslation();
|
|
29
30
|
const { user, setUser } = useAppUser();
|
|
31
|
+
const { addToAppBar, removeFromAppBar } = useContext(AppBarContext);
|
|
30
32
|
const { setDashboard, setRefreshRate: setRefreshRateBackend } = useMyUserFunctions();
|
|
31
33
|
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
32
34
|
const [lastViewed, setLastViewed] = useMyLocalStorageItem(StorageKey.LAST_VIEW, dayjs().utc().format(LUCENE_DATE_FMT));
|
|
@@ -34,7 +36,6 @@ const Home = () => {
|
|
|
34
36
|
const [isEditing, setIsEditing] = useState(false);
|
|
35
37
|
const [updatedHitTotal, setUpdatedHitTotal] = useState(0);
|
|
36
38
|
const [dashboard, setStateDashboard] = useState(user.dashboard ?? []);
|
|
37
|
-
const [openSettings, setOpenSettings] = useState(null);
|
|
38
39
|
const [refreshRate, setRefreshRate] = useState(user.refresh_rate ?? 15);
|
|
39
40
|
const [refreshTick, setRefreshTick] = useState(null);
|
|
40
41
|
const viewRefreshRef = useRef(null);
|
|
@@ -75,9 +76,6 @@ const Home = () => {
|
|
|
75
76
|
const handleRefresh = useCallback(() => {
|
|
76
77
|
setRefreshTick(Symbol());
|
|
77
78
|
}, []);
|
|
78
|
-
const handleOpenSettings = (event) => {
|
|
79
|
-
setOpenSettings(event.currentTarget);
|
|
80
|
-
};
|
|
81
79
|
const saveChanges = useCallback(async () => {
|
|
82
80
|
setLoading(true);
|
|
83
81
|
try {
|
|
@@ -119,18 +117,15 @@ const Home = () => {
|
|
|
119
117
|
};
|
|
120
118
|
}, []);
|
|
121
119
|
const viewCardCount = useMemo(() => (dashboard ?? []).filter(e => e.type === 'view').length, [dashboard]);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const rates = ['15s', '30s', '1m', '5m'];
|
|
132
|
-
return rates[value] || '';
|
|
133
|
-
} })] }) })] })] }), updatedHitTotal > 0 && (_jsxs(Alert, { severity: "info", variant: "outlined", action: _jsxs(Stack, { spacing: 1, direction: "row", children: [_jsx(IconButton, { color: "info", component: Link, to: `/hits?query=${encodeURIComponent(updateQuery)}`, onClick: () => setLastViewed(dayjs().utc().format(LUCENE_DATE_FMT)), children: _jsx(OpenInNew, {}) }), _jsx(IconButton, { color: "info", onClick: () => {
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
addToAppBar('left', 'view_refresh', _jsx(ViewRefresh, { ref: viewRefreshRef, refreshRate: refreshRate, viewCardCount: viewCardCount, onRefresh: handleRefresh }));
|
|
122
|
+
addToAppBar('left', 'home_settings', _jsx(HomeSettings, { isEditing: isEditing, refreshRate: refreshRate, onRefreshRateChange: handleRefreshRateChange, onEdit: () => setIsEditing(true) }));
|
|
123
|
+
return () => {
|
|
124
|
+
removeFromAppBar('view_refresh');
|
|
125
|
+
removeFromAppBar('home_settings');
|
|
126
|
+
};
|
|
127
|
+
}, [addToAppBar, handleRefresh, handleRefreshRateChange, isEditing, refreshRate, removeFromAppBar, viewCardCount]);
|
|
128
|
+
return (_jsx(PageCenter, { maxWidth: "100%", textAlign: "left", height: "100%", children: _jsx(ErrorBoundary, { children: _jsxs(Stack, { direction: "column", spacing: 1, sx: { height: '100%' }, children: [_jsxs(Stack, { direction: "row", justifyContent: "end", spacing: 1, children: [isEditing && (_jsx(CustomButton, { variant: "outlined", size: "small", color: "error", startIcon: _jsx(Cancel, {}), onClick: discardChanges, children: t('cancel') })), isEditing && (_jsx(CustomButton, { variant: "outlined", size: "small", disabled: isEqual(dashboard, user.dashboard), color: 'success', startIcon: loading ? _jsx(CircularProgress, { size: 20 }) : _jsx(Check, {}), onClick: saveChanges, children: t('save') }))] }), updatedHitTotal > 0 && (_jsxs(Alert, { severity: "info", variant: "outlined", action: _jsxs(Stack, { spacing: 1, direction: "row", children: [_jsx(IconButton, { color: "info", component: Link, to: `/hits?query=${encodeURIComponent(updateQuery)}`, onClick: () => setLastViewed(dayjs().utc().format(LUCENE_DATE_FMT)), children: _jsx(OpenInNew, {}) }), _jsx(IconButton, { color: "info", onClick: () => {
|
|
134
129
|
setLastViewed(dayjs().utc().format(LUCENE_DATE_FMT));
|
|
135
130
|
setUpdatedHitTotal(0);
|
|
136
131
|
}, children: _jsx(Close, {}) })] }), children: [_jsx(AlertTitle, { children: t('route.home.alert.updated.title') }), t('route.home.alert.updated.description', { count: updatedHitTotal })] })), _jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: (dashboard ?? []).map(entry => getIdFromEntry(entry)), children: _jsxs(Grid, { container: true, spacing: 1, alignItems: "stretch", sx: [
|