Skip to content
Closed
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
29 changes: 0 additions & 29 deletions .github/workflows/secrets-scan.yml

This file was deleted.

12 changes: 11 additions & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
* @contentstack/security-admin
* @contentstack/ghost-pr-reviewers

.github/workflows/sca-scan.yml @contentstack/security-admin

.github/workflows/codeql-anaylsis.yml @contentstack/security-admin

**/.snyk @contentstack/security-admin

.github/workflows/policy-scan.yml @contentstack/security-admin

.github/workflows/issues-jira.yml @contentstack/security-admin
16 changes: 16 additions & 0 deletions src/visualBuilder/generators/generateOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { FieldDataType } from "../utils/types/index.types";
import { getFieldType } from "../utils/getFieldType";
import { getMultilinePlaintext } from "../utils/getMultilinePlaintext";
import { showAllHiddenHighlightedCommentIcons } from "./generateHighlightedComment";
import {
restoreTruncateStyles,
hasStoredLineClampStyles,
} from "../utils/truncateHandler";

/**
* Adds a focus overlay to the target element.
Expand Down Expand Up @@ -224,6 +228,18 @@ export function hideOverlay(params: HideOverlayParams): void {
VisualBuilder.VisualBuilderGlobalState.value.focusElementObserver =
null;
}

// Restore truncate styles for the previously selected element before hiding overlay
const previousSelectedElement =
VisualBuilder.VisualBuilderGlobalState.value
.previousSelectedEditableDOM;
if (
previousSelectedElement &&
hasStoredLineClampStyles(previousSelectedElement)
) {
restoreTruncateStyles(previousSelectedElement);
}

hideFocusOverlay({
visualBuilderContainer: params.visualBuilderContainer,
visualBuilderOverlayWrapper: params.visualBuilderOverlayWrapper,
Expand Down
17 changes: 17 additions & 0 deletions src/visualBuilder/listeners/mouseClick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ import { fixSvgXPath } from "../utils/collabUtils";
import { v4 as uuidV4 } from "uuid";
import { CslpData } from "../../cslp/types/cslp.types";
import { fetchEntryPermissionsAndStageDetails } from "../utils/fetchEntryPermissionsAndStageDetails";
import {
removeTruncateStyles,
restoreTruncateStyles,
hasStoredLineClampStyles,
} from "../utils/truncateHandler";

export type HandleBuilderInteractionParams = Omit<
EventListenerHandlerParams,
Expand Down Expand Up @@ -243,6 +248,14 @@ function cleanResidualsIfNeeded(
previousSelectedElement !== editableElement) ||
params.reEvaluate
) {
// Restore truncate styles for the previously selected element
if (
previousSelectedElement &&
hasStoredLineClampStyles(previousSelectedElement)
) {
restoreTruncateStyles(previousSelectedElement);
}

cleanIndividualFieldResidual({
overlayWrapper: params.overlayWrapper!,
visualBuilderContainer: params.visualBuilderContainer,
Expand Down Expand Up @@ -277,6 +290,10 @@ function addOverlayAndToolbar(
isVariant: boolean
) {
VisualBuilder.VisualBuilderGlobalState.value.isFocussed = true;

// Remove truncate styles from the focused element
removeTruncateStyles(editableElement);

addOverlay({
overlayWrapper: params.overlayWrapper,
resizeObserver: params.resizeObserver,
Expand Down
6 changes: 5 additions & 1 deletion src/visualBuilder/utils/enableInlineEditing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ export function enableInlineEditing({
{ textContent: expectedFieldData }
);

(editableElement as HTMLElement).style.visibility = "hidden";
// Hide original element immediately, disabling any transitions/animations
const originalElement = editableElement as HTMLElement;
originalElement.style.visibility = "hidden";
originalElement.style.transition = "none";
originalElement.style.animation = "none";

// set field type attribute to the pseudo editable field
// ensures proper keydown handling similar to the actual editable field
Expand Down
144 changes: 144 additions & 0 deletions src/visualBuilder/utils/truncateHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Utility module for handling -webkit-line-clamp, overflow, and white-space properties
* when elements are focused/unfocused in the visual builder
*/

interface LineClampStyles {
webkitLineClamp?: string;
overflow?: string;
display?: string;
webkitBoxOrient?: string;
whiteSpace?: string;
}

// WeakMap to store original styles for each element
const originalStyles = new WeakMap<Element, LineClampStyles>();

