@cccsaurora/howler-ui 2.19.0-dev.1099 → 2.19.0-dev.1148
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 +4 -2
- package/components/app/providers/GridColumnsProvider.d.ts +52 -0
- package/components/app/providers/GridColumnsProvider.js +177 -0
- package/components/app/providers/GridColumnsProvider.test.js +255 -0
- package/components/elements/addons/buttons/CustomIconButton.test.d.ts +1 -0
- package/components/elements/addons/buttons/CustomIconButton.test.js +81 -0
- package/components/elements/addons/layout/FlexVertical.test.d.ts +1 -0
- package/components/elements/addons/layout/FlexVertical.test.js +32 -0
- package/components/elements/addons/layout/vsbox/VSBoxContent.test.d.ts +1 -0
- package/components/elements/addons/layout/vsbox/VSBoxContent.test.js +26 -0
- package/components/elements/addons/search/phrase/WordLexer.test.d.ts +1 -0
- package/components/elements/addons/search/phrase/WordLexer.test.js +55 -0
- package/components/elements/hit/HitContextMenu.test.d.ts +1 -0
- package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.d.ts +2 -1
- package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.js +3 -3
- package/components/{routes/hits/search → elements/hit}/grid/HitRow.d.ts +1 -1
- package/components/elements/hit/grid/HitTable.d.ts +12 -0
- package/components/elements/hit/grid/HitTable.js +82 -0
- package/components/elements/view/LayoutToggle.d.ts +8 -0
- package/components/elements/view/LayoutToggle.js +12 -0
- package/components/elements/view/ViewTitle.d.ts +3 -0
- package/components/elements/view/ViewTitle.js +6 -3
- package/components/hooks/useMyTheme.test.d.ts +1 -0
- package/components/hooks/useMyTheme.test.js +35 -0
- package/components/routes/advanced/eqlTokenProvider.test.d.ts +1 -0
- package/components/routes/advanced/eqlTokenProvider.test.js +34 -0
- package/components/routes/advanced/luceneTokenProvider.test.d.ts +1 -0
- package/components/routes/advanced/luceneTokenProvider.test.js +34 -0
- package/components/routes/hits/search/HitBrowser.js +2 -1
- package/components/routes/hits/search/SearchPane.js +6 -8
- package/components/routes/hits/search/ViewLink.js +3 -2
- package/components/routes/hits/search/grid/HitGrid.js +17 -85
- package/components/routes/hits/search/shared/LayoutSettings.js +25 -0
- package/components/routes/hits/search/shared/SearchActionMenu.d.ts +4 -0
- package/components/routes/hits/search/shared/SearchActionMenu.js +17 -0
- package/components/routes/home/HomeSettings.test.d.ts +1 -0
- package/components/routes/home/HomeSettings.test.js +44 -0
- package/components/routes/home/ViewCard.js +1 -1
- package/components/routes/overviews/markdownExtendedTokenProvider.test.d.ts +1 -0
- package/components/routes/overviews/markdownExtendedTokenProvider.test.js +50 -0
- package/components/routes/views/ViewComposer.js +32 -6
- package/locales/en/translation.json +6 -3
- package/locales/fr/translation.json +6 -3
- package/models/entities/generated/Column.d.ts +7 -0
- package/models/entities/generated/Host.d.ts +1 -0
- package/models/entities/generated/Settings.d.ts +4 -0
- package/package.json +2 -1
- package/rest/AxiosClient.test.d.ts +1 -0
- package/rest/AxiosClient.test.js +184 -0
- package/utils/stringUtils.d.ts +1 -0
- package/utils/stringUtils.js +7 -0
- package/utils/stringUtils.test.js +21 -1
- package/utils/utils.test.js +119 -2
- package/components/routes/hits/search/LayoutSettings.js +0 -24
- /package/components/{routes/hits/search/HitContextMenu.test.d.ts → app/providers/GridColumnsProvider.test.d.ts} +0 -0
- /package/components/{routes/hits/search → elements/hit}/HitContextMenu.d.ts +0 -0
- /package/components/{routes/hits/search → elements/hit}/HitContextMenu.js +0 -0
- /package/components/{routes/hits/search → elements/hit}/HitContextMenu.test.js +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.d.ts +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.js +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.d.ts +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.js +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/HitRow.js +0 -0
- /package/components/routes/hits/search/{BundleParentMenu.d.ts → shared/BundleParentMenu.d.ts} +0 -0
- /package/components/routes/hits/search/{BundleParentMenu.js → shared/BundleParentMenu.js} +0 -0
- /package/components/routes/hits/search/{LayoutSettings.d.ts → shared/LayoutSettings.d.ts} +0 -0
|
@@ -1,51 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow, ToggleButton, ToggleButtonGroup, Typography, useTheme } from '@mui/material';
|
|
6
|
-
import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
|
|
2
|
+
import { Info } from '@mui/icons-material';
|
|
3
|
+
import { LinearProgress, Paper, Stack, TableBody, Typography, useTheme } from '@mui/material';
|
|
4
|
+
import { GridColumnsContext } from '@cccsaurora/howler-ui/components/app/providers/GridColumnsProvider';
|
|
7
5
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
8
6
|
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
9
7
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
10
8
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
11
9
|
import DevelopmentBanner from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentBanner';
|
|
10
|
+
import AddColumnModal from '@cccsaurora/howler-ui/components/elements/hit/grid/AddColumnModal';
|
|
11
|
+
import HitTable from '@cccsaurora/howler-ui/components/elements/hit/grid/HitTable';
|
|
12
|
+
import HitContextMenu from '@cccsaurora/howler-ui/components/elements/hit/HitContextMenu';
|
|
12
13
|
import useHitSelection from '@cccsaurora/howler-ui/components/hooks/useHitSelection';
|
|
13
|
-
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
14
14
|
import { uniq } from 'lodash-es';
|
|
15
|
-
import { useCallback,
|
|
15
|
+
import { useCallback, useContext, useMemo } from 'react';
|
|
16
16
|
import { useTranslation } from 'react-i18next';
|
|
17
17
|
import { useContextSelector } from 'use-context-selector';
|
|
18
|
-
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
19
|
-
import HitContextMenu from '../HitContextMenu';
|
|
20
18
|
import HitQuery from '../HitQuery';
|
|
21
19
|
import QuerySettings from '../QuerySettings';
|
|
22
|
-
import
|
|
23
|
-
import ColumnHeader from './ColumnHeader';
|
|
24
|
-
import HitRow from './HitRow';
|
|
20
|
+
import SearchActionMenu from '../shared/SearchActionMenu';
|
|
25
21
|
const HitGrid = () => {
|
|
26
22
|
const { t } = useTranslation();
|
|
27
23
|
const theme = useTheme();
|
|
28
|
-
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
29
24
|
const { onClick } = useHitSelection();
|
|
30
|
-
const { getMatchingAnalytic } = useMatchers();
|
|
31
25
|
const search = useContextSelector(HitSearchContext, ctx => ctx.search);
|
|
32
|
-
const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
|
|
33
|
-
const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
|
|
34
26
|
const response = useContextSelector(HitSearchContext, ctx => ctx.response);
|
|
35
27
|
const searching = useContextSelector(HitSearchContext, ctx => ctx.searching);
|
|
36
28
|
const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
|
|
37
29
|
const query = useContextSelector(ParameterContext, ctx => ctx.query);
|
|
38
30
|
const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
|
|
39
|
-
const
|
|
40
|
-
const [columns, setColumns] = useMyLocalStorageItem(StorageKey.GRID_COLUMNS, [
|
|
41
|
-
'howler.outline.threat',
|
|
42
|
-
'howler.outline.target',
|
|
43
|
-
'howler.outline.indicators',
|
|
44
|
-
'howler.outline.summary'
|
|
45
|
-
]);
|
|
46
|
-
const [columnWidths, setColumnWidths] = useMyLocalStorageItem(StorageKey.GRID_COLUMN_WIDTHS, {});
|
|
47
|
-
const [analyticIds, setAnalyticIds] = useState({});
|
|
48
|
-
const resizingCol = useRef();
|
|
31
|
+
const { columns, setColumns, isReady } = useContext(GridColumnsContext);
|
|
49
32
|
const showSelectBar = useMemo(() => {
|
|
50
33
|
if (selectedHits.length > 1) {
|
|
51
34
|
return true;
|
|
@@ -55,60 +38,12 @@ const HitGrid = () => {
|
|
|
55
38
|
}
|
|
56
39
|
return false;
|
|
57
40
|
}, [selected, selectedHits]);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
response?.items.forEach(hit => {
|
|
60
|
-
if (!analyticIds[hit.howler.analytic]) {
|
|
61
|
-
getMatchingAnalytic(hit).then(_analytic => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [hit.howler.analytic]: _analytic.analytic_id })));
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
65
|
-
}, [analyticIds, response]);
|
|
66
|
-
const onMouseMove = useCallback((event) => {
|
|
67
|
-
event.stopPropagation();
|
|
68
|
-
event.preventDefault();
|
|
69
|
-
const [col, element] = resizingCol.current;
|
|
70
|
-
const rect = element.getBoundingClientRect();
|
|
71
|
-
document.querySelectorAll(`.col-${col.replaceAll('.', '-')}`).forEach(el => {
|
|
72
|
-
el.style.maxWidth = rect.width + event.movementX + 'px';
|
|
73
|
-
el.style.width = rect.width + event.movementX + 'px';
|
|
74
|
-
});
|
|
75
|
-
}, []);
|
|
76
|
-
const onMouseUp = useCallback(() => {
|
|
77
|
-
const [col, element] = resizingCol.current;
|
|
78
|
-
setColumnWidths({
|
|
79
|
-
...columnWidths,
|
|
80
|
-
[col]: element.style.width
|
|
81
|
-
});
|
|
82
|
-
element.style.width = null;
|
|
83
|
-
element.style.maxWidth = null;
|
|
84
|
-
document.querySelectorAll(`.col-${col.replaceAll('.', '-')}`).forEach(el => {
|
|
85
|
-
el.style.maxWidth = null;
|
|
86
|
-
el.style.width = null;
|
|
87
|
-
});
|
|
88
|
-
window.removeEventListener('mousemove', onMouseMove);
|
|
89
|
-
window.removeEventListener('mouseup', onMouseUp);
|
|
90
|
-
}, [columnWidths, onMouseMove, setColumnWidths]);
|
|
91
|
-
const onMouseDown = useCallback((col, event) => {
|
|
92
|
-
event.stopPropagation();
|
|
93
|
-
event.preventDefault();
|
|
94
|
-
resizingCol.current = [col, event.target.parentElement];
|
|
95
|
-
window.addEventListener('mousemove', onMouseMove);
|
|
96
|
-
window.addEventListener('mouseup', onMouseUp);
|
|
97
|
-
}, [onMouseMove, onMouseUp]);
|
|
98
41
|
const onScroll = useCallback((event) => {
|
|
99
42
|
const target = event.target;
|
|
100
43
|
if (target.scrollHeight - target.scrollTop === target.clientHeight) {
|
|
101
44
|
search(query, true);
|
|
102
45
|
}
|
|
103
46
|
}, [query, search]);
|
|
104
|
-
const handleDragEnd = useCallback((event) => {
|
|
105
|
-
const { active, over } = event;
|
|
106
|
-
if (over && active.id !== over.id) {
|
|
107
|
-
const oldIndex = (columns ?? []).findIndex(entry => entry === active.id);
|
|
108
|
-
const newIndex = (columns ?? []).findIndex(entry => entry === over.id);
|
|
109
|
-
setColumns(arrayMove(columns, oldIndex, newIndex));
|
|
110
|
-
}
|
|
111
|
-
}, [columns, setColumns]);
|
|
112
47
|
const getSelectedId = useCallback((event) => {
|
|
113
48
|
const target = event.target;
|
|
114
49
|
const selectedElement = target.closest('[id]');
|
|
@@ -117,16 +52,13 @@ const HitGrid = () => {
|
|
|
117
52
|
}
|
|
118
53
|
return selectedElement.id;
|
|
119
54
|
}, []);
|
|
120
|
-
return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
borderRight: 'thin solid',
|
|
129
|
-
borderRightColor: 'divider'
|
|
130
|
-
}, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(HitContextMenu, { Component: TableBody, getSelectedId: getSelectedId, children: [response?.items.map(hit => (_jsx(HitRow, { hit: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn, onClick: onClick }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
|
|
55
|
+
return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), _jsx(SearchActionMenu, { query: query })] }), _jsx(Stack, { direction: "row", spacing: 1, children: _jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(HitQuery, { searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
|
|
56
|
+
position: 'absolute',
|
|
57
|
+
left: 0,
|
|
58
|
+
right: 0,
|
|
59
|
+
bottom: 0,
|
|
60
|
+
borderBottomLeftRadius: theme.shape.borderRadius,
|
|
61
|
+
borderBottomRightRadius: theme.shape.borderRadius
|
|
62
|
+
} }))] }) }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", alignItems: "center", children: [_jsx(QuerySettings, { boxSx: { flex: 1 } }), _jsx(AddColumnModal, { columns: columns, addColumn: key => isReady && setColumns(uniq([...columns, key])) })] }), response && (_jsx(SearchTotal, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5 }, variant: "body2", offset: response.offset, pageLength: response.rows, total: response.total })), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsx(HitTable, { query: query, items: response?.items, refreshItems: search, ContextMenu: HitContextMenu, contextMenuProps: { Component: TableBody, getSelectedId: getSelectedId }, onItemClick: onClick }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
|
|
131
63
|
};
|
|
132
64
|
export default HitGrid;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowDropDown, InfoOutlined, Settings, ViewComfy, ViewCompact, ViewModule } from '@mui/icons-material';
|
|
3
|
+
import { Checkbox, Divider, FormLabel, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip } from '@mui/material';
|
|
4
|
+
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
5
|
+
import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
|
|
6
|
+
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
7
|
+
import LayoutToggle from '@cccsaurora/howler-ui/components/elements/view/LayoutToggle';
|
|
8
|
+
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
9
|
+
import { useTranslation } from 'react-i18next';
|
|
10
|
+
import { useContextSelector } from 'use-context-selector';
|
|
11
|
+
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
12
|
+
const LayoutSettings = () => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
|
|
15
|
+
const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
|
|
16
|
+
const [hitLayout, setHitLayout] = useMyLocalStorageItem(StorageKey.HIT_LAYOUT, false);
|
|
17
|
+
const [templateFieldCount, setTemplateFieldCount] = useMyLocalStorageItem(StorageKey.TEMPLATE_FIELD_COUNT, null);
|
|
18
|
+
return (_jsx(ChipPopper, { icon: _jsx(Tooltip, { title: t('search.layout.settings'), children: _jsx(Settings, {}) }), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, disablePortal: false, slotProps: { chip: { size: 'medium', 'aria-label': t('search.layout.settings') } }, placement: "bottom-end", children: _jsxs(Stack, { spacing: 1, alignItems: "start", children: [_jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "display_type", children: t('page.settings.local.hits.display_type') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.display_type.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsx(LayoutToggle, { displayType: displayType, setDisplayType: setDisplayType }), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "layout", children: t('page.settings.local.hits.layout') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.layout.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsxs(ToggleButtonGroup, { exclusive: true, size: "small", value: hitLayout, onChange: (_, value) => setHitLayout(value), "aria-labelledby": "layout", children: [_jsx(ToggleButton, { value: HitLayout.DENSE, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewCompact, {}), _jsx("span", { children: t('page.settings.local.hits.layout.dense') })] }) }), _jsx(ToggleButton, { value: HitLayout.NORMAL, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewModule, {}), _jsx("span", { children: t('page.settings.local.hits.layout.normal') })] }) }), _jsx(ToggleButton, { value: HitLayout.COMFY, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewComfy, {}), _jsx("span", { children: t('page.settings.local.hits.layout.comfy') })] }) })] }), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", alignSelf: "stretch", children: [_jsx(FormLabel, { id: "field_count", children: t('page.settings.local.hits.field_count') }), _jsx("div", { style: { flex: 1 } }), _jsx(Tooltip, { title: t('page.settings.local.hits.field_count.description'), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }), _jsxs(Stack, { direction: "row", spacing: 0.5, alignSelf: "stretch", children: [_jsx(Checkbox, { checked: templateFieldCount !== null, onChange: (_, checked) => setTemplateFieldCount(checked ? 3 : null), size: "small" }), _jsx(TextField, { type: "number", size: "small", disabled: templateFieldCount === null, value: templateFieldCount ?? 3, fullWidth: true, onChange: e => {
|
|
19
|
+
const val = parseInt(e.target.value);
|
|
20
|
+
if (!isNaN(val)) {
|
|
21
|
+
setTemplateFieldCount(Math.min(15, Math.max(0, val)));
|
|
22
|
+
}
|
|
23
|
+
}, inputProps: { min: 0, max: 15, 'aria-labelledby': 'field_count' } })] })] }) }));
|
|
24
|
+
};
|
|
25
|
+
export default LayoutSettings;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Close, SavedSearch, Terminal } from '@mui/icons-material';
|
|
3
|
+
import { IconButton, Stack, Tooltip } from '@mui/material';
|
|
4
|
+
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|
7
|
+
import { useContextSelector } from 'use-context-selector';
|
|
8
|
+
import BundleParentMenu from './BundleParentMenu';
|
|
9
|
+
import LayoutSettings from './LayoutSettings';
|
|
10
|
+
const SearchActionMenu = ({ query }) => {
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
const routeParams = useParams();
|
|
14
|
+
const bundleHit = useContextSelector(HitContext, ctx => location.pathname.startsWith('/bundles') ? ctx.hits[routeParams.id] : null);
|
|
15
|
+
return (_jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", children: [bundleHit?.howler.bundles.length > 0 && _jsx(BundleParentMenu, { bundle: bundleHit }), bundleHit && (_jsx(Tooltip, { title: t('hit.bundle.close'), children: _jsx(IconButton, { size: "small", onClick: () => navigate('/search'), children: _jsx(Close, {}) }) })), _jsx(Tooltip, { title: t('route.views.save'), children: _jsx(IconButton, { component: Link, disabled: !query, to: `/views/create?query=${query}`, children: _jsx(SavedSearch, {}) }) }), _jsx(Tooltip, { title: t('route.actions.save'), children: _jsx(IconButton, { component: Link, disabled: !query, to: `/action/execute?query=${query}`, children: _jsx(Terminal, {}) }) }), _jsx(LayoutSettings, {})] }));
|
|
16
|
+
};
|
|
17
|
+
export default SearchActionMenu;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/// <reference types="vitest" />
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import i18n from '@cccsaurora/howler-ui/i18n';
|
|
6
|
+
import { I18nextProvider } from 'react-i18next';
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import HomeSettings from './HomeSettings';
|
|
9
|
+
const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
|
|
10
|
+
describe('HomeSettings', () => {
|
|
11
|
+
let onRefreshRateChange;
|
|
12
|
+
let onEdit;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
onRefreshRateChange = vi.fn();
|
|
15
|
+
onEdit = vi.fn();
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
it('renders the settings icon button', () => {
|
|
19
|
+
render(_jsx(HomeSettings, { isEditing: false, refreshRate: 30, onRefreshRateChange: onRefreshRateChange, onEdit: onEdit }), { wrapper: Wrapper });
|
|
20
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
it('opens the settings menu when the icon button is clicked', async () => {
|
|
23
|
+
const user = userEvent.setup();
|
|
24
|
+
render(_jsx(HomeSettings, { isEditing: false, refreshRate: 30, onRefreshRateChange: onRefreshRateChange, onEdit: onEdit }), { wrapper: Wrapper });
|
|
25
|
+
await user.click(screen.getByRole('button'));
|
|
26
|
+
// The menu should now be open – the edit menu item should be visible
|
|
27
|
+
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
it('calls onEdit when the Edit menu item is clicked', async () => {
|
|
30
|
+
const user = userEvent.setup();
|
|
31
|
+
render(_jsx(HomeSettings, { isEditing: false, refreshRate: 30, onRefreshRateChange: onRefreshRateChange, onEdit: onEdit }), { wrapper: Wrapper });
|
|
32
|
+
await user.click(screen.getByRole('button'));
|
|
33
|
+
const editItem = screen.getAllByRole('menuitem')[0];
|
|
34
|
+
await user.click(editItem);
|
|
35
|
+
expect(onEdit).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
|
+
it('disables the Edit menu item when isEditing=true', async () => {
|
|
38
|
+
const user = userEvent.setup();
|
|
39
|
+
render(_jsx(HomeSettings, { isEditing: true, refreshRate: 30, onRefreshRateChange: onRefreshRateChange, onEdit: onEdit }), { wrapper: Wrapper });
|
|
40
|
+
await user.click(screen.getByRole('button'));
|
|
41
|
+
const editItem = screen.getAllByRole('menuitem')[0];
|
|
42
|
+
expect(editItem).toHaveAttribute('aria-disabled', 'true');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -6,9 +6,9 @@ import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppLi
|
|
|
6
6
|
import { useHitContextSelector } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
7
7
|
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
8
8
|
import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
|
|
9
|
+
import HitContextMenu from '@cccsaurora/howler-ui/components/elements/hit/HitContextMenu';
|
|
9
10
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
10
11
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
11
|
-
import HitContextMenu from '@cccsaurora/howler-ui/components/routes/hits/search/HitContextMenu';
|
|
12
12
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
14
14
|
import { Link, useNavigate } from 'react-router-dom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { conf, language } from './markdownExtendedTokenProvider';
|
|
4
|
+
describe('markdownExtendedTokenProvider', () => {
|
|
5
|
+
describe('conf', () => {
|
|
6
|
+
it('exports a language configuration object', () => {
|
|
7
|
+
expect(conf).toBeDefined();
|
|
8
|
+
expect(typeof conf).toBe('object');
|
|
9
|
+
});
|
|
10
|
+
it('defines block comment markers', () => {
|
|
11
|
+
expect(conf.comments?.blockComment).toEqual(['<!--', '-->']);
|
|
12
|
+
});
|
|
13
|
+
it('defines bracket pairs', () => {
|
|
14
|
+
expect(Array.isArray(conf.brackets)).toBe(true);
|
|
15
|
+
expect(conf.brackets.length).toBeGreaterThan(0);
|
|
16
|
+
});
|
|
17
|
+
it('defines auto-closing pairs', () => {
|
|
18
|
+
expect(Array.isArray(conf.autoClosingPairs)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it('defines folding markers', () => {
|
|
21
|
+
expect(conf.folding?.markers?.start).toBeInstanceOf(RegExp);
|
|
22
|
+
expect(conf.folding?.markers?.end).toBeInstanceOf(RegExp);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('language', () => {
|
|
26
|
+
it('exports a monarch language definition', () => {
|
|
27
|
+
expect(language).toBeDefined();
|
|
28
|
+
expect(typeof language).toBe('object');
|
|
29
|
+
});
|
|
30
|
+
it('has a root tokenizer state', () => {
|
|
31
|
+
expect(language.tokenizer).toBeDefined();
|
|
32
|
+
expect(Array.isArray(language.tokenizer.root)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it('uses an empty string as the default token', () => {
|
|
35
|
+
expect(language.defaultToken).toBe('');
|
|
36
|
+
});
|
|
37
|
+
it('includes Handlebars operators in the language definition', () => {
|
|
38
|
+
const operators = language.handlebars_operators;
|
|
39
|
+
expect(Array.isArray(operators)).toBe(true);
|
|
40
|
+
expect(operators).toContain('#if');
|
|
41
|
+
expect(operators).toContain('/if');
|
|
42
|
+
});
|
|
43
|
+
it('includes a handlebars tokenizer state', () => {
|
|
44
|
+
expect(language.tokenizer).toHaveProperty('handlebars');
|
|
45
|
+
});
|
|
46
|
+
it('includes an html tokenizer state', () => {
|
|
47
|
+
expect(language.tokenizer).toHaveProperty('html');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { useCallback, useContext, useEffect, useState } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { HelpOutline, Save } from '@mui/icons-material';
|
|
5
|
-
import { Alert, Checkbox, CircularProgress, LinearProgress, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip, Typography } from '@mui/material';
|
|
4
|
+
import { HelpOutline, Save, Settings } from '@mui/icons-material';
|
|
5
|
+
import { Alert, Checkbox, CircularProgress, LinearProgress, Paper, Stack, TextField, ToggleButton, ToggleButtonGroup, Tooltip, Typography } from '@mui/material';
|
|
6
6
|
import api from '@cccsaurora/howler-ui/api';
|
|
7
7
|
import AppListEmpty from '@cccsaurora/howler-ui/commons/components/display/AppListEmpty';
|
|
8
8
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
9
|
+
import { GridColumnsContext } from '@cccsaurora/howler-ui/components/app/providers/GridColumnsProvider';
|
|
9
10
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
11
|
+
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
10
12
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
11
13
|
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
12
14
|
import CustomButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomButton';
|
|
15
|
+
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
13
16
|
import FlexPort from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexPort';
|
|
14
17
|
import VSBox from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBox';
|
|
15
18
|
import VSBoxContent from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxContent';
|
|
16
19
|
import VSBoxHeader from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxHeader';
|
|
17
20
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
21
|
+
import ChipPopper from '@cccsaurora/howler-ui/components/elements/display/ChipPopper';
|
|
22
|
+
import AddColumnModal from '@cccsaurora/howler-ui/components/elements/hit/grid/AddColumnModal';
|
|
23
|
+
import HitTable from '@cccsaurora/howler-ui/components/elements/hit/grid/HitTable';
|
|
18
24
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
19
25
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
26
|
+
import LayoutToggle, {} from '@cccsaurora/howler-ui/components/elements/view/LayoutToggle';
|
|
20
27
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
21
28
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
22
29
|
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
30
|
+
import { uniq } from 'lodash-es';
|
|
23
31
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
24
32
|
import { useContextSelector } from 'use-context-selector';
|
|
25
33
|
import { DEFAULT_QUERY, StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
@@ -44,6 +52,7 @@ const ViewComposer = () => {
|
|
|
44
52
|
const [title, setTitle] = useState('');
|
|
45
53
|
const [type, setType] = useState('global');
|
|
46
54
|
const [advanceOnTriage, setAdvanceOnTriage] = useState(false);
|
|
55
|
+
const { columns, setColumns, columnWidths, isReady } = useContext(GridColumnsContext);
|
|
47
56
|
const query = useContextSelector(ParameterContext, ctx => ctx.query);
|
|
48
57
|
const setQuery = useContextSelector(ParameterContext, ctx => ctx.setQuery);
|
|
49
58
|
const sort = useContextSelector(ParameterContext, ctx => ctx.sort);
|
|
@@ -56,8 +65,14 @@ const ViewComposer = () => {
|
|
|
56
65
|
const [searching, setSearching] = useState(false);
|
|
57
66
|
const [error, setError] = useState(null);
|
|
58
67
|
const [response, setResponse] = useState();
|
|
68
|
+
const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
|
|
69
|
+
const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
|
|
59
70
|
const onSave = useCallback(async () => {
|
|
60
71
|
setLoading(true);
|
|
72
|
+
const _columnData = columns.map(column => ({
|
|
73
|
+
field: column,
|
|
74
|
+
width: columnWidths[column] ?? null
|
|
75
|
+
}));
|
|
61
76
|
try {
|
|
62
77
|
if (!routeParams.id) {
|
|
63
78
|
const newView = await addView({
|
|
@@ -67,7 +82,9 @@ const ViewComposer = () => {
|
|
|
67
82
|
sort: sort || null,
|
|
68
83
|
span: span || null,
|
|
69
84
|
settings: {
|
|
70
|
-
advance_on_triage: advanceOnTriage
|
|
85
|
+
advance_on_triage: advanceOnTriage,
|
|
86
|
+
display: displayType,
|
|
87
|
+
columns: displayType === 'grid' ? _columnData : null
|
|
71
88
|
}
|
|
72
89
|
});
|
|
73
90
|
navigate(buildViewUrl(newView));
|
|
@@ -79,7 +96,11 @@ const ViewComposer = () => {
|
|
|
79
96
|
query,
|
|
80
97
|
sort,
|
|
81
98
|
span,
|
|
82
|
-
settings: {
|
|
99
|
+
settings: {
|
|
100
|
+
advance_on_triage: advanceOnTriage,
|
|
101
|
+
display: displayType,
|
|
102
|
+
columns: displayType === 'grid' ? _columnData : null
|
|
103
|
+
}
|
|
83
104
|
});
|
|
84
105
|
}
|
|
85
106
|
showSuccessMessage(t(routeParams.id ? 'route.views.update.success' : 'route.views.create.success'));
|
|
@@ -101,7 +122,10 @@ const ViewComposer = () => {
|
|
|
101
122
|
sort,
|
|
102
123
|
span,
|
|
103
124
|
advanceOnTriage,
|
|
125
|
+
displayType,
|
|
126
|
+
columns,
|
|
104
127
|
navigate,
|
|
128
|
+
columnWidths,
|
|
105
129
|
editView,
|
|
106
130
|
showErrorMessage
|
|
107
131
|
]);
|
|
@@ -154,6 +178,8 @@ const ViewComposer = () => {
|
|
|
154
178
|
setTitle(viewToEdit.title);
|
|
155
179
|
setAdvanceOnTriage(viewToEdit.settings?.advance_on_triage ?? false);
|
|
156
180
|
setQuery(viewToEdit.query);
|
|
181
|
+
setDisplayType((viewToEdit.settings?.display ?? null));
|
|
182
|
+
setType(viewToEdit.type);
|
|
157
183
|
if (viewToEdit.sort) {
|
|
158
184
|
setSort(viewToEdit.sort);
|
|
159
185
|
}
|
|
@@ -172,6 +198,6 @@ const ViewComposer = () => {
|
|
|
172
198
|
fontSize: '0.9em',
|
|
173
199
|
fontStyle: 'italic',
|
|
174
200
|
mb: 0.5
|
|
175
|
-
}), variant: "body2", children: t('hit.search.prompt') }), _jsx(HitQuery, { triggerSearch: search, searching: searching, onChange: (_query, isDirty) => setIsSearchDirty(isDirty) }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(HitSort, {}), _jsx(SearchSpan, { omitCustom: true }), _jsx("div", { style: { flex: 1 } }), _jsxs(Stack, { spacing: 1, direction: "row", alignItems: "center", sx: { flex: '0 !important', minWidth: '300px' }, children: [_jsx(Typography, { component: "span", children: t('view.settings.advance_on_triage') }), _jsx(Tooltip, { title: t('view.settings.advance_on_triage.description'), children: _jsx(HelpOutline, { sx: { fontSize: '16px' } }) }), _jsx(Checkbox, { size: "small", checked: advanceOnTriage, onChange: (_event, checked) => setAdvanceOnTriage(checked) })] })] }), response
|
|
201
|
+
}), variant: "body2", children: t('hit.search.prompt') }), _jsx(Stack, { direction: "row", width: "100%", spacing: 2, alignItems: "flex-start", paddingBottom: 1, children: _jsxs(Stack, { direction: "column", width: "100%", spacing: 1, children: [_jsx(HitQuery, { triggerSearch: search, searching: searching, onChange: (_query, isDirty) => setIsSearchDirty(isDirty) }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(HitSort, {}), _jsx(SearchSpan, { omitCustom: true }), _jsx("div", { style: { flex: 1 } }), _jsx(ChipPopper, { label: _jsx(Typography, { variant: "body2", children: t('view.settings') }), deleteIcon: _jsx(Settings, {}), toggleOnDelete: true, slotProps: { chip: { size: 'small' } }, placement: "bottom-end", children: _jsxs(Stack, { direction: "column", spacing: 1, children: [_jsxs(Stack, { spacing: 1, direction: "row", alignItems: "center", sx: { flex: '0 !important', minWidth: '300px' }, children: [_jsx(Typography, { component: "span", children: t('view.settings.advance_on_triage') }), _jsx(Tooltip, { title: t('view.settings.advance_on_triage.description'), children: _jsx(HelpOutline, { sx: { fontSize: '16px' } }) }), _jsx(FlexOne, {}), _jsx(Checkbox, { size: "small", checked: advanceOnTriage, onChange: (_event, checked) => setAdvanceOnTriage(checked) })] }), _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", justifyContent: "space-between", children: [_jsx(Typography, { component: "span", children: t('view.settings.layout') }), _jsx(LayoutToggle, { displayType: displayType, setDisplayType: setDisplayType, size: "small", allowNullValue: true })] })] }) })] })] }) }), _jsxs(Stack, { direction: "row", spacing: 1, alignItems: "flex-end", justifyContent: "space-between", children: [response && (_jsx(SearchTotal, { total: response.total, pageLength: response.items.length, offset: response.offset, sx: theme => ({ color: theme.palette.text.secondary, fontSize: '0.9em', fontStyle: 'italic' }) })), _jsx(FlexOne, {}), displayType === 'grid' && (_jsx(AddColumnModal, { columns: columns, addColumn: key => isReady && setColumns(uniq([...columns, key])) }))] }), _jsx(LinearProgress, { sx: [!searching && { opacity: 0 }] })] }) }), _jsx(VSBoxContent, { children: displayType === 'grid' ? (_jsx(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, children: _jsx(HitTable, { query: query, items: response?.items }) })) : (_jsxs(Stack, { spacing: 1, children: [!response?.total && _jsx(AppListEmpty, {}), response?.items.map(hit => (_jsx(HitCard, { id: hit.howler.id, layout: HitLayout.DENSE }, hit.howler.id)))] })) })] }) }) }) }));
|
|
176
202
|
};
|
|
177
203
|
export default ViewComposer;
|
|
@@ -380,8 +380,8 @@
|
|
|
380
380
|
"page.settings.local.details.drawer.description": "Instead of switching between a divided view and the drawer on different screen sizes, this setting forces Howler to always use the drawer.",
|
|
381
381
|
"page.settings.local.flatten.json": "Flatten JSON in JSON View",
|
|
382
382
|
"page.settings.local.flatten.json.description": "Instead of showing the object structure of the JSON, show it as flattened keys.",
|
|
383
|
-
"page.settings.local.hits.display_type": "
|
|
384
|
-
"page.settings.local.hits.display_type.description": "This setting allows you to decide whether to use the list or grid view
|
|
383
|
+
"page.settings.local.hits.display_type": "Display type for results",
|
|
384
|
+
"page.settings.local.hits.display_type.description": "This setting allows you to decide whether to use the list or grid view when searching.",
|
|
385
385
|
"page.settings.local.hits.display_type.grid": "Grid",
|
|
386
386
|
"page.settings.local.hits.display_type.list": "List",
|
|
387
387
|
"page.settings.local.hits.field_count": "Template Field Count",
|
|
@@ -763,9 +763,12 @@
|
|
|
763
763
|
"usermenu.settings": "Settings",
|
|
764
764
|
"using": "using",
|
|
765
765
|
"view.assigned_to_me": "Assigned to Me",
|
|
766
|
+
"view.display.grid.inactive_warning": "The view has grid properties which cannot be applied",
|
|
766
767
|
"view.notfound": "This view is not available to you, or does not exist.",
|
|
767
768
|
"view.open": "Open View Query",
|
|
768
769
|
"view.refresh": "Refresh View",
|
|
770
|
+
"view.settings": "View Settings",
|
|
769
771
|
"view.settings.advance_on_triage": "Advance to next hit on triage",
|
|
770
|
-
"view.settings.advance_on_triage.description": "If this is enabled, triaging a hit on this view will automatically move to the next hit in line, making rapid triage of hits easier."
|
|
772
|
+
"view.settings.advance_on_triage.description": "If this is enabled, triaging a hit on this view will automatically move to the next hit in line, making rapid triage of hits easier.",
|
|
773
|
+
"view.settings.layout": "Default layout in search"
|
|
771
774
|
}
|
|
@@ -380,8 +380,8 @@
|
|
|
380
380
|
"page.settings.local.details.drawer.description": "Au lieu de basculer entre une vue divisée et le tiroir en fonction de la taille de l'écran, ce paramètre oblige Howler à toujours utiliser le tiroir.",
|
|
381
381
|
"page.settings.local.flatten.json": "Aplatir le JSON dans la vue JSON",
|
|
382
382
|
"page.settings.local.flatten.json.description": "Au lieu d'afficher la structure d'objet du JSON, affichez-la sous forme de clés aplaties",
|
|
383
|
-
"page.settings.local.hits.display_type": "Type d'affichage
|
|
384
|
-
"page.settings.local.hits.display_type.description": "Ce paramètre vous permet de choisir entre l'affichage sous forme de liste ou de grille
|
|
383
|
+
"page.settings.local.hits.display_type": "Type d'affichage pour les résultats",
|
|
384
|
+
"page.settings.local.hits.display_type.description": "Ce paramètre vous permet de choisir entre l'affichage sous forme de liste ou de grille lors d'une recherche.",
|
|
385
385
|
"page.settings.local.hits.display_type.grid": "Grille",
|
|
386
386
|
"page.settings.local.hits.display_type.list": "Liste",
|
|
387
387
|
"page.settings.local.hits.field_count": "Nombre de champs du modèle",
|
|
@@ -763,9 +763,12 @@
|
|
|
763
763
|
"usermenu.settings": "Paramètres",
|
|
764
764
|
"using": "utilisant",
|
|
765
765
|
"view.assigned_to_me": "Assigné à moi",
|
|
766
|
+
"view.display.grid.inactive_warning": "La vue possède des propriétés de grille qui ne peuvent pas être appliquées",
|
|
766
767
|
"view.notfound": "Cette vue n'est pas disponible pour vous, ou n'existe pas.",
|
|
767
768
|
"view.open": "Ouvrir requête de la vue",
|
|
768
769
|
"view.refresh": "Rafraîchir la vue",
|
|
770
|
+
"view.settings": "Paramètres de vue",
|
|
769
771
|
"view.settings.advance_on_triage": "Passer à la hit suivant lors du triage",
|
|
770
|
-
"view.settings.advance_on_triage.description": "Si cette option est activée, le triage d'une hit sur cette vue passera automatiquement à la hit suivant, ce qui facilitera le triage rapide des résultats."
|
|
772
|
+
"view.settings.advance_on_triage.description": "Si cette option est activée, le triage d'une hit sur cette vue passera automatiquement à la hit suivant, ce qui facilitera le triage rapide des résultats.",
|
|
773
|
+
"view.settings.layout": "Mise en page par défaut dans la recherche"
|
|
771
774
|
}
|
package/package.json
CHANGED
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"url": "https://github.com/CybercentreCanada/howler"
|
|
94
94
|
},
|
|
95
95
|
"type": "module",
|
|
96
|
-
"version": "2.19.0-dev.
|
|
96
|
+
"version": "2.19.0-dev.1148",
|
|
97
97
|
"exports": {
|
|
98
98
|
"./i18n": "./i18n.js",
|
|
99
99
|
"./index.css": "./index.css",
|
|
@@ -211,6 +211,7 @@
|
|
|
211
211
|
"./components/elements/hit/aggregate/*": "./components/elements/hit/aggregate/*.js",
|
|
212
212
|
"./components/elements/hit/outlines/*": "./components/elements/hit/outlines/*.js",
|
|
213
213
|
"./components/elements/hit/actions/*": "./components/elements/hit/actions/*.js",
|
|
214
|
+
"./components/elements/hit/grid/*": "./components/elements/hit/grid/*.js",
|
|
214
215
|
"./components/elements/hit/elements/*": "./components/elements/hit/elements/*.js",
|
|
215
216
|
"./components/elements/hit/outlines/al/*": "./components/elements/hit/outlines/al/*.js",
|
|
216
217
|
"./components/elements/addons/search/*": "./components/elements/addons/search/*.js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|