Skip to content

Commit

Permalink
Merge pull request #107 from js-tool-pack/draggable
Browse files Browse the repository at this point in the history
Draggable
  • Loading branch information
mengxinssfd committed Jun 1, 2024
2 parents 4d8e5be + a6cc91f commit dfab3fb
Show file tree
Hide file tree
Showing 27 changed files with 765 additions and 0 deletions.
5 changes: 5 additions & 0 deletions internal/playground/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ export const baseRouter = [
name: 'virtual-list 虚拟列表',
path: '/virtual-list',
},
{
element: getDemos(import.meta.glob('~/draggable/demo/*.tsx')),
name: 'draggable 拖拽',
path: '/draggable',
},
/* {import insert target} */
];

Expand Down
42 changes: 42 additions & 0 deletions packages/components/src/draggable/Draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// import EnUS from './locale/en-US';
import type { DraggableProps, DraggableFC } from './draggable.types';
import { createElement, forwardRef, FC } from 'react';
// import { useLocale } from '~/config-provider/useLocale';
import type { RequiredPart } from '@tool-pack/types';
import { getClassNames } from '@tool-pack/basic';
import { useDraggableChildren } from './hooks';
import { getClasses } from '@pkg/shared';

export const cls = getClasses('draggable', ['ghost', 'item'], []);
const defaultProps = {
tag: 'div',
list: [],
} satisfies Partial<DraggableProps>;

export const _Draggable: FC<DraggableProps> = forwardRef<
HTMLDivElement,
DraggableProps
>((props, ref) => {
// const locale = useLocale('draggable', EnUS);
const { attrs = {}, tag } = props as RequiredPart<
DraggableProps,
keyof typeof defaultProps
>;
const children = useDraggableChildren(props);

if (tag === null) return children;
return createElement(
tag,
{
...attrs,
className: getClassNames(cls.root, attrs.className),
ref,
},
children,
);
});

_Draggable.defaultProps = defaultProps;
_Draggable.displayName = 'Draggable';

export const Draggable = _Draggable as DraggableFC;
60 changes: 60 additions & 0 deletions packages/components/src/draggable/__tests__/Draggable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { render } from '@testing-library/react';
import { testAttrs } from '~/testAttrs';
import { useState } from 'react';
import { Draggable } from '..';

