@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.
Files changed (66) hide show
  1. package/components/app/App.js +4 -2
  2. package/components/app/providers/GridColumnsProvider.d.ts +52 -0
  3. package/components/app/providers/GridColumnsProvider.js +177 -0
  4. package/components/app/providers/GridColumnsProvider.test.js +255 -0
  5. package/components/elements/addons/buttons/CustomIconButton.test.d.ts +1 -0
  6. package/components/elements/addons/buttons/CustomIconButton.test.js +81 -0
  7. package/components/elements/addons/layout/FlexVertical.test.d.ts +1 -0
  8. package/components/elements/addons/layout/FlexVertical.test.js +32 -0
  9. package/components/elements/addons/layout/vsbox/VSBoxContent.test.d.ts +1 -0
  10. package/components/elements/addons/layout/vsbox/VSBoxContent.test.js +26 -0
  11. package/components/elements/addons/search/phrase/WordLexer.test.d.ts +1 -0
  12. package/components/elements/addons/search/phrase/WordLexer.test.js +55 -0
  13. package/components/elements/hit/HitContextMenu.test.d.ts +1 -0
  14. package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.d.ts +2 -1
  15. package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.js +3 -3
  16. package/components/{routes/hits/search → elements/hit}/grid/HitRow.d.ts +1 -1
  17. package/components/elements/hit/grid/HitTable.d.ts +12 -0
  18. package/components/elements/hit/grid/HitTable.js +82 -0
  19. package/components/elements/view/LayoutToggle.d.ts +8 -0
  20. package/components/elements/view/LayoutToggle.js +12 -0
  21. package/components/elements/view/ViewTitle.d.ts +3 -0
  22. package/components/elements/view/ViewTitle.js +6 -3
  23. package/components/hooks/useMyTheme.test.d.ts +1 -0
  24. package/components/hooks/useMyTheme.test.js +35 -0
  25. package/components/routes/advanced/eqlTokenProvider.test.d.ts +1 -0
  26. package/components/routes/advanced/eqlTokenProvider.test.js +34 -0
  27. package/components/routes/advanced/luceneTokenProvider.test.d.ts +1 -0
  28. package/components/routes/advanced/luceneTokenProvider.test.js +34 -0
  29. package/components/routes/hits/search/HitBrowser.js +2 -1
  30. package/components/routes/hits/search/SearchPane.js +6 -8
  31. package/components/routes/hits/search/ViewLink.js +3 -2
  32. package/components/routes/hits/search/grid/HitGrid.js +17 -85
  33. package/components/routes/hits/search/shared/LayoutSettings.js +25 -0
  34. package/components/routes/hits/search/shared/SearchActionMenu.d.ts +4 -0
  35. package/components/routes/hits/search/shared/SearchActionMenu.js +17 -0
  36. package/components/routes/home/HomeSettings.test.d.ts +1 -0
  37. package/components/routes/home/HomeSettings.test.js +44 -0
  38. package/components/routes/home/ViewCard.js +1 -1
  39. package/components/routes/overviews/markdownExtendedTokenProvider.test.d.ts +1 -0
  40. package/components/routes/overviews/markdownExtendedTokenProvider.test.js +50 -0
  41. package/components/routes/views/ViewComposer.js +32 -6
  42. package/locales/en/translation.json +6 -3
  43. package/locales/fr/translation.json +6 -3
  44. package/models/entities/generated/Column.d.ts +7 -0
  45. package/models/entities/generated/Host.d.ts +1 -0
  46. package/models/entities/generated/Settings.d.ts +4 -0
  47. package/package.json +2 -1
  48. package/rest/AxiosClient.test.d.ts +1 -0
  49. package/rest/AxiosClient.test.js +184 -0
  50. package/utils/stringUtils.d.ts +1 -0
  51. package/utils/stringUtils.js +7 -0
  52. package/utils/stringUtils.test.js +21 -1
  53. package/utils/utils.test.js +119 -2
  54. package/components/routes/hits/search/LayoutSettings.js +0 -24
  55. /package/components/{routes/hits/search/HitContextMenu.test.d.ts → app/providers/GridColumnsProvider.test.d.ts} +0 -0
  56. /package/components/{routes/hits/search → elements/hit}/HitContextMenu.d.ts +0 -0
  57. /package/components/{routes/hits/search → elements/hit}/HitContextMenu.js +0 -0
  58. /package/components/{routes/hits/search → elements/hit}/HitContextMenu.test.js +0 -0
  59. /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.d.ts +0 -0
  60. /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.js +0 -0
  61. /package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.d.ts +0 -0
  62. /package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.js +0 -0
  63. /package/components/{routes/hits/search → elements/hit}/grid/HitRow.js +0 -0
  64. /package/components/routes/hits/search/{BundleParentMenu.d.ts → shared/BundleParentMenu.d.ts} +0 -0
  65. /package/components/routes/hits/search/{BundleParentMenu.js → shared/BundleParentMenu.js} +0 -0
  66. /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 { DndContext, KeyboardSensor, PointerSensor, pointerWithin, useSensor, useSensors } from '@dnd-kit/core';
