diff --git a/package-lock.json b/package-lock.json index 0adda95eba..9b9eb97ba5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,9 +39,11 @@ "react-dnd": "10.0.2", "react-dnd-html5-backend": "10.0.2", "react-dom": "16.14.0", + "react-draggable": "4.4.6", "react-helmet": "6.1.0", "react-json-view": "1.21.3", "react-popper-tooltip": "4.4.2", + "react-resizable": "3.0.5", "react-router-dom": "6.4.1", "regenerator-runtime": "0.13.11" }, @@ -7732,6 +7734,15 @@ "mimic-response": "^1.0.0" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -20357,6 +20368,20 @@ "react": "^16.14.0" } }, + "node_modules/react-draggable": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", + "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "license": "MIT", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", @@ -20511,6 +20536,19 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, + "node_modules/react-resizable": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.5.tgz", + "integrity": "sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==", + "license": "MIT", + "dependencies": { + "prop-types": "15.x", + "react-draggable": "^4.0.3" + }, + "peerDependencies": { + "react": ">= 16.3" + } + }, "node_modules/react-router": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.1.tgz", diff --git a/package.json b/package.json index a432d38f66..7ba2c0b012 100644 --- a/package.json +++ b/package.json @@ -65,9 +65,11 @@ "react-dnd": "10.0.2", "react-dnd-html5-backend": "10.0.2", "react-dom": "16.14.0", + "react-draggable": "4.4.6", "react-helmet": "6.1.0", "react-json-view": "1.21.3", "react-popper-tooltip": "4.4.2", + "react-resizable": "3.0.5", "react-router-dom": "6.4.1", "regenerator-runtime": "0.13.11" }, diff --git a/src/components/AggregationPanel/AggregationPanel.js b/src/components/AggregationPanel/AggregationPanel.js new file mode 100644 index 0000000000..5cd5a0440b --- /dev/null +++ b/src/components/AggregationPanel/AggregationPanel.js @@ -0,0 +1,84 @@ +import LoaderDots from 'components/LoaderDots/LoaderDots.react'; +import React, { useEffect, useMemo } from 'react'; +import styles from './AggregationPanel.scss'; +import { + AudioElement, + ButtonElement, + ImageElement, + KeyValueElement, + TableElement, + TextElement, + VideoElement, +} from './AggregationPanelComponents'; + +const AggregationPanel = ({ + data, + isLoadingCloudFunction, + showAggregatedData, + setErrorAggregatedData, + errorAggregatedData, + showNote, + setSelectedObjectId, + selectedObjectId +}) => { + + useEffect(() => { + if (Object.keys(errorAggregatedData).length !== 0) { + setSelectedObjectId(null); + setErrorAggregatedData({}); + } + }, [errorAggregatedData, setSelectedObjectId, setErrorAggregatedData]); + + const isLoading = useMemo(() => + selectedObjectId && isLoadingCloudFunction && showAggregatedData, + [selectedObjectId, isLoadingCloudFunction, showAggregatedData] + ); + + const shouldShowAggregatedData = useMemo(() => + selectedObjectId && showAggregatedData && Object.keys(data).length !== 0 && Object.keys(errorAggregatedData).length === 0, [selectedObjectId, showAggregatedData, data, errorAggregatedData] + ); + + return ( + <> + {isLoading ? ( +
+ +
+ ) : shouldShowAggregatedData ? ( + data.panel.segments.map((segment, index) => ( +
+

{segment.title}

+
+ {segment.items.map((item, idx) => { + switch (item.type) { + case 'text': + return ; + case 'keyValue': + return ; + case 'table': + return ; + case 'image': + return ; + case 'video': + return ; + case 'audio': + return ; + case 'button': + return ; + default: + return null; + } + })} +
+
+ )) + ) : ( +
+ No object selected. +
+ )} + + ); +}; + +export default AggregationPanel; diff --git a/src/components/AggregationPanel/AggregationPanel.scss b/src/components/AggregationPanel/AggregationPanel.scss new file mode 100644 index 0000000000..2bfda14147 --- /dev/null +++ b/src/components/AggregationPanel/AggregationPanel.scss @@ -0,0 +1,99 @@ +@import 'stylesheets/globals.scss'; + +.heading { + font-size: 14px; + margin-top: 0; + padding: 8px; + padding-left: 10px; + background-color: $blue; + color: $white; +} + +.segmentItems { + font-size: 14px; + padding-left: 10px; + padding-right: 10px; + padding-top: 6px; + display: flex; + flex-direction: column; + border-left: 1px solid #e3e3ea; + gap: 10px; +} + +.keyValue { + font-size: 14px; + display: flex; + gap: 10px; +} + +.video { + width: 100%; + height: 100%; +} + +.image { + width: 100%; + height: 100%; +} + +.audio { + width: 100%; +} + +.segmentItems table, +.segmentItems th, +.segmentItems td { + font-size: 14px; + border: 1px solid #ddd; +} + +.segmentItems th, +.segmentItems td { + padding: 4px; + text-align: left; +} + +.buttonContainer { + display: flex; + justify-content: center; + align-items: center; +} + +.button { + width: auto; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; + margin-bottom: 15px; + background-color: $blue; + padding: 3px 13px; + border: none; + color: $white; + line-height: 28px; + outline: 0; + text-decoration: none; + text-align: center; + border-radius: 5px; + font-size: 14px; + &:hover, + &:focus { + background-color: $darkBlue; + } +} + +.loading{ + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; +} + +.center { + position: absolute; + text-align: center; + top: 50%; + left: 50%; + @include transform(translate(-50%, -50%)); + } \ No newline at end of file diff --git a/src/components/AggregationPanel/AggregationPanelComponents.js b/src/components/AggregationPanel/AggregationPanelComponents.js new file mode 100644 index 0000000000..2f876c17d0 --- /dev/null +++ b/src/components/AggregationPanel/AggregationPanelComponents.js @@ -0,0 +1,97 @@ +import React from 'react'; +import styles from './AggregationPanel.scss'; + +// Text Element Component +export const TextElement = ({ text }) => ( +
+

{text}

+
+); + +// Key-Value Element Component +export const KeyValueElement = ({ item }) => ( +
+ {item.key}: + {item.url ? {item.value} : {item.value}} +
+); + +// Table Element Component +export const TableElement = ({ columns, rows }) => ( +
+ + + + {columns.map((column, idx) => ( + + ))} + + + + {rows.map((row, idx) => ( + + {columns.map((column, colIdx) => ( + + ))} + + ))} + +
{column.name}
{row[column.name]}
+
+); + +// Image Element Component +export const ImageElement = ({ url }) => ( +
+ + Image + +
+); + +// Video Element Component +export const VideoElement = ({ url }) => ( +
+ +
+); + +// Audio Element Component +export const AudioElement = ({ url }) => ( +
+ +
+); + +// Button Element Component +export const ButtonElement = ({ item, showNote }) => { + const handleClick = () => { + fetch(item.action.url, { + method: item.action.method, + headers: item.action.headers, + body: JSON.stringify(item.action.body), + }) + .then(response => response.json()) + .then(data => { + const formattedData = JSON.stringify(data, null, 2); + showNote(`${formattedData}`,false) + }) + .catch(error => { + showNote(`${error}`,true) + }); + }; + + return ( +
+ +
+ ); +}; diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 52cccf903b..74614feb69 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -280,6 +280,7 @@ export default class BrowserCell extends Component { //#region Cell Context Menu related methods onContextMenu(event) { + this.props.setErrorAggregatedData({}); if (event.type !== 'contextmenu') { return; } @@ -289,6 +290,13 @@ export default class BrowserCell extends Component { onSelect({ row, col }); setCopyableValue(hidden ? undefined : this.copyableValue); + if (this.props.selectedObjectId !== this.props.objectId) { + this.props.setShowAggregatedData(true); + this.props.setSelectedObjectId(this.props.objectId); + if (this.props.isPanelVisible) { + this.props.callCloudFunction(this.props.objectId, this.props.className); + } + } const available = Filters.availableFilters( this.props.simplifiedSchema, @@ -535,7 +543,7 @@ export default class BrowserCell extends Component { field, constraint, compareTo, - class: className + class: className, }) ) ); @@ -556,6 +564,10 @@ export default class BrowserCell extends Component { current, onEditChange, setCopyableValue, + selectedObjectId, + setSelectedObjectId, + callCloudFunction, + isPanelVisible, onPointerCmdClick, row, col, @@ -565,6 +577,7 @@ export default class BrowserCell extends Component { markRequiredFieldRow, handleCellClick, selectedCells, + setShowAggregatedData } = this.props; const classes = [...this.state.classes]; @@ -628,6 +641,17 @@ export default class BrowserCell extends Component { onPointerCmdClick(value); } else { setCopyableValue(hidden ? undefined : this.copyableValue); + if (selectedObjectId !== this.props.objectId) { + setShowAggregatedData(true); + setSelectedObjectId(this.props.objectId); + if ( + this.props.objectId && + isPanelVisible && + ((e.shiftKey && !this.props.firstSelectedCell) || !e.shiftKey) + ) { + callCloudFunction(this.props.objectId, this.props.className); + } + } handleCellClick(e, row, col); } }} diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index c2054942b2..0e6f40a694 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -36,6 +36,10 @@ export default class BrowserRow extends Component { selection, selectRow, setCopyableValue, + selectedObjectId, + setSelectedObjectId, + callCloudFunction, + isPanelVisible, setCurrent, setEditing, setRelation, @@ -141,6 +145,10 @@ export default class BrowserRow extends Component { isRequired={isRequired} markRequiredFieldRow={markRequiredFieldRow} setCopyableValue={setCopyableValue} + selectedObjectId={selectedObjectId} + setSelectedObjectId={setSelectedObjectId} + isPanelVisible={isPanelVisible} + callCloudFunction={callCloudFunction} setContextMenu={setContextMenu} onEditSelectedRow={onEditSelectedRow} showNote={this.props.showNote} @@ -148,6 +156,9 @@ export default class BrowserRow extends Component { scripts={this.props.scripts} handleCellClick={this.props.handleCellClick} selectedCells={this.props.selectedCells} + setShowAggregatedData={this.props.setShowAggregatedData} + setErrorAggregatedData={this.props.setErrorAggregatedData} + firstSelectedCell={this.props.firstSelectedCell} /> ); })} diff --git a/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.react.js b/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.react.js index e714759376..1287e62ae5 100644 --- a/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.react.js +++ b/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.react.js @@ -24,6 +24,8 @@ export default class DataBrowserHeaderBar extends React.Component { preventSchemaEdits, selected, isDataLoaded, + setSelectedObjectId, + setCurrent } = this.props; const elements = [
@@ -48,7 +50,11 @@ export default class DataBrowserHeaderBar extends React.Component { !preventSort && (type === 'String' || type === 'Number' || type === 'Date' || type === 'Boolean') ) { - onClick = () => updateOrdering((order === 'descending' ? '' : '-') + name); + onClick = () =>{ + updateOrdering((order === 'descending' ? '' : '-') + name); + setSelectedObjectId(null); + setCurrent(null) + } } let className = styles.wrap; @@ -76,7 +82,9 @@ export default class DataBrowserHeaderBar extends React.Component { if (onAddColumn) { const finalStyle = {}; if (headers.length % 2) { - finalStyle.background = 'rgba(224,224,234,0.10)'; + finalStyle.background = '#726F85'; + } else{ + finalStyle.background = '#66637A'; } elements.push( diff --git a/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.scss b/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.scss index df45061d13..ba42c5694e 100644 --- a/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.scss +++ b/src/components/DataBrowserHeaderBar/DataBrowserHeaderBar.scss @@ -11,7 +11,7 @@ position: absolute; top: 0; left: 0; - height: 30px; + height: 0; background: #66637a; white-space: nowrap; display: inline-block; @@ -52,7 +52,7 @@ vertical-align: top; text-align: center; width: 30px; - background: rgba(224,224,234,0.10); + background: rgb(114, 111, 133) } .handle { diff --git a/src/components/Toolbar/Toolbar.react.js b/src/components/Toolbar/Toolbar.react.js index 5be4513fac..aad773e702 100644 --- a/src/components/Toolbar/Toolbar.react.js +++ b/src/components/Toolbar/Toolbar.react.js @@ -15,7 +15,7 @@ import { useNavigate, useNavigationType, NavigationType } from 'react-router-dom const POPOVER_CONTENT_ID = 'toolbarStatsPopover'; -const Stats = ({ data }) => { +const Stats = ({ data, classwiseCloudFunctions, className }) => { const [selected, setSelected] = React.useState(null); const [open, setOpen] = React.useState(false); const buttonRef = React.useRef(); @@ -98,10 +98,17 @@ const Stats = ({ data }) => { setSelected(statsOptions[0]); }, []); + const rightMarginStyle = classwiseCloudFunctions && classwiseCloudFunctions[className] ? '120px' : 'initial'; + return ( <> {selected ? ( - ) : null} @@ -133,8 +140,26 @@ const Toolbar = props => {
- {props?.selectedData?.length ? : null} + {props?.selectedData?.length ? : null}
{props.children}
+ {props.classwiseCloudFunctions && props.classwiseCloudFunctions[props.className] && ( + + )} ); }; diff --git a/src/components/Toolbar/Toolbar.scss b/src/components/Toolbar/Toolbar.scss index b5d41666e4..88f7261171 100644 --- a/src/components/Toolbar/Toolbar.scss +++ b/src/components/Toolbar/Toolbar.scss @@ -125,3 +125,31 @@ body:global(.expanded) { color: $blue; background-color: white; } + +.btn { + position: absolute; + right: 5px; + bottom: 5px; + border: none; + padding: 6px 3px; + width: 110px; + background: none; + color: white; + display: flex; + align-items: center; + gap: 5px; + cursor: pointer; + + svg { + + &:hover { + fill: #ffffff; + } + } + + &:hover { + svg { + fill: #ffffff; + } + } +} diff --git a/src/dashboard/Dashboard.js b/src/dashboard/Dashboard.js index e30a1824f8..0aebec1f25 100644 --- a/src/dashboard/Dashboard.js +++ b/src/dashboard/Dashboard.js @@ -52,7 +52,7 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import { Helmet } from 'react-helmet'; import Playground from './Data/Playground/Playground.react'; import DashboardSettings from './Settings/DashboardSettings/DashboardSettings.react'; -import Security from './Settings/Security/Security.react'; +import Security from './Settings/Security/Security.react'; const ShowSchemaOverview = false; //In progress features. Change false to true to work on this feature. @@ -121,6 +121,7 @@ export default class Dashboard extends React.Component { get('/parse-dashboard-config.json') .then(({ apps, newFeaturesInLatestVersion = [] }) => { this.setState({ newFeaturesInLatestVersion }); + const appInfoPromises = apps.map(app => { if (app.serverURL.startsWith('https://api.parse.com/1')) { //api.parse.com doesn't have feature availability endpoint, fortunately we know which features @@ -179,6 +180,7 @@ export default class Dashboard extends React.Component { configLoadingState: AsyncStatus.FAILED, }); }); + } render() { diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 188c6f46a6..9f97022618 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -40,6 +40,8 @@ import * as ClassPreferences from 'lib/ClassPreferences'; import { Helmet } from 'react-helmet'; import generatePath from 'lib/generatePath'; import { withRouter } from 'lib/withRouter'; +import { get } from 'lib/AJAX'; +import { setBasePath } from 'lib/AJAX'; // The initial and max amount of rows fetched by lazy loading const MAX_ROWS_FETCHED = 200; @@ -103,7 +105,12 @@ class Browser extends DashboardView { draggedRowSelection: false, classes: {}, - allClassesSchema: {} + allClassesSchema: {}, + configData: {}, + classwiseCloudFunctions: {}, + AggregationPanelData: {}, + isLoading: false, + errorAggregatedData: {}, }; this.addLocation = this.addLocation.bind(this); @@ -121,6 +128,8 @@ class Browser extends DashboardView { this.showExport = this.showExport.bind(this); this.login = this.login.bind(this); this.logout = this.logout.bind(this); + this.setLoading = this.setLoading.bind(this); + this.setErrorAggregatedData = this.setErrorAggregatedData.bind(this); this.toggleMasterKeyUsage = this.toggleMasterKeyUsage.bind(this); this.showAttachRowsDialog = this.showAttachRowsDialog.bind(this); this.cancelAttachRows = this.cancelAttachRows.bind(this); @@ -173,6 +182,9 @@ class Browser extends DashboardView { this.onMouseDownRowCheckBox = this.onMouseDownRowCheckBox.bind(this); this.onMouseUpRowCheckBox = this.onMouseUpRowCheckBox.bind(this); this.onMouseOverRowCheckBox = this.onMouseOverRowCheckBox.bind(this); + this.classAndCloudFuntionMap = this.classAndCloudFuntionMap.bind(this); + this.fetchAggregationPanelData = this.fetchAggregationPanelData.bind(this); + this.setAggregationPanelData = this.setAggregationPanelData.bind(this); this.dataBrowserRef = React.createRef(); @@ -200,6 +212,11 @@ class Browser extends DashboardView { componentDidMount() { this.addLocation(this.props.params.appId); window.addEventListener('mouseup', this.onMouseUpRowCheckBox); + setBasePath('/'); + get('/parse-dashboard-config.json').then(data => { + this.setState({ configData: data }); + this.classAndCloudFuntionMap(this.state.configData); + }); } componentWillUnmount() { @@ -229,11 +246,60 @@ class Browser extends DashboardView { if (!nextProps.params.className && nextProps.schema.data.get('classes')) { const t = nextProps.schema.data.get('classes'); this.classes = Object.keys(t.toObject()); - this.allClassesSchema = this.getAllClassesSchema(this.classes ,nextProps.schema.data.get('classes')); + this.allClassesSchema = this.getAllClassesSchema( + this.classes, + nextProps.schema.data.get('classes') + ); this.redirectToFirstClass(nextProps.schema.data.get('classes'), nextContext); } } + setLoading(bool) { + this.setState({ + isLoading: bool, + }); + } + + setErrorAggregatedData(data) { + this.setState({ + errorAggregatedData: data, + }); + } + + fetchAggregationPanelData(objectId, className) { + this.setState({ + isLoading: true, + }); + const params = { + objectId: objectId, + }; + const cloudCodeFunction = this.state.classwiseCloudFunctions[className][0].cloudCodeFunction; + + Parse.Cloud.run(cloudCodeFunction, params).then( + result => { + if (result && result.panel && result.panel && result.panel.segments) { + this.setState({ AggregationPanelData: result, isLoading: false }); + } else { + this.setState({ + isLoading: false, + errorAggregatedData: 'Improper JSON format', + }); + this.showNote(this.state.errorAggregatedData,true) + } + }, + error => { + this.setState({ + isLoading: false, + errorAggregatedData: error.message, + }); + this.showNote(this.state.errorAggregatedData,true) + } + ); + } + + setAggregationPanelData(data) { + this.setState({ AggregationPanelData: data }); + } addLocation(appId) { if (window.localStorage) { let pathname = null; @@ -259,6 +325,26 @@ class Browser extends DashboardView { } } + classAndCloudFuntionMap(data) { + const classMap = {}; + data.apps.forEach(app => { + app.infoPanel.forEach(panel => { + panel.classes.forEach(className => { + if (!classMap[className]) { + classMap[className] = []; + } + classMap[className].push({ + title: panel.title, + cloudCodeFunction: panel.cloudCodeFunction, + classes: panel.classes, + }); + }); + }); + }); + + this.setState({ classwiseCloudFunctions: classMap }); + } + removeLocation() { if (window.localStorage) { const lastLocation = { @@ -763,9 +849,9 @@ class Browser extends DashboardView { } } - getAllClassesSchema(allClasses , allClassesData) { + getAllClassesSchema(allClasses, allClassesData) { const schemaSimplifiedData = {}; - allClasses.forEach((className) => { + allClasses.forEach(className => { const classSchema = allClassesData.get(className); if (classSchema) { schemaSimplifiedData[className] = {}; @@ -1008,6 +1094,9 @@ class Browser extends DashboardView { { ordering: ordering, selection: {}, + errorAggregatedData: {}, + isLoading: false, + AggregationPanelData: {}, }, () => this.fetchData(source, this.state.filters) ); @@ -1277,7 +1366,7 @@ class Browser extends DashboardView { if (error.code === Parse.Error.AGGREGATE_ERROR) { if (error.errors.length == 1) { errorDeletingNote = - 'Error deleting ' + className + ' with id \'' + error.errors[0].object.id + '\''; + 'Error deleting ' + className + ' with id \'' + error.errors[0].object.id + '\''; } else if (error.errors.length < toDeleteObjectIds.length) { errorDeletingNote = 'Error deleting ' + @@ -1834,10 +1923,11 @@ class Browser extends DashboardView { } onMouseUpRowCheckBox() { - this.state.rowCheckboxDragging && this.setState({ - rowCheckboxDragging: false, - draggedRowSelection: false, - }); + this.state.rowCheckboxDragging && + this.setState({ + rowCheckboxDragging: false, + draggedRowSelection: false, + }); } onMouseOverRowCheckBox(id) { @@ -1968,6 +2058,14 @@ class Browser extends DashboardView { onMouseUpRowCheckBox={this.onMouseUpRowCheckBox} onMouseOverRowCheckBox={this.onMouseOverRowCheckBox} classes={this.classes} + classwiseCloudFunctions={this.state.classwiseCloudFunctions} + callCloudFunction={this.fetchAggregationPanelData} + isLoadingCloudFunction={this.state.isLoading} + setLoading={this.setLoading} + AggregationPanelData={this.state.AggregationPanelData} + setAggregationPanelData={this.setAggregationPanelData} + setErrorAggregatedData={this.setErrorAggregatedData} + errorAggregatedData={this.state.errorAggregatedData} /> ); } diff --git a/src/dashboard/Data/Browser/Browser.scss b/src/dashboard/Data/Browser/Browser.scss index ba6b92c463..97948cb9a0 100644 --- a/src/dashboard/Data/Browser/Browser.scss +++ b/src/dashboard/Data/Browser/Browser.scss @@ -82,9 +82,7 @@ body:global(.expanded) { top: 30px; bottom: 0; left: 0; - min-width: 100%; - overflow-y: auto; - overflow-x: hidden; + width: 100%; } .table .empty { @@ -264,4 +262,13 @@ body:global(.expanded) { position: relative; } } -} \ No newline at end of file +} + +.dataContainer { + height: 100%; + overflow: auto; +} + +.noScroll { + overflow-x: hidden; +} \ No newline at end of file diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 6bf22627fc..59dc53d5da 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -30,9 +30,14 @@ export default class BrowserTable extends React.Component { this.state = { offset: 0, + panelWidth: 300, + isResizing: false, + maxWidth: window.innerWidth - 300, }; this.handleScroll = this.handleScroll.bind(this); this.tableRef = React.createRef(); + this.handleResize = this.handleResize.bind(this); + this.updateMaxWidth = this.updateMaxWidth.bind(this); } componentWillReceiveProps(props) { @@ -56,10 +61,36 @@ export default class BrowserTable extends React.Component { componentDidMount() { this.tableRef.current.addEventListener('scroll', this.handleScroll); + window.addEventListener('resize', this.updateMaxWidth); } componentWillUnmount() { this.tableRef.current.removeEventListener('scroll', this.handleScroll); + window.removeEventListener('resize', this.updateMaxWidth); + } + + handleResize(event, { size }) { + this.setState({ panelWidth: size.width }); + } + + handleMouseDown() { + this.setState({ isResizing: true }); + document.body.style.cursor = 'ew-resize'; + } + + handleMouseMove(e) { + if (!this.state.isResizing) { + return; + } + this.setState({ panelWidth: e.clientX }); + } + + handleMouseUp() { + if (!this.state.isResizing) { + return; + } + this.setState({ isResizing: false }); + document.body.style.cursor = 'default'; } handleScroll() { @@ -92,6 +123,12 @@ export default class BrowserTable extends React.Component { } }); } + updateMaxWidth = () => { + this.setState({ maxWidth: window.innerWidth - 300 }); + if (this.state.panelWidth > window.innerWidth - 300) { + this.setState({ panelWidth: window.innerWidth - 300 }); + } + }; render() { let ordering = {}; @@ -163,6 +200,10 @@ export default class BrowserTable extends React.Component { setEditing={this.props.setEditing} setRelation={this.props.setRelation} setCopyableValue={this.props.setCopyableValue} + selectedObjectId={this.props.selectedObjectId} + setSelectedObjectId={this.props.setSelectedObjectId} + callCloudFunction={this.props.callCloudFunction} + isPanelVisible={this.props.isPanelVisible} setContextMenu={this.props.setContextMenu} onEditSelectedRow={this.props.onEditSelectedRow} markRequiredFieldRow={this.props.markRequiredFieldRow} @@ -174,6 +215,9 @@ export default class BrowserTable extends React.Component { onMouseDownRowCheckBox={this.props.onMouseDownRowCheckBox} onMouseUpRowCheckBox={this.props.onMouseUpRowCheckBox} onMouseOverRowCheckBox={this.props.onMouseOverRowCheckBox} + setShowAggregatedData={this.props.setShowAggregatedData} + setErrorAggregatedData={this.props.setErrorAggregatedData} + firstSelectedCell={this.props.firstSelectedCell} />