@charcoal-ui/react 5.4.4 → 5.5.0-beta.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/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 +16 -0
- package/dist/components/Pagination/index.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/components/Pagination/Pagination.story.tsx +57 -0
- package/src/components/Pagination/helper.ts +70 -0
- package/src/components/Pagination/index.css +67 -0
- package/src/components/Pagination/index.tsx +158 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../src/components/Pagination/helper.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,kBAAkB,SAAI,sBA+DvB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import './index.css';
|
|
2
|
+
interface CommonProps {
|
|
3
|
+
page: number;
|
|
4
|
+
pageCount: number;
|
|
5
|
+
pageRangeDisplayed?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface PaginationProps extends CommonProps {
|
|
8
|
+
onChange(newPage: number): void;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: import("react").NamedExoticComponent<PaginationProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, "ref">, "onChange">>;
|
|
11
|
+
export default _default;
|
|
12
|
+
export interface LinkPaginationProps extends CommonProps {
|
|
13
|
+
makeUrl(page: number): string;
|
|
14
|
+
}
|
|
15
|
+
export declare function LinkPagination({ page, pageCount, pageRangeDisplayed, makeUrl, className, ...props }: LinkPaginationProps & React.ComponentPropsWithoutRef<'nav'>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Pagination/index.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AASpB,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;;AAED,wBA0EE;AAEF,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,SAAS,EACT,kBAAkB,EAClB,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EACT,EAAE,mBAAmB,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,2CAmD7D"}
|
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.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
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/icons": "5.
|
|
59
|
-
"@charcoal-ui/
|
|
60
|
-
"@charcoal-ui/
|
|
57
|
+
"@charcoal-ui/foundation": "5.5.0-beta.0",
|
|
58
|
+
"@charcoal-ui/icons": "5.5.0-beta.0",
|
|
59
|
+
"@charcoal-ui/utils": "5.5.0-beta.0",
|
|
60
|
+
"@charcoal-ui/theme": "5.5.0-beta.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"react": ">=17.0.0"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5'
|
|
3
|
+
import Pagination, { LinkPagination } from '.'
|
|
4
|
+
|
|
5
|
+
function PaginationWithState(args: React.ComponentProps<typeof Pagination>) {
|
|
6
|
+
const [page, setPage] = useState(args.page)
|
|
7
|
+
return <Pagination {...args} page={page} onChange={setPage} />
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
title: 'react/Pagination',
|
|
12
|
+
component: Pagination,
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
},
|
|
16
|
+
} satisfies Meta<typeof Pagination>
|
|
17
|
+
|
|
18
|
+
export const Default: StoryObj<typeof Pagination> = {
|
|
19
|
+
args: {
|
|
20
|
+
page: 5,
|
|
21
|
+
pageCount: 10,
|
|
22
|
+
},
|
|
23
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const FirstPage: StoryObj<typeof Pagination> = {
|
|
27
|
+
args: {
|
|
28
|
+
page: 1,
|
|
29
|
+
pageCount: 10,
|
|
30
|
+
},
|
|
31
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const LastPage: StoryObj<typeof Pagination> = {
|
|
35
|
+
args: {
|
|
36
|
+
page: 10,
|
|
37
|
+
pageCount: 10,
|
|
38
|
+
},
|
|
39
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const ManyPages: StoryObj<typeof Pagination> = {
|
|
43
|
+
args: {
|
|
44
|
+
page: 50,
|
|
45
|
+
pageCount: 103,
|
|
46
|
+
},
|
|
47
|
+
render: (args) => <PaginationWithState {...args} />,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const LinkPaginationStory: StoryObj<typeof LinkPagination> = {
|
|
51
|
+
args: {
|
|
52
|
+
page: 5,
|
|
53
|
+
pageCount: 10,
|
|
54
|
+
makeUrl: (p) => `#page-${p}`,
|
|
55
|
+
},
|
|
56
|
+
render: (args) => <LinkPagination {...args} />,
|
|
57
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useDebugValue, useMemo } from 'react'
|
|
2
|
+
import warning from 'warning'
|
|
3
|
+
|
|
4
|
+
export function usePagerWindow(
|
|
5
|
+
page: number,
|
|
6
|
+
pageCount: number,
|
|
7
|
+
pageRangeDisplayed = 7,
|
|
8
|
+
) {
|
|
9
|
+
// ページャーのリンク生成例:
|
|
10
|
+
//
|
|
11
|
+
// < [ 1 ] [*2*] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] >
|
|
12
|
+
//
|
|
13
|
+
// < [ 1 ] [ 2 ] [ 3 ] [*4*] [ 5 ] [ 6 ] [ 7 ] >
|
|
14
|
+
//
|
|
15
|
+
// < [ 1 ] ... [ 4 ] [*5*] [ 6 ] [ 7 ] [ 8 ] >
|
|
16
|
+
//
|
|
17
|
+
// < [ 1 ] ... [ 99 ] [*100*] [ 101 ] [ 102 ] [ 103 ] >
|
|
18
|
+
//
|
|
19
|
+
// < [ 1 ] ... [ 99 ] [ 100 ] [ 101 ] [ 102 ] [*103*]
|
|
20
|
+
//
|
|
21
|
+
// [*1*] [ 2 ] >
|
|
22
|
+
//
|
|
23
|
+
// デザインの意図: 前後移動時のカーソル移動を最小限にする。
|
|
24
|
+
|
|
25
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
26
|
+
warning((page | 0) === page, `\`page\` must be integer (${page})`)
|
|
27
|
+
warning(
|
|
28
|
+
(pageCount | 0) === pageCount,
|
|
29
|
+
`\`pageCount\` must be integer (${pageCount})`,
|
|
30
|
+
)
|
|
31
|
+
warning(
|
|
32
|
+
(pageRangeDisplayed | 0) === pageRangeDisplayed,
|
|
33
|
+
`\`pageRangeDisplayed\` must be integer (${pageRangeDisplayed})`,
|
|
34
|
+
)
|
|
35
|
+
warning(pageRangeDisplayed > 2, `\`windowSize\` must be greater than 2`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const window = useMemo(() => {
|
|
39
|
+
const visibleFirstPage = 1
|
|
40
|
+
const visibleLastPage = Math.min(
|
|
41
|
+
pageCount,
|
|
42
|
+
Math.max(page + Math.floor(pageRangeDisplayed / 2), pageRangeDisplayed),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (visibleLastPage <= pageRangeDisplayed) {
|
|
46
|
+
// 表示範囲が1-7ページなら省略は無い。
|
|
47
|
+
return Array.from(
|
|
48
|
+
{ length: 1 + visibleLastPage - visibleFirstPage },
|
|
49
|
+
(_, i) => visibleFirstPage + i,
|
|
50
|
+
)
|
|
51
|
+
} else {
|
|
52
|
+
const start = visibleLastPage - (pageRangeDisplayed - 1) + 2
|
|
53
|
+
return [
|
|
54
|
+
// 表示範囲が1-7ページを超えるなら、
|
|
55
|
+
// - 1ページ目は固定で表示する
|
|
56
|
+
visibleFirstPage,
|
|
57
|
+
// - 2ページ目から現在のページの直前までは省略する
|
|
58
|
+
'...' as const,
|
|
59
|
+
...Array.from(
|
|
60
|
+
{ length: 1 + visibleLastPage - start },
|
|
61
|
+
(_, i) => start + i,
|
|
62
|
+
),
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}, [page, pageCount, pageRangeDisplayed])
|
|
66
|
+
|
|
67
|
+
useDebugValue(window)
|
|
68
|
+
|
|
69
|
+
return window
|
|
70
|
+
}
|
|
@@ -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,158 @@
|
|
|
1
|
+
import './index.css'
|
|
2
|
+
|
|
3
|
+
import { memo, useCallback } from 'react'
|
|
4
|
+
import { usePagerWindow } from './helper'
|
|
5
|
+
import { useClassNames } from '../../_lib/useClassNames'
|
|
6
|
+
import IconButton from '../IconButton'
|
|
7
|
+
|
|
8
|
+
const Text = 'span'
|
|
9
|
+
|
|
10
|
+
interface CommonProps {
|
|
11
|
+
page: number
|
|
12
|
+
pageCount: number
|
|
13
|
+
pageRangeDisplayed?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PaginationProps extends CommonProps {
|
|
17
|
+
onChange(newPage: number): void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default memo(function Pagination({
|
|
21
|
+
page,
|
|
22
|
+
pageCount,
|
|
23
|
+
pageRangeDisplayed,
|
|
24
|
+
onChange,
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: PaginationProps & Omit<React.ComponentPropsWithoutRef<'nav'>, 'onChange'>) {
|
|
28
|
+
const window = usePagerWindow(page, pageCount, pageRangeDisplayed)
|
|
29
|
+
const makeClickHandler = useCallback(
|
|
30
|
+
(value: number) => () => {
|
|
31
|
+
onChange(value)
|
|
32
|
+
},
|
|
33
|
+
[onChange],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const hasNext = page < pageCount
|
|
37
|
+
const hasPrev = page > 1
|
|
38
|
+
const classNames = useClassNames('charcoal-pagination', className)
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<nav {...props} className={classNames}>
|
|
42
|
+
<IconButton
|
|
43
|
+
icon="24/Prev"
|
|
44
|
+
size="M"
|
|
45
|
+
className="charcoal-pagination-button"
|
|
46
|
+
data-no-background
|
|
47
|
+
hidden={!hasPrev}
|
|
48
|
+
disabled={!hasPrev}
|
|
49
|
+
onClick={makeClickHandler(Math.max(1, page - 1))}
|
|
50
|
+
/>
|
|
51
|
+
{window.map((p) =>
|
|
52
|
+
p === '...' ? (
|
|
53
|
+
<IconButton
|
|
54
|
+
key={p}
|
|
55
|
+
icon="24/Dot"
|
|
56
|
+
size="M"
|
|
57
|
+
disabled
|
|
58
|
+
className="charcoal-pagination-button charcoal-pagination-spacer"
|
|
59
|
+
aria-hidden
|
|
60
|
+
/>
|
|
61
|
+
) : p === page ? (
|
|
62
|
+
// we remove the onClick but don't mark it as disabled to preserve keyboard focus
|
|
63
|
+
// not doing so causes the focus ring to flicker in and out of existence
|
|
64
|
+
<button
|
|
65
|
+
key={p}
|
|
66
|
+
type="button"
|
|
67
|
+
className="charcoal-pagination-button"
|
|
68
|
+
aria-current
|
|
69
|
+
>
|
|
70
|
+
<Text>{p}</Text>
|
|
71
|
+
</button>
|
|
72
|
+
) : (
|
|
73
|
+
<button
|
|
74
|
+
key={p}
|
|
75
|
+
type="button"
|
|
76
|
+
className="charcoal-pagination-button"
|
|
77
|
+
onClick={makeClickHandler(p)}
|
|
78
|
+
>
|
|
79
|
+
<Text>{p}</Text>
|
|
80
|
+
</button>
|
|
81
|
+
),
|
|
82
|
+
)}
|
|
83
|
+
<IconButton
|
|
84
|
+
icon="24/Next"
|
|
85
|
+
size="M"
|
|
86
|
+
className="charcoal-pagination-button"
|
|
87
|
+
data-no-background
|
|
88
|
+
hidden={!hasNext}
|
|
89
|
+
disabled={!hasNext}
|
|
90
|
+
onClick={makeClickHandler(Math.min(pageCount, page + 1))}
|
|
91
|
+
/>
|
|
92
|
+
</nav>
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
export interface LinkPaginationProps extends CommonProps {
|
|
97
|
+
makeUrl(page: number): string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function LinkPagination({
|
|
101
|
+
page,
|
|
102
|
+
pageCount,
|
|
103
|
+
pageRangeDisplayed,
|
|
104
|
+
makeUrl,
|
|
105
|
+
className,
|
|
106
|
+
...props
|
|
107
|
+
}: LinkPaginationProps & React.ComponentPropsWithoutRef<'nav'>) {
|
|
108
|
+
const window = usePagerWindow(page, pageCount, pageRangeDisplayed)
|
|
109
|
+
|
|
110
|
+
const hasNext = page < pageCount
|
|
111
|
+
const hasPrev = page > 1
|
|
112
|
+
const classNames = useClassNames('charcoal-pagination', className)
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<nav {...props} className={classNames}>
|
|
116
|
+
<IconButton
|
|
117
|
+
icon="24/Prev"
|
|
118
|
+
size="M"
|
|
119
|
+
component="a"
|
|
120
|
+
href={makeUrl(Math.max(1, page - 1))}
|
|
121
|
+
className="charcoal-pagination-button"
|
|
122
|
+
data-no-background
|
|
123
|
+
hidden={!hasPrev}
|
|
124
|
+
aria-disabled={!hasPrev}
|
|
125
|
+
/>
|
|
126
|
+
{window.map((p) =>
|
|
127
|
+
p === '...' ? (
|
|
128
|
+
<IconButton
|
|
129
|
+
key={p}
|
|
130
|
+
icon="24/Dot"
|
|
131
|
+
size="M"
|
|
132
|
+
disabled
|
|
133
|
+
className="charcoal-pagination-button charcoal-pagination-spacer"
|
|
134
|
+
aria-hidden
|
|
135
|
+
/>
|
|
136
|
+
) : p === page ? (
|
|
137
|
+
<span key={p} className="charcoal-pagination-button" aria-current>
|
|
138
|
+
<Text>{p}</Text>
|
|
139
|
+
</span>
|
|
140
|
+
) : (
|
|
141
|
+
<a key={p} href={makeUrl(p)} className="charcoal-pagination-button">
|
|
142
|
+
<Text>{p}</Text>
|
|
143
|
+
</a>
|
|
144
|
+
),
|
|
145
|
+
)}
|
|
146
|
+
<IconButton
|
|
147
|
+
icon="24/Next"
|
|
148
|
+
size="M"
|
|
149
|
+
component="a"
|
|
150
|
+
href={makeUrl(Math.min(pageCount, page + 1))}
|
|
151
|
+
className="charcoal-pagination-button"
|
|
152
|
+
data-no-background
|
|
153
|
+
hidden={!hasNext}
|
|
154
|
+
aria-disabled={!hasNext}
|
|
155
|
+
/>
|
|
156
|
+
</nav>
|
|
157
|
+
)
|
|
158
|
+
}
|