{createMessage(PAGE_NOT_FOUND)}
Either this page doesn't exist, or you don't have access to{" "}
diff --git a/app/client/src/pages/setup/DataCollectionForm.tsx b/app/client/src/pages/setup/DataCollectionForm.tsx
index bccda17acb..dc9771e217 100644
--- a/app/client/src/pages/setup/DataCollectionForm.tsx
+++ b/app/client/src/pages/setup/DataCollectionForm.tsx
@@ -38,8 +38,8 @@ export default memo(function DataCollectionForm() {
const [allowCollection, setAllowCollection] = useState(true);
return (
-
- 2.
+
+ 2.
{createMessage(WELCOME_FORM_DATA_COLLECTION_HEADER)}
diff --git a/app/client/src/pages/setup/DetailsForm.tsx b/app/client/src/pages/setup/DetailsForm.tsx
index 799ae4502f..a701a5a690 100644
--- a/app/client/src/pages/setup/DetailsForm.tsx
+++ b/app/client/src/pages/setup/DetailsForm.tsx
@@ -45,8 +45,8 @@ export default function DetailsForm(
return (
-
- 1.
+
+ 1.
{createMessage(WELCOME_FORM_HEADER)}
diff --git a/app/client/src/pages/setup/NewsletterForm.tsx b/app/client/src/pages/setup/NewsletterForm.tsx
index 113b9baf3c..8626110a4f 100644
--- a/app/client/src/pages/setup/NewsletterForm.tsx
+++ b/app/client/src/pages/setup/NewsletterForm.tsx
@@ -36,8 +36,8 @@ const NewsletterContainer = styled.div`
export default memo(function NewsletterForm() {
return (
-
- 3.
+
+ 3.
{createMessage(WELCOME_FORM_NEWLETTER_HEADER)}
diff --git a/app/client/src/pages/setup/common.tsx b/app/client/src/pages/setup/common.tsx
index ace96055a8..1e3029d59c 100644
--- a/app/client/src/pages/setup/common.tsx
+++ b/app/client/src/pages/setup/common.tsx
@@ -13,16 +13,12 @@ export const FormHeaderWrapper = styled.div`
export const FormHeaderLabel = styled.h5`
width: 100%;
font-size: 20px;
- margin: 8px 0 16px;
font-weight: 500;
`;
export const FormHeaderIndex = styled.h5`
font-size: 20px;
font-weight: 500;
- position: absolute;
- left: -33px;
- top: -33px;
`;
export const FormBodyWrapper = styled.div`
diff --git a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
index 0726a34f27..1b06c76f9a 100644
--- a/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
+++ b/app/client/src/reducers/entityReducers/canvasWidgetsReducer.tsx
@@ -5,7 +5,7 @@ import {
ReduxAction,
} from "constants/ReduxActionConstants";
import { WidgetProps } from "widgets/BaseWidget";
-import { UpdateCanvasLayout } from "actions/controlActions";
+import { UpdateCanvasLayoutPayload } from "actions/controlActions";
import { set } from "lodash";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
@@ -32,7 +32,7 @@ const canvasWidgetsReducer = createImmerReducer(initialState, {
},
[ReduxActionTypes.UPDATE_CANVAS_LAYOUT]: (
state: CanvasWidgetsReduxState,
- action: ReduxAction,
+ action: ReduxAction,
) => {
set(state[MAIN_CONTAINER_WIDGET_ID], "rightColumn", action.payload.width);
set(state[MAIN_CONTAINER_WIDGET_ID], "minHeight", action.payload.height);
diff --git a/app/client/src/reducers/uiReducers/editorReducer.tsx b/app/client/src/reducers/uiReducers/editorReducer.tsx
index 7a365aec29..9aac98ecc2 100644
--- a/app/client/src/reducers/uiReducers/editorReducer.tsx
+++ b/app/client/src/reducers/uiReducers/editorReducer.tsx
@@ -7,7 +7,6 @@ import {
} from "constants/ReduxActionConstants";
import moment from "moment";
import { PageAction } from "constants/AppsmithActionConstants/ActionConstants";
-import { CommentsReduxState } from "./commentsReducer/interfaces";
const initialState: EditorReduxState = {
initialized: false,
@@ -28,6 +27,8 @@ const initialState: EditorReduxState = {
updateWidgetNameError: false,
},
isSnipingMode: false,
+ isPreviewMode: false,
+ zoomLevel: 1,
};
const editorReducer = createReducer(initialState, {
@@ -175,7 +176,7 @@ const editorReducer = createReducer(initialState, {
return { ...state };
},
[ReduxActionTypes.SET_SNIPING_MODE]: (
- state: CommentsReduxState,
+ state: EditorReduxState,
action: ReduxAction,
) => {
return {
@@ -183,6 +184,15 @@ const editorReducer = createReducer(initialState, {
isSnipingMode: action.payload,
};
},
+ [ReduxActionTypes.SET_PREVIEW_MODE]: (
+ state: EditorReduxState,
+ action: ReduxAction,
+ ) => {
+ return {
+ ...state,
+ isPreviewMode: action.payload,
+ };
+ },
});
export interface EditorReduxState {
@@ -194,6 +204,8 @@ export interface EditorReduxState {
lastUpdatedTime?: number;
pageActions?: PageAction[][];
isSnipingMode: boolean;
+ isPreviewMode: boolean;
+ zoomLevel: number;
loadingStates: {
saving: boolean;
savingError: boolean;
diff --git a/app/client/src/reducers/uiReducers/explorerReducer.ts b/app/client/src/reducers/uiReducers/explorerReducer.ts
index 309ae4375d..6ec26737f5 100644
--- a/app/client/src/reducers/uiReducers/explorerReducer.ts
+++ b/app/client/src/reducers/uiReducers/explorerReducer.ts
@@ -4,27 +4,48 @@ import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
+import { get } from "lodash";
export interface ExplorerReduxState {
- updatingEntity?: string;
- updateEntityError?: string;
- editingEntityName?: string;
+ entity: {
+ updatingEntity?: string;
+ updateEntityError?: string;
+ editingEntityName?: string;
+ };
+ pinned: boolean;
+ width: number | undefined;
+ active: boolean;
}
-const initialState: ExplorerReduxState = {};
+
+const initialState: ExplorerReduxState = {
+ pinned: true,
+ entity: {},
+ width: undefined,
+ active: true,
+};
const setUpdatingEntity = (
state: ExplorerReduxState,
action: ReduxAction<{ id: string }>,
) => {
- return { updatingEntity: action.payload.id, updateEntityError: undefined };
+ return {
+ ...state,
+ entity: { updatingEntity: action.payload.id, updateEntityError: undefined },
+ };
};
const setEntityUpdateError = (state: ExplorerReduxState) => {
- return { updatingEntity: undefined, updateEntityError: state.updatingEntity };
+ return {
+ ...state,
+ entity: {
+ updatingEntity: undefined,
+ updateEntityError: state.entity.updatingEntity,
+ },
+ };
};
-const setEntityUpdateSuccess = () => {
- return {};
+const setEntityUpdateSuccess = (state: ExplorerReduxState) => {
+ return { ...state, entity: {} };
};
const setUpdatingDatasourceEntity = (
@@ -34,10 +55,13 @@ const setUpdatingDatasourceEntity = (
const pathParts = window.location.pathname.split("/");
const pageId = pathParts[pathParts.indexOf("pages") + 1];
- if (!state.updatingEntity?.includes(action.payload.id)) {
+ if (!get(state, "entity.updatingEntity", "")?.includes(action.payload.id)) {
return {
- updatingEntity: `${action.payload.id}-${pageId}`,
- updateEntityError: undefined,
+ ...state,
+ entity: {
+ updatingEntity: `${action.payload.id}-${pageId}`,
+ updateEntityError: undefined,
+ },
};
}
@@ -101,10 +125,33 @@ const explorerReducer = createReducer(initialState, {
state: ExplorerReduxState,
action: ReduxAction<{ id: string }>,
) => {
- return { editingEntityName: action.payload.id };
+ return { ...state, entity: { editingEntityName: action.payload.id } };
},
- [ReduxActionTypes.END_EXPLORER_ENTITY_NAME_EDIT]: () => {
- return {};
+ [ReduxActionTypes.END_EXPLORER_ENTITY_NAME_EDIT]: (
+ state: ExplorerReduxState,
+ ) => {
+ return { ...state, entity: {} };
+ },
+ [ReduxActionTypes.SET_EXPLORER_PINNED]: (
+ state: ExplorerReduxState,
+ action: ReduxAction<{ shouldPin: boolean }>,
+ ) => {
+ return { ...state, pinned: action.payload.shouldPin };
+ },
+ [ReduxActionTypes.UPDATE_EXPLORER_WIDTH]: (
+ state: ExplorerReduxState,
+ action: ReduxAction<{ width: number | undefined }>,
+ ) => {
+ return { ...state, width: action.payload.width };
+ },
+ [ReduxActionTypes.SET_EXPLORER_ACTIVE]: (
+ state: ExplorerReduxState,
+ action: ReduxAction,
+ ) => {
+ return {
+ ...state,
+ active: action.payload,
+ };
},
});
diff --git a/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts
index faa468b77d..bbb92510cf 100644
--- a/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts
+++ b/app/client/src/sagas/ActionExecution/ActionExecutionSagas.ts
@@ -66,7 +66,6 @@ export function* executeActionTriggers(
executePluginActionTriggerSaga,
trigger.payload,
eventType,
- triggerMeta,
);
break;
case ActionTriggerType.CLEAR_PLUGIN_ACTION:
diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts
index 56ff16bbf1..3ef1f12aa2 100644
--- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts
+++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts
@@ -86,7 +86,6 @@ import { RunPluginActionDescription } from "entities/DataTree/actionTriggers";
import { APP_MODE } from "entities/App";
import { FileDataTypes } from "widgets/constants";
import { hideDebuggerErrors } from "actions/debuggerActions";
-import { TriggerMeta } from "sagas/ActionExecution/ActionExecutionSagas";
import {
PluginTriggerFailureError,
PluginActionExecutionError,
@@ -243,7 +242,6 @@ function* confirmRunActionSaga() {
export default function* executePluginActionTriggerSaga(
pluginAction: RunPluginActionDescription["payload"],
eventType: EventType,
- triggerMeta: TriggerMeta,
) {
const { actionId, params } = pluginAction;
PerformanceTracker.startAsyncTracking(
@@ -310,7 +308,6 @@ export default function* executePluginActionTriggerSaga(
throw new PluginTriggerFailureError(
createMessage(ERROR_PLUGIN_ACTION_EXECUTE, action.name),
[payload.body, params],
- triggerMeta,
);
} else {
AppsmithConsole.info({
diff --git a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts
index c1a7bdb3e2..0d428a57c9 100644
--- a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts
+++ b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts
@@ -8,8 +8,8 @@ import { all, call } from "redux-saga/effects";
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import log from "loglevel";
import {
+ logActionExecutionError,
PluginTriggerFailureError,
- UncaughtAppsmithPromiseError,
UserCancelledActionExecutionError,
} from "sagas/ActionExecution/errorUtils";
@@ -65,7 +65,12 @@ export default function* executePromiseSaga(
});
} else {
log.error(e);
- throw new UncaughtAppsmithPromiseError(e.message, triggerMeta, e);
+ /* Logging the error instead of throwing an error as it was making the ui to go into a loading states */
+ logActionExecutionError(
+ e.message,
+ triggerMeta.source,
+ triggerMeta.triggerPropertyName,
+ );
}
}
diff --git a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts
index 50e4dfc13c..14c65a51aa 100644
--- a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts
+++ b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts
@@ -1,4 +1,4 @@
-import { Variant } from "components/ads/common";
+import { ToastTypeOptions, Variant } from "components/ads/common";
import { Toaster } from "components/ads/Toast";
import AppsmithConsole from "utils/AppsmithConsole";
import { ShowAlertActionDescription } from "entities/DataTree/actionTriggers";
@@ -32,7 +32,9 @@ export default function* showAlertSaga(
}
if (payload.style && !variant) {
throw new TriggerFailureError(
- `Toast type needs to be a one of ${Object.values(Variant).join(", ")}`,
+ `Toast type needs to be a one of ${Object.values(ToastTypeOptions).join(
+ ", ",
+ )}`,
triggerMeta,
);
}
diff --git a/app/client/src/sagas/ActionExecution/errorUtils.ts b/app/client/src/sagas/ActionExecution/errorUtils.ts
index 8fd3982f17..cc57d4ca9c 100644
--- a/app/client/src/sagas/ActionExecution/errorUtils.ts
+++ b/app/client/src/sagas/ActionExecution/errorUtils.ts
@@ -58,15 +58,11 @@ export const logActionExecutionError = (
});
};
-export class PluginTriggerFailureError extends TriggerFailureError {
+export class PluginTriggerFailureError extends Error {
responseData: unknown[] = [];
- constructor(
- reason: string,
- responseData: unknown[],
- triggerMeta: TriggerMeta,
- ) {
- super(reason, triggerMeta);
+ constructor(reason: string, responseData: unknown[]) {
+ super(reason);
this.responseData = responseData;
}
}
diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts
index 808516717b..8c6d98391b 100644
--- a/app/client/src/sagas/ActionSagas.ts
+++ b/app/client/src/sagas/ActionSagas.ts
@@ -35,10 +35,7 @@ import {
updateActionProperty,
updateActionSuccess,
} from "actions/pluginActionActions";
-import {
- getDynamicBindingsChangesSaga,
- removeBindingsFromActionObject,
-} from "utils/DynamicBindingUtils";
+import { getDynamicBindingsChangesSaga } from "utils/DynamicBindingUtils";
import { validateResponse } from "./ErrorSagas";
import { transformRestAction } from "transformers/RestActionTransformer";
import {
@@ -110,7 +107,7 @@ import {
filterCategories,
SEARCH_CATEGORY_ID,
} from "components/editorComponents/GlobalSearch/utils";
-import { getSelectedWidget, getWidgetById } from "./selectors";
+import { getSelectedWidget, getWidgetByID } from "./selectors";
import {
onApiEditor,
onQueryEditor,
@@ -447,11 +444,10 @@ function* moveActionSaga(
}>,
) {
const actionObject: Action = yield select(getAction, action.payload.id);
- const withoutBindings = removeBindingsFromActionObject(actionObject);
try {
const response = yield ActionAPI.moveAction({
action: {
- ...withoutBindings,
+ ...actionObject,
pageId: action.payload.originalPageId,
name: action.payload.name,
},
@@ -490,12 +486,9 @@ function* moveActionSaga(
function* copyActionSaga(
action: ReduxAction<{ id: string; destinationPageId: string; name: string }>,
) {
- let actionObject: Action = yield select(getAction, action.payload.id);
+ const actionObject: Action = yield select(getAction, action.payload.id);
try {
if (!actionObject) throw new Error("Could not find action to copy");
- if (action.payload.destinationPageId !== actionObject.pageId) {
- actionObject = removeBindingsFromActionObject(actionObject);
- }
const copyAction = Object.assign({}, actionObject, {
name: action.payload.name,
@@ -646,7 +639,9 @@ function* saveActionName(action: ReduxAction<{ id: string; name: string }>) {
}
}
-function* setActionPropertySaga(action: ReduxAction) {
+export function* setActionPropertySaga(
+ action: ReduxAction,
+) {
const { actionId, propertyName, value } = action.payload;
if (!actionId) return;
if (propertyName === "name") return;
@@ -796,8 +791,7 @@ function* buildMetaForSnippets(
}
if (entityType === ENTITY_TYPE.WIDGET && entityId) {
const currentEntity: FlattenedWidgetProps = yield select(
- getWidgetById,
- entityId,
+ getWidgetByID(entityId),
);
const type: string = currentEntity.type || "";
refinements.entities = [type, entityType];
diff --git a/app/client/src/sagas/DraggingCanvasSagas.ts b/app/client/src/sagas/DraggingCanvasSagas.ts
index b88bce2f6a..b7fed39368 100644
--- a/app/client/src/sagas/DraggingCanvasSagas.ts
+++ b/app/client/src/sagas/DraggingCanvasSagas.ts
@@ -19,7 +19,7 @@ import { calculateDropTargetRows } from "components/editorComponents/DropTargetU
import { GridDefaults } from "constants/WidgetConstants";
import { WidgetProps } from "widgets/BaseWidget";
import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors";
-import { OccupiedSpace } from "constants/editorConstants";
+import { OccupiedSpace } from "constants/CanvasEditorConstants";
export type WidgetMoveParams = {
widgetId: string;
diff --git a/app/client/src/sagas/OnboardingSagas.ts b/app/client/src/sagas/OnboardingSagas.ts
index 50ee93b7ba..01c91bf1ab 100644
--- a/app/client/src/sagas/OnboardingSagas.ts
+++ b/app/client/src/sagas/OnboardingSagas.ts
@@ -90,7 +90,6 @@ import { getQueryIdFromURL } from "pages/Editor/Explorer/helpers";
// import { calculateNewWidgetPosition } from "./WidgetOperationSagas";
import { RenderModes } from "constants/WidgetConstants";
import { generateReactKey } from "utils/generators";
-import { forceOpenPropertyPane } from "actions/widgetActions";
import { navigateToCanvas } from "pages/Editor/Explorer/Widgets/utils";
import {
batchUpdateWidgetProperty,
@@ -721,7 +720,6 @@ function* addWidget(widgetConfig: any) {
type: ReduxActionTypes.SELECT_WIDGET_INIT,
payload: { widgetId: newWidget.newWidgetId },
});
- yield put(forceOpenPropertyPane(newWidget.newWidgetId));
} catch (error) {}
}
@@ -835,7 +833,6 @@ function* addOnSubmitHandler() {
type: ReduxActionTypes.SELECT_WIDGET_INIT,
payload: { widgetId: inputWidget.widgetId },
});
- yield put(forceOpenPropertyPane(inputWidget.widgetId));
yield put(
updateWidgetPropertyRequest(
diff --git a/app/client/src/sagas/ReplaySaga.ts b/app/client/src/sagas/ReplaySaga.ts
index 12c086f724..121b6445a4 100644
--- a/app/client/src/sagas/ReplaySaga.ts
+++ b/app/client/src/sagas/ReplaySaga.ts
@@ -8,10 +8,7 @@ import {
getIsPropertyPaneVisible,
getCurrentWidgetId,
} from "../selectors/propertyPaneSelectors";
-import {
- closePropertyPane,
- forceOpenPropertyPane,
-} from "actions/widgetActions";
+import { closePropertyPane } from "actions/widgetActions";
import {
selectMultipleWidgetsInitAction,
selectWidgetAction,
@@ -60,7 +57,6 @@ export function* openPropertyPaneSaga(replay: any) {
//if property pane is not visible, select the widget and force open property pane
if (selectedWidgetId !== replayWidgetId || !isPropertyPaneVisible) {
yield put(selectWidgetAction(replayWidgetId, false));
- yield put(forceOpenPropertyPane(replayWidgetId));
}
flashElementsById(
diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts
index 9ce550b138..346a05e897 100644
--- a/app/client/src/sagas/SelectionCanvasSagas.ts
+++ b/app/client/src/sagas/SelectionCanvasSagas.ts
@@ -1,5 +1,5 @@
import { selectMultipleWidgetsAction } from "actions/widgetSelectionActions";
-import { OccupiedSpace } from "constants/editorConstants";
+import { OccupiedSpace } from "constants/CanvasEditorConstants";
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
import { isEqual } from "lodash";
diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts
index b3a60a2811..1b318341b8 100644
--- a/app/client/src/sagas/WidgetDeletionSagas.ts
+++ b/app/client/src/sagas/WidgetDeletionSagas.ts
@@ -204,6 +204,7 @@ function* deleteSaga(deleteAction: ReduxAction) {
if (!disallowUndo) {
// close property pane after delete
yield put(closePropertyPane());
+ yield put(selectWidgetInitAction(undefined));
yield call(postDelete, widgetId, widgetName, otherWidgetsToDelete);
}
}
diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx
index 0790a65ba9..64c397aa07 100644
--- a/app/client/src/sagas/WidgetOperationSagas.tsx
+++ b/app/client/src/sagas/WidgetOperationSagas.tsx
@@ -56,7 +56,6 @@ import {
getCurrentApplicationId,
getCurrentPageId,
} from "selectors/editorSelectors";
-import { forceOpenPropertyPane } from "actions/widgetActions";
import { selectMultipleWidgetsInitAction } from "actions/widgetSelectionActions";
import { getDataTree } from "selectors/dataTreeSelectors";
@@ -1097,7 +1096,6 @@ function* addSuggestedWidget(action: ReduxAction>) {
widgetId: newWidget.newWidgetId,
applicationId,
});
- yield put(forceOpenPropertyPane(newWidget.newWidgetId));
} catch (error) {
log.error(error);
}
diff --git a/app/client/src/sagas/selectors.tsx b/app/client/src/sagas/selectors.tsx
index f39c014309..6b6ec31461 100644
--- a/app/client/src/sagas/selectors.tsx
+++ b/app/client/src/sagas/selectors.tsx
@@ -19,6 +19,15 @@ export const getWidgetsMeta = (state: AppState) => state.entities.meta;
export const getWidgetMetaProps = (state: AppState, widgetId: string) =>
state.entities.meta[widgetId];
+export const getWidgetByID = (widgetId: string) => {
+ return createSelector(
+ getWidgets,
+ (canvasWidgets: { [widgetId: string]: FlattenedWidgetProps }) => {
+ return canvasWidgets[widgetId];
+ },
+ );
+};
+
export const getWidget = (state: AppState, widgetId: string): WidgetProps => {
return state.entities.canvasWidgets[widgetId];
};
@@ -111,14 +120,6 @@ export const getWidgetByName = (
);
};
-export const getWidgetById = (
- state: AppState,
- id: string,
-): FlattenedWidgetProps | undefined => {
- const widgets = state.entities.canvasWidgets;
- return widgets[id];
-};
-
export const getAllPageIds = (state: AppState) => {
return state.entities.pageList.pages.map((page) => page.pageId);
};
diff --git a/app/client/src/selectors/canvasSelectors.ts b/app/client/src/selectors/canvasSelectors.ts
new file mode 100644
index 0000000000..2613bc0edf
--- /dev/null
+++ b/app/client/src/selectors/canvasSelectors.ts
@@ -0,0 +1,5 @@
+import { AppState } from "reducers";
+
+export const getIsDraggingForSelection = (state: AppState) => {
+ return state.ui.canvasSelection.isDraggingForSelection;
+};
diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx
index 1f13740df3..d10ecb3520 100644
--- a/app/client/src/selectors/editorSelectors.tsx
+++ b/app/client/src/selectors/editorSelectors.tsx
@@ -9,7 +9,7 @@ import {
} from "reducers/entityReducers/canvasWidgetsReducer";
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
-import { OccupiedSpace } from "constants/editorConstants";
+import { OccupiedSpace } from "constants/CanvasEditorConstants";
import {
getActions,
getCanvasWidgets,
@@ -97,6 +97,9 @@ export const getPageList = (state: AppState) => state.entities.pageList.pages;
export const getCurrentPageId = (state: AppState) =>
state.entities.pageList.currentPageId;
+export const getCurrentApplication = (state: AppState) =>
+ state.ui.applications.currentApplication;
+
export const getCurrentApplicationId = (state: AppState) =>
state.entities.pageList.applicationId ||
""; /** this is set during init can assume it to be defined */
@@ -369,3 +372,23 @@ export const getJSCollectionById = createSelector(
export const getApplicationLastDeployedAt = (state: AppState) =>
state.ui.applications.currentApplication?.lastDeployedAt;
+
+/**
+ * returns the `state.ui.editor.isPreviewMode`
+ *
+ * @param state AppState
+ * @returns boolean
+ */
+export const previewModeSelector = (state: AppState) => {
+ return state.ui.editor.isPreviewMode;
+};
+
+/**
+ * returns the `state.ui.editor.zoomLevel`
+ *
+ * @param state AppState
+ * @returns number
+ */
+export const getZoomLevel = (state: AppState) => {
+ return state.ui.editor.zoomLevel;
+};
diff --git a/app/client/src/selectors/entitiesSelector.ts b/app/client/src/selectors/entitiesSelector.ts
index ac21aea257..a70da164e0 100644
--- a/app/client/src/selectors/entitiesSelector.ts
+++ b/app/client/src/selectors/entitiesSelector.ts
@@ -306,15 +306,6 @@ export const getJSCollectionsForCurrentPage = createSelector(
},
);
-export const getQueryActionsForCurrentPage = createSelector(
- getActionsForCurrentPage,
- (actions) => {
- return actions.filter((action) => {
- return action.config.pluginType === PluginType.DB;
- });
- },
-);
-
export const getPlugin = (state: AppState, pluginId: string) => {
return state.entities.plugins.list.find((plugin) => plugin.id === pluginId);
};
diff --git a/app/client/src/selectors/explorerSelector.ts b/app/client/src/selectors/explorerSelector.ts
new file mode 100644
index 0000000000..563af5d080
--- /dev/null
+++ b/app/client/src/selectors/explorerSelector.ts
@@ -0,0 +1,31 @@
+import { AppState } from "reducers";
+
+/**
+ * returns the pinned state of explorer
+ *
+ * @param state
+ * @returns
+ */
+export const getExplorerPinned = (state: AppState) => {
+ return state.ui.explorer.pinned;
+};
+
+/**
+ * returns the width of explorer
+ *
+ * @param state
+ * @returns
+ */
+export const getExplorerWidth = (state: AppState) => {
+ return state.ui.explorer.width;
+};
+
+/**
+ * returns the active state of explorer
+ *
+ * @param state
+ * @returns
+ */
+export const getExplorerActive = (state: AppState) => {
+ return state.ui.explorer.active;
+};
diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx
index 187498b99b..1d46d35a53 100644
--- a/app/client/src/selectors/propertyPaneSelectors.tsx
+++ b/app/client/src/selectors/propertyPaneSelectors.tsx
@@ -27,12 +27,12 @@ export const getCurrentWidgetId = createSelector(
export const getCurrentWidgetProperties = createSelector(
getCanvasWidgets,
- getPropertyPaneState,
+ getSelectedWidgets,
(
widgets: CanvasWidgetsReduxState,
- pane: PropertyPaneReduxState,
+ selectedWidgetIds: string[],
): WidgetProps | undefined => {
- return get(widgets, `${pane.widgetId}`);
+ return get(widgets, `${selectedWidgetIds[0]}`);
},
);
diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx
index cb00830447..39384e40e3 100644
--- a/app/client/src/utils/AppsmithUtils.tsx
+++ b/app/client/src/utils/AppsmithUtils.tsx
@@ -1,4 +1,8 @@
-import { ReduxAction } from "constants/ReduxActionConstants";
+import {
+ CurrentApplicationData,
+ Page,
+ ReduxAction,
+} from "constants/ReduxActionConstants";
import { getAppsmithConfigs } from "configs";
import * as Sentry from "@sentry/react";
import AnalyticsUtil from "./AnalyticsUtil";
@@ -13,6 +17,12 @@ import { AppIconCollection, AppIconName } from "components/ads/AppIcon";
import { ERROR_CODES } from "constants/ApiConstants";
import { createMessage, ERROR_500 } from "../constants/messages";
import localStorage from "utils/localStorage";
+import { APP_MODE } from "entities/App";
+import { trimQueryString } from "./helpers";
+import {
+ getApplicationEditorPageURL,
+ getApplicationViewerPageURL,
+} from "constants/routes";
export const createReducer = (
initialState: any,
@@ -345,3 +355,31 @@ export const parseBlobUrl = (blobId: string) => {
}/${blobId.substring(5)}`;
return url.split("?type=");
};
+
+/**
+ * gets the page url
+ *
+ * Note: for edit mode, the page will have different url ( contains '/edit' at the end )
+ *
+ * @param page
+ * @returns
+ */
+export const getPageURL = (
+ page: Page,
+ appMode: APP_MODE | undefined,
+ currentApplicationDetails: CurrentApplicationData | undefined,
+) => {
+ if (appMode === APP_MODE.PUBLISHED) {
+ return trimQueryString(
+ getApplicationViewerPageURL({
+ applicationId: currentApplicationDetails?.id,
+ pageId: page.pageId,
+ }),
+ );
+ }
+
+ return getApplicationEditorPageURL(
+ currentApplicationDetails?.id,
+ page.pageId,
+ );
+};
diff --git a/app/client/src/utils/DSLMigrations.ts b/app/client/src/utils/DSLMigrations.ts
index ed5cc50306..a5cb509894 100644
--- a/app/client/src/utils/DSLMigrations.ts
+++ b/app/client/src/utils/DSLMigrations.ts
@@ -24,6 +24,7 @@ import {
migrateTableWidgetSelectedRowBindings,
migrateTableSanitizeColumnKeys,
isSortableMigration,
+ migrateTableWidgetIconButtonVariant,
} from "./migrations/TableWidget";
import { migrateTextStyleFromTextWidget } from "./migrations/TextWidgetReplaceTextStyle";
import { DATA_BIND_REGEX_GLOBAL } from "constants/BindingsConstants";
@@ -978,6 +979,10 @@ export const transformDSL = (
}
if (currentDSL.version === 44) {
currentDSL = isSortableMigration(currentDSL);
+ currentDSL.version = 45;
+ }
+ if (currentDSL.version === 45) {
+ currentDSL = migrateTableWidgetIconButtonVariant(currentDSL);
currentDSL.version = LATEST_PAGE_VERSION;
}
diff --git a/app/client/src/utils/DynamicBindingUtils.ts b/app/client/src/utils/DynamicBindingUtils.ts
index 2ce82a896e..47054974c0 100644
--- a/app/client/src/utils/DynamicBindingUtils.ts
+++ b/app/client/src/utils/DynamicBindingUtils.ts
@@ -1,8 +1,5 @@
import _, { VERSION as lodashVersion } from "lodash";
-import {
- DATA_BIND_REGEX,
- DATA_BIND_REGEX_GLOBAL,
-} from "constants/BindingsConstants";
+import { DATA_BIND_REGEX } from "constants/BindingsConstants";
import { Action } from "entities/Action";
import moment from "moment-timezone";
import { WidgetProps } from "widgets/BaseWidget";
@@ -21,11 +18,6 @@ export type FormEditorConfigs = Record;
export type FormSettingsConfigs = Record;
export type FormDependencyConfigs = Record;
-export const removeBindingsFromActionObject = (obj: Action) => {
- const string = JSON.stringify(obj);
- const withBindings = string.replace(DATA_BIND_REGEX_GLOBAL, "{{ }}");
- return JSON.parse(withBindings);
-};
// referencing DATA_BIND_REGEX fails for the value "{{Table1.tableData[Table1.selectedRowIndex]}}" if you run it multiple times and don't recreate
export const isDynamicValue = (value: string): boolean =>
DATA_BIND_REGEX.test(value);
diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx
index ca8e87e179..8f24efd4eb 100644
--- a/app/client/src/utils/WidgetPropsUtils.tsx
+++ b/app/client/src/utils/WidgetPropsUtils.tsx
@@ -8,7 +8,7 @@ import {
} from "widgets/BaseWidget";
import { GridDefaults, RenderMode } from "constants/WidgetConstants";
import { snapToGrid } from "./helpers";
-import { OccupiedSpace } from "constants/editorConstants";
+import { OccupiedSpace } from "constants/CanvasEditorConstants";
import defaultTemplate from "templates/default";
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import { transformDSL } from "./DSLMigrations";
diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx
index a4e2955c28..ba1fabdbee 100644
--- a/app/client/src/utils/WidgetRegistry.tsx
+++ b/app/client/src/utils/WidgetRegistry.tsx
@@ -97,6 +97,9 @@ import AudioWidget, {
import AudioRecorderWidget, {
CONFIG as AUDIO_RECORDER_WIDGET_CONFIG,
} from "widgets/AudioRecorderWidget";
+import DocumentViewerWidget, {
+ CONFIG as DOCUMENT_VIEWER_WIDGET_CONFIG,
+} from "widgets/DocumentViewerWidget";
import ButtonGroupWidget, {
CONFIG as BUTTON_GROUP_CONFIG,
} from "widgets/ButtonGroupWidget";
@@ -147,6 +150,7 @@ export const registerWidgets = () => {
registerWidget(FilePickerWidgetV2, FILEPICKER_WIDGET_V2_CONFIG);
registerWidget(StatboxWidget, STATBOX_WIDGET_CONFIG);
registerWidget(AudioRecorderWidget, AUDIO_RECORDER_WIDGET_CONFIG);
+ registerWidget(DocumentViewerWidget, DOCUMENT_VIEWER_WIDGET_CONFIG);
registerWidget(ButtonGroupWidget, BUTTON_GROUP_CONFIG);
registerWidget(MultiSelectTreeWidget, MULTI_SELECT_TREE_WIDGET_CONFIG);
registerWidget(SingleSelectTreeWidget, SINGLE_SELECT_TREE_WIDGET_CONFIG);
diff --git a/app/client/src/utils/getPathAndValueFromActionDiffObject.ts b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts
new file mode 100644
index 0000000000..6e8448adfa
--- /dev/null
+++ b/app/client/src/utils/getPathAndValueFromActionDiffObject.ts
@@ -0,0 +1,60 @@
+import * as Sentry from "@sentry/react";
+
+//Following function is the fix for the missing where key
+/**
+ * NOTE:
+ * Action object returned by getAction comes from state.entities.action
+ * action api's payload is created from state.entities.action and response is saved in the same key
+ * Data passed to redux form is the merge of values present in state.entities.action, editorConfig, settingsConfig and has the correct datastrucure
+ * Data structure in state.entities.action is not correct
+ * Q. What does the following fix do?
+ * A. It calculates the diff between merged values and state.entities.action and saves the same in state.entities.action
+ * There is another key form that holds the formData
+ */
+export function getPathAndValueFromActionDiffObject(actionObjectDiff: any) {
+ if (!actionObjectDiff) {
+ return {
+ path: undefined,
+ value: undefined,
+ };
+ } else {
+ let path = "";
+ let value = "";
+ // Loop through the diff objects in difference Array
+ for (let i = 0; i < actionObjectDiff.length; i++) {
+ //kind = N indicates a newly added property/element
+ //This property is present in initialValues but not in action object
+ if (
+ actionObjectDiff &&
+ actionObjectDiff[i].hasOwnProperty("kind") &&
+ actionObjectDiff[i].path &&
+ Array.isArray(actionObjectDiff[i].path) &&
+ actionObjectDiff[i].path.length &&
+ actionObjectDiff[i]?.kind === "N"
+ ) {
+ // Calculate path from path[] in diff
+ path = actionObjectDiff[i].path.reduce(
+ (acc: string, item: number | string) => {
+ try {
+ if (typeof item === "string" && acc) {
+ acc += `${path}.${item}`;
+ } else if (typeof item === "string" && !acc) {
+ acc += `${item}`;
+ } else acc += `${path}[${item}]`;
+ return acc;
+ } catch (error) {
+ Sentry.captureException({
+ message: `Adding key: where failed, cannot create path`,
+ oldData: actionObjectDiff,
+ });
+ }
+ },
+ "",
+ );
+ // get value from diff object
+ value = actionObjectDiff[i]?.rhs;
+ }
+ return { path, value };
+ }
+ }
+}
diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx
index 192ae72471..d74e5d5128 100644
--- a/app/client/src/utils/helpers.tsx
+++ b/app/client/src/utils/helpers.tsx
@@ -587,3 +587,20 @@ export const getQueryParamsObject = () => {
return {};
}
};
+
+/*
+ * unfocus all window selection
+ *
+ * @param document
+ * @param window
+ */
+export function unFocus(document: Document, window: Window) {
+ if (document.getSelection()) {
+ document.getSelection()?.empty();
+ } else {
+ try {
+ window.getSelection()?.removeAllRanges();
+ // eslint-disable-next-line no-empty
+ } catch (e) {}
+ }
+}
diff --git a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts
index c62505e173..11fe68fc5d 100644
--- a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts
+++ b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts
@@ -1,6 +1,9 @@
import { AppState } from "reducers";
import { commentModeSelector } from "selectors/commentsSelectors";
-import { snipingModeSelector } from "selectors/editorSelectors";
+import {
+ previewModeSelector,
+ snipingModeSelector,
+} from "selectors/editorSelectors";
import { useSelector } from "store";
export const useAllowEditorDragToSelect = () => {
@@ -30,10 +33,12 @@ export const useAllowEditorDragToSelect = () => {
const isResizingOrDragging = !!isResizing || !!isDragging || !!isSelecting;
const isCommentMode = useSelector(commentModeSelector);
const isSnipingMode = useSelector(snipingModeSelector);
+ const isPreviewMode = useSelector(previewModeSelector);
return (
!isResizingOrDragging &&
!isDraggingDisabled &&
!isCommentMode &&
- !isSnipingMode
+ !isSnipingMode &&
+ !isPreviewMode
);
};
diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts
index f6412ae58a..771212bcb7 100644
--- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts
+++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts
@@ -9,7 +9,7 @@ import { AppState } from "reducers";
import { getSelectedWidgets } from "selectors/ui";
import { getOccupiedSpaces } from "selectors/editorSelectors";
import { getTableFilterState } from "selectors/tableFilterSelectors";
-import { OccupiedSpace } from "constants/editorConstants";
+import { OccupiedSpace } from "constants/CanvasEditorConstants";
import { getDragDetails, getWidgets } from "sagas/selectors";
import {
getDropZoneOffsets,
@@ -23,7 +23,6 @@ import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena";
import { useDispatch } from "react-redux";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { EditorContext } from "components/editorComponents/EditorContextProvider";
-import { useShowPropertyPane } from "./dragResizeHooks";
import { useWidgetSelection } from "./useWidgetSelection";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { snapToGrid } from "utils/helpers";
@@ -52,7 +51,6 @@ export const useBlocksToBeDraggedOnCanvas = ({
widgetId,
}: CanvasDraggingArenaProps) => {
const dispatch = useDispatch();
- const showPropertyPane = useShowPropertyPane();
const { selectWidget } = useWidgetSelection();
const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING;
@@ -233,7 +231,6 @@ export const useBlocksToBeDraggedOnCanvas = ({
// Not needed for most widgets except for Modal Widget.
setTimeout(() => {
selectWidget(updateWidgetParams.payload.newWidgetId);
- showPropertyPane(updateWidgetParams.payload.newWidgetId);
}, 100);
AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", {
widgetType: dragDetails.newWidget.type,
diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts
index fcd0cbd96a..5119908d21 100644
--- a/app/client/src/utils/hooks/useCanvasDragging.ts
+++ b/app/client/src/utils/hooks/useCanvasDragging.ts
@@ -5,6 +5,8 @@ import {
import { debounce, throttle } from "lodash";
import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena";
import { useEffect } from "react";
+import { useSelector } from "react-redux";
+import { getZoomLevel } from "selectors/editorSelectors";
import { getNearestParentCanvas } from "utils/generators";
import { noCollision } from "utils/WidgetPropsUtils";
import { useWidgetDragResize } from "./dragResizeHooks";
@@ -32,6 +34,7 @@ export const useCanvasDragging = (
widgetId,
}: CanvasDraggingArenaProps,
) => {
+ const canvasZoomLevel = useSelector(getZoomLevel);
const { devicePixelRatio: scale = 1 } = window;
const {
@@ -88,11 +91,12 @@ export const useCanvasDragging = (
height: scrollParentTopHeight,
} = parentCanvas.getBoundingClientRect();
const { width } = canvasRef.current.getBoundingClientRect();
- canvasDrawRef.current.style.width = width + "px";
+ canvasDrawRef.current.style.width = width / canvasZoomLevel + "px";
canvasDrawRef.current.style.position = canExtend ? "absolute" : "sticky";
canvasDrawRef.current.style.left = "0px";
canvasDrawRef.current.style.top = getCanvasTopOffset() + "px";
- canvasDrawRef.current.style.height = scrollParentTopHeight + "px";
+ canvasDrawRef.current.style.height =
+ scrollParentTopHeight / canvasZoomLevel + "px";
}
};
@@ -323,6 +327,7 @@ export const useCanvasDragging = (
canvasDrawRef.current.height,
);
isUpdatingRows = false;
+ canvasCtx.transform(canvasZoomLevel, 0, 0, canvasZoomLevel, 0, 0);
if (canvasIsDragging) {
currentRectanglesToDraw.forEach((each) => {
drawBlockOnCanvas(each);
diff --git a/app/client/src/utils/hooks/useClickToSelectWidget.tsx b/app/client/src/utils/hooks/useClickToSelectWidget.tsx
index 8340a83fd5..74b19bb41d 100644
--- a/app/client/src/utils/hooks/useClickToSelectWidget.tsx
+++ b/app/client/src/utils/hooks/useClickToSelectWidget.tsx
@@ -14,7 +14,6 @@ import { getWidgets } from "sagas/selectors";
import { useWidgetSelection } from "./useWidgetSelection";
import React, { ReactNode, useCallback } from "react";
import { stopEventPropagation } from "utils/AppsmithUtils";
-import { useShowPropertyPane } from "./dragResizeHooks";
/**
*
@@ -102,7 +101,6 @@ export function ClickContentToOpenPropPane({
export const useClickToSelectWidget = () => {
const { focusWidget, selectWidget } = useWidgetSelection();
- const showPropertyPane = useShowPropertyPane();
const isPropPaneVisible = useSelector(getIsPropertyPaneVisible);
const isTableFilterPaneVisible = useSelector(getIsTableFilterPaneVisible);
const widgets: CanvasWidgetsReduxState = useSelector(getWidgets);
@@ -146,7 +144,7 @@ export const useClickToSelectWidget = () => {
selectWidget(focusedWidgetId, isMultiSelect);
focusWidget(focusedWidgetId);
}
- showPropertyPane();
+
if (isMultiSelect) {
e.stopPropagation();
}
diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx
index b228ef5928..aa60919fb5 100644
--- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx
+++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx
@@ -1,104 +1,193 @@
-import { theme } from "constants/DefaultTheme";
-import { ReduxActionTypes } from "constants/ReduxActionConstants";
+import { debounce, get } from "lodash";
+import { useDispatch, useSelector } from "react-redux";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { getWidgetByID, getWidgets } from "sagas/selectors";
+
import {
DefaultLayoutType,
layoutConfigurations,
MAIN_CONTAINER_WIDGET_ID,
} from "constants/WidgetConstants";
-import { APP_MODE } from "entities/App";
-import { debounce } from "lodash";
-import { AppsmithDefaultLayout } from "pages/Editor/MainContainerLayoutControl";
-import { useCallback, useEffect } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { AppState } from "reducers";
-import { getWidget, getWidgets } from "sagas/selectors";
-import { getAppMode } from "selectors/applicationSelectors";
+import {
+ getExplorerPinned,
+ getExplorerWidth,
+} from "selectors/explorerSelector";
import {
getCurrentApplicationLayout,
getCurrentPageId,
+ previewModeSelector,
} from "selectors/editorSelectors";
-import { calculateDynamicHeight } from "utils/DSLMigrations";
+import { APP_MODE } from "entities/App";
+import { scrollbarWidth } from "utils/helpers";
import { useWindowSizeHooks } from "./dragResizeHooks";
+import { getAppMode } from "selectors/entitiesSelector";
+import { updateCanvasLayoutAction } from "actions/editorActions";
+import { calculateDynamicHeight } from "utils/DSLMigrations";
+
+const BORDERS_WIDTH = 2;
+const GUTTER_WIDTH = 72;
export const useDynamicAppLayout = () => {
+ const dispatch = useDispatch();
+ const [initialized, setInitialized] = useState(false);
+ const explorerWidth = useSelector(getExplorerWidth);
+ const isExplorerPinned = useSelector(getExplorerPinned);
+ const appMode: APP_MODE | undefined = useSelector(getAppMode);
+ const domEntityExplorer = document.querySelector(".js-entity-explorer");
+ const domPropertyPane = document.querySelector(".js-property-pane-sidebar");
const { height: screenHeight, width: screenWidth } = useWindowSizeHooks();
- const mainContainer = useSelector((state: AppState) =>
- getWidget(state, MAIN_CONTAINER_WIDGET_ID),
- );
+ const mainContainer = useSelector(getWidgetByID(MAIN_CONTAINER_WIDGET_ID));
+ const isPreviewMode = useSelector(previewModeSelector);
const currentPageId = useSelector(getCurrentPageId);
- const appMode = useSelector(getAppMode);
const canvasWidgets = useSelector(getWidgets);
const appLayout = useSelector(getCurrentApplicationLayout);
- const dispatch = useDispatch();
- const calculateFluidMaxWidth = (
- screenWidth: number,
- layoutMaxWidth: number,
- ) => {
- const screenWidthWithBuffer = 0.95 * screenWidth;
- const widthToFill =
- appMode === APP_MODE.EDIT
- ? screenWidthWithBuffer - parseInt(theme.sidebarWidth)
- : screenWidth;
- if (layoutMaxWidth < 0) {
- return widthToFill;
- } else {
- return widthToFill < layoutMaxWidth ? widthToFill : layoutMaxWidth;
+ /**
+ * calculates min height
+ */
+ const calculatedMinHeight = useMemo(() => {
+ return calculateDynamicHeight(canvasWidgets, mainContainer?.minHeight);
+ }, [mainContainer]);
+
+ /**
+ * app layout range i.e minWidth and maxWidth for the current layout
+ * if there is no config for the current layout, use default layout i.e desktop
+ */
+ const layoutWidthRange = useMemo(() => {
+ let minWidth = -1;
+ let maxWidth = -1;
+
+ if (appLayout) {
+ const { type } = appLayout;
+ const currentLayoutConfig = get(
+ layoutConfigurations,
+ type,
+ layoutConfigurations[DefaultLayoutType],
+ );
+
+ if (currentLayoutConfig.minWidth) minWidth = currentLayoutConfig.minWidth;
+ if (currentLayoutConfig.maxWidth) maxWidth = currentLayoutConfig.maxWidth;
+ }
+
+ return { minWidth, maxWidth };
+ }, [appLayout]);
+
+ /**
+ * calculate the width for the canvas
+ *
+ * cases:
+ * - if max width is negative, use calculated width
+ * - if calculated width is in range of min/max widths of layout, use calculated width
+ * - if calculated width is less then min width, use min Width
+ * - if calculated width is larger than max width, use max width
+ * - by default use min width
+ *
+ * @param screenWidth
+ * @param layoutMaxWidth
+ * @returns
+ */
+ const calculateCanvasWidth = () => {
+ const { maxWidth, minWidth } = layoutWidthRange;
+ let calculatedWidth = screenWidth - scrollbarWidth();
+
+ // if preview mode is on, we don't need to subtract the Property Pane width
+ if (isPreviewMode === false) {
+ const propertyPaneWidth = domPropertyPane?.clientWidth || 0;
+
+ calculatedWidth -= propertyPaneWidth;
+ }
+
+ // if explorer is unpinned or its preview mode, we don't need to subtract the EE width
+ if (isExplorerPinned === true && isPreviewMode === false) {
+ const explorerWidth = domEntityExplorer?.clientWidth || 0;
+
+ calculatedWidth -= explorerWidth;
+ }
+
+ switch (true) {
+ case maxWidth < 0:
+ case appLayout?.type === "FLUID":
+ case calculatedWidth < maxWidth && calculatedWidth > minWidth:
+ return (
+ calculatedWidth -
+ (appMode === APP_MODE.EDIT && !isPreviewMode
+ ? BORDERS_WIDTH + GUTTER_WIDTH
+ : 0)
+ );
+ case calculatedWidth < minWidth:
+ return minWidth;
+ case calculatedWidth > maxWidth:
+ return maxWidth;
+ default:
+ return minWidth;
}
};
- const resizeToLayout = (
- screenWidth: number,
- appLayout = AppsmithDefaultLayout,
- ) => {
- const { type } = appLayout;
- const { minWidth = -1, maxWidth = -1 } =
- layoutConfigurations[type] || layoutConfigurations[DefaultLayoutType];
- const calculatedMinWidth =
- appMode === APP_MODE.EDIT
- ? minWidth - parseInt(theme.sidebarWidth)
- : minWidth;
- const layoutWidth = calculateFluidMaxWidth(screenWidth, maxWidth);
+ /**
+ * resizes the layout based on the layout type
+ *
+ * @param screenWidth
+ * @param appLayout
+ */
+ const resizeToLayout = () => {
+ const calculatedWidth = calculateCanvasWidth();
const { rightColumn } = mainContainer || {};
- if (
- (type === "FLUID" || calculatedMinWidth <= layoutWidth) &&
- rightColumn !== layoutWidth
- ) {
- dispatch({
- type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT,
- payload: {
- width: layoutWidth,
- height: mainContainer?.minHeight,
- },
- });
+
+ if (rightColumn !== calculatedWidth) {
+ dispatch(
+ updateCanvasLayoutAction(calculatedWidth, mainContainer?.minHeight),
+ );
}
};
const debouncedResize = useCallback(debounce(resizeToLayout, 250), [
mainContainer,
+ screenWidth,
]);
+ /**
+ * when screen height is changed, update canvas layout
+ */
useEffect(() => {
- const calculatedMinHeight = calculateDynamicHeight(
- canvasWidgets,
- mainContainer?.minHeight,
- );
if (calculatedMinHeight !== mainContainer?.minHeight) {
- dispatch({
- type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT,
- payload: {
- height: calculatedMinHeight,
- width: mainContainer?.rightColumn,
- },
- });
+ dispatch(
+ updateCanvasLayoutAction(
+ mainContainer?.rightColumn,
+ calculatedMinHeight,
+ ),
+ );
}
}, [screenHeight, mainContainer?.minHeight]);
useEffect(() => {
- debouncedResize(screenWidth, appLayout);
+ debouncedResize();
}, [screenWidth]);
+ /**
+ * resize the layout if any of the following thing changes:
+ * - app layout
+ * - page
+ * - container right column
+ * - preview mode
+ * - explorer width
+ * - explorer is pinned
+ */
useEffect(() => {
- resizeToLayout(screenWidth, appLayout);
- }, [appLayout, currentPageId, mainContainer?.rightColumn]);
+ resizeToLayout();
+ }, [
+ appLayout,
+ currentPageId,
+ mainContainer?.rightColumn,
+ isPreviewMode,
+ explorerWidth,
+ isExplorerPinned,
+ initialized,
+ ]);
+
+ /**
+ * calling the setInitialized here so that property pane width is initialized
+ */
+ useEffect(() => {
+ setInitialized(true);
+ });
};
diff --git a/app/client/src/utils/hooks/useHorizontalResize.tsx b/app/client/src/utils/hooks/useHorizontalResize.tsx
new file mode 100644
index 0000000000..ef9e50a0b8
--- /dev/null
+++ b/app/client/src/utils/hooks/useHorizontalResize.tsx
@@ -0,0 +1,140 @@
+import React, { useState, useEffect, MutableRefObject } from "react";
+
+import { unFocus } from "utils/helpers";
+
+/**
+ * use horizontal resize
+ *
+ * @param ref
+ * @param onWidthChange
+ */
+const useHorizontalResize = (
+ ref: MutableRefObject,
+ onWidthChange?: (newWidth: number) => void,
+ onDragEnd?: () => void,
+ inverse = false,
+) => {
+ let MIN_WIDTH = 0;
+ let MAX_WIDTH = 0;
+ const [resizing, setResizing] = useState(false);
+ const [position, setPosition] = useState(0);
+
+ // saving min width and max width
+ useEffect(() => {
+ if (ref.current) {
+ MIN_WIDTH = parseInt(
+ window.getComputedStyle(ref.current).minWidth.replace("px", ""),
+ );
+ MAX_WIDTH = parseInt(
+ window.getComputedStyle(ref.current).maxWidth.replace("px", ""),
+ );
+ }
+ });
+
+ // registering event listeners
+ useEffect(() => {
+ document.addEventListener("mouseup", onMouseUp);
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("touchmove", onTouchMove);
+
+ return () => {
+ document.removeEventListener("mouseup", onMouseUp);
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("touchmove", onTouchMove);
+ };
+ }, [resizing, position]);
+
+ /**
+ * passing the event to touch start on mouse down
+ *
+ * @param event
+ */
+ const onMouseDown = (event: React.MouseEvent) => {
+ const eventWithTouches = Object.assign({}, event, {
+ touches: [{ clientX: event.clientX, clientY: event.clientY }],
+ });
+
+ onTouchStart(eventWithTouches);
+ };
+
+ /**
+ * sets resizing and position on touch start
+ */
+ const onTouchStart = (
+ event:
+ | React.TouchEvent
+ | (React.MouseEvent & {
+ touches: { clientX: number; clientY: number }[];
+ }),
+ ) => {
+ unFocus(document, window);
+ setPosition(event.touches[0].clientX);
+ setResizing(true);
+ document.body.classList.add("cursor-ew-resize");
+ };
+
+ /**
+ * sets resizing false on mouse up
+ * also calls onDragFinished if any
+ */
+ const onMouseUp = () => {
+ if (resizing) {
+ if (typeof onDragEnd === "function") {
+ onDragEnd();
+ }
+
+ setResizing(false);
+ document.body.classList.remove("cursor-ew-resize");
+ }
+ };
+
+ /**
+ * passing the event to touch move on mouse move
+ */
+ const onMouseMove = (event: MouseEvent) => {
+ const eventWithTouches = Object.assign({}, event, {
+ touches: [{ clientX: event.clientX, clientY: event.clientY }],
+ });
+ onTouchMove(eventWithTouches);
+ };
+
+ /**
+ * calculate the new width based on the pixel moved
+ *
+ * @param event
+ */
+ const onTouchMove = (
+ event:
+ | TouchEvent
+ | (MouseEvent & { touches: { clientX: number; clientY: number }[] }),
+ ) => {
+ if (resizing) {
+ unFocus(document, window);
+
+ if (ref.current) {
+ const width = ref.current.getBoundingClientRect().width;
+ const current = event.touches[0].clientX;
+ const positionDelta = position - current;
+ const widthDelta = inverse ? -positionDelta : positionDelta;
+ let newWidth = width - widthDelta;
+ const newPosition = position - positionDelta;
+
+ if (newWidth < MIN_WIDTH) {
+ newWidth = MIN_WIDTH;
+ } else if (newWidth > MAX_WIDTH) {
+ newWidth = MAX_WIDTH;
+ } else {
+ setPosition(newPosition);
+ }
+
+ if (typeof onWidthChange === "function") {
+ onWidthChange(newWidth);
+ }
+ }
+ }
+ };
+
+ return { onTouchStart, onMouseDown, onMouseUp, resizing };
+};
+
+export default useHorizontalResize;
diff --git a/app/client/src/utils/migrations/TableWidget.ts b/app/client/src/utils/migrations/TableWidget.ts
index 5f9f5b8dd1..e63033b30c 100644
--- a/app/client/src/utils/migrations/TableWidget.ts
+++ b/app/client/src/utils/migrations/TableWidget.ts
@@ -462,3 +462,27 @@ export const migrateTableSanitizeColumnKeys = (currentDSL: DSLWidget) => {
return currentDSL;
};
+
+export const migrateTableWidgetIconButtonVariant = (currentDSL: DSLWidget) => {
+ currentDSL.children = currentDSL.children?.map((child: WidgetProps) => {
+ if (child.type === "TABLE_WIDGET") {
+ const primaryColumns = child.primaryColumns as Record<
+ string,
+ ColumnProperties
+ >;
+ Object.keys(primaryColumns).forEach((accessor: string) => {
+ const primaryColumn = primaryColumns[accessor];
+
+ if (primaryColumn.columnType === "iconButton") {
+ if (!("buttonVariant" in primaryColumn)) {
+ primaryColumn.buttonVariant = "TERTIARY";
+ }
+ }
+ });
+ } else if (child.children && child.children.length > 0) {
+ child = migrateTableWidgetIconButtonVariant(child);
+ }
+ return child;
+ });
+ return currentDSL;
+};
diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx
index c69f702249..8ad7b414a1 100644
--- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx
+++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx
@@ -71,7 +71,7 @@ class AudioRecorderWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the recording starts",
diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx
index 86037d3d3a..11ab7cbe2c 100644
--- a/app/client/src/widgets/AudioWidget/widget/index.tsx
+++ b/app/client/src/widgets/AudioWidget/widget/index.tsx
@@ -67,7 +67,7 @@ class AudioWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the audio is played",
diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx
index 6a91b33989..4837396c6a 100644
--- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx
+++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx
@@ -9,6 +9,7 @@ import {
ButtonBoxShadow,
ButtonVariant,
ButtonBorderRadiusTypes,
+ ButtonVariantTypes,
} from "components/constants";
import ButtonGroupComponent from "../component";
@@ -40,30 +41,6 @@ class ButtonGroupWidget extends BaseWidget<
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
- {
- propertyName: "buttonVariant",
- label: "Button Variant",
- controlType: "DROP_DOWN",
- helpText: "Sets the variant of the icon button",
- options: [
- {
- label: "Primary",
- value: "PRIMARY",
- },
- {
- label: "Secondary",
- value: "SECONDARY",
- },
- {
- label: "Tertiary",
- value: "TERTIARY",
- },
- ],
- isJSConvertible: true,
- isBindProperty: false,
- isTriggerProperty: false,
- validation: { type: ValidationTypes.TEXT },
- },
{
helpText: "Controls the visibility of the widget",
propertyName: "isVisible",
@@ -376,6 +353,30 @@ class ButtonGroupWidget extends BaseWidget<
{
sectionName: "Styles",
children: [
+ {
+ propertyName: "buttonVariant",
+ label: "Button Variant",
+ controlType: "DROP_DOWN",
+ helpText: "Sets the variant of the button",
+ options: [
+ {
+ label: "Primary",
+ value: ButtonVariantTypes.PRIMARY,
+ },
+ {
+ label: "Secondary",
+ value: ButtonVariantTypes.SECONDARY,
+ },
+ {
+ label: "Tertiary",
+ value: ButtonVariantTypes.TERTIARY,
+ },
+ ],
+ isJSConvertible: true,
+ isBindProperty: false,
+ isTriggerProperty: false,
+ validation: { type: ValidationTypes.TEXT },
+ },
{
propertyName: "borderRadius",
label: "Border Radius",
diff --git a/app/client/src/widgets/ButtonWidget/component/index.tsx b/app/client/src/widgets/ButtonWidget/component/index.tsx
index 995c4164e9..9cb57e4dd6 100644
--- a/app/client/src/widgets/ButtonWidget/component/index.tsx
+++ b/app/client/src/widgets/ButtonWidget/component/index.tsx
@@ -160,7 +160,6 @@ const StyledButton = styled((props) => (
}
`}
-
border-radius: ${({ borderRadius }) =>
borderRadius === ButtonBorderRadiusTypes.ROUNDED ? "5px" : 0};
diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx
index 5b116e2a50..96e9f1e7d5 100644
--- a/app/client/src/widgets/ButtonWidget/widget/index.tsx
+++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx
@@ -104,7 +104,7 @@ class ButtonWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the button is clicked",
diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts
index f04d1f5819..2a7460264d 100644
--- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts
+++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts
@@ -295,7 +295,7 @@ export default [
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the chart data point is clicked",
diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx
index cd97fa1b6b..1622180a48 100644
--- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx
+++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx
@@ -166,7 +166,7 @@ class CheckboxGroupWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the check state is changed",
diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx
index 1385637f7e..51faa7c9bb 100644
--- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx
+++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx
@@ -84,7 +84,7 @@ class CheckboxWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the check state is changed",
diff --git a/app/client/src/widgets/DatePickerWidget/widget/index.tsx b/app/client/src/widgets/DatePickerWidget/widget/index.tsx
index 0f38289c27..5d4ea10922 100644
--- a/app/client/src/widgets/DatePickerWidget/widget/index.tsx
+++ b/app/client/src/widgets/DatePickerWidget/widget/index.tsx
@@ -284,7 +284,7 @@ class DatePickerWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
propertyName: "onDateSelected",
diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx
index 6060a11771..c4a7a8ae63 100644
--- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx
+++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx
@@ -198,7 +198,7 @@ class DatePickerWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
propertyName: "onDateSelected",
diff --git a/app/client/src/widgets/DividerWidget/widget/index.test.tsx b/app/client/src/widgets/DividerWidget/widget/index.test.tsx
index f47c547d34..f17732b277 100644
--- a/app/client/src/widgets/DividerWidget/widget/index.test.tsx
+++ b/app/client/src/widgets/DividerWidget/widget/index.test.tsx
@@ -26,6 +26,9 @@ describe("", () => {
comments: {
dragPointerOffset: null,
},
+ editor: {
+ isPreviewMode: false,
+ },
},
entities: { canvasWidgets: {}, app: { mode: "canvas" } },
};
diff --git a/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx
new file mode 100644
index 0000000000..196fcd0d0a
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx
@@ -0,0 +1,53 @@
+import React, { useEffect, useState } from "react";
+import mammoth from "mammoth";
+import styled from "styled-components";
+import Interweave from "interweave";
+
+const StyledViewer = styled.div`
+ width: 100%;
+ height: 100%;
+ background: #fff;
+ overflow: auto;
+`;
+export default function DocViewer(props: { blob?: Blob }) {
+ const [state, setState] = useState({ isLoading: false, isError: false });
+ const [htmlContent, setHtmlContent] = useState("");
+ // when DocViewer gets new Blob of uploaded file convert it to html for preview
+ useEffect(() => {
+ setState({ isLoading: true, isError: false });
+ setHtmlContent("");
+ props.blob
+ ?.arrayBuffer()
+ .then((buffer) => {
+ mammoth
+ .convertToHtml(
+ { arrayBuffer: buffer },
+ { includeEmbeddedStyleMap: true, includeDefaultStyleMap: true },
+ )
+ .then((result) => {
+ setState({ isLoading: false, isError: false });
+ setHtmlContent(result.value);
+ })
+ .catch(() => {
+ setHtmlContent("");
+ setState({ isLoading: false, isError: true });
+ });
+ })
+ .catch(() => {
+ setState({ isLoading: false, isError: false });
+ setHtmlContent("");
+ });
+ }, [props.blob]);
+ return (
+
+
+ {state.isLoading ? (
+ Loading...
+ ) : state.isError ? (
+ Failed to read docx content
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx
new file mode 100644
index 0000000000..ab034f2edd
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx
@@ -0,0 +1,204 @@
+import React, { useEffect, useState, useRef, useCallback } from "react";
+import styled from "styled-components";
+import Excel from "exceljs-lightweight";
+import { useTable, Column } from "react-table";
+import _ from "lodash";
+
+const StyledViewer = styled.div`
+ width: 100%;
+ height: 100%;
+ background: #fff;
+ overflow: auto;
+
+ table {
+ border: 1px solid #b0cbef;
+ border-width: 1px 0 0 1px;
+ border-spacing: 0;
+ border-collapse: collapse;
+ padding: 10px;
+
+ th {
+ font-weight: 700;
+ font-size: 14px;
+ border: 1px solid #9eb6ce;
+ border-width: 0 1px 1px 0;
+ height: 17px;
+ line-height: 17px;
+ text-align: center;
+ background: #9eb6ce4d;
+ }
+
+ td {
+ background-color: #fff;
+ padding: 0 4px 0 2px;
+ border: 1px solid #d0d7e5;
+ border-width: 0 1px 1px 0;
+ }
+ }
+`;
+
+const chars = [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+];
+
+// get excel column name from index, e.g. A,B,...,AA,AB
+const numberToExcelHeader = (index: number): string => {
+ index -= 1;
+ const quotient = Math.floor(index / 26);
+ if (quotient > 0) {
+ return numberToExcelHeader(quotient) + chars[index % 26];
+ }
+ return chars[index % 26];
+};
+
+type sheetsDataType = {
+ name: string;
+ id: number;
+};
+
+export default function XlsxViewer(props: { blob?: Blob }) {
+ const [sheets, setSheets] = useState([] as sheetsDataType[]);
+ const [tableData, setTableData] = useState([]);
+ const [headerData, setHeaderData] = useState([] as Column[]);
+ const workbook = useRef(new Excel.Workbook());
+
+ useEffect(() => {
+ props.blob?.arrayBuffer().then((buffer) => {
+ // read excel
+ workbook.current.xlsx.load(buffer).then(() => {
+ const newSheets = [] as any;
+ // get all sheets from excel
+ workbook.current.eachSheet((sheet, id) => {
+ newSheets.push({ name: sheet.name, id });
+ });
+ setSheets(newSheets);
+ // get 1st sheet data
+ getSheetData(1);
+ });
+ });
+ }, [props.blob]);
+
+ // get provided sheet data, read all row and columns
+ const getSheetData = useCallback((sheetId: number) => {
+ const worksheet = workbook.current.getWorksheet(sheetId);
+ // collect all row data
+ const data = [] as any;
+ worksheet.eachRow({ includeEmpty: true }, (row) => {
+ const currRow = {} as any;
+ // read value of each cell of current row
+ row.eachCell((cell) => {
+ // value can be merged value | Date | formula result | string | number
+ let value: any;
+ if (cell.isMerged) {
+ value = _.get(cell, "_mergeCount") ? cell.value : "";
+ } else {
+ value = cell.value;
+ }
+ if (_.isDate(value)) {
+ value = value.toDateString();
+ } else if (_.isObject(value) && _.has(value, "result")) {
+ value = _.get(value, "result", "");
+ }
+ currRow[String(numberToExcelHeader(Number(cell.col)))] = value;
+ });
+ data.push(currRow);
+ });
+ setTableData(data);
+ if (data.length) {
+ // create header letters based on columnCount
+ const newHeader = [];
+ for (let index = 1; index <= worksheet.columnCount; index++) {
+ const currHeader = numberToExcelHeader(index);
+ newHeader.push({
+ Header: currHeader,
+ accessor: currHeader,
+ });
+ }
+ setHeaderData(newHeader);
+ } else {
+ setHeaderData([]);
+ }
+ }, []);
+
+ // when user click on another sheet, re-generate data
+ const updateSheet = useCallback(
+ (sheetId) => () => {
+ getSheetData(sheetId);
+ },
+ [],
+ );
+
+ const {
+ getTableBodyProps,
+ getTableProps,
+ headerGroups,
+ prepareRow,
+ rows,
+ } = useTable({ columns: headerData, data: tableData });
+
+ return (
+
+
+ {sheets.map((sheet) => (
+
+ ))}
+
+
+
+ {headerGroups.map((headerGroup, hgInd) => (
+
+ {headerGroup.headers.map((column, colId) => (
+ |
+ {column.render("Header")}
+ |
+ ))}
+
+ ))}
+
+
+ {rows.map((row, rInd) => {
+ prepareRow(row);
+ return (
+
+ {row.cells.map((cell, ind) => {
+ return (
+ |
+ {cell.render("Cell")}
+ |
+ );
+ })}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx
new file mode 100644
index 0000000000..a6a2c4e402
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx
@@ -0,0 +1,68 @@
+import { getDocViewerConfigs } from "widgets/DocumentViewerWidget/component";
+import { Renderers } from "../constants";
+
+describe("validate document viewer url", () => {
+ it("validate correct config should return for extension based urls", () => {
+ const input = [
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx",
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt",
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf",
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf",
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt",
+ ];
+
+ const expected = [
+ {
+ url:
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx",
+ viewer: "office",
+ errorMessage: "",
+ renderer: Renderers.DOCUMENT_VIEWER,
+ },
+ {
+ url:
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt",
+ viewer: "url",
+ errorMessage: "Current file type is not supported",
+ renderer: Renderers.ERROR,
+ },
+ {
+ url:
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf",
+ viewer: "url",
+ errorMessage: "Current file type is not supported",
+ renderer: Renderers.ERROR,
+ },
+ {
+ url:
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf",
+ viewer: "url",
+ errorMessage: "",
+ renderer: Renderers.DOCUMENT_VIEWER,
+ },
+ {
+ url:
+ "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt",
+ viewer: "url",
+ errorMessage: "",
+ renderer: Renderers.DOCUMENT_VIEWER,
+ },
+ ];
+
+ for (let index = 0; index < input.length; index++) {
+ const result = getDocViewerConfigs(input[index]);
+ expect(result).toStrictEqual(expected[index]);
+ }
+ });
+
+ it("validate errorMessage should return for empty url", () => {
+ const input = "";
+ const result = getDocViewerConfigs(input);
+ expect(result).toStrictEqual({
+ url: "",
+ viewer: "url",
+ errorMessage: "No document url provided for viewer",
+ renderer: Renderers.ERROR,
+ });
+ });
+});
diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx
new file mode 100644
index 0000000000..a923f5a84f
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx
@@ -0,0 +1,220 @@
+import React, { Suspense, lazy } from "react";
+import styled from "styled-components";
+import { DocumentViewer } from "react-documents";
+import { includes, replace, split, get } from "lodash";
+import {
+ SUPPORTED_EXTENSIONS,
+ Renderers,
+ Renderer,
+ ViewerType,
+} from "../constants";
+import { retryPromise } from "utils/AppsmithUtils";
+import Skeleton from "components/utils/Skeleton";
+
+const DocViewer = lazy(() => retryPromise(() => import("./DocViewer")));
+const XlsxViewer = lazy(() => retryPromise(() => import("./XlsxViewer")));
+
+const ErrorWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: #fff;
+`;
+
+const checkUrlExtension = (docUrl: string) => {
+ // Remove everything to the last slash in URL
+ let url = docUrl.substr(1 + docUrl.lastIndexOf("/"));
+ // Break URL at ? and take first part (file name, extension)
+ url = url.split("?")[0];
+ // Sometimes URL doesn't have ? but #, so we should aslo do the same for #
+ url = url.split("#")[0];
+ // Now we have only filename and extension
+ const chunkList = url.split(".");
+ if (chunkList.length > 1) {
+ const ext = chunkList[chunkList.length - 1];
+ // check extension is valid or not
+ const validExtension = SUPPORTED_EXTENSIONS.includes(ext);
+ return {
+ hasExtension: true,
+ validExtension: validExtension,
+ extension: ext,
+ };
+ } else {
+ // might be preview url
+ return { hasExtension: false };
+ }
+};
+
+const getBlob = (docUrl: string) => {
+ // Split into two parts
+ const parts = docUrl.split(";base64,");
+ // Hold the content type
+ const mimeType = parts[0].split(":")[1];
+
+ try {
+ // Decode Base64 string
+ const decodedData = window.atob(parts[1]);
+ // Create UNIT8ARRAY of size same as row data length
+ const uInt8Array = new Uint8Array(decodedData.length);
+ // Insert all character code into uInt8Array
+ for (let i = 0; i < decodedData.length; ++i) {
+ uInt8Array[i] = decodedData.charCodeAt(i);
+ }
+ // Return BLOB image after conversion
+ const blob = new Blob([uInt8Array], { type: mimeType });
+ return blob;
+ } catch (error) {
+ return;
+ }
+};
+
+// get extension from base64
+const getFileExtensionFromBase64 = (docUrl: string) => {
+ let extension = "";
+ const fileType = docUrl.split(";")[0].split("/")[1];
+
+ switch (fileType) {
+ case "vnd.openxmlformats-officedocument.wordprocessingml.document":
+ extension = "docx";
+ break;
+ case "vnd.openxmlformats-officedocument.spreadsheetml.sheet":
+ extension = "xlsx";
+ break;
+ case "plain":
+ extension = "txt";
+ break;
+ case "pdf":
+ extension = "pdf";
+ break;
+ default:
+ break;
+ }
+
+ return extension;
+};
+
+interface ConfigResponse {
+ url: string;
+ blob?: Blob;
+ viewer: ViewerType;
+ renderer: Renderer;
+ errorMessage?: string;
+}
+
+export const getDocViewerConfigs = (docUrl: string): ConfigResponse => {
+ /**
+ * From user provided document url decide on which viewer to use
+ * handle different cases for url hosts
+ * Case 1: Dropbox
+ * if user exported download url than change that to raw url
+ * Case 2: Onedrive
+ * change url to embeded url to show in viewer
+ * Case 3:
+ * All other urls will be either Google Preview or urls with file extentions ( e.x. from aws s3 )
+ * need to verify urls with extention and use default viewer "url viewer" for them
+ */
+ let viewer = "url" as ViewerType;
+ let url = docUrl;
+ let blob;
+ let errorMessage = !!docUrl ? "" : "No document url provided for viewer";
+ let renderer: Renderer = errorMessage
+ ? Renderers.ERROR
+ : Renderers.DOCUMENT_VIEWER;
+
+ if (docUrl && includes(docUrl, "base64")) {
+ const extension = getFileExtensionFromBase64(docUrl);
+ const isValidExtension = SUPPORTED_EXTENSIONS.includes(extension);
+ if (isValidExtension) {
+ blob = getBlob(docUrl);
+ if (blob) {
+ if (extension === "docx") {
+ renderer = Renderers.DOCX_VIEWER;
+ } else if (extension === "xlsx") {
+ renderer = Renderers.XLSX_VIEWER;
+ }
+ } else {
+ errorMessage = "invalid base64 data";
+ renderer = Renderers.ERROR;
+ }
+ } else {
+ errorMessage = "Current file type is not supported " + extension;
+ renderer = Renderers.ERROR;
+ }
+ return { blob, url, viewer, errorMessage, renderer };
+ }
+
+ // handled dropbox url
+ if (includes(url, "dropbox.com")) {
+ if (includes(url, "?dl=0") || !includes(url, "?raw=1")) {
+ url = replace(url, "?dl=0", "");
+ url = url + "?raw=1";
+ }
+ return { url, viewer, renderer };
+ }
+ // handled onedrive url
+ if (includes(url, "onedrive.live.com")) {
+ const onedriveUrl = new URL(url);
+ const onedriveUrlParamList = split(onedriveUrl.search, /[?&=]+/);
+ const cid = get(
+ onedriveUrlParamList,
+ onedriveUrlParamList.indexOf("cid") + 1,
+ );
+ const resid = get(
+ onedriveUrlParamList,
+ onedriveUrlParamList.indexOf("id") + 1,
+ );
+
+ url = `https://onedrive.live.com/embed?cid=${cid}&resid=${resid}&em=2`;
+ return { url, viewer, renderer };
+ }
+
+ // check url extension and if it is supported
+ const { extension, hasExtension, validExtension } = checkUrlExtension(url);
+ if (hasExtension) {
+ if (validExtension) {
+ if (!(extension === "txt" || extension === "pdf")) {
+ viewer = "office";
+ renderer = Renderers.DOCUMENT_VIEWER;
+ }
+ } else {
+ errorMessage = "Current file type is not supported";
+ renderer = Renderers.ERROR;
+ }
+ }
+
+ return { url, viewer, errorMessage, renderer };
+};
+
+function DocumentViewerComponent(props: DocumentViewerComponentProps) {
+ const { blob, errorMessage, renderer, url, viewer } = React.useMemo(
+ () => getDocViewerConfigs(props.docUrl),
+ [props.docUrl],
+ );
+ switch (renderer) {
+ case Renderers.ERROR:
+ return {errorMessage};
+ case Renderers.DOCX_VIEWER:
+ return (
+ }>
+
+
+ );
+ case Renderers.XLSX_VIEWER:
+ return (
+ }>
+
+
+ );
+ case Renderers.DOCUMENT_VIEWER:
+ return ;
+
+ default:
+ return null;
+ }
+}
+
+export interface DocumentViewerComponentProps {
+ docUrl: string;
+}
+
+export default DocumentViewerComponent;
diff --git a/app/client/src/widgets/DocumentViewerWidget/constants.ts b/app/client/src/widgets/DocumentViewerWidget/constants.ts
new file mode 100644
index 0000000000..fc70832c8c
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/constants.ts
@@ -0,0 +1,23 @@
+// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders.
+export const DOCUMENTVIEWER_WIDGET_CONSTANT = "";
+// txt and pdf handle by viewerType = "url"
+// and other types handle by viewerType = "office"
+export const SUPPORTED_EXTENSIONS = [
+ "txt",
+ "pdf",
+ "docx",
+ "ppt",
+ "pptx",
+ "xlsx",
+];
+
+export const Renderers = {
+ DOCUMENT_VIEWER: "DOCUMENT_VIEWER",
+ DOCX_VIEWER: "DOCX_VIEWER",
+ XLSX_VIEWER: "XLSX_VIEWER",
+ ERROR: "ERROR",
+};
+
+export type Renderer = typeof Renderers[keyof typeof Renderers];
+
+export type ViewerType = "google" | "office" | "mammoth" | "pdf" | "url";
diff --git a/app/client/src/widgets/DocumentViewerWidget/icon.svg b/app/client/src/widgets/DocumentViewerWidget/icon.svg
new file mode 100644
index 0000000000..b48db5cb0a
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/icon.svg
@@ -0,0 +1,6 @@
+
diff --git a/app/client/src/widgets/DocumentViewerWidget/index.ts b/app/client/src/widgets/DocumentViewerWidget/index.ts
new file mode 100644
index 0000000000..a492c6c3cb
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/index.ts
@@ -0,0 +1,27 @@
+import Widget from "./widget";
+import IconSVG from "./icon.svg";
+import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
+
+export const CONFIG = {
+ type: Widget.getWidgetType(),
+ name: "Document Viewer", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces )
+ iconSVG: IconSVG,
+ needsMeta: false, // Defines if this widget adds any meta properties
+ isCanvas: false, // Defines if this widget has a canvas within in which we can drop other widgets
+ defaults: {
+ widgetName: "DocumentViewer",
+ docUrl:
+ "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf",
+ rows: 10 * GRID_DENSITY_MIGRATION_V1,
+ columns: 6 * GRID_DENSITY_MIGRATION_V1,
+ version: 1,
+ },
+ properties: {
+ derived: Widget.getDerivedPropertiesMap(),
+ default: Widget.getDefaultPropertiesMap(),
+ meta: Widget.getMetaPropertiesMap(),
+ config: Widget.getPropertyPaneConfig(),
+ },
+};
+
+export default Widget;
diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx
new file mode 100644
index 0000000000..19aab1c529
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx
@@ -0,0 +1,127 @@
+import { documentUrlValidation } from ".";
+
+describe("validate propertypane input : docUrl", () => {
+ it("validation for empty or space value", () => {
+ const input1 = "";
+ const expected1 = {
+ isValid: true,
+ parsed: "",
+ messages: [""],
+ };
+
+ const result = documentUrlValidation(input1);
+ expect(result).toStrictEqual(expected1);
+
+ const input2 = "https: //www.example.com";
+ const expected2 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result1 = documentUrlValidation(input2);
+ expect(result1).toStrictEqual(expected2);
+
+ const input3 = "https://www.exam ple.com";
+ const expected3 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result2 = documentUrlValidation(input3);
+ expect(result2).toStrictEqual(expected3);
+
+ const input4 = "https://examplecom";
+ const expected4 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result3 = documentUrlValidation(input4);
+ expect(result3).toStrictEqual(expected4);
+
+ const input6 = "://www.appsmith.com/docs/sample.pdf";
+ const expected6 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result5 = documentUrlValidation(input6);
+ expect(result5).toStrictEqual(expected6);
+ });
+
+ it("validation for invalid url or base64 value", () => {
+ const input1 = "htt";
+ const expected1 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result1 = documentUrlValidation(input1);
+ expect(result1).toStrictEqual(expected1);
+
+ const input2 = "data:application/pdf;base64";
+ const expected2 = {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+
+ const result2 = documentUrlValidation(input2);
+ expect(result2).toStrictEqual(expected2);
+ });
+
+ it("validation for valid url or base64 value", () => {
+ const input1 = "https://www.example.com";
+ const expected1 = {
+ isValid: true,
+ parsed: "https://www.example.com/",
+ };
+
+ const result1 = documentUrlValidation(input1);
+ expect(result1).toStrictEqual(expected1);
+
+ const input2 =
+ "data:application/pdf;base64,JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyAzMiBUZiggIFlPVVIgVEVYVCBIRVJFICAgKScgRVQKZW5kc3RyZWFtCmVuZG9iago0IDAgb2JqCjw8Ci9UeXBlIC9QYWdlCi9QYXJlbnQgNSAwIFIKL0NvbnRlbnRzIDkgMCBSCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9LaWRzIFs0IDAgUiBdCi9Db3VudCAxCi9UeXBlIC9QYWdlcwovTWVkaWFCb3ggWyAwIDAgMjUwIDUwIF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G";
+ const expected2 = {
+ isValid: true,
+ parsed: input2,
+ };
+
+ const result2 = documentUrlValidation(input2);
+ expect(result2).toStrictEqual(expected2);
+
+ const input3 = "https:www.appsmith.com/docs/sample.pdf";
+ const expected3 = {
+ isValid: true,
+ parsed: "https://www.appsmith.com/docs/sample.pdf",
+ };
+
+ const result3 = documentUrlValidation(input3);
+ expect(result3).toStrictEqual(expected3);
+
+ const input4 = "https://www.apsmith.com/docs/sample";
+ const expected4 = {
+ isValid: true,
+ parsed: "https://www.apsmith.com/docs/sample",
+ };
+
+ const result4 = documentUrlValidation(input4);
+ expect(result4).toStrictEqual(expected4);
+
+ const input5 =
+ "www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf";
+ const expected5 = {
+ isValid: true,
+ parsed:
+ "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf",
+ };
+
+ const result5 = documentUrlValidation(input5);
+ expect(result5).toStrictEqual(expected5);
+ });
+});
diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx
new file mode 100644
index 0000000000..b296327c30
--- /dev/null
+++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx
@@ -0,0 +1,121 @@
+import React from "react";
+import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget";
+import DocumentViewerComponent from "../component";
+import {
+ ValidationTypes,
+ ValidationResponse,
+} from "constants/WidgetValidation";
+import { AutocompleteDataType } from "utils/autocomplete/TernServer";
+
+export function documentUrlValidation(value: unknown): ValidationResponse {
+ // applied validations if value exist
+ if (value) {
+ const whiteSpaceRegex = /\s/g;
+ const urlRegex = /(?:https:\/\/|www)?([\da-z.-]+)\.([a-z.]{2,6})[/\w .-]*\/?/;
+ const base64Regex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
+ if (
+ urlRegex.test(value as string) &&
+ !whiteSpaceRegex.test(value as string)
+ ) {
+ if ((value as string).startsWith("www")) {
+ return {
+ isValid: true,
+ parsed: "https://" + value,
+ };
+ }
+ try {
+ const newUrl = new URL(value as string);
+ // URL is valid
+ return {
+ isValid: true,
+ parsed: newUrl.href,
+ };
+ } catch (error) {
+ return {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+ }
+ } else if (base64Regex.test(value as string)) {
+ // base 64 is valid
+ return {
+ isValid: true,
+ parsed: value,
+ };
+ } else {
+ // value is not valid URL / Base64
+ return {
+ isValid: false,
+ parsed: "",
+ messages: ["Provided URL / Base64 is invalid."],
+ };
+ }
+ }
+ // value is empty here
+ return {
+ isValid: true,
+ parsed: "",
+ messages: [""],
+ };
+}
+
+class DocumentViewerWidget extends BaseWidget<
+ DocumentViewerWidgetProps,
+ WidgetState
+> {
+ static getPropertyPaneConfig() {
+ return [
+ {
+ sectionName: "General",
+ children: [
+ {
+ helpText:
+ "Document url for preview. for URL, supported extensions are txt, pdf, docx, ppt, pptx, xlsx. ppt is currently not supported by base64.",
+ propertyName: "docUrl",
+ label: "Document Link",
+ controlType: "INPUT_TEXT",
+ placeholderText: "URL / Base64",
+ isBindProperty: true,
+ isTriggerProperty: false,
+ validation: {
+ type: ValidationTypes.FUNCTION,
+ params: {
+ fn: documentUrlValidation,
+ expected: {
+ type: "URL / Base64",
+ example: "https://www.example.com",
+ autocompleteDataType: AutocompleteDataType.STRING,
+ },
+ },
+ },
+ },
+ {
+ helpText: "Controls visibility of the widget",
+ propertyName: "isVisible",
+ label: "Visible",
+ controlType: "SWITCH",
+ isJSConvertible: true,
+ isBindProperty: true,
+ isTriggerProperty: false,
+ validation: { type: ValidationTypes.BOOLEAN },
+ },
+ ],
+ },
+ ];
+ }
+
+ getPageView() {
+ return ;
+ }
+
+ static getWidgetType(): string {
+ return "DOCUMENT_VIEWER_WIDGET";
+ }
+}
+
+export interface DocumentViewerWidgetProps extends WidgetProps {
+ docUrl: string;
+}
+
+export default DocumentViewerWidget;
diff --git a/app/client/src/widgets/DropdownWidget/component/index.tsx b/app/client/src/widgets/DropdownWidget/component/index.tsx
index 0374d1dab4..d573031539 100644
--- a/app/client/src/widgets/DropdownWidget/component/index.tsx
+++ b/app/client/src/widgets/DropdownWidget/component/index.tsx
@@ -13,6 +13,7 @@ import { Colors } from "constants/Colors";
import { TextSize } from "constants/WidgetConstants";
import { StyledLabel, TextLabelWrapper } from "./index.styled";
import Fuse from "fuse.js";
+import { WidgetContainerDiff } from "widgets/WidgetUtils";
import Icon from "components/ads/Icon";
const FUSE_OPTIONS = {
@@ -122,9 +123,22 @@ const StyledControlGroup = styled(ControlGroup)`
}
`;
-const DropdownStyles = createGlobalStyle<{ width: number }>`
+const DropdownStyles = createGlobalStyle<{
+ parentWidth: number;
+ dropDownWidth: number;
+ id: string;
+}>`
+${({ dropDownWidth, id, parentWidth }) => `
+ .select-popover-width-${id} {
+ min-width: ${parentWidth > dropDownWidth ? parentWidth : dropDownWidth}px;
+
+ & .${Classes.INPUT_GROUP} {
+ width: ${parentWidth > dropDownWidth ? parentWidth : dropDownWidth}px;
+ }
+ }
+`}
.select-popover-wrapper {
- width: 100%;
+ width: auto;
box-shadow: 0 6px 20px 0px rgba(0, 0, 0, 0.15) !important;
border-radius: 0;
background: white;
@@ -178,12 +192,12 @@ const DropdownStyles = createGlobalStyle<{ width: number }>`
margin-top: -3px;
max-width: 100%;
max-height: auto;
+ min-width: 0px !important;
}
&&&& .${Classes.MENU_ITEM} {
min-height: 38px;
padding: 9px 12px;
color: ${Colors.GREY_8};
- min-width: 180px;
&:hover{
background: ${Colors.GREEN_SOLID_LIGHT_HOVER};
}
@@ -243,8 +257,8 @@ class DropDownComponent extends React.Component<
]);
this.setState({ activeItemIndex });
};
-
- render = () => {
+ render() {
+ const id = _.uniqueId();
const {
compactMode,
disabled,
@@ -271,7 +285,11 @@ class DropDownComponent extends React.Component<
: this.props.placeholder || "-- Select --";
return (
-
+
{labelText && (
);
- };
+ }
itemListPredicate(query: string, items: DropdownOption[]) {
const fuse = new Fuse(items, FUSE_OPTIONS);
@@ -380,7 +398,6 @@ class DropDownComponent extends React.Component<
className={`single-select ${isFocused && "is-focused"}`}
key={option.value}
onClick={itemProps.handleClick}
- style={{ width: this.props.width - 7 }}
tabIndex={0}
text={option.label}
/>
@@ -403,6 +420,7 @@ export interface DropDownComponentProps extends ComponentProps {
isFilterable: boolean;
isValid: boolean;
width: number;
+ dropDownWidth: number;
height: number;
serverSideFiltering: boolean;
onFilterChange: (text: string) => void;
diff --git a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx
index 0c92d7dbb4..14f41d3866 100644
--- a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx
+++ b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx
@@ -30,6 +30,9 @@ describe("", () => {
comments: {
dragPointerOffset: null,
},
+ editor: {
+ isPreviewMode: false,
+ },
},
entities: { canvasWidgets: {}, app: { mode: "canvas" } },
};
diff --git a/app/client/src/widgets/DropdownWidget/widget/index.tsx b/app/client/src/widgets/DropdownWidget/widget/index.tsx
index ed2ed2e692..c5092950d4 100644
--- a/app/client/src/widgets/DropdownWidget/widget/index.tsx
+++ b/app/client/src/widgets/DropdownWidget/widget/index.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
@@ -11,7 +12,7 @@ import {
} from "constants/WidgetValidation";
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
-import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
+import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
function defaultOptionValueValidation(value: unknown): ValidationResponse {
if (typeof value === "string") return { isValid: true, parsed: value.trim() };
@@ -291,10 +292,12 @@ class DropdownWidget extends BaseWidget {
getPageView() {
const options = _.isArray(this.props.options) ? this.props.options : [];
+ const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
const selectedIndex = _.findIndex(this.props.options, {
value: this.props.defaultValue,
});
+ console.log("dropDownWidth Select", dropDownWidth);
const { componentHeight, componentWidth } = this.getComponentDimensions();
return (
@@ -307,6 +310,7 @@ class DropdownWidget extends BaseWidget {
)
}
disabled={this.props.isDisabled}
+ dropDownWidth={dropDownWidth}
height={componentHeight}
isFilterable={this.props.isFilterable}
isLoading={this.props.isLoading}
diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx
index a67a6f6f9e..67b66c123f 100644
--- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx
+++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx
@@ -187,7 +187,7 @@ class FilePickerWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText:
diff --git a/app/client/src/widgets/FilepickerWidget/widget/index.tsx b/app/client/src/widgets/FilepickerWidget/widget/index.tsx
index dcbc173312..347edd233c 100644
--- a/app/client/src/widgets/FilepickerWidget/widget/index.tsx
+++ b/app/client/src/widgets/FilepickerWidget/widget/index.tsx
@@ -186,7 +186,7 @@ class FilePickerWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText:
diff --git a/app/client/src/widgets/IconButtonWidget/component/index.tsx b/app/client/src/widgets/IconButtonWidget/component/index.tsx
index f330e3046a..8e552e6b19 100644
--- a/app/client/src/widgets/IconButtonWidget/component/index.tsx
+++ b/app/client/src/widgets/IconButtonWidget/component/index.tsx
@@ -58,7 +58,6 @@ export const StyledButton = styled((props) => (
])}
/>
))`
-
background-image: none !important;
height: ${({ dimension }) => (dimension ? `${dimension}px` : "auto")};
width: ${({ dimension }) => (dimension ? `${dimension}px` : "auto")};
@@ -136,7 +135,6 @@ export const StyledButton = styled((props) => (
}
`}
-
border-radius: ${({ borderRadius }) =>
borderRadius === ButtonBorderRadiusTypes.CIRCLE
? "50%"
diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx
index ec7136f18d..7fbb2fc8a0 100644
--- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx
+++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx
@@ -66,7 +66,7 @@ class IconButtonWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the button is clicked",
diff --git a/app/client/src/widgets/IframeWidget/component/index.tsx b/app/client/src/widgets/IframeWidget/component/index.tsx
index c6b47338eb..f452c2d111 100644
--- a/app/client/src/widgets/IframeWidget/component/index.tsx
+++ b/app/client/src/widgets/IframeWidget/component/index.tsx
@@ -3,9 +3,11 @@ import styled from "styled-components";
import { hexToRgba } from "widgets/WidgetUtils";
import { ComponentProps } from "widgets/BaseComponent";
-import { AppState } from "reducers";
import { useSelector } from "store";
-import { RenderMode, RenderModes } from "constants/WidgetConstants";
+import { RenderMode } from "constants/WidgetConstants";
+import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors";
+import { getAppMode } from "selectors/applicationSelectors";
+import { APP_MODE } from "entities/App";
interface IframeContainerProps {
borderColor?: string;
@@ -64,7 +66,6 @@ function IframeComponent(props: IframeComponentProps) {
borderWidth,
onMessageReceived,
onURLChanged,
- renderMode,
source,
title,
widgetId,
@@ -95,12 +96,8 @@ function IframeComponent(props: IframeComponentProps) {
}
}, [source]);
- const isPropertyPaneVisible = useSelector(
- (state: AppState) => state.ui.propertyPane.isVisible,
- );
- const selectedWidgetId = useSelector(
- (state: AppState) => state.ui.propertyPane.widgetId,
- );
+ const appMode = useSelector(getAppMode);
+ const selectedWidget = useSelector(getWidgetPropsForPropertyPane);
return (
- {renderMode === RenderModes.CANVAS &&
- !(isPropertyPaneVisible && widgetId === selectedWidgetId) && (
-
- )}
+ {appMode === APP_MODE.EDIT && widgetId !== selectedWidget?.widgetId && (
+
+ )}
{message ? message : }
diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx
index 48287294d2..3032d609b5 100644
--- a/app/client/src/widgets/IframeWidget/widget/index.tsx
+++ b/app/client/src/widgets/IframeWidget/widget/index.tsx
@@ -38,7 +38,7 @@ class IframeWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the source URL is changed",
diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx
index a65e0f6189..223899a88f 100644
--- a/app/client/src/widgets/ImageWidget/widget/index.tsx
+++ b/app/client/src/widgets/ImageWidget/widget/index.tsx
@@ -136,7 +136,7 @@ class ImageWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText:
diff --git a/app/client/src/widgets/InputWidget/widget/index.tsx b/app/client/src/widgets/InputWidget/widget/index.tsx
index 037b9be413..8a892fae4d 100644
--- a/app/client/src/widgets/InputWidget/widget/index.tsx
+++ b/app/client/src/widgets/InputWidget/widget/index.tsx
@@ -372,7 +372,7 @@ class InputWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the text is changed",
diff --git a/app/client/src/widgets/ListWidget/widget/index.tsx b/app/client/src/widgets/ListWidget/widget/index.tsx
index 6c36b53b95..8a282b1f94 100644
--- a/app/client/src/widgets/ListWidget/widget/index.tsx
+++ b/app/client/src/widgets/ListWidget/widget/index.tsx
@@ -36,6 +36,7 @@ import { ValidationTypes } from "constants/WidgetValidation";
import derivedProperties from "./parseDerivedProperties";
import { DSLWidget } from "widgets/constants";
import { entityDefinitions } from "utils/autocomplete/EntityDefinitions";
+import { escapeSpecialChars } from "../../WidgetUtils";
const LIST_WIDGEY_PAGINATION_HEIGHT = 36;
class ListWidget extends BaseWidget, WidgetState> {
@@ -446,15 +447,16 @@ class ListWidget extends BaseWidget, WidgetState> {
) {
const { jsSnippets } = getDynamicBindings(propertyValue);
const listItem = this.props.listData?.[itemIndex] || {};
-
+ const stringifiedListItem = JSON.stringify(listItem);
+ const escapedStringifiedListItem = escapeSpecialChars(
+ stringifiedListItem,
+ );
const newPropertyValue = jsSnippets.reduce(
(prev: string, next: string) => {
if (next.indexOf("currentItem") > -1) {
return (
prev +
- `{{((currentItem) => { ${next}})(JSON.parse('${JSON.stringify(
- listItem,
- )}'))}}`
+ `{{((currentItem) => { ${next}})(JSON.parse('${escapedStringifiedListItem}'))}}`
);
}
return prev + `{{${next}}}`;
diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts
index cc6316a864..617dad65ae 100644
--- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts
+++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts
@@ -96,7 +96,7 @@ const PropertyPaneConfig = [
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when a grid list item is clicked",
diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx
index 81ea19cedb..81e6303d6a 100644
--- a/app/client/src/widgets/MapWidget/widget/index.tsx
+++ b/app/client/src/widgets/MapWidget/widget/index.tsx
@@ -185,7 +185,7 @@ class MapWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
propertyName: "onMarkerClick",
diff --git a/app/client/src/widgets/MenuButtonWidget/component/index.tsx b/app/client/src/widgets/MenuButtonWidget/component/index.tsx
index 4711f01030..80032efb9a 100644
--- a/app/client/src/widgets/MenuButtonWidget/component/index.tsx
+++ b/app/client/src/widgets/MenuButtonWidget/component/index.tsx
@@ -20,7 +20,9 @@ import {
getCustomBorderColor,
getCustomHoverColor,
getCustomTextColor,
+ WidgetContainerDiff,
} from "widgets/WidgetUtils";
+import _ from "lodash";
type MenuButtonContainerProps = {
disabled?: boolean;
@@ -37,10 +39,27 @@ export const MenuButtonContainer = styled.div`
}
`;
-const PopoverStyles = createGlobalStyle`
+const PopoverStyles = createGlobalStyle<{
+ parentWidth: number;
+ menuDropDownWidth: number;
+ id: string;
+}>`
.menu-button-popover > .${Classes.POPOVER2_CONTENT} {
background: none;
}
+ ${({ id, menuDropDownWidth, parentWidth }) => `
+ .menu-button-width-${id} {
+
+ max-width: ${
+ menuDropDownWidth > parentWidth
+ ? `${menuDropDownWidth}px`
+ : `${parentWidth}px`
+ } !important;
+ min-width: ${
+ parentWidth > menuDropDownWidth ? parentWidth : menuDropDownWidth
+ }px !important;
+ }
+`}
`;
export interface BaseStyleProps {
@@ -193,6 +212,7 @@ const BaseMenuItem = styled(MenuItem)`
const StyledMenu = styled(Menu)`
padding: 0;
+ min-width: 0px;
`;
export interface PopoverContentProps {
@@ -357,6 +377,8 @@ export interface MenuButtonComponentProps {
iconAlign?: Alignment;
onItemClicked: (onClick: string | undefined) => void;
backgroundColor?: string;
+ width: number;
+ menuDropDownWidth: number;
}
function MenuButtonComponent(props: MenuButtonComponentProps) {
@@ -370,14 +392,21 @@ function MenuButtonComponent(props: MenuButtonComponentProps) {
isDisabled,
label,
menuColor,
+ menuDropDownWidth,
menuItems,
menuVariant,
onItemClicked,
+ width,
} = props;
+ const id = _.uniqueId();
return (
-
+
{
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText:
@@ -368,10 +369,15 @@ class MenuButtonWidget extends BaseWidget {
};
getPageView() {
+ const { componentWidth } = this.getComponentDimensions();
+ const menuDropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
+
return (
);
}
diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx
index f204ed5633..333c3d1119 100644
--- a/app/client/src/widgets/ModalWidget/component/index.tsx
+++ b/app/client/src/widgets/ModalWidget/component/index.tsx
@@ -73,11 +73,6 @@ const Container = styled.div<{
left: ${(props) => props.left}px;
bottom: ${(props) => props.bottom}px;
right: ${(props) => props.right}px;
- ${(props) => {
- if (props.isEditMode)
- return `transform: translate(${parseInt(props.theme.sidebarWidth) /
- 2}px) !important`;
- }}
}
}
}
diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx
index aaca9f8190..c6b0ffba57 100644
--- a/app/client/src/widgets/ModalWidget/widget/index.tsx
+++ b/app/client/src/widgets/ModalWidget/widget/index.tsx
@@ -48,7 +48,7 @@ export class ModalWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the modal is closed",
diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx
index 4d805b3ef3..242aa243bf 100644
--- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx
+++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx
@@ -76,7 +76,18 @@ const rcSelectDropdownSlideUpOut = keyframes`
}
`;
-export const DropdownStyles = createGlobalStyle`
+export const DropdownStyles = createGlobalStyle<{
+ parentWidth: number;
+ dropDownWidth: number;
+ id: string;
+}>`
+${({ dropDownWidth, id, parentWidth }) => `
+ .multiselecttree-popover-width-${id} {
+ min-width: ${
+ parentWidth > dropDownWidth ? parentWidth : dropDownWidth
+ }px !important;
+ }
+`}
.rc-tree-select-dropdown-hidden {
display: none;
}
@@ -252,7 +263,6 @@ border: 1px solid #E8E8E8;
}
.tree-multiselect-dropdown {
min-height: 100px;
- min-width: 250px !important;
position: absolute;
background: #fff;
width: 100%;
diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx
index fe7f708893..bee4c415b8 100644
--- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx
+++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx
@@ -23,6 +23,8 @@ import {
TextSize,
} from "constants/WidgetConstants";
import { Classes } from "@blueprintjs/core";
+import _ from "lodash";
+import { WidgetContainerDiff } from "widgets/WidgetUtils";
import Icon from "components/ads/Icon";
import { Colors } from "constants/Colors";
@@ -47,6 +49,8 @@ export interface TreeSelectProps
labelTextSize?: TextSize;
labelStyle?: string;
compactMode: boolean;
+ dropDownWidth: number;
+ width: number;
isValid: boolean;
}
@@ -90,6 +94,7 @@ function MultiTreeSelectComponent({
compactMode,
disabled,
dropdownStyle,
+ dropDownWidth,
expandAll,
isValid,
labelStyle,
@@ -102,6 +107,7 @@ function MultiTreeSelectComponent({
options,
placeholder,
value,
+ width,
}: TreeSelectProps): JSX.Element {
const [key, setKey] = useState(Math.random());
const _menu = useRef(null);
@@ -123,7 +129,7 @@ function MultiTreeSelectComponent({
}, []);
const onClear = useCallback(() => onChange([], []), []);
-
+ const id = _.uniqueId();
return (
}
>
-
+
{labelText && (
}
disabled={disabled}
- dropdownClassName="tree-multiselect-dropdown"
+ dropdownClassName={`tree-multiselect-dropdown multiselecttree-popover-width-${id}`}
dropdownStyle={dropdownStyle}
getPopupContainer={getDropdownPosition}
inputIcon={
diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx
index 93b84c6e25..75e9f0fcce 100644
--- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx
+++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx
@@ -11,7 +11,7 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
import { DefaultValueType } from "rc-select/lib/interface/generator";
import { Layers } from "constants/Layers";
import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil";
-import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
+import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants";
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
import MultiTreeSelectComponent from "../component";
@@ -309,7 +309,7 @@ class MultiSelectTreeWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when a user selects an option",
@@ -359,6 +359,8 @@ class MultiSelectTreeWidget extends BaseWidget<
: [];
const filteredValue = this.filterValues(values);
+ const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
+ const { componentWidth } = this.getComponentDimensions();
return (
);
}
diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx
index f6147427c6..d0ba954ac3 100644
--- a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx
+++ b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx
@@ -33,7 +33,18 @@ const rcSelectDropdownSlideUpOut = keyframes`
}
`;
-export const DropdownStyles = createGlobalStyle`
+export const DropdownStyles = createGlobalStyle<{
+ parentWidth: number;
+ dropDownWidth: number;
+ id: string;
+}>`
+${({ dropDownWidth, id, parentWidth }) => `
+ .multiselect-popover-width-${id} {
+ min-width: ${
+ parentWidth > dropDownWidth ? parentWidth : dropDownWidth
+ }px !important;
+ }
+`}
.rc-select-dropdown-hidden {
display: none;
}
@@ -58,7 +69,9 @@ export const DropdownStyles = createGlobalStyle`
.rc-select-item-option-content {
flex: 1 1 0;
overflow-wrap: break-word;
+ white-space: nowrap;
overflow: hidden;
+ text-overflow: ellipsis;
color: ${Colors.GREY_8};
font-weight: 400;
}
@@ -158,7 +171,6 @@ export const DropdownStyles = createGlobalStyle`
.multi-select-dropdown {
min-height: 100px;
- min-width: 170px !important;
position: absolute;
background: #fff;
width: auto;
@@ -166,6 +178,10 @@ export const DropdownStyles = createGlobalStyle`
margin-top: 5px;
background: white;
box-shadow: 0 6px 20px 0px rgba(0, 0, 0, 0.15) !important;
+ overflow-x: scroll;
+ > div {
+ min-width: ${({ dropDownWidth }) => dropDownWidth}px;
+ }
&&&& .${Classes.ALIGN_LEFT} {
font-size: 14px;
padding-left: 42px;
@@ -452,6 +468,9 @@ export const MultiSelectContainer = styled.div<{
`;
export const StyledCheckbox = styled(Checkbox)`
&&.${Classes.CHECKBOX}.${Classes.CONTROL} {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
height: 38px;
padding-bottom: 0 !important;
color: ${Colors.GREY_8} !important;
diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.tsx
index 22bf4bca44..18966e6f37 100644
--- a/app/client/src/widgets/MultiSelectWidget/component/index.tsx
+++ b/app/client/src/widgets/MultiSelectWidget/component/index.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
import React, { useEffect, useState, useCallback, useRef } from "react";
import Select, { SelectProps } from "rc-select";
import { DefaultValueType } from "rc-select/lib/interface/generator";
@@ -16,6 +17,8 @@ import {
import debounce from "lodash/debounce";
import Icon from "components/ads/Icon";
import { Classes } from "@blueprintjs/core";
+import { WidgetContainerDiff } from "widgets/WidgetUtils";
+import _ from "lodash";
import { Colors } from "constants/Colors";
const menuItemSelectedIcon = (props: { isSelected: boolean }) => {
@@ -34,6 +37,8 @@ export interface MultiSelectProps
onChange: (value: DefaultValueType) => void;
serverSideFiltering: boolean;
onFilterChange: (text: string) => void;
+ dropDownWidth: number;
+ width: number;
labelText?: string;
labelTextColor?: string;
labelTextSize?: TextSize;
@@ -48,6 +53,7 @@ function MultiSelectComponent({
compactMode,
disabled,
dropdownStyle,
+ dropDownWidth,
isValid,
labelStyle,
labelText,
@@ -60,6 +66,7 @@ function MultiSelectComponent({
placeholder,
serverSideFiltering,
value,
+ width,
}: MultiSelectProps): JSX.Element {
const [isSelectAll, setIsSelectAll] = useState(false);
const _menu = useRef(null);
@@ -138,6 +145,8 @@ function MultiSelectComponent({
return debounce(updateFilter, DEBOUNCE_TIMEOUT);
}, []);
+ const id = _.uniqueId();
+ console.log("dropDownWidth", dropDownWidth);
return (
}
>
-
+
{labelText && (
);
}
diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
index 137597f706..c3021e4305 100644
--- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
+++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
@@ -6,7 +6,7 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import { RadioOption } from "../constants";
import { ValidationTypes } from "constants/WidgetValidation";
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
-import { isArray } from "lodash";
+import { compact, isArray } from "lodash";
class RadioGroupWidget extends BaseWidget {
static getPropertyPaneConfig() {
@@ -99,7 +99,7 @@ class RadioGroupWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText:
@@ -144,7 +144,7 @@ class RadioGroupWidget extends BaseWidget {
key={this.props.widgetId}
label={`${this.props.label}`}
onRadioSelectionChange={this.onRadioSelectionChange}
- options={isArray(this.props.options) ? this.props.options : []}
+ options={isArray(this.props.options) ? compact(this.props.options) : []}
selectedOptionValue={this.props.selectedOptionValue}
widgetId={this.props.widgetId}
/>
diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx
index b245467320..14ae13a6dd 100644
--- a/app/client/src/widgets/RateWidget/widget/index.tsx
+++ b/app/client/src/widgets/RateWidget/widget/index.tsx
@@ -187,7 +187,7 @@ class RateWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the rate is changed",
diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx
index 7370dfab12..9b5f13dc74 100644
--- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx
+++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx
@@ -100,7 +100,7 @@ class RichTextEditorWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the text is changed",
diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx
index 0a63a66f1f..c1cae23891 100644
--- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx
+++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx
@@ -76,7 +76,18 @@ const rcSelectDropdownSlideUpOut = keyframes`
}
`;
-export const DropdownStyles = createGlobalStyle`
+export const DropdownStyles = createGlobalStyle<{
+ parentWidth: number;
+ dropDownWidth: number;
+ id: string;
+}>`
+${({ dropDownWidth, id, parentWidth }) => `
+ .treeselect-popover-width-${id} {
+ min-width: ${
+ parentWidth > dropDownWidth ? parentWidth : dropDownWidth
+ }px !important;
+ }
+`}
.rc-tree-select-dropdown-hidden {
display: none;
}
@@ -219,7 +230,6 @@ export const DropdownStyles = createGlobalStyle`
.tree-select-dropdown {
min-height: 100px;
- min-width: 250px !important;
position: absolute;
background: #fff;
width: 100%;
diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx
index a56cca01ee..db9b121b0b 100644
--- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx
+++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx
@@ -22,6 +22,8 @@ import {
TextSize,
} from "constants/WidgetConstants";
import { Classes } from "@blueprintjs/core";
+import { WidgetContainerDiff } from "widgets/WidgetUtils";
+import _ from "lodash";
import Icon from "components/ads/Icon";
import { Colors } from "constants/Colors";
@@ -45,6 +47,8 @@ export interface TreeSelectProps
labelTextSize?: TextSize;
labelStyle?: string;
compactMode: boolean;
+ dropDownWidth: number;
+ width: number;
isValid: boolean;
}
@@ -88,6 +92,7 @@ function SingleSelectTreeComponent({
compactMode,
disabled,
dropdownStyle,
+ dropDownWidth,
expandAll,
isValid,
labelStyle,
@@ -99,6 +104,7 @@ function SingleSelectTreeComponent({
options,
placeholder,
value,
+ width,
}: TreeSelectProps): JSX.Element {
const [key, setKey] = useState(Math.random());
const _menu = useRef(null);
@@ -119,6 +125,7 @@ function SingleSelectTreeComponent({
return document.querySelector(`.${CANVAS_CLASSNAME}`) as HTMLElement;
}, []);
const onClear = useCallback(() => onChange([], []), []);
+ const id = _.uniqueId();
return (
}
>
-
+
{labelText && (
}
disabled={disabled}
- dropdownClassName="tree-select-dropdown single-tree-select-dropdown"
+ dropdownClassName={`tree-select-dropdown single-tree-select-dropdown treeselect-popover-width-${id}`}
dropdownStyle={dropdownStyle}
getPopupContainer={getDropdownPosition}
inputIcon={
diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx
index c31e2eab79..796dbbf2f7 100644
--- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx
+++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx
@@ -12,7 +12,7 @@ import { DefaultValueType } from "rc-select/lib/interface/generator";
import { Layers } from "constants/Layers";
import { isString } from "../../../utils/helpers";
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
-import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
+import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants";
import SingleSelectTreeComponent from "../component";
function defaultOptionValueValidation(value: unknown): ValidationResponse {
@@ -273,7 +273,7 @@ class SingleSelectTreeWidget extends BaseWidget<
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when a user selects an option",
@@ -324,7 +324,8 @@ class SingleSelectTreeWidget extends BaseWidget<
: undefined;
const filteredValue = this.filterValues(values);
-
+ const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
+ const { componentWidth } = this.getComponentDimensions();
return (
);
}
diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx
index d9c5d3e510..a4995ebeb1 100644
--- a/app/client/src/widgets/SwitchWidget/widget/index.tsx
+++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx
@@ -77,7 +77,7 @@ class SwitchWidget extends BaseWidget {
],
},
{
- sectionName: "Actions",
+ sectionName: "Events",
children: [
{
helpText: "Triggers an action when the switch state is changed",
diff --git a/app/client/src/widgets/TableWidget/component/Table.tsx b/app/client/src/widgets/TableWidget/component/Table.tsx
index a85d317765..e2836894d7 100644
--- a/app/client/src/widgets/TableWidget/component/Table.tsx
+++ b/app/client/src/widgets/TableWidget/component/Table.tsx
@@ -230,9 +230,13 @@ export function Table(props: TableProps) {
width={props.width}
>
{
+ props.applyFilter([{ ...DEFAULT_FILTER }]);
+ }, []);
+
const columns: DropdownOption[] = props.columns
.map((column: ReactTableColumnProps) => {
const type = column.metaProperties?.type || "text";
@@ -153,7 +182,12 @@ function TableFilterPaneContent(props: TableFilterProps) {
}}
>
- {createMessage(TABLE_FILTER_COLUMN_TYPE_CALLOUT)}
+
+ {createMessage(TABLE_FILTER_COLUMN_TYPE_CALLOUT)}
+
+
+
+
e.stopPropagation()}>
{filters.map((filter: ReactTableFilter, index: number) => {
@@ -205,11 +239,11 @@ function TableFilterPaneContent(props: TableFilterProps) {
/>