diff --git a/.gitignore b/.gitignore index b2ec084..fa7e72b 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ lerna-debug.log # For vim *.swp -public \ No newline at end of file +public +**/generated/**/*.js diff --git a/packages/module/buildSass.js b/packages/module/buildSass.js index 79f3064..8be2ee0 100644 --- a/packages/module/buildSass.js +++ b/packages/module/buildSass.js @@ -29,4 +29,4 @@ if (!fs.existsSync(outDir)) { fs.mkdirSync(outDir, { recursive: true }); } -fs.writeFileSync(path.join(outDir, 'react-catalog-view-extension.css'), res.css); +fs.writeFileSync(path.join('src/components', 'react-catalog-view-extension.css'), res.css); diff --git a/packages/module/package.json b/packages/module/package.json index 15ac391..50532d1 100644 --- a/packages/module/package.json +++ b/packages/module/package.json @@ -17,9 +17,10 @@ "docs:screenshots": "pf-docs-framework screenshots --urlPrefix http://localhost:5000", "test:a11y": "patternfly-a11y --config patternfly-a11y.config", "serve:a11y": "yarn serve coverage", - "generate": "yarn build:sass && yarn copy:sass", + "generate": "yarn build:sass && yarn copy:sass && yarn copy:css", "build:sass": "node buildSass.js", - "copy:sass": "shx mkdir -p dist/sass && shx cp -r sass/react-catalog-view-extension/* dist/sass" + "copy:sass": "shx mkdir -p dist/sass && shx cp -r sass/react-catalog-view-extension/* dist/sass", + "copy:css": "shx mkdir -p dist/css && shx cp src/components/*.css dist/css" }, "repository": { "type": "git", diff --git a/packages/module/patternfly-docs/content/examples/CatalogItemHeader.md b/packages/module/patternfly-docs/content/examples/CatalogItemHeader.md index ac7767e..1f35f5a 100644 --- a/packages/module/patternfly-docs/content/examples/CatalogItemHeader.md +++ b/packages/module/patternfly-docs/content/examples/CatalogItemHeader.md @@ -9,7 +9,6 @@ sourceLink: https://github.com/patternfly/react-catalog-view/tree/main/packages/ import { CatalogItemHeader } from '@patternfly/react-catalog-view-extension'; import pfLogo6 from './pfLogo6.svg'; -import './catalogItemHeader.css'; ## Introduction Note: Catalog item header lives in its own package at [`@patternfly/react-catalog-view-extension`](https://www.npmjs.com/package/@patternfly/react-catalog-view-extension)! diff --git a/packages/module/patternfly-docs/content/examples/CatalogTile.md b/packages/module/patternfly-docs/content/examples/CatalogTile.md index e82dbda..bb33e49 100644 --- a/packages/module/patternfly-docs/content/examples/CatalogTile.md +++ b/packages/module/patternfly-docs/content/examples/CatalogTile.md @@ -11,7 +11,6 @@ import { CatalogTile, CatalogTileBadge } from '@patternfly/react-catalog-view-ex import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon'; import OutlinedCheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/outlined-check-circle-icon'; import pfLogo6 from './pfLogo6.svg'; -import './catalogTile.css'; ## Introduction diff --git a/packages/module/patternfly-docs/content/examples/CatalogView.md b/packages/module/patternfly-docs/content/examples/CatalogView.md new file mode 100644 index 0000000..b333c67 --- /dev/null +++ b/packages/module/patternfly-docs/content/examples/CatalogView.md @@ -0,0 +1,659 @@ +--- +id: Catalog view demo +section: extensions +subsection: Catalog view +source: react +--- + +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import OutlinedCheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/outlined-check-circle-icon'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; +import { +CatalogTile, +FilterSidePanel, +FilterSidePanelCategory, +FilterSidePanelCategoryItem, +PropertiesSidePanel, +PropertyItem, +VerticalTabs, +VerticalTabsTab +} from '@patternfly/react-catalog-view-extension'; +import pfLogo6 from './pfLogo6.svg'; + +## Catalog view + +To use the catalog view extension components, you need to import the CSS file into your project to get the proper styles: `import '@patternfly/react-catalog-view-extension/dist/css/react-catalog-view-extension.css';` + +### Catalog view demo + +```ts isFullscreen +import React from 'react'; +import { + Page, + PageSection, + Toolbar, + ToolbarContent, + ToolbarGroup, + ToolbarItem, + SearchInput, + Button, + ButtonVariant, + Pagination, + Divider, + Checkbox, + Badge, + Grid, + GridItem, + Split, + SplitItem, + Stack, + StackItem, + Drawer, + DrawerContent, + DrawerContentBody, + DrawerPanelContent, + DrawerPanelBody, + DrawerHead, + DrawerActions, + DrawerCloseButton, + DescriptionList, + DescriptionListGroup, + DescriptionListTerm, + DescriptionListDescription, + Title, + Text, + TextContent, + Masthead, + TextVariants, + Icon, + EmptyState, + EmptyStateBody, + EmptyStateActions, + EmptyStateFooter +} from '@patternfly/react-core'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import OutlinedCheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/outlined-check-circle-icon'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; +import { + CatalogTile, + FilterSidePanel, + FilterSidePanelCategory, + FilterSidePanelCategoryItem, + PropertiesSidePanel, + PropertyItem, + VerticalTabs, + VerticalTabsTab +} from '@patternfly/react-catalog-view-extension'; +import pfLogo6 from './pfLogo6.svg'; + +export const CatalogViewDemo: React.FunctionComponent = () => { + const [searchValue, setSearchValue] = React.useState(''); + const [selectedFilters, setSelectedFilters] = React.useState([]); + const [currentPage, setCurrentPage] = React.useState(1); + const [perPage, setPerPage] = React.useState(10); + const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false); + const [selectedItem, setSelectedItem] = React.useState(null); + const [activeTab, setActiveTab] = React.useState('all'); + + const handleSearchChange = (event: React.FormEvent, value: string) => { + setSearchValue(value); + setCurrentPage(1); // Reset to first page when searching + }; + + const handleFilterChange = (filterId: string, checked: boolean) => { + if (checked) { + setSelectedFilters([...selectedFilters, filterId]); + } else { + setSelectedFilters(selectedFilters.filter((id) => id !== filterId)); + } + setCurrentPage(1); // Reset to first page when filtering + }; + + const handleTileClick = (item: any) => { + setSelectedItem(item); + setIsDrawerExpanded(true); + }; + + const handleDrawerClose = () => { + setIsDrawerExpanded(false); + setSelectedItem(null); + }; + + const handleTabActivate = (tabId: string) => { + setActiveTab(tabId); + setCurrentPage(1); // Reset to first page when changing tabs + }; + + const catalogItems = [ + { + id: '1', + title: 'Red Hat OpenShift', + vendor: 'Red Hat, Inc.', + description: + 'Enterprise Kubernetes platform that provides a consistent foundation for cloud-native applications across hybrid and multi-cloud environments.', + iconImg: pfLogo6, + badge: 'Beta', + badgeColor: 'blue', + featured: true, + href: '#', + version: '4.15.2', + lastUpdated: '2024-01-15', + category: 'platform' + }, + { + id: '2', + title: 'Kubernetes Operator', + vendor: 'Community', + description: + 'A Kubernetes operator that automates the deployment, configuration, and management of complex applications on Kubernetes clusters.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + version: '1.8.3', + lastUpdated: '2024-01-10', + category: 'operators' + }, + { + id: '3', + title: 'Ansible Automation', + vendor: 'Red Hat, Inc.', + description: + 'IT automation platform that automates cloud provisioning, configuration management, application deployment, and service orchestration.', + iconImg: pfLogo6, + badge: 'Stable', + badgeColor: 'green', + featured: false, + href: '#', + version: '2.16.1', + lastUpdated: '2024-01-08', + category: 'automation' + }, + { + id: '4', + title: 'Red Hat Enterprise Linux', + vendor: 'Red Hat, Inc.', + description: + 'Enterprise Linux operating system designed for mission-critical workloads with enhanced security, performance, and reliability features.', + iconImg: pfLogo6, + badge: 'Certified', + badgeColor: 'green', + featured: false, + href: '#', + version: '9.3', + lastUpdated: '2024-01-12', + category: 'platform' + }, + { + id: '5', + title: 'Red Hat JBoss', + vendor: 'Red Hat, Inc.', + description: + 'Enterprise application platform that provides a complete solution for developing, deploying, and managing Java applications.', + iconImg: pfLogo6, + badge: 'Stable', + badgeColor: 'green', + featured: false, + href: '#', + version: '7.4.10', + lastUpdated: '2024-01-05', + category: 'applications' + }, + { + id: '6', + title: 'Red Hat Quay', + vendor: 'Red Hat, Inc.', + description: + 'Enterprise container registry that provides secure storage, distribution, and deployment of container images with advanced security features.', + iconImg: pfLogo6, + badge: 'Beta', + badgeColor: 'blue', + featured: false, + href: '#', + version: '3.9.2', + lastUpdated: '2024-01-18', + category: 'platform' + }, + { + id: '7', + title: 'Red Hat AMQ', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'messaging' + }, + { + id: '8', + title: 'Red Hat Fuse', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'integration' + }, + { + id: '9', + title: 'Red Hat Data Grid', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'data' + }, + { + id: '10', + title: 'Red Hat Single Sign-On', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'security' + }, + { + id: '11', + title: 'Red Hat 3scale', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'api' + }, + { + id: '12', + title: 'Red Hat CodeReady', + vendor: 'Provided by Red Hat', + description: + 'This is a very, very long stretch of child text that should not be truncated and illustrates how the component handles long descriptions.', + iconImg: pfLogo6, + badge: 'Community', + badgeColor: 'grey', + featured: false, + href: '#', + category: 'development' + } + ]; + + // Get items for the current tab + const getItemsForTab = (tabId: string) => { + if (tabId === 'all') { + return catalogItems; + } + return catalogItems.filter(item => item.category === tabId); + }; + + // Calculate counts for each filter option based on current tab + const getFilterCounts = () => { + const currentItems = getItemsForTab(activeTab); + const counts = { + vendor: {}, + badge: {}, + featured: {} + }; + + // Count vendor options + currentItems.forEach((item) => { + if (item.vendor.includes('Red Hat')) { + counts.vendor['red-hat'] = (counts.vendor['red-hat'] || 0) + 1; + } + if (item.vendor.includes('Community')) { + counts.vendor['community'] = (counts.vendor['community'] || 0) + 1; + } + }); + + // Count badge options + currentItems.forEach((item) => { + const badgeLower = item.badge.toLowerCase(); + counts.badge[badgeLower] = (counts.badge[badgeLower] || 0) + 1; + }); + + // Count featured options + currentItems.forEach((item) => { + if (item.featured) { + counts.featured['featured'] = (counts.featured['featured'] || 0) + 1; + } else { + counts.featured['not-featured'] = (counts.featured['not-featured'] || 0) + 1; + } + }); + + return counts; + }; + + const filterCounts = getFilterCounts(); + + const filterCategories = [ + { + id: 'vendor', + title: 'Provider', + items: [ + { id: 'red-hat', label: `Red Hat, Inc.` }, + { id: 'community', label: `Community` } + ] + }, + { + id: 'badge', + title: 'Type', + items: [ + { id: 'beta', label: `Beta (${filterCounts.badge['beta'] || 0})` }, + { id: 'community', label: `Community (${filterCounts.badge['community'] || 0})` }, + { id: 'stable', label: `Stable (${filterCounts.badge['stable'] || 0})` }, + { id: 'certified', label: `Certified (${filterCounts.badge['certified'] || 0})` } + ] + }, + { + id: 'featured', + title: 'Featured', + items: [ + { id: 'featured', label: `Featured` }, + { id: 'not-featured', label: `Not Featured` } + ] + } + ]; + + const renderBadge = (badge: string, color: string) => { + const badgeColors = { + blue: 'blue', + grey: 'grey' + }; + return {badge}; + }; + + const renderCatalogTile = (item: any) => ( + handleTileClick(item)} + isSelected={selectedItem?.id === item.id} + footer={ + <> + + + {' '} + Enabled + + } + /> + ); + + // Filter items based on search and selected filters + const getFilteredItems = () => { + const currentItems = getItemsForTab(activeTab); + + return currentItems.filter((item) => { + // Search filter + if (searchValue && typeof searchValue === 'string') { + const searchLower = searchValue.toLowerCase(); + const matchesSearch = + item.title.toLowerCase().includes(searchLower) || + item.vendor.toLowerCase().includes(searchLower) || + item.description.toLowerCase().includes(searchLower); + if (!matchesSearch) return false; + } + + // Filter by selected filters + if (selectedFilters.length > 0) { + // Group filters by category + const filtersByCategory = selectedFilters.reduce((acc, filterId) => { + const [category, value] = filterId.split(':'); + if (!acc[category]) acc[category] = []; + acc[category].push(value); + return acc; + }, {} as Record); + + // Check if item matches any filter in each category (OR logic within categories) + const matchesFilters = Object.entries(filtersByCategory).every(([category, values]) => { + switch (category) { + case 'vendor': + return values.some((value) => { + if (value === 'red-hat') return item.vendor.includes('Red Hat'); + if (value === 'community') return item.vendor.includes('Community'); + return item.vendor === value; + }); + case 'badge': + return values.some((value) => item.badge.toLowerCase() === value); + case 'featured': + return values.some((value) => { + if (value === 'featured') return item.featured === true; + if (value === 'not-featured') return item.featured === false; + return false; + }); + default: + return false; + } + }); + + if (!matchesFilters) return false; + } + return true; + }); + }; + + const filteredItems = getFilteredItems(); + const paginatedItems = filteredItems.slice((currentPage - 1) * perPage, currentPage * perPage); + + // Check if we should show empty state + const showEmptyState = filteredItems.length === 0; + + return ( + }> + + + + + + setSearchValue('')} + /> + + + + + + + + + + + + + setCurrentPage(page)} + onPerPageSelect={(_, perPage) => setPerPage(perPage)} + isCompact + /> + + + + + + + + + + + {selectedItem?.title || 'Catalog Item Details'} + +

