Skip to content

Commit 1c141b3

Browse files
ryo-manbaLFDanLu
andauthored
refactor: isolate Table stories (#5620)
Co-authored-by: Daniel Lu <dl1644@gmail.com>
1 parent f12d72a commit 1c141b3

File tree

3 files changed

+244
-221
lines changed

3 files changed

+244
-221
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright 2022 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {action} from '@storybook/addon-actions';
14+
import {Button, Cell, Column, ColumnProps, ColumnResizer, Dialog, DialogTrigger, Heading, Menu, MenuTrigger, Modal, ModalOverlay, Popover, ResizableTableContainer, Row, Table, TableBody, TableHeader, useDragAndDrop} from 'react-aria-components';
15+
import {isTextDropItem} from 'react-aria';
16+
import {MyMenuItem} from './utils';
17+
import React from 'react';
18+
import styles from '../example/index.css';
19+
import {useListData} from 'react-stately';
20+
21+
export default {
22+
title: 'React Aria Components'
23+
};
24+
25+
const ReorderableTable = ({initialItems}: {initialItems: {id: string, name: string}[]}) => {
26+
let list = useListData({initialItems});
27+
28+
const {dragAndDropHooks} = useDragAndDrop({
29+
getItems: keys => {
30+
return [...keys].map(k => {
31+
const item = list.getItem(k);
32+
return {
33+
'text/plain': item.id,
34+
item: JSON.stringify(item)
35+
};
36+
});
37+
},
38+
getDropOperation: () => 'move',
39+
onReorder: e => {
40+
if (e.target.dropPosition === 'before') {
41+
list.moveBefore(e.target.key, e.keys);
42+
} else if (e.target.dropPosition === 'after') {
43+
list.moveAfter(e.target.key, e.keys);
44+
}
45+
},
46+
onInsert: async e => {
47+
const processedItems = await Promise.all(
48+
e.items.filter(isTextDropItem).map(async item => JSON.parse(await item.getText('item')))
49+
);
50+
if (e.target.dropPosition === 'before') {
51+
list.insertBefore(e.target.key, ...processedItems);
52+
} else if (e.target.dropPosition === 'after') {
53+
list.insertAfter(e.target.key, ...processedItems);
54+
}
55+
},
56+
57+
onDragEnd: e => {
58+
if (e.dropOperation === 'move' && !e.isInternal) {
59+
list.remove(...e.keys);
60+
}
61+
},
62+
63+
onRootDrop: async e => {
64+
const processedItems = await Promise.all(
65+
e.items.filter(isTextDropItem).map(async item => JSON.parse(await item.getText('item')))
66+
);
67+
68+
list.append(...processedItems);
69+
}
70+
});
71+
72+
return (
73+
<Table aria-label="Reorderable table" dragAndDropHooks={dragAndDropHooks}>
74+
<TableHeader>
75+
<MyColumn isRowHeader defaultWidth="50%">Id</MyColumn>
76+
<MyColumn>Name</MyColumn>
77+
</TableHeader>
78+
<TableBody items={list.items} renderEmptyState={({isDropTarget}) => <span style={{color: isDropTarget ? 'red' : 'black'}}>Drop items here</span>}>
79+
{item => (
80+
<Row>
81+
<Cell>{item.id}</Cell>
82+
<Cell>{item.name}</Cell>
83+
</Row>
84+
)}
85+
</TableBody>
86+
</Table>
87+
);
88+
};
89+
90+
export const ReorderableTableExample = () => (
91+
<>
92+
<ResizableTableContainer style={{width: 300, overflow: 'auto'}}>
93+
<ReorderableTable initialItems={[{id: '1', name: 'Bob'}]} />
94+
</ResizableTableContainer>
95+
<ResizableTableContainer style={{width: 300, overflow: 'auto'}}>
96+
<ReorderableTable initialItems={[{id: '2', name: 'Alex'}]} />
97+
</ResizableTableContainer>
98+
</>
99+
);
100+
101+
export const TableExample = () => {
102+
let list = useListData({
103+
initialItems: [
104+
{id: 1, name: 'Games', date: '6/7/2020', type: 'File folder'},
105+
{id: 2, name: 'Program Files', date: '4/7/2021', type: 'File folder'},
106+
{id: 3, name: 'bootmgr', date: '11/20/2010', type: 'System file'},
107+
{id: 4, name: 'log.txt', date: '1/18/2016', type: 'Text Document'}
108+
]
109+
});
110+
111+
return (
112+
<ResizableTableContainer style={{width: 300, overflow: 'auto'}}>
113+
<Table aria-label="Example table">
114+
<TableHeader>
115+
<MyColumn isRowHeader defaultWidth="50%">Name</MyColumn>
116+
<MyColumn>Type</MyColumn>
117+
<MyColumn>Date Modified</MyColumn>
118+
<MyColumn>Actions</MyColumn>
119+
</TableHeader>
120+
<TableBody items={list.items}>
121+
{item => (
122+
<Row>
123+
<Cell>{item.name}</Cell>
124+
<Cell>{item.type}</Cell>
125+
<Cell>{item.date}</Cell>
126+
<Cell>
127+
<DialogTrigger>
128+
<Button>Delete</Button>
129+
<ModalOverlay
130+
style={{
131+
position: 'fixed',
132+
zIndex: 100,
133+
top: 0,
134+
left: 0,
135+
bottom: 0,
136+
right: 0,
137+
background: 'rgba(0, 0, 0, 0.5)',
138+
display: 'flex',
139+
alignItems: 'center',
140+
justifyContent: 'center'
141+
}}>
142+
<Modal
143+
style={{
144+
background: 'Canvas',
145+
color: 'CanvasText',
146+
border: '1px solid gray',
147+
padding: 30
148+
}}>
149+
<Dialog>
150+
{({close}) => (<>
151+
<Heading slot="title">Delete item</Heading>
152+
<p>Are you sure?</p>
153+
<Button onPress={close}>Cancel</Button>
154+
<Button
155+
onPress={() => {
156+
close();
157+
list.remove(item.id);
158+
}}>
159+
Delete
160+
</Button>
161+
</>)}
162+
</Dialog>
163+
</Modal>
164+
</ModalOverlay>
165+
</DialogTrigger>
166+
</Cell>
167+
</Row>
168+
)}
169+
</TableBody>
170+
</Table>
171+
</ResizableTableContainer>
172+
);
173+
};
174+
175+
export const TableDynamicExample = () => {
176+
let columns = [
177+
{name: 'Name', id: 'name', isRowHeader: true},
178+
{name: 'Type', id: 'type'},
179+
{name: 'Date Modified', id: 'date'}
180+
];
181+
182+
let rows = [
183+
{id: 1, name: 'Games', date: '6/7/2020', type: 'File folder'},
184+
{id: 2, name: 'Program Files', date: '4/7/2021', type: 'File folder'},
185+
{id: 3, name: 'bootmgr', date: '11/20/2010', type: 'System file'},
186+
{id: 4, name: 'log.txt', date: '1/18/20167', type: 'Text Document'}
187+
];
188+
189+
return (
190+
<Table aria-label="Files">
191+
<TableHeader columns={columns}>
192+
{(column) => (
193+
<Column isRowHeader={column.isRowHeader}>{column.name}</Column>
194+
)}
195+
</TableHeader>
196+
<TableBody items={rows}>
197+
{(item) => (
198+
<Row columns={columns}>
199+
{(column) => {
200+
return <Cell>{item[column.id]}</Cell>;
201+
}}
202+
</Row>
203+
)}
204+
</TableBody>
205+
</Table>
206+
);
207+
};
208+
209+
const MyColumn = (props: ColumnProps) => {
210+
return (
211+
<Column {...props}>
212+
{({startResize}) => (
213+
<div style={{display: 'flex'}}>
214+
<MenuTrigger>
215+
<Button style={{flex: 1, textAlign: 'left'}}>{props.children as React.ReactNode}</Button>
216+
<Popover>
217+
<Menu className={styles.menu} onAction={() => startResize()}>
218+
<MyMenuItem id="resize">Resize</MyMenuItem>
219+
</Menu>
220+
</Popover>
221+
</MenuTrigger>
222+
<ColumnResizer onHoverStart={action('onHoverStart')} onHoverChange={action('onHoverChange')} onHoverEnd={action('onHoverEnd')}>
223+
224+
</ColumnResizer>
225+
</div>
226+
)}
227+
</Column>
228+
);
229+
};

0 commit comments

Comments
 (0)