describe('Draggable', () => {
testAttrs(Draggable);
test('base', () => {
const App = () => {
const [state, setState] = useState<{ name: string; id: number }[]>([
{ name: 'John', id: 1 },
{ name: 'Joao', id: 2 },
{ name: 'Jean', id: 3 },
]);
return (
<Draggable onChange={setState} list={state}>
{state.map((item, index) => (
<div className="draggable-item" key={item.id}>
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
<span>{item.id}</span>
</div>
))}
</Draggable>
);
};
const r = render(<App />);
expect(r.container.firstChild).toMatchSnapshot();
});
test('tag', () => {
expect(
(
render(
<Draggable list={[]}>
<span>1</span>
</Draggable>,
).container.firstChild as HTMLElement
).tagName,
).toBe('DIV');

expect(
(
render(
<Draggable tag="section" list={[]}>
<span>1</span>
</Draggable>,
).container.firstChild as HTMLElement
).tagName,
).toBe('SECTION');

expect(
(
render(
<Draggable tag={null} list={[]}>
<span>1</span>
</Draggable>,
).container.firstChild as HTMLElement
).tagName,
).toBe('SPAN');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Draggable base 1`] = `
<div
class="t-draggable"
>
<div
class="draggable-item t-draggable__item"
draggable="true"
>
<span>
1
.
</span>
<span>
John
</span>
<span>
1
</span>
</div>
<div
class="draggable-item t-draggable__item"
draggable="true"
>
<span>
2
.
</span>
<span>
Joao
</span>
<span>
2
</span>
</div>
<div
class="draggable-item t-draggable__item"
draggable="true"
>
<span>
3
.
</span>
<span>
Jean
</span>
<span>
3
</span>
</div>
</div>
`;
21 changes: 21 additions & 0 deletions packages/components/src/draggable/demo/basic.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.root {
:global {
.main {
display: flex;
margin-top: 1rem;
}
.t-draggable {
flex: 1;
}
.draggable-item {
padding: 0 0.5rem;
border: 1px solid #e6e6e6;
background: #fff1d7;
line-height: 32px;
}
.data {
padding: 0 20px;
white-space: pre-wrap;
}
}
}
54 changes: 54 additions & 0 deletions packages/components/src/draggable/demo/basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* title: 基础用法
* description: Draggable 基础用法。
*/

import { ButtonGroup, Draggable, Button } from '@tool-pack/react-ui';
import styles from './basic.module.scss';
import React from 'react';

const App: React.FC = () => {
const [state, setState] = React.useState<{ name: string; id: number }[]>([
{ name: 'John', id: 1 },
{ name: 'Joao', id: 2 },
{ name: 'Jean', id: 3 },
{ name: 'Gerard', id: 4 },
]);
return (
<div className={styles['root']}>
<ButtonGroup>
<Button
onClick={() => {
const id = state.length + 1;
setState([...state, { name: 'anyone', id }]);
}}
type="primary"
>
添加
</Button>
<Button onClick={() => setState(state.slice(0, -1))} type="success">
删减
</Button>
</ButtonGroup>
<div className="main">
<Draggable onChange={setState} list={state}>
{state.map((item, index) => (
<div className="draggable-item" key={item.id}>
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
<span>{item.id}</span>
</div>
))}
</Draggable>
<div className="data">
[
{state.map((it) => (
<div key={it.id}>{JSON.stringify(it)}</div>
))}
]
</div>
</div>
</div>
);
};

export default App;
24 changes: 24 additions & 0 deletions packages/components/src/draggable/demo/draggable.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.root {
:global {
.main {
display: flex;
margin-top: 1rem;
}
.t-draggable {
flex: 1;
}
.draggable-item {
padding: 0 0.5rem;
border: 1px solid #e6e6e6;
background: #fff1d7;
line-height: 32px;
&[draggable='false'] {
background: #f6f4f0;
}
}
.data {
padding: 0 20px;
white-space: pre-wrap;
}
}
}
40 changes: 40 additions & 0 deletions packages/components/src/draggable/demo/draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* title: draggable
* description: html 元素设置 draggable 为 false 时不可拖动。
*/

import { Draggable } from '@tool-pack/react-ui';
import styles from './draggable.module.scss';
import React from 'react';

const App: React.FC = () => {
const [state, setState] = React.useState<{ name: string; id: number }[]>([
{ name: 'John', id: 1 },
{ name: 'Joao', id: 2 },
{ name: 'Jean', id: 3 },
{ name: 'Gerard', id: 4 },
]);
return (
<div className={styles['root']}>
<div className="main">
<Draggable onChange={setState} list={state}>
{state.map((item, index) => (
<div className="draggable-item" draggable={index > 1} key={item.id}>
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
<span>{item.id}</span>
</div>
))}
</Draggable>
<div className="data">
[
{state.map((it) => (
<div key={it.id}>{JSON.stringify(it)}</div>
))}
]
</div>
</div>
</div>
);
};

export default App;
21 changes: 21 additions & 0 deletions packages/components/src/draggable/demo/tag.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.root {
:global {
.main {
display: flex;
margin-top: 1rem;
}
.draggable-item {
padding: 0 0.5rem;
border: 1px solid #e6e6e6;
background: #fff1d7;
line-height: 32px;
&[draggable='true'] {
cursor: col-resize;
}
}
.data {
padding: 0 20px;
white-space: pre-wrap;
}
}
}
40 changes: 40 additions & 0 deletions packages/components/src/draggable/demo/tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* title: tag
* description: 默认根元素为 div,当 tag 为 null 时 Draggable 组件不提供根元素。
*/

import { Draggable } from '@tool-pack/react-ui';
import styles from './tag.module.scss';
import React from 'react';

const App: React.FC = () => {
const [state, setState] = React.useState<{ name: string; id: number }[]>([
{ name: 'John', id: 1 },
{ name: 'Joao', id: 2 },
{ name: 'Jean', id: 3 },
{ name: 'Gerard', id: 4 },
]);
return (
<div className={styles['root']}>
<div className="main">
<Draggable onChange={setState} list={state} tag={null}>
{state.map((item, index) => (
<div className="draggable-item" key={item.id}>
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
<span>{item.id}</span>
</div>
))}
</Draggable>
<div className="data">
[
{state.map((it) => (
<div key={it.id}>{JSON.stringify(it)}</div>
))}
]
</div>
</div>
</div>
);
};

export default App;
10 changes: 10 additions & 0 deletions packages/components/src/draggable/draggable.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { PropsBase } from '@pkg/shared';
import type { ReactElement } from 'react';

export interface DraggableProps<T = unknown> extends PropsBase<HTMLDivElement> {
tag?: keyof HTMLElementTagNameMap | null;
onChange?: (list: T[]) => void;
list: T[];
}
export type DraggableFC = <T>(props: DraggableProps<T>) => ReactElement;
// export interface DraggableLocale {}
1 change: 1 addition & 0 deletions packages/components/src/draggable/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useDraggableChildren';
Loading

0 comments on commit dfab3fb

Please sign in to comment.