@charcoal-ui/react 5.4.4 → 5.5.0-beta.1
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/dist/components/Pagination/helper.d.ts +2 -0
- package/dist/components/Pagination/helper.d.ts.map +1 -0
- package/dist/components/Pagination/index.d.ts +43 -0
- package/dist/components/Pagination/index.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Modal/index.tsx +1 -1
- package/src/components/Pagination/Pagination.story.tsx +98 -0
- package/src/components/Pagination/helper.test.ts +58 -0
- package/src/components/Pagination/helper.ts +71 -0
- package/src/components/Pagination/index.css +67 -0
- package/src/components/Pagination/index.tsx +153 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@charcoal-ui/react",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0-beta.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
"@react-aria/switch": "^3.6.0",
|
|
49
49
|
"@react-aria/utils": "^3.23.0",
|
|
50
50
|
"@react-aria/visually-hidden": "^3.8.8",
|
|
51
|
+
"@react-spring/web": "^10",
|
|
51
52
|
"@react-stately/radio": "^3.10.2",
|
|
52
53
|
"polished": "^4.1.4",
|
|
53
54
|
"react-compiler-runtime": "1.0.0",
|
|
54
|
-
"react-spring": "^9.0.0",
|
|
55
55
|
"react-stately": "^3.26.0",
|
|
56
56
|
"warning": "^4.0.3",
|
|
57
|
-
"@charcoal-ui/foundation": "5.
|
|
58
|
-
"@charcoal-ui/
|
|
59
|
-
"@charcoal-ui/
|
|
60
|
-
"@charcoal-ui/utils": "5.
|
|
57
|
+
"@charcoal-ui/foundation": "5.5.0-beta.1",
|
|
58
|
+
"@charcoal-ui/theme": "5.5.0-beta.1",
|
|
59
|
+
"@charcoal-ui/icons": "5.5.0-beta.1",
|
|
60
|
+
"@charcoal-ui/utils": "5.5.0-beta.1"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"react": ">=17.0.0"
|
|
@@ -2,7 +2,7 @@ import { useContext, forwardRef, memo } from 'react'
|
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { Overlay } from '@react-aria/overlays'
|
|
4
4
|
import type { AriaDialogProps } from '@react-types/dialog'
|
|
5
|
-
import { animated, useTransition, easings } from 'react-spring'
|
|
5
|
+
import { animated, useTransition, easings } from '@react-spring/web'
|
|
6
6
|
import Button, { ButtonProps } from '../Button'
|
|
7
7
|
import IconButton, { IconButtonProps } from '../IconButton'
|
|
8
8
|
import { useObjectRef } from '@react-aria/utils'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5'
|
|
3
|
+
import Pagination from '.'
|
|
4
|
+
|
|
5
|
+
type PaginationStoryArgs = {
|
|
6
|
+
page: number
|
|
7
|
+
pageCount: number
|
|
8
|
+
pageRangeDisplayed?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function PaginationWithState(args: PaginationStoryArgs) {
|
|
12
|
+
const [page, setPage] = useState(args.page)
|
|
13
|
+
return (
|
|
14
|
+
<Pagination
|
|
15
|
+
page={page}
|
|
16
|
+
pageCount={args.pageCount}
|
|
17
|
+
pageRangeDisplayed={args.pageRangeDisplayed}
|
|
18
|
+
onChange={setPage}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parsePageFromHash(fallback: number): number {
|
|
24
|
+
const match = window.location.hash.match(/^#page-(\d+)$/)
|
|
25
|
+
return match ? parseInt(match[1], 10) : fallback
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function LinkPaginationWithState(args: PaginationStoryArgs) {
|
|
29
|
+
const [page, setPage] = useState(() => parsePageFromHash(args.page))
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const handleHashChange = () => setPage(parsePageFromHash(args.page))
|
|
33
|
+
window.addEventListener('hashchange', handleHashChange)
|
|
34
|
+
return () => window.removeEventListener('hashchange', handleHashChange)
|
|
35
|
+
}, [args.page])
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
<p style={{ marginBottom: 8, fontSize: 14, color: '#666' }}>
|
|
40
|
+
Current page: {page}
|
|
41
|
+
</p>
|
|
42
|
+
<Pagination
|
|
43
|
+
page={page}
|
|
44
|
+
pageCount={args.pageCount}
|
|
45
|
+
pageRangeDisplayed={args.pageRangeDisplayed}
|
|
46
|
+
makeUrl={(p) => `#page-${p}`}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
title: 'react/Pagination',
|
|
54
|
+
component: Pagination,
|
|
55
|
+
parameters: {
|
|
56
|
+
layout: 'centered',
|
|
57
|
+
},
|
|
58
|
+
} satisfies Meta<typeof Pagination>
|
|
59
|
+
|
|
60
|
+
export const Default: StoryObj<typeof Pagination> = {
|
|
61
|
+
args: {
|
|
62
|
+
page: 5,
|
|
63
|
+
pageCount: 10,
|
|
64
|
+
},
|
|
65
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const FirstPage: StoryObj<typeof Pagination> = {
|
|
69
|
+
args: {
|
|
70
|
+
page: 1,
|
|
71
|
+
pageCount: 10,
|
|
72
|
+
},
|
|
73
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const LastPage: StoryObj<typeof Pagination> = {
|
|
77
|
+
args: {
|
|
78
|
+
page: 10,
|
|
79
|
+
pageCount: 10,
|
|
80
|
+
},
|
|
81
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const ManyPages: StoryObj<typeof Pagination> = {
|
|
85
|
+
args: {
|
|
86
|
+
page: 50,
|
|
87
|
+
pageCount: 103,
|
|
88
|
+
},
|
|
89
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const LinkPaginationStory: StoryObj<typeof Pagination> = {
|
|
93
|
+
args: {
|
|
94
|
+
page: 5,
|
|
95
|
+
pageCount: 10,
|
|
96
|
+
},
|
|
97
|
+
render: (args) => <LinkPaginationWithState {...args} />,
|
|
98
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { usePaginationWindow } from './helper'
|
|
3
|
+
|
|
4
|
+
describe('usePaginationWindow', () => {
|
|
5
|
+
describe('7 pages or less (no ellipsis)', () => {
|
|
6
|
+
it('returns [1,2,3,4,5,6,7] when page=1, pageCount=10', () => {
|
|
7
|
+
const { result } = renderHook(() => usePaginationWindow(1, 10))
|
|
8
|
+
expect(result.current).toEqual([1, 2, 3, 4, 5, 6, 7])
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('returns [1,2,3,4,5,6,7] when page=2, pageCount=10', () => {
|
|
12
|
+
const { result } = renderHook(() => usePaginationWindow(2, 10))
|
|
13
|
+
expect(result.current).toEqual([1, 2, 3, 4, 5, 6, 7])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('returns [1,2,3,4,5,6,7] when page=4, pageCount=10', () => {
|
|
17
|
+
const { result } = renderHook(() => usePaginationWindow(4, 10))
|
|
18
|
+
expect(result.current).toEqual([1, 2, 3, 4, 5, 6, 7])
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('with ellipsis', () => {
|
|
23
|
+
it('returns [1, ..., 4, 5, 6, 7, 8] when page=5, pageCount=100', () => {
|
|
24
|
+
const { result } = renderHook(() => usePaginationWindow(5, 100))
|
|
25
|
+
expect(result.current).toEqual([1, '...', 4, 5, 6, 7, 8])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('returns [1, ..., 96, 97, 98, 99, 100] when page=100, pageCount=100', () => {
|
|
29
|
+
const { result } = renderHook(() => usePaginationWindow(100, 100))
|
|
30
|
+
expect(result.current).toEqual([1, '...', 96, 97, 98, 99, 100])
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('with pageRangeDisplayed specified', () => {
|
|
35
|
+
it('shows first 5 pages when pageRangeDisplayed=5 (no ellipsis)', () => {
|
|
36
|
+
const { result } = renderHook(() => usePaginationWindow(2, 20, 5))
|
|
37
|
+
expect(result.current).toEqual([1, 2, 3, 4, 5])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('shows ellipsis when pageRangeDisplayed=5', () => {
|
|
41
|
+
const { result } = renderHook(() => usePaginationWindow(5, 20, 5))
|
|
42
|
+
expect(result.current).toEqual([1, '...', 5, 6, 7])
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('when arguments change', () => {
|
|
47
|
+
it('updates window when page changes', () => {
|
|
48
|
+
const { result, rerender } = renderHook(
|
|
49
|
+
({ page, pageCount }) => usePaginationWindow(page, pageCount),
|
|
50
|
+
{ initialProps: { page: 2, pageCount: 10 } },
|
|
51
|
+
)
|
|
52
|
+
expect(result.current).toEqual([1, 2, 3, 4, 5, 6, 7])
|
|
53
|
+
|
|
54
|
+
rerender({ page: 5, pageCount: 10 })
|
|
55
|
+
expect(result.current).toEqual([1, '...', 4, 5, 6, 7, 8])
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useDebugValue } from 'react'
|
|
2
|
+
import warning from 'warning'
|
|
3
|
+
|
|
4
|
+
export function usePaginationWindow(
|
|
5
|
+
page: number,
|
|
6
|
+
pageCount: number,
|
|
7
|
+
pageRangeDisplayed = 7,
|
|
8
|
+
) {
|
|
9
|
+
'use memo'
|
|
10
|
+
// ページャーのリンク生成例:
|
|
11
|
+
//
|
|
12
|
+
// < [ 1 ] [*2*] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] >
|
|
13
|
+
//
|
|
14
|
+
// < [ 1 ] [ 2 ] [ 3 ] [*4*] [ 5 ] [ 6 ] [ 7 ] >
|
|
15
|
+
//
|
|
16
|
+
// < [ 1 ] ... [ 4 ] [*5*] [ 6 ] [ 7 ] [ 8 ] >
|
|
17
|
+
//
|
|
18
|
+
// < [ 1 ] ... [ 99 ] [*100*] [ 101 ] [ 102 ] [ 103 ] >
|
|
19
|
+
//
|
|
20
|
+
// < [ 1 ] ... [ 99 ] [ 100 ] [ 101 ] [ 102 ] [*103*]
|
|
21
|
+
//
|
|
22
|
+
// [*1*] [ 2 ] >
|
|
23
|
+
//
|
|
24
|
+
// デザインの意図: 前後移動時のカーソル移動を最小限にする。
|
|
25
|
+
|
|
26
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
27
|
+
warning((page | 0) === page, `\`page\` must be integer (${page})`)
|
|
28
|
+
warning(
|
|
29
|
+
(pageCount | 0) === pageCount,
|
|
30
|
+
`\`pageCount\` must be integer (${pageCount})`,
|
|
31
|
+
)
|
|
32
|
+
warning(
|
|
33
|
+
(pageRangeDisplayed | 0) === pageRangeDisplayed,
|
|
34
|
+
`\`pageRangeDisplayed\` must be integer (${pageRangeDisplayed})`,
|
|
35
|
+
)
|
|
36
|
+
warning(pageRangeDisplayed > 2, `\`windowSize\` must be greater than 2`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const visibleFirstPage = 1
|
|
40
|
+
const visibleLastPage = Math.min(
|
|
41
|
+
pageCount,
|
|
42
|
+
Math.max(page + Math.floor(pageRangeDisplayed / 2), pageRangeDisplayed),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const window = (() => {
|
|
46
|
+
if (visibleLastPage <= pageRangeDisplayed) {
|
|
47
|
+
// 表示範囲が1-7ページなら省略は無い。
|
|
48
|
+
return Array.from(
|
|
49
|
+
{ length: 1 + visibleLastPage - visibleFirstPage },
|
|
50
|
+
(_, i) => visibleFirstPage + i,
|
|
51
|
+
)
|
|
52
|
+
} else {
|
|
53
|
+
const start = visibleLastPage - (pageRangeDisplayed - 1) + 2
|
|
54
|
+
// 表示範囲が1-7ページを超えるなら、
|
|
55
|
+
// - 1ページ目は固定で表示する
|
|
56
|
+
// - 2ページ目から現在のページの直前までは省略する
|
|
57
|
+
return [
|
|
58
|
+
visibleFirstPage,
|
|
59
|
+
'...' as const,
|
|
60
|
+
...Array.from(
|
|
61
|
+
{ length: 1 + visibleLastPage - start },
|
|
62
|
+
(_, i) => start + i,
|
|
63
|
+
),
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
})()
|
|
67
|
+
|
|
68
|
+
useDebugValue(window)
|
|
69
|
+
|
|
70
|
+
return window
|
|
71
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
.charcoal-pagination {
|
|
2
|
+
display: flex;
|
|
3
|
+
justify-content: center;
|
|
4
|
+
align-items: center;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.charcoal-pagination-button {
|
|
8
|
+
font-size: 1rem;
|
|
9
|
+
line-height: calc(1em + 8px);
|
|
10
|
+
text-decoration: none;
|
|
11
|
+
border: none;
|
|
12
|
+
outline: none;
|
|
13
|
+
touch-action: manipulation;
|
|
14
|
+
user-select: none;
|
|
15
|
+
transition:
|
|
16
|
+
box-shadow 0.2s ease 0s,
|
|
17
|
+
color 0.2s ease 0s,
|
|
18
|
+
background 0.2s ease 0s,
|
|
19
|
+
opacity 0.2s ease 0s;
|
|
20
|
+
|
|
21
|
+
display: flex;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
align-items: center;
|
|
24
|
+
box-sizing: content-box;
|
|
25
|
+
min-width: 24px;
|
|
26
|
+
min-height: 24px;
|
|
27
|
+
padding: 8px;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
font-weight: bold;
|
|
30
|
+
/* HACK: Safari repaint fix */
|
|
31
|
+
/* stylelint-disable-next-line property-no-vendor-prefix */
|
|
32
|
+
-webkit-transform: translateZ(0);
|
|
33
|
+
|
|
34
|
+
background: transparent;
|
|
35
|
+
color: var(--charcoal-text3);
|
|
36
|
+
border-radius: 48px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.charcoal-pagination-button[hidden] {
|
|
40
|
+
visibility: hidden;
|
|
41
|
+
display: block;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.charcoal-pagination-button:hover {
|
|
45
|
+
background: var(--charcoal-surface3);
|
|
46
|
+
color: var(--charcoal-text2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.charcoal-pagination-button[aria-current] {
|
|
50
|
+
background-color: var(--charcoal-surface6);
|
|
51
|
+
color: var(--charcoal-text5);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.charcoal-pagination-button[aria-current]:hover {
|
|
55
|
+
background-color: var(--charcoal-surface6);
|
|
56
|
+
color: var(--charcoal-text5);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.charcoal-pagination-spacer {
|
|
60
|
+
cursor: default;
|
|
61
|
+
color: var(--charcoal-text3);
|
|
62
|
+
background: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.charcoal-pagination-spacer.charcoal-icon-button:disabled {
|
|
66
|
+
opacity: 1;
|
|
67
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import './index.css'
|
|
2
|
+
|
|
3
|
+
import { usePaginationWindow } from './helper'
|
|
4
|
+
import { useClassNames } from '../../_lib/useClassNames'
|
|
5
|
+
import IconButton from '../IconButton'
|
|
6
|
+
|
|
7
|
+
interface CommonProps {
|
|
8
|
+
page: number
|
|
9
|
+
pageCount: number
|
|
10
|
+
pageRangeDisplayed?: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type LinkComponentProps = {
|
|
14
|
+
href: string
|
|
15
|
+
className?: string
|
|
16
|
+
children?: React.ReactNode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type NavProps = Omit<React.ComponentPropsWithoutRef<'nav'>, 'onChange'>
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Pagination component. Use either `onChange` (button mode) or `makeUrl` (link mode).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Button mode - for client-side state
|
|
26
|
+
* <Pagination page={1} pageCount={10} onChange={setPage} />
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Link mode - for server routing / static pages
|
|
30
|
+
* <Pagination page={1} pageCount={10} makeUrl={(p) => `?page=${p}`} />
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Link mode with custom component (e.g. Next.js Link)
|
|
34
|
+
* <Pagination page={1} pageCount={10} makeUrl={(p) => `?page=${p}`} component={Link} />
|
|
35
|
+
*/
|
|
36
|
+
export type PaginationProps = CommonProps &
|
|
37
|
+
NavProps &
|
|
38
|
+
(
|
|
39
|
+
| { onChange(newPage: number): void; makeUrl?: never; component?: never }
|
|
40
|
+
| {
|
|
41
|
+
makeUrl(page: number): string
|
|
42
|
+
onChange?: never
|
|
43
|
+
/**
|
|
44
|
+
* The component used for link elements. Receives `href`, `className`, and `children`.
|
|
45
|
+
* @default 'a'
|
|
46
|
+
*/
|
|
47
|
+
component?: React.ElementType<LinkComponentProps>
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
export default function Pagination({
|
|
52
|
+
page,
|
|
53
|
+
pageCount,
|
|
54
|
+
pageRangeDisplayed,
|
|
55
|
+
onChange,
|
|
56
|
+
makeUrl,
|
|
57
|
+
component: LinkComponent = 'a',
|
|
58
|
+
className,
|
|
59
|
+
...navProps
|
|
60
|
+
}: PaginationProps) {
|
|
61
|
+
'use memo'
|
|
62
|
+
const window = usePaginationWindow(page, pageCount, pageRangeDisplayed)
|
|
63
|
+
const isLinkMode = makeUrl !== undefined
|
|
64
|
+
|
|
65
|
+
// 'use memo' により React Compiler が自動でメモ化するため useCallback は不要
|
|
66
|
+
const makeClickHandler = (value: number) => () => onChange?.(value)
|
|
67
|
+
|
|
68
|
+
const classNames = useClassNames('charcoal-pagination', className)
|
|
69
|
+
|
|
70
|
+
const NavButton = ({ direction }: { direction: 'prev' | 'next' }) => {
|
|
71
|
+
const isPrev = direction === 'prev'
|
|
72
|
+
const targetPage = isPrev
|
|
73
|
+
? Math.max(1, page - 1)
|
|
74
|
+
: Math.min(pageCount, page + 1)
|
|
75
|
+
const disabled = isPrev ? page <= 1 : page >= pageCount
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<IconButton
|
|
79
|
+
icon={isPrev ? '24/Prev' : '24/Next'}
|
|
80
|
+
size="M"
|
|
81
|
+
className="charcoal-pagination-button"
|
|
82
|
+
hidden={disabled}
|
|
83
|
+
{...(isLinkMode && makeUrl
|
|
84
|
+
? {
|
|
85
|
+
component: LinkComponent as 'a',
|
|
86
|
+
href: makeUrl(targetPage),
|
|
87
|
+
'aria-disabled': disabled,
|
|
88
|
+
}
|
|
89
|
+
: {
|
|
90
|
+
disabled,
|
|
91
|
+
onClick: makeClickHandler(targetPage),
|
|
92
|
+
})}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const PageItem = ({ value }: { value: number | string }) => {
|
|
98
|
+
// 省略記号
|
|
99
|
+
if (value === '...') {
|
|
100
|
+
return (
|
|
101
|
+
<IconButton
|
|
102
|
+
icon="24/Dot"
|
|
103
|
+
size="M"
|
|
104
|
+
disabled
|
|
105
|
+
className="charcoal-pagination-button charcoal-pagination-spacer"
|
|
106
|
+
aria-hidden
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
// 現在ページ(クリック不可)
|
|
111
|
+
if (value === page) {
|
|
112
|
+
return (
|
|
113
|
+
<span className="charcoal-pagination-button" aria-current="page">
|
|
114
|
+
{value}
|
|
115
|
+
</span>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (typeof value !== 'number') return null
|
|
120
|
+
|
|
121
|
+
// リンクモード: ページへのリンク
|
|
122
|
+
if (isLinkMode && makeUrl) {
|
|
123
|
+
return (
|
|
124
|
+
<LinkComponent
|
|
125
|
+
href={makeUrl(value)}
|
|
126
|
+
className="charcoal-pagination-button"
|
|
127
|
+
>
|
|
128
|
+
{value}
|
|
129
|
+
</LinkComponent>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
// ボタンモード: クリックでページ遷移
|
|
133
|
+
return (
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
className="charcoal-pagination-button"
|
|
137
|
+
onClick={makeClickHandler(value)}
|
|
138
|
+
>
|
|
139
|
+
{value}
|
|
140
|
+
</button>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<nav {...navProps} className={classNames} aria-label="Pagination">
|
|
146
|
+
<NavButton direction="prev" />
|
|
147
|
+
{window.map((p) => (
|
|
148
|
+
<PageItem key={p} value={p} />
|
|
149
|
+
))}
|
|
150
|
+
<NavButton direction="next" />
|
|
151
|
+
</nav>
|
|
152
|
+
)
|
|
153
|
+
}
|