diff --git a/packages/react/src/components/DropzoneCompact/DropzoneCompact.api.mdx b/packages/react/src/components/DropzoneCompact/DropzoneCompact.api.mdx
new file mode 100644
index 000000000..c259f67cc
--- /dev/null
+++ b/packages/react/src/components/DropzoneCompact/DropzoneCompact.api.mdx
@@ -0,0 +1,31 @@
+import { Meta } from '@storybook/blocks';
+import LinkTo from '@storybook/addon-links/react';
+import { TableInterface } from '~storybook/components/TableInterface';
+
+
+
+# DropzoneCompact API
+
+```js
+import { DropzoneCompact } from '@esfront/react';
+```
+
+## Component name
+
+The name `ESDropzoneCompact` can be used when providing default props.
+
+## Props
+
+
+
+
+
+## Demos
+
+
+ -
+
+
DropzoneCompact
+
+
+
diff --git a/packages/react/src/components/DropzoneCompact/DropzoneCompact.stories.tsx b/packages/react/src/components/DropzoneCompact/DropzoneCompact.stories.tsx
new file mode 100644
index 000000000..8c53bae3a
--- /dev/null
+++ b/packages/react/src/components/DropzoneCompact/DropzoneCompact.stories.tsx
@@ -0,0 +1,51 @@
+import { Args, Meta, StoryContext, StoryObj } from '@storybook/react';
+
+import { DropzoneCompact } from '.';
+
+import { Button } from '../Button';
+
+const getText = (args: Args, context: StoryContext) => {
+ return args.children || (context.globals.locale === 'en' ? 'Drag over this area' : 'Переместите в эту область');
+};
+
+const getButtonText = (context: StoryContext) => {
+ return context.globals.locale === 'en' ? 'Drag this button' : 'Переместите кнопку';
+};
+
+const meta: Meta = {
+ tags: ['autodocs'],
+ component: DropzoneCompact,
+ parameters: {
+ references: ['DropzoneCompact'],
+ },
+ argTypes: {
+ children: {
+ control: {
+ type: 'text',
+ },
+ },
+ },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Demo: Story = {
+ render: (args, context) => {
+ const onDrop = () => {
+ console.info('DROP');
+ };
+
+ return (
+ <>
+
+
+ {getText(args, context)}
+
+ >
+ );
+ },
+};
diff --git a/packages/react/src/components/DropzoneCompact/DropzoneCompact.tsx b/packages/react/src/components/DropzoneCompact/DropzoneCompact.tsx
new file mode 100644
index 000000000..5c267ba36
--- /dev/null
+++ b/packages/react/src/components/DropzoneCompact/DropzoneCompact.tsx
@@ -0,0 +1,64 @@
+import { forwardRef } from 'react';
+
+import { DropzoneCompactProps } from './DropzoneCompact.types';
+
+import clsx from 'clsx';
+
+import { useDefaultProps } from '@mui/system/DefaultPropsProvider';
+
+import { useDragOver } from '../../hooks/useDragOver';
+
+/**
+ * This component allows to create droppable area when using dnd.
+ */
+export const DropzoneCompact = forwardRef(function DropzoneCompact(inProps, ref) {
+ const {
+ className,
+ style,
+ children,
+ onDragEnter: onDragEnterProp,
+ onDragLeave: onDragLeaveProp,
+ onDragOver: onDragOverProp,
+ onDrop: onDropProp,
+ } = useDefaultProps({
+ props: inProps,
+ name: 'ESDropzoneCompact',
+ });
+
+ const { isDragOver, onDragEnter, onDragLeave, onDrop } = useDragOver();
+
+ const onDropzoneDragEnter = (event: React.DragEvent) => {
+ onDragEnter();
+ onDragEnterProp?.(event);
+ };
+
+ const onDropzoneDragLeave = (event: React.DragEvent) => {
+ onDragLeave();
+ onDragLeaveProp?.(event);
+ };
+
+ const onDropzoneDragOver = (event: React.DragEvent) => {
+ event.preventDefault();
+ onDragOverProp?.(event);
+ };
+
+ const onDropzoneDrop = (event: React.DragEvent) => {
+ event.preventDefault();
+ onDrop();
+ onDropProp?.(event);
+ };
+
+ return (
+
+ {children}
+
+ );
+});
diff --git a/packages/react/src/components/DropzoneCompact/DropzoneCompact.types.ts b/packages/react/src/components/DropzoneCompact/DropzoneCompact.types.ts
new file mode 100644
index 000000000..d57143f9b
--- /dev/null
+++ b/packages/react/src/components/DropzoneCompact/DropzoneCompact.types.ts
@@ -0,0 +1,15 @@
+import { CSSProperties, ReactNode } from 'react';
+
+export interface DropzoneCompactProps {
+ children?: ReactNode;
+
+ /** Class applied to the root element. */
+ className?: string;
+ /** Style applied to the root element. */
+ style?: CSSProperties;
+
+ onDragEnter?: (event: React.DragEvent) => void;
+ onDragLeave?: (event: React.DragEvent) => void;
+ onDragOver?: (event: React.DragEvent) => void;
+ onDrop?: (event: React.DragEvent) => void;
+}
diff --git a/packages/react/src/components/DropzoneCompact/index.ts b/packages/react/src/components/DropzoneCompact/index.ts
new file mode 100644
index 000000000..4b3bff570
--- /dev/null
+++ b/packages/react/src/components/DropzoneCompact/index.ts
@@ -0,0 +1,2 @@
+export { DropzoneCompact } from './DropzoneCompact';
+export type { DropzoneCompactProps } from './DropzoneCompact.types';
diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts
index 2ea56d6ca..18d21fd09 100644
--- a/packages/react/src/components/index.ts
+++ b/packages/react/src/components/index.ts
@@ -23,6 +23,7 @@ export * from './Dialog';
export * from './DialogStack';
export * from './Divider';
export * from './Dropzone';
+export * from './DropzoneCompact';
export * from './EmptyState';
export * from './EmptyStateCompact';
export * from './ErrorPage';
diff --git a/packages/react/src/hooks/useDragOver/useDragOver.ts b/packages/react/src/hooks/useDragOver/useDragOver.ts
index 88d7a2674..5cd09d3a7 100644
--- a/packages/react/src/hooks/useDragOver/useDragOver.ts
+++ b/packages/react/src/hooks/useDragOver/useDragOver.ts
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useCallback, useState } from 'react';
/**
* The hook that allows to watch over the drag over state.
@@ -7,17 +7,17 @@ import { useState } from 'react';
export const useDragOver = () => {
const [count, setCount] = useState(0);
- const onDragEnter = () => {
+ const onDragEnter = useCallback(() => {
setCount((c) => c + 1);
- };
+ }, []);
- const onDragLeave = () => {
+ const onDragLeave = useCallback(() => {
setCount((c) => c - 1);
- };
+ }, []);
- const onDrop = () => {
+ const onDrop = useCallback(() => {
setCount(0);
- };
+ }, []);
return { onDragEnter, onDragLeave, onDrop, isDragOver: count > 0 };
};
diff --git a/packages/react/src/overrides.d.ts b/packages/react/src/overrides.d.ts
index 0b12797a1..35debd86f 100644
--- a/packages/react/src/overrides.d.ts
+++ b/packages/react/src/overrides.d.ts
@@ -40,6 +40,7 @@ import {
} from './components/Dialog';
import { DividerProps } from './components/Divider';
import { DropzoneProps } from './components/Dropzone';
+import { DropzoneCompactProps } from './components/DropzoneCompact';
import { EmptyStateProps } from './components/EmptyState';
import { EmptyStateCompactProps } from './components/EmptyStateCompact';
import {
@@ -208,6 +209,7 @@ declare module '@mui/material/styles/props' {
ESDialogTitle: DialogTitleProps;
ESDivider: DividerProps;
ESDropzone: DropzoneProps;
+ ESDropzoneCompact: DropzoneCompactProps;
ESEmptyState: EmptyStateProps;
ESEmptyStateCompact: EmptyStateCompactProps;
ESErrorPage: ErrorPageProps;
@@ -535,6 +537,9 @@ declare module '@mui/material/styles/components' {
ESDropzone?: {
defaultProps?: ComponentsProps['ESDropzone'];
};
+ ESDropzoneCompact?: {
+ defaultProps?: ComponentsProps['ESDropzoneCompact'];
+ };
ESFileIcon?: {
defaultProps?: ComponentsProps['ESFileIcon'];
};
diff --git a/packages/theme/lib/components/_dropzone-compact.scss b/packages/theme/lib/components/_dropzone-compact.scss
new file mode 100644
index 000000000..e25b40aaa
--- /dev/null
+++ b/packages/theme/lib/components/_dropzone-compact.scss
@@ -0,0 +1,18 @@
+@mixin include() {
+ .es-dropzone-compact {
+ align-items: center;
+ background-color: var(--es-mono-a-a25);
+ border-radius: 4px;
+ color: var(--es-mono-a-a550);
+ display: flex;
+ justify-content: center;
+ outline: 1px dashed var(--es-mono-a-a300);
+ outline-offset: -1px;
+ padding: 12px;
+ transition: background-color 200ms;
+
+ &--drag-over {
+ background-color: var(--es-mono-a-a50);
+ }
+ }
+}
diff --git a/packages/theme/lib/components/_index.scss b/packages/theme/lib/components/_index.scss
index 81b71047f..2c87158eb 100644
--- a/packages/theme/lib/components/_index.scss
+++ b/packages/theme/lib/components/_index.scss
@@ -20,6 +20,7 @@
@use './dialog';
@use './divider';
@use './dropzone';
+@use './dropzone-compact';
@use './empty-state-compact';
@use './empty-state';
@use './error-page';
@@ -89,6 +90,7 @@
@include dialog.include;
@include divider.include;
@include dropzone.include;
+ @include dropzone-compact.include;
@include empty-state-compact.include;
@include empty-state.include;
@include error-page.include;