← All changes
|
js/components/common/ListTable/TableNavigation.tsx
+71
-11
4.0.0-beta.1
→
trunk
View file →
| @@ -48,11 +48,18 @@ | ||
| 48 | 48 | which: 'top' | 'bottom' |
| 49 | 49 | actions: ListTableAction<A>[] |
| 50 | 50 | selectedAction: A | undefined |
| 51 | 51 | setSelectedAction: Dispatch<SetStateAction<A | undefined>> |
| 52 | + label: string | |
| 52 | 53 | } |
| 53 | 54 | |
| 54 | -const BulkActionSelect = <A extends string>({ which, actions, selectedAction, setSelectedAction }: BulkActionSelectProps<A>) => | |
| 55 | +const BulkActionSelect = <A extends string>({ | |
| 56 | + which, | |
| 57 | + actions, | |
| 58 | + selectedAction, | |
| 59 | + setSelectedAction, | |
| 60 | + label | |
| 61 | +}: BulkActionSelectProps<A>) => | |
| 55 | 62 | <select |
| 56 | 63 | name={`action${'bottom' === which ? '-2' : ''}`} |
| 57 | 64 | id={`bulk-action-selector-${which}`} |
| 58 | 65 | value={selectedAction} |
| @@ -63,16 +70,17 @@ | ||
| 63 | 70 | setSelectedAction(value) |
| 64 | 71 | } |
| 65 | 72 | }} |
| 66 | 73 | > |
| 67 | - <option value="-1">{__('Bulk actions', 'code-snippets')}</option> | |
| 74 | + <option value="-1">{label}</option> | |
| 68 | 75 | <BulkActionSelectOptions actions={actions} /> |
| 69 | 76 | </select> |
| 70 | 77 | |
| 71 | 78 | interface BulkActionsProps<K extends Key, A extends string> extends Required<Pick<TableNavProps<K, A>, 'which' | 'actions' | 'doAction'>> { |
| 72 | - onActionSuccess?: () => void | |
| 79 | + onActionSuccess?: VoidFunction | |
| 73 | 80 | disabled?: boolean |
| 74 | 81 | selected: Set<K> |
| 82 | + selectLabel?: string | |
| 75 | 83 | } |
| 76 | 84 | |
| 77 | 85 | const BulkActions = function BulkActions<K extends Key, A extends string>({ |
| 78 | 86 | which, |
| @@ -79,9 +87,10 @@ | ||
| 79 | 87 | actions, |
| 80 | 88 | selected, |
| 81 | 89 | doAction, |
| 82 | 90 | onActionSuccess, |
| 83 | - disabled | |
| 91 | + disabled, | |
| 92 | + selectLabel | |
| 84 | 93 | }: BulkActionsProps<K, A>) { |
| 85 | 94 | const [selectedAction, setSelectedAction] = useState<A>() |
| 86 | 95 | const [isPerformingAction, setIsPerformingAction] = useState(false) |
| 87 | 96 | |
| @@ -105,9 +114,12 @@ | ||
| 105 | 114 | {/* translators: Hidden accessibility text. */} |
| 106 | 115 | {__('Select bulk action', 'code-snippets')} |
| 107 | 116 | </label> |
| 108 | 117 | |
| 109 | - <BulkActionSelect {...{ which, actions, selectedAction, setSelectedAction }} /> | |
| 118 | + <BulkActionSelect | |
| 119 | + {...{ which, actions, selectedAction, setSelectedAction }} | |
| 120 | + label={selectLabel ?? __('Bulk actions', 'code-snippets')} | |
| 121 | + /> | |
| 110 | 122 | |
| 111 | 123 | <SubmitButton |
| 112 | 124 | id={`doaction${'bottom' === which ? '-2' : ''}`} |
| 113 | 125 | name="bulk_action" |
| @@ -112,9 +124,9 @@ | ||
| 112 | 124 | id={`doaction${'bottom' === which ? '-2' : ''}`} |
| 113 | 125 | name="bulk_action" |
| 114 | 126 | text={__('Apply', 'code-snippets')} |
| 115 | 127 | className="action" |
| 116 | - disabled={!!disabled || isPerformingAction || !selectedAction} | |
| 128 | + disabled={!!disabled || isPerformingAction || !selectedAction || 0 === selected.size} | |
| 117 | 129 | onClick={handleSubmit} |
| 118 | 130 | /> |
| 119 | 131 | |
| 120 | 132 | {isPerformingAction ? <Spinner /> : null} |
| @@ -121,8 +133,38 @@ | ||
| 121 | 133 | </div> |
| 122 | 134 | ) |
| 123 | 135 | } |
| 124 | 136 | |
| 137 | +export interface SelectAllControlProps<K extends Key> { | |
| 138 | + keys: K[] | |
| 139 | + selected: Set<K> | |
| 140 | + setSelected: Dispatch<SetStateAction<Set<K>>> | |
| 141 | +} | |
| 142 | + | |
| 143 | +export const SelectAllControl = <K extends Key>({ | |
| 144 | + keys, | |
| 145 | + selected, | |
| 146 | + setSelected | |
| 147 | +}: SelectAllControlProps<K>) => | |
| 148 | + <label className="tablenav-select-all"> | |
| 149 | + <input | |
| 150 | + type="checkbox" | |
| 151 | + checked={0 < keys.length && keys.every(key => selected.has(key))} | |
| 152 | + disabled={0 === keys.length} | |
| 153 | + aria-label={__('Select all items', 'code-snippets')} | |
| 154 | + onChange={event => { | |
| 155 | + const { checked } = event.target | |
| 156 | + | |
| 157 | + setSelected(previous => { | |
| 158 | + const updated = new Set(previous) | |
| 159 | + keys.forEach(key => checked ? updated.add(key) : updated.delete(key)) | |
| 160 | + return updated | |
| 161 | + }) | |
| 162 | + }} | |
| 163 | + /> | |
| 164 | + {__('Select all', 'code-snippets')} | |
| 165 | + </label> | |
| 166 | + | |
| 125 | 167 | export interface TableNavProps<K extends Key, A extends string> extends TableNavigationProps<K, A> { |
| 126 | 168 | which: 'top' | 'bottom' |
| 127 | 169 | } |
| 128 | 170 | |
| @@ -134,14 +176,19 @@ | ||
| 134 | 176 | setSelected, |
| 135 | 177 | totalItems, |
| 136 | 178 | totalPages = 0, |
| 137 | 179 | extraTableNav, |
| 180 | + endTableNav, | |
| 181 | + selectAllKeys, | |
| 138 | 182 | ...paginationProps |
| 139 | -}: TableNavProps<K, A>) => | |
| 140 | - extraTableNav || 0 < totalItems && actions | |
| 183 | +}: TableNavProps<K, A>) => { | |
| 184 | + const isTop = 'top' === which | |
| 185 | + const hasBulkActions = 0 < totalItems && Boolean(actions) | |
| 186 | + | |
| 187 | + return isTop && Boolean(extraTableNav ?? endTableNav) || hasBulkActions || 0 < totalPages | |
| 141 | 188 | ? <div className={`tablenav ${which}`}> |
| 142 | 189 | |
| 143 | - {0 < totalItems && actions && ( | |
| 190 | + {0 < totalItems && actions && doAction && ( | |
| 144 | 191 | <BulkActions |
| 145 | 192 | which={which} |
| 146 | 193 | actions={actions} |
| 147 | 194 | doAction={doAction} |
| @@ -149,14 +196,26 @@ | ||
| 149 | 196 | selected={selected} |
| 150 | 197 | onActionSuccess={() => setSelected(new Set())} |
| 151 | 198 | />)} |
| 152 | 199 | |
| 153 | - {extraTableNav?.(which)} | |
| 154 | - {0 < totalPages && <TablePagination {...{ totalPages, totalItems, which, ...paginationProps }} />} | |
| 200 | + {isTop && selectAllKeys | |
| 201 | + ? <SelectAllControl keys={selectAllKeys} selected={selected} setSelected={setSelected} /> | |
| 202 | + : null} | |
| 155 | 203 | |
| 204 | + {isTop ? extraTableNav?.(which) : null} | |
| 205 | + | |
| 206 | + {0 < totalPages || isTop && endTableNav | |
| 207 | + ? <div className="tablenav-end-group"> | |
| 208 | + {0 < totalPages && | |
| 209 | + <TablePagination {...{ totalPages, totalItems, which, ...paginationProps }} />} | |
| 210 | + {isTop ? endTableNav?.(which) : null} | |
| 211 | + </div> | |
| 212 | + : null} | |
| 213 | + | |
| 156 | 214 | <br className="clear" /> |
| 157 | 215 | </div> |
| 158 | 216 | : null |
| 217 | +} | |
| 159 | 218 | |
| 160 | 219 | export interface TableNavigationProps<K extends Key, A extends string> extends ListTableNavProps<K, A>, |
| 161 | 220 | Omit<TablePaginationProps, 'totalPages' | 'which'> { |
| 162 | 221 | selected: Set<K> |
| @@ -162,8 +221,9 @@ | ||
| 162 | 221 | selected: Set<K> |
| 163 | 222 | setSelected: Dispatch<SetStateAction<Set<K>>> |
| 164 | 223 | totalItems: number |
| 165 | 224 | totalPages: number | undefined |
| 225 | + selectAllKeys?: K[] | |
| 166 | 226 | } |
| 167 | 227 | |
| 168 | 228 | export const TableNavigation = <K extends Key, A extends string>({ |
| 169 | 229 | children, |