@eeacms/volto-clms-theme 1.1.240 → 1.1.241

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/CHANGELOG.md CHANGED
@@ -4,11 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [1.1.240](https://github.com/eea/volto-clms-theme/compare/1.1.239...1.1.240) - 30 May 2025
7
+ ### [1.1.241](https://github.com/eea/volto-clms-theme/compare/1.1.240...1.1.241) - 11 June 2025
8
8
 
9
9
  #### :hammer_and_wrench: Others
10
10
 
11
- - Refs #287636 - Add CDSE data source. [GhitaB - [`972944f`](https://github.com/eea/volto-clms-theme/commit/972944f1fb36010cbf828b2afba78b2db3690c27)]
11
+ - fix lint [ana-oprea - [`29cd33a`](https://github.com/eea/volto-clms-theme/commit/29cd33a4798f2a429acfa7ad18b08c9ad2f1532a)]
12
+ - Refs #286059 - Technical Library search: hide Back to search home, not needed for this config. [GhitaB - [`eeeee74`](https://github.com/eea/volto-clms-theme/commit/eeeee74d742454a624db930f90072cbf90e80fc8)]
13
+ - Refs #286059 - Technical Library search: clusters tabs style. [GhitaB - [`4a8614d`](https://github.com/eea/volto-clms-theme/commit/4a8614d99459eaf98bb06b8a8569e6df59079b8e)]
14
+ - Refs #286059 - Technical Library search: Disable Include archived content filter. [GhitaB - [`b92daf8`](https://github.com/eea/volto-clms-theme/commit/b92daf8e265833523091c4690bc0e0a6e77a9753)]
15
+ ### [1.1.240](https://github.com/eea/volto-clms-theme/compare/1.1.239...1.1.240) - 30 May 2025
16
+
12
17
  ### [1.1.239](https://github.com/eea/volto-clms-theme/compare/1.1.238...1.1.239) - 28 May 2025
13
18
 
14
19
  #### :house: Internal changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-clms-theme",
3
- "version": "1.1.240",
3
+ "version": "1.1.241",
4
4
  "description": "volto-clms-theme: Volto theme for CLMS site",
5
5
  "main": "src/index.js",
6
6
  "author": "CodeSyntax for the European Environment Agency",
@@ -7,6 +7,18 @@ body.clmsSearchTechnicalLibrary-view {
7
7
  }
8
8
 
9
9
  .searchapp.searchapp-clmsSearchTechnicalLibrary {
10
+ a.back-link {
11
+ display: none;
12
+ }
13
+
14
+ .ui.secondary.pointing.content-section-tabs.menu {
15
+ .active.item {
16
+ border-color: @clms-primary;
17
+ }
18
+
19
+ overflow-x: visible !important;
20
+ }
21
+
10
22
  i.icon.window.minimize:before {
11
23
  content: '⇕';
12
24
  }
@@ -0,0 +1,225 @@
1
+ import { Pagination, Table, Ref } from 'semantic-ui-react';
2
+ import { usePagination, useTable } from 'react-table';
3
+ import { Icon } from '@plone/volto/components';
4
+ import React from 'react';
5
+ import { compose } from 'redux';
6
+ import deleteSVG from '@plone/volto/icons/delete.svg';
7
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
8
+ import paginationLeftSVG from '@plone/volto/icons/left-key.svg';
9
+ import paginationRightSVG from '@plone/volto/icons/right-key.svg';
10
+ import plusSVG from '@plone/volto/icons/circle-plus.svg';
11
+ import dragSVG from '@plone/volto/icons/drag.svg';
12
+ import { EditableCell } from '@eeacms/volto-react-table-widget/components/Widgets/EditableCell';
13
+ import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
14
+
15
+ const defaultColumn = {
16
+ Cell: EditableCell,
17
+ };
18
+
19
+ function EditableTable(props) {
20
+ const {
21
+ columns,
22
+ data,
23
+ updateCell,
24
+ removeRow,
25
+ addRowAfter,
26
+ reorderRow,
27
+ selectedRow,
28
+ setSelectedRow,
29
+ schema,
30
+ reactSelect,
31
+ } = props;
32
+ const {
33
+ getTableProps,
34
+ getTableBodyProps,
35
+ headerGroups,
36
+ prepareRow,
37
+ page,
38
+ pageCount,
39
+ gotoPage,
40
+ setPageSize,
41
+ state: { pageIndex, pageSize },
42
+ } = useTable(
43
+ {
44
+ columns,
45
+ data,
46
+ defaultColumn,
47
+ updateCell,
48
+ selectedRow,
49
+ setSelectedRow,
50
+ schema,
51
+ reactSelect,
52
+ },
53
+ usePagination,
54
+ );
55
+ if (data.length === 0) {
56
+ addRowAfter({ key: 'Enter' }, 0, pageIndex, pageSize);
57
+ }
58
+ React.useEffect(() => {
59
+ gotoPage(pageIndex);
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, [data]);
62
+
63
+ const handleDragEnd = (result) => {
64
+ if (!result.destination || !reorderRow) {
65
+ return;
66
+ }
67
+
68
+ const { source, destination } = result;
69
+
70
+ if (source.index !== destination.index) {
71
+ reorderRow(source.index, destination.index, pageIndex, pageSize);
72
+ }
73
+ };
74
+ // Render the UI for your table
75
+ return (
76
+ <>
77
+ <Table celled {...getTableProps()}>
78
+ <Table.Header>
79
+ {headerGroups.map((headerGroup, key) => (
80
+ <Table.Row key={key} {...headerGroup.getHeaderGroupProps()}>
81
+ <Table.HeaderCell width={1}></Table.HeaderCell>
82
+ {headerGroup.headers.map((column) => (
83
+ <Table.HeaderCell {...column.getHeaderProps()}>
84
+ {column.render('Header')}
85
+ </Table.HeaderCell>
86
+ ))}
87
+ <Table.HeaderCell>{'Actions'}</Table.HeaderCell>
88
+ </Table.Row>
89
+ ))}
90
+ </Table.Header>
91
+ <DragDropContext onDragEnd={handleDragEnd}>
92
+ <Droppable droppableId="table-body">
93
+ {(provided) => (
94
+ <Ref innerRef={provided.innerRef}>
95
+ <Table.Body
96
+ {...getTableBodyProps()}
97
+ {...provided.droppableProps}
98
+ >
99
+ {page.map((row, i) => {
100
+ prepareRow(row);
101
+ return (
102
+ <Draggable
103
+ key={
104
+ row.original['@id'] ||
105
+ `row-${pageIndex * pageSize + i}`
106
+ }
107
+ draggableId={
108
+ row.original['@id'] ||
109
+ `row-${pageIndex * pageSize + i}`
110
+ }
111
+ index={i}
112
+ >
113
+ {(provided, snapshot) => (
114
+ <Ref innerRef={provided.innerRef}>
115
+ <Table.Row
116
+ className={`
117
+ ${
118
+ selectedRow === pageIndex * pageSize + i
119
+ ? 'selected-row'
120
+ : ''
121
+ }
122
+ ${snapshot.isDragging ? 'dragging' : ''}
123
+ `}
124
+ {...row.getRowProps()}
125
+ {...provided.draggableProps}
126
+ >
127
+ <Table.Cell {...provided.dragHandleProps}>
128
+ <Icon name={dragSVG} size="18px" />
129
+ </Table.Cell>
130
+ {row.cells.map((cell) => {
131
+ return (
132
+ <Table.Cell {...cell.getCellProps()}>
133
+ {cell.render('Cell')}
134
+ </Table.Cell>
135
+ );
136
+ })}
137
+ <Table.Cell>
138
+ <div className={'row-actions'}>
139
+ <span
140
+ onClick={(e) => {
141
+ addRowAfter(e, i, pageIndex, pageSize);
142
+ }}
143
+ onKeyDown={(e) => {
144
+ addRowAfter(e, i, pageIndex, pageSize);
145
+ }}
146
+ tabIndex={0}
147
+ role="button"
148
+ className="row-action"
149
+ >
150
+ <Icon name={plusSVG} size="23px" />
151
+ </span>
152
+ <span
153
+ onClick={(e) =>
154
+ removeRow(e, i, pageIndex, pageSize)
155
+ }
156
+ onKeyDown={(e) =>
157
+ removeRow(e, i, pageIndex, pageSize)
158
+ }
159
+ tabIndex={0}
160
+ role="button"
161
+ className="row-action"
162
+ >
163
+ <Icon
164
+ name={deleteSVG}
165
+ size="23px"
166
+ color="red"
167
+ />
168
+ </span>
169
+ </div>
170
+ </Table.Cell>
171
+ </Table.Row>
172
+ </Ref>
173
+ )}
174
+ </Draggable>
175
+ );
176
+ })}
177
+ {provided.placeholder}
178
+ </Table.Body>
179
+ </Ref>
180
+ )}
181
+ </Droppable>
182
+ </DragDropContext>
183
+ </Table>
184
+ <div className="pagination-wrapper react-table-pagination">
185
+ <Pagination
186
+ activePage={pageIndex + 1}
187
+ totalPages={pageCount}
188
+ onPageChange={(e, { activePage }) => {
189
+ gotoPage(activePage - 1);
190
+ }}
191
+ firstItem={null}
192
+ lastItem={null}
193
+ prevItem={{
194
+ content: <Icon name={paginationLeftSVG} size="18px" />,
195
+ icon: true,
196
+ 'aria-disabled': pageIndex + 1 === 1,
197
+ className: pageIndex + 1 === 1 ? 'disabled' : null,
198
+ }}
199
+ nextItem={{
200
+ content: <Icon name={paginationRightSVG} size="18px" />,
201
+ icon: true,
202
+ 'aria-disabled': pageIndex + 1 === pageCount,
203
+ className: pageIndex + 1 === pageCount ? 'disabled' : null,
204
+ }}
205
+ ></Pagination>
206
+ {/* eslint-disable-next-line jsx-a11y/no-onchange */}
207
+ <select
208
+ style={{ maxWidth: '7rem' }}
209
+ value={pageSize}
210
+ onChange={(e) => {
211
+ setPageSize(Number(e.target.value));
212
+ }}
213
+ >
214
+ {[10, 25, 50, 100].map((pageSize) => (
215
+ <option key={pageSize} value={pageSize}>
216
+ Show {pageSize}
217
+ </option>
218
+ ))}
219
+ </select>
220
+ </div>
221
+ </>
222
+ );
223
+ }
224
+
225
+ export default compose(injectLazyLibs(['reactSelect']))(EditableTable);
@@ -0,0 +1,274 @@
1
+ import '@eeacms/volto-react-table-widget/components/Widgets/react-table-widget.css';
2
+
3
+ import { Button, Modal, Segment, Grid } from 'semantic-ui-react';
4
+ import { defineMessages, useIntl } from 'react-intl';
5
+
6
+ import { CSVLink } from 'react-csv';
7
+ import EditableTable from './EditableTable';
8
+ import React from 'react';
9
+ import { Toast } from '@plone/volto/components';
10
+ import { toast } from 'react-toastify';
11
+ import { useCSVReader } from 'react-papaparse';
12
+ import { v4 as uuid } from 'uuid';
13
+
14
+ const messages = defineMessages({
15
+ csv_file_imported_correctly: {
16
+ id: 'CSV file imported correctly',
17
+ defaultMessage: 'CSV file imported correctly',
18
+ },
19
+ import_new_imported_item_count: {
20
+ id: 'Imported item count',
21
+ defaultMessage: '{count} new items imported',
22
+ },
23
+ import_modified_item_count: {
24
+ id: 'Modified item count',
25
+ defaultMessage: '{count} items modified',
26
+ },
27
+ import_csv_file: {
28
+ id: 'Import CSV file',
29
+ defaultMessage: 'Import CSV file',
30
+ },
31
+ export_csv_file: {
32
+ id: 'Export CSV file',
33
+ defaultMessage: 'Export CSV file',
34
+ },
35
+ undo_all_modifications: {
36
+ id: 'Undo all modifications',
37
+ defaultMessage: 'Undo all modifications',
38
+ },
39
+ undo_header: {
40
+ id: 'Undo',
41
+ defaultMessage: 'Undo',
42
+ },
43
+ undo_message: {
44
+ id: 'Undo message',
45
+ defaultMessage: 'Undo message',
46
+ },
47
+ });
48
+
49
+ const ReactDataTableWidget = (props) => {
50
+ let {
51
+ schema,
52
+ value,
53
+ onChange,
54
+ id,
55
+ csvexport,
56
+ csvimport,
57
+ undomodifications,
58
+ fieldSet,
59
+ } = props;
60
+
61
+ const intl = useIntl();
62
+ const header_columns = schema.fieldsets[0].fields.map((field) => {
63
+ return { Header: schema.properties[field]?.title, accessor: field };
64
+ });
65
+
66
+ const tablecolumns = React.useMemo(
67
+ () => [...header_columns],
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ [],
70
+ );
71
+
72
+ const [originalData] = React.useState(value);
73
+ const [hasModifiedData, setHasModifiedData] = React.useState(false);
74
+ const [selectedRow, setSelectedRow] = React.useState(0);
75
+
76
+ const updateCell = (rowIndex, columnId, updateValue) => {
77
+ setHasModifiedData(true);
78
+ const newvalue = value.map((v, i) =>
79
+ i !== rowIndex ? v : { ...v, [columnId]: updateValue },
80
+ );
81
+ onChange(id, newvalue);
82
+ };
83
+
84
+ const removeRow = (event, rowIndex, pageIndex, pageSize) => {
85
+ setHasModifiedData(true);
86
+ if (event.type === 'click' || event.key === 'Enter') {
87
+ setSelectedRow(pageIndex * pageSize + rowIndex);
88
+ const newvalue = value.filter(
89
+ (v, i) => i !== pageIndex * pageSize + rowIndex,
90
+ );
91
+ onChange(id, newvalue);
92
+ }
93
+ };
94
+
95
+ const addRowAfter = (event, rowIndex, pageIndex, pageSize) => {
96
+ setHasModifiedData(true);
97
+ if (event.type === 'click' || event.key === 'Enter') {
98
+ let newRowValue = {};
99
+ schema.fieldsets[0].fields.forEach((field) => {
100
+ newRowValue[field] = '';
101
+ });
102
+ setSelectedRow(pageIndex * pageSize + rowIndex + 1);
103
+ const newvalue = [
104
+ ...value.slice(0, pageIndex * pageSize + rowIndex + 1),
105
+ { '@id': uuid(), ...newRowValue },
106
+ ...value.slice(pageIndex * pageSize + rowIndex + 1),
107
+ ];
108
+ onChange(id, newvalue);
109
+ }
110
+ };
111
+
112
+ const reorderRow = (draggedRowIndex, dropRowIndex, pageIndex, pageSize) => {
113
+ setHasModifiedData(true);
114
+ const absoluteDraggedIndex = pageIndex * pageSize + draggedRowIndex;
115
+ const absoluteDropIndex = pageIndex * pageSize + dropRowIndex;
116
+
117
+ // Validate indices are within bounds
118
+ if (
119
+ absoluteDraggedIndex < 0 ||
120
+ absoluteDraggedIndex >= value.length ||
121
+ absoluteDropIndex < 0 ||
122
+ absoluteDropIndex >= value.length
123
+ ) {
124
+ return;
125
+ }
126
+
127
+ const newData = [...value];
128
+ const [movedRow] = newData.splice(absoluteDraggedIndex, 1);
129
+
130
+ newData.splice(absoluteDropIndex, 0, movedRow);
131
+
132
+ onChange(id, newData);
133
+ setSelectedRow(absoluteDropIndex);
134
+ };
135
+
136
+ const resetData = () => {
137
+ onChange(id, originalData);
138
+ setSelectedRow(0);
139
+ setHasModifiedData(false);
140
+ };
141
+
142
+ const csvcolumns = tablecolumns.map((d) => {
143
+ return {
144
+ label: d.accessor,
145
+ key: d.accessor,
146
+ };
147
+ });
148
+
149
+ csvcolumns.push({
150
+ label: '@id',
151
+ key: '@id',
152
+ });
153
+
154
+ const { CSVReader } = useCSVReader();
155
+ return (
156
+ <div className="inline field">
157
+ <Grid>
158
+ <Grid.Row stretched>
159
+ <Grid.Column width={4}>
160
+ <div className="wrapper">
161
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control*/}
162
+ <label id={`fieldset-${fieldSet}-field-label-${id}`}>
163
+ {schema.title}
164
+ </label>
165
+ </div>
166
+ </Grid.Column>
167
+ <Grid.Column width={8}>
168
+ <p className="help">{schema.description}</p>
169
+ </Grid.Column>
170
+ </Grid.Row>
171
+ </Grid>
172
+ <Segment basic textAlign="center">
173
+ {undomodifications && (
174
+ <Modal
175
+ trigger={
176
+ <Button
177
+ onClick={(e) => {
178
+ e.preventDefault();
179
+ }}
180
+ disabled={!hasModifiedData}
181
+ >
182
+ {intl.formatMessage(messages.undo_all_modifications)}
183
+ </Button>
184
+ }
185
+ header={intl.formatMessage(messages.undo_header)}
186
+ content={intl.formatMessage(messages.undo_message)}
187
+ actions={[
188
+ 'Cancel',
189
+ {
190
+ key: 'ok',
191
+ content: 'OK',
192
+ positive: true,
193
+ onClick: () => {
194
+ resetData();
195
+ },
196
+ },
197
+ ]}
198
+ />
199
+ )}
200
+
201
+ {csvexport && (
202
+ <CSVLink
203
+ className="ui button"
204
+ filename={`${schema.title} export.csv`}
205
+ separator=";"
206
+ data={value}
207
+ >
208
+ {intl.formatMessage(messages.export_csv_file)}
209
+ </CSVLink>
210
+ )}
211
+
212
+ {csvimport && (
213
+ <CSVReader
214
+ onUploadAccepted={(results) => {
215
+ let newdatacount = 0;
216
+ let newdata = results.data.map((item) => {
217
+ if (!item['@id']) {
218
+ newdatacount += 1;
219
+ return {
220
+ ...item,
221
+ '@id': uuid(),
222
+ };
223
+ }
224
+ return item;
225
+ });
226
+ let modifiedcount = newdata.length - newdatacount;
227
+ onChange(id, newdata);
228
+ toast.success(
229
+ <Toast
230
+ success
231
+ autoClose={5000}
232
+ content={`${intl.formatMessage(
233
+ messages.csv_file_imported_correctly,
234
+ )} ${intl.formatMessage(
235
+ messages.import_new_imported_item_count,
236
+ {
237
+ count: newdatacount,
238
+ },
239
+ )} ${intl.formatMessage(messages.import_modified_item_count, {
240
+ count: modifiedcount,
241
+ })}`}
242
+ />,
243
+ );
244
+ }}
245
+ config={{ header: true }}
246
+ >
247
+ {({ getRootProps, ProgressBar }) => (
248
+ <>
249
+ <Button type="button" {...getRootProps()}>
250
+ {intl.formatMessage(messages.import_csv_file)}
251
+ </Button>
252
+ <ProgressBar />
253
+ </>
254
+ )}
255
+ </CSVReader>
256
+ )}
257
+
258
+ <EditableTable
259
+ columns={tablecolumns}
260
+ data={value}
261
+ updateCell={updateCell}
262
+ removeRow={removeRow}
263
+ addRowAfter={addRowAfter}
264
+ reorderRow={reorderRow}
265
+ selectedRow={selectedRow}
266
+ setSelectedRow={setSelectedRow}
267
+ schema={schema}
268
+ />
269
+ </Segment>
270
+ </div>
271
+ );
272
+ };
273
+
274
+ export default ReactDataTableWidget;
@@ -19,4 +19,9 @@ if (facet && facet.default && Array.isArray(facet.default.values)) {
19
19
  facet.default.values[0] = 'All time';
20
20
  }
21
21
 
22
+ // Disable Include archived content filter
23
+ const facet_is_archived = facets.find((f) => f.field === 'IncludeArchived');
24
+ if (facet_is_archived && facet_is_archived.showInSecondaryFacetsList) {
25
+ facet_is_archived.showInSecondaryFacetsList = false;
26
+ }
22
27
  export default facets;