/**
* Removes truncate related styles from an element and stores the original values
* @param element - The element to remove line-clamp and white-space styles from
*/
export function removeTruncateStyles(element: Element): void {
if (!element || !(element instanceof HTMLElement)) return;

const computedStyles = window.getComputedStyle(element);
const styles: LineClampStyles = {};

// Store original styles if they exist
if (
computedStyles.webkitLineClamp &&
computedStyles.webkitLineClamp !== "none"
) {
styles.webkitLineClamp = computedStyles.webkitLineClamp;
}

if (computedStyles.overflow && computedStyles.overflow !== "visible") {
styles.overflow = computedStyles.overflow;
}

if (computedStyles.display && computedStyles.display.includes("box")) {
styles.display = computedStyles.display;
}

if (
computedStyles.webkitBoxOrient &&
computedStyles.webkitBoxOrient !== "horizontal"
) {
styles.webkitBoxOrient = computedStyles.webkitBoxOrient;
}

if (computedStyles.whiteSpace && computedStyles.whiteSpace === "nowrap") {
styles.whiteSpace = computedStyles.whiteSpace;
}

// Only store if we found line-clamp related styles
if (Object.keys(styles).length > 0) {
originalStyles.set(element, styles);

// Remove the styles
element.style.webkitLineClamp = "none";
element.style.overflow = "visible";
// Reset display if it was -webkit-box
if (styles.display?.includes("box")) {
element.style.display = "block";
}
if (styles.webkitBoxOrient) {
element.style.webkitBoxOrient = "horizontal";
}
if (styles.whiteSpace) {
element.style.whiteSpace = "normal";
}
}
}

/**
* Restores the original truncate related styles to an element
* @param element - The element to restore line-clamp and white-space styles to
*/
export function restoreTruncateStyles(element: Element): void {
if (!element || !(element instanceof HTMLElement)) return;

const storedStyles = originalStyles.get(element);
if (!storedStyles) return;

// Restore original styles
if (storedStyles.webkitLineClamp) {
element.style.webkitLineClamp = storedStyles.webkitLineClamp;
} else {
element.style.removeProperty("-webkit-line-clamp");
}

if (storedStyles.overflow) {
element.style.overflow = storedStyles.overflow;
} else {
element.style.removeProperty("overflow");
}

if (storedStyles.display) {
element.style.display = storedStyles.display;
} else {
element.style.removeProperty("display");
}

if (storedStyles.webkitBoxOrient) {
element.style.webkitBoxOrient = storedStyles.webkitBoxOrient;
} else {
element.style.removeProperty("-webkit-box-orient");
}

if (storedStyles.whiteSpace) {
element.style.whiteSpace = storedStyles.whiteSpace;
} else {
element.style.removeProperty("white-space");
}

// Clean up stored styles
originalStyles.delete(element);
}

/**
* Checks if an element has truncate or white-space styles applied
* @param element - The element to check
* @returns boolean indicating if the element has line-clamp or white-space styles
*/
export function hasTruncateStyles(element: Element): boolean {
if (!element || !(element instanceof HTMLElement)) return false;

const computedStyles = window.getComputedStyle(element);
return (
computedStyles.webkitLineClamp !== "none" ||
(computedStyles.overflow === "hidden" &&
computedStyles.display?.includes("box") &&
computedStyles.webkitBoxOrient === "vertical") ||
computedStyles.whiteSpace === "nowrap"
);
}

/**
* Checks if an element currently has stored truncate styles
* @param element - The element to check
* @returns boolean indicating if the element has stored styles
*/
export function hasStoredLineClampStyles(element: Element): boolean {
return originalStyles.has(element);
}
14 changes: 14 additions & 0 deletions src/visualBuilder/utils/updateFocussedState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import getChildrenDirection from "./getChildrenDirection";
import { getPsuedoEditableElementStyles } from "./getPsuedoEditableStylesElement";
import { isFieldDisabled } from "./isFieldDisabled";
import { fetchEntryPermissionsAndStageDetails } from "./fetchEntryPermissionsAndStageDetails";
import {
removeTruncateStyles,
restoreTruncateStyles,
hasStoredLineClampStyles,
} from "./truncateHandler";

interface ToolbarPositionParams {
focusedToolbar: HTMLElement | null;
Expand Down Expand Up @@ -140,6 +145,11 @@ export async function updateFocussedState({

hideHoverOutline(visualBuilderContainer);

// Remove line-clamp styles from the currently focused element
if (editableElement) {
removeTruncateStyles(editableElement);
}

// in every case, this function will bring cached values
// and this should be quick
const fieldSchema = await FieldSchemaMap.getFieldSchema(
Expand Down Expand Up @@ -265,6 +275,10 @@ export function updateFocussedStateOnMutation(
`[data-cslp-unique-id="${selectedElementCslpUniqueId}"]`
) || document.querySelector(`[data-cslp="${selectedElementCslp}"]`);
if (!newSelectedElement && resizeObserver) {
// Restore truncate styles for the selected element before hiding focus
if (selectedElement && hasStoredLineClampStyles(selectedElement)) {
restoreTruncateStyles(selectedElement);
}
hideFocusOverlay({
visualBuilderOverlayWrapper: focusOverlayWrapper,
focusedToolbar,
Expand Down
Loading