3
- import { arrayMove, SortableContext, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
4
- import { FormatIndentDecrease, FormatIndentIncrease, Info, List, Search, TableChart } from '@mui/icons-material';
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, useEffect, useMemo, useRef, useState } from 'react';
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 AddColumnModal from './AddColumnModal';
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 [collapseMainColumn, setCollapseMainColumn] = useMyLocalStorageItem(StorageKey.GRID_COLLAPSE_COLUMN, false);
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') }), 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, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(HitQuery, { searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
121
- position: 'absolute',
122
- left: 0,
123
- right: 0,
124
- bottom: 0,
125
- borderBottomLeftRadius: theme.shape.borderRadius,
126
- borderBottomRightRadius: theme.shape.borderRadius
127
- } }))] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", children: [_jsx(ToggleButton, { value: "list", children: _jsx(List, {}) }), _jsx(ToggleButton, { value: "grid", children: _jsx(TableChart, {}) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", alignItems: "center", children: [_jsx(QuerySettings, { boxSx: { flex: 1 } }), _jsx(AddColumnModal, { columns: columns, addColumn: key => setColumns(uniq([...columns, key])) })] }), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsxs(Table, { sx: { '& td,th': { px: 1, py: 0.25, whiteSpace: 'nowrap' } }, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { sx: {
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,4 @@
1
+ declare const SearchActionMenu: ({ query }: {
2
+ query: string;
3
+ }) => import("react/jsx-runtime").JSX.Element;
4
+ export default SearchActionMenu;
@@ -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,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: { advance_on_triage: advanceOnTriage }
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?.total ? (_jsx(SearchTotal, { total: response.total, pageLength: response.items.length, offset: response.offset, sx: theme => ({ color: theme.palette.text.secondary, fontSize: '0.9em', fontStyle: 'italic' }) })) : null, _jsx(LinearProgress, { sx: [!searching && { opacity: 0 }] })] }) }), _jsx(VSBoxContent, { children: _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)))] }) })] }) }) }) }));
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": "Default 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 by default when searching.",
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 par défaut 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 par défaut lors d'une recherche.",
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
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * NOTE: This is an auto-generated file. Don't edit this manually.
3
+ */
4
+ export interface Column {
5
+ field?: string;
6
+ width?: number;
7
+ }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  export interface Host {
5
5
  domain?: string;
6
+ hostname?: string;
6
7
  id?: string;
7
8
  ip?: string[];
8
9
  mac?: string[];
@@ -1,6 +1,10 @@
1
+ import type { Column } from './Column';
2
+
1
3
  /**
2
4
  * NOTE: This is an auto-generated file. Don't edit this manually.
3
5
  */
4
6
  export interface Settings {
5
7
  advance_on_triage?: boolean;
8
+ columns?: Column[];
9
+ display?: string;
6
10
  }
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.1099",
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 {};