diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/index.js b/src/components/manage/Blocks/DiscodataConnectorBlock/index.js new file mode 100644 index 0000000..d775912 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/index.js @@ -0,0 +1,24 @@ +import worldSVG from '@plone/volto/icons/world.svg'; +import DiscodataConnectorBlockEdit from './v1/Edit'; +import DiscodataConnectorBlockView from './v1/View'; + +// TODO: Move this to volto-forests-theme + +export default (config) => { + config.blocks.blocksConfig.discodata_connector_block = { + id: 'discodata_connector_block', + title: 'Discodata connector block', + icon: worldSVG, + group: 'data_blocks', + view: DiscodataConnectorBlockView, + edit: DiscodataConnectorBlockEdit, + restricted: false, + mostUsed: false, + sidebarTab: 1, + security: { + addPermission: [], + view: [], + }, + }; + return config; +}; diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/styles.css b/src/components/manage/Blocks/DiscodataConnectorBlock/styles.css new file mode 100644 index 0000000..db5c8e9 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/styles.css @@ -0,0 +1,16 @@ +.connected-data-block { + background-color: lightblue; +} + +.block-container .data-content span { + white-space: break-spaces; +} + +#sidebar-properties .accordion .ui.menu { + flex-wrap: wrap; +} + +#sidebar-properties .accordion .ui.menu .active.item { + border: 1px solid #d4d4d5; + border-radius: 0.28rem !important; +} diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v1/Edit.jsx b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/Edit.jsx new file mode 100644 index 0000000..d0b04df --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/Edit.jsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect } from 'react'; +import { compose } from 'redux'; +import _uniqueId from 'lodash/uniqueId'; +import { v4 as uuid } from 'uuid'; +import View from './View'; +import { SidebarPortal } from '@plone/volto/components'; +import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; +import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs'; + +import { getSchema } from './schema'; + +import '../styles.css'; + +const Edit = (props) => { + const [state, setState] = useState({ + id: _uniqueId('block_'), + schema: getSchema(props), + }); + + useEffect(() => { + const newData = { ...props.data }; + if (props.data.data_providers) { + if ( + typeof props.data?.data_providers === 'object' && + props.data?.data_providers?.value + ) { + try { + newData.data_providers = []; + const dataProvidersSchema = JSON.parse( + props.data?.data_providers?.value, + ); + dataProvidersSchema?.fieldsets?.[0]?.fields && + dataProvidersSchema.fieldsets[0].fields.forEach((dataProvider) => { + newData.data_providers.push({ + ...dataProvidersSchema.properties[dataProvider], + '@id': uuid(), + id: dataProvider, + }); + }); + } catch {} + } + } + if (JSON.stringify(newData) !== JSON.stringify(props.data)) { + props.onChangeBlock(props.block, { + ...newData, + }); + } + /* eslint-disable-next-line */ + }, []); + + useEffect(() => { + setState({ + ...state, + schema: getSchema(props), + }); + /* eslint-disable-next-line */ + }, [props.providers_data, props.data.data_providers_new]); + + return ( +
+ + { + props.onChangeBlock(props.block, { + ...(props.data || {}), + [field]: data, + }); + }} + formData={props.data || {}} + block={props.block} + /> + + +
+ ); +}; + +export default compose( + connectToMultipleProviders((props) => ({ + providers: + props.data?.data_providers + ?.map((provider) => ({ + provider_url: provider.path, + title: provider.title, + })) + ?.filter((provider) => provider.provider_url) || [], + })), +)(Edit); diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx new file mode 100644 index 0000000..bb82ae5 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx @@ -0,0 +1,180 @@ +import React, { useState, useEffect } from 'react'; +import { compose } from 'redux'; +import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs'; +import { DataConnectedValue } from '@eeacms/volto-datablocks/Utils'; +import { Sources } from '@eeacms/volto-datablocks/Utils'; + +const providerView = (dataProviderKey, dataProvider) => { + return ( +
+ + {dataProvider.hasDiscodataConnector && ( + + )} + {dataProvider.measurmentUnit || ''} + + {' ' + (dataProvider.additionalText || '')} +
+ ); +}; + +const bulletListView = (items) => ( +
+ {items && + Object.entries(items).map(([key, item]) => ( +
+ {item.leftText} + {item.rightText} +
+ ))} +
+); + +const View = (props) => { + const [dataProviders, setDataProviders] = useState({}); + const [parentsDataProviders, setParentsDataProviders] = useState({}); + const { providers_data, providers_metadata } = props; + const bulletList = + props.data?.bullet_list?.value && + JSON.parse(props.data?.bullet_list?.value).properties; + + const updateDataProviders = () => { + let newDataProviders = { ...dataProviders }; + if (props.data.data_providers) { + if ( + typeof props.data.data_providers === 'object' && + props.data.data_providers.value + ) { + newDataProviders = {}; + const dataProvidersSchema = + props.data?.data_providers?.value && + JSON.parse(props.data?.data_providers?.value); + dataProvidersSchema?.fieldsets?.[0]?.fields && + dataProvidersSchema.fieldsets[0].fields.forEach((dataProvider) => { + newDataProviders[dataProvider] = { + ...dataProvidersSchema.properties[dataProvider], + }; + }); + } else if (Array.isArray(props.data.data_providers)) { + newDataProviders = {}; + props.data.data_providers.forEach((provider) => { + newDataProviders[provider.id] = { ...provider }; + }); + } + } + setDataProviders({ ...newDataProviders }); + return newDataProviders; + }; + + const updateParentsDataProviders = () => { + const newParentsDataProviders = {}; + dataProviders && + Object.entries(dataProviders).forEach( + ([dataProviderKey, dataProvider]) => { + if (!dataProvider.hasParent) { + newParentsDataProviders[dataProviderKey] = { ...dataProvider }; + } else if ( + dataProvider.parent && + newParentsDataProviders[dataProvider.parent] + ) { + if (!newParentsDataProviders[dataProvider.parent].children) { + newParentsDataProviders[dataProvider.parent].children = {}; + } + newParentsDataProviders[dataProvider.parent].children[ + dataProviderKey + ] = dataProvider; + } + }, + ); + setParentsDataProviders({ ...newParentsDataProviders }); + return newParentsDataProviders; + }; + + useEffect(() => { + updateDataProviders(); + /* eslint-disable-next-line */ + }, [JSON.stringify(props.data?.data_providers)]); + + useEffect(() => { + updateParentsDataProviders(); + /* eslint-disable-next-line */ + }, [JSON.stringify(dataProviders)]); + + const view = ( +
+
+ {/* {props.data?.block_title ?
{props.data.block_title}
: ''} */} + {parentsDataProviders && + Object.entries(parentsDataProviders).map( + ([dataProviderKey, dataProvider]) => { + if (dataProvider.children) { + return ( +
+ {providerView(dataProviderKey, dataProvider)} + {Object.entries( + dataProvider.children, + ).map(([cildrenKey, children]) => + providerView(cildrenKey, children), + )} +
+ ); + } + return ( +
+ {providerView(dataProviderKey, dataProvider)} +
+ ); + }, + )} + {bulletList && bulletListView(bulletList)} + {props?.data?.chartSources && ( +
+ provider.path) + ?.filter((path) => path)?.[0] + } + /> +
+ )} +
+
+ ); + return view; +}; + +export default compose( + connectToMultipleProviders((props) => ({ + providers: + props.data?.data_providers + ?.map((provider) => ({ + provider_url: provider.path, + title: provider.title, + has_data_query_by_context: provider.has_data_query_by_context, + has_data_query_by_provider: provider.has_data_query_by_provider, + data_query: provider.data_query, + })) + ?.filter((provider) => provider.provider_url) || [], + })), +)(View); diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v1/schema.js b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/schema.js new file mode 100644 index 0000000..cb983b2 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/schema.js @@ -0,0 +1,254 @@ +import React from 'react'; + +const makeChoices = (keys) => keys && keys.map((k) => [k, k]); + +const getDataProvidersIds = (data_providers = [], child = {}) => { + const ids = data_providers + .map((data_provider) => data_provider.id) + .filter((id) => id && id !== child.id); + return makeChoices(ids); +}; + +const dataProviderSchemaExtender = (schema, child = {}, props) => { + const data_providers = props.data.data_providers || []; + return { + ...schema, + fieldsets: [ + { + ...schema.fieldsets[0], + }, + { + id: 'properties', + title: 'Properties', + fields: ['measurmentUnit', 'additionalText', 'className'], + }, + ...(child.hasDiscodataConnector + ? [ + { + id: 'Discodata connector', + title: 'Discodata connector', + fields: [ + 'path', + 'displayColumn', + 'textTemplate', + 'specifier', + 'data_query', + ], + }, + ] + : []), + ...(child.hasParent + ? [ + { + id: 'parent', + title: 'Parent', + fields: ['parent'], + }, + ] + : []), + ], + properties: { + ...schema.properties, + displayColumn: { + ...schema.properties.displayColumn, + choices: makeChoices( + Object.keys(props.providers_data?.[child.title] || {}), + ), + }, + parent: { + ...schema.properties.parent, + choices: getDataProvidersIds(data_providers, child), + }, + }, + }; +}; + +const dataProviderSchema = { + title: 'Data provider', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: [ + 'title', + 'id', + 'hasDiscodataConnector', + 'hasParent', + 'wrapperClassName', + ], + }, + { + id: 'advanced', + title: 'Advanced', + fields: [ + 'path', + 'displayColumn', + 'textTemplate', + 'specifier', + 'measurmentUnit', + 'additionalText', + 'className', + 'parent', + 'wrapperClassName', + ], + }, + ], + properties: { + title: { + type: 'text', + title: 'Title', + }, + id: { + type: 'text', + title: 'Id', + }, + hasDiscodataConnector: { + type: 'boolean', + title: 'Has discodata connector', + }, + hasParent: { + type: 'boolean', + title: 'Has parent', + }, + path: { + widget: 'object_by_path', + title: 'Discodata connector', + }, + displayColumn: { + type: 'select', + title: 'Display column', + choices: [], + }, + textTemplate: { + title: 'Text template', + widget: 'textarea', + description: 'Add suffix/prefix to text. Use {} for value placeholder', + }, + specifier: { + title: 'Format', + description: ( + <> + See{' '} + + D3 format documentation + + + ), + }, + measurmentUnit: { + type: 'text', + title: 'Measurment unit', + }, + additionalText: { + type: 'text', + title: 'Additional text', + }, + + className: { + type: 'select', + title: 'Class name', + choices: [ + ['data', 'Data'], + ['data-content', 'Data content'], + ], + }, + parent: { + type: 'select', + title: 'Parent', + choices: [], + }, + wrapperClassName: { + type: 'select', + title: 'Wrapper class name', + choices: [ + ['data-wrapper brown', 'Brown wrapper'], + ['data-wrapper green', 'Green wrapper'], + ['data-wrapper blue', 'Blue wrapper'], + ['data-wrapper purple', 'Purple wrapper'], + ], + }, + data_query: { + title: 'Data query', + widget: 'data_query', + }, + }, + required: ['title', 'id'], +}; + +const SourceSchema = { + title: 'Source', + + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['chart_source', 'chart_source_link'], + }, + ], + + properties: { + chart_source: { + type: 'string', + title: 'Source', + }, + chart_source_link: { + type: 'string', + title: 'Link', + }, + }, + + required: ['source'], +}; + +export const getSchema = (props) => ({ + title: 'Discodata connector block', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['block_title'], + }, + { + id: 'advanced', + title: 'Advanced', + fields: ['data_providers'], + }, + { + id: 'sources', + title: 'Sources', + fields: ['chartSources', 'download_button'], + }, + ], + properties: { + block_title: { + title: 'Title', + widget: 'textarea', + }, + download_button: { + title: 'Download button', + type: 'boolean', + defaultValue: true, + }, + chartSources: { + widget: 'object_list', + title: 'Sources', + schema: SourceSchema, + }, + data_providers: { + title: 'Data providers', + widget: 'object_list', + schema: dataProviderSchema, + schemaExtender: (schema, child) => + dataProviderSchemaExtender(schema, child, props), + defaultData: { + hasDiscodataConnector: true, + hasParent: false, + }, + }, + }, + required: [], +}); diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v2/Edit.jsx b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/Edit.jsx new file mode 100644 index 0000000..70a1926 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/Edit.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { compose } from 'redux'; +import { SidebarPortal } from '@plone/volto/components'; +import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; +import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs'; +import getSchema from './schema'; +import View from './View'; + +const Edit = (props) => { + const schema = React.useMemo(() => getSchema(props), [props]); + + return ( + <> + + + + { + props.onChangeBlock(props.block, { + ...props.data, + [id]: value, + }); + }} + formData={props.data} + /> + + + ); +}; + +export default compose( + connectToMultipleProviders((props) => ({ + providers: props.data?.providers, + })), +)(Edit); diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v2/View.jsx b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/View.jsx new file mode 100644 index 0000000..b0f1d11 --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/View.jsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { compose } from 'redux'; +import { connectToMultipleProviders } from '@eeacms/volto-datablocks/hocs'; +import { DataConnectedValue } from '@eeacms/volto-datablocks/Utils'; +import { Sources } from '@eeacms/volto-datablocks/Utils'; + +const ProviderView = ({ provider }) => { + if (!provider) return ''; + return ( +
+ + + +
+ ); +}; + +const View = (props) => { + const { data = {} } = props; + const { providers = [] } = data; + + if (!providers.length) return

Add providers

; + + return ( +
+
+ {providers.map((provider, index) => { + if ((index + 1) % 2 === 0) return ''; + return ( +
+ + +
+ ); + })} + {props.data?.chartSources && providers?.[0]?.url && ( +
+ +
+ )} +
+
+ ); +}; + +export default compose( + connectToMultipleProviders((props) => ({ + providers: props.data.providers, + })), +)(View); diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v2/schema.js b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/schema.js new file mode 100644 index 0000000..629743d --- /dev/null +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v2/schema.js @@ -0,0 +1,204 @@ +import React from 'react'; + +const dataProviderSchemaExtender = (schema = {}, child = {}, props) => { + const title = child.title || child.url; + if (!title || !props.providers_data) return schema; + const provider_data = props.providers_data[title] || {}; + const columns = Array.from( + new Set(Object.keys(provider_data || {})), + ).map((n) => [n, n]); + const rows = + child.column && provider_data[child.column] + ? provider_data[child.column].map((value, index) => [index, value]) + : []; + + return { + ...schema, + properties: { + ...schema.properties, + column: { + title: 'Column', + choices: columns, + }, + row: { + title: 'Row', + choices: rows, + }, + }, + }; +}; + +const dataProviderSchema = { + title: 'Data provider', + fieldsets: [ + { + id: 'default', + title: 'Properties', + fields: [ + 'title', + 'url', + 'column', + 'row', + 'specifier', + 'textTemplate', + 'placeholder', + ], + }, + { + id: 'advanced', + title: 'Advanced', + fields: [ + 'className', + 'wrapperClassName', + 'has_data_query_by_context', + 'has_data_query_by_provider', + 'data_query', + ], + }, + ], + properties: { + title: { + type: 'text', + title: 'Title', + }, + url: { + widget: 'object_by_path', + title: 'Data provider', + }, + column: { + title: 'Column', + choices: [], + }, + row: { + title: 'Row', + default: 0, + choices: [], + }, + specifier: { + title: 'Format specifier', + description: ( + <> + See{' '} + + D3 format documentation + + + ), + }, + textTemplate: { + title: 'Text template', + widget: 'textarea', + description: 'Add suffix/prefix to text. Use {} for value placeholder', + }, + placeholder: { + title: 'Placeholder', + }, + className: { + type: 'select', + title: 'Class name', + choices: [ + ['data', 'Data'], + ['data-content', 'Data content'], + ], + }, + wrapperClassName: { + type: 'select', + title: 'Wrapper class name', + choices: [ + ['data-wrapper brown', 'Brown wrapper'], + ['data-wrapper green', 'Green wrapper'], + ['data-wrapper blue', 'Blue wrapper'], + ['data-wrapper purple', 'Purple wrapper'], + ], + }, + has_data_query_by_context: { + title: 'Has data_query by context', + type: 'boolean', + defaultValue: true, + }, + has_data_query_by_provider: { + title: 'Has data_query by provider', + type: 'boolean', + defaultValue: true, + }, + data_query: { + title: 'Data query', + widget: 'data_query', + }, + }, + required: ['title', 'id'], +}; + +const SourceSchema = { + title: 'Source', + + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['chart_source', 'chart_source_link'], + }, + ], + + properties: { + chart_source: { + type: 'string', + title: 'Source', + }, + chart_source_link: { + type: 'string', + title: 'Link', + }, + }, + + required: ['source'], +}; + +export default (props) => ({ + title: 'Discodata connector block', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: ['block_title'], + }, + { + id: 'advanced', + title: 'Advanced', + fields: ['providers'], + }, + { + id: 'sources', + title: 'Sources', + fields: ['chartSources', 'download_button'], + }, + ], + properties: { + block_title: { + title: 'Title', + widget: 'textarea', + }, + chartSources: { + widget: 'object_list', + title: 'Sources', + schema: SourceSchema, + }, + download_button: { + title: 'Download button', + type: 'boolean', + defaultValue: true, + }, + providers: { + title: 'Data providers', + widget: 'object_list', + schema: dataProviderSchema, + schemaExtender: (schema, child) => + dataProviderSchemaExtender(schema, child, props), + }, + }, + required: [], +}); diff --git a/src/index.js b/src/index.js index 18a580e..6100ebe 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorize import installAppExtras from '@eeacms/volto-forests-theme/components/theme/AppExtras'; import { applyConfig as installFiseFrontend } from './localconfig'; +import installDiscodataConnectorBlock from '@eeacms/volto-forests-theme/components/manage/Blocks/DiscodataConnectorBlock'; import ObjectListInlineWidget from './components/manage/Widgets/ObjectListInlineWidget'; import reducers from '@eeacms/volto-forests-theme/reducers'; @@ -12,10 +13,11 @@ import './slate-styles.css'; export default function applyConfig(config) { // Add here your project's configuration here by modifying `config` accordingly - config = [installAppExtras, installFiseFrontend].reduce( - (acc, apply) => apply(acc), - config, - ); + config = [ + installAppExtras, + installFiseFrontend, + installDiscodataConnectorBlock, + ].reduce((acc, apply) => apply(acc), config); config.settings = { ...config.settings, diff --git a/theme/site/globals/site.overrides b/theme/site/globals/site.overrides index 41b8843..e8b80e0 100644 --- a/theme/site/globals/site.overrides +++ b/theme/site/globals/site.overrides @@ -176,7 +176,7 @@ body.has-toolbar { .sticky-header-nav { - + position: absolute; bottom: -5px; z-index: 4; @@ -190,7 +190,7 @@ body.has-toolbar { .header-navigation-lead { - + .navigation-prev { position: absolute; @@ -1558,239 +1558,242 @@ span.float-right { } // Europe forest specific blocks -h5 { - color: @darkBrown; - font-weight: bold; - text-transform: uppercase; -} - -div { - &.flex { - display: flex; +.block-container, +.columns-view { + h5 { + color: @darkBrown; + font-weight: bold; + text-transform: uppercase; } - &.flex-column { - flex-direction: column; - } + div { + &.flex { + display: flex; + } - &.h-100 { - height: 100%; - } + &.flex-column { + flex-direction: column; + } - &.w-100 { - width: 100%; + &.h-100 { + height: 100%; + } + + &.w-100 { + width: 100%; + } + + &.pa-1 { + padding: 10px; + } } - &.pa-1 { + .forest-block-wrapper { + display: flex; + height: 100%; padding: 10px; } -} -.forest-block-wrapper { - display: flex; - height: 100%; - padding: 10px; -} - -.forest-specific-block { - display: flex; - width: 100%; - flex-direction: column; -} + .forest-specific-block { + display: flex; + width: 100%; + flex-direction: column; + } -.land-data-wrapper, -.data-wrapper { - display: flex; - align-items: center; - padding: 1em; - margin-bottom: 1em; - border-radius: 5px; + .land-data-wrapper, + .data-wrapper { + display: flex; + align-items: center; + padding: 1em; + margin-bottom: 1em; + border-radius: 5px; - .formatted-value { - span { - color: #000 !important; - font-size: 13px; - font-weight: normal; + .formatted-value { + span { + color: #000 !important; + font-size: 13px; + font-weight: normal; + } } } -} -.land-data, -.data { - padding-right: 10px; + .land-data, + .data { + padding-right: 10px; - span { - font-size: 50px; - font-weight: 700; - line-height: 46px; + span { + font-size: 50px; + font-weight: 700; + line-height: 46px; + } } -} -.land-data-content, -.data-content { - font-size: 14px; - font-weight: 300; + .land-data-content, + .data-content { + font-size: 14px; + font-weight: 300; - span { - display: block; - font-size: 18px; - font-weight: 500; - } + span { + display: block; + font-size: 18px; + font-weight: 500; + } - .data-entity { - display: inline; + .data-entity { + display: inline; + } } -} -.data-wrapper { - &.brown { - background-color: @tileBackgroundColor; + .data-wrapper { + &.brown { + background-color: @tileBackgroundColor; - span { - color: @darkBrown; + span { + color: @darkBrown; + } } - } - &.green { - background-color: @tileBackgroundColor; - color: @darkGreen; + &.green { + background-color: @tileBackgroundColor; + color: @darkGreen; - .data-content { - color: initial; + .data-content { + color: initial; - span { - color: @darkGreen; + span { + color: @darkGreen; + } } } - } - &.blue { - background-color: @tileBackgroundColor; - color: @darkBlue; + &.blue { + background-color: @tileBackgroundColor; + color: @darkBlue; - .data-content { - color: initial; + .data-content { + color: initial; - span { - color: @darkBlue; + span { + color: @darkBlue; + } } } - } - &.purple { - background-color: @tileBackgroundColor; - color: @purple; + &.purple { + background-color: @tileBackgroundColor; + color: @purple; - .data-content { - color: initial; + .data-content { + color: initial; - span { - color: @purple; + span { + color: @purple; + } } } } -} -.ui.list .item span { - float: right; -} + .ui.list .item span { + float: right; + } -.ui.bulleted.list > .item { - padding: 7px 0; - font-size: 14px; + .ui.bulleted.list > .item { + padding: 7px 0; + font-size: 14px; - a { - color: @textColor; - text-decoration: underline; + a { + color: @textColor; + text-decoration: underline; + } } -} -ul.ui.list li:before, -.ui.bulleted.list .list > .item:before, -.ui.bulleted.list > .item:before { - top: 10px; - width: 8px; - height: 8px; - background-color: @darkBrown; - border-radius: 50%; - content: ''; -} + ul.ui.list li:before, + .ui.bulleted.list .list > .item:before, + .ui.bulleted.list > .item:before { + top: 10px; + width: 8px; + height: 8px; + background-color: @darkBrown; + border-radius: 50%; + content: ''; + } -.forest-comparation { - .land-data-wrapper { - margin-bottom: 0; - background-color: @tileBackgroundColor; + .forest-comparation { + .land-data-wrapper { + margin-bottom: 0; + background-color: @tileBackgroundColor; - span { - color: @darkBrown; + span { + color: @darkBrown; + } } } -} -@tileBackgroundColor: #f6f6f5; -@darkBlue: #002d54; + @tileBackgroundColor: #f6f6f5; + @darkBlue: #002d54; -.forest-area-block { - .land-data-wrapper.eu28-data { - background-color: @tileBackgroundColor; - color: @darkGreen; + .forest-area-block { + .land-data-wrapper.eu28-data { + background-color: @tileBackgroundColor; + color: @darkGreen; - .land-data-content { - color: initial; + .land-data-content { + color: initial; - span { - color: @darkGreen; + span { + color: @darkGreen; + } + } + + &.purple { + color: @purple; + + .land-data-content { + color: initial; + + span { + color: @purple; + } + } } } - &.purple { - color: @purple; + .land-data-wrapper.eea39-data { + background-color: @tileBackgroundColor; + color: @darkBlue; .land-data-content { color: initial; span { - color: @purple; + color: @darkBlue; } } } - } - - .land-data-wrapper.eea39-data { - background-color: @tileBackgroundColor; - color: @darkBlue; - .land-data-content { - color: initial; + .coverage-data { + display: flex; + margin: 1em 0; + color: #fff; + font-size: 16px; span { - color: @darkBlue; + display: block; + margin-bottom: 0.2em; + font-size: 28px; } } } - .coverage-data { - display: flex; - margin: 1em 0; - color: #fff; - font-size: 16px; - - span { - display: block; - margin-bottom: 0.2em; - font-size: 28px; - } + .map-wrapper { + padding: 5px; + border: 1px solid @darkGreen; + border-radius: 5px; } -} -.map-wrapper { - padding: 5px; - border: 1px solid @darkGreen; - border-radius: 5px; -} - -.map-buttons { - margin-top: 2em; + .map-buttons { + margin-top: 2em; + } } .forest-patch-size-distribution {