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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ See [LICENSE FILE](https://github.com/syncfusion/react-ui-components/blob/master


## Resources
* [Getting Started](https://react.syncfusion.com/getting-started/introduction)
* [Getting Started](https://react.syncfusion.com/overview/introduction)
* [View Online Demos](https://react.syncfusion.com/button)

## Pure React Components highlights
Expand Down Expand Up @@ -241,6 +241,36 @@ Built-in Material 3 stylesheets provide a modern, accessible design out of the b
</tr>
</table>

### Navigations

<table>
<tr>
<td>
<b>Context Menu</b>
</td>
<td rowspan="2">
<a href="https://www.npmjs.com/package/@syncfusion/react-navigations"><img src="https://ej2.syncfusion.com/github/images/npm-logo.png" alt="npm package @syncfusion/react-navigations" title="@syncfusion/react-navigations" style="height:20px;" />
</td>
<td>
<a href="components/navigations/src/contextMenu">Source</a>
</td>
<td>
<a href="https://react.syncfusion.com/context-menu">Live demo</a>
</td>
</tr>
<tr>
<td>
<b>Toolbar</b>
</td>
<td>
<a href="components/navigations/src/toolbar">Source</a>
</td>
<td>
<a href="https://react.syncfusion.com/toolbar">Live demo</a>
</td>
</tr>
</table>

## Support
Product support is available for through following mediums.
* Creating incident in Syncfusion [Direct-trac](https://www.syncfusion.com/support/directtrac/incidents?utm_source=npm&utm_campaign=react-ui-components) support system or [Community forum](https://www.syncfusion.com/forums/pure-react?utm_source=npm&utm_campaign=react-ui-components).
Expand All @@ -250,6 +280,6 @@ Built-in Material 3 stylesheets provide a modern, accessible design out of the b
Check the license detail [here](https://github.com/syncfusion/react-ui-components/blob/master/license).

## Changelog
Check the changelog [here](https://react-api.syncfusion.com/release-notes/29.2.4).
Check the changelog [here](https://react-api.syncfusion.com/release-notes/30.1.37).

&copy; Copyright 2025 Syncfusion, Inc. All Rights Reserved. The Syncfusion Essential Studio license and copyright applies to this distribution.
2 changes: 1 addition & 1 deletion components/base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ Check the changelog [here](https://github.com/syncfusion/react-ui-components/blo

See [LICENSE FILE](https://github.com/syncfusion/react-ui-components/blob/master/license?utm_source=npm&utm_campaign=notification) for more info.

&copy; Copyright 2025 Syncfusion, Inc. All Rights Reserved. The Syncfusion Essential Studio license and copyright applies to this distribution.
&copy; Copyright 2025 Syncfusion, Inc. All Rights Reserved. The Syncfusion Essential Studio license and copyright applies to this distribution.
7 changes: 5 additions & 2 deletions components/base/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@syncfusion/react-base",
"version": "29.2.4",
"description": "A common package of core Pure React base, methods and class definitions",
"version": "30.1.37",
"description": "A common package of core React base, methods and class definitions",
"author": "Syncfusion Inc.",
"license": "SEE LICENSE IN license",
"keywords": [
Expand All @@ -21,6 +21,9 @@
"homepage": "https://www.syncfusion.com/react-ui-components",
"module": "./index.js",
"readme": "README.md",
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
},
"devDependencies": {
"gulp": "4.0.2",
"gulp-typescript": "5.0.1",
Expand Down
211 changes: 211 additions & 0 deletions components/base/src/drag-util.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import { select } from './dom';
import { IPosition } from './draggable';
import { isNullOrUndefined } from './util';

/**
* Position coordinates
*/
export interface PositionCoordinates {
left?: number;
top?: number;
bottom?: number;
right?: number;
}

/**
* Coordinates for element position
*
* @private
*/
export interface Coordinates {
/**
* Defines the x Coordinate of page.
*/
pageX?: number;
/**
* Defines the y Coordinate of page.
*/
pageY?: number;
/**
* Defines the x Coordinate of client.
*/
clientX?: number;
/**
* Defines the y Coordinate of client.
*/
clientY?: number;
}

let left: number;
let top: number;
let width: number;
let height: number;

/**
* Sets the permitted drag area boundaries based on the defined dragArea.
*
* @private
* @param {HTMLElement | string} dragArea - The element or selector string defining the drag area
* @param {Element} helperElement - The helper element used in dragging
* @param {PositionCoordinates} borderWidth - The border width of the drag area
* @param {PositionCoordinates} padding - The padding of the drag area
* @param {PositionCoordinates} dragLimit - The object to store the calculated drag limits
* @returns {void}
*/
export function setDragArea(
dragArea: HTMLElement | string, helperElement: Element,
borderWidth: PositionCoordinates, padding: PositionCoordinates,
dragLimit: PositionCoordinates
): void {
let eleWidthBound: number;
let eleHeightBound: number;
let top: number = 0;
let left: number = 0;
let ele: HTMLElement;
const type: string = typeof dragArea;
if (type === 'string') {
ele = select(dragArea as string) as HTMLElement;
} else {
ele = dragArea as HTMLElement;
}
if (ele) {
const elementArea: ClientRect | DOMRect = ele.getBoundingClientRect();
eleWidthBound = ele.scrollWidth ? ele.scrollWidth : elementArea.right - elementArea.left;
eleHeightBound = ele.scrollHeight ? (dragArea && !isNullOrUndefined(helperElement) && helperElement.classList.contains('sf-treeview')) ? ele.clientHeight : ele.scrollHeight : elementArea.bottom - elementArea.top;
const keys: string[] = ['Top', 'Left', 'Bottom', 'Right'];
const styles: CSSStyleDeclaration = getComputedStyle(ele);
for (let i: number = 0; i < keys.length; i++) {
const key: string = keys[parseInt(i.toString(), 10)];
const tborder: string = styles['border' + key + 'Width'];
const tpadding: string = styles['padding' + key];
const lowerKey: string = key.toLowerCase();
(borderWidth as Record<string, number>)[`${lowerKey}`] = isNaN(parseFloat(tborder)) ? 0 : parseFloat(tborder);
(padding as Record<string, number>)[`${lowerKey}`] = isNaN(parseFloat(tpadding)) ? 0 : parseFloat(tpadding);
}
if (dragArea && !isNullOrUndefined(helperElement) && helperElement.classList.contains('sf-treeview')) {
top = elementArea.top + document.scrollingElement.scrollTop;
} else {
top = elementArea.top;
}
left = elementArea.left;
dragLimit.left = left + borderWidth.left + padding.left;
dragLimit.top = ele.offsetTop + borderWidth.top + padding.top;
dragLimit.right = left + eleWidthBound - (borderWidth.right + padding.right);
dragLimit.bottom = top + eleHeightBound - (borderWidth.bottom + padding.bottom);
}
}

/**
* Retrieves the document's full height or width, considering the scroll and offset values.
*
* @private
* @param {string} str - The dimension type ('Height' or 'Width') to calculate.
* @returns {number} - The maximum value across scroll, offset, and client dimensions.
*/
export function getDocumentWidthHeight(str: 'Height' | 'Width'): number {
const docBody: HTMLElement = document.body;
const docEle: HTMLElement = document.documentElement;
return Math.max(
docBody['scroll' + str], docEle['scroll' + str],
docBody['offset' + str], docEle['offset' + str],
docEle['client' + str]
);
}

/**
* Determines if a given element is within the bounds of the viewport.
*
* @private
* @param {HTMLElement} el - The element to check.
* @returns {boolean} - True if the element is in the viewport, false otherwise.
*/
export function elementInViewport(el: HTMLElement): boolean {
top = el.offsetTop;
left = el.offsetLeft;
width = el.offsetWidth;
height = el.offsetHeight;
while (el.offsetParent) {
el = el.offsetParent as HTMLElement;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top >= window.pageYOffset &&
left >= window.pageXOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) &&
(left + width) <= (window.pageXOffset + window.innerWidth)
);
}

/**
* Gets the coordinates of a mouse or touch event.
*
* @private
* @param {MouseEvent | TouchEvent} evt - The event object.
* @returns {Coordinates} - The x and y coordinates of the page and client.
*/
export function getCoordinates(evt: MouseEvent & TouchEvent): Coordinates {
if (evt.type.indexOf('touch') > -1) {
return evt.changedTouches[0];
}
return evt as Coordinates;
}

/**
* Calculates the parent position of the element relative to the document.
*
* @private
* @param {Element} ele - The element for which the parent position is calculated.
* @returns {IPosition} - The calculated left and top position.
*/
export function calculateParentPosition(ele: Element): IPosition {
if (isNullOrUndefined(ele)) {
return { left: 0, top: 0 };
}
const rect: ClientRect | DOMRect = ele.getBoundingClientRect();
const style: CSSStyleDeclaration = getComputedStyle(ele);
return {
left: (rect.left + window.pageXOffset) - parseInt(style.marginLeft, 10),
top: (rect.top + window.pageYOffset) - parseInt(style.marginTop, 10)
};
}

/**
* Retrieves all elements from a point defined by event coordinates.
*
* @private
* @param {MouseEvent | TouchEvent} evt - The event object containing coordinates.
* @returns {Element[]} - An array of elements located at the event's point.
*/
export function getPathElements(evt: MouseEvent & TouchEvent): Element[] {
const elementTop: number = evt.clientX > 0 ? evt.clientX : 0;
const elementLeft: number = evt.clientY > 0 ? evt.clientY : 0;
return document.elementsFromPoint(elementTop, elementLeft);
}

/**
* Identifies the scrollable parent of the current node element.
*
* @private
* @param {Element[]} nodes - The path of elements to check.
* @param {boolean} reverse - Whether to reverse the array to check from bottom to top.
* @returns {Element | null} - The first scrollable parent element or null.
*/
export function getScrollParent(nodes: Element[], reverse: boolean): Element | null {
const nodeList: Element[] = reverse ? [...nodes].reverse() : nodes;
for (const node of nodeList) {
const computedStyle: CSSStyleDeclaration = window.getComputedStyle(node);
const overflowY: string = computedStyle.overflowY;
if ((overflowY === 'auto' || overflowY === 'scroll') &&
node.scrollHeight > node.clientHeight) {
return node;
}
}
const scrollingElement: HTMLElement = document.scrollingElement as HTMLElement;
const docOverflowY: string = window.getComputedStyle(scrollingElement).overflowY;
if (docOverflowY === 'visible') {
scrollingElement.style.overflow = 'auto';
return scrollingElement;
}
return null;
}
Loading