diff --git a/src/app/Resources/Resources.tsx b/src/app/Resources/Resources.tsx new file mode 100644 index 00000000..db009693 --- /dev/null +++ b/src/app/Resources/Resources.tsx @@ -0,0 +1,654 @@ +import React from 'react'; +import { + Badge, + Button, + Card, + CardHeader, + CardBody, + CardTitle, + Divider, + Drawer, + DrawerActions, + DrawerPanelBody, + DrawerCloseButton, + DrawerContent, + DrawerContentBody, + DrawerHead, + DrawerPanelContent, + Dropdown, + DropdownItem, + DropdownList, + Flex, + FlexItem, + Gallery, + MenuToggle, + MenuToggleCheckbox, + PageSection, + PageSectionVariants, + Pagination, + Progress, + Select, + SelectList, + SelectOption, + TextContent, + Text, + Title, + Toolbar, + ToolbarFilter, + ToolbarItem, + ToolbarContent, + MenuToggleElement, +} from '@patternfly/react-core'; +import TrashIcon from '@patternfly/react-icons/dist/esm/icons/trash-icon'; +import pfIcon from '../../assets/pf-logo-small.svg'; +import activeMQIcon from '../../assets/activemq-core_200x150.png'; +import avroIcon from '../../assets/camel-avro_200x150.png'; +import dropBoxIcon from '../../assets/camel-dropbox_200x150.png'; +import infinispanIcon from '../../assets/camel-infinispan_200x150.png'; +import saxonIcon from '../../assets/camel-saxon_200x150.png'; +import sparkIcon from '../../assets/camel-spark_200x150.png'; +import swaggerIcon from '../../assets/camel-swagger-java_200x150.png'; +import azureIcon from '../../assets/FuseConnector_Icons_AzureServices.png'; +import restIcon from '../../assets/FuseConnector_Icons_REST.png'; +import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; +import { DashboardWrapper } from '@patternfly/react-core/src/demos/DashboardWrapper'; +import { data } from '@patternfly/react-core/src/demos/CardView/examples/CardViewData.jsx'; + +export const PrimaryDetailCardView: React.FunctionComponent = () => { + const [totalItemCount, setTotalItemCount] = React.useState(10); + const [cardData, setCardData] = React.useState(data); + const [isChecked, setIsChecked] = React.useState(false); + const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false); + const [selectedItems, setSelectedItems] = React.useState([]); + const [areAllSelected, setAreAllSelected] = React.useState(false); + const [splitButtonDropdownIsOpen, setSplitButtonDropdownIsOpen] = React.useState(false); + const [isLowerToolbarDropdownOpen, setIsLowerToolbarDropdownOpen] = React.useState(false); + const [isLowerToolbarKebabDropdownOpen, setIsLowerToolbarKebabDropdownOpen] = React.useState(false); + const [page, setPage] = React.useState(1); + const [perPage, setPerPage] = React.useState(10); + const [filters, setFilters] = React.useState>({ products: [] }); + const [state, setState] = React.useState({}); + const [activeCard, setActiveCard] = React.useState(-1); + + interface ProductType { + id: number; + name: string; + icon: string; + description: string; + } + + const onToolbarDropdownToggle = () => { + setIsLowerToolbarDropdownOpen(!isLowerToolbarDropdownOpen); + }; + + const onToolbarKebabDropdownToggle = () => { + setIsLowerToolbarKebabDropdownOpen(!isLowerToolbarKebabDropdownOpen); + }; + + const onToolbarKebabDropdownSelect = () => { + setIsLowerToolbarKebabDropdownOpen(!isLowerToolbarKebabDropdownOpen); + }; + + const onCardKebabDropdownToggle = ( + event: React.MouseEvent | React.MouseEvent, + key: string, + ) => { + setState({ + [key]: !state[key as keyof Object], + }); + }; + + const checkAllSelected = (selected: number, total: number) => { + if (selected && selected < total) { + return null; + } + return selected === total; + }; + + const onNameSelect = (event: any, selection = '') => { + const checked = event.target.checked; + const prevSelections = filters.products; + + setFilters({ + ...filters, + products: checked ? [...prevSelections, selection] : prevSelections.filter((value) => value !== selection), + }); + }; + + const onDelete = (type = '', _id = '') => { + if (type) { + setFilters(filters); + } else { + setFilters({ products: [] }); + } + }; + + const deleteItem = (item: ProductType) => { + const filter = (getter) => (val) => getter(val) !== item.id; + + setCardData(cardData.filter(filter(({ id }) => id))); + + setSelectedItems(selectedItems.filter(filter((id) => id))); + + setTotalItemCount(totalItemCount - 1); + setIsDrawerExpanded(false); + setActiveCard(-1); + }; + + const onSetPage = (_event: any, pageNumber: number) => { + setPage(pageNumber); + }; + + const onPerPageSelect = (_event: any, perPage: number) => { + setPerPage(perPage); + setPage(1); + }; + + const onSplitButtonToggle = () => { + setSplitButtonDropdownIsOpen(!splitButtonDropdownIsOpen); + }; + + const onSplitButtonSelect = () => { + setSplitButtonDropdownIsOpen(false); + setIsDrawerExpanded(false); + setActiveCard(-1); + }; + + const onCloseDrawerClick = () => { + setActiveCard(-1); + setIsDrawerExpanded(false); + }; + + const onChange = (event: React.FormEvent) => { + const name = event.currentTarget.name; + const productId = Number(name.charAt(name.length - 1)); + + if (selectedItems.includes(productId * 1)) { + setSelectedItems(selectedItems.filter((id) => productId * 1 !== id)); + + const checkAll = checkAllSelected(selectedItems.length - 1, totalItemCount); + setAreAllSelected(!!checkAll); + } else { + setSelectedItems([...selectedItems, productId * 1]); + const checkAll = checkAllSelected(selectedItems.length + 1, totalItemCount); + setAreAllSelected(!!checkAll); + } + }; + + const onCardClick = (productId) => { + if (productId === activeCard) { + setIsDrawerExpanded(!isDrawerExpanded); + setActiveCard(-1); + } else { + setActiveCard(productId); + setIsDrawerExpanded(true); + } + }; + + const selectPage = (e: { target: { checked: any } }) => { + const { checked } = e.target; + let collection: number[] = []; + + collection = getAllItems(); + + setSelectedItems(collection); + setIsChecked(checked); + setAreAllSelected(totalItemCount === perPage ? true : false); + + updateSelected(); + }; + + const selectAll = () => { + let collection: number[] = []; + for (let i = 0; i <= 9; i++) { + collection = [...collection, i]; + } + + setSelectedItems(collection); + setIsChecked(true); + setAreAllSelected(true); + + updateSelected(); + }; + + const selectNone = () => { + setSelectedItems([]); + setIsChecked(false); + setAreAllSelected(false); + setIsDrawerExpanded(false); + setActiveCard(-1); + + updateSelected(); + }; + + const updateSelected = () => { + const rows = cardData.map((post) => { + post.selected = selectedItems.includes(post.id); + return post; + }); + + setCardData(rows); + }; + + const getAllItems = () => { + const collection: number[] = []; + for (const items of cardData) { + collection.push(items.id); + } + + return collection; + }; + + const splitCheckboxSelectAll = (e: any) => { + let collection: number[] = []; + + if (e.target.checked) { + for (let i = 0; i <= 9; i++) { + collection = [...collection, i]; + } + } + + setSelectedItems(collection); + setIsChecked(isChecked); + setAreAllSelected(e.target.checked); + setIsDrawerExpanded(false); + setActiveCard(-1); + + updateSelected(); + }; + + const renderPagination = () => { + const defaultPerPageOptions = [ + { + title: '1', + value: 1, + }, + { + title: '5', + value: 5, + }, + { + title: '10', + value: 10, + }, + ]; + + return ( + + ); + }; + + const buildSelectDropdown = () => { + const numSelected = selectedItems.length; + const anySelected = numSelected > 0; + const splitButtonDropdownItems = ( + <> + + Select none (0 items) + + + Select page ({perPage} items) + + + Select all ({totalItemCount} items) + + + ); + + return ( + setSplitButtonDropdownIsOpen(isOpen)} + toggle={(toggleRef) => ( + + {numSelected !== 0 && `${numSelected} selected`} + , + ], + }} + > + )} + > + {splitButtonDropdownItems} + + ); + }; + + const buildFilterDropdown = () => { + const filterDropdownItems = ( + + + PatternFly + + + ActiveMQ + + + Apache Spark + + + Avro + + + Azure Services + + + Crypto + + + DropBox + + + JBoss Data Grid + + + REST + + + SWAGGER + + + ); + + return ( + onDelete(type as string, id as string)} + > + + + ); + }; + + const toolbarKebabDropdownItems = ( + <> + + Action + + ev.preventDefault()} + > + Link + + + Disabled Action + + + Disabled Link + + + + Separated Action + + ev.preventDefault()}> + Separated Link + + + ); + const toolbarItems = ( + + {buildSelectDropdown()} + {buildFilterDropdown()} + + + + + + + + setIsLowerToolbarKebabDropdownOpen(isOpen)} + toggle={(toggleRef) => ( + + + )} + > + {toolbarKebabDropdownItems} + + + + {renderPagination()} + + + ); + + const filtered = + filters.products.length > 0 + ? data.filter((card: { name: string }) => filters.products.length === 0 || filters.products.includes(card.name)) + : cardData.slice((page - 1) * perPage, perPage === 1 ? page * perPage : page * perPage - 1); + + const icons = { + pfIcon, + activeMQIcon, + sparkIcon, + avroIcon, + azureIcon, + saxonIcon, + dropBoxIcon, + infinispanIcon, + restIcon, + swaggerIcon, + }; + + const drawerContent = ( + + {filtered.map((product, key) => ( + + + setState({ [key]: isOpen })} + popperProps={{ position: 'right' }} + toggle={(toggleRef: React.Ref) => ( + { + onCardKebabDropdownToggle(e, key.toString()); + }} + isExpanded={!!state[key]} + > + + + )} + > + + { + deleteItem(product); + }} + > + + Delete + + + + + ), + }} + selectableActions={{ + isChecked: selectedItems.includes(product.id), + selectableActionId: `selectable-actions-item-${product.id}`, + selectableActionAriaLabelledby: `${'card-view-' + key}`, + name: `check-${product.id}`, + onChange, + }} + > + {`${product.name} + + + + + + + + + + Provided by Red Hat + + + + + + + {product.description} + + ))} + + ); + + const panelContent = ( + + + + node-{activeCard} + + + + + + + + +

+ The content of the drawer really is up to you. It could have form fields, definition lists, text lists, + labels, charts, progress bars, etc. Spacing recommendation is 24px margins. You can put tabs in here, and + can also make the drawer scrollable. +

+
+ + + + + + +
+
+
+ ); + + return ( + + + + Projects + This is a demo that showcases Patternfly Cards. + + + + + {toolbarItems} + + + + + + + {drawerContent} + + + + + + + + ); +}; diff --git a/src/app/Support/Support.tsx b/src/app/Support/Support.tsx deleted file mode 100644 index a966d53e..00000000 --- a/src/app/Support/Support.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import { CubesIcon } from '@patternfly/react-icons'; -import { - Button, - EmptyState, - EmptyStateActions, - EmptyStateBody, - EmptyStateFooter, - EmptyStateVariant, - PageSection, - Text, - TextContent, - TextVariants, -} from '@patternfly/react-core'; - -export interface ISupportProps { - sampleProp?: string; -} - -// eslint-disable-next-line prefer-const -let Support: React.FunctionComponent = () => ( - - - - - - This represents an the empty state pattern in Patternfly 4. Hopefully it's simple enough to use but - flexible enough to meet a variety of needs. - - - This text has overridden a css component variable to demonstrate how to apply customizations using - PatternFly's global variable API. - - - - - - - - - - - - - - -); - -export { Support }; diff --git a/src/app/routes.tsx b/src/app/routes.tsx index 4acb14ef..0a3c54b4 100644 --- a/src/app/routes.tsx +++ b/src/app/routes.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Route, RouteComponentProps, Switch, useLocation } from 'react-router-dom'; import { Dashboard } from '@app/Dashboard/Dashboard'; -import { Support } from '@app/Support/Support'; +import { PrimaryDetailCardView as Resources } from '@app/Resources/Resources'; import { GeneralSettings } from '@app/Settings/General/GeneralSettings'; import { ProfileSettings } from '@app/Settings/Profile/ProfileSettings'; import { NotFound } from '@app/NotFound/NotFound'; @@ -35,11 +35,11 @@ const routes: AppRouteConfig[] = [ title: 'PatternFly Seed | Main Dashboard', }, { - component: Support, + component: Resources, exact: true, - label: 'Support', - path: '/support', - title: 'PatternFly Seed | Support Page', + label: 'Resources', + path: '/resources', + title: 'PatternFly Seed | Resources Page', }, { label: 'Settings', @@ -99,7 +99,7 @@ const PageNotFound = ({ title }: { title: string }) => { const flattenedRoutes: IAppRoute[] = routes.reduce( (flattened, route) => [...flattened, ...(route.routes ? route.routes : [route])], - [] as IAppRoute[] + [] as IAppRoute[], ); const AppRoutes = (): React.ReactElement => ( diff --git a/src/assets/FuseConnector_Icons_AzureServices.png b/src/assets/FuseConnector_Icons_AzureServices.png new file mode 100644 index 00000000..2b901285 Binary files /dev/null and b/src/assets/FuseConnector_Icons_AzureServices.png differ diff --git a/src/assets/FuseConnector_Icons_REST.png b/src/assets/FuseConnector_Icons_REST.png new file mode 100644 index 00000000..c37c95b5 Binary files /dev/null and b/src/assets/FuseConnector_Icons_REST.png differ diff --git a/src/assets/activemq-core_200x150.png b/src/assets/activemq-core_200x150.png new file mode 100644 index 00000000..b022627b Binary files /dev/null and b/src/assets/activemq-core_200x150.png differ diff --git a/src/assets/camel-avro_200x150.png b/src/assets/camel-avro_200x150.png new file mode 100644 index 00000000..4114648d Binary files /dev/null and b/src/assets/camel-avro_200x150.png differ diff --git a/src/assets/camel-dropbox_200x150.png b/src/assets/camel-dropbox_200x150.png new file mode 100644 index 00000000..4c623751 Binary files /dev/null and b/src/assets/camel-dropbox_200x150.png differ diff --git a/src/assets/camel-infinispan_200x150.png b/src/assets/camel-infinispan_200x150.png new file mode 100644 index 00000000..ad48236b Binary files /dev/null and b/src/assets/camel-infinispan_200x150.png differ diff --git a/src/assets/camel-saxon_200x150.png b/src/assets/camel-saxon_200x150.png new file mode 100644 index 00000000..6b3110f7 Binary files /dev/null and b/src/assets/camel-saxon_200x150.png differ diff --git a/src/assets/camel-spark_200x150.png b/src/assets/camel-spark_200x150.png new file mode 100644 index 00000000..44ba1945 Binary files /dev/null and b/src/assets/camel-spark_200x150.png differ diff --git a/src/assets/camel-swagger-java_200x150.png b/src/assets/camel-swagger-java_200x150.png new file mode 100644 index 00000000..2134e75e Binary files /dev/null and b/src/assets/camel-swagger-java_200x150.png differ diff --git a/src/assets/index.d.ts b/src/assets/index.d.ts new file mode 100644 index 00000000..cdb2b1a9 --- /dev/null +++ b/src/assets/index.d.ts @@ -0,0 +1,4 @@ +declare module '*.svg' { + const content: string; + export default content; +} diff --git a/src/assets/pf-logo-small.svg b/src/assets/pf-logo-small.svg new file mode 100644 index 00000000..44c09243 --- /dev/null +++ b/src/assets/pf-logo-small.svg @@ -0,0 +1,23 @@ + + + + Group 12 + Created with Sketch. + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/stories/Support.stories.tsx b/stories/Support.stories.tsx index bef4ae66..2c41bb7b 100644 --- a/stories/Support.stories.tsx +++ b/stories/Support.stories.tsx @@ -1,5 +1,5 @@ import React, { ComponentProps } from 'react'; -import { Support } from '@app/Support/Support'; +import { Support } from '@app/Resources/Resources'; import { Story } from '@storybook/react'; //👇 This default export determines where your story goes in the story list