Merge branch 'release' of gitlab.com:theappsmith/internal-tools-client into fix/first-page-load-performance
This commit is contained in:
commit
ac4911d896
|
|
@ -73,6 +73,7 @@
|
|||
"react-dnd-html5-backend": "^9.3.4",
|
||||
"react-dnd-touch-backend": "^9.4.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-google-maps": "^9.4.5",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-json-view": "^1.19.1",
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ export type WidgetAddChild = {
|
|||
parentRowSpace: number;
|
||||
parentColumnSpace: number;
|
||||
newWidgetId: string;
|
||||
tabId: string;
|
||||
props?: Record<string, any>;
|
||||
};
|
||||
|
||||
|
|
|
|||
3
app/client/src/assets/icons/control/decrease.svg
Normal file
3
app/client/src/assets/icons/control/decrease.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="2" viewBox="0 0 12 2" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.25 2H0.75C0.551088 2 0.360322 1.89464 0.21967 1.70711C0.0790178 1.51957 0 1.26522 0 1C0 0.734784 0.0790178 0.48043 0.21967 0.292893C0.360322 0.105357 0.551088 0 0.75 0H11.25C11.4489 0 11.6397 0.105357 11.7803 0.292893C11.921 0.48043 12 0.734784 12 1C12 1.26522 11.921 1.51957 11.7803 1.70711C11.6397 1.89464 11.4489 2 11.25 2Z" fill="#768896"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 459 B |
4
app/client/src/assets/icons/control/draggable.svg
Normal file
4
app/client/src/assets/icons/control/draggable.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.2611 9H5.73889C5.33081 9 5 9.42533 5 9.95V10.05C5 10.5747 5.33081 11 5.73889 11H18.2611C18.6692 11 19 10.5747 19 10.05V9.95C19 9.42533 18.6692 9 18.2611 9Z" fill="#A3B3BF"/>
|
||||
<path d="M18.2611 13H5.73889C5.33081 13 5 13.4253 5 13.95V14.05C5 14.5747 5.33081 15 5.73889 15H18.2611C18.6692 15 19 14.5747 19 14.05V13.95C19 13.4253 18.6692 13 18.2611 13Z" fill="#A3B3BF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 483 B |
3
app/client/src/assets/icons/control/increase.svg
Normal file
3
app/client/src/assets/icons/control/increase.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.25 5.25H6.75V0.75C6.75 0.551088 6.67098 0.360322 6.53033 0.21967C6.38968 0.0790178 6.19891 0 6 0C5.80109 0 5.61032 0.0790178 5.46967 0.21967C5.32902 0.360322 5.25 0.551088 5.25 0.75V5.25H0.75C0.551088 5.25 0.360322 5.32902 0.21967 5.46967C0.0790178 5.61032 0 5.80109 0 6C0 6.19891 0.0790178 6.38968 0.21967 6.53033C0.360322 6.67098 0.551088 6.75 0.75 6.75H5.25V11.25C5.25 11.4489 5.32902 11.6397 5.46967 11.7803C5.61032 11.921 5.80109 12 6 12C6.19891 12 6.38968 11.921 6.53033 11.7803C6.67098 11.6397 6.75 11.4489 6.75 11.25V6.75H11.25C11.4489 6.75 11.6397 6.67098 11.7803 6.53033C11.921 6.38968 12 6.19891 12 6C12 5.80109 11.921 5.61032 11.7803 5.46967C11.6397 5.32902 11.4489 5.25 11.25 5.25Z" fill="#768896"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 829 B |
1
app/client/src/assets/icons/control/pick-my-location.svg
Normal file
1
app/client/src/assets/icons/control/pick-my-location.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="crosshairs" class="svg-inline--fa fa-crosshairs fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M500 224h-30.364C455.724 130.325 381.675 56.276 288 42.364V12c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v30.364C130.325 56.276 56.276 130.325 42.364 224H12c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h30.364C56.276 381.675 130.325 455.724 224 469.636V500c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12v-30.364C381.675 455.724 455.724 381.675 469.636 288H500c6.627 0 12-5.373 12-12v-40c0-6.627-5.373-12-12-12zM288 404.634V364c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40.634C165.826 392.232 119.783 346.243 107.366 288H148c6.627 0 12-5.373 12-12v-40c0-6.627-5.373-12-12-12h-40.634C119.768 165.826 165.757 119.783 224 107.366V148c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12v-40.634C346.174 119.768 392.217 165.757 404.634 224H364c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40.634C392.232 346.174 346.243 392.217 288 404.634zM288 256c0 17.673-14.327 32-32 32s-32-14.327-32-32c0-17.673 14.327-32 32-32s32 14.327 32 32z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
3
app/client/src/assets/icons/widget/map.svg
Executable file
3
app/client/src/assets/icons/widget/map.svg
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="22" height="29" viewBox="0 0 22 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.9997 0.664001C15.4157 0.664001 18.9997 4.216 18.9997 8.59733C18.9997 14.548 10.9997 23.3307 10.9997 23.3307C10.9997 23.3307 2.99967 14.548 2.99967 8.59733C2.99967 4.216 6.58101 0.664001 10.9997 0.664001ZM10.9997 6C10.2924 6 9.61415 6.28095 9.11406 6.78105C8.61396 7.28115 8.33301 7.95942 8.33301 8.66667C8.33301 9.37391 8.61396 10.0522 9.11406 10.5523C9.61415 11.0524 10.2924 11.3333 10.9997 11.3333C11.7069 11.3333 12.3852 11.0524 12.8853 10.5523C13.3854 10.0522 13.6663 9.37391 13.6663 8.66667C13.6663 7.95942 13.3854 7.28115 12.8853 6.78105C12.3852 6.28095 11.7069 6 10.9997 6ZM21.6663 23.3333C21.6663 26.28 16.8903 28.6667 10.9997 28.6667C5.10901 28.6667 0.333008 26.28 0.333008 23.3333C0.333008 21.6147 1.95967 20.0867 4.48367 19.1107L5.32767 20.316C3.88767 20.92 2.99967 21.7493 2.99967 22.6667C2.99967 24.5067 6.58101 26 10.9997 26C15.4183 26 18.9997 24.5067 18.9997 22.6667C18.9997 21.7493 18.1103 20.92 16.6717 20.316L17.5157 19.1107C20.041 20.0867 21.6663 21.6147 21.6663 23.3333Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { ChartType, ChartData } from "widgets/ChartWidget";
|
||||
import { ChartType, ChartData, ChartDataPoint } from "widgets/ChartWidget";
|
||||
import styled from "styled-components";
|
||||
import { invisible } from "constants/DefaultTheme";
|
||||
import _ from "lodash";
|
||||
|
|
@ -18,6 +18,7 @@ export interface ChartComponentProps {
|
|||
chartName: string;
|
||||
widgetId: string;
|
||||
isVisible?: boolean;
|
||||
allowHorizontalScroll: boolean;
|
||||
}
|
||||
|
||||
const CanvasContainer = styled.div<ChartComponentProps>`
|
||||
|
|
@ -29,29 +30,49 @@ const CanvasContainer = styled.div<ChartComponentProps>`
|
|||
box-shadow: 0 1px 1px 0 rgba(60,75,100,.14),0 2px 1px -1px rgba(60,75,100,.12),0 1px 3px 0 rgba(60,75,100,.2);
|
||||
position: relative;
|
||||
${props => (!props.isVisible ? invisible : "")};
|
||||
padding: 10px 0 0 0;
|
||||
}`;
|
||||
|
||||
class ChartComponent extends React.Component<ChartComponentProps> {
|
||||
chartInstance = new FusionCharts();
|
||||
getChartType = (chartType: ChartType) => {
|
||||
getChartType = () => {
|
||||
const { chartType, allowHorizontalScroll, chartData } = this.props;
|
||||
const isMSChart = chartData.length > 1;
|
||||
switch (chartType) {
|
||||
case "LINE_CHART":
|
||||
return "line";
|
||||
case "BAR_CHART":
|
||||
return "bar2d";
|
||||
case "PIE_CHART":
|
||||
return "pie2d";
|
||||
case "LINE_CHART":
|
||||
return allowHorizontalScroll
|
||||
? "scrollline2d"
|
||||
: isMSChart
|
||||
? "msline"
|
||||
: "line";
|
||||
case "BAR_CHART":
|
||||
return allowHorizontalScroll
|
||||
? "scrollBar2D"
|
||||
: isMSChart
|
||||
? "msbar2d"
|
||||
: "bar2d";
|
||||
case "COLUMN_CHART":
|
||||
return "column2d";
|
||||
return allowHorizontalScroll
|
||||
? "scrollColumn2D"
|
||||
: isMSChart
|
||||
? "mscolumn2d"
|
||||
: "column2d";
|
||||
case "AREA_CHART":
|
||||
return "area2d";
|
||||
return allowHorizontalScroll
|
||||
? "scrollarea2d"
|
||||
: isMSChart
|
||||
? "msarea"
|
||||
: "area2d";
|
||||
default:
|
||||
return "column2d";
|
||||
return allowHorizontalScroll ? "scrollColumn2D" : "mscolumn2d";
|
||||
}
|
||||
};
|
||||
|
||||
getChartData = (chartData: ChartData[]) => {
|
||||
return chartData.map(item => {
|
||||
const data: ChartDataPoint[] = chartData[0].data;
|
||||
return data.map(item => {
|
||||
return {
|
||||
label: item.x,
|
||||
value: item.y,
|
||||
|
|
@ -59,26 +80,123 @@ class ChartComponent extends React.Component<ChartComponentProps> {
|
|||
});
|
||||
};
|
||||
|
||||
getChartCategoriesMutliSeries = (chartData: ChartData[]) => {
|
||||
const categories: string[] = [];
|
||||
for (let index = 0; index < chartData.length; index++) {
|
||||
const data: ChartDataPoint[] = chartData[index].data;
|
||||
for (let dataIndex = 0; dataIndex < data.length; dataIndex++) {
|
||||
const category = data[dataIndex].x;
|
||||
if (!categories.includes(category)) {
|
||||
categories.push(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
};
|
||||
|
||||
getChartCategories = (chartData: ChartData[]) => {
|
||||
const categories: string[] = this.getChartCategoriesMutliSeries(chartData);
|
||||
return categories.map(item => {
|
||||
return {
|
||||
label: item,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
getSeriesChartData = (data: ChartDataPoint[], categories: string[]) => {
|
||||
const dataMap: { [key: string]: string } = {};
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const item: ChartDataPoint = data[index];
|
||||
dataMap[item.x] = item.y;
|
||||
}
|
||||
return categories.map((category: string) => {
|
||||
return {
|
||||
value: dataMap[category] ? dataMap[category] : null,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
getChartDataset = (chartData: ChartData[]) => {
|
||||
const categories: string[] = this.getChartCategoriesMutliSeries(chartData);
|
||||
return chartData.map((item: ChartData) => {
|
||||
const seriesChartData: object[] = this.getSeriesChartData(
|
||||
item.data,
|
||||
categories,
|
||||
);
|
||||
return {
|
||||
seriesName: item.seriesName,
|
||||
data: seriesChartData,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
getChartConfig = () => {
|
||||
return {
|
||||
caption: this.props.chartName,
|
||||
xAxisName: this.props.xAxisName,
|
||||
yAxisName: this.props.yAxisName,
|
||||
theme: "fusion",
|
||||
captionAlignment: "left",
|
||||
captionHorizontalPadding: 10,
|
||||
alignCaptionWithCanvas: 0,
|
||||
};
|
||||
};
|
||||
|
||||
getChartDataSource = () => {
|
||||
if (
|
||||
this.props.chartData.length === 1 ||
|
||||
this.props.chartType === "PIE_CHART"
|
||||
) {
|
||||
return {
|
||||
chart: this.getChartConfig(),
|
||||
data: this.getChartData(this.props.chartData),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
chart: this.getChartConfig(),
|
||||
categories: [
|
||||
{
|
||||
category: this.getChartCategories(this.props.chartData),
|
||||
},
|
||||
],
|
||||
dataset: this.getChartDataset(this.props.chartData),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
getScrollChartDataSource = () => {
|
||||
const chartConfig = this.getChartConfig();
|
||||
return {
|
||||
chart: {
|
||||
...chartConfig,
|
||||
scrollheight: "10",
|
||||
showvalues: "1",
|
||||
numVisiblePlot: "5",
|
||||
flatScrollBars: "1",
|
||||
},
|
||||
categories: [
|
||||
{
|
||||
category: this.getChartCategories(this.props.chartData),
|
||||
},
|
||||
],
|
||||
dataset: this.getChartDataset(this.props.chartData),
|
||||
};
|
||||
};
|
||||
|
||||
createGraph = () => {
|
||||
const dataSource =
|
||||
this.props.allowHorizontalScroll && this.props.chartType !== "PIE_CHART"
|
||||
? this.getScrollChartDataSource()
|
||||
: this.getChartDataSource();
|
||||
const chartConfig = {
|
||||
type: this.getChartType(this.props.chartType),
|
||||
type: this.getChartType(),
|
||||
renderAt: this.props.widgetId + "chart-container",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
dataFormat: "json",
|
||||
dataSource: {
|
||||
chart: {
|
||||
caption: this.props.chartName,
|
||||
xAxisName: this.props.xAxisName,
|
||||
yAxisName: this.props.yAxisName,
|
||||
theme: "fusion",
|
||||
captionAlignment: "left",
|
||||
captionHorizontalPadding: 10,
|
||||
alignCaptionWithCanvas: 0,
|
||||
},
|
||||
data: this.getChartData(this.props.chartData),
|
||||
},
|
||||
dataSource: dataSource,
|
||||
};
|
||||
console.log("chartConfig", chartConfig);
|
||||
this.chartInstance = new FusionCharts(chartConfig);
|
||||
};
|
||||
|
||||
|
|
@ -91,19 +209,15 @@ class ChartComponent extends React.Component<ChartComponentProps> {
|
|||
|
||||
componentDidUpdate(prevProps: ChartComponentProps) {
|
||||
if (!_.isEqual(prevProps, this.props)) {
|
||||
if (prevProps.chartType !== this.props.chartType) {
|
||||
const chartType = this.getChartType(this.props.chartType);
|
||||
this.chartInstance.chartType(chartType);
|
||||
const chartType = this.getChartType();
|
||||
this.chartInstance.chartType(chartType);
|
||||
if (
|
||||
this.props.allowHorizontalScroll &&
|
||||
this.props.chartType !== "PIE_CHART"
|
||||
) {
|
||||
this.chartInstance.setChartData(this.getScrollChartDataSource());
|
||||
} else {
|
||||
this.chartInstance.setChartData({
|
||||
chart: {
|
||||
caption: this.props.chartName,
|
||||
xAxisName: this.props.xAxisName,
|
||||
yAxisName: this.props.yAxisName,
|
||||
theme: "fusion",
|
||||
},
|
||||
data: this.getChartData(this.props.chartData),
|
||||
});
|
||||
this.chartInstance.setChartData(this.getChartDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
import React from "react";
|
||||
import { ControlWrapper } from "../../propertyControls/StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { Droppable, Draggable } from "react-beautiful-dnd";
|
||||
|
||||
const StyledListWrapper = styled(ControlWrapper)`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-right: 16px;
|
||||
margin: 8px 0 0 0;
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
};
|
||||
|
||||
type DraggableComponentProps = {
|
||||
index: number;
|
||||
draggableId: string;
|
||||
item: {
|
||||
label: string;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
renderComponent: (props: RenderComponentProps) => JSX.Element;
|
||||
};
|
||||
|
||||
export const DraggableComponent = (props: DraggableComponentProps) => {
|
||||
const {
|
||||
deleteOption,
|
||||
updateOption,
|
||||
item,
|
||||
index,
|
||||
draggableId,
|
||||
renderComponent,
|
||||
} = props;
|
||||
return (
|
||||
<Draggable draggableId={draggableId} key={draggableId} index={index}>
|
||||
{({ innerRef, draggableProps, dragHandleProps }) => (
|
||||
<StyledListWrapper
|
||||
orientation={"HORIZONTAL"}
|
||||
{...draggableProps}
|
||||
{...dragHandleProps}
|
||||
ref={innerRef as React.Ref<HTMLDivElement>}
|
||||
style={{
|
||||
...draggableProps.style,
|
||||
userSelect: "none",
|
||||
position: "static",
|
||||
}}
|
||||
>
|
||||
{renderComponent({ deleteOption, updateOption, item, index })}
|
||||
</StyledListWrapper>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
|
||||
type DroppableComponentProps = {
|
||||
items: object[];
|
||||
renderComponent: (props: RenderComponentProps) => JSX.Element;
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
};
|
||||
|
||||
export const DroppableComponent = (props: DroppableComponentProps) => {
|
||||
const { items } = props;
|
||||
return (
|
||||
<Droppable droppableId="0">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div ref={innerRef as React.Ref<HTMLDivElement>} {...droppableProps}>
|
||||
{items.map((item: { id: string } & any, index: number) => {
|
||||
return (
|
||||
<DraggableComponent
|
||||
key={index}
|
||||
index={index}
|
||||
item={item}
|
||||
draggableId={item.id}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import React from "react";
|
||||
import {
|
||||
withScriptjs,
|
||||
withGoogleMap,
|
||||
GoogleMap,
|
||||
Marker,
|
||||
} from "react-google-maps";
|
||||
import SearchBox from "react-google-maps/lib/components/places/SearchBox";
|
||||
import { MarkerProps } from "widgets/MapWidget";
|
||||
import { ControlIcons } from "icons/ControlIcons";
|
||||
import styled, { AnyStyledComponent } from "styled-components";
|
||||
|
||||
interface MapComponentProps {
|
||||
widgetId: string;
|
||||
isDisabled?: boolean;
|
||||
isVisible?: boolean;
|
||||
enableSearch: boolean;
|
||||
zoomLevel: number;
|
||||
enablePickLocation: boolean;
|
||||
allowZoom: boolean;
|
||||
center: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
};
|
||||
markers?: Array<MarkerProps>;
|
||||
enableCreateMarker: boolean;
|
||||
updateCenter: (lat: number, lng: number) => void;
|
||||
saveMarker: (lat: number, lng: number) => void;
|
||||
selectMarker: (lat: number, lng: number, title: string) => void;
|
||||
}
|
||||
|
||||
const StyledInput = styled.input`
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
width: 240px;
|
||||
height: 32px;
|
||||
margin-top: 27px;
|
||||
padding: 0 12px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
text-overflow: ellipses;
|
||||
`;
|
||||
type PickMyLocationProps = {
|
||||
allowZoom: boolean;
|
||||
};
|
||||
|
||||
const PickMyLocationWrapper = styled.div<PickMyLocationProps>`
|
||||
position: absolute;
|
||||
bottom: ${props => (props.allowZoom ? 110 : 20)}px;
|
||||
right: -95px;
|
||||
width: 140px;
|
||||
`;
|
||||
|
||||
const StyledPickMyLocationIcon = styled(
|
||||
ControlIcons.PICK_MY_LOCATION_CONTROL as AnyStyledComponent,
|
||||
)`
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const MyMapComponent = withScriptjs(
|
||||
withGoogleMap((props: any) => (
|
||||
<GoogleMap
|
||||
options={{
|
||||
zoomControl: props.allowZoom,
|
||||
fullscreenControl: false,
|
||||
mapTypeControl: false,
|
||||
scrollwheel: false,
|
||||
}}
|
||||
zoom={props.zoom}
|
||||
center={props.center}
|
||||
onClick={e => {
|
||||
if (props.enableCreateMarker) {
|
||||
props.saveMarker(e.latLng.lat(), e.latLng.lng());
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.enableSearch && (
|
||||
<SearchBox
|
||||
controlPosition={2}
|
||||
onPlacesChanged={props.onPlacesChanged}
|
||||
ref={props.onSearchBoxMounted}
|
||||
>
|
||||
<StyledInput type="text" placeholder="Enter location to search" />
|
||||
</SearchBox>
|
||||
)}
|
||||
{props.markers.map((marker: any, index: number) => (
|
||||
<Marker
|
||||
key={index}
|
||||
title={marker.title}
|
||||
position={{ lat: marker.lat, lng: marker.lng }}
|
||||
clickable
|
||||
onClick={e => {
|
||||
props.selectMarker(marker.lat, marker.lng, marker.title);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{props.enablePickLocation && (
|
||||
<PickMyLocationWrapper
|
||||
onClick={props.getUserLocation}
|
||||
title="Pick My Location"
|
||||
allowZoom={props.allowZoom}
|
||||
>
|
||||
<StyledPickMyLocationIcon />
|
||||
</PickMyLocationWrapper>
|
||||
)}
|
||||
</GoogleMap>
|
||||
)),
|
||||
);
|
||||
|
||||
class MapComponent extends React.Component<MapComponentProps> {
|
||||
private searchBox = React.createRef<SearchBox>();
|
||||
|
||||
onSearchBoxMounted = (ref: any) => {
|
||||
this.searchBox = ref;
|
||||
};
|
||||
onPlacesChanged = () => {
|
||||
const node: any = this.searchBox;
|
||||
if (node) {
|
||||
const places: any = node.getPlaces();
|
||||
const location = places[0].geometry.location;
|
||||
const lat = location.lat();
|
||||
const lng = location.lng();
|
||||
this.props.updateCenter(lat, lng);
|
||||
}
|
||||
};
|
||||
|
||||
getUserLocation = () => {
|
||||
if ("geolocation" in navigator) {
|
||||
return navigator.geolocation.getCurrentPosition(data => {
|
||||
const {
|
||||
coords: { latitude: lat, longitude: lng },
|
||||
} = data;
|
||||
this.props.saveMarker(lat, lng);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const zoom = Math.floor(this.props.zoomLevel / 5);
|
||||
return (
|
||||
<MyMapComponent
|
||||
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC2H_twoNbEKMm9Q0nYAh7715Dplg2asCI&v=3.exp&libraries=geometry,drawing,places"
|
||||
loadingElement={<div style={{ height: `100%` }} />}
|
||||
containerElement={<div style={{ height: `100%` }} />}
|
||||
mapElement={<div style={{ height: `100%` }} />}
|
||||
{...this.props}
|
||||
zoom={zoom}
|
||||
onPlacesChanged={this.onPlacesChanged}
|
||||
onSearchBoxMounted={this.onSearchBoxMounted}
|
||||
getUserLocation={this.getUserLocation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MapComponent;
|
||||
|
|
@ -41,7 +41,7 @@ export const RichtextEditorComponent = (
|
|||
"insertdatetime media table paste code help",
|
||||
],
|
||||
toolbar:
|
||||
"undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help",
|
||||
"undo redo | formatselect | bold italic backcolor forecolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help",
|
||||
}}
|
||||
onEditorChange={(content: any) => props.onValueChange(content)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
import React from "react";
|
||||
import { ControlIcons } from "icons/ControlIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
import styled from "constants/DefaultTheme";
|
||||
|
||||
const StyledIncreaseIcon = styled(
|
||||
ControlIcons.INCREASE_CONTROL as AnyStyledComponent,
|
||||
)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 32px;
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.colors.paneSectionLabel};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDecreaseIcon = styled(
|
||||
ControlIcons.DECREASE_CONTROL as AnyStyledComponent,
|
||||
)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 32px;
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.colors.paneSectionLabel};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StepWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
background: #121518;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
`;
|
||||
|
||||
const InputWrapper = styled.div`
|
||||
width: calc(100% - 80px);
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background: #23292e;
|
||||
font-size: 14px;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
text-align: center;
|
||||
letter-spacing: 1.44px;
|
||||
`;
|
||||
|
||||
interface StepComponentProps {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
steps: number;
|
||||
displayFormat: (value: number) => string;
|
||||
onChange: (value: number) => void;
|
||||
}
|
||||
|
||||
export const StepComponent = (props: StepComponentProps) => {
|
||||
function decrease() {
|
||||
if (props.value < props.min) {
|
||||
return;
|
||||
}
|
||||
const value = props.value - props.steps;
|
||||
props.onChange(value);
|
||||
}
|
||||
function increase() {
|
||||
if (props.value > props.max) {
|
||||
return;
|
||||
}
|
||||
const value = props.value + props.steps;
|
||||
props.onChange(value);
|
||||
}
|
||||
return (
|
||||
<StepWrapper>
|
||||
<StyledDecreaseIcon height={2} width={12} onClick={decrease} />
|
||||
<InputWrapper>{props.displayFormat(props.value)}</InputWrapper>
|
||||
<StyledIncreaseIcon height={12} width={12} onClick={increase} />
|
||||
</StepWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepComponent;
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface TabsComponentProps {
|
||||
isVisible?: boolean;
|
||||
tabs?: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
}>;
|
||||
selectedTabId?: string;
|
||||
onTabChange: (tabId: string) => void;
|
||||
}
|
||||
|
||||
const TabsContainer = styled.div`
|
||||
&& {
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
|
||||
type TabProps = {
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
const StyledTab = styled.div`
|
||||
height: 32px;
|
||||
border-bottom: 1px solid;
|
||||
border-color: ${props => props.theme.colors.bodyBG};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledText = styled.div<TabProps>`
|
||||
white-space: nowrap;
|
||||
background: ${props => props.theme.colors.builderBodyBG};
|
||||
color: ${props => props.theme.colors.menuIconColorInactive};
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
box-shadow: ${props => (props.selected ? props.theme.shadows[2] : "")};
|
||||
border-bottom: ${props => (props.selected ? "none" : "1px solid")};
|
||||
border-color: ${props => props.theme.colors.bodyBG};
|
||||
&:hover {
|
||||
background: ${props =>
|
||||
props.selected
|
||||
? props.theme.colors.textOnDarkBG
|
||||
: props.theme.colors.hover};
|
||||
box-shadow: ${props => (props.selected ? "" : props.theme.shadows[3])};
|
||||
}
|
||||
`;
|
||||
|
||||
class TabsComponent extends React.Component<TabsComponentProps> {
|
||||
selectTab = (tab: { id: string; label: string }) => {
|
||||
this.props.onTabChange(tab.id);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TabsContainer>
|
||||
{this.props.tabs &&
|
||||
this.props.tabs.map((tab, index) => (
|
||||
<StyledText
|
||||
onClick={this.selectTab.bind(this, tab)}
|
||||
selected={this.props.selectedTabId === tab.id}
|
||||
key={index}
|
||||
>
|
||||
{tab.label}
|
||||
</StyledText>
|
||||
))}
|
||||
<StyledTab></StyledTab>
|
||||
</TabsContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TabsComponent;
|
||||
|
|
@ -8,6 +8,7 @@ import moment from "moment-timezone";
|
|||
import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css";
|
||||
import { DatePickerType } from "widgets/DatePickerWidget";
|
||||
import { WIDGET_PADDING } from "constants/WidgetConstants";
|
||||
import { TimePrecision } from "@blueprintjs/datetime";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
const StyledControlGroup = styled(ControlGroup)`
|
||||
|
|
@ -45,19 +46,34 @@ const StyledControlGroup = styled(ControlGroup)`
|
|||
}
|
||||
&&& {
|
||||
input {
|
||||
border: 1px solid #a1acb3;
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${Colors.HIT_GRAY};
|
||||
border-radius: ${props => props.theme.radii[1]}px;
|
||||
box-shadow: none;
|
||||
color: #2e3d49;
|
||||
font-size: 14px;
|
||||
color: ${Colors.OXFORD_BLUE};
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class DatePickerComponent extends React.Component<DatePickerComponentProps> {
|
||||
render() {
|
||||
const now = moment();
|
||||
const year = now.get("year");
|
||||
const month = now.get("month");
|
||||
const date = now.get("date");
|
||||
const minDate = now.clone().set({ month, date: date - 1, year: year - 20 });
|
||||
const maxDate = now.clone().set({ month, date: date + 1, year: year + 20 });
|
||||
const selectedDate = new Date(
|
||||
new Date(this.props.selectedDate || "").getTime() +
|
||||
this.getOffset(new Date(this.props.selectedDate || "")),
|
||||
);
|
||||
return (
|
||||
<StyledControlGroup fill>
|
||||
<StyledControlGroup
|
||||
fill
|
||||
onClick={(e: any) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{this.props.label && (
|
||||
<Label
|
||||
className={
|
||||
|
|
@ -77,18 +93,14 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
|
|||
placeholder={this.props.dateFormat}
|
||||
disabled={this.props.isDisabled}
|
||||
showActionsBar={true}
|
||||
timePickerProps={
|
||||
this.props.enableTimePicker
|
||||
? {
|
||||
useAmPm: true,
|
||||
value: this.props.selectedDate,
|
||||
showArrowButtons: true,
|
||||
}
|
||||
: undefined
|
||||
timePrecision={
|
||||
this.props.enableTimePicker ? TimePrecision.MINUTE : undefined
|
||||
}
|
||||
closeOnSelection={true}
|
||||
closeOnSelection
|
||||
onChange={this.onDateSelected}
|
||||
value={this.props.selectedDate}
|
||||
value={selectedDate}
|
||||
maxDate={maxDate.toDate()}
|
||||
minDate={minDate.toDate()}
|
||||
/>
|
||||
) : (
|
||||
<DateRangeInput
|
||||
|
|
@ -106,30 +118,28 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
|
|||
);
|
||||
}
|
||||
|
||||
getOffset = (date: Date) => {
|
||||
const timezone = this.props.timezone || moment.tz.guess();
|
||||
return moment.tz.zone(timezone)!.utcOffset(date.getTime()) * 60 * 1000;
|
||||
};
|
||||
|
||||
formatDate = (date: Date): string => {
|
||||
let dateFormat = "DD/MM/YYYY";
|
||||
if (this.props.enableTimePicker) {
|
||||
dateFormat = "DD/MM/YYYY HH:mm";
|
||||
}
|
||||
if (this.props.timezone) {
|
||||
return moment(date)
|
||||
.tz(this.props.timezone)
|
||||
.format(dateFormat);
|
||||
}
|
||||
return moment(date).format(dateFormat);
|
||||
};
|
||||
|
||||
parseDate = (dateStr: string): Date => {
|
||||
if (this.props.timezone) {
|
||||
return moment(dateStr)
|
||||
.tz(this.props.timezone)
|
||||
.toDate();
|
||||
}
|
||||
return moment(dateStr).toDate();
|
||||
};
|
||||
|
||||
onDateSelected = (selectedDate: Date) => {
|
||||
this.props.onDateSelected(selectedDate);
|
||||
const dateValue = new Date(
|
||||
selectedDate.getTime() - this.getOffset(selectedDate),
|
||||
).getTime();
|
||||
this.props.onDateSelected(new Date(dateValue));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
210
app/client/src/components/propertyControls/ChartDataControl.tsx
Normal file
210
app/client/src/components/propertyControls/ChartDataControl.tsx
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
ControlWrapper,
|
||||
StyledInputGroup,
|
||||
StyledPropertyPaneButton,
|
||||
} from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { FormIcons } from "icons/FormIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocompleteInput";
|
||||
|
||||
const StyledOptionControlWrapper = styled(ControlWrapper)`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
|
||||
margin-right: 2px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
&&& {
|
||||
input {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
&:focus {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDynamicInput = styled.div`
|
||||
width: 100%;
|
||||
&&& {
|
||||
input {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
&:focus {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDeleteIcon = styled(FormIcons.DELETE_ICON as AnyStyledComponent)`
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
seriesName: string;
|
||||
data: Array<{
|
||||
x: string;
|
||||
y: string;
|
||||
}>;
|
||||
};
|
||||
length: number;
|
||||
deleteOption: Function;
|
||||
updateOption: Function;
|
||||
};
|
||||
|
||||
function DataControlComponent(props: RenderComponentProps) {
|
||||
const { deleteOption, updateOption, item, index, length } = props;
|
||||
return (
|
||||
<StyledOptionControlWrapper orientation={"VERTICAL"}>
|
||||
{length > 1 && (
|
||||
<StyledOptionControlWrapper orientation={"HORIZONTAL"}>
|
||||
<StyledOptionControlInputGroup
|
||||
type="text"
|
||||
placeholder="Series Name"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateOption(index, "seriesName", event.target.value);
|
||||
}}
|
||||
defaultValue={item.seriesName}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
height={20}
|
||||
width={20}
|
||||
onClick={() => {
|
||||
deleteOption(index);
|
||||
}}
|
||||
/>
|
||||
</StyledOptionControlWrapper>
|
||||
)}
|
||||
<StyledDynamicInput>
|
||||
<DynamicAutocompleteInput
|
||||
input={{
|
||||
value: item.data,
|
||||
onChange: (
|
||||
event: React.ChangeEvent<HTMLTextAreaElement> | string,
|
||||
) => {
|
||||
let value = event;
|
||||
if (typeof event !== "string") {
|
||||
value = event.target.value;
|
||||
}
|
||||
updateOption(index, "data", value);
|
||||
},
|
||||
}}
|
||||
meta={{
|
||||
error: "",
|
||||
touched: true,
|
||||
}}
|
||||
theme={"DARK"}
|
||||
singleLine={false}
|
||||
placeholder=""
|
||||
/>
|
||||
</StyledDynamicInput>
|
||||
</StyledOptionControlWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
class ChartDataControl extends BaseControl<ControlProps> {
|
||||
render() {
|
||||
const chartData: Array<{
|
||||
seriesName: string;
|
||||
data: Array<{
|
||||
x: string;
|
||||
y: string;
|
||||
}>;
|
||||
}> = this.props.propertyValue || [];
|
||||
const dataLength = chartData.length;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{chartData.map((data, index) => {
|
||||
return (
|
||||
<DataControlComponent
|
||||
key={index}
|
||||
index={index}
|
||||
item={data}
|
||||
length={dataLength}
|
||||
deleteOption={this.deleteOption}
|
||||
updateOption={this.updateOption}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<StyledPropertyPaneButton
|
||||
text="Add Data"
|
||||
icon="plus"
|
||||
color="#FFFFFF"
|
||||
minimal
|
||||
onClick={this.addOption}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
deleteOption = (index: number) => {
|
||||
const chartData: object[] = this.props.propertyValue.slice();
|
||||
chartData.splice(index, 1);
|
||||
this.updateProperty(this.props.propertyName, chartData);
|
||||
};
|
||||
|
||||
updateOption = (
|
||||
index: number,
|
||||
propertyName: string,
|
||||
updatedValue: string,
|
||||
) => {
|
||||
const chartData: Array<{
|
||||
seriesName: string;
|
||||
data: Array<{
|
||||
x: string;
|
||||
y: string;
|
||||
}>;
|
||||
}> = this.props.propertyValue;
|
||||
this.updateProperty(
|
||||
this.props.propertyName,
|
||||
chartData.map((item, i) => {
|
||||
if (index === i) {
|
||||
if (propertyName === "seriesName") {
|
||||
item.seriesName = updatedValue;
|
||||
} else {
|
||||
item.data = JSON.parse(updatedValue);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
addOption = () => {
|
||||
const chartData: Array<{
|
||||
seriesName: string;
|
||||
data: Array<{
|
||||
x: string;
|
||||
y: string;
|
||||
}>;
|
||||
}> = this.props.propertyValue ? this.props.propertyValue.slice() : [];
|
||||
chartData.push({ seriesName: "", data: [{ x: "", y: "" }] });
|
||||
this.updateProperty(this.props.propertyName, chartData);
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
return "CHART_DATA";
|
||||
}
|
||||
}
|
||||
|
||||
export default ChartDataControl;
|
||||
|
|
@ -1,37 +1,184 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import { StyledDatePicker } from "./StyledControls";
|
||||
import {
|
||||
StyledDatePicker,
|
||||
StyledTimeZonePicker,
|
||||
StyledSwitch,
|
||||
} from "./StyledControls";
|
||||
import moment from "moment-timezone";
|
||||
import "../../../node_modules/@blueprintjs/timezone/lib/css/blueprint-timezone.css";
|
||||
import styled from "styled-components";
|
||||
import { TIMEZONE, ENABLE_TIME } from "constants/messages";
|
||||
import { TimePrecision } from "@blueprintjs/datetime";
|
||||
|
||||
const DatePickerControlWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&&& {
|
||||
input {
|
||||
background: ${props => props.theme.colors.paneTextBG};
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const LabelWrapper = styled.div`
|
||||
color: ${props => props.theme.colors.paneText};
|
||||
margin-bottom: 4px;
|
||||
font-size: ${props => props.theme.fontSizes[3]}px;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
type DatePickerContorlState = {
|
||||
date: Date;
|
||||
dateValue: number;
|
||||
isTimeEnabled: boolean;
|
||||
timezone: string;
|
||||
};
|
||||
|
||||
class DatePickerControl extends BaseControl<
|
||||
DatePickerControlProps,
|
||||
DatePickerContorlState
|
||||
> {
|
||||
constructor(props: DatePickerControlProps) {
|
||||
super(props);
|
||||
const timezone =
|
||||
this.props.propertyValue && this.props.propertyValue.timezone
|
||||
? this.props.propertyValue.timezone
|
||||
: moment.tz.guess();
|
||||
const isTimeEnabled =
|
||||
this.props.propertyValue && this.props.propertyValue.isTimeEnabled
|
||||
? this.props.propertyValue.isTimeEnabled
|
||||
: false;
|
||||
const dateValue =
|
||||
this.props.propertyValue && this.props.propertyValue.dateValue
|
||||
? this.props.propertyValue.dateValue
|
||||
: new Date().getTime();
|
||||
const offset = this.getOffset(timezone, dateValue);
|
||||
const date = new Date(dateValue + offset);
|
||||
this.state = {
|
||||
date,
|
||||
timezone,
|
||||
dateValue,
|
||||
isTimeEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const timezone =
|
||||
this.props.propertyValue && this.props.propertyValue.timezone
|
||||
? this.props.propertyValue.timezone
|
||||
: moment.tz.guess();
|
||||
const isTimeEnabled =
|
||||
this.props.propertyValue && this.props.propertyValue.isTimeEnabled
|
||||
? this.props.propertyValue.isTimeEnabled
|
||||
: false;
|
||||
const dateValue =
|
||||
this.props.propertyValue && this.props.propertyValue.dateValue
|
||||
? this.props.propertyValue.dateValue
|
||||
: new Date().getTime();
|
||||
const offset = this.getOffset(timezone, dateValue);
|
||||
const date = new Date(dateValue + offset);
|
||||
this.setState({
|
||||
date,
|
||||
timezone,
|
||||
dateValue,
|
||||
isTimeEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
getOffset = (timezone: string, timeStamp: number) => {
|
||||
return moment.tz.zone(timezone)!.utcOffset(timeStamp) * 60 * 1000;
|
||||
};
|
||||
|
||||
class DatePickerControl extends BaseControl<DatePickerControlProps> {
|
||||
render() {
|
||||
const now = moment();
|
||||
const year = now.get("year");
|
||||
const date = now.get("date");
|
||||
const month = now.get("month");
|
||||
const minDate = now.clone().set({ month, date: date - 1, year: year - 20 });
|
||||
const maxDate = now.clone().set({ month, date: date + 1, year: year + 20 });
|
||||
|
||||
return (
|
||||
<StyledDatePicker
|
||||
formatDate={this.formatDate}
|
||||
parseDate={this.parseDate}
|
||||
placeholder={"DD/MM/YYYY"}
|
||||
showActionsBar={true}
|
||||
timePickerProps={{
|
||||
useAmPm: true,
|
||||
value: new Date(this.props.propertyValue),
|
||||
showArrowButtons: true,
|
||||
}}
|
||||
onChange={this.onDateSelected}
|
||||
value={new Date(this.props.propertyValue)}
|
||||
/>
|
||||
<DatePickerControlWrapper>
|
||||
<StyledDatePicker
|
||||
formatDate={this.formatDate}
|
||||
parseDate={this.parseDate}
|
||||
placeholder="DD/MM/YYYY"
|
||||
showActionsBar
|
||||
timePrecision={
|
||||
this.state.isTimeEnabled ? TimePrecision.MINUTE : undefined
|
||||
}
|
||||
closeOnSelection
|
||||
onChange={this.onDateSelected}
|
||||
maxDate={maxDate.toDate()}
|
||||
minDate={minDate.toDate()}
|
||||
value={this.state.date}
|
||||
/>
|
||||
<LabelWrapper>{TIMEZONE}</LabelWrapper>
|
||||
<StyledTimeZonePicker
|
||||
onChange={this.onTimeZoneSelected}
|
||||
valueDisplayFormat="composite"
|
||||
showLocalTimezone
|
||||
value={this.state.timezone || moment.tz.guess()}
|
||||
/>
|
||||
<LabelWrapper>{ENABLE_TIME}</LabelWrapper>
|
||||
<StyledSwitch
|
||||
onChange={this.onToggle}
|
||||
checked={this.state.isTimeEnabled}
|
||||
large
|
||||
/>
|
||||
</DatePickerControlWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
formatDate = (date: Date): string => {
|
||||
return moment(date).format("DD/MM/YYYY");
|
||||
onToggle = () => {
|
||||
const { isTimeEnabled } = this.state;
|
||||
this.setState({ isTimeEnabled: !isTimeEnabled }, () => {
|
||||
this.updatePropertyValue();
|
||||
});
|
||||
};
|
||||
|
||||
parseDate = (dateStr: string): Date => {
|
||||
return moment(dateStr).toDate();
|
||||
onTimeZoneSelected = (timezone: string): void => {
|
||||
const { dateValue } = this.state;
|
||||
const offset = this.getOffset(timezone, dateValue);
|
||||
const dateVisible = new Date(dateValue + offset);
|
||||
this.setState({ timezone: timezone, date: dateVisible }, () => {
|
||||
this.updatePropertyValue();
|
||||
});
|
||||
};
|
||||
|
||||
onDateSelected = (date: Date): void => {
|
||||
this.updateProperty(this.props.propertyName, date);
|
||||
const { timezone } = this.state;
|
||||
const offset = this.getOffset(timezone, date.getTime());
|
||||
const dateValue = new Date(date.getTime() - offset).getTime();
|
||||
this.setState({ date: date, dateValue: dateValue }, () => {
|
||||
this.updatePropertyValue();
|
||||
});
|
||||
};
|
||||
|
||||
updatePropertyValue = () => {
|
||||
const { dateValue, isTimeEnabled, timezone } = this.state;
|
||||
this.updateProperty(this.props.propertyName, {
|
||||
timezone: timezone,
|
||||
dateValue: dateValue,
|
||||
isTimeEnabled: isTimeEnabled,
|
||||
});
|
||||
};
|
||||
|
||||
formatDate = (date: Date): string => {
|
||||
let dateFormat = "DD/MM/YYYY";
|
||||
if (this.state.isTimeEnabled) {
|
||||
dateFormat = "DD/MM/YYYY HH:mm";
|
||||
}
|
||||
return moment(date).format(dateFormat);
|
||||
};
|
||||
|
||||
parseDate = (dateStr: string): Date => {
|
||||
const date = moment(dateStr).toDate();
|
||||
return date;
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
|
|
@ -41,8 +188,11 @@ class DatePickerControl extends BaseControl<DatePickerControlProps> {
|
|||
|
||||
export interface DatePickerControlProps extends ControlProps {
|
||||
placeholderText: string;
|
||||
propertyValue: Date;
|
||||
enableTimePicker: boolean;
|
||||
propertyValue: {
|
||||
timezone: string;
|
||||
dateValue: number;
|
||||
isTimeEnabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export default DatePickerControl;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
import React, { MutableRefObject } from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import styled from "styled-components";
|
||||
import SearchBox from "react-google-maps/lib/components/places/SearchBox";
|
||||
import StandaloneSearchBox from "react-google-maps/lib/components/places/StandaloneSearchBox";
|
||||
const { compose, withProps, lifecycle } = require("recompose");
|
||||
const { withScriptjs } = require("react-google-maps");
|
||||
|
||||
const StyledInput = styled.input`
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
text-overflow: ellipses;
|
||||
background: #272821;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
`;
|
||||
|
||||
interface StandaloneSearchBoxProps {
|
||||
onSearchBoxMounted: (ref: SearchBoxRef) => void;
|
||||
onPlacesChanged: () => void;
|
||||
}
|
||||
|
||||
type SearchBoxRef = MutableRefObject<StandaloneSearchBox | null>;
|
||||
|
||||
const PlacesWithStandaloneSearchBox = compose(
|
||||
withProps({
|
||||
googleMapURL:
|
||||
"https://maps.googleapis.com/maps/api/js?key=AIzaSyC2H_twoNbEKMm9Q0nYAh7715Dplg2asCI&v=3.exp&libraries=geometry,drawing,places",
|
||||
loadingElement: <div style={{ height: `100%` }} />,
|
||||
containerElement: <div style={{ height: `400px` }} />,
|
||||
}),
|
||||
lifecycle({
|
||||
componentWillMount() {
|
||||
let searchBox: SearchBoxRef = React.createRef<SearchBox>();
|
||||
this.setState({
|
||||
places: [],
|
||||
onSearchBoxMounted: (ref: SearchBoxRef) => {
|
||||
searchBox = ref;
|
||||
},
|
||||
onPlacesChanged: () => {
|
||||
if (searchBox === null) return;
|
||||
if (searchBox.current === null) return;
|
||||
if (searchBox.current.getPlaces === null) return;
|
||||
const places = searchBox.current.getPlaces();
|
||||
this.setState({
|
||||
places,
|
||||
});
|
||||
this.props.onLocationSelection(places);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
withScriptjs,
|
||||
)((props: any) => (
|
||||
<div data-standalone-searchbox="">
|
||||
<StandaloneSearchBox
|
||||
ref={props.onSearchBoxMounted}
|
||||
onPlacesChanged={props.onPlacesChanged}
|
||||
>
|
||||
<StyledInput type="text" placeholder="Enter location" />
|
||||
</StandaloneSearchBox>
|
||||
</div>
|
||||
));
|
||||
|
||||
class LocationSearchControl extends BaseControl<ControlProps> {
|
||||
onLocationSelection = (
|
||||
places: Array<{
|
||||
geometry: { location: { lat: () => number; lng: () => number } };
|
||||
}>,
|
||||
) => {
|
||||
const location = places[0].geometry.location;
|
||||
const lat = location.lat();
|
||||
const lng = location.lng();
|
||||
const value = { lat, lng };
|
||||
this.updateProperty(this.props.propertyName, value);
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<PlacesWithStandaloneSearchBox
|
||||
onLocationSelection={this.onLocationSelection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
static getControlType() {
|
||||
return "LOCATION_SEARCH";
|
||||
}
|
||||
}
|
||||
|
||||
export default LocationSearchControl;
|
||||
55
app/client/src/components/propertyControls/StepControl.tsx
Normal file
55
app/client/src/components/propertyControls/StepControl.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import StepComponent from "components/designSystems/appsmith/StepComponent";
|
||||
|
||||
class StepControl extends BaseControl<StepControlProps> {
|
||||
getStepTypeControls = () => {
|
||||
const { stepType } = this.props;
|
||||
if (stepType === "ZOOM_PERCENTAGE") {
|
||||
return {
|
||||
min: 0,
|
||||
max: 100,
|
||||
steps: 5,
|
||||
displayFormat: (value: number): string => {
|
||||
return `${value}%`;
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
min: 0,
|
||||
max: 100,
|
||||
steps: 1,
|
||||
displayFormat: (value: number): string => {
|
||||
return `${value}`;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
const { min, max, steps, displayFormat } = this.getStepTypeControls();
|
||||
return (
|
||||
<StepComponent
|
||||
min={min}
|
||||
max={max}
|
||||
steps={steps}
|
||||
value={this.props.propertyValue}
|
||||
onChange={(value: number) => {
|
||||
this.updateProperty(this.props.propertyName, value);
|
||||
}}
|
||||
displayFormat={displayFormat}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
static getControlType() {
|
||||
return "STEP";
|
||||
}
|
||||
}
|
||||
|
||||
export type StepType = "ZOOM_PERCENTAGE";
|
||||
|
||||
export interface StepControlProps extends ControlProps {
|
||||
stepType: StepType;
|
||||
}
|
||||
|
||||
export default StepControl;
|
||||
|
|
@ -225,6 +225,7 @@ export const StyledSwitch = styled(Switch)`
|
|||
`;
|
||||
|
||||
export const StyledDynamicInput = styled.div`
|
||||
width: 100%;
|
||||
&&& {
|
||||
input {
|
||||
border: none;
|
||||
|
|
@ -248,14 +249,11 @@ export const StyledInputGroup = styled(InputGroup)`
|
|||
`;
|
||||
|
||||
export const StyledDatePicker = styled(DateInput)`
|
||||
& {
|
||||
input {
|
||||
placeholderText: ${props => props.placeholder};
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
> input {
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
border: 1px solid green;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledTimeZonePicker = styled(TimezonePicker)`
|
||||
|
|
|
|||
180
app/client/src/components/propertyControls/TabControl.tsx
Normal file
180
app/client/src/components/propertyControls/TabControl.tsx
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import { StyledInputGroup, StyledPropertyPaneButton } from "./StyledControls";
|
||||
import { ControlType } from "constants/PropertyControlConstants";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { FormIcons } from "icons/FormIcons";
|
||||
import { ControlIcons } from "icons/ControlIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
import { DragDropContext } from "react-beautiful-dnd";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { DroppableComponent } from "../designSystems/appsmith/DraggableListComponent";
|
||||
|
||||
const StyledDeleteIcon = styled(FormIcons.DELETE_ICON as AnyStyledComponent)`
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledDragIcon = styled(
|
||||
ControlIcons.DRAGGABLE_CONTROL as AnyStyledComponent,
|
||||
)`
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin-left: 15px;
|
||||
cursor: move;
|
||||
svg {
|
||||
path {
|
||||
fill: ${props => props.theme.colors.paneSectionLabel};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledPropertyPaneButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
|
||||
margin-right: 2px;
|
||||
&&& {
|
||||
input {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
&:focus {
|
||||
border: none;
|
||||
color: ${props => props.theme.colors.textOnDarkBG};
|
||||
background: ${props => props.theme.colors.paneInputBG};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
};
|
||||
|
||||
function TabControlComponent(props: RenderComponentProps) {
|
||||
const { deleteOption, updateOption, item, index } = props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<StyledOptionControlInputGroup
|
||||
type="text"
|
||||
placeholder="Tab Title"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateOption(index, event.target.value);
|
||||
}}
|
||||
defaultValue={item.label}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
height={20}
|
||||
width={20}
|
||||
onClick={() => {
|
||||
deleteOption(index);
|
||||
}}
|
||||
/>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
class TabControl extends BaseControl<ControlProps> {
|
||||
onDragEnd = (result: any) => {
|
||||
const { destination, source } = result;
|
||||
if (!destination) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
destination.droppableId === source.droppableId &&
|
||||
destination.index === source.index
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const tabs: string[] = this.props.propertyValue || [""];
|
||||
const sourceTab = tabs[source.index];
|
||||
const destinationTab = tabs[destination.index];
|
||||
this.updateProperty(
|
||||
this.props.propertyName,
|
||||
tabs.map((tab, index) => {
|
||||
if (index === source.index) {
|
||||
return destinationTab;
|
||||
} else if (index === destination.index) {
|
||||
return sourceTab;
|
||||
}
|
||||
return tab;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const tabs: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
}> = this.props.propertyValue || [{ id: "" }];
|
||||
return (
|
||||
<DragDropContext onDragEnd={this.onDragEnd}>
|
||||
<DroppableComponent
|
||||
items={tabs}
|
||||
renderComponent={TabControlComponent}
|
||||
deleteOption={this.deleteOption}
|
||||
updateOption={this.updateOption}
|
||||
/>
|
||||
<StyledPropertyPaneButtonWrapper>
|
||||
<StyledPropertyPaneButton
|
||||
text="Add a Tab"
|
||||
color="#FFFFFF"
|
||||
minimal
|
||||
onClick={this.addOption}
|
||||
/>
|
||||
</StyledPropertyPaneButtonWrapper>
|
||||
</DragDropContext>
|
||||
);
|
||||
}
|
||||
|
||||
deleteOption = (index: number) => {
|
||||
const tabs: object[] = this.props.propertyValue.slice();
|
||||
tabs.splice(index, 1);
|
||||
this.updateProperty(this.props.propertyName, tabs);
|
||||
};
|
||||
|
||||
updateOption = (index: number, updatedLabel: string) => {
|
||||
const tabs: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
}> = this.props.propertyValue;
|
||||
this.updateProperty(
|
||||
this.props.propertyName,
|
||||
tabs.map((tab, tabIndex) => {
|
||||
if (index === tabIndex) {
|
||||
tab.label = updatedLabel;
|
||||
}
|
||||
return tab;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
addOption = () => {
|
||||
const tabs: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
}> = this.props.propertyValue ? this.props.propertyValue.slice() : [];
|
||||
const newTabId = generateReactKey({ prefix: "tab" });
|
||||
tabs.push({ id: newTabId, label: `Tab ${tabs.length + 1}` });
|
||||
this.updateProperty(this.props.propertyName, tabs);
|
||||
};
|
||||
|
||||
getControlType(): ControlType {
|
||||
return "TABS_INPUT";
|
||||
}
|
||||
}
|
||||
|
||||
export default TabControl;
|
||||
|
|
@ -18,9 +18,11 @@ import MultiSelectControl, {
|
|||
import DatePickerControl, {
|
||||
DatePickerControlProps,
|
||||
} from "components/propertyControls/DatePickerControl";
|
||||
import TimeZoneControl, {
|
||||
TimeZoneControlProps,
|
||||
} from "components/propertyControls/TimezoneControl";
|
||||
import ChartDataControl from "components/propertyControls/ChartDataControl";
|
||||
import LocationSearchControl from "components/propertyControls/LocationSearchControl";
|
||||
import StepControl, {
|
||||
StepControlProps,
|
||||
} from "components/propertyControls/StepControl";
|
||||
import ActionSelectorControl from "components/propertyControls/ActionSelectorControl";
|
||||
import ColumnActionSelectorControl from "components/propertyControls/ColumnActionSelectorControl";
|
||||
import MultiSwitchControl, {
|
||||
|
|
@ -35,10 +37,12 @@ export const PropertyControls = {
|
|||
CodeEditorControl,
|
||||
MultiSelectControl,
|
||||
DatePickerControl,
|
||||
TimeZoneControl,
|
||||
ActionSelectorControl,
|
||||
ColumnActionSelectorControl,
|
||||
MultiSwitchControl,
|
||||
ChartDataControl,
|
||||
LocationSearchControl,
|
||||
StepControl,
|
||||
};
|
||||
|
||||
export type PropertyControlPropsType =
|
||||
|
|
@ -48,8 +52,8 @@ export type PropertyControlPropsType =
|
|||
| SwitchControlProps
|
||||
| MultiSelectControlProps
|
||||
| DatePickerControlProps
|
||||
| TimeZoneControlProps
|
||||
| MultiSwitchControlProps;
|
||||
| MultiSwitchControlProps
|
||||
| StepControlProps;
|
||||
|
||||
export const getPropertyControlTypes = (): { [key: string]: string } => {
|
||||
const _types: { [key: string]: string } = {};
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ export enum EventType {
|
|||
ON_DATE_SELECTED = "ON_DATE_SELECTED",
|
||||
ON_DATE_RANGE_SELECTED = "ON_DATE_RANGE_SELECTED",
|
||||
ON_OPTION_CHANGE = "ON_OPTION_CHANGE",
|
||||
ON_MARKER_CLICK = "ON_MARKER_CLICK",
|
||||
ON_CREATE_MARKER = "ON_CREATE_MARKER",
|
||||
}
|
||||
|
||||
export type ActionType =
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ export const theme: Theme = {
|
|||
paneInputBG: Colors.SHARK,
|
||||
paneBG: Colors.OUTER_SPACE,
|
||||
paneText: Colors.GRAY_CHATEAU,
|
||||
paneTextBG: Colors.DEEP_SPACE,
|
||||
paneTextUnderline: Colors.LIGHT_GREYISH_BLUE,
|
||||
paneSectionLabel: Colors.CADET_BLUE,
|
||||
navBG: Colors.SHARK,
|
||||
|
|
@ -414,6 +415,8 @@ export const theme: Theme = {
|
|||
shadows: [
|
||||
"0px 2px 4px rgba(67, 70, 74, 0.14)",
|
||||
`0px 2px 4px ${Colors.MYSTIC}`,
|
||||
`inset -1px 0px 0px ${Colors.ATHENS_GRAY}, inset 1px 0px 0px ${Colors.ATHENS_GRAY}, inset 0px 4px 0px ${Colors.GREEN}`,
|
||||
`inset -1px 0px 0px ${Colors.ATHENS_GRAY}, inset 1px 0px 0px ${Colors.ATHENS_GRAY}, inset 0px 1px 0px ${Colors.ATHENS_GRAY}`,
|
||||
],
|
||||
widgets: {
|
||||
tableWidget: {
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ export type WidgetType =
|
|||
| "RADIO_GROUP_WIDGET"
|
||||
| "FILE_PICKER_WIDGET"
|
||||
| "INPUT_WIDGET"
|
||||
| "TABS_WIDGET"
|
||||
| "CHART_WIDGET"
|
||||
| "MODAL_WIDGET"
|
||||
| "FORM_WIDGET"
|
||||
| "FORM_BUTTON_WIDGET"
|
||||
| "MAP_WIDGET"
|
||||
| "CANVAS_WIDGET"
|
||||
| "ICON_WIDGET";
|
||||
|
||||
|
|
@ -29,11 +31,13 @@ export const WidgetTypes: { [id: string]: WidgetType } = {
|
|||
DROP_DOWN_WIDGET: "DROP_DOWN_WIDGET",
|
||||
CHECKBOX_WIDGET: "CHECKBOX_WIDGET",
|
||||
RADIO_GROUP_WIDGET: "RADIO_GROUP_WIDGET",
|
||||
TABS_WIDGET: "TABS_WIDGET",
|
||||
MODAL_WIDGET: "MODAL_WIDGET",
|
||||
RICH_TEXT_EDITOR_WIDGET: "RICH_TEXT_EDITOR_WIDGET",
|
||||
CHART_WIDGET: "CHART_WIDGET",
|
||||
FORM_WIDGET: "FORM_WIDGET",
|
||||
FORM_BUTTON_WIDGET: "FORM_BUTTON_WIDGET",
|
||||
MAP_WIDGET: "MAP_WIDGET",
|
||||
CANVAS_WIDGET: "CANVAS_WIDGET",
|
||||
ICON_WIDGET: "ICON_WIDGET",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export const VALIDATION_TYPES = {
|
|||
OPTIONS_DATA: "OPTIONS_DATA",
|
||||
DATE: "DATE",
|
||||
CHART_DATA: "CHART_DATA",
|
||||
MARKERS: "MARKERS",
|
||||
ACTION_SELECTOR: "ACTION_SELECTOR",
|
||||
ARRAY_ACTION_SELECTOR: "ARRAY_ACTION_SELECTOR",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -129,3 +129,6 @@ export const INPUT_WIDGET_DEFAULT_VALIDATION_ERROR = "Invalid input";
|
|||
export const AUTOFIT_ALL_COLUMNS = "Autofit all columns";
|
||||
export const AUTOFIT_THIS_COLUMN = "Autofit this column";
|
||||
export const AUTOFIT_COLUMN = "Autofit column";
|
||||
|
||||
export const TIMEZONE = "Timezone";
|
||||
export const ENABLE_TIME = "Enable Time";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@ import { ReactComponent as ViewIcon } from "assets/icons/control/view.svg";
|
|||
import { ReactComponent as MoreVerticalIcon } from "assets/icons/control/more-vertical.svg";
|
||||
import { ReactComponent as OverflowMenuIcon } from "assets/icons/menu/overflow-menu.svg";
|
||||
import { ReactComponent as JsToggleIcon } from "assets/icons/control/js-toggle.svg";
|
||||
import { ReactComponent as IncreaseIcon } from "assets/icons/control/increase.svg";
|
||||
import { ReactComponent as DecreaseIcon } from "assets/icons/control/decrease.svg";
|
||||
|
||||
import { ReactComponent as DraggableIcon } from "assets/icons/control/draggable.svg";
|
||||
import { ReactComponent as CloseIcon } from "assets/icons/control/close.svg";
|
||||
import { ReactComponent as PickMyLocationIcon } from "assets/icons/control/pick-my-location.svg";
|
||||
/* eslint-disable react/display-name */
|
||||
|
||||
export const ControlIcons: {
|
||||
|
|
@ -48,11 +53,31 @@ export const ControlIcons: {
|
|||
<JsToggleIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
INCREASE_CONTROL: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<IncreaseIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
DECREASE_CONTROL: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<DecreaseIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
DRAGGABLE_CONTROL: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<DraggableIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
CLOSE_CONTROL: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<CloseIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
PICK_MY_LOCATION_CONTROL: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<PickMyLocationIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
};
|
||||
|
||||
export type ControlIconName = keyof typeof ControlIcons;
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ import { ReactComponent as SwitchIcon } from "assets/icons/widget/switch.svg";
|
|||
import { ReactComponent as TextIcon } from "assets/icons/widget/text.svg";
|
||||
import { ReactComponent as ImageIcon } from "assets/icons/widget/image.svg";
|
||||
import { ReactComponent as FilePickerIcon } from "assets/icons/widget/filepicker.svg";
|
||||
import { ReactComponent as TabsIcon } from "assets/icons/widget/tabs.svg";
|
||||
import { ReactComponent as RichTextEditorIcon } from "assets/icons/widget/rich-text.svg";
|
||||
import { ReactComponent as ChartIcon } from "assets/icons/widget/chart.svg";
|
||||
import { ReactComponent as FormIcon } from "assets/icons/widget/form.svg";
|
||||
import { ReactComponent as MapIcon } from "assets/icons/widget/map.svg";
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
|
||||
|
|
@ -98,6 +100,11 @@ export const WidgetIcons: {
|
|||
<FilePickerIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
TABS_WIDGET: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<TabsIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
CHART_WIDGET: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<ChartIcon />
|
||||
|
|
@ -108,6 +115,11 @@ export const WidgetIcons: {
|
|||
<FormIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
MAP_WIDGET: (props: IconProps) => (
|
||||
<IconWrapper {...props}>
|
||||
<MapIcon />
|
||||
</IconWrapper>
|
||||
),
|
||||
};
|
||||
|
||||
export type WidgetIcon = typeof WidgetIcons[keyof typeof WidgetIcons];
|
||||
|
|
|
|||
|
|
@ -318,6 +318,12 @@ const PropertyPaneConfigResponse = {
|
|||
errorMessage: "Must be a valid string",
|
||||
controlType: "INPUT_TEXT",
|
||||
},
|
||||
{
|
||||
id: "16.1.3",
|
||||
propertyName: "shouldScrollContents",
|
||||
label: "Scroll Contents",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -404,47 +410,6 @@ const PropertyPaneConfigResponse = {
|
|||
],
|
||||
},
|
||||
],
|
||||
MODAL_WIDGET: [
|
||||
{
|
||||
sectionName: "General",
|
||||
id: "18.1",
|
||||
children: [
|
||||
{
|
||||
id: "18.1.1",
|
||||
propertyName: "canOutsideClickClose",
|
||||
label: "Quick Dismiss",
|
||||
helpText: "Allows dismissing the modal when user taps outside",
|
||||
validationType: "BOOLEAN",
|
||||
errorMessage: "Must be a valid boolean value",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "18.1.2",
|
||||
propertyName: "size",
|
||||
label: "Modal Type",
|
||||
controlType: "DROP_DOWN",
|
||||
options: [
|
||||
{
|
||||
label: "Form Modal",
|
||||
value: "MODAL_LARGE",
|
||||
},
|
||||
{
|
||||
label: "Alert Modal",
|
||||
value: "MODAL_SMALL",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "18.1.3",
|
||||
propertyName: "shouldScrollContents",
|
||||
label: "Scroll Contents",
|
||||
validationType: "BOOLEAN",
|
||||
errorMessage: "Must be a valid boolean value",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
INPUT_WIDGET: [
|
||||
{
|
||||
id: "4.1",
|
||||
|
|
@ -758,63 +723,6 @@ const PropertyPaneConfigResponse = {
|
|||
],
|
||||
},
|
||||
],
|
||||
MAP_WIDGET: [
|
||||
{
|
||||
sectionName: "General",
|
||||
id: "19",
|
||||
children: [
|
||||
{
|
||||
id: "19.1",
|
||||
propertyName: "mapCenter",
|
||||
label: "Initial location",
|
||||
controlType: "LOCATION_SEARCH",
|
||||
},
|
||||
{
|
||||
id: "19.2",
|
||||
propertyName: "enableSearch",
|
||||
label: "Enable search location",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "19.3",
|
||||
propertyName: "zoomToLocation",
|
||||
label: "Zoom to location",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "19.4",
|
||||
propertyName: "defaultMarkers",
|
||||
label: "Default markers",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "ARRAY",
|
||||
},
|
||||
{
|
||||
id: "19.5",
|
||||
propertyName: "location",
|
||||
label: "Creat new marker",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "ZOOM SETTINGS",
|
||||
id: "20",
|
||||
children: [
|
||||
{
|
||||
id: "20.1",
|
||||
propertyName: "zoomLevel",
|
||||
label: "Zoom Level",
|
||||
controlType: "PERCENTAGE_LEVEL",
|
||||
},
|
||||
{
|
||||
id: "20.2",
|
||||
propertyName: "allowZoom",
|
||||
label: "Zoomable",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
BUTTON_WIDGET: [
|
||||
{
|
||||
id: "1.1",
|
||||
|
|
@ -1232,6 +1140,113 @@ const PropertyPaneConfigResponse = {
|
|||
],
|
||||
},
|
||||
],
|
||||
MODAL_WIDGET: [
|
||||
{
|
||||
sectionName: "General",
|
||||
id: "24.1",
|
||||
children: [
|
||||
{
|
||||
id: "24.1.1",
|
||||
propertyName: "canOutsideClickClose",
|
||||
label: "Close on blur",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "24.1.3",
|
||||
propertyName: "canEscapeKeyClose",
|
||||
label: "Close on ESC",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "24.1.4",
|
||||
propertyName: "size",
|
||||
label: "Size",
|
||||
controlType: "DROP_DOWN",
|
||||
options: [
|
||||
{
|
||||
label: "Large",
|
||||
value: "MODAL_LARGE",
|
||||
},
|
||||
{
|
||||
label: "Small",
|
||||
value: "MODAL_SMALL",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
MAP_WIDGET: [
|
||||
{
|
||||
sectionName: "General",
|
||||
id: "25",
|
||||
children: [
|
||||
{
|
||||
id: "25.1",
|
||||
propertyName: "mapCenter",
|
||||
label: "Initial location",
|
||||
controlType: "LOCATION_SEARCH",
|
||||
},
|
||||
{
|
||||
id: "25.2",
|
||||
propertyName: "enableSearch",
|
||||
label: "Enable search location",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "25.3",
|
||||
propertyName: "enablePickLocation",
|
||||
label: "Enable pick location",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
{
|
||||
id: "25.4",
|
||||
propertyName: "defaultMarkers",
|
||||
label: "Default markers",
|
||||
controlType: "INPUT_TEXT",
|
||||
inputType: "ARRAY",
|
||||
},
|
||||
{
|
||||
id: "25.5",
|
||||
propertyName: "enableCreateMarker",
|
||||
label: "Create new marker",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "ZOOM SETTINGS",
|
||||
id: "26",
|
||||
children: [
|
||||
{
|
||||
id: "26.1",
|
||||
propertyName: "zoomLevel",
|
||||
label: "Zoom Level",
|
||||
controlType: "STEP",
|
||||
stepType: "ZOOM_PERCENTAGE",
|
||||
},
|
||||
{
|
||||
id: "26.2",
|
||||
propertyName: "allowZoom",
|
||||
label: "Zoomable",
|
||||
controlType: "SWITCH",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "27",
|
||||
sectionName: "Actions",
|
||||
children: [
|
||||
{
|
||||
id: "27.1",
|
||||
propertyName: "onMarkerClick",
|
||||
label: "onMarkerClick",
|
||||
controlType: "ACTION_SELECTOR",
|
||||
isJSConvertible: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
ICON_WIDGET: [
|
||||
{
|
||||
id: "25.1",
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
widgetName: "Text",
|
||||
},
|
||||
RICH_TEXT_EDITOR_WIDGET: {
|
||||
text: "This is the initial <b>content</b> of the editor",
|
||||
defaultText: "This is the initial <b>content</b> of the editor",
|
||||
rows: 5,
|
||||
columns: 8,
|
||||
isDisabled: false,
|
||||
isVisible: true,
|
||||
widgetName: "RichTextEditor",
|
||||
isDefaultClickDisabled: true,
|
||||
},
|
||||
IMAGE_WIDGET: {
|
||||
defaultImage:
|
||||
|
|
@ -79,7 +80,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
},
|
||||
},
|
||||
DATE_PICKER_WIDGET: {
|
||||
enableTimePicker: true,
|
||||
isDisabled: false,
|
||||
datePickerType: "DATE_PICKER",
|
||||
rows: 1,
|
||||
|
|
@ -87,6 +87,11 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
columns: 8,
|
||||
label: "Date",
|
||||
widgetName: "DatePicker",
|
||||
defaultDate: {
|
||||
dateValue: new Date().getTime(),
|
||||
timezone: "",
|
||||
isTimeEnabled: true,
|
||||
},
|
||||
},
|
||||
TABLE_WIDGET: {
|
||||
rows: 7,
|
||||
|
|
@ -181,6 +186,45 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
widgetName: "FilePicker",
|
||||
isDefaultClickDisabled: true,
|
||||
},
|
||||
TABS_WIDGET: {
|
||||
rows: 7,
|
||||
columns: 8,
|
||||
shouldScrollContents: false,
|
||||
widgetName: "Tabs",
|
||||
tabs: [
|
||||
{ label: "Tab 1", id: "tab1" },
|
||||
{ label: "Tab 2", id: "tab2" },
|
||||
],
|
||||
selectedTab: "Tab 1",
|
||||
blueprint: {
|
||||
view: [
|
||||
{
|
||||
type: "CANVAS_WIDGET",
|
||||
position: { top: 0, left: 0 },
|
||||
size: { rows: 6, cols: 16 },
|
||||
props: {
|
||||
containerStyle: "none",
|
||||
canExtend: false,
|
||||
detachFromLayout: true,
|
||||
children: [],
|
||||
tabId: "tab1",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "CANVAS_WIDGET",
|
||||
position: { top: 0, left: 0 },
|
||||
size: { rows: 6, cols: 16 },
|
||||
props: {
|
||||
containerStyle: "none",
|
||||
canExtend: false,
|
||||
detachFromLayout: true,
|
||||
children: [],
|
||||
tabId: "tab2",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
MODAL_WIDGET: {
|
||||
rows: 456,
|
||||
columns: 456,
|
||||
|
|
@ -286,34 +330,40 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
widgetName: "Chart",
|
||||
chartType: "LINE_CHART",
|
||||
chartName: "Sales on working days",
|
||||
allowHorizontalScroll: false,
|
||||
chartData: [
|
||||
{
|
||||
x: "Mon",
|
||||
y: 10000,
|
||||
},
|
||||
{
|
||||
x: "Tue",
|
||||
y: 12000,
|
||||
},
|
||||
{
|
||||
x: "Wed",
|
||||
y: 32000,
|
||||
},
|
||||
{
|
||||
x: "Thu",
|
||||
y: 28000,
|
||||
},
|
||||
{
|
||||
x: "Fri",
|
||||
y: 14000,
|
||||
},
|
||||
{
|
||||
x: "Sat",
|
||||
y: 19000,
|
||||
},
|
||||
{
|
||||
x: "Sun",
|
||||
y: 36000,
|
||||
seriesName: "",
|
||||
data: [
|
||||
{
|
||||
x: "Mon",
|
||||
y: 10000,
|
||||
},
|
||||
{
|
||||
x: "Tue",
|
||||
y: 12000,
|
||||
},
|
||||
{
|
||||
x: "Wed",
|
||||
y: 32000,
|
||||
},
|
||||
{
|
||||
x: "Thu",
|
||||
y: 28000,
|
||||
},
|
||||
{
|
||||
x: "Fri",
|
||||
y: 14000,
|
||||
},
|
||||
{
|
||||
x: "Sat",
|
||||
y: 19000,
|
||||
},
|
||||
{
|
||||
x: "Sun",
|
||||
y: 36000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
xAxisName: "Last Week",
|
||||
|
|
@ -382,6 +432,20 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
|||
],
|
||||
},
|
||||
},
|
||||
MAP_WIDGET: {
|
||||
rows: 12,
|
||||
columns: 8,
|
||||
isDisabled: false,
|
||||
isVisible: true,
|
||||
widgetName: "Map",
|
||||
enableSearch: true,
|
||||
enableCreateMarker: true,
|
||||
zoomLevel: 50,
|
||||
enablePickLocation: true,
|
||||
allowZoom: true,
|
||||
mapCenter: { lat: -34.397, lng: 150.644 },
|
||||
defaultMarkers: [{ lat: -34.397, lng: 150.644, title: "Test A" }],
|
||||
},
|
||||
},
|
||||
configVersion: 1,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -106,6 +106,11 @@ const WidgetSidebarResponse: {
|
|||
widgetCardName: "Chart",
|
||||
key: generateReactKey(),
|
||||
},
|
||||
{
|
||||
type: "MAP_WIDGET",
|
||||
widgetCardName: "Map",
|
||||
key: generateReactKey(),
|
||||
},
|
||||
],
|
||||
["Layout widgets"]: [
|
||||
{
|
||||
|
|
@ -113,6 +118,11 @@ const WidgetSidebarResponse: {
|
|||
widgetCardName: "Container",
|
||||
key: generateReactKey(),
|
||||
},
|
||||
{
|
||||
type: "TABS_WIDGET",
|
||||
widgetCardName: "Tabs",
|
||||
key: generateReactKey(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,14 @@ import { CheckboxWidgetProps } from "../../widgets/CheckboxWidget";
|
|||
import { RadioGroupWidgetProps } from "../../widgets/RadioGroupWidget";
|
||||
import { AlertWidgetProps } from "../../widgets/AlertWidget";
|
||||
import { FilePickerWidgetProps } from "../../widgets/FilepickerWidget";
|
||||
import {
|
||||
TabsWidgetProps,
|
||||
TabContainerWidgetProps,
|
||||
} from "../../widgets/TabsWidget";
|
||||
import { ChartWidgetProps } from "../../widgets/ChartWidget";
|
||||
import { FormWidgetProps } from "widgets/FormWidget";
|
||||
import { FormButtonWidgetProps } from "widgets/FormButtonWidget";
|
||||
import { MapWidgetProps } from "widgets/MapWidget";
|
||||
import { ModalWidgetProps } from "widgets/ModalWidget";
|
||||
import { IconWidgetProps } from "widgets/IconWidget";
|
||||
|
||||
|
|
@ -56,10 +61,13 @@ export interface WidgetConfigReducerState {
|
|||
RADIO_GROUP_WIDGET: Partial<RadioGroupWidgetProps> & WidgetConfigProps;
|
||||
ALERT_WIDGET: Partial<AlertWidgetProps> & WidgetConfigProps;
|
||||
FILE_PICKER_WIDGET: Partial<FilePickerWidgetProps> & WidgetConfigProps;
|
||||
TABS_WIDGET: Partial<TabsWidgetProps<TabContainerWidgetProps>> &
|
||||
WidgetConfigProps;
|
||||
MODAL_WIDGET: Partial<ModalWidgetProps> & WidgetConfigProps;
|
||||
CHART_WIDGET: Partial<ChartWidgetProps> & WidgetConfigProps;
|
||||
FORM_WIDGET: Partial<FormWidgetProps> & WidgetConfigProps;
|
||||
FORM_BUTTON_WIDGET: Partial<FormButtonWidgetProps> & WidgetConfigProps;
|
||||
MAP_WIDGET: Partial<MapWidgetProps> & WidgetConfigProps;
|
||||
CANVAS_WIDGET: Partial<ContainerWidgetProps<WidgetProps>> &
|
||||
WidgetConfigProps;
|
||||
ICON_WIDGET: Partial<IconWidgetProps> & WidgetConfigProps;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ export function* createModalSaga(action: ReduxAction<{ modalName: string }>) {
|
|||
topRow: 0,
|
||||
columns: 0,
|
||||
rows: 0,
|
||||
tabId: "",
|
||||
};
|
||||
yield put({
|
||||
type: ReduxActionTypes.WIDGET_ADD_CHILD,
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ function* getChildWidgetProps(
|
|||
}
|
||||
|
||||
const widgetProps = { ...defaultConfig, ...props, columns, rows, minHeight };
|
||||
|
||||
const widget = generateWidgetProps(
|
||||
parent,
|
||||
type,
|
||||
|
|
@ -257,7 +256,6 @@ export function* resizeSaga(resizeAction: ReduxAction<WidgetResize>) {
|
|||
|
||||
widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow };
|
||||
widgets[widgetId] = widget;
|
||||
|
||||
yield put(updateAndSaveLayout(widgets));
|
||||
} catch (error) {
|
||||
yield put({
|
||||
|
|
|
|||
|
|
@ -211,6 +211,23 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
|
|||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.MARKERS]: (value: any): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](value);
|
||||
if (!isValid) {
|
||||
return {
|
||||
isValid,
|
||||
parsed,
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
} else if (!_.every(parsed, datum => _.isObject(datum))) {
|
||||
return {
|
||||
isValid: false,
|
||||
parsed: [],
|
||||
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Marker Data`,
|
||||
};
|
||||
}
|
||||
return { isValid, parsed };
|
||||
},
|
||||
[VALIDATION_TYPES.OPTIONS_DATA]: (value: any): ValidationResponse => {
|
||||
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](value);
|
||||
if (!isValid) {
|
||||
|
|
|
|||
|
|
@ -359,7 +359,6 @@ export const generateWidgetProps = (
|
|||
...derivedProperties,
|
||||
parentId: parent.widgetId,
|
||||
};
|
||||
|
||||
delete props.rows;
|
||||
delete props.columns;
|
||||
return props;
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@ import ButtonWidget, { ButtonWidgetProps } from "widgets/ButtonWidget";
|
|||
import DropdownWidget, { DropdownWidgetProps } from "widgets/DropdownWidget";
|
||||
import ImageWidget, { ImageWidgetProps } from "widgets/ImageWidget";
|
||||
import TableWidget, { TableWidgetProps } from "widgets/TableWidget";
|
||||
import TabsWidget, {
|
||||
TabsWidgetProps,
|
||||
TabContainerWidgetProps,
|
||||
} from "widgets/TabsWidget";
|
||||
import ModalWidget, { ModalWidgetProps } from "widgets/ModalWidget";
|
||||
import RichTextEditorWidget, {
|
||||
RichTextEditorWidgetProps,
|
||||
} from "widgets/RichTextEditorWidget";
|
||||
import ChartWidget, { ChartWidgetProps } from "widgets/ChartWidget";
|
||||
import MapWidget, { MapWidgetProps } from "widgets/MapWidget";
|
||||
|
||||
import FilePickerWidget, {
|
||||
FilePickerWidgetProps,
|
||||
|
|
@ -165,6 +170,19 @@ class WidgetBuilderRegistry {
|
|||
DatePickerWidget.getDerivedPropertiesMap(),
|
||||
DatePickerWidget.getTriggerPropertyMap(),
|
||||
);
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
"TABS_WIDGET",
|
||||
{
|
||||
buildWidget(
|
||||
widgetProps: TabsWidgetProps<TabContainerWidgetProps>,
|
||||
): JSX.Element {
|
||||
return <TabsWidget {...widgetProps} />;
|
||||
},
|
||||
},
|
||||
TabsWidget.getPropertyValidationMap(),
|
||||
TabsWidget.getDerivedPropertiesMap(),
|
||||
TabsWidget.getTriggerPropertyMap(),
|
||||
);
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
WidgetTypes.MODAL_WIDGET,
|
||||
{
|
||||
|
|
@ -224,6 +242,18 @@ class WidgetBuilderRegistry {
|
|||
FormButtonWidget.getTriggerPropertyMap(),
|
||||
);
|
||||
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
"MAP_WIDGET",
|
||||
{
|
||||
buildWidget(widgetProps: MapWidgetProps): JSX.Element {
|
||||
return <MapWidget {...widgetProps} />;
|
||||
},
|
||||
},
|
||||
MapWidget.getPropertyValidationMap(),
|
||||
MapWidget.getDerivedPropertiesMap(),
|
||||
MapWidget.getTriggerPropertyMap(),
|
||||
);
|
||||
|
||||
WidgetFactory.registerWidgetBuilder(
|
||||
WidgetTypes.CANVAS_WIDGET,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -91,6 +91,15 @@ abstract class BaseWidget<
|
|||
disableDrag && disable !== undefined && disableDrag(disable);
|
||||
}
|
||||
|
||||
updateWidget(
|
||||
operationName: string,
|
||||
widgetId: string,
|
||||
widgetProperties: any,
|
||||
): void {
|
||||
const { updateWidget } = this.context;
|
||||
updateWidget && updateWidget(operationName, widgetId, widgetProperties);
|
||||
}
|
||||
|
||||
updateWidgetProperty(propertyName: string, propertyValue: any): void {
|
||||
const { updateWidgetProperty } = this.context;
|
||||
const { widgetId } = this.props;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
|||
class ChartWidget extends BaseWidget<ChartWidgetProps, WidgetState> {
|
||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||
return {
|
||||
chartData: VALIDATION_TYPES.CHART_DATA,
|
||||
xAxisName: VALIDATION_TYPES.TEXT,
|
||||
yAxisName: VALIDATION_TYPES.TEXT,
|
||||
chartName: VALIDATION_TYPES.TEXT,
|
||||
|
|
@ -27,6 +26,7 @@ class ChartWidget extends BaseWidget<ChartWidgetProps, WidgetState> {
|
|||
chartName={this.props.chartName}
|
||||
chartData={this.props.chartData}
|
||||
widgetId={this.props.widgetId}
|
||||
allowHorizontalScroll={this.props.allowHorizontalScroll}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -44,11 +44,16 @@ export type ChartType =
|
|||
| "AREA_CHART"
|
||||
| "SCATTER_CHART";
|
||||
|
||||
export interface ChartData {
|
||||
export interface ChartDataPoint {
|
||||
x: any;
|
||||
y: any;
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
seriesName?: string;
|
||||
data: ChartDataPoint[];
|
||||
}
|
||||
|
||||
export interface ChartWidgetProps extends WidgetProps {
|
||||
chartType: ChartType;
|
||||
chartData: ChartData[];
|
||||
|
|
@ -56,6 +61,7 @@ export interface ChartWidgetProps extends WidgetProps {
|
|||
yAxisName: string;
|
||||
chartName: string;
|
||||
isVisible?: boolean;
|
||||
allowHorizontalScroll: boolean;
|
||||
}
|
||||
|
||||
export default ChartWidget;
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ import {
|
|||
DerivedPropertiesMap,
|
||||
TriggerPropertiesMap,
|
||||
} from "utils/WidgetFactory";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
|
||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||
return {
|
||||
...BASE_WIDGET_VALIDATION,
|
||||
defaultDate: VALIDATION_TYPES.DATE,
|
||||
timezone: VALIDATION_TYPES.TEXT,
|
||||
enableTimePicker: VALIDATION_TYPES.BOOLEAN,
|
||||
dateFormat: VALIDATION_TYPES.TEXT,
|
||||
|
|
@ -43,18 +43,30 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
|
|||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
if (this.props.defaultDate) {
|
||||
this.updateWidgetMetaProperty(
|
||||
"selectedDate",
|
||||
new Date(this.props.defaultDate.dateValue),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: DatePickerWidgetProps) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
if (this.props.defaultDate) {
|
||||
if (
|
||||
(this.props.selectedDate !== prevProps.selectedDate &&
|
||||
this.props.selectedDate === undefined) ||
|
||||
prevProps.defaultDate === undefined ||
|
||||
this.props.defaultDate.toDateString() !==
|
||||
prevProps.defaultDate.toDateString()
|
||||
) {
|
||||
this.updateWidgetMetaProperty("selectedDate", this.props.defaultDate);
|
||||
}
|
||||
if (
|
||||
this.props.defaultDate &&
|
||||
(prevProps.defaultDate === undefined ||
|
||||
!moment(this.props.defaultDate.dateValue).isSame(
|
||||
moment(prevProps.defaultDate.dateValue),
|
||||
"second",
|
||||
))
|
||||
) {
|
||||
this.updateWidgetMetaProperty(
|
||||
"selectedDate",
|
||||
new Date(this.props.defaultDate.dateValue),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,8 +76,16 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
|
|||
label={`${this.props.label}${this.props.isRequired ? " *" : ""}`}
|
||||
dateFormat={this.props.dateFormat}
|
||||
widgetId={this.props.widgetId}
|
||||
timezone={this.props.timezone}
|
||||
enableTimePicker={this.props.enableTimePicker}
|
||||
timezone={
|
||||
this.props.defaultDate && this.props.defaultDate.timezone
|
||||
? this.props.defaultDate.timezone
|
||||
: ""
|
||||
}
|
||||
enableTimePicker={
|
||||
this.props.defaultDate && this.props.defaultDate.isTimeEnabled
|
||||
? this.props.defaultDate.isTimeEnabled
|
||||
: false
|
||||
}
|
||||
isDisabled={this.props.isDisabled}
|
||||
datePickerType={"DATE_PICKER"}
|
||||
onDateSelected={this.onDateSelected}
|
||||
|
|
@ -95,10 +115,12 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
|
|||
export type DatePickerType = "DATE_PICKER" | "DATE_RANGE_PICKER";
|
||||
|
||||
export interface DatePickerWidgetProps extends WidgetProps {
|
||||
defaultDate: Date;
|
||||
defaultDate: {
|
||||
dateValue: number;
|
||||
isTimeEnabled: boolean;
|
||||
timezone: string;
|
||||
};
|
||||
selectedDate: Date;
|
||||
timezone?: string;
|
||||
enableTimePicker: boolean;
|
||||
isDisabled: boolean;
|
||||
dateFormat: string;
|
||||
label: string;
|
||||
|
|
|
|||
151
app/client/src/widgets/MapWidget.tsx
Normal file
151
app/client/src/widgets/MapWidget.tsx
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import React from "react";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import MapComponent from "components/designSystems/appsmith/MapComponent";
|
||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||
import { EventType } from "constants/ActionConstants";
|
||||
import { TriggerPropertiesMap } from "utils/WidgetFactory";
|
||||
|
||||
class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
|
||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||
return {
|
||||
defaultMarkers: VALIDATION_TYPES.MARKERS,
|
||||
isDisabled: VALIDATION_TYPES.BOOLEAN,
|
||||
isVisible: VALIDATION_TYPES.BOOLEAN,
|
||||
enableSearch: VALIDATION_TYPES.BOOLEAN,
|
||||
enablePickLocation: VALIDATION_TYPES.BOOLEAN,
|
||||
allowZoom: VALIDATION_TYPES.BOOLEAN,
|
||||
enableCreateMarker: VALIDATION_TYPES.BOOLEAN,
|
||||
zoomLevel: VALIDATION_TYPES.NUMBER,
|
||||
onMarkerClick: VALIDATION_TYPES.ACTION_SELECTOR,
|
||||
onCreateMarker: VALIDATION_TYPES.ACTION_SELECTOR,
|
||||
};
|
||||
}
|
||||
|
||||
static getTriggerPropertyMap(): TriggerPropertiesMap {
|
||||
return {
|
||||
onMarkerClick: true,
|
||||
onCreateMarker: true,
|
||||
};
|
||||
}
|
||||
|
||||
updateCenter = (lat: number, lng: number) => {
|
||||
this.updateWidgetMetaProperty("center", { lat, lng });
|
||||
};
|
||||
|
||||
onCreateMarker = (lat: number, lng: number) => {
|
||||
const markers: Array<MarkerProps> = [...this.props.markers];
|
||||
markers.push({ lat: lat, lng: lng });
|
||||
this.updateWidgetMetaProperty("markers", markers);
|
||||
if (this.props.onCreateMarker) {
|
||||
super.executeAction({
|
||||
dynamicString: this.props.onCreateMarker,
|
||||
event: {
|
||||
type: EventType.ON_CREATE_MARKER,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onMarkerClick = (lat: number, lng: number, title: string) => {
|
||||
this.updateWidgetMetaProperty("selectedMarker", {
|
||||
lat: lat,
|
||||
lng: lng,
|
||||
title: title,
|
||||
});
|
||||
if (this.props.onMarkerClick) {
|
||||
super.executeAction({
|
||||
dynamicString: this.props.onMarkerClick,
|
||||
event: {
|
||||
type: EventType.ON_MARKER_CLICK,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
if (this.props.mapCenter) {
|
||||
this.updateWidgetMetaProperty("center", this.props.mapCenter);
|
||||
}
|
||||
if (this.props.defaultMarkers && !this.props.markers) {
|
||||
this.updateWidgetMetaProperty("markers", [...this.props.defaultMarkers]);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: MapWidgetProps) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
if (
|
||||
this.props.mapCenter &&
|
||||
prevProps.mapCenter &&
|
||||
(this.props.mapCenter.lat !== prevProps.mapCenter.lat ||
|
||||
this.props.mapCenter.lng !== prevProps.mapCenter.lng)
|
||||
) {
|
||||
this.updateWidgetMetaProperty("center", this.props.mapCenter);
|
||||
}
|
||||
if (
|
||||
this.props.defaultMarkers &&
|
||||
!this.props.markers &&
|
||||
this.props.defaultMarkers.length !== prevProps.defaultMarkers?.length
|
||||
) {
|
||||
this.updateWidgetMetaProperty("markers", [...this.props.defaultMarkers]);
|
||||
}
|
||||
}
|
||||
|
||||
getPageView() {
|
||||
return (
|
||||
<MapComponent
|
||||
widgetId={this.props.widgetId}
|
||||
isVisible={this.props.isVisible}
|
||||
zoomLevel={this.props.zoomLevel}
|
||||
allowZoom={this.props.allowZoom}
|
||||
center={this.props.center || this.props.mapCenter}
|
||||
enableCreateMarker={this.props.enableCreateMarker}
|
||||
updateCenter={this.updateCenter}
|
||||
isDisabled={this.props.isDisabled}
|
||||
enableSearch={this.props.enableSearch}
|
||||
enablePickLocation={this.props.enablePickLocation}
|
||||
saveMarker={this.onCreateMarker}
|
||||
selectMarker={this.onMarkerClick}
|
||||
markers={this.props.markers || []}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
getWidgetType(): WidgetType {
|
||||
return "MAP_WIDGET";
|
||||
}
|
||||
}
|
||||
|
||||
export interface MarkerProps {
|
||||
lat: number;
|
||||
lng: number;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface MapWidgetProps extends WidgetProps {
|
||||
isDisabled?: boolean;
|
||||
isVisible?: boolean;
|
||||
enableSearch: boolean;
|
||||
zoomLevel: number;
|
||||
allowZoom: boolean;
|
||||
enablePickLocation: boolean;
|
||||
enableCreateMarker: boolean;
|
||||
mapCenter: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
};
|
||||
center?: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
};
|
||||
defaultMarkers?: Array<MarkerProps>;
|
||||
markers?: Array<MarkerProps>;
|
||||
selectedMarker?: MarkerProps;
|
||||
onMarkerClick?: string;
|
||||
onCreateMarker?: string;
|
||||
}
|
||||
|
||||
export default MapWidget;
|
||||
|
|
@ -29,22 +29,22 @@ class RichTextEditorWidget extends BaseWidget<
|
|||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
if (this.props.text) {
|
||||
this.updateWidgetMetaProperty("value", this.props.text);
|
||||
if (this.props.defaultText) {
|
||||
this.updateWidgetMetaProperty("text", this.props.defaultText);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: RichTextEditorWidgetProps) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
if (this.props.text) {
|
||||
if (this.props.text !== prevProps.text) {
|
||||
this.updateWidgetMetaProperty("value", this.props.text);
|
||||
if (this.props.defaultText) {
|
||||
if (this.props.defaultText !== prevProps.defaultText) {
|
||||
this.updateWidgetMetaProperty("text", this.props.defaultText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onValueChange = (value: string) => {
|
||||
this.updateWidgetMetaProperty("value", value);
|
||||
onValueChange = (text: string) => {
|
||||
this.updateWidgetMetaProperty("text", text);
|
||||
if (this.props.onTextChange) {
|
||||
super.executeAction({
|
||||
dynamicString: this.props.onTextChange,
|
||||
|
|
@ -59,7 +59,7 @@ class RichTextEditorWidget extends BaseWidget<
|
|||
return (
|
||||
<RichtextEditorComponent
|
||||
onValueChange={this.onValueChange}
|
||||
defaultValue={this.props.value}
|
||||
defaultValue={this.props.text}
|
||||
widgetId={this.props.widgetId}
|
||||
placeholder={this.props.placeholder}
|
||||
key={this.props.widgetId}
|
||||
|
|
@ -80,8 +80,8 @@ export interface InputValidator {
|
|||
}
|
||||
|
||||
export interface RichTextEditorWidgetProps extends WidgetProps {
|
||||
defaultText?: string;
|
||||
text?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
onTextChange?: string;
|
||||
isDisabled?: boolean;
|
||||
|
|
|
|||
196
app/client/src/widgets/TabsWidget.tsx
Normal file
196
app/client/src/widgets/TabsWidget.tsx
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import TabsComponent from "components/designSystems/appsmith/TabsComponent";
|
||||
import { WidgetType, WidgetTypes } from "constants/WidgetConstants";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
|
||||
const TabsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const ChildrenWrapper = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border: 1px solid;
|
||||
border-top: none;
|
||||
border-color: ${props => props.theme.colors.bodyBG};
|
||||
background: ${props => props.theme.colors.builderBodyBG};
|
||||
`;
|
||||
|
||||
const ScrollableCanvasWrapper = styled.div`
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
class TabsWidget extends BaseWidget<
|
||||
TabsWidgetProps<TabContainerWidgetProps>,
|
||||
WidgetState
|
||||
> {
|
||||
onTabChange = (tabId: string) => {
|
||||
this.updateWidgetMetaProperty("selectedTabId", tabId);
|
||||
};
|
||||
|
||||
getPageView() {
|
||||
return (
|
||||
<TabsContainer>
|
||||
<TabsComponent {...this.props} onTabChange={this.onTabChange} />
|
||||
<ChildrenWrapper>
|
||||
<ScrollableCanvasWrapper>
|
||||
{this.renderComponent()}
|
||||
</ScrollableCanvasWrapper>
|
||||
</ChildrenWrapper>
|
||||
</TabsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
renderComponent = () => {
|
||||
const selectedTabId = this.props.selectedTabId;
|
||||
const children = this.props.children.filter(item => {
|
||||
return selectedTabId === item.tabId;
|
||||
})[0];
|
||||
const childWidgetData: TabContainerWidgetProps = children;
|
||||
if (!childWidgetData) {
|
||||
return <div></div>;
|
||||
}
|
||||
childWidgetData.shouldScrollContents = false;
|
||||
childWidgetData.canExtend = this.props.shouldScrollContents;
|
||||
const { componentWidth, componentHeight } = this.getComponentDimensions();
|
||||
childWidgetData.rightColumn = componentWidth;
|
||||
childWidgetData.bottomRow = this.props.shouldScrollContents
|
||||
? (this.props.bottomRow - this.props.topRow - 1) *
|
||||
this.props.parentRowSpace
|
||||
: componentHeight;
|
||||
return WidgetFactory.createWidget(childWidgetData, this.props.renderMode);
|
||||
};
|
||||
|
||||
getWidgetType(): WidgetType {
|
||||
return "TABS_WIDGET";
|
||||
}
|
||||
|
||||
addTabContainer = () => {
|
||||
let tabId = "";
|
||||
const childrenTabIds: string[] = this.props.children.map(children => {
|
||||
return children.tabId;
|
||||
});
|
||||
for (let index = 0; index < this.props.tabs.length; index++) {
|
||||
const tab = this.props.tabs[index];
|
||||
if (!childrenTabIds.includes(tab.id)) {
|
||||
tabId = tab.id;
|
||||
}
|
||||
}
|
||||
const columns =
|
||||
(this.props.rightColumn - this.props.leftColumn) *
|
||||
this.props.parentColumnSpace;
|
||||
const rows =
|
||||
(this.props.bottomRow - this.props.topRow - 1) *
|
||||
this.props.parentRowSpace;
|
||||
const config = {
|
||||
type: WidgetTypes.CANVAS_WIDGET,
|
||||
columns: columns,
|
||||
rows: rows,
|
||||
parentRowSpace: 1,
|
||||
parentColumnSpace: 1,
|
||||
leftColumn: 0,
|
||||
topRow: 1,
|
||||
newWidgetId: generateReactKey(),
|
||||
widgetId: this.props.widgetId,
|
||||
props: {
|
||||
tabId: tabId,
|
||||
containerStyle: "none",
|
||||
canExtend: false,
|
||||
detachFromLayout: true,
|
||||
children: [],
|
||||
},
|
||||
};
|
||||
this.updateWidget("ADD_CHILD", this.props.widgetId, config);
|
||||
};
|
||||
|
||||
removeTabContainer = () => {
|
||||
let removedContainerWidgetId = "";
|
||||
const tabIds: string[] = this.props.tabs.map(tab => {
|
||||
return tab.id;
|
||||
});
|
||||
for (let index = 0; index < this.props.children.length; index++) {
|
||||
const children = this.props.children[index];
|
||||
if (!tabIds.includes(children.tabId)) {
|
||||
removedContainerWidgetId = children.widgetId;
|
||||
}
|
||||
}
|
||||
this.updateWidget("REMOVE_CHILD", removedContainerWidgetId, {
|
||||
parentId: this.props.widgetId,
|
||||
});
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: TabsWidgetProps<TabContainerWidgetProps>) {
|
||||
super.componentDidUpdate(prevProps);
|
||||
if (this.props.tabs) {
|
||||
if (
|
||||
this.props.tabs.length !== prevProps.tabs.length &&
|
||||
this.props.children.length !== this.props.tabs.length
|
||||
) {
|
||||
//adding container widget for the new tab
|
||||
if (this.props.tabs.length > this.props.children.length) {
|
||||
this.addTabContainer();
|
||||
}
|
||||
//removing container widget for the removed tab
|
||||
else {
|
||||
this.removeTabContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.props.selectedTab) {
|
||||
if (this.props.selectedTab !== prevProps.selectedTab) {
|
||||
let selectedTabId = "";
|
||||
for (let index = 0; index < this.props.tabs.length; index++) {
|
||||
if (this.props.tabs[index].label === this.props.selectedTab) {
|
||||
selectedTabId = this.props.tabs[index].id;
|
||||
}
|
||||
}
|
||||
this.updateWidgetMetaProperty("selectedTabId", selectedTabId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.selectedTab) {
|
||||
let selectedTabId = "";
|
||||
for (let index = 0; index < this.props.tabs.length; index++) {
|
||||
if (this.props.tabs[index].label === this.props.selectedTab) {
|
||||
selectedTabId = this.props.tabs[index].id;
|
||||
}
|
||||
}
|
||||
this.updateWidgetMetaProperty("selectedTabId", selectedTabId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface TabContainerWidgetProps extends WidgetProps {
|
||||
tabId: string;
|
||||
}
|
||||
|
||||
export interface TabsWidgetProps<T extends TabContainerWidgetProps>
|
||||
extends WidgetProps {
|
||||
isVisible?: boolean;
|
||||
shouldScrollContents: boolean;
|
||||
tabs: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
}>;
|
||||
children: T[];
|
||||
snapColumns?: number;
|
||||
snapRows?: number;
|
||||
selectedTab: string;
|
||||
selectedTabId: string;
|
||||
}
|
||||
|
||||
export default TabsWidget;
|
||||
|
|
@ -3301,7 +3301,6 @@ async@^2.1.4, async@^2.6.2:
|
|||
async@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
|
||||
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
|
|
@ -3690,7 +3689,7 @@ babel-preset-react-app@^9.1.1:
|
|||
babel-plugin-macros "2.8.0"
|
||||
babel-plugin-transform-react-remove-prop-types "0.4.24"
|
||||
|
||||
babel-runtime@^6.26.0:
|
||||
babel-runtime@^6.11.6, babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
|
|
@ -4049,7 +4048,6 @@ cache-base@^1.0.1:
|
|||
cachedir@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"
|
||||
integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==
|
||||
|
||||
call-me-maybe@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
|
@ -4188,6 +4186,10 @@ chance@^1.1.3:
|
|||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.4.tgz#d8743bf8e40bb05e024c305ca1ff441195eb23db"
|
||||
|
||||
change-emitter@^0.1.2:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
|
||||
|
||||
character-entities-legacy@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1"
|
||||
|
|
@ -4502,7 +4504,6 @@ comma-separated-tokens@^1.0.0:
|
|||
commander@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83"
|
||||
integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==
|
||||
|
||||
commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
|
||||
version "2.20.3"
|
||||
|
|
@ -5015,7 +5016,6 @@ cypress-xpath@^1.4.0:
|
|||
cypress@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-4.1.0.tgz#295f115d2e8a08fff2760ab49d94d876f5877aee"
|
||||
integrity sha512-FFV8pS9iuriSX4M9rna6awJUhiqozZD1D5z5BprCUJoho1ctbcgpkEUIUnqxli2OwjQqVz07egO+iqoGL+tw7g==
|
||||
dependencies:
|
||||
"@cypress/listr-verbose-renderer" "0.4.1"
|
||||
"@cypress/xvfb" "1.2.4"
|
||||
|
|
@ -6656,7 +6656,6 @@ get-value@^2.0.3, get-value@^2.0.6:
|
|||
getos@3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.4.tgz#29cdf240ed10a70c049add7b6f8cb08c81876faf"
|
||||
integrity sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==
|
||||
dependencies:
|
||||
async "^3.1.0"
|
||||
|
||||
|
|
@ -6816,6 +6815,10 @@ good-listener@^1.2.2:
|
|||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
google-maps-infobox@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/google-maps-infobox/-/google-maps-infobox-2.0.0.tgz#1ea6de93c0cdf4138c2d586331835c83dcc59dc2"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
|
||||
|
|
@ -7019,6 +7022,10 @@ hmac-drbg@^1.0.0:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^2.3.1:
|
||||
version "2.5.5"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
|
||||
|
||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
|
|
@ -7409,7 +7416,7 @@ interweave@^12.1.1:
|
|||
escape-html "^1.0.3"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4:
|
||||
invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
dependencies:
|
||||
|
|
@ -8786,7 +8793,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
|
|||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
|
||||
lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@~4.17.10:
|
||||
lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.0.1, lodash@^4.16.2, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@~4.17.10:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
|
||||
|
|
@ -8935,6 +8942,14 @@ markdown-to-jsx@^6.10.3, markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3:
|
|||
prop-types "^15.6.2"
|
||||
unquote "^1.1.0"
|
||||
|
||||
marker-clusterer-plus@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz#f8eff74d599dab3b7d0e3fed5264ea0e704f5d67"
|
||||
|
||||
markerwithlabel@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/markerwithlabel/-/markerwithlabel-2.0.2.tgz#fa6aee4abb0ee553e24e2b708226858f58b8729e"
|
||||
|
||||
material-colors@^1.2.1:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
|
||||
|
|
@ -9873,7 +9888,6 @@ osenv@0:
|
|||
ospath@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
|
||||
integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=
|
||||
|
||||
p-defer@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
@ -11210,7 +11224,6 @@ raf@^3.4.1:
|
|||
ramda@0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
||||
|
||||
ramda@^0.21.0:
|
||||
version "0.21.0"
|
||||
|
|
@ -11483,6 +11496,22 @@ react-focus-lock@^2.1.0:
|
|||
use-callback-ref "^1.2.1"
|
||||
use-sidecar "^1.0.1"
|
||||
|
||||
react-google-maps@^9.4.5:
|
||||
version "9.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-google-maps/-/react-google-maps-9.4.5.tgz#920c199bdc925e0ce93880edffb09428d263aafa"
|
||||
dependencies:
|
||||
babel-runtime "^6.11.6"
|
||||
can-use-dom "^0.1.0"
|
||||
google-maps-infobox "^2.0.0"
|
||||
invariant "^2.2.1"
|
||||
lodash "^4.16.2"
|
||||
marker-clusterer-plus "^2.1.4"
|
||||
markerwithlabel "^2.0.1"
|
||||
prop-types "^15.5.8"
|
||||
recompose "^0.26.0"
|
||||
scriptjs "^2.5.8"
|
||||
warning "^3.0.0"
|
||||
|
||||
react-helmet-async@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97"
|
||||
|
|
@ -11945,6 +11974,15 @@ rechoir@^0.6.2:
|
|||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
recompose@^0.26.0:
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.26.0.tgz#9babff039cb72ba5bd17366d55d7232fbdfb2d30"
|
||||
dependencies:
|
||||
change-emitter "^0.1.2"
|
||||
fbjs "^0.8.1"
|
||||
hoist-non-react-statics "^2.3.1"
|
||||
symbol-observable "^1.0.4"
|
||||
|
||||
recursive-readdir@2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
|
||||
|
|
@ -12517,6 +12555,10 @@ schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6
|
|||
ajv "^6.10.2"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
scriptjs@^2.5.8:
|
||||
version "2.5.9"
|
||||
resolved "https://registry.yarnpkg.com/scriptjs/-/scriptjs-2.5.9.tgz#343915cd2ec2ed9bfdde2b9875cd28f59394b35f"
|
||||
|
||||
scss-tokenizer@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
|
||||
|
|
@ -13294,7 +13336,6 @@ supports-color@^2.0.0:
|
|||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
|
|
@ -13326,7 +13367,7 @@ svgo@^1.0.0, svgo@^1.2.2:
|
|||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
symbol-observable@^1.0.2, symbol-observable@^1.1.0, symbol-observable@^1.2.0:
|
||||
symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
|
||||
|
|
@ -13914,7 +13955,6 @@ unset-value@^1.0.0:
|
|||
untildify@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
||||
|
||||
upath@^1.1.1:
|
||||
version "1.2.0"
|
||||
|
|
@ -14142,6 +14182,12 @@ walker@^1.0.7, walker@~1.0.5:
|
|||
dependencies:
|
||||
makeerror "1.0.x"
|
||||
|
||||
warning@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
warning@^4.0.2, warning@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user