From f7a18cfce6c3c986de2ebfdbca14b0cf6e432038 Mon Sep 17 00:00:00 2001 From: andreiggr Date: Mon, 11 Apr 2022 12:25:56 +0300 Subject: [PATCH] Expandable template on simpledatatable --- package.json | 3 +- .../DiscodataConnectorBlock/v1/View.jsx | 13 + .../manage/Blocks/SimpleDataTable/index.js | 21 ++ .../templates/expandable/Edit.jsx | 134 +++++++++ .../templates/expandable/PopupMap.jsx | 90 ++++++ .../templates/expandable/PopupRow.jsx | 253 ++++++++++++++++ .../templates/expandable/PopupTable.jsx | 88 ++++++ .../templates/expandable/ReadMore.jsx | 29 ++ .../templates/expandable/View.jsx | 275 ++++++++++++++++++ .../templates/expandable/index.js | 6 + .../templates/expandable/schema.js | 200 +++++++++++++ .../templates/expandable/style.less | 159 ++++++++++ src/index.js | 2 + 13 files changed, 1272 insertions(+), 1 deletion(-) create mode 100644 src/components/manage/Blocks/SimpleDataTable/index.js create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/Edit.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupMap.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupRow.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupTable.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/ReadMore.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/View.jsx create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/index.js create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/schema.js create mode 100644 src/components/manage/Blocks/SimpleDataTable/templates/expandable/style.less diff --git a/package.json b/package.json index bdad86e..43158aa 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "react-highlight-words": "^0.16.0", "react-image-gallery": "1.2.7", "react-lazy-load-image-component": "^1.5.0", - "react-stickynode": "^2.1.1" + "react-stickynode": "^2.1.1", + "pigeon-maps": "0.21.0" }, "devDependencies": { "@cypress/code-coverage": "^3.9.5", diff --git a/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx index d6ce53b..d6bd027 100644 --- a/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx +++ b/src/components/manage/Blocks/DiscodataConnectorBlock/v1/View.jsx @@ -47,6 +47,19 @@ const bulletListView = (items) => ( ); const View = (props) => { + console.log('props', props); + console.log( + 'themprov', + props.data?.data_providers + ?.map((provider) => ({ + provider_url: provider.path, + 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) || [], + ); + const [dataProviders, setDataProviders] = useState({}); const [parentsDataProviders, setParentsDataProviders] = useState({}); const { providers_data, providers_metadata } = props; diff --git a/src/components/manage/Blocks/SimpleDataTable/index.js b/src/components/manage/Blocks/SimpleDataTable/index.js new file mode 100644 index 0000000..b810750 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/index.js @@ -0,0 +1,21 @@ +import { + ExpandableEdit, + expandableSchema, + ExpandableView, +} from './templates/expandable'; + +export default (config) => { + config.blocks.blocksConfig.simpleDataConnectedTable = { + ...config.blocks.blocksConfig.simpleDataConnectedTable, + templates: { + ...config.blocks.blocksConfig.simpleDataConnectedTable.templates, + expandable: { + title: 'Expandable', + view: ExpandableView, + edit: ExpandableEdit, + schema: expandableSchema, + }, + }, + }; + return config; +}; diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/Edit.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/Edit.jsx new file mode 100644 index 0000000..715d5a1 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/Edit.jsx @@ -0,0 +1,134 @@ +import React, { Component } from 'react'; +import { compose } from 'redux'; +import { SidebarPortal } from '@plone/volto/components'; // EditBlock +import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; + +import config from '@plone/volto/registry'; + +import { + connectToProviderData, + connectToMultipleProviders, +} from '@eeacms/volto-datablocks/hocs'; + +import { SimpleDataTableSchema } from '@eeacms/volto-datablocks/components/manage/Blocks/SimpleDataTable/schema'; +import { SimpleDataTableView } from '@eeacms/volto-datablocks/components/manage/Blocks/SimpleDataTable/View'; + +class Edit extends Component { + getSchema = () => { + const template = this.props.data.template || 'default'; + const templateSchema = + config.blocks.blocksConfig.simpleDataConnectedTable?.templates?.[template] + ?.schema || {}; + + const schema = SimpleDataTableSchema(config, templateSchema(config)); + + // TODO: create picker for columns to include + // console.log('bigtableprovurl', this.props.data.provider_url); + // console.log( + // 'bigprovdata', + // this.props.providers_data[this.props.data.provider_url], + // ); + + const provider_url = this.props.data?.provider_url; + const provider_data = provider_url + ? this.props.providers_data[provider_url] + : ''; + + // const { provider_data } = this.props; + + if (!provider_data) return schema; + + const choices = Array.from(Object.keys(provider_data).sort()).map((n) => [ + n, + n, + ]); + + schema.properties.columns.schema.properties.column.choices = choices; + schema.properties.columns.schema.properties.column_link.choices = choices; + + const map_provider_url = this.props.data.popup_map_provider_url + ? this.props.data.popup_map_provider_url + : ''; + const table_provider_url = this.props.data.popup_table_provider_url + ? this.props.data.popup_table_provider_url + : ''; + + const map_provider_data = + this.props.providers_data && this.props.providers_data[map_provider_url] + ? this.props.providers_data[map_provider_url] + : ''; + + const table_provider_data = + this.props.providers_data && this.props.providers_data[table_provider_url] + ? this.props.providers_data[table_provider_url] + : ''; + + const mapChoices = map_provider_data + ? Array.from(Object.keys(map_provider_data).sort()).map((n) => [n, n]) + : []; + + const tableChoices = table_provider_data + ? Array.from(Object.keys(table_provider_data).sort()).map((n) => [n, n]) + : []; + schema.properties.popup_data_query.choices = choices; + + schema.properties.image_url.choices = choices; + schema.properties.popupTitle.choices = choices; + schema.properties.popupDescription.choices = choices; + schema.properties.popupUrl.choices = choices; + + //set choices for the popup table columns + schema.properties.popupTableColumns.schema.properties.column.choices = tableChoices; + schema.properties.popupTableColumns.schema.properties.column_link.choices = tableChoices; + + //set choices for the popup map + schema.properties.popupLong.choices = mapChoices; + schema.properties.popupLat.choices = mapChoices; + schema.properties.popupCountryCode.choices = mapChoices; + schema.properties.popupMapLabel.choices = mapChoices; + + return schema; + }; + + render() { + const schema = this.getSchema(); + return ( + <> + + + + { + this.props.onChangeBlock(this.props.block, { + ...this.props.data, + [id]: value, + }); + }} + formData={this.props.data} + /> + + + ); + } +} + +export default compose( + connectToMultipleProviders((props) => { + const { + provider_url, + popup_map_provider_url, + popup_table_provider_url, + } = props.data; + const { max_count = 5 } = props.data; + const providers = [ + { + provider_url: provider_url, + }, + { provider_url: popup_map_provider_url }, + { provider_url: popup_table_provider_url }, + ]; + return { providers }; + }), +)(Edit); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupMap.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupMap.jsx new file mode 100644 index 0000000..b35db07 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupMap.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { compose } from 'redux'; +import { connectToProviderData } from '@eeacms/volto-datablocks/hocs'; + +import { Map, Marker } from 'pigeon-maps'; + +const getProviderDataLength = (provider_data) => { + return provider_data + ? provider_data[Object.keys(provider_data)[0]]?.length || 0 + : 0; +}; + +const PopupMap = ({ rowData, provider_data, mapData }) => { + const [mapCenter, setMapCenter] = React.useState([45, 9]); + + const [selectedData, setSelectedData] = React.useState([]); + + React.useEffect(() => { + const { long, lat } = mapData; + const allLong = + selectedData.length > 0 ? selectedData.map((i) => i[long]) : ''; + const allLat = + selectedData.length > 0 ? selectedData.map((i) => i[lat]) : ''; + const minLong = allLong && allLong.length > 0 ? Math.min(...allLong) : ''; + const maxLong = allLong && allLong.length > 0 ? Math.max(...allLong) : ''; + const minLat = allLong && allLong.length > 0 ? Math.min(...allLat) : ''; + const maxLat = allLong && allLong.length > 0 ? Math.max(...allLat) : ''; + + const centerLat = minLat && maxLat ? (minLat + maxLat) / 2 : ''; + const centerLong = minLong && maxLong ? (minLong + maxLong) / 2 : ''; + + if (centerLat && centerLong) { + setMapCenter([centerLat, centerLong]); + } + }, [selectedData, mapData]); + + React.useEffect(() => { + const provider_data_length = getProviderDataLength(provider_data); + const newMapData = []; + if (provider_data_length) { + const keys = Object.keys(provider_data); + Array(provider_data_length) + .fill() + .forEach((_, i) => { + const obj = {}; + keys.forEach((key) => { + obj[key] = provider_data[key][i]; + }); + newMapData.push(obj); + }); + } + setSelectedData(newMapData); + /* eslint-disable-next-line */ + }, [provider_data]); + + // const countries = + // provider_data && provider_data[mapData.country] + // ? provider_data[mapData.country] + // : ''; + + //const uniqueCountries = [...new Set(countries)]; + + if (!provider_data) { + return 'Loading..'; + } + return ( +
+ {selectedData.length > 0 ? ( + + {selectedData.map((item, i) => { + const long = item[mapData.long] ? item[mapData.long] : ''; + const lat = item[mapData.lat] ? item[mapData.lat] : ''; + /* const label = item[mapData.label] ? item[mapData.label] : ''; */ + return ; + })} + + ) : ( +

No data available for map.

+ )} +
+ ); +}; + +export default compose( + connectToProviderData((props) => { + return { + provider_url: props.providerUrl, + }; + }), +)(PopupMap); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupRow.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupRow.jsx new file mode 100644 index 0000000..c5d5be7 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupRow.jsx @@ -0,0 +1,253 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; + +import { Icon } from '@plone/volto/components'; +import expandSVG from '@plone/volto/icons/fullscreen.svg'; + +import { Button, Modal } from 'semantic-ui-react'; + +import ReadMore from './ReadMore'; +import PopupMap from './PopupMap'; +import PopupTable from './PopupTable'; + +import { + setConnectedDataParameters, + deleteConnectedDataParameters, +} from '@eeacms/volto-datablocks/actions'; + +const defaultSchema = { + title: '', + description: '', // + tableColumns: [], + url: '', + logo: '', + mapData: {}, +}; + +const ValidImage = ({ imageUrl }) => { + const [isValidImg, setIsValidImg] = React.useState(true); + + React.useEffect(() => { + setIsValidImg(true); + }, [imageUrl]); + + return imageUrl && isValidImg ? ( + {imageUrl} setIsValidImg(false)} + /> + ) : ( + + ); +}; + +const PopupRow = ({ + rowData, + tableData, + provider_data, + connected_data_parameters, + setConnectedDataParameters, + deleteConnectedDataParameters, +}) => { + const [expand, setExpand] = React.useState(false); + const [popupSchema, setPopupSchema] = React.useState(defaultSchema); + const type = tableData['@type']; + + const { + popup_table_provider_url, + popup_map_provider_url, + popup_data_query, + } = tableData; + + const queryVal = rowData[popup_data_query]; + + React.useEffect(() => { + if (expand) { + const { + popupTitle, + image_url, + popupDescription, + popupUrl, + popupTableColumns, + popupLong, + popupLat, + popupMapLabel, + popupCountryCode, + } = tableData; + + setPopupSchema({ + ...popupSchema, + title: rowData[popupTitle], + logo: rowData[image_url], + description: rowData[popupDescription], + url: rowData[popupUrl], + tableColumns: popupTableColumns, + mapData: { + long: popupLong, + lat: popupLat, + label: popupMapLabel, + country: popupCountryCode, + }, + }); + } else { + setPopupSchema(defaultSchema); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [expand, tableData, rowData]); + + const handleSetFilterProvider = (provider_url, query, value, type) => { + if (provider_url && popup_data_query) { + setConnectedDataParameters( + provider_url, + { + i: query, + o: 'plone.app.querystring.operation.selection.any', + v: [value], + }, + `${type}_${query}`, + ); + } + }; + + const handleRemoveFilterProvider = (provider_url, query, type) => { + if ( + popup_map_provider_url && + popup_table_provider_url && + popup_data_query + ) { + deleteConnectedDataParameters( + provider_url, + `${type}_${popup_data_query}`, + ); + } + }; + + const handleExpand = () => { + setExpand(true); + //this will filter the popup map & table data + if ( + popup_map_provider_url && + popup_table_provider_url && + popup_data_query + ) { + handleSetFilterProvider( + popup_map_provider_url, + popup_data_query, + queryVal, + type, + ); + handleSetFilterProvider( + popup_table_provider_url, + popup_data_query, + queryVal, + type, + ); + } + }; + + const handleClose = () => { + setExpand(false); + + //unfilter data on popup close + if ( + popup_map_provider_url && + popup_table_provider_url && + popup_data_query + ) { + handleRemoveFilterProvider( + popup_map_provider_url, + popup_data_query, + type, + ); + handleRemoveFilterProvider( + popup_table_provider_url, + popup_data_query, + type, + ); + } + }; + + return ( + handleClose()} + onOpen={() => handleExpand()} + open={expand} + trigger={ + + + + } + > + {popupSchema.title} + + + {popupSchema.description && ( +
+ +
+ )} + {popupSchema.logo && ( + {popupSchema.logo} setPopupSchema({ ...popupSchema, logo: '' })} // don't show it if it's not available + /> + )} +
+
+
+ {rowData && ( + + )} + + {popupSchema.url} + +
+
+ {rowData && ( + + )} +
+
+
+ + + + +
+ ); +}; + +export default compose( + connect( + (state) => { + return { + connected_data_parameters: state.connected_data_parameters, + }; + }, + { setConnectedDataParameters, deleteConnectedDataParameters }, + ), +)(PopupRow); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupTable.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupTable.jsx new file mode 100644 index 0000000..b9d972b --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/PopupTable.jsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { Table } from 'semantic-ui-react'; +import { compose } from 'redux'; +import { connectToProviderData } from '@eeacms/volto-datablocks/hocs'; +import RenderComponent from '@eeacms/volto-datablocks/components/manage/Blocks/SimpleDataTable/components'; + +const getProviderDataLength = (provider_data) => { + return provider_data + ? provider_data[Object.keys(provider_data)[0]]?.length || 0 + : 0; +}; + +const PopupTable = ({ rowData, providerUrl, provider_data, tableColumns }) => { + const [tableData, setTableData] = React.useState([]); + + React.useEffect(() => { + const provider_data_length = getProviderDataLength(provider_data); + const newTableData = []; + if (provider_data_length) { + const keys = Object.keys(provider_data); + Array(provider_data_length) + .fill() + .forEach((_, i) => { + const obj = {}; + keys.forEach((key) => { + obj[key] = provider_data[key][i]; + }); + newTableData.push(obj); + }); + } + setTableData(newTableData); + /* eslint-disable-next-line */ + }, [provider_data]); + + if (!provider_data) { + return 'Loading..'; + } + + return ( +
+ + + + {tableColumns && + tableColumns.length > 0 && + tableColumns.map((col) => ( + + {col.title ? col.title : col.column} + + ))} + + + + + {tableData && tableData.length > 0 ? ( + Array(tableData.length) + .fill() + .map((_, i) => { + return ( + + {tableColumns.map((col, j) => ( + + + + ))} + + ); + }) + ) : ( +

No data available for table.

+ )} +
+
+
+ ); +}; + +export default compose( + connectToProviderData((props) => { + return { + provider_url: props.providerUrl, + }; + }), +)(PopupTable); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/ReadMore.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/ReadMore.jsx new file mode 100644 index 0000000..46aae48 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/ReadMore.jsx @@ -0,0 +1,29 @@ +import React from 'react'; + +const truncate = (input, max) => + input.length > max ? `${input.substring(0, max)}..` : input; + +const ReadMore = ({ text, maxChars }) => { + const [displayText, setDisplayText] = React.useState(''); + const [more, setMore] = React.useState(false); + + const showAction = text.length > maxChars; + + React.useEffect(() => { + const truncated = truncate(text, maxChars); + setDisplayText(truncated); + }, [text, maxChars]); + + return ( + +

{more ? text : displayText}

+ {showAction && ( + + )} +
+ ); +}; + +export default ReadMore; diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/View.jsx b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/View.jsx new file mode 100644 index 0000000..3d77fd4 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/View.jsx @@ -0,0 +1,275 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; + +import _ from 'lodash'; +import qs from 'querystring'; +import { Icon } from '@plone/volto/components'; +import { Table, Pagination, Search } from 'semantic-ui-react'; +import RenderComponent from '@eeacms/volto-datablocks/components/manage/Blocks/SimpleDataTable/components'; + +import upSVG from '@eeacms/volto-datablocks/icons/up-arrow.svg'; +import downSVG from '@eeacms/volto-datablocks/icons/down-arrow.svg'; +import upDownSVG from '@eeacms/volto-datablocks/icons/up-down-arrow.svg'; + +import './style.less'; +import PopupRow from './PopupRow'; + +const getSortedTableData = (tableData, sortBy) => { + const property = sortBy[0]; + const ASC = sortBy[1]; + + return tableData.sort((a, b) => { + const a_value = a[property]; + const b_value = b[property]; + if (a_value < b_value) return ASC ? -1 : 1; + if (a_value > b_value) return ASC ? 1 : -1; + return 0; + }); +}; + +const getFilteredTableData = (tableData) => { + return tableData; +}; + +const View = (props) => { + const { + data = {}, + getAlignmentOfColumn, + getNameOfColumn, + getTitleOfColumn, + loadingProviderData, + placeholder, + provider_data_length, + provider_data, + row_size, + show_header, + } = props; + const selectedColumns = data.columns; + const timeoutRef = React.useRef(); + const search = React.useRef(); + const [mounted, setMounted] = React.useState(false); + const [tableData, setTableData] = React.useState([]); + const [filteredTableData, setFilteredTableData] = React.useState([]); + const [sortBy, setSortBy] = React.useState([]); + const [activePage, setActivePage] = React.useState(1); + const [value, setValue] = React.useState(props.query.searchTerm || ''); + const { loading, results } = props.search; + + const items = + results.length || (!loading && value.length) ? results : filteredTableData; + + const handleSearchChange = React.useCallback( + (e, data, time = 1000) => { + clearTimeout(timeoutRef.current); + props.dispatch({ type: 'TABLE_START_SEARCH', query: data.value }); + timeoutRef.current = setTimeout(() => { + props.history.push({ + search: + '?' + + qs.stringify({ + ...props.query, + searchTerm: data.value, + }), + }); + if (data.value.length === 0) { + props.dispatch({ type: 'TABLE_CLEAN_QUERY' }); + return; + } + const re = new RegExp(_.escapeRegExp(data.value), 'i'); + const isMatch = (result) => { + for (let colDef of selectedColumns) { + if (re.test(result[colDef.column])) { + return true; + } + } + return false; + }; + props.dispatch({ + type: 'TABLE_FINISH_SEARCH', + results: _.filter(filteredTableData, isMatch), + payload: { activePage: data.activePage, row_size }, + }); + }, time); + }, + /* eslint-disable-next-line */ + [filteredTableData, selectedColumns], + ); + + React.useEffect(() => { + const newTableData = []; + if (provider_data_length) { + const keys = Object.keys(provider_data); + Array(provider_data_length) + .fill() + .forEach((_, i) => { + const obj = {}; + keys.forEach((key) => { + obj[key] = provider_data[key][i]; + }); + newTableData.push(obj); + }); + } + setTableData(newTableData); + /* eslint-disable-next-line */ + }, [provider_data]); + + React.useEffect(() => { + const newFilteredTableData = [ + ...getSortedTableData(getFilteredTableData(tableData), sortBy), + ]; + setFilteredTableData(newFilteredTableData); + /* eslint-disable-next-line */ + }, [tableData, sortBy]); + + React.useEffect(() => { + if (mounted) { + handleSearchChange({}, { value, activePage }); + } else { + setMounted(true); + } + /* eslint-disable-next-line */ + }, [JSON.stringify(filteredTableData)]); + + React.useEffect(() => { + return () => { + clearTimeout(timeoutRef.current); + props.dispatch({ type: 'TABLE_CLEAN_QUERY' }); + }; + /* eslint-disable-next-line */ + }, []); + return ( +
+ {}} + showNoResults={false} + onSearchChange={(e, data) => { + handleSearchChange(e, { ...data, activePage: 1 }); + setValue(data.value); + if (activePage > 1) { + setActivePage(1); + } + }} + results={[]} + value={value} + resultRenderer={() => { + return ''; + }} + /> + {data.searchDescription ? ( +

{data.searchDescription}

+ ) : ( + '' + )} + + {show_header ? ( + + + + {selectedColumns.map((colDef, j) => ( + + + + ))} + + + ) : null} + + {items.map((_, i) => { + //const row_index = i + (activePage - 1) * row_size; + const row_data = items[i]; + return ( + + + + + {selectedColumns.map((colDef, j) => ( + + + + ))} + + ); + })} + {!items.length ? ( + + + {placeholder} + + + ) : ( + '' + )} + + {/* {Math.ceil(items.length / row_size) > 1 ? ( + + + + { + setActivePage(data.activePage); + handleSearchChange( + {}, + { value, activePage: data.activePage }, + ); + }} + /> + + + + ) : ( + '' + )} */} +
+
+ ); +}; + +export default compose( + // extra connectors + connect((state) => ({ + search: state.table_search || {}, + query: qs.parse(state.router.location?.search?.replace('?', '')) || {}, + })), +)(View); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/index.js b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/index.js new file mode 100644 index 0000000..7344ad8 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/index.js @@ -0,0 +1,6 @@ +import ExpandableView from './View'; +import ExpandableEdit from './Edit'; + +import expandableSchema from './schema'; + +export { ExpandableView, ExpandableEdit, expandableSchema }; diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/schema.js b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/schema.js new file mode 100644 index 0000000..27ad070 --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/schema.js @@ -0,0 +1,200 @@ +const columnSchema = { + title: 'Column', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: [ + 'column', + 'title', + 'component', + 'render_as', + 'textTemplate', + 'placeholder', + 'specifier', + 'textAlign', + ], + }, + ], + properties: { + title: { + title: 'Header', + }, + component: { + title: 'Component type', + choices: [ + ['text', 'Text'], + ['link', 'Link'], + ], + defaultValue: 'text', + }, + target: { + title: 'Target', + choices: [ + ['_blank', 'New window'], + ['_self', 'Same window'], + ], + }, + external: { + title: 'External link', + type: 'boolean', + }, + linkTemplate: { + title: 'Link template', + description: 'Add suffix/prefix to text. Use {} for value placeholder', + }, + render_as: { + title: 'HTML tag', + }, + specifier: { + title: 'Format specifier', + description: ( + <> + See{' '} + + D3 format documentation + + + ), + }, + textTemplate: { + title: 'Text template', + description: 'Add suffix/prefix to text. Use {} for value placeholder', + }, + placeholder: { + title: 'Placeholder', + }, + textAlign: { + title: 'Align', + widget: 'align', + type: 'string', + }, + column: { + title: 'Data column', + choices: [], + }, + column_link: { + title: 'Data column link', + choices: [], + }, + }, + required: ['column'], +}; + +const getColumnSchema = (schema, child) => { + return { + ...columnSchema, + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: [ + 'column', + ...(child?.component === 'link' ? ['column_link'] : []), + 'title', + 'component', + ...(child?.component === 'link' + ? ['target', 'external', 'linkTemplate'] + : []), + 'render_as', + 'textTemplate', + 'specifier', + 'textAlign', + ], + }, + ], + required: ['column', 'column_link'], + }; +}; + +export default () => ({ + title: 'Expandable datatable', + fieldsets: [ + { + id: 'default', + title: 'Default', + fields: [ + 'searchDescription', + 'popup_data_query', + 'image_url', + 'popupTitle', + 'popupDescription', + 'popupUrl', + 'popup_table_provider_url', + 'popupTableColumns', + 'popup_map_provider_url', + 'popupLong', + 'popupLat', + 'popupCountryCode', + 'popupMapLabel', + ], + }, + ], + properties: { + searchDescription: { + title: 'Search description', + widget: 'textarea', + }, + popup_table_provider_url: { + title: 'Popup Table provider', + widget: 'object_by_path', + }, + popup_map_provider_url: { + title: 'Popup Map provider', + widget: 'object_by_path', + }, + popup_data_query: { + title: 'Popup Data Query', + description: 'Select attribute by which to query popup data', + choices: [], + }, + image_url: { + title: 'Image Url', + description: 'Select Image URL param for the main table and popup', + choices: [], + }, + popupTitle: { + title: 'Popup Title', + choices: [], + }, + popupDescription: { + title: 'Popup description', + choices: [], + }, + popupUrl: { + title: 'Popup Url', + choices: [], + }, + popupTableColumns: { + title: 'Popup Table Columns', + description: 'Define popup table columns', + schema: columnSchema, + schemaExtender: (schema, child) => getColumnSchema(schema, child), + widget: 'object_list', + }, + popupLong: { + title: 'Popup Map Long', + description: 'Define popup map Long', + choices: [], + }, + popupLat: { + title: 'Popup Map Lat', + description: 'Define popup map Lat', + choices: [], + }, + popupCountryCode: { + title: 'Popup Country', + description: 'Define popup country code', + choices: [], + }, + popupMapLabel: { + title: 'Popup Map Label', + choices: [], + }, + }, + required: [], +}); diff --git a/src/components/manage/Blocks/SimpleDataTable/templates/expandable/style.less b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/style.less new file mode 100644 index 0000000..c0a894e --- /dev/null +++ b/src/components/manage/Blocks/SimpleDataTable/templates/expandable/style.less @@ -0,0 +1,159 @@ +.smart-table { + .ui.search .ui.icon.input { + width: 100%; + + > input { + padding-left: 1rem; + } + } + + .search-description { + padding: 0 1rem; + margin-top: 0.5rem; + } + + .sortable-th { + display: flex; + align-items: center; + justify-content: center; + border: none; + background: none; + cursor: pointer; + + > * { + font-size: 1rem; + line-height: 1rem; + } + } + + table { + overflow: hidden; + border: 1px solid #e8e8e8 !important; + background: #fff !important; + border-collapse: separate !important; + border-radius: 12px !important; + + thead > tr > th::after { + display: none; + } + + thead, + td { + background-color: #fff; + } + + th { + font-weight: bold !important; + + &:after { + padding: 0; + border: none; + } + } + + th, + td { + border-bottom: 1px solid #e8e8e8 !important; + } + + tbody { + td { + height: 46px; + } + + tr:last-child { + td { + border-bottom: none !important; + } + } + } + + tfoot { + tr th { + border-bottom: none !important; + } + } + + .expand-row-icon { + cursor: pointer; + } + + .expand-row-icon:hover { + fill: #058373 !important; + } + + .expand-row-img { + width: 120px; + max-width: 100% !important; + height: 60px; + padding: 5px; + cursor: pointer; + object-fit: contain; + } + + .expand-row-img:hover { + border-radius: 5px; + box-shadow: 0px 0px 13px -7px #564535; + } + } +} + +.readmore-container { + font-size: 15px; + + .readmore-text { + margin: 5px 0; + } + + .readmore-action { + border: 2px solid transparent; + margin: 0; + background-color: transparent; + color: #025e37; + cursor: pointer; + font-weight: bold; + } + + .readmore-action:hover { + border: 2px solid #1e8339; + border-radius: 5px; + color: #1e8339; + } +} + +.popup-header { + font-size: 1.5rem !important; +} + +.popup-url { + font-size: 24px; + font-weight: bold; + overflow-wrap: break-word; + word-wrap: break-word; +} + +.popup-table-container { + margin-bottom: 20px; +} + +.popup-logo { + width: 175px; + height: 175px; + margin-right: auto; + margin-left: auto; + object-fit: contain; +} + +.description-container { + width: 70%; +} + +.popup-table { + thead th { + background-color: #a7c942 !important; + } + + tr:nth-child(odd) td { + background-color: #eaf2d3; + } +} diff --git a/src/index.js b/src/index.js index 6100ebe..ef75854 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ import installAppExtras from '@eeacms/volto-forests-theme/components/theme/AppEx import { applyConfig as installFiseFrontend } from './localconfig'; import installDiscodataConnectorBlock from '@eeacms/volto-forests-theme/components/manage/Blocks/DiscodataConnectorBlock'; +import installExpandableDataTable from './components/manage/Blocks/SimpleDataTable'; import ObjectListInlineWidget from './components/manage/Widgets/ObjectListInlineWidget'; import reducers from '@eeacms/volto-forests-theme/reducers'; @@ -17,6 +18,7 @@ export default function applyConfig(config) { installAppExtras, installFiseFrontend, installDiscodataConnectorBlock, + installExpandableDataTable, ].reduce((acc, apply) => apply(acc), config); config.settings = {