@automattic/vip-design-system 2.18.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview-head.html +1 -0
- package/build/system/Badge/Badge.js +2 -1
- package/build/system/DescriptionList/DescriptionList.js +0 -1
- package/build/system/Form/Label.d.ts +1 -3
- package/build/system/Form/Label.js +1 -3
- package/build/system/Form/RadioBoxGroup.jsx +12 -1
- package/build/system/Form/RadioBoxGroup.stories.jsx +6 -1
- package/build/system/Heading/Heading.js +2 -3
- package/build/system/Heading/Heading.stories.js +15 -2
- package/build/system/Nav/styles/variants/menu.js +1 -2
- package/build/system/Notice/Notice.js +1 -1
- package/build/system/Pagination/Pagination.d.ts +10 -20
- package/build/system/Pagination/Pagination.js +50 -68
- package/build/system/Pagination/Pagination.stories.js +13 -11
- package/build/system/Pagination/Pagination.test.js +4 -4
- package/build/system/Pagination/PaginationLayout.d.ts +27 -0
- package/build/system/Pagination/PaginationLayout.js +63 -0
- package/build/system/Pagination/SimplePagination.d.ts +26 -0
- package/build/system/Pagination/SimplePagination.js +76 -0
- package/build/system/Pagination/SimplePagination.stories.d.ts +13 -0
- package/build/system/Pagination/SimplePagination.stories.js +130 -0
- package/build/system/Pagination/SimplePagination.test.d.ts +2 -0
- package/build/system/Pagination/SimplePagination.test.js +171 -0
- package/build/system/Pagination/index.d.ts +3 -1
- package/build/system/Pagination/index.js +2 -1
- package/build/system/Pagination/styles.js +1 -4
- package/build/system/Table/TableCell.js +1 -1
- package/build/system/Text/Text.js +0 -1
- package/build/system/Text/Text.stories.js +16 -13
- package/build/system/Toolbar/Logo.js +22 -6
- package/build/system/Wizard/Wizard.stories.js +11 -11
- package/build/system/Wizard/WizardStep.js +0 -2
- package/build/system/index.d.ts +2 -2
- package/build/system/index.js +2 -2
- package/build/system/theme/generated/valet-theme-dark.json +224 -227
- package/build/system/theme/generated/valet-theme-light.json +224 -227
- package/build/system/theme/getPropValue.js +3 -7
- package/build/system/theme/index.d.ts +20 -12
- package/build/system/theme/index.js +27 -20
- package/docs/SETUP.md +1 -1
- package/package.json +1 -1
- package/src/system/Badge/Badge.tsx +2 -1
- package/src/system/DescriptionList/DescriptionList.tsx +0 -1
- package/src/system/Form/Label.tsx +1 -3
- package/src/system/Form/RadioBoxGroup.jsx +12 -1
- package/src/system/Form/RadioBoxGroup.stories.jsx +6 -1
- package/src/system/Heading/Heading.stories.tsx +10 -1
- package/src/system/Heading/Heading.tsx +1 -2
- package/src/system/Nav/styles/variants/menu.ts +1 -2
- package/src/system/Notice/Notice.tsx +1 -1
- package/src/system/Pagination/Pagination.stories.tsx +13 -10
- package/src/system/Pagination/Pagination.test.tsx +4 -6
- package/src/system/Pagination/Pagination.tsx +36 -71
- package/src/system/Pagination/PaginationLayout.tsx +93 -0
- package/src/system/Pagination/SimplePagination.stories.tsx +127 -0
- package/src/system/Pagination/SimplePagination.test.tsx +120 -0
- package/src/system/Pagination/SimplePagination.tsx +97 -0
- package/src/system/Pagination/index.ts +3 -1
- package/src/system/Pagination/styles.ts +1 -4
- package/src/system/Table/TableCell.tsx +1 -1
- package/src/system/Text/Text.stories.tsx +7 -4
- package/src/system/Text/Text.tsx +0 -1
- package/src/system/Toolbar/Logo.tsx +19 -2
- package/src/system/Wizard/Wizard.stories.tsx +11 -11
- package/src/system/Wizard/WizardStep.tsx +0 -2
- package/src/system/index.ts +2 -1
- package/src/system/theme/generated/valet-theme-dark.json +224 -227
- package/src/system/theme/generated/valet-theme-light.json +224 -227
- package/src/system/theme/getPropValue.ts +1 -8
- package/src/system/theme/index.ts +33 -18
- package/tokens/valet-core/valet-core.json +39 -9
- package/tokens/valet-core/wpvip-product-core.json +88 -125
|
@@ -16,6 +16,7 @@ var _ThemeBuilder = ThemeBuilder(Valet),
|
|
|
16
16
|
ValetTheme = _ThemeBuilder.ValetTheme,
|
|
17
17
|
getHeadingStyles = _ThemeBuilder.getHeadingStyles;
|
|
18
18
|
var light = ColorBuilder(ValetTheme);
|
|
19
|
+
var supportLabelDefaultTypography = getPropValue('support', 'label-default');
|
|
19
20
|
|
|
20
21
|
// Dark
|
|
21
22
|
var _ThemeBuilder2 = ThemeBuilder(ValetDark),
|
|
@@ -29,12 +30,6 @@ var outline = {
|
|
|
29
30
|
outlineWidth: '1px',
|
|
30
31
|
boxShadow: "0 0 0 1px " + getPropValue('focus', 'inset') + ", 0 0 0 3px " + getPropValue('focus')
|
|
31
32
|
};
|
|
32
|
-
var fonts = {
|
|
33
|
-
body: '-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',
|
|
34
|
-
heading: 'inherit',
|
|
35
|
-
monospace: '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',
|
|
36
|
-
serif: 'recoletaregular, Georgia, serif'
|
|
37
|
-
};
|
|
38
33
|
var getComponentColors = function getComponentColors(theme, gColor, gVariants) {
|
|
39
34
|
return {
|
|
40
35
|
// Valet Theme Colors
|
|
@@ -175,13 +170,15 @@ var getComponentColors = function getComponentColors(theme, gColor, gVariants) {
|
|
|
175
170
|
export default {
|
|
176
171
|
outline: outline,
|
|
177
172
|
space: getVariants('space'),
|
|
178
|
-
fonts:
|
|
173
|
+
fonts: {
|
|
174
|
+
monospace: getPropValue('fontFamily', 'monospace'),
|
|
175
|
+
"default": getPropValue('fontFamily', 'default')
|
|
176
|
+
},
|
|
179
177
|
fontSizes: getVariants('fontSize.static'),
|
|
180
178
|
breakpoints: generateBreakpoints(getVariants('breakpoint')),
|
|
181
179
|
fontWeights: {
|
|
182
|
-
body: getPropValue('fontWeight', 'body'),
|
|
183
|
-
heading: getPropValue('fontWeight', 'heading'),
|
|
184
180
|
regular: getPropValue('fontWeight', 'regular'),
|
|
181
|
+
semibold: getPropValue('fontWeight', 'semibold'),
|
|
185
182
|
bold: getPropValue('fontWeight', 'bold'),
|
|
186
183
|
medium: getPropValue('fontWeight', 'medium'),
|
|
187
184
|
light: getPropValue('fontWeight', 'light')
|
|
@@ -276,9 +273,13 @@ export default {
|
|
|
276
273
|
}
|
|
277
274
|
}
|
|
278
275
|
},
|
|
276
|
+
forms: {
|
|
277
|
+
label: _extends({}, supportLabelDefaultTypography)
|
|
278
|
+
},
|
|
279
279
|
buttons: {
|
|
280
|
-
primary: {
|
|
281
|
-
|
|
280
|
+
primary: _extends({}, supportLabelDefaultTypography, {
|
|
281
|
+
// Button label weight: theme `medium` (500).
|
|
282
|
+
fontWeight: 'medium',
|
|
282
283
|
color: 'button.primary.label.default',
|
|
283
284
|
bg: 'button.primary.background.default',
|
|
284
285
|
border: '1px solid transparent',
|
|
@@ -287,7 +288,6 @@ export default {
|
|
|
287
288
|
minHeight: '38px',
|
|
288
289
|
display: 'inline-flex',
|
|
289
290
|
cursor: 'pointer',
|
|
290
|
-
fontWeight: 'medium',
|
|
291
291
|
boxShadow: 'none',
|
|
292
292
|
borderRadius: 1,
|
|
293
293
|
'&:hover': {
|
|
@@ -326,7 +326,7 @@ export default {
|
|
|
326
326
|
backgroundColor: 'button.secondary.background.disabled',
|
|
327
327
|
color: 'button.secondary.label.default'
|
|
328
328
|
}
|
|
329
|
-
},
|
|
329
|
+
}),
|
|
330
330
|
secondary: {
|
|
331
331
|
variant: 'buttons.primary',
|
|
332
332
|
color: 'button.secondary.label.default',
|
|
@@ -490,7 +490,14 @@ export default {
|
|
|
490
490
|
variant: 'buttons.tertiary'
|
|
491
491
|
}
|
|
492
492
|
},
|
|
493
|
-
text: getHeadingStyles(),
|
|
493
|
+
text: _extends({}, getHeadingStyles(), getVariants('body'), {
|
|
494
|
+
'support-helper-text': getPropValue('support', 'helper-text'),
|
|
495
|
+
'support-label-xs': getPropValue('support', 'label-xs'),
|
|
496
|
+
'support-label-small': getPropValue('support', 'label-small'),
|
|
497
|
+
'support-label-default': getPropValue('support', 'label-default'),
|
|
498
|
+
'support-label-default-quiet': getPropValue('support', 'label-default-quiet'),
|
|
499
|
+
'support-label-default-loud': getPropValue('support', 'label-default-loud')
|
|
500
|
+
}),
|
|
494
501
|
dialog: {
|
|
495
502
|
modal: {
|
|
496
503
|
position: 'fixed',
|
|
@@ -569,10 +576,10 @@ export default {
|
|
|
569
576
|
}
|
|
570
577
|
},
|
|
571
578
|
styles: {
|
|
572
|
-
root:
|
|
573
|
-
fontFamily: 'body',
|
|
574
|
-
lineHeight: 'body',
|
|
575
|
-
fontWeight: 'body',
|
|
579
|
+
root: {
|
|
580
|
+
fontFamily: getPropValue('body', 'default').fontFamily,
|
|
581
|
+
lineHeight: getPropValue('body', 'default').lineHeight,
|
|
582
|
+
fontWeight: getPropValue('body', 'default').fontWeight,
|
|
576
583
|
color: 'text',
|
|
577
584
|
backgroundColor: 'backgrounds.primary',
|
|
578
585
|
webkitFontSmoothing: 'antialiased',
|
|
@@ -589,11 +596,11 @@ export default {
|
|
|
589
596
|
display: 'block'
|
|
590
597
|
},
|
|
591
598
|
pre: {
|
|
592
|
-
fontFamily: '
|
|
599
|
+
fontFamily: 'default'
|
|
593
600
|
},
|
|
594
601
|
p: {
|
|
595
602
|
color: 'text'
|
|
596
603
|
}
|
|
597
|
-
}
|
|
604
|
+
}
|
|
598
605
|
}
|
|
599
606
|
};
|
package/docs/SETUP.md
CHANGED
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ export const Badge = forwardRef< HTMLDivElement, BadgeProps >(
|
|
|
27
27
|
as="span"
|
|
28
28
|
sx={ {
|
|
29
29
|
fontSize: 0,
|
|
30
|
+
letterSpacing: '0.01em',
|
|
30
31
|
padding: 0, // do we need padding declared twice here?
|
|
31
32
|
bg: `tag.${ variant }.background`,
|
|
32
33
|
color: `tag.${ variant }.text`,
|
|
@@ -35,7 +36,7 @@ export const Badge = forwardRef< HTMLDivElement, BadgeProps >(
|
|
|
35
36
|
px: 2,
|
|
36
37
|
display: 'inline-block',
|
|
37
38
|
borderRadius: 1,
|
|
38
|
-
fontWeight: '
|
|
39
|
+
fontWeight: 'medium',
|
|
39
40
|
a: {
|
|
40
41
|
color: `tag.${ variant }.text`,
|
|
41
42
|
'&:hover, &:focus, &:active': {
|
|
@@ -13,9 +13,7 @@ import { RequiredLabel } from './RequiredLabel';
|
|
|
13
13
|
|
|
14
14
|
export const baseLabelColor = 'input.label.default';
|
|
15
15
|
export const baseLabelStyle = {
|
|
16
|
-
|
|
17
|
-
fontSize: 2,
|
|
18
|
-
lineHeight: 1.5,
|
|
16
|
+
variant: 'forms.label',
|
|
19
17
|
color: baseLabelColor,
|
|
20
18
|
};
|
|
21
19
|
|
|
@@ -72,7 +72,14 @@ const RadioOption = ( {
|
|
|
72
72
|
{ ...restOption }
|
|
73
73
|
/>
|
|
74
74
|
<div
|
|
75
|
-
sx={ {
|
|
75
|
+
sx={ {
|
|
76
|
+
mb: 0,
|
|
77
|
+
color: 'input.radio-box.label.primary.default',
|
|
78
|
+
fontWeight: 'semibold',
|
|
79
|
+
p: 3,
|
|
80
|
+
pr: 0,
|
|
81
|
+
flex: 'auto',
|
|
82
|
+
} }
|
|
76
83
|
>
|
|
77
84
|
<label htmlFor={ forLabel } { ...labelProps }>
|
|
78
85
|
{ label }
|
|
@@ -82,7 +89,11 @@ const RadioOption = ( {
|
|
|
82
89
|
sx={ {
|
|
83
90
|
color: 'input.radio-box.label.secondary.default',
|
|
84
91
|
mb: 0,
|
|
92
|
+
mt: 1,
|
|
85
93
|
fontSize: 1,
|
|
94
|
+
fontWeight: 'regular',
|
|
95
|
+
letterSpacing: '0.01em',
|
|
96
|
+
lineHeight: '140%',
|
|
86
97
|
display: 'block',
|
|
87
98
|
} }
|
|
88
99
|
id={ describedById }
|
|
@@ -60,7 +60,12 @@ export const Primary = {
|
|
|
60
60
|
args: {
|
|
61
61
|
defaultValue: 'one',
|
|
62
62
|
options: [
|
|
63
|
-
{
|
|
63
|
+
{
|
|
64
|
+
label: 'One',
|
|
65
|
+
value: 'one',
|
|
66
|
+
description:
|
|
67
|
+
'This is a longer description that allows us to see the text wrap and determine if the line height is correct',
|
|
68
|
+
},
|
|
64
69
|
{ label: 'Two', value: 'two', description: 'This is a description' },
|
|
65
70
|
{ label: 'Three', value: 'three', description: 'This is a description' },
|
|
66
71
|
],
|
|
@@ -27,7 +27,16 @@ export const Default: Story = {
|
|
|
27
27
|
<Heading variant="h3">Heading Three</Heading>
|
|
28
28
|
<Heading variant="h4">Heading Four</Heading>
|
|
29
29
|
<Heading variant="h5">Heading Five</Heading>
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
<Heading variant="h3" as="h1">
|
|
32
|
+
Heading One with Heading Three Styles
|
|
33
|
+
</Heading>
|
|
34
|
+
<Heading as="p" sx={ { variant: 'text.caps' } }>
|
|
35
|
+
Paragraph with Caps Styles
|
|
36
|
+
</Heading>
|
|
37
|
+
<Heading as="h3" sx={ { variant: 'text.caps' } }>
|
|
38
|
+
Heading Three with Caps Styles
|
|
39
|
+
</Heading>
|
|
31
40
|
</Box>
|
|
32
41
|
),
|
|
33
42
|
};
|
|
@@ -20,10 +20,9 @@ export const Heading = forwardRef< HTMLHeadingElement, HeadingProps >(
|
|
|
20
20
|
( { variant = 'h3', sx, className, ...rest }: HeadingProps, ref: Ref< HTMLHeadingElement > ) => (
|
|
21
21
|
<ThemeHeading
|
|
22
22
|
as={ variant }
|
|
23
|
+
variant={ variant }
|
|
23
24
|
sx={ {
|
|
24
25
|
color: 'heading',
|
|
25
|
-
// pass variant prop to sx
|
|
26
|
-
variant: `text.${ variant.toString() }`,
|
|
27
26
|
...sx,
|
|
28
27
|
} }
|
|
29
28
|
className={ classNames( 'vip-heading-component', className ) }
|
|
@@ -21,10 +21,12 @@ const meta: Meta< typeof Pagination > = {
|
|
|
21
21
|
component: `
|
|
22
22
|
A Pagination component for navigating paged data.
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Modes
|
|
25
25
|
|
|
26
|
-
- **
|
|
27
|
-
- **compact
|
|
26
|
+
- **Default**: Shows individual page number buttons with ellipsis for large page counts.
|
|
27
|
+
- **Compact** (\`compact\` prop): Shows a dropdown page selector instead of individual page numbers.
|
|
28
|
+
|
|
29
|
+
For cursor-based pagination with only prev/next arrows, see \`SimplePagination\`.
|
|
28
30
|
|
|
29
31
|
## Component Properties
|
|
30
32
|
`,
|
|
@@ -42,12 +44,13 @@ const PaginationWithState = ( {
|
|
|
42
44
|
totalItems = 200,
|
|
43
45
|
initialItemsPerPage = 20,
|
|
44
46
|
displayItemsPerPageSelector = false,
|
|
47
|
+
compact = false,
|
|
45
48
|
...props
|
|
46
49
|
}: {
|
|
47
50
|
initialPage?: number;
|
|
48
51
|
totalItems?: number;
|
|
49
52
|
initialItemsPerPage?: number;
|
|
50
|
-
|
|
53
|
+
compact?: boolean;
|
|
51
54
|
pageSizeOptions?: number[];
|
|
52
55
|
displayItemsPerPageSelector?: boolean;
|
|
53
56
|
} ) => {
|
|
@@ -62,6 +65,7 @@ const PaginationWithState = ( {
|
|
|
62
65
|
itemsPerPage={ itemsPerPage }
|
|
63
66
|
onPageChange={ setCurrentPage }
|
|
64
67
|
displayItemsPerPageSelector={ displayItemsPerPageSelector }
|
|
68
|
+
compact={ compact }
|
|
65
69
|
onItemsPerPageChange={ size => {
|
|
66
70
|
setItemsPerPage( size );
|
|
67
71
|
setCurrentPage( 1 );
|
|
@@ -84,12 +88,12 @@ const OpenEndedPaginationWithState = ( {
|
|
|
84
88
|
initialPage = 1,
|
|
85
89
|
initialItemsPerPage = 20,
|
|
86
90
|
hasNextPage,
|
|
87
|
-
|
|
91
|
+
compact = false,
|
|
88
92
|
}: {
|
|
89
93
|
initialPage?: number;
|
|
90
94
|
initialItemsPerPage?: number;
|
|
91
95
|
hasNextPage?: boolean;
|
|
92
|
-
|
|
96
|
+
compact?: boolean;
|
|
93
97
|
} ) => {
|
|
94
98
|
const [ currentPage, setCurrentPage ] = useState( initialPage );
|
|
95
99
|
const [ itemsPerPage, setItemsPerPage ] = useState( initialItemsPerPage );
|
|
@@ -104,7 +108,7 @@ const OpenEndedPaginationWithState = ( {
|
|
|
104
108
|
setCurrentPage( 1 );
|
|
105
109
|
} }
|
|
106
110
|
hasNextPage={ hasNextPage }
|
|
107
|
-
{
|
|
111
|
+
compact={ compact }
|
|
108
112
|
>
|
|
109
113
|
<Flex sx={ { justifyContent: 'center', alignItems: 'center', verticalAlign: 'middle' } }>
|
|
110
114
|
<Badge variant="gold" sx={ { mr: 2 } }>
|
|
@@ -121,7 +125,6 @@ export const Primary: Story = {
|
|
|
121
125
|
currentPage: 1,
|
|
122
126
|
totalItems: 200,
|
|
123
127
|
itemsPerPage: 20,
|
|
124
|
-
variant: 'full',
|
|
125
128
|
displayItemsPerPageSelector: false,
|
|
126
129
|
},
|
|
127
130
|
};
|
|
@@ -131,7 +134,7 @@ export const Default: Story = {
|
|
|
131
134
|
};
|
|
132
135
|
|
|
133
136
|
export const Compact: Story = {
|
|
134
|
-
render: () => <PaginationWithState
|
|
137
|
+
render: () => <PaginationWithState compact />,
|
|
135
138
|
};
|
|
136
139
|
|
|
137
140
|
export const FewPages: Story = {
|
|
@@ -212,7 +215,7 @@ export const OpenEnded: Story = {
|
|
|
212
215
|
};
|
|
213
216
|
|
|
214
217
|
export const OpenEndedCompact: Story = {
|
|
215
|
-
render: () => <OpenEndedPaginationWithState
|
|
218
|
+
render: () => <OpenEndedPaginationWithState compact />,
|
|
216
219
|
};
|
|
217
220
|
|
|
218
221
|
export const OpenEndedLastPage: Story = {
|
|
@@ -109,7 +109,7 @@ describe( '<Pagination />', () => {
|
|
|
109
109
|
} );
|
|
110
110
|
|
|
111
111
|
it( 'renders compact variant with Page text', () => {
|
|
112
|
-
render( <Pagination { ...defaultProps }
|
|
112
|
+
render( <Pagination { ...defaultProps } compact currentPage={ 3 } /> );
|
|
113
113
|
|
|
114
114
|
expect( screen.getByText( 'Page' ) ).toBeInTheDocument();
|
|
115
115
|
expect( screen.getByText( 'of 10' ) ).toBeInTheDocument();
|
|
@@ -136,9 +136,7 @@ describe( '<Pagination />', () => {
|
|
|
136
136
|
} );
|
|
137
137
|
|
|
138
138
|
it( 'has no accessibility violations (compact variant)', async () => {
|
|
139
|
-
const { container } = render(
|
|
140
|
-
<Pagination { ...defaultProps } variant="compact" currentPage={ 5 } />
|
|
141
|
-
);
|
|
139
|
+
const { container } = render( <Pagination { ...defaultProps } compact currentPage={ 5 } /> );
|
|
142
140
|
|
|
143
141
|
expect( await axe( container ) ).toHaveNoViolations();
|
|
144
142
|
} );
|
|
@@ -304,7 +302,7 @@ describe( '<Pagination /> open-ended mode', () => {
|
|
|
304
302
|
} );
|
|
305
303
|
|
|
306
304
|
it( 'renders compact variant with "Page" but without "of Y"', () => {
|
|
307
|
-
render( <Pagination { ...openEndedProps }
|
|
305
|
+
render( <Pagination { ...openEndedProps } compact /> );
|
|
308
306
|
|
|
309
307
|
expect( screen.getByText( 'Page' ) ).toBeInTheDocument();
|
|
310
308
|
expect( screen.queryByText( /of \d+/ ) ).not.toBeInTheDocument();
|
|
@@ -317,7 +315,7 @@ describe( '<Pagination /> open-ended mode', () => {
|
|
|
317
315
|
} );
|
|
318
316
|
|
|
319
317
|
it( 'has no accessibility violations (open-ended compact)', async () => {
|
|
320
|
-
const { container } = render( <Pagination { ...openEndedProps }
|
|
318
|
+
const { container } = render( <Pagination { ...openEndedProps } compact /> );
|
|
321
319
|
|
|
322
320
|
expect( await axe( container ) ).toHaveNoViolations();
|
|
323
321
|
} );
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/** @jsxImportSource theme-ui */
|
|
2
2
|
|
|
3
|
-
import classNames from 'classnames';
|
|
4
3
|
import { forwardRef } from 'react';
|
|
5
4
|
import { BiDotsHorizontalRounded } from 'react-icons/bi';
|
|
6
5
|
import { MdChevronLeft, MdChevronRight } from 'react-icons/md';
|
|
7
|
-
import { Flex
|
|
6
|
+
import { Flex } from 'theme-ui';
|
|
8
7
|
|
|
8
|
+
import { PaginationLayout, PaginationLayoutProps } from './PaginationLayout';
|
|
9
9
|
import {
|
|
10
|
-
containerStyles,
|
|
11
10
|
navigationStyles,
|
|
12
11
|
pageButtonStyles,
|
|
13
12
|
activePageButtonStyles,
|
|
@@ -19,13 +18,7 @@ import { Button } from '../Button';
|
|
|
19
18
|
import { Select } from '../NewForm';
|
|
20
19
|
import { Text } from '../Text';
|
|
21
20
|
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
export interface PaginationProps {
|
|
25
|
-
/** Whether to show the items-per-page dropdown selector.
|
|
26
|
-
* @default false
|
|
27
|
-
*/
|
|
28
|
-
displayItemsPerPageSelector?: boolean;
|
|
21
|
+
export interface PaginationProps extends PaginationLayoutProps {
|
|
29
22
|
/** The currently active page number (1-based). */
|
|
30
23
|
currentPage: number;
|
|
31
24
|
/** Total number of items across all pages. Used to compute totalPages if not provided. */
|
|
@@ -42,20 +35,15 @@ export interface PaginationProps {
|
|
|
42
35
|
hasNextPage?: boolean;
|
|
43
36
|
/** The maximum page number that can be reached. Used for open-ended pagination without totalPages. */
|
|
44
37
|
maxReachablePage?: number;
|
|
45
|
-
/**
|
|
46
|
-
* @default
|
|
38
|
+
/** When true, shows a compact dropdown page selector instead of individual page buttons.
|
|
39
|
+
* @default false
|
|
47
40
|
*/
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @default
|
|
41
|
+
compact?: boolean;
|
|
42
|
+
/** Display variant. Use 'compact' for dropdown page selector. Equivalent to the `compact` prop.
|
|
43
|
+
* @default 'full'
|
|
44
|
+
* @deprecated Use the `compact` prop instead, or `SimplePagination` for cursor-based navigation.
|
|
51
45
|
*/
|
|
52
|
-
|
|
53
|
-
/** Additional CSS class name for the pagination container. */
|
|
54
|
-
className?: string;
|
|
55
|
-
/** Theme UI style overrides. */
|
|
56
|
-
sx?: ThemeUIStyleObject;
|
|
57
|
-
/** Optional content rendered between the items-per-page selector and page navigation. */
|
|
58
|
-
children?: React.ReactNode;
|
|
46
|
+
variant?: 'full' | 'compact';
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
export type PageNumberItem = number | 'ellipsis';
|
|
@@ -132,28 +120,6 @@ export function getPageNumbers(
|
|
|
132
120
|
];
|
|
133
121
|
}
|
|
134
122
|
|
|
135
|
-
const ItemsPerPageSelect = ( {
|
|
136
|
-
itemsPerPage,
|
|
137
|
-
pageSizeOptions,
|
|
138
|
-
onItemsPerPageChange,
|
|
139
|
-
}: {
|
|
140
|
-
itemsPerPage: number;
|
|
141
|
-
pageSizeOptions: number[];
|
|
142
|
-
onItemsPerPageChange: ( size: number ) => void;
|
|
143
|
-
} ) => (
|
|
144
|
-
<Select
|
|
145
|
-
id="items-per-page"
|
|
146
|
-
aria-label="Items per page"
|
|
147
|
-
separator={ false }
|
|
148
|
-
value={ itemsPerPage }
|
|
149
|
-
options={ pageSizeOptions.map( size => ( {
|
|
150
|
-
value: size,
|
|
151
|
-
label: `${ size.toString() } / page`,
|
|
152
|
-
} ) ) }
|
|
153
|
-
onChange={ option => onItemsPerPageChange( Number( option?.value ) ) }
|
|
154
|
-
/>
|
|
155
|
-
);
|
|
156
|
-
|
|
157
123
|
const PageNumbers = ( {
|
|
158
124
|
currentPage,
|
|
159
125
|
totalPages,
|
|
@@ -235,12 +201,11 @@ const CompactPageSelector = ( {
|
|
|
235
201
|
|
|
236
202
|
/**
|
|
237
203
|
* A pagination control for navigating through paged content.
|
|
238
|
-
*
|
|
204
|
+
* Shows page-number buttons by default, or a compact dropdown when `compact` is true.
|
|
239
205
|
*/
|
|
240
206
|
export const Pagination = forwardRef< HTMLElement, PaginationProps >(
|
|
241
207
|
(
|
|
242
208
|
{
|
|
243
|
-
displayItemsPerPageSelector = false,
|
|
244
209
|
currentPage,
|
|
245
210
|
totalItems,
|
|
246
211
|
totalPages,
|
|
@@ -249,8 +214,10 @@ export const Pagination = forwardRef< HTMLElement, PaginationProps >(
|
|
|
249
214
|
onItemsPerPageChange,
|
|
250
215
|
hasNextPage,
|
|
251
216
|
maxReachablePage,
|
|
252
|
-
|
|
253
|
-
|
|
217
|
+
compact = false,
|
|
218
|
+
variant,
|
|
219
|
+
displayItemsPerPageSelector,
|
|
220
|
+
pageSizeOptions,
|
|
254
221
|
className,
|
|
255
222
|
sx,
|
|
256
223
|
children,
|
|
@@ -258,47 +225,45 @@ export const Pagination = forwardRef< HTMLElement, PaginationProps >(
|
|
|
258
225
|
},
|
|
259
226
|
ref
|
|
260
227
|
) => {
|
|
228
|
+
const isCompact = compact || variant === 'compact';
|
|
229
|
+
|
|
261
230
|
const resolvedTotalPages =
|
|
262
231
|
totalPages ??
|
|
263
232
|
( totalItems !== undefined ? Math.ceil( totalItems / itemsPerPage ) : undefined );
|
|
264
233
|
|
|
265
234
|
const isFirstPage = currentPage <= 1;
|
|
266
|
-
|
|
267
|
-
|
|
235
|
+
let isLastPage: boolean;
|
|
236
|
+
if ( resolvedTotalPages !== undefined ) {
|
|
237
|
+
isLastPage = currentPage >= resolvedTotalPages;
|
|
238
|
+
} else {
|
|
239
|
+
isLastPage = hasNextPage === false;
|
|
240
|
+
}
|
|
268
241
|
|
|
269
242
|
return (
|
|
270
|
-
<
|
|
243
|
+
<PaginationLayout
|
|
271
244
|
ref={ ref }
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
245
|
+
displayItemsPerPageSelector={ displayItemsPerPageSelector }
|
|
246
|
+
itemsPerPage={ itemsPerPage }
|
|
247
|
+
pageSizeOptions={ pageSizeOptions }
|
|
248
|
+
onItemsPerPageChange={ onItemsPerPageChange }
|
|
249
|
+
className={ className }
|
|
250
|
+
sx={ sx }
|
|
275
251
|
{ ...rest }
|
|
276
252
|
>
|
|
277
|
-
<Box>
|
|
278
|
-
{ displayItemsPerPageSelector && (
|
|
279
|
-
<ItemsPerPageSelect
|
|
280
|
-
itemsPerPage={ itemsPerPage }
|
|
281
|
-
pageSizeOptions={ pageSizeOptions }
|
|
282
|
-
onItemsPerPageChange={ onItemsPerPageChange }
|
|
283
|
-
/>
|
|
284
|
-
) }
|
|
285
|
-
</Box>
|
|
286
253
|
<Box sx={ { flex: 1 } }>{ children }</Box>
|
|
287
254
|
<Flex sx={ navigationStyles }>
|
|
288
|
-
{
|
|
289
|
-
<
|
|
255
|
+
{ isCompact ? (
|
|
256
|
+
<CompactPageSelector
|
|
290
257
|
currentPage={ currentPage }
|
|
291
258
|
totalPages={ resolvedTotalPages }
|
|
292
|
-
hasNextPage={ hasNextPage }
|
|
293
259
|
maxReachablePage={ maxReachablePage }
|
|
294
260
|
onPageChange={ onPageChange }
|
|
295
261
|
/>
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
{ variant === 'compact' && (
|
|
299
|
-
<CompactPageSelector
|
|
262
|
+
) : (
|
|
263
|
+
<PageNumbers
|
|
300
264
|
currentPage={ currentPage }
|
|
301
265
|
totalPages={ resolvedTotalPages }
|
|
266
|
+
hasNextPage={ hasNextPage }
|
|
302
267
|
maxReachablePage={ maxReachablePage }
|
|
303
268
|
onPageChange={ onPageChange }
|
|
304
269
|
/>
|
|
@@ -322,7 +287,7 @@ export const Pagination = forwardRef< HTMLElement, PaginationProps >(
|
|
|
322
287
|
<MdChevronRight size={ 20 } />
|
|
323
288
|
</Button>
|
|
324
289
|
</Flex>
|
|
325
|
-
</
|
|
290
|
+
</PaginationLayout>
|
|
326
291
|
);
|
|
327
292
|
}
|
|
328
293
|
);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import { forwardRef } from 'react';
|
|
5
|
+
import { ThemeUIStyleObject } from 'theme-ui';
|
|
6
|
+
|
|
7
|
+
import { containerStyles } from './styles';
|
|
8
|
+
import { Box } from '../Box';
|
|
9
|
+
import { Select } from '../NewForm';
|
|
10
|
+
|
|
11
|
+
export interface PaginationLayoutProps {
|
|
12
|
+
/** Whether to show the items-per-page dropdown selector.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
displayItemsPerPageSelector?: boolean;
|
|
16
|
+
/** Number of items displayed per page. */
|
|
17
|
+
itemsPerPage?: number;
|
|
18
|
+
/** Available page size options for the items-per-page selector.
|
|
19
|
+
* @default [20, 50, 100]
|
|
20
|
+
*/
|
|
21
|
+
pageSizeOptions?: number[];
|
|
22
|
+
/** Callback fired when the user changes the items-per-page value. */
|
|
23
|
+
onItemsPerPageChange?: ( itemsPerPage: number ) => void;
|
|
24
|
+
/** Additional CSS class name for the pagination container. */
|
|
25
|
+
className?: string;
|
|
26
|
+
/** Theme UI style overrides. */
|
|
27
|
+
sx?: ThemeUIStyleObject;
|
|
28
|
+
/** Slot for variant-specific content (page numbers, arrows, etc.). */
|
|
29
|
+
children?: React.ReactNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ItemsPerPageSelect = ( {
|
|
33
|
+
itemsPerPage,
|
|
34
|
+
pageSizeOptions,
|
|
35
|
+
onItemsPerPageChange,
|
|
36
|
+
}: {
|
|
37
|
+
itemsPerPage: number;
|
|
38
|
+
pageSizeOptions: number[];
|
|
39
|
+
onItemsPerPageChange: ( size: number ) => void;
|
|
40
|
+
} ) => (
|
|
41
|
+
<Select
|
|
42
|
+
id="items-per-page"
|
|
43
|
+
aria-label="Items per page"
|
|
44
|
+
separator={ false }
|
|
45
|
+
value={ itemsPerPage }
|
|
46
|
+
options={ pageSizeOptions.map( size => ( {
|
|
47
|
+
value: size,
|
|
48
|
+
label: `${ size.toString() } / page`,
|
|
49
|
+
} ) ) }
|
|
50
|
+
onChange={ option => onItemsPerPageChange( Number( option?.value ) ) }
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Shared layout wrapper for pagination components.
|
|
56
|
+
* Renders the nav landmark, optional items-per-page selector, and variant content.
|
|
57
|
+
*/
|
|
58
|
+
export const PaginationLayout = forwardRef< HTMLElement, PaginationLayoutProps >(
|
|
59
|
+
(
|
|
60
|
+
{
|
|
61
|
+
displayItemsPerPageSelector = false,
|
|
62
|
+
itemsPerPage,
|
|
63
|
+
pageSizeOptions = [ 20, 50, 100 ],
|
|
64
|
+
onItemsPerPageChange,
|
|
65
|
+
className,
|
|
66
|
+
sx,
|
|
67
|
+
children,
|
|
68
|
+
...rest
|
|
69
|
+
},
|
|
70
|
+
ref
|
|
71
|
+
) => (
|
|
72
|
+
<nav
|
|
73
|
+
ref={ ref }
|
|
74
|
+
aria-label="Pagination"
|
|
75
|
+
className={ classNames( 'vip-pagination-component', className ) }
|
|
76
|
+
sx={ { ...containerStyles, ...sx } }
|
|
77
|
+
{ ...rest }
|
|
78
|
+
>
|
|
79
|
+
<Box>
|
|
80
|
+
{ displayItemsPerPageSelector && itemsPerPage && onItemsPerPageChange && (
|
|
81
|
+
<ItemsPerPageSelect
|
|
82
|
+
itemsPerPage={ itemsPerPage }
|
|
83
|
+
pageSizeOptions={ pageSizeOptions }
|
|
84
|
+
onItemsPerPageChange={ onItemsPerPageChange }
|
|
85
|
+
/>
|
|
86
|
+
) }
|
|
87
|
+
</Box>
|
|
88
|
+
{ children }
|
|
89
|
+
</nav>
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
PaginationLayout.displayName = 'PaginationLayout';
|