@instructure/ui-pagination 8.13.1-snapshot.9 → 8.14.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/CHANGELOG.md +7 -0
- package/es/Pagination/PaginationArrowButton/index.js +36 -4
- package/es/Pagination/PaginationArrowButton/props.js +6 -3
- package/es/Pagination/PaginationButton/props.js +9 -2
- package/es/Pagination/PaginationLocator.js +4 -0
- package/es/Pagination/PaginationPageInput/PaginationPageInputLocator.js +35 -0
- package/es/Pagination/PaginationPageInput/index.js +208 -0
- package/es/Pagination/PaginationPageInput/props.js +35 -0
- package/es/Pagination/PaginationPageInput/styles.js +55 -0
- package/es/Pagination/PaginationPageInput/theme.js +40 -0
- package/es/Pagination/index.js +178 -52
- package/es/Pagination/props.js +32 -2
- package/lib/Pagination/PaginationArrowButton/index.js +39 -3
- package/lib/Pagination/PaginationArrowButton/props.js +6 -3
- package/lib/Pagination/PaginationButton/props.js +9 -2
- package/lib/Pagination/PaginationLocator.js +5 -0
- package/lib/Pagination/PaginationPageInput/PaginationPageInputLocator.js +46 -0
- package/lib/Pagination/PaginationPageInput/index.js +202 -0
- package/lib/Pagination/PaginationPageInput/props.js +46 -0
- package/lib/Pagination/PaginationPageInput/styles.js +63 -0
- package/lib/Pagination/PaginationPageInput/theme.js +48 -0
- package/lib/Pagination/index.js +179 -52
- package/lib/Pagination/props.js +32 -2
- package/package.json +23 -22
- package/src/Pagination/PaginationArrowButton/index.tsx +32 -4
- package/src/Pagination/PaginationArrowButton/props.ts +22 -6
- package/src/Pagination/PaginationButton/props.ts +18 -2
- package/src/Pagination/PaginationLocator.ts +5 -1
- package/src/Pagination/PaginationPageInput/PaginationPageInputLocator.ts +42 -0
- package/src/Pagination/PaginationPageInput/index.tsx +242 -0
- package/src/Pagination/PaginationPageInput/props.ts +112 -0
- package/src/Pagination/PaginationPageInput/styles.ts +60 -0
- package/src/Pagination/PaginationPageInput/theme.ts +46 -0
- package/src/Pagination/README.md +116 -0
- package/src/Pagination/index.tsx +213 -80
- package/src/Pagination/props.ts +53 -4
- package/tsconfig.build.json +26 -2
- package/tsconfig.build.tsbuildinfo +1 -0
- package/types/Pagination/PaginationArrowButton/PaginationArrowButtonLocator.d.ts +57 -56
- package/types/Pagination/PaginationArrowButton/PaginationArrowButtonLocator.d.ts.map +1 -1
- package/types/Pagination/PaginationArrowButton/index.d.ts +9 -4
- package/types/Pagination/PaginationArrowButton/index.d.ts.map +1 -1
- package/types/Pagination/PaginationArrowButton/props.d.ts +5 -3
- package/types/Pagination/PaginationArrowButton/props.d.ts.map +1 -1
- package/types/Pagination/PaginationButton/index.d.ts +2 -0
- package/types/Pagination/PaginationButton/index.d.ts.map +1 -1
- package/types/Pagination/PaginationButton/props.d.ts +1 -0
- package/types/Pagination/PaginationButton/props.d.ts.map +1 -1
- package/types/Pagination/PaginationLocator.d.ts +944 -234
- package/types/Pagination/PaginationLocator.d.ts.map +1 -1
- package/types/Pagination/PaginationPageInput/PaginationPageInputLocator.d.ts +581 -0
- package/types/Pagination/PaginationPageInput/PaginationPageInputLocator.d.ts.map +1 -0
- package/types/Pagination/PaginationPageInput/index.d.ts +63 -0
- package/types/Pagination/PaginationPageInput/index.d.ts.map +1 -0
- package/types/Pagination/PaginationPageInput/props.d.ts +46 -0
- package/types/Pagination/PaginationPageInput/props.d.ts.map +1 -0
- package/types/Pagination/PaginationPageInput/styles.d.ts +15 -0
- package/types/Pagination/PaginationPageInput/styles.d.ts.map +1 -0
- package/types/Pagination/PaginationPageInput/theme.d.ts +10 -0
- package/types/Pagination/PaginationPageInput/theme.d.ts.map +1 -0
- package/types/Pagination/index.d.ts +50 -14
- package/types/Pagination/index.d.ts.map +1 -1
- package/types/Pagination/props.d.ts +13 -3
- package/types/Pagination/props.d.ts.map +1 -1
- package/LICENSE.md +0 -27
package/src/Pagination/index.tsx
CHANGED
|
@@ -34,32 +34,42 @@ import { withStyle, jsx } from '@instructure/emotion'
|
|
|
34
34
|
|
|
35
35
|
import { PaginationButton } from './PaginationButton'
|
|
36
36
|
import { PaginationArrowButton } from './PaginationArrowButton'
|
|
37
|
+
import { PaginationPageInput } from './PaginationPageInput'
|
|
37
38
|
|
|
38
39
|
import generateStyle from './styles'
|
|
39
40
|
|
|
41
|
+
import type { PaginationPageProps } from './PaginationButton/props'
|
|
42
|
+
import type { PaginationArrowDirections } from './PaginationArrowButton/props'
|
|
43
|
+
|
|
40
44
|
import { propTypes, allowedProps } from './props'
|
|
41
|
-
import type { PaginationProps } from './props'
|
|
45
|
+
import type { PaginationProps, PaginationSnapshot } from './props'
|
|
42
46
|
|
|
43
47
|
/** This is an [].findIndex optimized to work on really big, but sparse, arrays */
|
|
44
48
|
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'arr' implicitly has an 'any' type.
|
|
45
49
|
const fastFindIndex = (arr, fn) =>
|
|
46
50
|
Number(Object.keys(arr).find((k) => fn(arr[Number(k)])))
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const childrenArray = (props: PaginationProps) => {
|
|
53
|
+
const { children } = props
|
|
54
|
+
|
|
55
|
+
if (!children) {
|
|
56
|
+
return []
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Array.isArray(children)
|
|
60
|
+
? (children as React.ReactElement<PaginationPageProps>[])
|
|
61
|
+
: ([children] as React.ReactElement<PaginationPageProps>[])
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return propsHaveCompactView(props) && currentPageIndex > 0
|
|
64
|
+
function propsHaveCompactView(props: PaginationProps) {
|
|
65
|
+
return props.variant === 'compact' && childrenArray(props).length > 5
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
68
|
+
type ArrowConfig = {
|
|
69
|
+
pageIndex: number
|
|
70
|
+
label: string
|
|
71
|
+
shouldEnableIcon: boolean
|
|
72
|
+
handleButtonRef: (el: HTMLButtonElement) => void
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/**
|
|
@@ -67,7 +77,6 @@ function shouldShowNextButton(props, currentPageIndex) {
|
|
|
67
77
|
category: components
|
|
68
78
|
---
|
|
69
79
|
**/
|
|
70
|
-
|
|
71
80
|
@withStyle(generateStyle, null)
|
|
72
81
|
@testable()
|
|
73
82
|
class Pagination extends Component<PaginationProps> {
|
|
@@ -78,31 +87,41 @@ class Pagination extends Component<PaginationProps> {
|
|
|
78
87
|
static defaultProps = {
|
|
79
88
|
children: null,
|
|
80
89
|
disabled: false,
|
|
90
|
+
withFirstAndLastButton: false,
|
|
91
|
+
showDisabledButtons: false,
|
|
81
92
|
variant: 'full',
|
|
82
93
|
as: 'div',
|
|
83
94
|
// @ts-expect-error ts-migrate(6133) FIXME: 'el' is declared but its value is never read.
|
|
84
95
|
elementRef: (el) => {},
|
|
96
|
+
labelNumberInput: (numberOfPages: number) => `of ${numberOfPages}`,
|
|
97
|
+
screenReaderLabelNumberInput: (
|
|
98
|
+
currentPage: number,
|
|
99
|
+
numberOfPages: number
|
|
100
|
+
) => `Select page (${currentPage} of ${numberOfPages})`,
|
|
85
101
|
shouldHandleFocus: true
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
static Page = PaginationButton
|
|
89
105
|
static Navigation = PaginationArrowButton
|
|
90
106
|
|
|
107
|
+
_labelId: string
|
|
108
|
+
|
|
91
109
|
ref: Element | null = null
|
|
110
|
+
_inputRef: Element | null = null
|
|
111
|
+
|
|
112
|
+
_firstButton: HTMLButtonElement | null = null
|
|
113
|
+
_prevButton: HTMLButtonElement | null = null
|
|
114
|
+
_nextButton: HTMLButtonElement | null = null
|
|
115
|
+
_lastButton: HTMLButtonElement | null = null
|
|
92
116
|
|
|
93
117
|
// @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' ty... Remove this comment to see the full error message
|
|
94
118
|
constructor(...args) {
|
|
95
119
|
// @ts-expect-error ts-migrate(2556) FIXME: Expected 1-2 arguments, but got 0 or more.
|
|
96
120
|
super(...args)
|
|
97
121
|
|
|
98
|
-
// @ts-expect-error ts-migrate(2339) FIXME: Property '_labelId' does not exist on type 'Pagina... Remove this comment to see the full error message
|
|
99
122
|
this._labelId = uid('Pagination')
|
|
100
|
-
|
|
101
|
-
// @ts-expect-error ts-migrate(2339) FIXME: Property '_prevButton' does not exist on type 'Pag... Remove this comment to see the full error message
|
|
102
|
-
this._prevButton = null
|
|
103
|
-
// @ts-expect-error ts-migrate(2339) FIXME: Property '_nextButton' does not exist on type 'Pag... Remove this comment to see the full error message
|
|
104
|
-
this._nextButton = null
|
|
105
123
|
}
|
|
124
|
+
|
|
106
125
|
get _root() {
|
|
107
126
|
console.warn(
|
|
108
127
|
'_root property is deprecated and will be removed in v9, please use ref instead'
|
|
@@ -111,40 +130,80 @@ class Pagination extends Component<PaginationProps> {
|
|
|
111
130
|
return this.ref
|
|
112
131
|
}
|
|
113
132
|
|
|
114
|
-
|
|
133
|
+
get inputMode() {
|
|
134
|
+
return this.props.variant === 'input'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get childPages() {
|
|
138
|
+
return childrenArray(this.props)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get withFirstAndLastButton() {
|
|
142
|
+
return this.inputMode || this.props.withFirstAndLastButton
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get showDisabledButtons() {
|
|
146
|
+
return this.inputMode || this.props.showDisabledButtons
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getSnapshotBeforeUpdate(): PaginationSnapshot {
|
|
115
150
|
const activeElement = getActiveElement()
|
|
116
151
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
if (
|
|
153
|
+
activeElement === this._firstButton ||
|
|
154
|
+
activeElement === this._prevButton ||
|
|
155
|
+
activeElement === this._nextButton ||
|
|
156
|
+
activeElement === this._lastButton
|
|
157
|
+
) {
|
|
158
|
+
return { lastFocusedButton: activeElement as HTMLButtonElement }
|
|
159
|
+
} else {
|
|
160
|
+
return { lastFocusedButton: undefined }
|
|
122
161
|
}
|
|
123
162
|
}
|
|
124
163
|
|
|
125
164
|
componentDidMount() {
|
|
126
|
-
|
|
127
|
-
this.props.makeStyles()
|
|
165
|
+
this.props.makeStyles?.()
|
|
128
166
|
}
|
|
129
167
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
168
|
+
componentDidUpdate(
|
|
169
|
+
prevProps: PaginationProps,
|
|
170
|
+
_prevState: unknown,
|
|
171
|
+
snapshot: PaginationSnapshot
|
|
172
|
+
) {
|
|
173
|
+
this.props.makeStyles?.()
|
|
174
|
+
|
|
134
175
|
if (
|
|
135
176
|
!this.props.shouldHandleFocus ||
|
|
136
177
|
(!propsHaveCompactView(prevProps) && !propsHaveCompactView(this.props))
|
|
137
178
|
) {
|
|
138
179
|
return
|
|
180
|
+
} else {
|
|
181
|
+
this.focusElementAfterUpdate(snapshot)
|
|
139
182
|
}
|
|
183
|
+
}
|
|
140
184
|
|
|
141
|
-
|
|
185
|
+
focusElementAfterUpdate(snapshot: PaginationSnapshot) {
|
|
186
|
+
const { lastFocusedButton } = snapshot
|
|
142
187
|
|
|
143
|
-
if (
|
|
188
|
+
if (lastFocusedButton) {
|
|
144
189
|
const focusable = findTabbable(this.ref)
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
190
|
+
const direction = lastFocusedButton.dataset.direction
|
|
191
|
+
|
|
192
|
+
// By default we want to focus the previously focused button
|
|
193
|
+
let nextFocusElement: Element = lastFocusedButton
|
|
194
|
+
|
|
195
|
+
// In case the button is not focusable anymore
|
|
196
|
+
// (disabled or not in the DOM), we focus to the next available page
|
|
197
|
+
if (!focusable.includes(nextFocusElement)) {
|
|
198
|
+
if (direction === 'first' || direction === 'prev') {
|
|
199
|
+
nextFocusElement = focusable[0]
|
|
200
|
+
}
|
|
201
|
+
if (direction === 'next' || direction === 'last') {
|
|
202
|
+
nextFocusElement = focusable[focusable.length - 1]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
;(nextFocusElement as HTMLElement).focus()
|
|
148
207
|
}
|
|
149
208
|
}
|
|
150
209
|
|
|
@@ -152,17 +211,15 @@ class Pagination extends Component<PaginationProps> {
|
|
|
152
211
|
return propsHaveCompactView(this.props)
|
|
153
212
|
}
|
|
154
213
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return this.props.disabled
|
|
214
|
+
transferDisabledPropToChildren(children: PaginationProps['children']) {
|
|
215
|
+
return children && this.props.disabled
|
|
158
216
|
? React.Children.map(children, (page) =>
|
|
159
217
|
React.cloneElement(page, { disabled: this.props.disabled })
|
|
160
218
|
)
|
|
161
219
|
: children
|
|
162
220
|
}
|
|
163
221
|
|
|
164
|
-
|
|
165
|
-
handleElementRef = (el) => {
|
|
222
|
+
handleElementRef = (el: Element | null) => {
|
|
166
223
|
this.ref = el
|
|
167
224
|
if (el) {
|
|
168
225
|
if (typeof this.props.elementRef === 'function') {
|
|
@@ -171,8 +228,12 @@ class Pagination extends Component<PaginationProps> {
|
|
|
171
228
|
}
|
|
172
229
|
}
|
|
173
230
|
|
|
231
|
+
handleInputRef = (el: Element | null) => {
|
|
232
|
+
this._inputRef = el
|
|
233
|
+
}
|
|
234
|
+
|
|
174
235
|
renderLabel() {
|
|
175
|
-
const display = this.props.variant === '
|
|
236
|
+
const display = this.props.variant === 'full' ? 'inline-block' : 'block'
|
|
176
237
|
const visibleLabel = hasVisibleChildren(this.props.label)
|
|
177
238
|
|
|
178
239
|
return (
|
|
@@ -180,7 +241,6 @@ class Pagination extends Component<PaginationProps> {
|
|
|
180
241
|
as="span"
|
|
181
242
|
padding={visibleLabel ? 'small' : '0'}
|
|
182
243
|
display={visibleLabel ? display : 'auto'}
|
|
183
|
-
// @ts-expect-error ts-migrate(2339) FIXME: Property '_labelId' does not exist on type 'Pagina... Remove this comment to see the full error message
|
|
184
244
|
id={this._labelId}
|
|
185
245
|
>
|
|
186
246
|
{this.props.label}
|
|
@@ -188,14 +248,36 @@ class Pagination extends Component<PaginationProps> {
|
|
|
188
248
|
)
|
|
189
249
|
}
|
|
190
250
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
251
|
+
renderPageInput(currentPageIndex: number) {
|
|
252
|
+
return (
|
|
253
|
+
<PaginationPageInput
|
|
254
|
+
numberOfPages={this.childPages.length}
|
|
255
|
+
currentPageIndex={currentPageIndex}
|
|
256
|
+
onChange={this.handleInputChange.bind(this)}
|
|
257
|
+
screenReaderLabel={this.props.screenReaderLabelNumberInput!}
|
|
258
|
+
label={this.props.labelNumberInput}
|
|
259
|
+
disabled={this.props.disabled}
|
|
260
|
+
inputRef={this.handleInputRef}
|
|
261
|
+
/>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
handleInputChange(
|
|
266
|
+
event:
|
|
267
|
+
| React.KeyboardEvent<HTMLInputElement>
|
|
268
|
+
| React.MouseEvent<HTMLButtonElement>
|
|
269
|
+
| React.FocusEvent<HTMLInputElement>,
|
|
270
|
+
pageIndex: number
|
|
271
|
+
) {
|
|
272
|
+
this.childPages[pageIndex].props.onClick?.(event)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
renderPages(currentPageIndex: number) {
|
|
276
|
+
const allPages = this.childPages
|
|
194
277
|
let visiblePages = allPages
|
|
195
278
|
|
|
196
279
|
if (this.compactView) {
|
|
197
280
|
const firstIndex = 0
|
|
198
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
199
281
|
const lastIndex = allPages.length - 1
|
|
200
282
|
|
|
201
283
|
const sliceStart = Math.min(
|
|
@@ -204,31 +286,24 @@ class Pagination extends Component<PaginationProps> {
|
|
|
204
286
|
)
|
|
205
287
|
const sliceEnd = Math.min(currentPageIndex + 4, lastIndex)
|
|
206
288
|
|
|
207
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
208
289
|
visiblePages = allPages.slice(sliceStart, sliceEnd)
|
|
209
290
|
|
|
210
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
211
291
|
const firstPage = allPages[firstIndex]
|
|
212
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
213
292
|
const lastPage = allPages[lastIndex]
|
|
214
293
|
|
|
215
294
|
if (sliceStart - firstIndex > 1)
|
|
216
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
217
295
|
visiblePages.unshift(
|
|
218
296
|
<span key="first" aria-hidden="true">
|
|
219
297
|
…
|
|
220
298
|
</span>
|
|
221
299
|
)
|
|
222
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
223
300
|
if (sliceStart - firstIndex > 0) visiblePages.unshift(firstPage)
|
|
224
301
|
if (lastIndex - sliceEnd + 1 > 1)
|
|
225
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
226
302
|
visiblePages.push(
|
|
227
303
|
<span key="last" aria-hidden="true">
|
|
228
304
|
…
|
|
229
305
|
</span>
|
|
230
306
|
)
|
|
231
|
-
// @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'.
|
|
232
307
|
if (lastIndex - sliceEnd + 1 > 0) visiblePages.push(lastPage)
|
|
233
308
|
}
|
|
234
309
|
|
|
@@ -239,33 +314,88 @@ class Pagination extends Component<PaginationProps> {
|
|
|
239
314
|
)
|
|
240
315
|
}
|
|
241
316
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
317
|
+
getArrowVariant(
|
|
318
|
+
direction: PaginationArrowDirections,
|
|
319
|
+
currentPageIndex: number,
|
|
320
|
+
pagesCount: number
|
|
321
|
+
): ArrowConfig {
|
|
322
|
+
switch (direction) {
|
|
323
|
+
case 'first':
|
|
324
|
+
return {
|
|
325
|
+
pageIndex: 0,
|
|
326
|
+
label: this.props.labelFirst || 'First Page',
|
|
327
|
+
shouldEnableIcon: currentPageIndex > 1,
|
|
328
|
+
handleButtonRef: (el) => {
|
|
329
|
+
this._firstButton = el
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
case 'prev':
|
|
333
|
+
return {
|
|
334
|
+
pageIndex: currentPageIndex - 1,
|
|
335
|
+
label: this.props.labelPrev || 'Previous Page',
|
|
336
|
+
shouldEnableIcon: currentPageIndex > 0,
|
|
337
|
+
handleButtonRef: (el) => {
|
|
338
|
+
this._prevButton = el
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
case 'next':
|
|
342
|
+
return {
|
|
343
|
+
pageIndex: currentPageIndex + 1,
|
|
344
|
+
label: this.props.labelNext || 'Next Page',
|
|
345
|
+
shouldEnableIcon: currentPageIndex < pagesCount - 1,
|
|
346
|
+
handleButtonRef: (el) => {
|
|
347
|
+
this._nextButton = el
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
case 'last':
|
|
351
|
+
return {
|
|
352
|
+
pageIndex: pagesCount - 1,
|
|
353
|
+
label: this.props.labelLast || 'Last Page',
|
|
354
|
+
shouldEnableIcon: currentPageIndex < pagesCount - 2,
|
|
355
|
+
handleButtonRef: (el) => {
|
|
356
|
+
this._lastButton = el
|
|
357
|
+
}
|
|
358
|
+
}
|
|
258
359
|
}
|
|
360
|
+
}
|
|
259
361
|
|
|
260
|
-
|
|
362
|
+
renderArrowButton(
|
|
363
|
+
direction: PaginationArrowDirections,
|
|
364
|
+
currentPageIndex: number
|
|
365
|
+
) {
|
|
366
|
+
const { childPages } = this
|
|
367
|
+
|
|
368
|
+
// We don't display the arrows in "compact" variant under 6 items
|
|
369
|
+
if (!(propsHaveCompactView(this.props) || this.inputMode)) {
|
|
370
|
+
return null
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (
|
|
374
|
+
!this.withFirstAndLastButton &&
|
|
375
|
+
(direction === 'first' || direction === 'last')
|
|
376
|
+
) {
|
|
377
|
+
return null
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const { pageIndex, label, shouldEnableIcon, handleButtonRef } =
|
|
381
|
+
this.getArrowVariant(direction, currentPageIndex, childPages.length)
|
|
382
|
+
|
|
383
|
+
const page = childPages[pageIndex]
|
|
384
|
+
|
|
385
|
+
const disabled =
|
|
386
|
+
page?.props?.disabled || this.props.disabled || !shouldEnableIcon
|
|
387
|
+
const onClick = page?.props?.onClick
|
|
388
|
+
|
|
389
|
+
return shouldEnableIcon || this.showDisabledButtons ? (
|
|
261
390
|
<PaginationArrowButton
|
|
262
|
-
direction={direction
|
|
391
|
+
direction={direction}
|
|
392
|
+
data-direction={direction}
|
|
263
393
|
label={label}
|
|
264
394
|
onClick={onClick}
|
|
265
395
|
disabled={disabled}
|
|
266
396
|
buttonRef={handleButtonRef}
|
|
267
397
|
/>
|
|
268
|
-
)
|
|
398
|
+
) : null
|
|
269
399
|
}
|
|
270
400
|
|
|
271
401
|
render() {
|
|
@@ -290,16 +420,19 @@ class Pagination extends Component<PaginationProps> {
|
|
|
290
420
|
elementRef={this.handleElementRef}
|
|
291
421
|
margin={this.props.margin}
|
|
292
422
|
css={this.props.styles?.pagination}
|
|
293
|
-
|
|
294
|
-
aria-labelledby={this.props.label && this._labelId}
|
|
423
|
+
aria-labelledby={this.props.label ? this._labelId : undefined}
|
|
295
424
|
>
|
|
296
425
|
{this.props.label && this.renderLabel()}
|
|
297
426
|
<View display="inline-block" css={this.props.styles?.pages}>
|
|
298
|
-
{
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
{
|
|
302
|
-
this.
|
|
427
|
+
{this.renderArrowButton('first', currentPageIndex)}
|
|
428
|
+
{this.renderArrowButton('prev', currentPageIndex)}
|
|
429
|
+
|
|
430
|
+
{this.inputMode
|
|
431
|
+
? this.renderPageInput(currentPageIndex)
|
|
432
|
+
: this.renderPages(currentPageIndex)}
|
|
433
|
+
|
|
434
|
+
{this.renderArrowButton('next', currentPageIndex)}
|
|
435
|
+
{this.renderArrowButton('last', currentPageIndex)}
|
|
303
436
|
</View>
|
|
304
437
|
</View>
|
|
305
438
|
)
|
package/src/Pagination/props.ts
CHANGED
|
@@ -40,14 +40,29 @@ import type {
|
|
|
40
40
|
OtherHTMLAttributes,
|
|
41
41
|
PropValidators
|
|
42
42
|
} from '@instructure/shared-types'
|
|
43
|
+
import type { PaginationPageProps } from './PaginationButton/props'
|
|
43
44
|
|
|
44
45
|
type PaginationOwnProps = {
|
|
45
|
-
|
|
46
|
+
// this won't filter the children completely well,
|
|
47
|
+
// it will allow simple elements (e.g. `<div>`) and components with compatible props
|
|
48
|
+
// TODO: find a better way to type Children.oneOf([PaginationButton])
|
|
49
|
+
children?:
|
|
50
|
+
| React.ReactElement<PaginationPageProps>
|
|
51
|
+
| React.ReactElement<PaginationPageProps>[]
|
|
46
52
|
disabled?: boolean
|
|
53
|
+
withFirstAndLastButton?: boolean
|
|
54
|
+
showDisabledButtons?: boolean
|
|
47
55
|
label?: React.ReactNode
|
|
48
56
|
labelNext?: string
|
|
49
57
|
labelPrev?: string
|
|
50
|
-
|
|
58
|
+
labelFirst?: string
|
|
59
|
+
labelLast?: string
|
|
60
|
+
labelNumberInput?: (numberOfPages: number) => React.ReactNode
|
|
61
|
+
screenReaderLabelNumberInput?: (
|
|
62
|
+
currentPage: number,
|
|
63
|
+
numberOfPages: number
|
|
64
|
+
) => string
|
|
65
|
+
variant?: 'full' | 'compact' | 'input'
|
|
51
66
|
margin?: Spacing
|
|
52
67
|
as?: AsElementType
|
|
53
68
|
elementRef?: (element: Element | null) => void
|
|
@@ -73,6 +88,14 @@ const propTypes: PropValidators<PropKeys> = {
|
|
|
73
88
|
* Disables interaction with all pages
|
|
74
89
|
*/
|
|
75
90
|
disabled: PropTypes.bool,
|
|
91
|
+
/**
|
|
92
|
+
* Displays "jump to first" and "jump to last" buttons. Always turned on with `input` variant.
|
|
93
|
+
*/
|
|
94
|
+
withFirstAndLastButton: PropTypes.bool,
|
|
95
|
+
/**
|
|
96
|
+
* Displays the unavailable navigation buttons as disabled instead of hiding them. Always turned on with `input` variant.
|
|
97
|
+
*/
|
|
98
|
+
showDisabledButtons: PropTypes.bool,
|
|
76
99
|
/**
|
|
77
100
|
* Visible label for component
|
|
78
101
|
*/
|
|
@@ -85,12 +108,28 @@ const propTypes: PropValidators<PropKeys> = {
|
|
|
85
108
|
* Accessible label for previous button
|
|
86
109
|
*/
|
|
87
110
|
labelPrev: PropTypes.string,
|
|
111
|
+
/**
|
|
112
|
+
* Accessible label for "jump to first" button
|
|
113
|
+
*/
|
|
114
|
+
labelFirst: PropTypes.string,
|
|
115
|
+
/**
|
|
116
|
+
* Accessible label for "jump to last" button
|
|
117
|
+
*/
|
|
118
|
+
labelLast: PropTypes.string,
|
|
119
|
+
/**
|
|
120
|
+
* Label for number input (for `input` variant)
|
|
121
|
+
*/
|
|
122
|
+
labelNumberInput: PropTypes.func,
|
|
123
|
+
/**
|
|
124
|
+
* ScreenReaderLabel for number input (for `input` variant)
|
|
125
|
+
*/
|
|
126
|
+
screenReaderLabelNumberInput: PropTypes.func,
|
|
88
127
|
/**
|
|
89
128
|
* The compact variant truncates the page navigation to show only the first,
|
|
90
129
|
* last, and pages immediately surrounding the current page. Fewer than 5 pages,
|
|
91
130
|
* no next/previous arrow buttons will be shown, and all pages will be listed
|
|
92
131
|
*/
|
|
93
|
-
variant: PropTypes.oneOf(['full', 'compact']),
|
|
132
|
+
variant: PropTypes.oneOf(['full', 'compact', 'input']),
|
|
94
133
|
/**
|
|
95
134
|
* Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
|
|
96
135
|
* `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
|
|
@@ -116,9 +155,15 @@ const propTypes: PropValidators<PropKeys> = {
|
|
|
116
155
|
const allowedProps: AllowedPropKeys = [
|
|
117
156
|
'children',
|
|
118
157
|
'disabled',
|
|
158
|
+
'withFirstAndLastButton',
|
|
159
|
+
'showDisabledButtons',
|
|
119
160
|
'label',
|
|
120
161
|
'labelNext',
|
|
121
162
|
'labelPrev',
|
|
163
|
+
'labelFirst',
|
|
164
|
+
'labelLast',
|
|
165
|
+
'labelNumberInput',
|
|
166
|
+
'screenReaderLabelNumberInput',
|
|
122
167
|
'variant',
|
|
123
168
|
'margin',
|
|
124
169
|
'as',
|
|
@@ -126,5 +171,9 @@ const allowedProps: AllowedPropKeys = [
|
|
|
126
171
|
'shouldHandleFocus'
|
|
127
172
|
]
|
|
128
173
|
|
|
129
|
-
|
|
174
|
+
type PaginationSnapshot = {
|
|
175
|
+
lastFocusedButton?: HTMLButtonElement
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export type { PaginationProps, PaginationStyle, PaginationSnapshot }
|
|
130
179
|
export { propTypes, allowedProps }
|
package/tsconfig.build.json
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": "../../tsconfig.build.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
-
"outDir": "./types"
|
|
4
|
+
"outDir": "./types",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"composite": true
|
|
5
7
|
},
|
|
6
|
-
"include": ["src
|
|
8
|
+
"include": ["src"],
|
|
9
|
+
"references": [
|
|
10
|
+
{ "path": "../ui-babel-preset/tsconfig.build.json" },
|
|
11
|
+
{ "path": "../ui-test-locator/tsconfig.build.json" },
|
|
12
|
+
{ "path": "../ui-test-queries/tsconfig.build.json" },
|
|
13
|
+
{ "path": "../ui-test-utils/tsconfig.build.json" },
|
|
14
|
+
{ "path": "../emotion/tsconfig.build.json" },
|
|
15
|
+
{ "path": "../shared-types/tsconfig.build.json" },
|
|
16
|
+
{ "path": "../ui-a11y-content/tsconfig.build.json" },
|
|
17
|
+
{ "path": "../ui-a11y-utils/tsconfig.build.json" },
|
|
18
|
+
{ "path": "../ui-buttons/tsconfig.build.json" },
|
|
19
|
+
{ "path": "../ui-dom-utils/tsconfig.build.json" },
|
|
20
|
+
{ "path": "../ui-icons/tsconfig.build.json" },
|
|
21
|
+
{ "path": "../ui-portal/tsconfig.build.json" },
|
|
22
|
+
{ "path": "../ui-prop-types/tsconfig.build.json" },
|
|
23
|
+
{ "path": "../ui-react-utils/tsconfig.build.json" },
|
|
24
|
+
{ "path": "../ui-testable/tsconfig.build.json" },
|
|
25
|
+
{ "path": "../ui-tooltip/tsconfig.build.json" },
|
|
26
|
+
{ "path": "../ui-utils/tsconfig.build.json" },
|
|
27
|
+
{ "path": "../ui-view/tsconfig.build.json" },
|
|
28
|
+
{ "path": "../uid/tsconfig.build.json" },
|
|
29
|
+
{ "path": "../ui-number-input/tsconfig.build.json" }
|
|
30
|
+
]
|
|
7
31
|
}
|