{selectedItem?.description || 'Select a catalog item to view details'}

+
+ + + +
+ + {selectedItem && ( + + + + + + + Enabled + + } + /> + + + + + + )} + + + } + > + + + + + + handleTabActivate('all')} + /> + handleTabActivate('platform')} + /> + handleTabActivate('applications')} + /> + handleTabActivate('operators')} + /> + handleTabActivate('automation')} + /> + handleTabActivate('security')} + /> + + + + {filterCategories.map((category) => ( + + + {category.items.map((item) => ( + + handleFilterChange( + `${category.id}:${item.id}`, + !selectedFilters.includes(`${category.id}:${item.id}`) + ) + } + title={item.label} + > + {item.label} + + ))} + + + ))} + + + + + {showEmptyState ? ( + + + No catalog items match your current search criteria or selected filters. +
+ Try adjusting your search terms or clearing some filters. +
+ + + + + +
+ ) : ( + + {paginatedItems.map((item) => ( + + {renderCatalogTile(item)} + + ))} + + )} +
+
+
+
+
+
+
+
+ ); +}; + +export default CatalogViewDemo; +``` diff --git a/packages/module/patternfly-docs/content/examples/FilterSidePanel.md b/packages/module/patternfly-docs/content/examples/FilterSidePanel.md index 0afabf2..37d1938 100644 --- a/packages/module/patternfly-docs/content/examples/FilterSidePanel.md +++ b/packages/module/patternfly-docs/content/examples/FilterSidePanel.md @@ -16,8 +16,6 @@ import CcVisaIcon from '@patternfly/react-icons/dist/esm/icons/cc-visa-icon'; import CcMastercardIcon from '@patternfly/react-icons/dist/esm/icons/cc-mastercard-icon'; import CcDinersClubIcon from '@patternfly/react-icons/dist/esm/icons/cc-diners-club-icon'; -import './filterSidePanel.css'; - ## Introduction Note: FilterSidePanel lives in its own package at [`@patternfly/react-catalog-view-extension`](https://www.npmjs.com/package/@patternfly/react-catalog-view-extension)! diff --git a/packages/module/patternfly-docs/content/examples/PropertiesSidePanel.md b/packages/module/patternfly-docs/content/examples/PropertiesSidePanel.md index c34ac9d..0b6a8eb 100644 --- a/packages/module/patternfly-docs/content/examples/PropertiesSidePanel.md +++ b/packages/module/patternfly-docs/content/examples/PropertiesSidePanel.md @@ -11,7 +11,6 @@ import { PropertiesSidePanel, PropertyItem } from '@patternfly/react-catalog-vie import OkIcon from '@patternfly/react-icons/dist/esm/icons/ok-icon'; import ExternalLinkAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-alt-icon'; import GlobeIcon from '@patternfly/react-icons/dist/esm/icons/globe-icon'; -import './propertiesSidePanel.css'; ## Introduction diff --git a/packages/module/patternfly-docs/content/examples/VerticalTabs.md b/packages/module/patternfly-docs/content/examples/VerticalTabs.md index 2c3a828..7e91eb4 100644 --- a/packages/module/patternfly-docs/content/examples/VerticalTabs.md +++ b/packages/module/patternfly-docs/content/examples/VerticalTabs.md @@ -8,7 +8,6 @@ sourceLink: https://github.com/patternfly/react-catalog-view/tree/main/packages/ --- import { VerticalTabs, VerticalTabsTab } from '@patternfly/react-catalog-view-extension'; -import './verticalTab.css'; ## Introduction diff --git a/packages/module/patternfly-docs/content/examples/catalogItemHeader.css b/packages/module/patternfly-docs/content/examples/catalogItemHeader.css deleted file mode 100644 index e49ae3d..0000000 --- a/packages/module/patternfly-docs/content/examples/catalogItemHeader.css +++ /dev/null @@ -1,27 +0,0 @@ -.ws-react-e-catalog-item-header .text-overflow-pf { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - word-wrap: normal; -} -.ws-react-e-catalog-item-header .catalog-item-header-pf { - display: flex; - align-items: center; -} -.ws-react-e-catalog-item-header .catalog-item-header-pf-icon { - font-size: var(--pf-t--global--icon--size--2xl); - max-height: var(--pf-t--global--icon--size--2xl); - width: var(--pf-t--global--icon--size--2xl); -} -.ws-react-e-catalog-item-header .catalog-item-header-pf-text { - margin-inline-start: var(--pf-t--global--spacer--md); -} -.ws-react-e-catalog-item-header .catalog-item-header-pf-title { - margin-block-end: 0; - margin-block-start: 0; -} -.ws-react-e-catalog-item-header .catalog-item-header-pf-subtitle { - color: var(--pf-t--global--text--color--subtle); - font-size: var(--pf-t--global--font--size--body--sm); - margin-block-end: 0; -} diff --git a/packages/module/patternfly-docs/content/examples/catalogTile.css b/packages/module/patternfly-docs/content/examples/catalogTile.css deleted file mode 100644 index 3182ade..0000000 --- a/packages/module/patternfly-docs/content/examples/catalogTile.css +++ /dev/null @@ -1,50 +0,0 @@ -.ws-react-e-catalog-tile .catalog-tile-pf:active, .ws-react-e-catalog-tile .catalog-tile-pf:hover, .ws-react-e-catalog-tile .catalog-tile-pf:focus, .ws-react-e-catalog-tile .catalog-tile-pf:visited { - color: var(--pf-t--global--text--color--regular); - text-decoration: none; -} -.ws-react-e-catalog-tile .pf-v6-c-card__header-main { - display: flex; -} -.ws-react-e-catalog-tile .catalog-tile-pf-header .catalog-tile-pf-subtitle { - color: var(--pf-t--global--text--color--subtle); - font-size: var(--pf-t--global--font--size--body--sm); - font-family: var(--pf-t--global--font--family--body); - font-weight: var(--pf-t--global--font--weight--body); - padding-block-start: var(--pf-t--global--spacer--xs); -} -.ws-react-e-catalog-tile .catalog-tile-pf-header .catalog-tile-pf-subtitle a { - color: var(--pf-t--global--text--color--link--default); - text-decoration: none; -} -.ws-react-e-catalog-tile .catalog-tile-pf-header .catalog-tile-pf-subtitle a:hover { - color: var(--pf-t--global--text--color--link--hover); - text-decoration: underline; -} -.ws-react-e-catalog-tile .catalog-tile-pf-icon { - font-size: 37px; - height: 37px; - max-width: 60px; - min-width: 37px; -} -.ws-react-e-catalog-tile .catalog-tile-pf-badge-container { - display: flex; - flex: 1; - justify-content: flex-end; - margin-inline-start: var(--pf-t--global--spacer--sm); -} -.ws-react-e-catalog-tile .catalog-tile-pf-badge-container .catalog-tile-pf-badge { - font-size: var(--pf-t--global--font--size--body--default); - margin-inline-start: var(--pf-t--global--spacer--xs); -} -.ws-react-e-catalog-tile .catalog-tile-pf-body .catalog-tile-pf-description span { - display: -webkit-box; - overflow: hidden; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; -} -.ws-react-e-catalog-tile .catalog-tile-pf-body .catalog-tile-pf-description span.has-footer { - -webkit-line-clamp: 1; -} -.ws-react-e-catalog-tile .example-ok-icon { - color: var(--pf-t--global--icon--color--status--success--default); -} diff --git a/packages/module/patternfly-docs/content/examples/filterSidePanel.css b/packages/module/patternfly-docs/content/examples/filterSidePanel.css deleted file mode 100644 index f256934..0000000 --- a/packages/module/patternfly-docs/content/examples/filterSidePanel.css +++ /dev/null @@ -1,46 +0,0 @@ -.filter-panel-pf { - margin-block-start: 0; - margin-block-end: var(--pf-t--global--spacer--xl); - margin-inline: 0; - padding-inline: var(--pf-t--global--spacer--md); - display: flex; - flex-direction: column; - gap: var(--pf-t--global--spacer--lg); -} - -.filter-panel-pf-category-title { - border: 0; - font-size: var(--pf-t--global--font--size--body--sm); - margin-block-start: 0; - margin-block-end: var(--pf-t--global--spacer--sm); - margin-inline: 0; - font-weight: var(--pf-t--global--font--weight--body--bold); -} - -.filter-panel-pf-category-items { - margin-block: 0; -} - -.filter-panel-pf-category-items .pf-v6-c-button.pf-m-link { - padding: 0; -} - -.filter-panel-pf-category-items > * + * { - margin-block-start: var(--pf-t--global--spacer--sm); -} - -.filter-panel-pf-category-item { - font-weight: var(--pf-t--global--font--weight--body); -} - -.filter-panel-pf-category-item:first-of-type { - margin-block-start: 0; -} - -.filter-panel-pf-category-item .item-icon { - padding-inline-end: var(--pf-t--global--spacer--xs); -} - -.filter-panel-pf-category-item .item-count { - padding-inline-start: var(--pf-t--global--spacer--xs); -} diff --git a/packages/module/patternfly-docs/content/examples/propertiesSidePanel.css b/packages/module/patternfly-docs/content/examples/propertiesSidePanel.css deleted file mode 100644 index 1cf5375..0000000 --- a/packages/module/patternfly-docs/content/examples/propertiesSidePanel.css +++ /dev/null @@ -1,18 +0,0 @@ -.properties-side-panel-pf { - width: 165px; - display: flex; - flex-direction: column; - gap: var(--pf-t--global--spacer--lg); -} - -.properties-side-panel-pf-property-label { - font-weight: var(--pf-t--global--font--weight--body--bold) !important; - font-size: var(--pf-t--global--font--size--body--sm) !important; - margin: 0 !important; -} - -.properties-side-panel-pf-property-value { - font-size: var(--pf-t--global--font--size--body--regular) !important; - margin-top: var(--pf-t--global--spacer--sm); - word-break: break-word; -} diff --git a/packages/module/patternfly-docs/content/examples/verticalTab.css b/packages/module/patternfly-docs/content/examples/verticalTab.css deleted file mode 100644 index 6d04ae2..0000000 --- a/packages/module/patternfly-docs/content/examples/verticalTab.css +++ /dev/null @@ -1,77 +0,0 @@ -.ws-react-e-vertical-tabs .vertical-tabs-pf { - list-style: none; - margin-block-start: 0; - margin-block-end: 30px; - margin-inline-start: 0; - margin-inline-end: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: var(--pf-t--global--spacer--xs); -} -.ws-react-e-vertical-tabs .vertical-tabs-pf .vertical-tabs-pf { - margin-block-end: 0; - padding-block-start: var(--pf-t--global--spacer--xs); -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab { - position: relative; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a { - color: var(--pf-t--global--text--color--regular); - text-decoration: none; - display: inline-block; - font-size: var(--pf-t--global--font--size--body--default); - padding-block: var(--pf-t--global--spacer--xs); - padding-inline: var(--pf-t--global--spacer--sm); - vertical-align: top; - width: 100%; - word-break: break-word; - background-color: var(--pf-t--global--background--color--action--plain--default); - border-radius: var(--pf-t--global--border--radius--small); - margin-inline-start: var(--pf-t--global--spacer--md); -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a:hover, .ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a:focus { - background-color: var(--pf-t--global--background--color--action--plain--hover); - text-decoration: none; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a.no-wrap { - overflow-x: hidden; - white-space: nowrap; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a.truncate { - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > a.disabled { - color: var(--pf-t--global--text--color--on-disabled); - background: var(--pf-t--global--background--color--disabled--default); - pointer-events: none; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab.active::before { - background: var(--pf-t--global--border--color--clicked); - content: " "; - inset-inline-start: 0; - inset-block-start: var(--pf-t--global--spacer--xs); - position: absolute; - width: var(--pf-t--global--border--width--extra-strong); -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab:first-of-type { - margin-block-start: 0; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf-tab > .vertical-tabs-pf > .vertical-tabs-pf-tab { - margin-inline-start: var(--pf-t--global--spacer--md); -} -.ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab { - display: none; - /* Show any active tab, tab that has an active descendant, or is force shown */ -} -.ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active, .ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active-descendant, .ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.shown { - display: block; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs.active-tab > .vertical-tabs-pf-tab { - display: block; -} -.ws-react-e-vertical-tabs .vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active > .vertical-tabs-pf > .vertical-tabs-pf-tab { - display: block; -} diff --git a/packages/module/patternfly-docs/patternfly-docs.css.js b/packages/module/patternfly-docs/patternfly-docs.css.js index 240ac3b..5e778f8 100644 --- a/packages/module/patternfly-docs/patternfly-docs.css.js +++ b/packages/module/patternfly-docs/patternfly-docs.css.js @@ -6,3 +6,4 @@ import '@patternfly/patternfly/patternfly-addons.css'; import '@patternfly/documentation-framework/global.css'; // Add your extension CSS below +import '../src/components/react-catalog-view-extension.css'; diff --git a/packages/module/sass/react-catalog-view-extension/_catalog-item.scss b/packages/module/sass/react-catalog-view-extension/_catalog-item.scss index 5e6e1ca..84051ca 100644 --- a/packages/module/sass/react-catalog-view-extension/_catalog-item.scss +++ b/packages/module/sass/react-catalog-view-extension/_catalog-item.scss @@ -10,10 +10,10 @@ } .catalog-item-header-pf-text { - margin-left: 20px; + margin-inline-start: var(--pf-t--global--spacer--md); } .catalog-item-header-pf-subtitle { color: var(--pf-t--global--text--color--subtle); - margin-bottom: 0; + margin-block-end: 0; } diff --git a/packages/module/sass/react-catalog-view-extension/_catalog-tile.scss b/packages/module/sass/react-catalog-view-extension/_catalog-tile.scss index 2fdc287..df86464 100644 --- a/packages/module/sass/react-catalog-view-extension/_catalog-tile.scss +++ b/packages/module/sass/react-catalog-view-extension/_catalog-tile.scss @@ -1,7 +1,4 @@ .catalog-tile-pf { - &.featured { - border-top: 2px solid var(--pf-t--global--border--color--brand--default); - } &:active, &:hover, @@ -16,24 +13,17 @@ } .pf-v6-c-card__actions { - padding-left: 5px; + padding-inline-start: var(--pf-t--global--spacer--sm); } } .catalog-tile-pf-header { - font-size: 16px; - font-weight: 400; - padding-bottom: 16px; - - .catalog-tile-pf-title { - font-size: 15px; - font-weight: 400; - } .catalog-tile-pf-subtitle { color: var(--pf-t--global--text--color--subtle); - font-size: 13px; + font-size: var(--pf-t--global--FontSize--sm); font-weight: initial; + padding-block-start: var(--pf-t--global--spacer--sm); a, a:hover { @@ -61,7 +51,7 @@ } .catalog-tile-pf-description { - margin-top: 0; + margin-block-start: 0; span { display: -webkit-box; diff --git a/packages/module/sass/react-catalog-view-extension/_filter-side-panel.scss b/packages/module/sass/react-catalog-view-extension/_filter-side-panel.scss index 94676eb..0d08a59 100644 --- a/packages/module/sass/react-catalog-view-extension/_filter-side-panel.scss +++ b/packages/module/sass/react-catalog-view-extension/_filter-side-panel.scss @@ -1,44 +1,52 @@ .filter-panel-pf { - margin: 0 0 30px; - padding: 0 15px; + margin: 0 0 var(--pf-t--global--spacer--lg); + padding: 0 var(--pf-t--global--spacer--md); } .filter-panel-pf-category { - margin-top: 20px; + margin-block-start: var(--pf-t--global--spacer--md); &:first-of-type { - margin-top: 0; + margin-block-start: 0; } } .filter-panel-pf-category-title { border: 0; font-size: inherit; - margin: 0 0 8px 0; + margin: 0 0 var(--pf-t--global--spacer--sm) 0; font-weight: 700; } .filter-panel-pf-category-items { - margin-top: 0; - margin-bottom: 0; + margin-block-start: 0; + margin-block-end: 0; } .filter-panel-pf-category-item { - margin-top: 5px; + margin-block-start: var(--pf-t--global--spacer--sm); + border-radius: var(--pf-t--global--BorderRadius--sm); &:first-of-type { - margin-top: 0; + margin-block-start: 0; + } + + &:hover { + background-color: var(--pf-t--global--background--color--action--plain--hover); } .pf-v6-c-check__label { - min-height: 23px; + min-height: var(--pf-t--global--spacer--md); + padding: var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--xs); + margin: calc(var(--pf-t--global--spacer--sm) * -1) calc(var(--pf-t--global--spacer--xs) * -1); + border-radius: var(--pf-t--global--border--radius--small); } .item-icon { - padding-right: 3px; + padding-inline-end: var(--pf-t--global--spacer--xs); } .item-count { - padding-left: 3px; + padding-inline-start: var(--pf-t--global--spacer--xs); } } diff --git a/packages/module/sass/react-catalog-view-extension/_properties-side-panel.scss b/packages/module/sass/react-catalog-view-extension/_properties-side-panel.scss index 707b47a..5104b0f 100644 --- a/packages/module/sass/react-catalog-view-extension/_properties-side-panel.scss +++ b/packages/module/sass/react-catalog-view-extension/_properties-side-panel.scss @@ -3,21 +3,19 @@ } .properties-side-panel-pf-property { - margin-top: 24px; + margin-block-start: var(--pf-t--global--spacer--lg); &:first-of-type { - margin-top: 0; + margin-block-start: 0; } } .properties-side-panel-pf-property-label { font-weight: 700 !important; - font-size: 14px !important; margin: 0 !important; } .properties-side-panel-pf-property-value { - font-size: 14px !important; - margin-top: 8px; + margin-block-start: var(--pf-t--global--spacer--sm); word-break: break-word; } diff --git a/packages/module/sass/react-catalog-view-extension/_vertical-tabs.scss b/packages/module/sass/react-catalog-view-extension/_vertical-tabs.scss index b80aca2..24a3068 100644 --- a/packages/module/sass/react-catalog-view-extension/_vertical-tabs.scss +++ b/packages/module/sass/react-catalog-view-extension/_vertical-tabs.scss @@ -1,15 +1,15 @@ .vertical-tabs-pf { list-style: none; - margin: 0 0 30px; + margin: 0 0 var(--pf-t--global--spacer--lg); padding: 0; .vertical-tabs-pf { - margin-bottom: 0; + margin-block-end: 0; } } .vertical-tabs-pf-tab { - margin-top: 4px; + margin-block-start: var(--pf-t--global--spacer--sm); position: relative; > a { @@ -17,10 +17,11 @@ text-decoration: none; display: inline-block; font-size: 13px; - padding: 3px 6px 3px 15px; + padding: var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--md); vertical-align: top; width: 100%; word-break: break-word; + border-radius: var(--pf-t--global--border--radius--small); &:hover, &:focus { @@ -42,26 +43,25 @@ &.active { > a { - color: var(--pf-t--global--text--color--brand--clicked); &::before { background: var( --pf-t--global--border--color--brand--clicked); content: "\00a0"; //   left: 0; position: absolute; - width: 3px; + width: var(--pf-t--global--border--width--extra-strong); } } } &:first-of-type { - margin-top: 0; + margin-block-start: 0; } > .vertical-tabs-pf { > .vertical-tabs-pf-tab { position: initial; - padding-left: 15px; + padding-inline-start: var(--pf-t--global--spacer--md); } } } diff --git a/packages/module/src/components/CatalogTile/CatalogTile.tsx b/packages/module/src/components/CatalogTile/CatalogTile.tsx index 2eaf578..7329b1c 100644 --- a/packages/module/src/components/CatalogTile/CatalogTile.tsx +++ b/packages/module/src/components/CatalogTile/CatalogTile.tsx @@ -3,13 +3,15 @@ import { Card, CardHeader, CardTitle, CardBody, CardFooter, CardProps } from '@p import { css } from '@patternfly/react-styles'; import { getUniqueId } from '@patternfly/react-core'; -export interface CatalogTileProps extends Omit { +export interface CatalogTileProps extends Omit { /** Id */ id?: any; /** Additional css classes */ className?: string; /** Flag if the tile is 'featured' */ featured: boolean; + /** Flag if the tile is selected */ + isSelected?: boolean; /** Callback for a click on the tile */ onClick?: (event: React.FormEvent | React.MouseEvent) => void; /** href for the tile if used as a link */ @@ -47,6 +49,7 @@ export class CatalogTile extends React.Component { id: null as string, className: '', featured: false, + isSelected: false, onClick: null as (event: React.SyntheticEvent) => void, href: null as string, iconImg: null as string, @@ -102,6 +105,7 @@ export class CatalogTile extends React.Component { id, className, featured, + isSelected, onClick, href, icon, @@ -122,7 +126,7 @@ export class CatalogTile extends React.Component { return ( a{color:var(--pf-t--global--text--color--regular);text-decoration:none;display:inline-block;font-size:13px;padding:var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--md);vertical-align:top;width:100%;word-break:break-word;border-radius:var(--pf-t--global--border--radius--small)}.vertical-tabs-pf-tab>a:hover,.vertical-tabs-pf-tab>a:focus{background-color:var(--pf-t--global--background--color--action--plain--hover);text-decoration:none}.vertical-tabs-pf-tab>a.no-wrap{overflow-x:hidden;white-space:nowrap}.vertical-tabs-pf-tab>a.truncate{overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.vertical-tabs-pf-tab.active>a::before{background:var(--pf-t--global--border--color--brand--clicked);content:" ";left:0;position:absolute;width:var(--pf-t--global--border--width--extra-strong)}.vertical-tabs-pf-tab:first-of-type{margin-block-start:0}.vertical-tabs-pf-tab>.vertical-tabs-pf>.vertical-tabs-pf-tab{position:initial;padding-inline-start:var(--pf-t--global--spacer--md)}.vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab{display:none}.vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active,.vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active-descendant,.vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.shown{display:block}.vertical-tabs-pf.restrict-tabs.active-tab>.vertical-tabs-pf-tab{display:block}.vertical-tabs-pf.restrict-tabs .vertical-tabs-pf-tab.active>.vertical-tabs-pf>.vertical-tabs-pf-tab{display:block}.properties-side-panel-pf{width:165px}.properties-side-panel-pf-property{margin-block-start:var(--pf-t--global--spacer--lg)}.properties-side-panel-pf-property:first-of-type{margin-block-start:0}.properties-side-panel-pf-property-label{font-weight:700 !important;margin:0 !important}.properties-side-panel-pf-property-value{margin-block-start:var(--pf-t--global--spacer--sm);word-break:break-word}.filter-panel-pf{margin:0 0 var(--pf-t--global--spacer--lg);padding:0 var(--pf-t--global--spacer--md)}.filter-panel-pf-category{margin-block-start:var(--pf-t--global--spacer--md)}.filter-panel-pf-category:first-of-type{margin-block-start:0}.filter-panel-pf-category-title{border:0;font-size:inherit;margin:0 0 var(--pf-t--global--spacer--sm) 0;font-weight:700}.filter-panel-pf-category-items{margin-block-start:0;margin-block-end:0}.filter-panel-pf-category-item{margin-block-start:var(--pf-t--global--spacer--sm);border-radius:var(--pf-t--global--BorderRadius--sm)}.filter-panel-pf-category-item:first-of-type{margin-block-start:0}.filter-panel-pf-category-item:hover{background-color:var(--pf-t--global--background--color--action--plain--hover)}.filter-panel-pf-category-item .pf-v6-c-check__label{min-height:var(--pf-t--global--spacer--md);padding:var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--xs);margin:calc(var(--pf-t--global--spacer--sm)*-1) calc(var(--pf-t--global--spacer--xs)*-1);border-radius:var(--pf-t--global--border--radius--small)}.filter-panel-pf-category-item .item-icon{padding-inline-end:var(--pf-t--global--spacer--xs)}.filter-panel-pf-category-item .item-count{padding-inline-start:var(--pf-t--global--spacer--xs)} \ No newline at end of file