mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Merge branch 'feature/mobile-layout' into develop
This commit is contained in:
commit
d09e6eed73
12 changed files with 368 additions and 63 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState, memo } from 'react';
|
||||
|
||||
export const BoxDragPreview = memo(function BoxDragPreview({ item }) {
|
||||
export const BoxDragPreview = memo(function BoxDragPreview({ item, currentLayout }) {
|
||||
const [tickTock, setTickTock] = useState(false);
|
||||
|
||||
useEffect(
|
||||
|
|
@ -11,7 +11,8 @@ export const BoxDragPreview = memo(function BoxDragPreview({ item }) {
|
|||
[tickTock]
|
||||
);
|
||||
|
||||
let { width, height } = item;
|
||||
const layouts = item.layouts;
|
||||
let { width, height } = layouts ? item.layouts[currentLayout] : {};
|
||||
|
||||
if (item.id === undefined) {
|
||||
width = item.component.defaultSize.width;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ export const componentTypes = [
|
|||
actionButtonBackgroundColor: { type: 'color', displayName: 'Background color'},
|
||||
actionButtonTextColor: { type: 'color', displayName: 'Text color'}
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
defaultSize: {
|
||||
width: 810,
|
||||
height: 300
|
||||
|
|
@ -35,6 +39,10 @@ export const componentTypes = [
|
|||
searchText: ''
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
title: { value: 'Table' },
|
||||
visible: { value: true },
|
||||
|
|
@ -85,6 +93,10 @@ export const componentTypes = [
|
|||
width: 120,
|
||||
height: 30
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
text: { type: 'code', displayName: 'Button Text' },
|
||||
loadingState: { type: 'code', displayName: 'Loading State'}
|
||||
|
|
@ -98,6 +110,10 @@ export const componentTypes = [
|
|||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
text: { value: `Button` },
|
||||
visible: { value: true },
|
||||
|
|
@ -125,6 +141,10 @@ export const componentTypes = [
|
|||
width: 600,
|
||||
height: 400
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
title: { type: 'string', displayName: 'Title' },
|
||||
data: { type: 'json', displayName: 'Data' },
|
||||
|
|
@ -145,6 +165,10 @@ export const componentTypes = [
|
|||
show: null
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
title: { value: 'This title can be changed' },
|
||||
markerColor: { value: '#CDE1F8' },
|
||||
|
|
@ -174,6 +198,10 @@ export const componentTypes = [
|
|||
width: 600,
|
||||
height: 400
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
title: { type: 'string', displayName: 'Title' },
|
||||
size: { type: 'select', displayName: 'Modal size', options: [
|
||||
|
|
@ -190,6 +218,10 @@ export const componentTypes = [
|
|||
show: null
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
title: { value: 'This title can be changed' },
|
||||
size: { value: 'md' },
|
||||
|
|
@ -211,6 +243,10 @@ export const componentTypes = [
|
|||
width: 200,
|
||||
height: 30
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' }
|
||||
},
|
||||
|
|
@ -222,6 +258,10 @@ export const componentTypes = [
|
|||
value: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
placeholder: { value: 'Placeholder text' }
|
||||
},
|
||||
|
|
@ -242,6 +282,10 @@ export const componentTypes = [
|
|||
width: 150,
|
||||
height: 30
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
format: { type: 'code', displayName: 'Format' },
|
||||
enableTime: { type: 'code', displayName: 'Enable time selection?' },
|
||||
|
|
@ -255,6 +299,10 @@ export const componentTypes = [
|
|||
value: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
format: { value: 'DD/MM/YYYY' },
|
||||
enableTime: { value: '{{false}}' },
|
||||
|
|
@ -277,6 +325,10 @@ export const componentTypes = [
|
|||
width: 100,
|
||||
height: 60
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
label: { type: 'code', displayName: 'Label' }
|
||||
},
|
||||
|
|
@ -289,6 +341,10 @@ export const componentTypes = [
|
|||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
label: { value: 'Checkbox label' }
|
||||
},
|
||||
|
|
@ -318,6 +374,10 @@ export const componentTypes = [
|
|||
width: 250,
|
||||
height: 100
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
value: { type: 'code', displayName: 'Default value' },
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' }
|
||||
|
|
@ -330,6 +390,10 @@ export const componentTypes = [
|
|||
value: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
value: { value: '' },
|
||||
placeholder: { value: 'Placeholder text' }
|
||||
|
|
@ -351,6 +415,10 @@ export const componentTypes = [
|
|||
width: 300,
|
||||
height: 32
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
format: { type: 'code', displayName: 'Format' }
|
||||
},
|
||||
|
|
@ -363,6 +431,10 @@ export const componentTypes = [
|
|||
startDate: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
format: { value: 'DD/MM/YYYY' }
|
||||
},
|
||||
|
|
@ -379,6 +451,10 @@ export const componentTypes = [
|
|||
displayName: 'Text',
|
||||
description: 'Display markdown or HTML',
|
||||
component: 'Text',
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
text: { type: 'code', displayName: 'Text' },
|
||||
loadingState: { type: 'code', displayName: 'Show loading state' }
|
||||
|
|
@ -395,6 +471,10 @@ export const componentTypes = [
|
|||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
text: { value: 'Text goes here !' },
|
||||
visible: { value: true },
|
||||
|
|
@ -417,6 +497,10 @@ export const componentTypes = [
|
|||
height: 200
|
||||
},
|
||||
component: 'Image',
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
source: { type: 'code', displayName: 'URL' },
|
||||
},
|
||||
|
|
@ -428,6 +512,10 @@ export const componentTypes = [
|
|||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
source: { value: 'https://www.svgrepo.com/show/34217/image.svg' },
|
||||
visible: { value: true }
|
||||
|
|
@ -449,6 +537,10 @@ export const componentTypes = [
|
|||
height: 200
|
||||
},
|
||||
component: 'Container',
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
},
|
||||
events: {},
|
||||
|
|
@ -457,6 +549,10 @@ export const componentTypes = [
|
|||
},
|
||||
exposedVariables: {},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
visible: { value: true }
|
||||
},
|
||||
|
|
@ -475,6 +571,10 @@ export const componentTypes = [
|
|||
height: 30
|
||||
},
|
||||
component: 'DropDown',
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
label: { type: 'code', displayName: 'Label' },
|
||||
value: { type: 'code', displayName: 'Default value' },
|
||||
|
|
@ -491,6 +591,10 @@ export const componentTypes = [
|
|||
value: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
label: { value: 'Select' },
|
||||
value: { value: '' },
|
||||
|
|
@ -515,6 +619,10 @@ export const componentTypes = [
|
|||
height: 60
|
||||
},
|
||||
component: 'Multiselect',
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
label: { type: 'code', displayName: 'Label' },
|
||||
value: { type: 'code', displayName: 'Default value' },
|
||||
|
|
@ -531,6 +639,10 @@ export const componentTypes = [
|
|||
values: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
label: { value: 'Select' },
|
||||
values: { value: '[]' },
|
||||
|
|
@ -555,6 +667,10 @@ export const componentTypes = [
|
|||
width: 600,
|
||||
height: 210
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
placeholder: { type: 'code', displayName: 'Placeholder' }
|
||||
},
|
||||
|
|
@ -566,6 +682,10 @@ export const componentTypes = [
|
|||
value: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
placeholder: { value: 'Placeholder text' }
|
||||
},
|
||||
|
|
@ -586,6 +706,10 @@ export const componentTypes = [
|
|||
width: 400,
|
||||
height: 400
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop? '},
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile?'},
|
||||
},
|
||||
properties: {
|
||||
initialLocation: { type: 'code', displayName: 'Initial location', tip: 'This location will be the initial center of the map', options: {
|
||||
mode: 'javascript',
|
||||
|
|
@ -612,6 +736,10 @@ export const componentTypes = [
|
|||
center: {}
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value : false }
|
||||
},
|
||||
properties: {
|
||||
initialLocation: { value: `{
|
||||
"lat": 40.7128,
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ import update from 'immutability-helper';
|
|||
import { componentTypes } from './Components/components';
|
||||
import { computeComponentName } from '@/_helpers/utils';
|
||||
|
||||
const styles = {
|
||||
width: 1292,
|
||||
height: 2400,
|
||||
position: 'absolute'
|
||||
};
|
||||
|
||||
|
||||
function uuidv4() {
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
|
||||
|
|
@ -30,8 +26,18 @@ export const Container = ({
|
|||
appLoading,
|
||||
configHandleClicked,
|
||||
zoomLevel,
|
||||
removeComponent
|
||||
currentLayout,
|
||||
removeComponent,
|
||||
deviceWindowWidth,
|
||||
scaleValue
|
||||
}) => {
|
||||
|
||||
const styles = {
|
||||
width: currentLayout === 'mobile' ? deviceWindowWidth : 1292,
|
||||
height: 2400,
|
||||
position: 'absolute'
|
||||
};
|
||||
|
||||
const components = appDefinition.components || [];
|
||||
|
||||
const [boxes, setBoxes] = useState(components);
|
||||
|
|
@ -43,11 +49,11 @@ export const Container = ({
|
|||
}, [components]);
|
||||
|
||||
const moveBox = useCallback(
|
||||
(id, left, top) => {
|
||||
(id, layouts) => {
|
||||
setBoxes(
|
||||
update(boxes, {
|
||||
[id]: {
|
||||
$merge: { left, top }
|
||||
$merge: { layouts }
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
@ -86,6 +92,9 @@ export const Container = ({
|
|||
return;
|
||||
}
|
||||
|
||||
let layouts = item['layouts'];
|
||||
const currentLayoutOptions = layouts ? layouts[item.currentLayout] : {};
|
||||
|
||||
let componentData = {};
|
||||
let componentMeta = {};
|
||||
let id = item.id;
|
||||
|
|
@ -107,8 +116,30 @@ export const Container = ({
|
|||
}
|
||||
|
||||
componentData = item.component;
|
||||
left = Math.round(item.left + deltaX);
|
||||
top = Math.round(item.top + deltaY);
|
||||
left = Math.round(currentLayoutOptions.left + deltaX);
|
||||
top = Math.round(currentLayoutOptions.top + deltaY);
|
||||
|
||||
if (snapToGrid) {
|
||||
[left, top] = doSnapToGrid(left, top);
|
||||
}
|
||||
|
||||
let newBoxes = {
|
||||
...boxes,
|
||||
[id]: {
|
||||
...boxes[id],
|
||||
layouts: {
|
||||
...boxes[id]['layouts'],
|
||||
[item.currentLayout]: {
|
||||
...boxes[id]['layouts'][item.currentLayout],
|
||||
top: top,
|
||||
left: left,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setBoxes(newBoxes);
|
||||
|
||||
} else {
|
||||
// This is a new component
|
||||
componentMeta = componentTypes.find((component) => component.component === item.component.component);
|
||||
|
|
@ -124,22 +155,31 @@ export const Container = ({
|
|||
top = Math.round(currentOffset.y + (currentOffset.y * (1 - zoomLevel)) - offsetFromTopOfWindow);
|
||||
|
||||
id = uuidv4();
|
||||
}
|
||||
|
||||
if (snapToGrid) {
|
||||
[left, top] = doSnapToGrid(left, top);
|
||||
}
|
||||
|
||||
setBoxes({
|
||||
...boxes,
|
||||
[id]: {
|
||||
top: top,
|
||||
left: left,
|
||||
width: item.width > 0 ? item.width : componentMeta.defaultSize.width,
|
||||
height: item.height > 0 ? item.height : componentMeta.defaultSize.height,
|
||||
component: componentData
|
||||
if (snapToGrid) {
|
||||
[left, top] = doSnapToGrid(left, top);
|
||||
}
|
||||
});
|
||||
|
||||
if(item.currentLayout === 'mobile') {
|
||||
componentData.definition.others.showOnDesktop.value = false;
|
||||
componentData.definition.others.showOnMobile.value = true;
|
||||
}
|
||||
|
||||
setBoxes({
|
||||
...boxes,
|
||||
[id]: {
|
||||
component: componentData,
|
||||
layouts: {
|
||||
[item.currentLayout]: {
|
||||
top: top,
|
||||
left: left,
|
||||
width: componentMeta.defaultSize.width,
|
||||
height: componentMeta.defaultSize.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
|
@ -153,21 +193,36 @@ export const Container = ({
|
|||
|
||||
let { x, y } = position;
|
||||
|
||||
let { left, top, width, height } = boxes[id];
|
||||
const defaultData = {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: 445,
|
||||
height: 500
|
||||
};
|
||||
|
||||
let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData;
|
||||
|
||||
top = y;
|
||||
left = x;
|
||||
|
||||
width = width + deltaWidth;
|
||||
height = height + deltaHeight
|
||||
height = height + deltaHeight;
|
||||
|
||||
setBoxes(
|
||||
update(boxes, {
|
||||
[id]: {
|
||||
$merge: { width, height, top, left }
|
||||
let newBoxes = {
|
||||
...boxes,
|
||||
[id]: {
|
||||
...boxes[id],
|
||||
layouts: {
|
||||
...boxes[id]['layouts'],
|
||||
[currentLayout]: {
|
||||
...boxes[id]['layouts'][currentLayout],
|
||||
width, height, top, left
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
setBoxes(newBoxes);
|
||||
}
|
||||
|
||||
function paramUpdated(id, param, value) {
|
||||
|
|
@ -196,7 +251,11 @@ export const Container = ({
|
|||
return (
|
||||
<div ref={drop} style={styles} className={`real-canvas ${isDragging || isResizing ? ' show-grid' : ''}`}>
|
||||
{Object.keys(boxes).map((key) => {
|
||||
if(!boxes[key].parent) {
|
||||
|
||||
const box = boxes[key];
|
||||
const canShowInCurrentLayout = box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value;
|
||||
|
||||
if(!box.parent && canShowInCurrentLayout) {
|
||||
return <DraggableBox
|
||||
onComponentClick={onComponentClick}
|
||||
onEvent={onEvent}
|
||||
|
|
@ -214,6 +273,8 @@ export const Container = ({
|
|||
zoomLevel={zoomLevel}
|
||||
configHandleClicked={configHandleClicked}
|
||||
removeComponent={removeComponent}
|
||||
currentLayout={currentLayout}
|
||||
scaleValue={scaleValue}
|
||||
containerProps={{
|
||||
mode,
|
||||
snapToGrid,
|
||||
|
|
@ -227,7 +288,9 @@ export const Container = ({
|
|||
appLoading,
|
||||
zoomLevel,
|
||||
configHandleClicked,
|
||||
removeComponent
|
||||
removeComponent,
|
||||
currentLayout,
|
||||
scaleValue
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const layerStyles = {
|
|||
height: '100%'
|
||||
};
|
||||
|
||||
function getItemStyles(delta, item, initialOffset, currentOffset) {
|
||||
function getItemStyles(delta, item, initialOffset, currentOffset, currentLayout) {
|
||||
if (!initialOffset || !currentOffset) {
|
||||
return {
|
||||
display: 'none'
|
||||
|
|
@ -29,8 +29,10 @@ function getItemStyles(delta, item, initialOffset, currentOffset) {
|
|||
const realCanvasDelta = realCanvasBoundingRect.x - canvasContainerBoundingRect.x;
|
||||
|
||||
if (id) { // Dragging within the canvas
|
||||
x = Math.round(item.left + delta.x);
|
||||
y = Math.round(item.top + delta.y);
|
||||
|
||||
x = Math.round(item.layouts[currentLayout].left + delta.x);
|
||||
y = Math.round(item.layouts[currentLayout].top + delta.y);
|
||||
|
||||
} else { // New component being dragged from components sidebar
|
||||
const offsetFromTopOfWindow = realCanvasBoundingRect.top;
|
||||
const offsetFromLeftOfWindow = realCanvasBoundingRect.left;
|
||||
|
|
@ -50,7 +52,7 @@ function getItemStyles(delta, item, initialOffset, currentOffset) {
|
|||
WebkitTransform: transform
|
||||
};
|
||||
}
|
||||
export const CustomDragLayer = () => {
|
||||
export const CustomDragLayer = ({ currentLayout }) => {
|
||||
const {
|
||||
itemType, isDragging, item, initialOffset, currentOffset, delta
|
||||
} = useDragLayer((monitor) => ({
|
||||
|
|
@ -64,7 +66,7 @@ export const CustomDragLayer = () => {
|
|||
function renderItem() {
|
||||
switch (itemType) {
|
||||
case ItemTypes.BOX:
|
||||
return <BoxDragPreview item={item} />;
|
||||
return <BoxDragPreview item={item} currentLayout={currentLayout}/>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
@ -76,7 +78,7 @@ export const CustomDragLayer = () => {
|
|||
|
||||
return (
|
||||
<div style={layerStyles}>
|
||||
<div style={getItemStyles(delta, item, initialOffset, currentOffset)}>
|
||||
<div style={getItemStyles(delta, item, initialOffset, currentOffset, currentLayout)}>
|
||||
{renderItem()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@ export const DraggableBox = function DraggableBox({
|
|||
zoomLevel,
|
||||
containerProps,
|
||||
configHandleClicked,
|
||||
removeComponent
|
||||
removeComponent,
|
||||
currentLayout,
|
||||
layouts,
|
||||
scaleValue
|
||||
}) {
|
||||
const [isResizing, setResizing] = useState(false);
|
||||
const [canDrag, setCanDrag] = useState(true);
|
||||
|
|
@ -76,13 +79,13 @@ export const DraggableBox = function DraggableBox({
|
|||
() => ({
|
||||
type: ItemTypes.BOX,
|
||||
item: {
|
||||
id, left, top, width, height, title, component, zoomLevel, parent
|
||||
id, title, component, zoomLevel, parent, layouts, currentLayout
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragging: monitor.isDragging()
|
||||
})
|
||||
}),
|
||||
[id, left, top, height, width, title, component, index, zoomLevel, parent]
|
||||
[id, title, component, index, zoomLevel, parent, layouts, currentLayout]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -114,6 +117,21 @@ export const DraggableBox = function DraggableBox({
|
|||
setCanDrag(newState);
|
||||
}
|
||||
|
||||
const defaultData = {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: 445,
|
||||
height: 500
|
||||
}
|
||||
|
||||
const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
|
||||
const [currentLayoutOptions, setCurrentLayoutOptions] = useState(layoutData);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(layoutData)
|
||||
setCurrentLayoutOptions(layoutData);
|
||||
}, [layoutData.height, layoutData.width, layoutData.left, layoutData.top]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{inCanvas ? (
|
||||
|
|
@ -126,8 +144,8 @@ export const DraggableBox = function DraggableBox({
|
|||
|
||||
<Rnd
|
||||
style={{ ...style }}
|
||||
size={{ width: width + 6, height: height + 6 }}
|
||||
position={{ x: left, y: top }}
|
||||
size={{ width: currentLayoutOptions.width * scaleValue + 6, height: currentLayoutOptions.height + 6}}
|
||||
position={{ x: currentLayoutOptions ? currentLayoutOptions.left : 0, y: currentLayoutOptions ? currentLayoutOptions.top : 0 }}
|
||||
defaultSize={{}}
|
||||
className={`resizer ${mouseOver ? 'resizer-active' : ''}`}
|
||||
onResize={() => setResizing(true)}
|
||||
|
|
@ -153,9 +171,9 @@ export const DraggableBox = function DraggableBox({
|
|||
<Box
|
||||
component={component}
|
||||
id={id}
|
||||
width={width}
|
||||
width={currentLayoutOptions.width * scaleValue}
|
||||
height={currentLayoutOptions.height}
|
||||
mode={mode}
|
||||
height={height}
|
||||
changeCanDrag={changeCanDrag}
|
||||
inCanvas={inCanvas}
|
||||
paramUpdated={paramUpdated}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ class Editor extends React.Component {
|
|||
showQueryEditor: true,
|
||||
showLeftSidebar: true,
|
||||
zoomLevel: 1.0,
|
||||
currentLayout: 'desktop',
|
||||
scaleValue: 1,
|
||||
deviceWindowWidth: 450,
|
||||
appDefinition: {
|
||||
components: null
|
||||
},
|
||||
|
|
@ -249,7 +252,8 @@ class Editor extends React.Component {
|
|||
...this.state.appDefinition.components,
|
||||
[newDefinition.id]: {
|
||||
...this.state.appDefinition.components[newDefinition.id],
|
||||
component: newDefinition.component
|
||||
component: newDefinition.component,
|
||||
layouts: newDefinition.layouts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -393,8 +397,9 @@ class Editor extends React.Component {
|
|||
currentState,
|
||||
isLoading,
|
||||
zoomLevel,
|
||||
previewLoading,
|
||||
queryPreviewData
|
||||
currentLayout,
|
||||
deviceWindowWidth,
|
||||
scaleValue
|
||||
} = this.state;
|
||||
|
||||
const appLink = `/applications/${appId}`;
|
||||
|
|
@ -489,6 +494,27 @@ class Editor extends React.Component {
|
|||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="layout-buttons">
|
||||
<div class="btn-group" role="group" aria-label="Basic example">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-light"
|
||||
onClick={() => this.setState({ currentLayout: 'desktop' })}
|
||||
disabled={currentLayout === 'desktop'}
|
||||
>
|
||||
<img src="https://www.svgrepo.com/show/108320/desktop.svg" width="12" height="12" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-light"
|
||||
onClick={() => this.setState({ currentLayout: 'mobile' })}
|
||||
disabled={currentLayout === 'mobile'}
|
||||
>
|
||||
<img src="https://www.svgrepo.com/show/80383/touch-screen-mobile-device.svg" width="12" height="12" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="navbar-nav flex-row order-md-last">
|
||||
<div className="nav-item dropdown d-none d-md-flex me-3">
|
||||
{app
|
||||
|
|
@ -631,13 +657,16 @@ class Editor extends React.Component {
|
|||
</Resizable>
|
||||
<div className="main">
|
||||
<div className="canvas-container align-items-center" style={{ transform: `scale(${zoomLevel})` }}>
|
||||
<div className="canvas-area">
|
||||
<div className="canvas-area" style={{width: currentLayout === 'desktop' ? '1292px' : '450px'}}>
|
||||
<Container
|
||||
appDefinition={appDefinition}
|
||||
appDefinitionChanged={this.appDefinitionChanged}
|
||||
snapToGrid={true}
|
||||
mode={'edit'}
|
||||
zoomLevel={zoomLevel}
|
||||
currentLayout={currentLayout}
|
||||
deviceWindowWidth={deviceWindowWidth}
|
||||
scaleValue={scaleValue}
|
||||
appLoading={isLoading}
|
||||
onEvent={(eventName, options) => onEvent(this, eventName, options)}
|
||||
onComponentOptionChanged={(component, optionName, value) => onComponentOptionChanged(this, component, optionName, value)
|
||||
|
|
@ -653,7 +682,10 @@ class Editor extends React.Component {
|
|||
onComponentClick(this, id, component);
|
||||
}}
|
||||
/>
|
||||
<CustomDragLayer snapToGrid={true} />
|
||||
<CustomDragLayer
|
||||
snapToGrid={true}
|
||||
currentLayout={currentLayout}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -795,6 +827,7 @@ class Editor extends React.Component {
|
|||
<WidgetManager
|
||||
componentTypes={componentTypes}
|
||||
zoomLevel={zoomLevel}
|
||||
currentLayout={currentLayout}
|
||||
></WidgetManager>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ class Table extends React.Component {
|
|||
const actions = component.component.definition.properties.actions || { value: [] };
|
||||
|
||||
return (
|
||||
<div className="properties-container p-2 mb-5 pb-3" key={this.props.component.id}>
|
||||
<div className="properties-container p-2 " key={this.props.component.id}>
|
||||
{renderElement(component, componentMeta, paramUpdated, dataQueries, 'data', 'properties', currentState, components)}
|
||||
|
||||
<div className="field mb-2 mt-3">
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const Inspector = ({
|
|||
currentState
|
||||
}) => {
|
||||
|
||||
const selectedComponent = { id: selectedComponentId, component: allComponents[selectedComponentId].component }
|
||||
const selectedComponent = { id: selectedComponentId, component: allComponents[selectedComponentId].component, layouts: allComponents[selectedComponentId].layouts}
|
||||
const [component, setComponent] = useState(selectedComponent);
|
||||
|
||||
const [components, setComponents] = useState(allComponents);
|
||||
|
|
@ -68,6 +68,32 @@ export const Inspector = ({
|
|||
componentDefinitionChanged(newComponent);
|
||||
}
|
||||
|
||||
function layoutPropertyChanged(param, attr, value, paramType) {
|
||||
paramUpdated(param, attr, value, paramType);
|
||||
|
||||
// User wants to show the widget on mobile devices
|
||||
if(param.name === 'showOnMobile' && value === true) {
|
||||
let newComponent = {
|
||||
...component
|
||||
};
|
||||
|
||||
const { width, height } = newComponent.layouts['desktop'];
|
||||
|
||||
newComponent['layouts'] = {
|
||||
...newComponent.layouts,
|
||||
mobile: {
|
||||
top: 100,
|
||||
left: 0,
|
||||
width: Math.min(width, 445),
|
||||
height: height
|
||||
}
|
||||
}
|
||||
|
||||
setComponent(newComponent);
|
||||
componentDefinitionChanged(newComponent);
|
||||
}
|
||||
}
|
||||
|
||||
function eventUpdated(event, actionId) {
|
||||
let newDefinition = { ...component.component.definition };
|
||||
newDefinition.events[event.name] = { actionId };
|
||||
|
|
@ -170,14 +196,25 @@ export const Inspector = ({
|
|||
}
|
||||
|
||||
{!['Table', 'Chart'].includes(componentMeta.component) &&
|
||||
<div className="properties-container p-2 mb-5 pb-3">
|
||||
<div className="properties-container p-2">
|
||||
{Object.keys(componentMeta.properties).map((property) => renderElement(component, componentMeta, paramUpdated, dataQueries, property, 'properties', currentState, components))}
|
||||
<div class="hr-text">Style</div>
|
||||
{Object.keys(componentMeta.styles).map((style) => renderElement(component, componentMeta, paramUpdated, dataQueries, style, 'styles', currentState, components))}
|
||||
<div class="hr-text">Events</div>
|
||||
{Object.keys(componentMeta.events).map((eventName) => renderEvent(component, eventUpdated, dataQueries, eventOptionUpdated, eventName, componentMeta.events[eventName], currentState, components))}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
{/* Show on desktop & show on mobile params */}
|
||||
<div class="hr-text">Layout</div>
|
||||
<div className="properties-container p-2 pb-3 mb-5">
|
||||
{renderElement(component, componentMeta, layoutPropertyChanged, dataQueries, 'showOnDesktop', 'others', currentState, components)}
|
||||
{renderElement(component, componentMeta, layoutPropertyChanged, dataQueries, 'showOnMobile', 'others', currentState, components)}
|
||||
</div>
|
||||
|
||||
<div className="widget-documentation-link p-2">
|
||||
<a href={`https://docs.tooljet.io/docs/widgets/${componentMeta.name.toLowerCase()}`} target="_blank">
|
||||
<small>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,17 @@ class Viewer extends React.Component {
|
|||
componentDidMount() {
|
||||
const id = this.props.match.params.id;
|
||||
|
||||
this.setState({isLoading: true});
|
||||
const deviceWindowWidth = window.screen.width;
|
||||
const isMobileDevice = deviceWindowWidth < 600;
|
||||
|
||||
let scaleValue = isMobileDevice ? deviceWindowWidth / 450 : 1;
|
||||
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
deviceWindowWidth,
|
||||
scaleValue,
|
||||
currentLayout: isMobileDevice ? 'mobile' : 'desktop'
|
||||
});
|
||||
|
||||
appService.getApp(id).then((data) => this.setState(
|
||||
{
|
||||
|
|
@ -83,7 +93,15 @@ class Viewer extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { appDefinition, showQueryConfirmation, currentState, isLoading } = this.state;
|
||||
const {
|
||||
appDefinition,
|
||||
showQueryConfirmation,
|
||||
currentState,
|
||||
isLoading,
|
||||
currentLayout,
|
||||
deviceWindowWidth,
|
||||
scaleValue
|
||||
} = this.state;
|
||||
|
||||
console.log('currentState', currentState);
|
||||
|
||||
|
|
@ -116,7 +134,7 @@ class Viewer extends React.Component {
|
|||
<div className="sub-section">
|
||||
<div className="main">
|
||||
<div className="canvas-container align-items-center">
|
||||
<div className="canvas-area">
|
||||
<div className="canvas-area" style={{width: currentLayout === 'desktop' ? '1292px' : `${deviceWindowWidth}px`}}>
|
||||
<Container
|
||||
appDefinition={appDefinition}
|
||||
appDefinitionChanged={() => false} // function not relevant in viewer
|
||||
|
|
@ -124,6 +142,9 @@ class Viewer extends React.Component {
|
|||
appLoading={isLoading}
|
||||
onEvent={(eventName, options) => onEvent(this, eventName, options)}
|
||||
mode="view"
|
||||
scaleValue={scaleValue}
|
||||
deviceWindowWidth={deviceWindowWidth}
|
||||
currentLayout={currentLayout}
|
||||
currentState={this.state.currentState}
|
||||
onComponentClick={(id, component) => onComponentClick(this, id, component)}
|
||||
onComponentOptionChanged={(component, optionName, value) => onComponentOptionChanged(this, component, optionName, value)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import { result } from 'lodash';
|
|||
|
||||
export const WidgetManager = function WidgetManager({
|
||||
componentTypes,
|
||||
zoomLevel
|
||||
zoomLevel,
|
||||
currentLayout
|
||||
}) {
|
||||
|
||||
const [filteredComponents, setFilteredComponents] = useState(componentTypes);
|
||||
|
|
@ -21,7 +22,7 @@ export const WidgetManager = function WidgetManager({
|
|||
}
|
||||
|
||||
function renderComponentCard(component, index) {
|
||||
return <DraggableBox key={index} index={index} component={component} zoomLevel={zoomLevel}/>;
|
||||
return <DraggableBox key={index} index={index} component={component} zoomLevel={zoomLevel} currentLayout={currentLayout} />;
|
||||
};
|
||||
|
||||
return <div className="components-container m-2">
|
||||
|
|
|
|||
|
|
@ -270,7 +270,6 @@ body {
|
|||
}
|
||||
|
||||
.canvas-area {
|
||||
width: 1292px;
|
||||
min-height: 2400px;
|
||||
background: #edeff5;
|
||||
margin: 0px auto;
|
||||
|
|
@ -424,6 +423,7 @@ body {
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
-webkit-box-pack: center;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ToolJet - Dashboard</title>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue