| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * External dependencies. |
| 3 | 3 | */ |
| 4 | -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; | |
| 4 | +import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'; | |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | 7 | * WordPress dependencies. |
| 8 | 8 | */ |
| @@ -19,8 +19,10 @@ | ||
| 19 | 19 | |
| 20 | 20 | export class Pointers extends Component { |
| 21 | 21 | titleInput = null; |
| 22 | 22 | |
| 23 | + publishButton = null; | |
| 24 | + | |
| 23 | 25 | debouncedDefaultResults = debounce(() => { |
| 24 | 26 | this.getDefaultResults(); |
| 25 | 27 | }, 200); |
| 26 | 28 | |
| @@ -54,8 +56,9 @@ | ||
| 54 | 56 | super(props); |
| 55 | 57 | |
| 56 | 58 | // We need to know the title of the page and react to changes since this is the query we search for |
| 57 | 59 | this.titleInput = document.getElementById('title'); |
| 60 | + this.publishButton = document.getElementById('publish'); | |
| 58 | 61 | |
| 59 | 62 | this.state = { |
| 60 | 63 | pointers: window.epOrdering.pointers, |
| 61 | 64 | posts: window.epOrdering.posts, |
| @@ -62,8 +65,9 @@ | ||
| 62 | 65 | title: this.titleInput.value, |
| 63 | 66 | defaultResults: {}, |
| 64 | 67 | searchText: '', |
| 65 | 68 | searchResults: {}, |
| 69 | + removedPointers: [], | |
| 66 | 70 | }; |
| 67 | 71 | } |
| 68 | 72 | |
| 69 | 73 | componentDidMount() { |
| @@ -73,14 +77,30 @@ | ||
| 73 | 77 | |
| 74 | 78 | if (title?.length > 0) { |
| 75 | 79 | this.getDefaultResults(); |
| 76 | 80 | } |
| 81 | + | |
| 82 | + this.updatePublishButtonState(); | |
| 77 | 83 | } |
| 78 | 84 | |
| 85 | + componentDidUpdate() { | |
| 86 | + this.updatePublishButtonState(); | |
| 87 | + } | |
| 88 | + | |
| 79 | 89 | componentWillUnmount() { |
| 80 | 90 | this.titleInput.removeEventListener('keyup', this.debouncedHandleTitleChange); |
| 81 | 91 | } |
| 82 | 92 | |
| 93 | + /** | |
| 94 | + * Updates the publish button disabled state based on loading status. | |
| 95 | + */ | |
| 96 | + updatePublishButtonState = () => { | |
| 97 | + const { title, defaultResults } = this.state; | |
| 98 | + const isLoading = title.length === 0 || !defaultResults[title]; | |
| 99 | + | |
| 100 | + this.publishButton.disabled = isLoading; | |
| 101 | + }; | |
| 102 | + | |
| 83 | 103 | handleTitleChange = () => { |
| 84 | 104 | this.setState({ title: this.titleInput.value }); |
| 85 | 105 | this.debouncedDefaultResults(); |
| 86 | 106 | }; |
| @@ -100,11 +120,13 @@ | ||
| 100 | 120 | }; |
| 101 | 121 | |
| 102 | 122 | removePointer = (pointer) => { |
| 103 | 123 | let { pointers } = this.state; |
| 124 | + const { removedPointers } = this.state; | |
| 104 | 125 | |
| 105 | 126 | delete pointers[pointers.indexOf(pointer)]; |
| 106 | 127 | pointers = pointers.filter((item) => item !== null); |
| 128 | + removedPointers.push(pointer.ID); | |
| 107 | 129 | |
| 108 | 130 | this.setState({ pointers }); |
| 109 | 131 | }; |
| 110 | 132 | |
| @@ -112,25 +134,18 @@ | ||
| 112 | 134 | let { pointers } = this.state; |
| 113 | 135 | const { title, defaultResults } = this.state; |
| 114 | 136 | let merged = defaultResults[title].slice(); |
| 115 | 137 | |
| 116 | - const setIds = {}; | |
| 117 | - merged.forEach((item) => { | |
| 118 | - setIds[item.ID] = item; | |
| 119 | - }); | |
| 120 | - | |
| 121 | 138 | pointers = pointers.sort((a, b) => { |
| 122 | 139 | return a.order > b.order ? 1 : -1; |
| 123 | 140 | }); |
| 141 | + const pointersIds = pluck(pointers, 'ID'); | |
| 124 | 142 | |
| 143 | + // Remove all custom pointers from the default results | |
| 144 | + merged = merged.filter((item) => pointersIds.indexOf(item.ID) === -1); | |
| 145 | + | |
| 146 | + // Insert pointers into their proper location | |
| 125 | 147 | pointers.forEach((pointer) => { |
| 126 | - // Remove the original if a duplicate | |
| 127 | - if (setIds[pointer.ID]) { | |
| 128 | - delete merged[merged.indexOf(setIds[pointer.ID])]; | |
| 129 | - merged = merged.filter((item) => item); | |
| 130 | - } | |
| 131 | - | |
| 132 | - // Insert into proper location | |
| 133 | 148 | merged.splice(parseInt(pointer.order, 10) - 1, 0, pointer); |
| 134 | 149 | }); |
| 135 | 150 | |
| 136 | 151 | return merged; |
| @@ -189,8 +204,9 @@ | ||
| 189 | 204 | |
| 190 | 205 | pointers.push({ |
| 191 | 206 | ID: id, |
| 192 | 207 | order: position, |
| 208 | + type: 'custom-result', | |
| 193 | 209 | }); |
| 194 | 210 | |
| 195 | 211 | this.setState({ pointers }); |
| 196 | 212 | }; |
| @@ -226,20 +242,15 @@ | ||
| 226 | 242 | // Now _all_ the items are in order - grab the pointers and set the new positions to state |
| 227 | 243 | const pointers = []; |
| 228 | 244 | |
| 229 | 245 | items.forEach((item, index) => { |
| 230 | - if (item.order) { | |
| 231 | - // Reordering an existing pointer | |
| 246 | + // Reordering an existing pointer or adding a default post to the pointers array | |
| 247 | + if (item.order || Number(item.ID) === Number(result.draggableId)) { | |
| 232 | 248 | pointers.push({ |
| 233 | 249 | ID: item.ID, |
| 234 | 250 | order: index + 1, |
| 251 | + type: item?.type || 'reordered', | |
| 235 | 252 | }); |
| 236 | - } else if (item.ID === result.draggableId) { | |
| 237 | - // Adding a default post to the pointers array | |
| 238 | - pointers.push({ | |
| 239 | - ID: item.ID, | |
| 240 | - order: index + 1, | |
| 241 | - }); | |
| 242 | 253 | } |
| 243 | 254 | }); |
| 244 | 255 | |
| 245 | 256 | this.setState({ pointers }); |
| @@ -299,8 +310,9 @@ | ||
| 299 | 310 | posts, |
| 300 | 311 | defaultResults, |
| 301 | 312 | title, |
| 302 | 313 | pointers, |
| 314 | + removedPointers, | |
| 303 | 315 | searchText, |
| 304 | 316 | searchResults: searchResultsFromState, |
| 305 | 317 | } = this.state; |
| 306 | 318 | |
| @@ -337,9 +349,9 @@ | ||
| 337 | 349 | |
| 338 | 350 | const searchResults = searchResultsFromState[searchText] |
| 339 | 351 | ? searchResultsFromState[searchText].filter( |
| 340 | 352 | (item) => renderedIds.indexOf(item.ID) === -1, |
| 341 | - ) | |
| 353 | + ) | |
| 342 | 354 | : false; |
| 343 | 355 | |
| 344 | 356 | return ( |
| 345 | 357 | <div> |
| @@ -358,8 +370,10 @@ | ||
| 358 | 370 | parseInt(window.epOrdering.postsPerPage, 10) <= index |
| 359 | 371 | ? index + 1 |
| 360 | 372 | : index; |
| 361 | 373 | |
| 374 | + const isRemoved = removedPointers.includes(item.ID); | |
| 375 | + | |
| 362 | 376 | let { title } = item; |
| 363 | 377 | if (undefined === title) { |
| 364 | 378 | title = |
| 365 | 379 | undefined !== posts[item.ID] |
| @@ -367,17 +381,16 @@ | ||
| 367 | 381 | : defaultResultsById[item.ID].post_title; |
| 368 | 382 | } |
| 369 | 383 | |
| 370 | 384 | // Determine if this result is part of default search results or not |
| 371 | - const isDefaultResult = | |
| 372 | - undefined !== defaultResultsById[item.ID]; | |
| 385 | + const itemType = item?.type || 'reordered'; | |
| 373 | 386 | const tooltipText = |
| 374 | - isDefaultResult === true | |
| 387 | + itemType === 'reordered' | |
| 375 | 388 | ? __('Return to original position', 'elasticpress') |
| 376 | 389 | : __( |
| 377 | 390 | 'Remove custom result from results list', |
| 378 | 391 | 'elasticpress', |
| 379 | - ); | |
| 392 | + ); | |
| 380 | 393 | |
| 381 | 394 | return ( |
| 382 | 395 | <Fragment key={item.ID}> |
| 383 | 396 | {parseInt(window.epOrdering.postsPerPage, 10) === |
| @@ -407,36 +420,27 @@ | ||
| 407 | 420 | )} |
| 408 | 421 | |
| 409 | 422 | <Draggable |
| 410 | 423 | key={item.ID} |
| 411 | - draggableId={item.ID} | |
| 424 | + draggableId={String(item.ID)} | |
| 412 | 425 | index={draggableIndex} |
| 413 | 426 | > |
| 414 | 427 | {(provided2) => ( |
| 415 | 428 | <div |
| 416 | - className={`pointer ${draggableIndex}`} | |
| 429 | + className={`pointer ${draggableIndex} ${ | |
| 430 | + isRemoved ? 'removed' : '' | |
| 431 | + }`} | |
| 417 | 432 | ref={provided2.innerRef} |
| 418 | 433 | {...provided2.draggableProps} |
| 419 | 434 | > |
| 420 | - {item.order && isDefaultResult === true && ( | |
| 435 | + {item.order && itemType === 'reordered' && ( | |
| 421 | 436 | <span className="pointer-type">RD</span> |
| 422 | 437 | )} |
| 423 | - {item.order && | |
| 424 | - isDefaultResult === false && ( | |
| 425 | - <span className="pointer-type"> | |
| 426 | - CR | |
| 427 | - </span> | |
| 428 | - )} | |
| 438 | + {item.order && itemType !== 'reordered' && ( | |
| 439 | + <span className="pointer-type">CR</span> | |
| 440 | + )} | |
| 429 | 441 | <strong className="title">{title}</strong> |
| 430 | 442 | <div className="pointer-actions"> |
| 431 | - <span | |
| 432 | - className="dashicons dashicons-menu handle" | |
| 433 | - {...provided2.dragHandleProps} | |
| 434 | - title={__( | |
| 435 | - 'Drag post up or down to reposition', | |
| 436 | - 'elasticpress', | |
| 437 | - )} | |
| 438 | - /> | |
| 439 | 443 | {item.order && ( |
| 440 | 444 | <span |
| 441 | 445 | role="button" |
| 442 | 446 | tabIndex="0" |
| @@ -455,8 +459,16 @@ | ||
| 455 | 459 | Remove Post |
| 456 | 460 | </span> |
| 457 | 461 | </span> |
| 458 | 462 | )} |
| 463 | + <span | |
| 464 | + className="dashicons dashicons-menu handle" | |
| 465 | + {...provided2.dragHandleProps} | |
| 466 | + title={__( | |
| 467 | + 'Drag post up or down to reposition', | |
| 468 | + 'elasticpress', | |
| 469 | + )} | |
| 470 | + /> | |
| 459 | 471 | </div> |
| 460 | 472 | </div> |
| 461 | 473 | )} |
| 462 | 474 | </Draggable> |