@eeacms/volto-clms-theme 1.0.168 → 1.0.170

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,6 +4,38 @@ 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.0.170](https://github.com/eea/volto-clms-theme/compare/1.0.169...1.0.170) - 14 February 2023
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Update package.json [Mikel Larreategi - [`3f4bc70`](https://github.com/eea/volto-clms-theme/commit/3f4bc70e0c6468071887c671cda9960b6660b730)]
12
+ ### [1.0.169](https://github.com/eea/volto-clms-theme/compare/1.0.168...1.0.169) - 14 February 2023
13
+
14
+ #### :rocket: New Features
15
+
16
+ - feat: add a select for the cart Type column and add the possibility to duplicate a row [ionlizarazu - [`4e4ee1b`](https://github.com/eea/volto-clms-theme/commit/4e4ee1b418a1837da30b94d511f37d9771da75ec)]
17
+
18
+ #### :bug: Bug Fixes
19
+
20
+ - fix: cart icons [ionlizarazu - [`07377c9`](https://github.com/eea/volto-clms-theme/commit/07377c9fa15df658b615c93f5bc23cca4fc94679)]
21
+ - fix: wrong functionalities of cart [ionlizarazu - [`4cb2c78`](https://github.com/eea/volto-clms-theme/commit/4cb2c783fa3f678bdb179d454fced2b0656557eb)]
22
+
23
+ #### :hammer_and_wrench: Others
24
+
25
+ - typo [ionlizarazu - [`39dc6f6`](https://github.com/eea/volto-clms-theme/commit/39dc6f68540fbb9f89f8fa18f4e7319c5031925c)]
26
+ - add search input and pagination to related listing blocks with more than 10 elements [ionlizarazu - [`ea2df24`](https://github.com/eea/volto-clms-theme/commit/ea2df24074b54d4341fa0f9f2bf3f5cf041b8304)]
27
+ - download_full_dataset_text from Plone [ionlizarazu - [`33d57a1`](https://github.com/eea/volto-clms-theme/commit/33d57a1f10bee79693d9bedf31c9e342bd08e07d)]
28
+ - dynamicSort and useFilteredPagination [ionlizarazu - [`3d0c3fe`](https://github.com/eea/volto-clms-theme/commit/3d0c3fe54c0be7e16c05e18149705a53a0291bcf)]
29
+ - don't show selector when there is only one collection for this type [ionlizarazu - [`d741b92`](https://github.com/eea/volto-clms-theme/commit/d741b92bac7336058f40c16e9fc5b5d03dcce36f)]
30
+ - add items as downloadableObjects in case we are rendering them with Universal Link [Mikel Larreategi - [`d6d64d5`](https://github.com/eea/volto-clms-theme/commit/d6d64d57dbff70cc145f2d29a3c173ed348c5ba0)]
31
+ - new DoubleRange facet for Spatial resolutions [ionlizarazu - [`dee81d4`](https://github.com/eea/volto-clms-theme/commit/dee81d407282aae8afeabffa69d2d9668bd50d6d)]
32
+ - search block with no facets [ionlizarazu - [`5ea89fc`](https://github.com/eea/volto-clms-theme/commit/5ea89fc6282143c28fdc2056a85509cda31c26b8)]
33
+ - comment deleted [Unai - [`5dcadea`](https://github.com/eea/volto-clms-theme/commit/5dcadeaf8f4343c527c3676bfbe0a074205d6070)]
34
+ - comment deleted [Unai - [`3dbd923`](https://github.com/eea/volto-clms-theme/commit/3dbd923b37e5734c96c13b954c94dfed0127abea)]
35
+ - unused code [Unai - [`e8c9bce`](https://github.com/eea/volto-clms-theme/commit/e8c9bcefb7d23e7fa5c71443b6b8c3bc802a6ded)]
36
+ - lint fix [Unai - [`620390c`](https://github.com/eea/volto-clms-theme/commit/620390cf7a6f7381bc409c4ddd727207f3d4ea77)]
37
+ - prettier fix [Unai - [`39942ee`](https://github.com/eea/volto-clms-theme/commit/39942ee85949c2277840deb8baa2256ccba42931)]
38
+ - slate style overrided and margin-left -15rem added to fix 1426 [Unai - [`1ff3792`](https://github.com/eea/volto-clms-theme/commit/1ff3792eacd42b527979bd1b4bb905b8e78d1eb3)]
7
39
  ### [1.0.168](https://github.com/eea/volto-clms-theme/compare/1.0.167...1.0.168) - 7 February 2023
8
40
 
9
41
  #### :bug: Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-clms-theme",
3
- "version": "1.0.168",
3
+ "version": "1.0.170",
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",
@@ -37,7 +37,7 @@
37
37
  "volto-cookie-banner": "2.1.0",
38
38
  "@eeacms/volto-accordion-block": "3.5.0",
39
39
  "@eeacms/volto-arcgis-block": "*",
40
- "@eeacms/volto-clms-utils": "0.1.6",
40
+ "@eeacms/volto-clms-utils": "0.1.7",
41
41
  "@eeacms/volto-columns-block": "4.4.3",
42
42
  "@eeacms/volto-metadata-block": "^2.1.0",
43
43
  "@eeacms/volto-react-table-widget": "0.1.1",
@@ -1,12 +1,22 @@
1
+ import React from 'react';
1
2
  import { useDispatch, useSelector } from 'react-redux';
3
+ import { Segment, Input, Pagination } from 'semantic-ui-react';
2
4
 
3
- import React from 'react';
4
- import config from '@plone/volto/registry';
5
5
  import { searchContent } from '@plone/volto/actions';
6
- import { Segment } from 'semantic-ui-react';
6
+ import { Icon } from '@plone/volto/components';
7
+ import paginationLeftSVG from '@plone/volto/icons/left-key.svg';
8
+ import paginationRightSVG from '@plone/volto/icons/right-key.svg';
9
+ import config from '@plone/volto/registry';
10
+
11
+ import { useFilteredPagination } from '../../CclUtils/useFilteredPagination';
7
12
 
8
13
  const CclRelatedListingView = (props) => {
9
14
  const { data, id, properties, metadata } = props;
15
+ const use_pagination = useFilteredPagination([]);
16
+ const p_functions = use_pagination.functions;
17
+ const p_data = use_pagination.data;
18
+ const { pagination, currentPage, paginationSize, dataList } = p_data;
19
+
10
20
  const dispatch = useDispatch();
11
21
  const searchSubrequests = useSelector(
12
22
  (state) => state.search.subrequests?.[props.id],
@@ -59,6 +69,11 @@ const CclRelatedListingView = (props) => {
59
69
  }
60
70
 
61
71
  React.useEffect(() => {
72
+ if (searchSubrequests?.loaded) {
73
+ p_functions.setOriginalDataList([...searchSubrequests.items]);
74
+ p_functions.setDataList([...searchSubrequests.items]);
75
+ }
76
+
62
77
  uid &&
63
78
  !searchSubrequests?.loading &&
64
79
  !searchSubrequests?.loaded &&
@@ -83,7 +98,55 @@ const CclRelatedListingView = (props) => {
83
98
  return (
84
99
  <>
85
100
  {searchSubrequests?.loaded && libraries.length > 0 ? (
86
- <TemplateView items={libraries} variation={template_id} />
101
+ <div>
102
+ {paginationSize < libraries.length && (
103
+ <div className="block search">
104
+ <div className="search-wrapper">
105
+ <div className="search-input">
106
+ <Input
107
+ id={`${props.id}-searchtext`}
108
+ placeholder={'Search in the following items'}
109
+ fluid
110
+ onChange={(event, value) => {
111
+ p_functions.applySearch(event, value, 'title');
112
+ p_functions.setCurrentPage(1);
113
+ }}
114
+ />
115
+ </div>
116
+ </div>
117
+ </div>
118
+ )}
119
+ <TemplateView items={pagination} variation={template_id} />
120
+ {dataList.length / paginationSize > 1 && (
121
+ <div className="pagination-wrapper">
122
+ <Pagination
123
+ activePage={currentPage}
124
+ totalPages={Math.ceil(dataList.length / paginationSize)}
125
+ onPageChange={(e, { activePage }) => {
126
+ p_functions.setCurrentPage(activePage);
127
+ }}
128
+ firstItem={null}
129
+ lastItem={null}
130
+ prevItem={{
131
+ content: <Icon name={paginationLeftSVG} size="18px" />,
132
+ icon: true,
133
+ 'aria-disabled': currentPage === 1,
134
+ className: currentPage === 1 ? 'disabled' : null,
135
+ }}
136
+ nextItem={{
137
+ content: <Icon name={paginationRightSVG} size="18px" />,
138
+ icon: true,
139
+ 'aria-disabled':
140
+ currentPage === Math.ceil(dataList.length / paginationSize),
141
+ className:
142
+ currentPage === Math.ceil(dataList.length / paginationSize)
143
+ ? 'disabled'
144
+ : null,
145
+ }}
146
+ ></Pagination>
147
+ </div>
148
+ )}
149
+ </div>
87
150
  ) : (
88
151
  <p>There are no related items.</p>
89
152
  )}
@@ -0,0 +1,163 @@
1
+ import 'react-input-range/lib/css/index.css';
2
+ import './range.css';
3
+
4
+ import React from 'react';
5
+
6
+ import InputRange from 'react-input-range';
7
+ import { Segment } from 'semantic-ui-react';
8
+ import {
9
+ selectFacetStateToValue,
10
+ selectFacetValueToQuery,
11
+ } from '@plone/volto/components/manage/Blocks/Search/components/base';
12
+
13
+ const doubleRangeSpatialFacetSchemaEnhancer = ({ schema, formData }) => {
14
+ // adds (enables) the 'multiple' field after the 'type' dropdown
15
+ let { fields } = schema.fieldsets[0];
16
+ const pos = fields.indexOf('type') + 1;
17
+ fields = [
18
+ ...fields.slice(0, pos),
19
+ 'step',
20
+ 'multiple',
21
+ ...fields.slice(pos, fields.length),
22
+ ];
23
+
24
+ schema.properties = {
25
+ ...schema.properties,
26
+ step: { title: 'Step', type: 'number', default: 1 },
27
+ };
28
+ schema.fieldsets[0].fields = fields;
29
+ return schema;
30
+ };
31
+
32
+ const mToKm = (m) => {
33
+ return m >= 1000 ? Math.round((m / 1000) * 10) / 10 + 'km' : m + 'm';
34
+ };
35
+
36
+ const realToCoded = (number, step) => {
37
+ // the meters are going to be the same
38
+ if (number <= 1000) return number;
39
+ // for the km we are going to code any km after the first as tens
40
+ // so 2 km will be 1020 and 3 km will be 1040
41
+ const relation = step ? parseInt(step) : 1;
42
+ const thousands = Math.floor(number / 1000);
43
+ const hundreds = Math.floor((number - thousands * 1000) / 100);
44
+ const tens = Math.floor((number - thousands * 1000 - hundreds * 100) / 10);
45
+ let finalThousands = thousands;
46
+ if (hundreds > 0 || tens > 0) finalThousands = thousands + 1;
47
+ return 1000 + parseInt(finalThousands * relation);
48
+ };
49
+
50
+ const codedToReal = (number, step) => {
51
+ // the meters are going to be the same
52
+ if (number <= 1000) return number;
53
+ // for the km we have to decode any tens after 1000
54
+ // so 1020 will be 2 km and 1040 will be 3 km
55
+ const relation = step ? parseInt(step) : 1;
56
+ const codedThousands = parseInt(number) - 1000;
57
+ return parseInt(codedThousands * (1000 / relation));
58
+ };
59
+
60
+ const convertToRange = (values, step) => {
61
+ return {
62
+ min: realToCoded(
63
+ Math.min.apply(
64
+ Math,
65
+ values.map(function (o) {
66
+ return o.value.replace(/[a-zA-Z]/, '');
67
+ }),
68
+ ),
69
+ step,
70
+ ),
71
+ max: realToCoded(
72
+ Math.max.apply(
73
+ Math,
74
+ values.map(function (o) {
75
+ return o.value.replace(/[a-zA-Z]/, '');
76
+ }),
77
+ ),
78
+ step,
79
+ ),
80
+ };
81
+ };
82
+
83
+ const DoubleRangeSpatialFacet = (props) => {
84
+ const { facet, choices, onChange, value } = props;
85
+ const facetValue = value;
86
+ var [open, setOpen] = React.useState(false);
87
+
88
+ const startingValues = convertToRange(choices, facet?.step);
89
+
90
+ const onChangeRange = (nValue, onChangeR, sValue) => {
91
+ const fixedValue = {
92
+ min: nValue.min < sValue.min ? sValue.min : nValue.min,
93
+ max: nValue.max > sValue.max ? sValue.max : nValue.max,
94
+ };
95
+ onChangeR(
96
+ facet.field.value,
97
+ [...Array(fixedValue.max - fixedValue.min + 1).keys()].map((i) =>
98
+ (i + fixedValue.min).toString(),
99
+ ),
100
+ );
101
+ };
102
+
103
+ return (
104
+ <fieldset className="ccl-fieldset">
105
+ <div
106
+ className="ccl-expandable__button"
107
+ aria-expanded={open}
108
+ onClick={() => setOpen(!open)}
109
+ onKeyDown={() => setOpen(!open)}
110
+ tabIndex={0}
111
+ role={'button'}
112
+ >
113
+ <legend className="ccl-form-legend">{facet.title}</legend>
114
+ </div>
115
+ <div className="range-container">
116
+ <Segment basic padded>
117
+ <InputRange
118
+ minValue={startingValues.min}
119
+ maxValue={startingValues.max}
120
+ step={facet.step ? parseInt(facet.step) : 1}
121
+ value={
122
+ facetValue.length > 0
123
+ ? {
124
+ min: convertToRange(facetValue, facet?.step).min,
125
+ max: convertToRange(facetValue, facet?.step).max,
126
+ }
127
+ : {
128
+ min: startingValues.min,
129
+ max: startingValues.max,
130
+ }
131
+ }
132
+ onChange={(changedValue) =>
133
+ onChangeRange(
134
+ {
135
+ min: codedToReal(changedValue.min, facet?.step),
136
+ max: codedToReal(changedValue.max, facet?.step),
137
+ },
138
+ onChange,
139
+ {
140
+ min: codedToReal(startingValues.min, facet?.step),
141
+ max: codedToReal(startingValues.max, facet?.step),
142
+ },
143
+ )
144
+ }
145
+ formatLabel={(value) => {
146
+ if (facet.field.value === 'spatial_resolution') {
147
+ return mToKm(codedToReal(value, facet?.step));
148
+ } else {
149
+ return value;
150
+ }
151
+ }}
152
+ />
153
+ </Segment>
154
+ <br />
155
+ </div>
156
+ </fieldset>
157
+ );
158
+ };
159
+
160
+ DoubleRangeSpatialFacet.schemaEnhancer = doubleRangeSpatialFacetSchemaEnhancer;
161
+ DoubleRangeSpatialFacet.stateToValue = selectFacetStateToValue;
162
+ DoubleRangeSpatialFacet.valueToQuery = selectFacetValueToQuery;
163
+ export default DoubleRangeSpatialFacet;
@@ -18,14 +18,14 @@ const FilterList = (props) => {
18
18
  const { facets, setFacets, isEditMode, data, querystring } = props;
19
19
  const showFilterList = !Object.values(facets).every((facet) => !facet.length);
20
20
 
21
- const baseFacets = data.facets;
21
+ const baseFacets = data?.facets;
22
22
  const currentFilters = Object.fromEntries(
23
23
  Object.entries(facets)
24
24
  .filter((v) => v[1] && v[0] !== 'SearchableText')
25
25
  .filter(
26
26
  (v) =>
27
27
  v[1] &&
28
- baseFacets.length > 0 &&
28
+ baseFacets?.length > 0 &&
29
29
  baseFacets.map((bf) => bf.field?.value).includes(v[0]),
30
30
  ),
31
31
  );
@@ -1,3 +1,4 @@
1
+ import DoubleRangeSpatialFacet from './DoubleRangeSpatialFacet';
1
2
  import DoubleRangeFacet from './DoubleRangeFacet';
2
3
  import AccordionFacet from './AccordionFacet';
3
4
  import RightModalFacets from './RightModalFacets';
@@ -11,6 +12,7 @@ export {
11
12
  AccordionFacet,
12
13
  WithType,
13
14
  DoubleRangeFacet,
15
+ DoubleRangeSpatialFacet,
14
16
  CheckboxTreeFacet,
15
17
  CheckboxTreeParentFacet,
16
18
  rewriteOptions,
@@ -3,6 +3,7 @@ import {
3
3
  RightModalFacets,
4
4
  WithType,
5
5
  DoubleRangeFacet,
6
+ DoubleRangeSpatialFacet,
6
7
  CheckboxTreeFacet,
7
8
  CheckboxTreeParentFacet,
8
9
  rewriteOptions,
@@ -385,6 +386,16 @@ const customBlocks = (config) => ({
385
386
  valueToQuery: DoubleRangeFacet.valueToQuery,
386
387
  filterListComponent: SelectFacetFilterListEntry,
387
388
  },
389
+ {
390
+ id: 'doubleRangeSpatialFacet',
391
+ title: 'Double Range for Spatial Resolutions',
392
+ view: WithType(DoubleRangeSpatialFacet, 'range'),
393
+ isDefault: false,
394
+ schemaEnhancer: DoubleRangeSpatialFacet.schemaEnhancer,
395
+ stateToValue: DoubleRangeSpatialFacet.stateToValue,
396
+ valueToQuery: DoubleRangeSpatialFacet.valueToQuery,
397
+ filterListComponent: SelectFacetFilterListEntry,
398
+ },
388
399
  {
389
400
  id: 'checkboxTreeFacet',
390
401
  title: 'Checkbox Tree',
@@ -6,7 +6,7 @@ import { useLocation } from 'react-router-dom';
6
6
  import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
7
7
  import CclDownloadTable from '@eeacms/volto-clms-theme/components/CclDownloadTable/CclDownloadTable';
8
8
  import CclLoginModal from '@eeacms/volto-clms-theme/components/CclLoginModal/CclLoginModal';
9
- import { UniversalLink } from '@plone/volto/components';
9
+ import { StringToHTML } from '@eeacms/volto-clms-theme/components/CclUtils';
10
10
 
11
11
  const DownloadDataSetContent = (data, token) => {
12
12
  const location = useLocation();
@@ -58,13 +58,12 @@ const DownloadDataSetContent = (data, token) => {
58
58
  <br />
59
59
  <br />
60
60
  <h2>Download full dataset</h2>
61
- <p>
62
- If you want to download the full dataset, click{' '}
63
- <UniversalLink href="/en/how-to-guides/how-to-download-spatial-data/how-to-download-m2m">
64
- here
65
- </UniversalLink>{' '}
66
- to learn more
67
- </p>
61
+ {data.download_full_dataset_text?.data && (
62
+ <StringToHTML
63
+ string={data.download_full_dataset_text?.data || ''}
64
+ />
65
+ )}
66
+
68
67
  {/* {data.token === '' ? (
69
68
  <CclButton
70
69
  url={location.pathname + '/download-by-area'}
@@ -1,33 +1,38 @@
1
- /* eslint-disable react-hooks/exhaustive-deps */
2
- /**
3
- * CLMSCartContent container.
4
- * @module components/CLMSDownloadCartView/CLMSCartContent
5
- */
6
-
7
- import { Checkbox, Modal, Segment, Select } from 'semantic-ui-react';
8
1
  import React, { useEffect, useState } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import { Checkbox, Modal, Segment, Select } from 'semantic-ui-react';
4
+
5
+ import { Icon } from '@plone/volto/components';
6
+ import { Toast } from '@plone/volto/components';
7
+ import removeSVG from '@plone/volto/icons/delete.svg';
8
+ import addDocumentSVG from '@plone/volto/icons/add-document.svg';
9
+ import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
10
+ import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
11
+ import { cleanDuplicatesEntries } from '@eeacms/volto-clms-utils/utils';
12
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
13
+
14
+ import { getDownloadtool, postDownloadtool } from '../../actions';
9
15
  import {
10
- getCartObjectFromMapviewer,
11
- getCartObjectFromPrepackaged,
12
16
  getDownloadToolPostBody,
13
17
  formatNaming,
14
18
  originalFormatNaming,
19
+ getCollectionByItem,
20
+ duplicateCartItem,
21
+ concatRequestedCartItem,
15
22
  } from './cartUtils';
16
- import { getDownloadtool, postDownloadtool } from '../../actions';
17
- import { useDispatch, useSelector } from 'react-redux';
18
-
19
- import CclButton from '@eeacms/volto-clms-theme/components/CclButton/CclButton';
20
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
21
- import { Toast } from '@plone/volto/components';
22
- import { cleanDuplicatesEntries } from '@eeacms/volto-clms-utils/utils';
23
23
  import { getAvailableConversion } from './conversion';
24
24
  import { toast } from 'react-toastify';
25
- import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
25
+
26
+ /* eslint-disable react-hooks/exhaustive-deps */
27
+ /**
28
+ * CLMSCartContent container.
29
+ * @module components/CLMSDownloadCartView/CLMSCartContent
30
+ */
26
31
 
27
32
  const CLMSCartContent = (props) => {
28
33
  const { localSessionCart } = props;
29
34
  const dispatch = useDispatch();
30
- const { removeCartItem, removeCartItems } = useCartState();
35
+ const { removeCartItem, removeCartItems, updateCart } = useCartState();
31
36
 
32
37
  // state connections
33
38
  const cart = useSelector((state) => state.cart_items.items);
@@ -67,46 +72,17 @@ const CLMSCartContent = (props) => {
67
72
  cart.length > 0 &&
68
73
  cart.length !== newCart.length
69
74
  ) {
70
- concatRequestedCartItem();
75
+ concatRequestedCartItem(
76
+ cartItems,
77
+ setCartItems,
78
+ localSessionCart,
79
+ datasets_items,
80
+ projections,
81
+ nutsnames,
82
+ );
71
83
  }
72
84
  }, [cart, datasets_items]);
73
85
 
74
- useEffect(() => {
75
- if (Object.keys(nutsnames).length > 0 && cart.length > 0) {
76
- concatRequestedCartItem();
77
- }
78
- }, [nutsnames]);
79
-
80
- function concatRequestedCartItem() {
81
- let newCartItems = [...cartItems];
82
- localSessionCart.forEach((localItem) => {
83
- const requestedItem = datasets_items
84
- ? datasets_items.find((req) => req.UID === localItem.UID)
85
- : false;
86
- if (requestedItem) {
87
- const file_data = requestedItem?.downloadable_files?.items.find(
88
- (item) => item['@id'] === localItem.file_id,
89
- );
90
- if (file_data) {
91
- newCartItems.push(
92
- getCartObjectFromPrepackaged(file_data, requestedItem),
93
- );
94
- setCartItems(cleanDuplicatesEntries(newCartItems));
95
- } else {
96
- newCartItems.push(
97
- getCartObjectFromMapviewer(
98
- localItem,
99
- requestedItem,
100
- projections,
101
- nutsnames,
102
- ),
103
- );
104
- setCartItems(cleanDuplicatesEntries(newCartItems));
105
- }
106
- }
107
- });
108
- }
109
-
110
86
  const selectAllCart = (checked) => {
111
87
  if (checked) {
112
88
  setCartSelection(
@@ -212,8 +188,11 @@ const CLMSCartContent = (props) => {
212
188
  }
213
189
  };
214
190
 
215
- const TypeNaming = (typeProps) => {
216
- const { item } = typeProps;
191
+ const TypeNaming = ({ item }) => {
192
+ const types_options =
193
+ item?.type_options?.length > 0
194
+ ? [...new Set(item.type_options.map((ddi) => ddi.name))]
195
+ : [];
217
196
  if (item.file_id) {
218
197
  return (
219
198
  <span className={'tag tag-' + item?.type?.toLowerCase()}>
@@ -223,22 +202,51 @@ const CLMSCartContent = (props) => {
223
202
  } else if (!item.type) {
224
203
  return '-';
225
204
  } else {
226
- let values = item.type_options.filter((myitem) => {
227
- return myitem['id'] === item.type;
228
- });
229
- return values.length > 0 ? values[0].name : '.';
205
+ let defaultType = getCollectionByItem(item);
206
+ return types_options.length > 1 ? (
207
+ <Select
208
+ placeholder="Select type"
209
+ value={defaultType.name}
210
+ options={types_options.map((option) => {
211
+ return {
212
+ key: option,
213
+ value: option,
214
+ text: option,
215
+ };
216
+ })}
217
+ onChange={(e, data) => {
218
+ const new_cartItems = [...cartItems];
219
+ const objIndex = new_cartItems.findIndex(
220
+ (obj) => obj.unique_id === item.unique_id,
221
+ );
222
+ const first_type_id = item.type_options.filter(
223
+ (t_o) => t_o.name === data.value,
224
+ )[0].id;
225
+ new_cartItems[objIndex].type = first_type_id;
226
+ const dataset = datasets_items
227
+ ? datasets_items.find((req) => req.UID === item.dataset_uid)
228
+ : false;
229
+ const format_item = dataset.dataset_download_information.items.find(
230
+ (item) => item['@id'] === first_type_id,
231
+ );
232
+ new_cartItems[objIndex].format = format_item.full_format;
233
+ setCartItems([...new_cartItems]);
234
+ }}
235
+ />
236
+ ) : (
237
+ defaultType.name
238
+ );
230
239
  }
231
240
  };
232
241
 
233
- const CollectionNaming = (typeProps) => {
234
- const { item } = typeProps;
242
+ const CollectionNaming = ({ item }) => {
235
243
  if (item.file_id) {
236
244
  return '-';
237
245
  } else if (!item.type) {
238
246
  return '-';
239
247
  }
240
-
241
- return (
248
+ return item.type_options.filter((t_o) => t_o.id === item.type).length >
249
+ 1 ? (
242
250
  <Select
243
251
  placeholder="Select type"
244
252
  value={
@@ -248,30 +256,65 @@ const CLMSCartContent = (props) => {
248
256
  }
249
257
  options={
250
258
  item?.type_options?.length > 0 &&
251
- item?.type_options.map((option) => {
252
- return {
253
- key: option.id,
254
- value: option.id,
255
- text:
256
- (option.collection === undefined && '-') || option.collection,
257
- };
258
- })
259
+ item?.type_options
260
+ .filter(
261
+ (o) =>
262
+ o.name ===
263
+ item?.type_options.find((t_o) => t_o.id === item.type).name,
264
+ )
265
+ .map((option) => {
266
+ return {
267
+ key: option.id,
268
+ value: option.id,
269
+ text: option.collection ?? '-',
270
+ };
271
+ })
259
272
  }
260
273
  onChange={(e, data) => {
261
- const objIndex = cartItems.findIndex(
274
+ const new_cartItems = [...cartItems];
275
+ const objIndex = new_cartItems.findIndex(
262
276
  (obj) => obj.unique_id === item.unique_id,
263
277
  );
264
- cartItems[objIndex].type = data.value;
278
+ new_cartItems[objIndex].type = data.value;
265
279
  const dataset = datasets_items
266
280
  ? datasets_items.find((req) => req.UID === item.dataset_uid)
267
281
  : false;
268
282
  const format_item = dataset.dataset_download_information.items.find(
269
283
  (item) => item['@id'] === data.value,
270
284
  );
271
- cartItems[objIndex].format = format_item.full_format;
272
- setCartItems([...cartItems]);
285
+ new_cartItems[objIndex].format = format_item.full_format;
286
+ setCartItems([...new_cartItems]);
273
287
  }}
274
288
  />
289
+ ) : (
290
+ getCollectionByItem(item).collection ?? '-'
291
+ );
292
+ };
293
+ const FormatNaming = ({ item }) => {
294
+ const format_options = getAvailableConversion(
295
+ formatConversionTable,
296
+ originalFormatNaming(item),
297
+ );
298
+ const item_format_name = formatNaming(item);
299
+ return !item.file_id ? (
300
+ format_options.length > 1 ? (
301
+ <Select
302
+ placeholder="Select format"
303
+ value={item_format_name}
304
+ options={format_options}
305
+ onChange={(e, data) => {
306
+ const objIndex = cartItems.findIndex(
307
+ (obj) => obj.unique_id === item.unique_id,
308
+ );
309
+ cartItems[objIndex].format = data.value;
310
+ setCartItems([...cartItems]);
311
+ }}
312
+ />
313
+ ) : (
314
+ item_format_name
315
+ )
316
+ ) : (
317
+ item_format_name
275
318
  );
276
319
  };
277
320
 
@@ -307,6 +350,7 @@ const CLMSCartContent = (props) => {
307
350
  <th>Format</th>
308
351
  <th>Projection</th>
309
352
  <th></th>
353
+ <th></th>
310
354
  </tr>
311
355
  </thead>
312
356
  <tbody>
@@ -364,25 +408,7 @@ const CLMSCartContent = (props) => {
364
408
  <CollectionNaming item={item} />
365
409
  </td>
366
410
  <td className="table-td-format">
367
- {!item.file_id ? (
368
- <Select
369
- placeholder="Select format"
370
- value={formatNaming(item)}
371
- options={getAvailableConversion(
372
- formatConversionTable,
373
- originalFormatNaming(item),
374
- )}
375
- onChange={(e, data) => {
376
- const objIndex = cartItems.findIndex(
377
- (obj) => obj.unique_id === item.unique_id,
378
- );
379
- cartItems[objIndex].format = data.value;
380
- setCartItems([...cartItems]);
381
- }}
382
- />
383
- ) : (
384
- formatNaming(item)
385
- )}
411
+ <FormatNaming item={item} />
386
412
  </td>
387
413
  <td className="table-td-projections">
388
414
  {!item.file_id ? (
@@ -407,14 +433,73 @@ const CLMSCartContent = (props) => {
407
433
  <td>
408
434
  {item.task_in_progress ? (
409
435
  <FontAwesomeIcon icon="spinner" spin />
436
+ ) : !item.file_id ? (
437
+ <span
438
+ className="info-icon"
439
+ tooltip="Add a duplicated row below"
440
+ direction="up"
441
+ >
442
+ <button
443
+ onClick={() => {
444
+ duplicateCartItem(
445
+ item.unique_id,
446
+ cartItems,
447
+ setCartItems,
448
+ updateCart,
449
+ );
450
+ }}
451
+ style={{
452
+ backgroundColor: 'transparent',
453
+ color: 'inherit',
454
+ border: 'none',
455
+ padding: 0,
456
+ font: 'inherit',
457
+ cursor: 'pointer',
458
+ outline: 'inherit',
459
+ }}
460
+ >
461
+ <Icon
462
+ name={addDocumentSVG}
463
+ size={25}
464
+ title={'Add a duplicated row below'}
465
+ />
466
+ </button>
467
+ </span>
410
468
  ) : (
411
- <FontAwesomeIcon
412
- icon={['fas', 'trash']}
413
- style={{ cursor: 'pointer' }}
414
- onClick={() => {
415
- removeCartItem(item.unique_id);
416
- }}
417
- />
469
+ <></>
470
+ )}
471
+ </td>
472
+ <td>
473
+ {item.task_in_progress ? (
474
+ <FontAwesomeIcon icon="spinner" spin />
475
+ ) : (
476
+ <span
477
+ className="info-icon"
478
+ tooltip="Remove this row from the cart"
479
+ direction="up"
480
+ >
481
+ <button
482
+ onClick={() => {
483
+ removeCartItem(item.unique_id);
484
+ }}
485
+ style={{
486
+ backgroundColor: 'transparent',
487
+ color: 'inherit',
488
+ border: 'none',
489
+ padding: 0,
490
+ font: 'inherit',
491
+ cursor: 'pointer',
492
+ outline: 'inherit',
493
+ }}
494
+ >
495
+ <Icon
496
+ name={removeSVG}
497
+ size={25}
498
+ color="#e40166"
499
+ title={'Remove this row from the cart'}
500
+ />
501
+ </button>
502
+ </span>
418
503
  )}
419
504
  </td>
420
505
  </tr>
@@ -1,13 +1,22 @@
1
+ import { cleanDuplicatesEntries } from '@eeacms/volto-clms-utils/utils';
2
+
1
3
  export const formatNaming = (item) => {
2
4
  return item?.format?.token || item?.format;
3
5
  };
4
6
 
5
7
  export const originalFormatNaming = (item) => {
6
8
  const original = item?.original_format?.token || item?.original_format;
7
- const format = item?.format?.token || item?.format;
9
+ const collection = getCollectionByItem(item);
10
+ const format = collection?.full_format?.token || collection?.full_format;
8
11
  return format ? format : original;
9
12
  };
10
13
 
14
+ export const getCollectionByItem = (item) => {
15
+ return item?.type_options
16
+ ? item.type_options.find((t_o) => t_o['id'] === item.type)
17
+ : { id: '' };
18
+ };
19
+
11
20
  export const getDownloadToolPostBody = (selectedItems) => {
12
21
  const datasetList = selectedItems.map((item) => {
13
22
  let body_extras = {};
@@ -108,3 +117,80 @@ export const getCartObjectFromMapviewer = (
108
117
  timeExtent: local_cart_data.timeExtent || [],
109
118
  };
110
119
  };
120
+
121
+ export const duplicateCartItem = (
122
+ unique_id,
123
+ cartItems,
124
+ setCartItems,
125
+ updateCart,
126
+ ) => {
127
+ if (cartItems.length > 0) {
128
+ const itemIndex = cartItems.findIndex((obj) => obj.unique_id === unique_id);
129
+ const new_item = Object.assign(
130
+ {},
131
+ {
132
+ ...cartItems[itemIndex],
133
+ unique_id: cartItems[itemIndex].unique_id + '-copy',
134
+ },
135
+ );
136
+ while (cartItems.some((c_i) => c_i.unique_id === new_item.unique_id)) {
137
+ new_item['unique_id'] = new_item.unique_id + '-copy';
138
+ }
139
+ cartItems.splice(itemIndex + 1, 0, new_item);
140
+ refreshCart(cartItems, setCartItems, updateCart);
141
+ }
142
+ };
143
+
144
+ export const refreshCart = (cartItems, setCartItems, updateCart) => {
145
+ setCartItems([...cartItems]);
146
+ updateCart([
147
+ ...cartItems.map((c_i) => {
148
+ const file_id = c_i.file_id ? { file_id: c_i.file_id } : {};
149
+ const id = c_i.id ? { id: c_i.id } : {};
150
+ return {
151
+ ...file_id,
152
+ ...id,
153
+ UID: c_i.dataset_uid,
154
+ unique_id: c_i.unique_id,
155
+ area: c_i.area,
156
+ };
157
+ }),
158
+ ]);
159
+ };
160
+
161
+ export const concatRequestedCartItem = (
162
+ cartItems,
163
+ setCartItems,
164
+ localSessionCart,
165
+ datasets_items,
166
+ projections,
167
+ nutsnames,
168
+ ) => {
169
+ let newCartItems = [...cartItems];
170
+ localSessionCart.forEach((localItem) => {
171
+ const requestedItem = datasets_items
172
+ ? datasets_items.find((req) => req.UID === localItem.UID)
173
+ : false;
174
+ if (requestedItem) {
175
+ const file_data = requestedItem?.downloadable_files?.items.find(
176
+ (item) => item['@id'] === localItem.file_id,
177
+ );
178
+ if (file_data) {
179
+ newCartItems.push(
180
+ getCartObjectFromPrepackaged(file_data, requestedItem),
181
+ );
182
+ setCartItems(cleanDuplicatesEntries(newCartItems));
183
+ } else {
184
+ newCartItems.push(
185
+ getCartObjectFromMapviewer(
186
+ localItem,
187
+ requestedItem,
188
+ projections,
189
+ nutsnames,
190
+ ),
191
+ );
192
+ setCartItems(cleanDuplicatesEntries(newCartItems));
193
+ }
194
+ }
195
+ });
196
+ };
@@ -10,6 +10,7 @@ import { useDispatch, useSelector } from 'react-redux';
10
10
  import FileCard from './FileCard';
11
11
  import { FormattedMessage } from 'react-intl';
12
12
  import { Grid } from 'semantic-ui-react';
13
+ import { dynamicSort } from '../CclUtils';
13
14
 
14
15
  const CLMSDownloadTasks = (props) => {
15
16
  const dispatch = useDispatch();
@@ -145,22 +146,6 @@ const CLMSDownloadTasks = (props) => {
145
146
  setter(intermediate);
146
147
  }
147
148
 
148
- const dynamicSort = (property) => {
149
- var sortOrder = 1;
150
- if (property[0] === '-') {
151
- sortOrder = -1;
152
- property = property.substr(1);
153
- }
154
- return function (a, b) {
155
- /* next line works with strings and numbers,
156
- * and you may want to customize it to your needs
157
- */
158
- var result =
159
- a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
160
- return result * sortOrder;
161
- };
162
- };
163
-
164
149
  const deleteTaskInProgress = (task_id) => {
165
150
  setShowDeleteTaskLoading(task_id);
166
151
  dispatch(deleteDownloadtool(task_id));
@@ -0,0 +1,15 @@
1
+ export const dynamicSort = (property) => {
2
+ var sortOrder = 1;
3
+ if (property[0] === '-') {
4
+ sortOrder = -1;
5
+ property = property.substr(1);
6
+ }
7
+ return function (a, b) {
8
+ /* next line works with strings and numbers,
9
+ * and you may want to customize it to your needs
10
+ */
11
+ var result =
12
+ a[property] < b[property] ? -1 : a[property] > b[property] ? 1 : 0;
13
+ return result * sortOrder;
14
+ };
15
+ };
@@ -1,9 +1,8 @@
1
1
  import StringToHTML from './StringToHTML';
2
2
 
3
- export { toBase64, fromBase64 } from './base64';
4
-
5
3
  export { StringToHTML };
6
-
4
+ export { toBase64, fromBase64 } from './base64';
5
+ export { dynamicSort } from './dynamicSort';
7
6
  export {
8
7
  workOpportunitiesCclDateFormat,
9
8
  workOpportunitiesCclDateTimeFormat,
@@ -0,0 +1,73 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { dynamicSort } from './dynamicSort';
3
+ export const useFilteredPagination = (original_data) => {
4
+ const [originalDataList, setOriginalDataList] = useState(original_data);
5
+ const [dataList, setDataList] = useState(original_data);
6
+ const [paginationSize, setPaginationSize] = useState(10);
7
+ const [currentPage, setCurrentPage] = useState(1);
8
+ const [pagination, setPagination] = useState(
9
+ original_data.slice(
10
+ (currentPage - 1) * paginationSize,
11
+ (currentPage - 1) * paginationSize + paginationSize,
12
+ ),
13
+ );
14
+ const [search, setSearch] = useState({});
15
+ const applySearch = (e, { value }, field, setter) => {
16
+ const new_filters = { [field]: value };
17
+ setSearch(new_filters);
18
+ };
19
+
20
+ const applyOrder = (criteria) => {
21
+ setDataList([...dataList.sort(dynamicSort(criteria))]);
22
+ };
23
+ const clearSearch = () => {
24
+ setDataList(originalDataList);
25
+ setSearch({});
26
+ };
27
+ useEffect(() => {
28
+ let filtered_data_list = [...originalDataList];
29
+ Object.entries(search).forEach((filter) => {
30
+ const filter_key = filter[0];
31
+ const filter_data = filter[1];
32
+ if (filter_data.length > 0) {
33
+ filtered_data_list = filtered_data_list.filter((data) => {
34
+ return data[filter_key]
35
+ .toLowerCase()
36
+ .includes(filter_data.toLowerCase());
37
+ });
38
+ }
39
+ });
40
+ setDataList(filtered_data_list);
41
+ // eslint-disable-next-line react-hooks/exhaustive-deps
42
+ }, [search]);
43
+
44
+ useEffect(() => {
45
+ setPagination(
46
+ dataList.slice(
47
+ (currentPage - 1) * paginationSize,
48
+ (currentPage - 1) * paginationSize + paginationSize,
49
+ ),
50
+ );
51
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
+ }, [dataList, currentPage, paginationSize]);
53
+
54
+ return {
55
+ functions: {
56
+ setCurrentPage,
57
+ setPaginationSize,
58
+ applySearch,
59
+ setDataList,
60
+ setOriginalDataList,
61
+ clearSearch,
62
+ applyOrder,
63
+ },
64
+ data: {
65
+ pagination,
66
+ currentPage,
67
+ paginationSize,
68
+ originalDataList,
69
+ dataList,
70
+ search,
71
+ },
72
+ };
73
+ };
@@ -0,0 +1,166 @@
1
+ @import 'globals.less';
2
+
3
+ @addon: 'volto-slate';
4
+ @addontype: 'editor';
5
+ @addonelement: 'slate';
6
+
7
+ // TODO: move these to less variables
8
+
9
+ & {
10
+ .slate-inline-toolbar {
11
+ position: absolute;
12
+
13
+ /* should be above admin panes (100), and below the Link modal form (1000) */
14
+ z-index: 1500;
15
+
16
+ top: -10000px;
17
+ left: -10000px;
18
+
19
+ max-width: 100vw;
20
+ opacity: 0;
21
+
22
+ &.upper {
23
+ transform: translateY(-100%);
24
+ }
25
+ }
26
+
27
+ .toolbar-wrapper.active {
28
+ margin-bottom: 0.6em;
29
+ }
30
+
31
+ .slate-editor p {
32
+ /* In editor the <p> are wrapped in weird markup */
33
+ margin-bottom: 0 !important;
34
+ }
35
+
36
+ .slate-toolbar {
37
+ display: flex;
38
+ box-sizing: border-box;
39
+ padding: 3px;
40
+ border: none;
41
+ margin-left: -15rem;
42
+ background: #fff;
43
+ border-radius: 2px;
44
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
45
+ font-family: 'Poppins', 'Helvetica Neue', Arial, Helvetica, sans-serif;
46
+ font-size: 1rem;
47
+ font-weight: normal;
48
+
49
+ .expando {
50
+ flex-grow: 1;
51
+ background-color: #fff;
52
+ }
53
+
54
+ .toolbar-separator {
55
+ display: inline-block;
56
+ height: 32px;
57
+ border-right: 1px solid #ddd;
58
+ margin: 0 0.5rem;
59
+ }
60
+
61
+ .toolbar-separator + .button-wrapper {
62
+ margin-left: 0px;
63
+ }
64
+
65
+ .button-wrapper {
66
+ display: inline-block;
67
+ margin-left: 3px;
68
+
69
+ .ui.tiny.compact.icon.toggle.button {
70
+ width: 32px;
71
+ height: 32px;
72
+ box-sizing: border-box;
73
+ padding: 4px !important;
74
+ border: 0;
75
+ background: rgba(255, 255, 255, 0.975);
76
+ border-radius: 1px;
77
+ color: @brown;
78
+ font-size: 18px;
79
+ vertical-align: bottom;
80
+
81
+ & > svg {
82
+ fill: #888;
83
+ }
84
+
85
+ &:hover,
86
+ &:focus {
87
+ background: #f3f3f3;
88
+ outline: 0; /* reset for :focus */
89
+ }
90
+
91
+ &.active {
92
+ background: #efefef !important;
93
+ border-radius: 3px;
94
+ box-shadow: inset 0 0 0 1px @blue !important;
95
+ color: @blue !important;
96
+
97
+ & > svg {
98
+ fill: #444;
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ .ui.buttons {
105
+ width: 100%;
106
+ }
107
+
108
+ .ui.button:not(.icon) > .icon:not(.button):not(.dropdown) {
109
+ margin: auto auto auto auto !important;
110
+ }
111
+
112
+ .ui.button:last-child {
113
+ margin-right: 0 !important;
114
+ }
115
+ }
116
+
117
+ .highlight-selection {
118
+ background-color: grey;
119
+ color: white;
120
+ }
121
+
122
+ .ui.input.editor-link input {
123
+ padding: 0;
124
+ border: none;
125
+ }
126
+ }
127
+
128
+ .sidebar-container {
129
+ .slate-editor {
130
+ ul {
131
+ li {
132
+ display: list-item;
133
+ padding: 0;
134
+
135
+ span {
136
+ display: initial;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ .power-user-menu {
144
+ position: absolute;
145
+ z-index: 10;
146
+ top: 29px;
147
+ left: -9px;
148
+ width: 210px;
149
+ background-color: rgba(255, 255, 255, 0.975);
150
+ border-radius: 2px;
151
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.05);
152
+
153
+ .ui.menu {
154
+ border: 0;
155
+ border-radius: 2px;
156
+
157
+ .icon {
158
+ margin-right: 12px;
159
+ vertical-align: middle;
160
+ }
161
+
162
+ .item.active {
163
+ background: #efefef !important;
164
+ }
165
+ }
166
+ }
@@ -0,0 +1,18 @@
1
+ /* Needed by semantic.less */
2
+ @type: 'extra';
3
+ @element: 'custom';
4
+
5
+ @import (multiple, reference, optional) '../../theme.config';
6
+
7
+ /* Enables customization of addons */
8
+ .loadAddonOverrides() {
9
+ @import (optional)
10
+ '@{siteFolder}/../addons/@{addon}/@{addontype}s/@{addonelement}.overrides';
11
+ }
12
+
13
+ /* Helper to load variables */
14
+ .loadAddonVariables() {
15
+ @import (optional) '@{addonelement}.variables';
16
+ @import (optional)
17
+ '@{siteFolder}/../addons/@{addon}/@{addontype}s/@{addonelement}.variables';
18
+ }
package/src/index.js CHANGED
@@ -110,6 +110,7 @@ const applyConfig = (config) => {
110
110
  };
111
111
  config.settings = {
112
112
  ...config.settings,
113
+ downloadableObjects: ['File', 'TechnicalLibrary'],
113
114
  appExtras: [
114
115
  ...config.settings.appExtras,
115
116
  {
@@ -150,9 +150,9 @@ a:hover,
150
150
  /* CLMS-1356 */
151
151
  @media only screen and (max-width: 768px) {
152
152
  .ccl-container {
153
- width: auto!important;
154
- margin-right: 1em!important;
155
- margin-left: 1em!important;
153
+ width: auto !important;
154
+ margin-right: 1em !important;
155
+ margin-left: 1em !important;
156
156
  }
157
157
  }
158
158
 
@@ -568,8 +568,9 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
568
568
  /* Downloads cart CLMS-1357 */
569
569
  @media only screen and (max-width: 500px) {
570
570
  .center.aligned.middle.aligned.two.wide.column {
571
- padding:0px;
571
+ padding: 0px;
572
572
  }
573
+
573
574
  .ui.olive.segment {
574
575
  padding-left: 2px;
575
576
  }
@@ -920,11 +921,11 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
920
921
  .dataset-download-table .ui.message {
921
922
  left: 25%;
922
923
  width: 50%;
923
- background-color: #F8F8F9;
924
+ background-color: #f8f8f9;
925
+ box-shadow: 0px 0px 4px 0px #dadada;
924
926
  color: inherit;
925
927
  font-size: 1rem;
926
928
  font-weight: bold;
927
- box-shadow: 0px 0px 4px 0px #dadada;
928
929
  }
929
930
 
930
931
  .dataset-download-table .ui.checkbox input:checked ~ label:before,
@@ -942,8 +943,8 @@ body:not(.contenttype-lrf:not(.section-cart):not(.section-profile))
942
943
  }
943
944
 
944
945
  .dataset-download-table > .ui.basic.segment {
945
- padding-left: 0px;
946
946
  padding-right: 0px;
947
+ padding-left: 0px;
947
948
  }
948
949
 
949
950
  /* Downloads */
@@ -1255,14 +1256,15 @@ div#page-document h1.documentFirstHeading {
1255
1256
 
1256
1257
  // CLMS-1344
1257
1258
  #sidebar-metadata .react-select__option {
1258
- white-space: nowrap;
1259
1259
  width: fit-content;
1260
+ white-space: nowrap;
1260
1261
  }
1261
1262
 
1262
1263
  // CLMS-1424
1263
1264
  .cart-table td {
1264
1265
  word-break: unset !important;
1265
1266
  }
1267
+
1266
1268
  .cart-table .dropdown {
1267
1269
  min-width: 7rem !important;
1268
1270
  }
@@ -1273,8 +1275,8 @@ div#page-document h1.documentFirstHeading {
1273
1275
 
1274
1276
  // CLMS-1460
1275
1277
  .technical-library-edit-link {
1276
- vertical-align: middle;
1277
1278
  margin-left: 5px;
1279
+ vertical-align: middle;
1278
1280
  }
1279
1281
 
1280
1282
  // CLMS-1542
@@ -1294,4 +1296,4 @@ main {
1294
1296
  // CLMS-1588
1295
1297
  .carousel-text-link-container {
1296
1298
  min-height: 30px;
1297
- }
1299
+ }