UNPKG

19.7 kBMarkdownView Raw
1---
2id: Card view
3section: demos
4---
5
6import DashboardWrapper from '../examples/DashboardWrapper';
7
8import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
9import TrashIcon from '@patternfly/react-icons/dist/esm/icons/trash-icon';
10import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon';
11import pfIcon from './pf-logo-small.svg';
12import activeMQIcon from './activemq-core_200x150.png';
13import avroIcon from './camel-avro_200x150.png';
14import dropBoxIcon from './camel-dropbox_200x150.png';
15import infinispanIcon from './camel-infinispan_200x150.png';
16import saxonIcon from './camel-saxon_200x150.png';
17import sparkIcon from './camel-spark_200x150.png';
18import swaggerIcon from './camel-swagger-java_200x150.png';
19import azureIcon from './FuseConnector_Icons_AzureServices.png';
20import restIcon from './FuseConnector_Icons_REST.png';
21
22## Demos
23
24This demonstrates how you can assemble a full page view that contains a grid of equal sized cards that includes a toolbar for managing card grid contents.
25
26### Card view
27
28```js isFullscreen
29import React from 'react';
30import {
31 Bullseye,
32 Button,
33 Card,
34 CardHeader,
35 CardActions,
36 CardTitle,
37 CardBody,
38 Checkbox,
39 Dropdown,
40 DropdownToggle,
41 DropdownItem,
42 DropdownSeparator,
43 DropdownPosition,
44 DropdownToggleCheckbox,
45 EmptyState,
46 EmptyStateIcon,
47 EmptyStateVariant,
48 EmptyStateSecondaryActions,
49 Gallery,
50 KebabToggle,
51 OverflowMenu,
52 OverflowMenuControl,
53 OverflowMenuDropdownItem,
54 OverflowMenuItem,
55 PageSection,
56 PageSectionVariants,
57 Pagination,
58 Select,
59 SelectOption,
60 SelectVariant,
61 TextContent,
62 Text,
63 Title,
64 Toolbar,
65 ToolbarItem,
66 ToolbarFilter,
67 ToolbarContent
68} from '@patternfly/react-core';
69import DashboardWrapper from '../examples/DashboardWrapper';
70
71import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
72import TrashIcon from '@patternfly/react-icons/dist/esm/icons/trash-icon';
73import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon';
74import pfIcon from './pf-logo-small.svg';
75import activeMQIcon from './activemq-core_200x150.png';
76import avroIcon from './camel-avro_200x150.png';
77import dropBoxIcon from './camel-dropbox_200x150.png';
78import infinispanIcon from './camel-infinispan_200x150.png';
79import saxonIcon from './camel-saxon_200x150.png';
80import sparkIcon from './camel-spark_200x150.png';
81import swaggerIcon from './camel-swagger-java_200x150.png';
82import azureIcon from './FuseConnector_Icons_AzureServices.png';
83import restIcon from './FuseConnector_Icons_REST.png';
84
85class CardViewBasic extends React.Component {
86 constructor(props) {
87 super(props);
88
89 this.state = {
90 filters: {
91 products: []
92 },
93 res: [],
94 isChecked: false,
95 selectedItems: [],
96 areAllSelected: false,
97 isUpperToolbarDropdownOpen: false,
98 isUpperToolbarKebabDropdownOpen: false,
99 isLowerToolbarDropdownOpen: false,
100 isLowerToolbarKebabDropdownOpen: false,
101 isCardKebabDropdownOpen: false,
102 activeItem: 0,
103 splitButtonDropdownIsOpen: false,
104 page: 1,
105 perPage: 10,
106 totalItemCount: 10
107 };
108
109 this.onToolbarDropdownToggle = isLowerToolbarDropdownOpen => {
110 this.setState(prevState => ({
111 isLowerToolbarDropdownOpen
112 }));
113 };
114
115 this.onToolbarKebabDropdownToggle = isLowerToolbarKebabDropdownOpen => {
116 this.setState({
117 isLowerToolbarKebabDropdownOpen
118 });
119 };
120
121 this.onToolbarKebabDropdownSelect = event => {
122 this.setState({
123 isLowerToolbarKebabDropdownOpen: !this.state.isLowerToolbarKebabDropdownOpen
124 });
125 };
126
127 this.onCardKebabDropdownToggle = (key, isCardKebabDropdownOpen) => {
128 this.setState({
129 [key]: isCardKebabDropdownOpen
130 });
131 };
132
133 this.onCardKebabDropdownSelect = (key, event) => {
134 this.setState({
135 [key]: !this.state[key]
136 });
137 };
138
139 this.deleteItem = item => event => {
140 const filter = getter => val => getter(val) !== item.id;
141 this.setState({
142 res: this.state.res.filter(filter(({ id }) => id)),
143 selectedItems: this.state.selectedItems.filter(filter(id => id))
144 });
145 };
146
147 this.onSetPage = (_event, pageNumber) => {
148 this.setState({
149 page: pageNumber
150 });
151 };
152
153 this.onPerPageSelect = (_event, perPage) => {
154 this.setState({
155 perPage
156 });
157 };
158
159 this.onSplitButtonToggle = isOpen => {
160 this.setState({
161 splitButtonDropdownIsOpen: isOpen
162 });
163 };
164
165 this.onSplitButtonSelect = event => {
166 this.setState((prevState, props) => {
167 return { splitButtonDropdownIsOpen: !prevState.splitButtonDropdownIsOpen };
168 });
169 };
170
171 this.onNameSelect = (event, selection) => {
172 const checked = event.target.checked;
173 this.setState(prevState => {
174 const prevSelections = prevState.filters['products'];
175 return {
176 filters: {
177 ...prevState.filters,
178 ['products']: checked ? [...prevSelections, selection] : prevSelections.filter(value => value !== selection)
179 }
180 };
181 });
182 };
183
184 this.onDelete = (type = '', id = '') => {
185 if (type) {
186 this.setState(prevState => {
187 prevState.filters[type.toLowerCase()] = prevState.filters[type.toLowerCase()].filter(s => s !== id);
188 return {
189 filters: prevState.filters
190 };
191 });
192 } else {
193 this.setState({
194 filters: {
195 products: []
196 }
197 });
198 }
199 };
200
201 this.onKeyDown = (event, productId) => {
202 console.log(productId);
203 if (event.target !== event.currentTarget) {
204 return;
205 }
206 if ([' ', 'Enter'].includes(event.key)) {
207 event.preventDefault();
208 this.setState(prevState => {
209 return prevState.selectedItems.includes(productId * 1)
210 ? {
211 selectedItems: [...prevState.selectedItems.filter(id => productId * 1 != id)],
212 areAllSelected: false
213 }
214 : {
215 selectedItems: [...prevState.selectedItems, productId * 1],
216 areAllSelected: prevState.totalItemCount === prevState.selectedItems.length + 1
217 };
218 });
219 }
220 };
221
222 this.onClick = productId => {
223 this.setState(prevState => {
224 return prevState.selectedItems.includes(productId * 1)
225 ? {
226 selectedItems: [...prevState.selectedItems.filter(id => productId * 1 != id)],
227 areAllSelected: false
228 }
229 : {
230 selectedItems: [...prevState.selectedItems, productId * 1],
231 areAllSelected: prevState.totalItemCount === prevState.selectedItems.length + 1
232 };
233 });
234 };
235 }
236
237 selectedItems(e) {
238 const { value, checked } = e.target;
239 let { selectedItems } = this.state;
240
241 if (checked) {
242 selectedItems = [...selectedItems, value];
243 } else {
244 selectedItems = selectedItems.filter(el => el !== value);
245 if (this.state.areAllSelected) {
246 this.setState({
247 areAllSelected: !this.state.areAllSelected
248 });
249 }
250 }
251 this.setState({ selectedItems });
252 }
253
254 splitCheckboxSelectAll(e) {
255 const { checked } = e.target;
256 const { isChecked, res } = this.state;
257 let collection = [];
258
259 if (checked) {
260 for (var i = 0; i <= 9; i++) collection = [...collection, i];
261 }
262
263 this.setState(
264 {
265 selectedItems: collection,
266 isChecked: isChecked,
267 areAllSelected: checked
268 },
269 this.updateSelected
270 );
271 }
272
273 selectPage(e) {
274 const { checked } = e.target;
275 const { isChecked, totalItemCount, perPage } = this.state;
276 let collection = [];
277
278 collection = this.getAllItems();
279
280 this.setState(
281 {
282 selectedItems: collection,
283 isChecked: checked,
284 areAllSelected: totalItemCount === perPage ? true : false
285 },
286 this.updateSelected
287 );
288 }
289
290 selectAll(e) {
291 const { checked } = e.target;
292 const { isChecked } = this.state;
293
294 let collection = [];
295 for (var i = 0; i <= 9; i++) collection = [...collection, i];
296
297 this.setState(
298 {
299 selectedItems: collection,
300 isChecked: true,
301 areAllSelected: true
302 },
303 this.updateSelected
304 );
305 }
306
307 selectNone(e) {
308 const { checked } = e.target;
309 const { isChecked, selectedItems } = this.state;
310 this.setState(
311 {
312 selectedItems: [],
313 isChecked: false,
314 areAllSelected: false
315 },
316 this.updateSelected
317 );
318 }
319
320 getAllItems() {
321 const { res } = this.state;
322 const collection = [];
323 for (const items of res) {
324 collection.push(items.id);
325 }
326
327 return collection;
328 }
329
330 updateSelected() {
331 const { res, selectedItems } = this.state;
332 let rows = res.map(post => {
333 post.selected = selectedItems.includes(post.id);
334 return post;
335 });
336
337 this.setState({
338 res: rows
339 });
340 }
341
342 fetch(page, perPage) {
343 fetch(`https://my-json-server.typicode.com/jenny-s51/cardviewdata/posts?_page=${page}&_limit=${perPage}`)
344 .then(resp => resp.json())
345 .then(resp => this.setState({ res: resp, perPage, page }))
346 .then(() => this.updateSelected())
347 .catch(err => this.setState({ error: err }));
348 }
349
350 componentDidMount() {
351 this.fetch(this.state.page, this.state.perPage);
352 }
353
354 renderPagination() {
355 const { page, perPage, totalItemCount } = this.state;
356
357 const defaultPerPageOptions = [
358 {
359 title: '1',
360 value: 1
361 },
362 {
363 title: '5',
364 value: 5
365 },
366 {
367 title: '10',
368 value: 10
369 }
370 ];
371
372 return (
373 <Pagination
374 itemCount={totalItemCount}
375 page={page}
376 perPage={perPage}
377 perPageOptions={defaultPerPageOptions}
378 onSetPage={(_evt, value) => {
379 this.fetch(value, perPage);
380 }}
381 onPerPageSelect={(_evt, value) => {
382 this.fetch(1, value);
383 }}
384 variant="top"
385 isCompact
386 />
387 );
388 }
389
390 buildSelectDropdown() {
391 const { splitButtonDropdownIsOpen, selectedItems, areAllSelected } = this.state;
392 const numSelected = selectedItems.length;
393 const allSelected = areAllSelected;
394 const anySelected = numSelected > 0;
395 const someChecked = anySelected ? null : false;
396 const isChecked = allSelected ? true : someChecked;
397 const splitButtonDropdownItems = [
398 <DropdownItem key="item-1" onClick={this.selectNone.bind(this)}>
399 Select none (0 items)
400 </DropdownItem>,
401 <DropdownItem key="item-2" onClick={this.selectPage.bind(this)}>
402 Select page ({this.state.perPage} items)
403 </DropdownItem>,
404 <DropdownItem key="item-3" onClick={this.selectAll.bind(this)}>
405 Select all ({this.state.totalItemCount} items)
406 </DropdownItem>
407 ];
408
409 return (
410 <Dropdown
411 position={DropdownPosition.left}
412 onSelect={this.onSplitButtonSelect}
413 toggle={
414 <DropdownToggle
415 splitButtonItems={[
416 <DropdownToggleCheckbox
417 id="example-checkbox-2"
418 key="split-checkbox"
419 aria-label={anySelected ? 'Deselect all' : 'Select all'}
420 isChecked={areAllSelected}
421 onClick={this.splitCheckboxSelectAll.bind(this)}
422 ></DropdownToggleCheckbox>
423 ]}
424 onToggle={this.onSplitButtonToggle}
425 >
426 {numSelected !== 0 && <React.Fragment>{numSelected} selected</React.Fragment>}
427 </DropdownToggle>
428 }
429 isOpen={splitButtonDropdownIsOpen}
430 dropdownItems={splitButtonDropdownItems}
431 />
432 );
433 }
434
435 buildFilterDropdown() {
436 const { isLowerToolbarDropdownOpen, filters } = this.state;
437
438 const filterDropdownItems = [
439 <SelectOption key="patternfly" value="PatternFly" />,
440 <SelectOption key="activemq" value="ActiveMQ" />,
441 <SelectOption key="apachespark" value="Apache Spark" />,
442 <SelectOption key="avro" value="Avro" />,
443 <SelectOption key="azureservices" value="Azure Services" />,
444 <SelectOption key="crypto" value="Crypto" />,
445 <SelectOption key="dropbox" value="DropBox" />,
446 <SelectOption key="jbossdatagrid" value="JBoss Data Grid" />,
447 <SelectOption key="rest" value="REST" />,
448 <SelectOption key="swagger" value="SWAGGER" />
449 ];
450
451 return (
452 <ToolbarFilter categoryName="Products" chips={filters.products} deleteChip={this.onDelete}>
453 <Select
454 variant={SelectVariant.checkbox}
455 aria-label="Products"
456 onToggle={this.onToolbarDropdownToggle}
457 onSelect={this.onNameSelect}
458 selections={filters.products}
459 isExpanded={isLowerToolbarDropdownOpen}
460 placeholderText="Creator"
461 >
462 {filterDropdownItems}
463 </Select>
464 </ToolbarFilter>
465 );
466 }
467
468 render() {
469 const {
470 isUpperToolbarDropdownOpen,
471 isLowerToolbarDropdownOpen,
472 isUpperToolbarKebabDropdownOpen,
473 isLowerToolbarKebabDropdownOpen,
474 isCardKebabDropdownOpen,
475 splitButtonDropdownIsOpen,
476 activeItem,
477 filters,
478 res,
479 checked,
480 selectedItems,
481 areAllSelected,
482 isChecked,
483 page,
484 perPage
485 } = this.state;
486
487 const toolbarKebabDropdownItems = [
488 <OverflowMenuDropdownItem key="link">Link</OverflowMenuDropdownItem>,
489 <OverflowMenuDropdownItem key="action" component="button">
490 Action
491 </OverflowMenuDropdownItem>,
492 <OverflowMenuDropdownItem key="disabled link" isDisabled>
493 Disabled Link
494 </OverflowMenuDropdownItem>,
495 <OverflowMenuDropdownItem key="disabled action" isDisabled component="button">
496 Disabled Action
497 </OverflowMenuDropdownItem>,
498 <DropdownSeparator key="separator" />,
499 <OverflowMenuDropdownItem key="separated link">Separated Link</OverflowMenuDropdownItem>,
500 <OverflowMenuDropdownItem key="separated action" component="button">
501 Separated Action
502 </OverflowMenuDropdownItem>
503 ];
504
505 const toolbarItems = (
506 <React.Fragment>
507 <ToolbarItem variant="bulk-select">{this.buildSelectDropdown()}</ToolbarItem>
508 <ToolbarItem breakpoint="xl">{this.buildFilterDropdown()}</ToolbarItem>
509 <ToolbarItem variant="overflow-menu">
510 <OverflowMenu breakpoint="md">
511 <OverflowMenuItem>
512 <Button variant="primary">Create a project</Button>
513 </OverflowMenuItem>
514 <OverflowMenuControl hasAdditionalOptions>
515 <Dropdown
516 onSelect={this.onToolbarKebabDropdownSelect}
517 toggle={<KebabToggle onToggle={this.onToolbarKebabDropdownToggle} id="toggle-id-6" />}
518 isOpen={isLowerToolbarKebabDropdownOpen}
519 isPlain
520 dropdownItems={toolbarKebabDropdownItems}
521 />
522 </OverflowMenuControl>
523 </OverflowMenu>
524 </ToolbarItem>
525 <ToolbarItem variant="pagination" alignment={{ default: 'alignRight' }}>
526 {this.renderPagination()}
527 </ToolbarItem>
528 </React.Fragment>
529 );
530
531 const filtered =
532 filters.products.length > 0
533 ? res.filter(card => {
534 return filters.products.length === 0 || filters.products.includes(card.name);
535 })
536 : res;
537
538 const icons = {
539 pfIcon,
540 activeMQIcon,
541 sparkIcon,
542 avroIcon,
543 azureIcon,
544 saxonIcon,
545 dropBoxIcon,
546 infinispanIcon,
547 restIcon,
548 swaggerIcon
549 };
550
551 return (
552 <React.Fragment>
553 <DashboardWrapper mainContainerId="main-content-card-view-default-nav" breadcrumb={null}>
554 <PageSection variant={PageSectionVariants.light}>
555 <TextContent>
556 <Text component="h1">Projects</Text>
557 <Text component="p">This is a demo that showcases PatternFly cards.</Text>
558 </TextContent>
559 <Toolbar id="toolbar-group-types" clearAllFilters={this.onDelete}>
560 <ToolbarContent>{toolbarItems}</ToolbarContent>
561 </Toolbar>
562 </PageSection>
563 <PageSection isFilled>
564 <Gallery hasGutter aria-label="Selectable card container">
565 <Card isSelectableRaised isCompact>
566 <Bullseye>
567 <EmptyState variant={EmptyStateVariant.xs}>
568 <EmptyStateIcon icon={PlusCircleIcon} />
569 <Title headingLevel="h2" size="md">
570 Add a new card to your page
571 </Title>
572 <EmptyStateSecondaryActions>
573 <Button variant="link">Add card</Button>
574 </EmptyStateSecondaryActions>
575 </EmptyState>
576 </Bullseye>
577 </Card>
578 {filtered.map((product, key) => (
579 <Card
580 isSelectableRaised
581 hasSelectableInput
582 isCompact
583 key={product.name}
584 id={product.name.replace(/ /g, '-')}
585 onKeyDown={e => this.onKeyDown(e, product.id)}
586 onClick={() => this.onClick(product.id)}
587 onSelectableInputChange={() => this.onClick(product.id)}
588 isSelected={selectedItems.includes(product.id)}
589 >
590 <CardHeader>
591 <img src={icons[product.icon]} alt={`${product.name} icon`} style={{ maxWidth: '60px' }} />
592 <CardActions>
593 <Dropdown
594 isPlain
595 position="right"
596 onSelect={e => this.onCardKebabDropdownSelect(key, e)}
597 toggle={
598 <KebabToggle
599 onToggle={isCardKebabDropdownOpen =>
600 this.onCardKebabDropdownToggle(key, isCardKebabDropdownOpen)
601 }
602 />
603 }
604 isOpen={this.state[key]}
605 dropdownItems={[
606 <DropdownItem key="trash" onClick={this.deleteItem(product)} position="right">
607 <TrashIcon />
608 Delete
609 </DropdownItem>
610 ]}
611 />
612 <Checkbox
613 checked={isChecked}
614 value={product.id}
615 isChecked={selectedItems.includes(product.id)}
616 aria-label="card checkbox example"
617 id={`check-${product.id}`}
618 />
619 </CardActions>
620 </CardHeader>
621 <CardTitle>{product.name}</CardTitle>
622 <CardBody>{product.description}</CardBody>
623 </Card>
624 ))}
625 </Gallery>
626 </PageSection>
627 <PageSection isFilled={false} sticky="bottom" padding={{ default: 'noPadding' }} variant="light">
628 <Pagination
629 itemCount={this.state.totalItemCount}
630 page={page}
631 page={this.state.page}
632 perPage={this.state.perPage}
633 onPerPageSelect={this.onPerPageSelect}
634 onSetPage={this.onSetPage}
635 variant="bottom"
636 />
637 </PageSection>
638 </DashboardWrapper>
639 </React.Fragment>
640 );
641 }
642}
643```