@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.
@@ -0,0 +1,2 @@
1
+ export declare function usePagerWindow(page: number, pageCount: number, pageRangeDisplayed?: number): (number | "...")[];
2
+ //# sourceMappingURL=helper.d.ts.map
@@ -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.4.4",
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.4.4",
58
- "@charcoal-ui/icons": "5.4.4",
59
- "@charcoal-ui/theme": "5.4.4",
60
- "@charcoal-ui/utils": "5.4.4"
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
+ }