From 83558cb22d8cd6d7b08e55511f689cf4dfc2c25f Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:35:21 +0000 Subject: [PATCH 1/4] initial commit --- .../src/Pagination/Pagination.module.css | 174 +++++++++++ packages/react/src/Pagination/Pagination.tsx | 274 ++++++++++-------- 2 files changed, 323 insertions(+), 125 deletions(-) create mode 100644 packages/react/src/Pagination/Pagination.module.css diff --git a/packages/react/src/Pagination/Pagination.module.css b/packages/react/src/Pagination/Pagination.module.css new file mode 100644 index 00000000000..359e884fcb2 --- /dev/null +++ b/packages/react/src/Pagination/Pagination.module.css @@ -0,0 +1,174 @@ +.Page { + display: inline-block; + min-width: 32px; /* primer.control.medium.size */ + height: 32px; /* primer.control.medium.size */ + padding: var(--base-size-8) calc((var(--base-size-32) - var(--base-size-20)) / 2); /* primer.control.medium.paddingInline.condensed primer.control.medium.paddingBlock */ + margin-right: var(--base-size-4); + font-style: normal; + /* stylelint-disable-next-line primer/typography */ + line-height: 1; + color: var(----fgColor-default); + text-align: center; + text-decoration: none; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + user-select: none; + background-color: transparent; + border-radius: var(--borderRadius-medium); + transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1); +} + +@supports (clip-path: polygon(50% 0, 100% 50%, 50% 100%)) { + .Page[rel='prev']::before, + .Page[rel='next']::after { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: text-bottom; + content: ''; + background-color: currentColor; + } + + /* chevron-left */ + .Page[rel='prev']::before { + margin-right: var(--base-size-4); + clip-path: polygon( + 9.8px 12.8px, + 8.7px 12.8px, + 4.5px 8.5px, + 4.5px 7.5px, + 8.7px 3.2px, + 9.8px 4.3px, + 6.1px 8px, + 9.8px 11.7px, + 9.8px 12.8px + ); + } + + /* chevron-right */ + .Page[rel='next']::after { + margin-left: var(--base-size-4); + clip-path: polygon( + 6.2px 3.2px, + 7.3px 3.2px, + 11.5px 7.5px, + 11.5px 8.5px, + 7.3px 12.8px, + 6.2px 11.7px, + 9.9px 8px, + 6.2px 4.3px, + 6.2px 3.2px + ); + } +} + +.Page:last-child { + margin-right: 0; +} + +.Page:hover, +.Page:focus { + text-decoration: none; + background-color: var(--control-transparent-bgColor-hover); + outline: 0; + transition-duration: 0.1s; +} + +.Page:focus-visible { + outline: 2px solid var(--bgColor-accent-emphasis); + outline-offset: -2px; +} + +.Page:active { + border-color: var(--borderColor-muted); +} + +.Page[rel='prev'], +.Page[rel='next'] { + color: var(--fgColor-accent); +} + +.Page[aria-current], +.Page[aria-current]:hover { + color: var(--fgColor-onEmphasis); + background-color: var(--bgColor-accent-emphasis); + border-color: transparent; +} + +.Page[aria-current]:focus-visible { + outline: 2px solid var(--bgColor-accent-emphasis); + outline-offset: -2px; + /* stylelint-disable-next-line primer/box-shadow */ + box-shadow: inset 0 0 0 3px var(--fgColor-onEmphasis); +} + +.Page[aria-disabled], +.Page[aria-disabled]:hover { + /* stylelint-disable-next-line primer/spacing */ + margin: 0 2px; +} + +.Page:first-child { + /* stylelint-disable-next-line primer/spacing */ + margin-right: 6px; +} + +.Page[aria-disabled], +.Page[aria-disabled]:hover, +.Page[role='presentation'], +.Page[role='presentation']:hover { + color: var(--fgColor-disabled); + cursor: default; + background-color: transparent; +} + +.PaginationContainer { + /* stylelint-disable-next-line primer/spacing */ + margin-top: 20px; + /* stylelint-disable-next-line primer/spacing */ + margin-bottom: 15px; + text-align: center; +} + +@media ((max-width: calc(768px - 0.02px))) { + .TablePaginationSteps[data-hidden-viewport-ranges*='narrow'] > *:not(:first-child):not(:last-child) { + display: none; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='narrow'] > *:first-child { + margin-inline-end: 0; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='narrow'] > *:last-child { + margin-inline-start: 0; + } +} + +@media ((min-width: 768px)) { + .TablePaginationSteps[data-hidden-viewport-ranges*='regular'] > *:not(:first-child):not(:last-child) { + display: none; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='regular'] > *:first-child { + margin-inline-end: 0; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='regular'] > *:last-child { + margin-inline-start: 0; + } +} + +@media ((min-width: 1400px)) { + .TablePaginationSteps[data-hidden-viewport-ranges*='wide'] > *:not(:first-child):not(:last-child) { + display: none; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='wide'] > *:first-child { + margin-inline-end: 0; + } + + .TablePaginationSteps[data-hidden-viewport-ranges*='wide'] > *:last-child { + margin-inline-start: 0; + } +} diff --git a/packages/react/src/Pagination/Pagination.tsx b/packages/react/src/Pagination/Pagination.tsx index db3671c8dd8..f28d9f05310 100644 --- a/packages/react/src/Pagination/Pagination.tsx +++ b/packages/react/src/Pagination/Pagination.tsx @@ -7,6 +7,12 @@ import sx from '../sx' import {buildComponentData, buildPaginationModel} from './model' import type {ResponsiveValue} from '../hooks/useResponsiveValue' import {viewportRanges} from '../hooks/useResponsiveValue' +import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' +import {useFeatureFlag} from '../FeatureFlags' +import {clsx} from 'clsx' +import classes from './Pagination.module.css' + +const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team' const getViewportRangesToHidePages = (showPages: PaginationProps['showPages']) => { if (showPages && typeof showPages !== 'boolean') { @@ -22,128 +28,132 @@ const getViewportRangesToHidePages = (showPages: PaginationProps['showPages']) = } } -const Page = styled.a` - display: inline-block; - min-width: 32px; /* primer.control.medium.size */ - height: 32px; /* primer.control.medium.size */ - padding: 0.5rem calc((2rem - 1.25rem) / 2); /* primer.control.medium.paddingInline.condensed primer.control.medium.paddingBlock */ - font-style: normal; - line-height: 1; - color: ${get('colors.fg.default')}; - text-align: center; - white-space: nowrap; - vertical-align: middle; - cursor: pointer; - user-select: none; - text-decoration: none; - - margin-right: ${get('space.1')}; - - &:last-child { - margin-right: 0; - } - - background-color: transparent; - border-radius: ${get('radii.2')}; - transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1); - - &:hover, - &:focus { +const Page = toggleStyledComponent( + CSS_MODULES_FEATURE_FLAG, + 'a', + styled.a` + display: inline-block; + min-width: 32px; /* primer.control.medium.size */ + height: 32px; /* primer.control.medium.size */ + padding: 0.5rem calc((2rem - 1.25rem) / 2); /* primer.control.medium.paddingInline.condensed primer.control.medium.paddingBlock */ + font-style: normal; + line-height: 1; + color: ${get('colors.fg.default')}; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + user-select: none; text-decoration: none; - background-color: ${get('colors.actionListItem.default.hoverBg')}; - outline: 0; - transition-duration: 0.1s; - } - &:focus-visible { - outline: 2px solid ${get('colors.accent.emphasis')}; - outline-offset: -2px; - } + margin-right: ${get('space.1')}; - &:active { - border-color: ${get('colors.border.muted')}; - } + &:last-child { + margin-right: 0; + } - &[rel='prev'], - &[rel='next'] { - color: ${get('colors.accent.fg')}; - } + background-color: transparent; + border-radius: ${get('radii.2')}; + transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1); + + &:hover, + &:focus { + text-decoration: none; + background-color: ${get('colors.actionListItem.default.hoverBg')}; + outline: 0; + transition-duration: 0.1s; + } - &[aria-current], - &[aria-current]:hover { - color: ${get('colors.fg.onEmphasis')}; - background-color: ${get('colors.accent.emphasis')}; - border-color: transparent; - } + &:focus-visible { + outline: 2px solid ${get('colors.accent.emphasis')}; + outline-offset: -2px; + } - &[aria-current]:focus-visible { - outline: 2px solid ${get('colors.accent.emphasis')}; - outline-offset: -2px; - box-shadow: inset 0 0 0 3px ${get('colors.fg.onEmphasis')}; - } + &:active { + border-color: ${get('colors.border.muted')}; + } - &[aria-disabled], - &[aria-disabled]:hover { - margin: 0 2px; + &[rel='prev'], + &[rel='next'] { + color: ${get('colors.accent.fg')}; + } - &:first-child { - margin-right: 6px; + &[aria-current], + &[aria-current]:hover { + color: ${get('colors.fg.onEmphasis')}; + background-color: ${get('colors.accent.emphasis')}; + border-color: transparent; } - } - &[aria-disabled], - &[aria-disabled]:hover, - &[role='presentation'], - &[role='presentation']:hover { - color: ${get('colors.primer.fg.disabled')}; // check - cursor: default; - background-color: transparent; - } + &[aria-current]:focus-visible { + outline: 2px solid ${get('colors.accent.emphasis')}; + outline-offset: -2px; + box-shadow: inset 0 0 0 3px ${get('colors.fg.onEmphasis')}; + } + + &[aria-disabled], + &[aria-disabled]:hover { + margin: 0 2px; - @supports (clip-path: polygon(50% 0, 100% 50%, 50% 100%)) { - &[rel='prev']::before, - &[rel='next']::after { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: text-bottom; - content: ''; - background-color: currentColor; + &:first-child { + margin-right: 6px; + } } - // chevron-left - &[rel='prev']::before { - margin-right: ${get('space.1')}; - clip-path: polygon( - 9.8px 12.8px, - 8.7px 12.8px, - 4.5px 8.5px, - 4.5px 7.5px, - 8.7px 3.2px, - 9.8px 4.3px, - 6.1px 8px, - 9.8px 11.7px, - 9.8px 12.8px - ); + &[aria-disabled], + &[aria-disabled]:hover, + &[role='presentation'], + &[role='presentation']:hover { + color: ${get('colors.primer.fg.disabled')}; // check + cursor: default; + background-color: transparent; } - // chevron-right - &[rel='next']::after { - margin-left: ${get('space.1')}; - clip-path: polygon( - 6.2px 3.2px, - 7.3px 3.2px, - 11.5px 7.5px, - 11.5px 8.5px, - 7.3px 12.8px, - 6.2px 11.7px, - 9.9px 8px, - 6.2px 4.3px, - 6.2px 3.2px - ); + @supports (clip-path: polygon(50% 0, 100% 50%, 50% 100%)) { + &[rel='prev']::before, + &[rel='next']::after { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: text-bottom; + content: ''; + background-color: currentColor; + } + + // chevron-left + &[rel='prev']::before { + margin-right: ${get('space.1')}; + clip-path: polygon( + 9.8px 12.8px, + 8.7px 12.8px, + 4.5px 8.5px, + 4.5px 7.5px, + 8.7px 3.2px, + 9.8px 4.3px, + 6.1px 8px, + 9.8px 11.7px, + 9.8px 12.8px + ); + } + + // chevron-right + &[rel='next']::after { + margin-left: ${get('space.1')}; + clip-path: polygon( + 6.2px 3.2px, + 7.3px 3.2px, + 11.5px 7.5px, + 11.5px 8.5px, + 7.3px 12.8px, + 6.2px 11.7px, + 9.9px 8px, + 6.2px 4.3px, + 6.2px 3.2px + ); + } } - } -` + `, +) type UsePaginationPagesParameters = { theme?: Record // set to theme type once /src/theme.js is converted @@ -168,6 +178,8 @@ function usePaginationPages({ }: UsePaginationPagesParameters) { const pageChange = React.useCallback((n: number) => (e: React.MouseEvent) => onPageChange(e, n), [onPageChange]) + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + const model = React.useMemo(() => { return buildPaginationModel(pageCount, currentPage, !!showPages, marginPageCount, surroundingPageCount) }, [pageCount, currentPage, showPages, marginPageCount, surroundingPageCount]) @@ -176,26 +188,29 @@ function usePaginationPages({ return model.map(page => { const {props, key, content} = buildComponentData(page, hrefBuilder, pageChange(page.num)) return ( - + {content} ) }) - }, [model, hrefBuilder, pageChange, theme]) + }, [model, hrefBuilder, pageChange, theme, enabled]) return children } -const PaginationContainer = styled.nav` - margin-top: 20px; - margin-bottom: 15px; - text-align: center; - - ${ - // Hides pages based on the viewport range passed to `showPages` - Object.keys(viewportRanges) - .map(viewportRangeKey => { - return ` +const PaginationContainer = toggleStyledComponent( + CSS_MODULES_FEATURE_FLAG, + 'nav', + styled.nav` + margin-top: 20px; + margin-bottom: 15px; + text-align: center; + + ${ + // Hides pages based on the viewport range passed to `showPages` + Object.keys(viewportRanges) + .map(viewportRangeKey => { + return ` @media (${viewportRanges[viewportRangeKey as keyof typeof viewportRanges]}) { .TablePaginationSteps[data-hidden-viewport-ranges*='${viewportRangeKey}'] > *:not(:first-child):not(:last-child) { display: none; @@ -210,12 +225,13 @@ const PaginationContainer = styled.nav` } } ` - }) - .join('') - } + }) + .join('') + } - ${sx}; -` + ${sx}; + `, +) export type PaginationProps = { theme?: Record @@ -249,12 +265,20 @@ function Pagination({ showPages, surroundingPageCount, }) + + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + return ( - + {pageElements} From 58cfcd1424fc9fd198723b61ce880b4e7941bed7 Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:53:07 +0000 Subject: [PATCH 2/4] fix query selector --- packages/react/src/Pagination/Pagination.module.css | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/react/src/Pagination/Pagination.module.css b/packages/react/src/Pagination/Pagination.module.css index 359e884fcb2..db8dd56bb8c 100644 --- a/packages/react/src/Pagination/Pagination.module.css +++ b/packages/react/src/Pagination/Pagination.module.css @@ -103,17 +103,18 @@ box-shadow: inset 0 0 0 3px var(--fgColor-onEmphasis); } -.Page[aria-disabled], -.Page[aria-disabled]:hover { +.Page[aria-disabled]:first-child, +.Page[aria-disabled]:hover:first-child { /* stylelint-disable-next-line primer/spacing */ margin: 0 2px; -} - -.Page:first-child { /* stylelint-disable-next-line primer/spacing */ margin-right: 6px; } +/* .Page:first-child { + +} */ + .Page[aria-disabled], .Page[aria-disabled]:hover, .Page[role='presentation'], From d3930d98042240001d2552350aad46c4a3a630ec Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:54:41 +0000 Subject: [PATCH 3/4] add changeset --- .changeset/quick-chairs-rule.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quick-chairs-rule.md diff --git a/.changeset/quick-chairs-rule.md b/.changeset/quick-chairs-rule.md new file mode 100644 index 00000000000..45e191d552b --- /dev/null +++ b/.changeset/quick-chairs-rule.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Convert `Pagination` component to use CSS modules From 1343ed839e53db9b35120f11ef57ab6865691494 Mon Sep 17 00:00:00 2001 From: Randall Krauskopf <104226843+randall-krauskopf@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:01:00 -0600 Subject: [PATCH 4/4] remove code comments --- packages/react/src/Pagination/Pagination.module.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react/src/Pagination/Pagination.module.css b/packages/react/src/Pagination/Pagination.module.css index db8dd56bb8c..a0dd8de6eed 100644 --- a/packages/react/src/Pagination/Pagination.module.css +++ b/packages/react/src/Pagination/Pagination.module.css @@ -111,10 +111,6 @@ margin-right: 6px; } -/* .Page:first-child { - -} */ - .Page[aria-disabled], .Page[aria-disabled]:hover, .Page[role='presentation'],