Skip to content

Commit dfab3fb

Browse files
authored
Merge pull request #107 from js-tool-pack/draggable
Draggable
2 parents 4d8e5be + a6cc91f commit dfab3fb

27 files changed

+765
-0
lines changed

internal/playground/src/router.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ export const baseRouter = [
181181
name: 'virtual-list 虚拟列表',
182182
path: '/virtual-list',
183183
},
184+
{
185+
element: getDemos(import.meta.glob('~/draggable/demo/*.tsx')),
186+
name: 'draggable 拖拽',
187+
path: '/draggable',
188+
},
184189
/* {import insert target} */
185190
];
186191

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// import EnUS from './locale/en-US';
2+
import type { DraggableProps, DraggableFC } from './draggable.types';
3+
import { createElement, forwardRef, FC } from 'react';
4+
// import { useLocale } from '~/config-provider/useLocale';
5+
import type { RequiredPart } from '@tool-pack/types';
6+
import { getClassNames } from '@tool-pack/basic';
7+
import { useDraggableChildren } from './hooks';
8+
import { getClasses } from '@pkg/shared';
9+
10+
export const cls = getClasses('draggable', ['ghost', 'item'], []);
11+
const defaultProps = {
12+
tag: 'div',
13+
list: [],
14+
} satisfies Partial<DraggableProps>;
15+
16+
export const _Draggable: FC<DraggableProps> = forwardRef<
17+
HTMLDivElement,
18+
DraggableProps
19+
>((props, ref) => {
20+
// const locale = useLocale('draggable', EnUS);
21+
const { attrs = {}, tag } = props as RequiredPart<
22+
DraggableProps,
23+
keyof typeof defaultProps
24+
>;
25+
const children = useDraggableChildren(props);
26+
27+
if (tag === null) return children;
28+
return createElement(
29+
tag,
30+
{
31+
...attrs,
32+
className: getClassNames(cls.root, attrs.className),
33+
ref,
34+
},
35+
children,
36+
);
37+
});
38+
39+
_Draggable.defaultProps = defaultProps;
40+
_Draggable.displayName = 'Draggable';
41+
42+
export const Draggable = _Draggable as DraggableFC;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { render } from '@testing-library/react';
2+
import { testAttrs } from '~/testAttrs';
3+
import { useState } from 'react';
4+
import { Draggable } from '..';
5+
6+
describe('Draggable', () => {
7+
testAttrs(Draggable);
8+
test('base', () => {
9+
const App = () => {
10+
const [state, setState] = useState<{ name: string; id: number }[]>([
11+
{ name: 'John', id: 1 },
12+
{ name: 'Joao', id: 2 },
13+
{ name: 'Jean', id: 3 },
14+
]);
15+
return (
16+
<Draggable onChange={setState} list={state}>
17+
{state.map((item, index) => (
18+
<div className="draggable-item" key={item.id}>
19+
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
20+
<span>{item.id}</span>
21+
</div>
22+
))}
23+
</Draggable>
24+
);
25+
};
26+
const r = render(<App />);
27+
expect(r.container.firstChild).toMatchSnapshot();
28+
});
29+
test('tag', () => {
30+
expect(
31+
(
32+
render(
33+
<Draggable list={[]}>
34+
<span>1</span>
35+
</Draggable>,
36+
).container.firstChild as HTMLElement
37+
).tagName,
38+
).toBe('DIV');
39+
40+
expect(
41+
(
42+
render(
43+
<Draggable tag="section" list={[]}>
44+
<span>1</span>
45+
</Draggable>,
46+
).container.firstChild as HTMLElement
47+
).tagName,
48+
).toBe('SECTION');
49+
50+
expect(
51+
(
52+
render(
53+
<Draggable tag={null} list={[]}>
54+
<span>1</span>
55+
</Draggable>,
56+
).container.firstChild as HTMLElement
57+
).tagName,
58+
).toBe('SPAN');
59+
});
60+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Draggable base 1`] = `
4+
<div
5+
class="t-draggable"
6+
>
7+
<div
8+
class="draggable-item t-draggable__item"
9+
draggable="true"
10+
>
11+
<span>
12+
1
13+
.
14+
</span>
15+
16+
<span>
17+
John
18+
</span>
19+
20+
<span>
21+
1
22+
</span>
23+
</div>
24+
<div
25+
class="draggable-item t-draggable__item"
26+
draggable="true"
27+
>
28+
<span>
29+
2
30+
.
31+
</span>
32+
33+
<span>
34+
Joao
35+
</span>
36+
37+
<span>
38+
2
39+
</span>
40+
</div>
41+
<div
42+
class="draggable-item t-draggable__item"
43+
draggable="true"
44+
>
45+
<span>
46+
3
47+
.
48+
</span>
49+
50+
<span>
51+
Jean
52+
</span>
53+
54+
<span>
55+
3
56+
</span>
57+
</div>
58+
</div>
59+
`;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.root {
2+
:global {
3+
.main {
4+
display: flex;
5+
margin-top: 1rem;
6+
}
7+
.t-draggable {
8+
flex: 1;
9+
}
10+
.draggable-item {
11+
padding: 0 0.5rem;
12+
border: 1px solid #e6e6e6;
13+
background: #fff1d7;
14+
line-height: 32px;
15+
}
16+
.data {
17+
padding: 0 20px;
18+
white-space: pre-wrap;
19+
}
20+
}
21+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* title: 基础用法
3+
* description: Draggable 基础用法。
4+
*/
5+
6+
import { ButtonGroup, Draggable, Button } from '@tool-pack/react-ui';
7+
import styles from './basic.module.scss';
8+
import React from 'react';
9+
10+
const App: React.FC = () => {
11+
const [state, setState] = React.useState<{ name: string; id: number }[]>([
12+
{ name: 'John', id: 1 },
13+
{ name: 'Joao', id: 2 },
14+
{ name: 'Jean', id: 3 },
15+
{ name: 'Gerard', id: 4 },
16+
]);
17+
return (
18+
<div className={styles['root']}>
19+
<ButtonGroup>
20+
<Button
21+
onClick={() => {
22+
const id = state.length + 1;
23+
setState([...state, { name: 'anyone', id }]);
24+
}}
25+
type="primary"
26+
>
27+
添加
28+
</Button>
29+
<Button onClick={() => setState(state.slice(0, -1))} type="success">
30+
删减
31+
</Button>
32+
</ButtonGroup>
33+
<div className="main">
34+
<Draggable onChange={setState} list={state}>
35+
{state.map((item, index) => (
36+
<div className="draggable-item" key={item.id}>
37+
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
38+
<span>{item.id}</span>
39+
</div>
40+
))}
41+
</Draggable>
42+
<div className="data">
43+
[
44+
{state.map((it) => (
45+
<div key={it.id}>{JSON.stringify(it)}</div>
46+
))}
47+
]
48+
</div>
49+
</div>
50+
</div>
51+
);
52+
};
53+
54+
export default App;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.root {
2+
:global {
3+
.main {
4+
display: flex;
5+
margin-top: 1rem;
6+
}
7+
.t-draggable {
8+
flex: 1;
9+
}
10+
.draggable-item {
11+
padding: 0 0.5rem;
12+
border: 1px solid #e6e6e6;
13+
background: #fff1d7;
14+
line-height: 32px;
15+
&[draggable='false'] {
16+
background: #f6f4f0;
17+
}
18+
}
19+
.data {
20+
padding: 0 20px;
21+
white-space: pre-wrap;
22+
}
23+
}
24+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* title: draggable
3+
* description: html 元素设置 draggable 为 false 时不可拖动。
4+
*/
5+
6+
import { Draggable } from '@tool-pack/react-ui';
7+
import styles from './draggable.module.scss';
8+
import React from 'react';
9+
10+
const App: React.FC = () => {
11+
const [state, setState] = React.useState<{ name: string; id: number }[]>([
12+
{ name: 'John', id: 1 },
13+
{ name: 'Joao', id: 2 },
14+
{ name: 'Jean', id: 3 },
15+
{ name: 'Gerard', id: 4 },
16+
]);
17+
return (
18+
<div className={styles['root']}>
19+
<div className="main">
20+
<Draggable onChange={setState} list={state}>
21+
{state.map((item, index) => (
22+
<div className="draggable-item" draggable={index > 1} key={item.id}>
23+
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
24+
<span>{item.id}</span>
25+
</div>
26+
))}
27+
</Draggable>
28+
<div className="data">
29+
[
30+
{state.map((it) => (
31+
<div key={it.id}>{JSON.stringify(it)}</div>
32+
))}
33+
]
34+
</div>
35+
</div>
36+
</div>
37+
);
38+
};
39+
40+
export default App;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.root {
2+
:global {
3+
.main {
4+
display: flex;
5+
margin-top: 1rem;
6+
}
7+
.draggable-item {
8+
padding: 0 0.5rem;
9+
border: 1px solid #e6e6e6;
10+
background: #fff1d7;
11+
line-height: 32px;
12+
&[draggable='true'] {
13+
cursor: col-resize;
14+
}
15+
}
16+
.data {
17+
padding: 0 20px;
18+
white-space: pre-wrap;
19+
}
20+
}
21+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* title: tag
3+
* description: 默认根元素为 div,当 tag 为 null 时 Draggable 组件不提供根元素。
4+
*/
5+
6+
import { Draggable } from '@tool-pack/react-ui';
7+
import styles from './tag.module.scss';
8+
import React from 'react';
9+
10+
const App: React.FC = () => {
11+
const [state, setState] = React.useState<{ name: string; id: number }[]>([
12+
{ name: 'John', id: 1 },
13+
{ name: 'Joao', id: 2 },
14+
{ name: 'Jean', id: 3 },
15+
{ name: 'Gerard', id: 4 },
16+
]);
17+
return (
18+
<div className={styles['root']}>
19+
<div className="main">
20+
<Draggable onChange={setState} list={state} tag={null}>
21+
{state.map((item, index) => (
22+
<div className="draggable-item" key={item.id}>
23+
<span>{index + 1}.</span> <span>{item.name}</span>{' '}
24+
<span>{item.id}</span>
25+
</div>
26+
))}
27+
</Draggable>
28+
<div className="data">
29+
[
30+
{state.map((it) => (
31+
<div key={it.id}>{JSON.stringify(it)}</div>
32+
))}
33+
]
34+
</div>
35+
</div>
36+
</div>
37+
);
38+
};
39+
40+
export default App;

0 commit comments

Comments
 (0)