Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled, { css } from "styled-components";
import type { BadgeKind } from "./Badge.types";
import type { BadgeKind, BadgeSize } from "./Badge.types";

const Kind = {
error: css`
Expand All @@ -11,14 +11,28 @@ const Kind = {
success: css`
--badge-color-bg: var(--ads-v2-color-fg-success);
`,
info: css`
--badge-color-bg: var(--ads-v2-color-fg);
`,
};

const Size = {
small: css`
width: 5px;
height: 5px;
`,
medium: css`
width: 8px;
height: 8px;
`,
};

export const StyledBadge = styled.div<{
kind?: BadgeKind;
size?: BadgeSize;
}>`
width: 8px;
height: 8px;
background-color: var(--badge-color-bg);
border-radius: 50%;
${({ kind }) => kind && Kind[kind]}
${({ size }) => size && Size[size]}
`;
11 changes: 9 additions & 2 deletions app/client/packages/design-system/ads/src/Badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import { StyledBadge } from "./Badge.styles";
* @param className
* @constructor
*/
export function Badge({ className, kind = "success", ...rest }: BadgeProps) {
return <StyledBadge className={className} kind={kind} {...rest} />;
export function Badge({
className,
kind = "success",
size = "medium",
...rest
}: BadgeProps) {
return (
<StyledBadge className={className} kind={kind} size={size} {...rest} />
);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { Kind } from "../__config__/types";

export type BadgeKind = Exclude<Kind, "info" | undefined>;
export type BadgeKind = Exclude<Kind, undefined>;
export type BadgeSize = "small" | "medium";

export interface BadgeProps {
/** visual style to be used indicating type of badge */
kind?: BadgeKind;
/** (try not to) pass addition classes here */
className?: string;
/** Size of the badge */
size?: BadgeSize;
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ export const RightControlWrapper = styled.div`
align-items: center;
`;

export const UnsavedChangesWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
`;

export const TopContentWrapper = styled.div`
display: flex;
align-items: center;
Expand Down
8 changes: 8 additions & 0 deletions app/client/packages/design-system/ads/src/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
StyledListItem,
TooltipTextWrapper,
TopContentWrapper,
UnsavedChangesWrapper,
} from "./List.styles";
import type { TextProps } from "../Text";
import { Text } from "../Text";
Expand All @@ -26,6 +27,7 @@ import {
ListItemTitleClassName,
} from "./List.constants";
import { useEventCallback } from "usehooks-ts";
import { Badge } from "../Badge";

function List({ children, className, groupTitle, ...rest }: ListProps) {
return groupTitle ? (
Expand Down Expand Up @@ -86,6 +88,7 @@ function ListItem(props: ListItemProps) {
hasError,
rightControl,
rightControlVisibility = "always",
showUnsavedChanges,
size = "md",
startIcon,
title,
Expand Down Expand Up @@ -159,6 +162,11 @@ function ListItem(props: ListItemProps) {
{rightControl}
</RightControlWrapper>
)}
{showUnsavedChanges ? (
<UnsavedChangesWrapper>
<Badge kind="info" size="small" />
</UnsavedChangesWrapper>
) : null}
</TopContentWrapper>
{isBlockDescription && (
<BottomContentWrapper data-isiconpresent={Boolean(startIcon)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface ListItemProps {
customTitleComponent?: ReactNode | ReactNode[];
/** dataTestId which will be used in automated tests */
dataTestId?: string;
/** Whether to show the unsaved changes indicator */
showUnsavedChanges?: boolean;
}

export interface ListProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EditableEntityName } from "../EditableEntityName";

import type { EditableDismissibleTabProps } from "./EditableDismissibleTab.types";
import { useActiveDoubleClick } from "../../__hooks__";
import { Badge } from "../../Badge";

export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
const {
Expand All @@ -22,6 +23,7 @@ export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
onEnterEditMode: propOnEnterEditMode,
onExitEditMode: propOnExitEditMode,
onNameSave,
showUnsavedChanges,
validateName,
} = props;

Expand Down Expand Up @@ -60,6 +62,7 @@ export const EditableDismissibleTab = (props: EditableDismissibleTabProps) => {
onNameSave={onNameSave}
validateName={validateName}
/>
{showUnsavedChanges ? <Badge kind="info" size="small" /> : null}
</DismissibleTab>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ export interface EditableDismissibleTabProps {
onNameSave: (name: string) => void;
/** Function to validate the name. */
validateName: (name: string) => string | null;
/** Show unsaved changes indicator. */
showUnsavedChanges?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { JSCollection } from "entities/JSCollection";

// Implementation exists in ee
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getHighlightedLines = (jsCollection: JSCollection): number[] => {
return [];
};
43 changes: 43 additions & 0 deletions app/client/src/ce/selectors/entitiesSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ export const getActions = (state: AppState): ActionDataState =>
export const getJSCollections = (state: AppState): JSCollectionDataState =>
state.entities.jsActions;

export const getAllJSCollectionActions = (state: AppState) => {
return state.entities.jsActions.flatMap(
(jsCollection) => jsCollection.config.actions,
);
};

export const getDatasource = (
state: AppState,
datasourceId: string,
Expand Down Expand Up @@ -1752,3 +1758,40 @@ export const getIsSavingEntityName = (

return isSavingEntityName;
};

export const getActionSchemaDirtyState = createSelector(
getAction,
(state: AppState) =>
getPluginByPackageName(state, PluginPackageName.APPSMITH_AI),
(action, agentPlugin) => {
if (!action) return false;

if (agentPlugin?.id === action.pluginId) {
return false;
}

return action.isDirtyMap?.SCHEMA_GENERATION;
},
);

export const getJSCollectionSchemaDirtyState = createSelector(
(state: AppState, collectionId: string) =>
getJSCollection(state, collectionId),
(jsCollection) => {
if (!jsCollection) return false;

return jsCollection.actions.some(
(action) => action.isDirtyMap?.SCHEMA_GENERATION,
);
},
);

export const getJSCollectionActionSchemaDirtyState = createSelector(
(state: AppState, collectionId: string, actionId: string) =>
getJSCollectionAction(state, collectionId, actionId),
(action) => {
if (!action) return false;

return action.isDirtyMap?.SCHEMA_GENERATION;
},
);
33 changes: 30 additions & 3 deletions app/client/src/components/editorComponents/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ export type EditorProps = EditorStyleProps &
removeHoverAndFocusStyle?: boolean;

customErrors?: LintError[];
highlightedLines?: number[]; // Array of line numbers to highlight
};

interface Props extends ReduxStateProps, EditorProps, ReduxDispatchProps {}
Expand Down Expand Up @@ -445,11 +446,11 @@ class CodeEditor extends Component<Props, State> {
this: CodeEditor,
editor: CodeMirror.Editor,
) {
// If you need to do something with the editor right after its been created,
// If you need to do something with the editor right after it's been created,
// put that code here.
//
// This helps with performance: finishInit() is called inside
// CodeMirrors `operation()` (https://codemirror.net/doc/manual.html#operation
// CodeMirror's `operation()` (https://codemirror.net/doc/manual.html#operation
// which means CodeMirror recalculates itself only one time, once all CodeMirror
// changes here are completed
//
Expand Down Expand Up @@ -500,7 +501,13 @@ class CodeEditor extends Component<Props, State> {

// Finally create the Codemirror editor
this.editor = CodeMirror(this.codeEditorTarget.current, options);
// DO NOT ADD CODE BELOW. If you need to do something with the editor right after it’s created,

// Add highlighting for initial render
if (this.props.highlightedLines?.length) {
this.updateLineHighlighting(this.props.highlightedLines);
}

// DO NOT ADD CODE BELOW. If you need to do something with the editor right after it's created,
// put that code into `options.finishInit()`.
}

Expand Down Expand Up @@ -612,6 +619,11 @@ class CodeEditor extends Component<Props, State> {
}
}

// Check if highlighted lines have changed
if (!isEqual(prevProps.highlightedLines, this.props.highlightedLines)) {
this.updateLineHighlighting(this.props.highlightedLines || []);
}

this.editor.operation(() => {
const editorValue = this.editor.getValue();
// Safe update of value of the editor when value updated outside the editor
Expand Down Expand Up @@ -1634,6 +1646,21 @@ class CodeEditor extends Component<Props, State> {
this.editor.setValue(value);
};

// Add new method to handle line highlighting
private updateLineHighlighting = (lines: number[]) => {
// Clear existing highlights
for (let i = 0; i < this.editor.lineCount(); i++) {
this.editor.removeLineClass(i, "background", "highlighted-line");
}

// Add new highlights
lines.forEach((lineNumber) => {
if (lineNumber >= 0 && lineNumber < this.editor.lineCount()) {
this.editor.addLineClass(lineNumber, "background", "highlighted-line");
}
});
};

render() {
const {
border,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ export const EditorWrapper = styled.div<{
.CodeMirror-activeline-background {
background-color: #ececec;
}

.highlighted-line {
background-color: rgba(255, 255, 0, 0.2);
}
}
.CodeMirror-guttermarker-subtle {
color: var(--ads-v2-color-fg-subtle);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getHighlightedLines } from "ce/pages/Editor/JSEditor/utils/getHighlightedLines";
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React, { useCallback, useMemo } from "react";
import { EntityItem, EntityContextMenu } from "@appsmith/ads";
import type { AppState } from "ee/reducers";
import { getJsCollectionByBaseId } from "ee/selectors/entitiesSelector";
import {
getJsCollectionByBaseId,
getJSCollectionSchemaDirtyState,
} from "ee/selectors/entitiesSelector";
import { useDispatch, useSelector } from "react-redux";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
Expand Down Expand Up @@ -105,6 +108,10 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
validateName,
]);

const isJSActionSchemaDirty = useSelector((state: AppState) =>
getJSCollectionSchemaDirtyState(state, item.key),
);

return (
<EntityItem
className={clsx("t--jsaction", {
Expand All @@ -118,6 +125,7 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
onDoubleClick={() => enterEditMode(jsAction.id)}
rightControl={contextMenu}
rightControlVisibility="hover"
showUnsavedChanges={isJSActionSchemaDirty}
startIcon={JsFileIconV2(16, 16)}
title={item.title}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EntityItem, EntityContextMenu } from "@appsmith/ads";
import type { AppState } from "ee/reducers";
import {
getActionByBaseId,
getActionSchemaDirtyState,
getDatasource,
getPlugins,
} from "ee/selectors/entitiesSelector";
Expand Down Expand Up @@ -117,6 +118,10 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
validateName,
]);

const isActionSchemaDirty = useSelector((state: AppState) =>
getActionSchemaDirtyState(state, action.id),
);

return (
<EntityItem
className="action t--action-entity"
Expand All @@ -128,6 +133,7 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
onDoubleClick={() => enterEditMode(action.id)}
rightControl={contextMenu}
rightControlVisibility="hover"
showUnsavedChanges={isActionSchemaDirty}
startIcon={icon}
title={item.title}
/>
Expand Down
Loading
Loading