| @@ -579,168 +579,8 @@ | ||
| 579 | 579 | return host ? host.getAttribute("data-plan-id") || "" : ""; |
| 580 | 580 | } |
| 581 | 581 | |
| 582 | 582 | /* ------------------------------------------------------------------ * |
| 583 | - * Products tab: bulk-set one price across products x durations. | |
| 584 | - * ------------------------------------------------------------------ */ | |
| 585 | - | |
| 586 | - /** | |
| 587 | - * Reflect a variable product's variations in its parent control checkbox: | |
| 588 | - * on when all are ticked, indeterminate when some are. | |
| 589 | - * | |
| 590 | - * @param {HTMLElement} group The [data-subscrpt-bulk-group] wrapper. | |
| 591 | - */ | |
| 592 | - function syncBulkParent(group) { | |
| 593 | - var parent = group && group.querySelector("[data-subscrpt-bulk-parent]"); | |
| 594 | - if (!parent) { | |
| 595 | - return; | |
| 596 | - } | |
| 597 | - var kids = group.querySelectorAll("[data-subscrpt-bulk-product]"); | |
| 598 | - var checked = 0; | |
| 599 | - kids.forEach(function (cb) { | |
| 600 | - if (cb.checked) { | |
| 601 | - checked += 1; | |
| 602 | - } | |
| 603 | - }); | |
| 604 | - parent.checked = kids.length > 0 && checked === kids.length; | |
| 605 | - parent.indeterminate = checked > 0 && checked < kids.length; | |
| 606 | - } | |
| 607 | - | |
| 608 | - /** | |
| 609 | - * Update the "N selected" tally beside each column header. | |
| 610 | - * | |
| 611 | - * @param {HTMLElement} modal The bulk-price modal. | |
| 612 | - */ | |
| 613 | - function syncBulkCounts(modal) { | |
| 614 | - [ | |
| 615 | - ["term", "[data-subscrpt-bulk-term]"], | |
| 616 | - ["product", "[data-subscrpt-bulk-product]"], | |
| 617 | - ].forEach(function (pair) { | |
| 618 | - var out = modal.querySelector('[data-subscrpt-bulk-count="' + pair[0] + '"]'); | |
| 619 | - if (!out) { | |
| 620 | - return; | |
| 621 | - } | |
| 622 | - var n = modal.querySelectorAll(pair[1] + ":checked").length; | |
| 623 | - out.textContent = n ? (i18n.bulkSelected || "%d selected").replace("%d", n) : ""; | |
| 624 | - }); | |
| 625 | - } | |
| 626 | - | |
| 627 | - // Any change inside the modal refreshes the per-column tallies. | |
| 628 | - document.addEventListener("change", function (e) { | |
| 629 | - var modal = e.target.closest("[data-subscrpt-bulk-price]"); | |
| 630 | - if (modal) { | |
| 631 | - syncBulkCounts(modal); | |
| 632 | - } | |
| 633 | - }); | |
| 634 | - | |
| 635 | - // A column's "All" checkbox ticks/unticks every row in that column. | |
| 636 | - document.addEventListener("change", function (e) { | |
| 637 | - var all = e.target.closest("[data-subscrpt-bulk-toggle-all]"); | |
| 638 | - var modal = all && all.closest("[data-subscrpt-bulk-price]"); | |
| 639 | - if (!modal) { | |
| 640 | - return; | |
| 641 | - } | |
| 642 | - var col = all.getAttribute("data-subscrpt-bulk-toggle-all"); | |
| 643 | - var sel = "product" === col ? "[data-subscrpt-bulk-product]" : "[data-subscrpt-bulk-term]"; | |
| 644 | - modal.querySelectorAll(sel).forEach(function (cb) { | |
| 645 | - if (!cb.disabled) { | |
| 646 | - cb.checked = all.checked; | |
| 647 | - } | |
| 648 | - }); | |
| 649 | - if ("product" === col) { | |
| 650 | - modal.querySelectorAll("[data-subscrpt-bulk-parent]").forEach(function (parent) { | |
| 651 | - parent.checked = all.checked; | |
| 652 | - parent.indeterminate = false; | |
| 653 | - }); | |
| 654 | - } | |
| 655 | - }); | |
| 656 | - | |
| 657 | - // A variable product's parent control ticks/unticks all its variations. | |
| 658 | - document.addEventListener("change", function (e) { | |
| 659 | - var parent = e.target.closest("[data-subscrpt-bulk-parent]"); | |
| 660 | - var group = parent && parent.closest("[data-subscrpt-bulk-group]"); | |
| 661 | - if (!group || !parent.closest("[data-subscrpt-bulk-price]")) { | |
| 662 | - return; | |
| 663 | - } | |
| 664 | - group.querySelectorAll("[data-subscrpt-bulk-product]").forEach(function (cb) { | |
| 665 | - if (!cb.disabled) { | |
| 666 | - cb.checked = parent.checked; | |
| 667 | - } | |
| 668 | - }); | |
| 669 | - parent.indeterminate = false; | |
| 670 | - }); | |
| 671 | - | |
| 672 | - // A variation tick keeps its parent control in sync. | |
| 673 | - document.addEventListener("change", function (e) { | |
| 674 | - var child = e.target.closest("[data-subscrpt-bulk-product]"); | |
| 675 | - var group = child && child.closest("[data-subscrpt-bulk-group]"); | |
| 676 | - if (!group || !child.closest("[data-subscrpt-bulk-price]")) { | |
| 677 | - return; | |
| 678 | - } | |
| 679 | - syncBulkParent(group); | |
| 680 | - }); | |
| 681 | - | |
| 682 | - // Apply the entered price to every selected product on every selected duration. | |
| 683 | - document.addEventListener("click", function (e) { | |
| 684 | - var btn = e.target.closest("[data-subscrpt-bulk-apply]"); | |
| 685 | - var modal = btn && btn.closest("[data-subscrpt-bulk-price]"); | |
| 686 | - if (!modal) { | |
| 687 | - return; | |
| 688 | - } | |
| 689 | - | |
| 690 | - var regInput = modal.querySelector("[data-subscrpt-bulk-regular]"); | |
| 691 | - var saleInput = modal.querySelector("[data-subscrpt-bulk-sale]"); | |
| 692 | - var regular = regInput ? regInput.value.trim() : ""; | |
| 693 | - if ("" === regular) { | |
| 694 | - save.notify(i18n.bulkNoPrice || i18n.genericError, "error"); | |
| 695 | - return; | |
| 696 | - } | |
| 697 | - | |
| 698 | - var products = Array.prototype.map.call( | |
| 699 | - modal.querySelectorAll("[data-subscrpt-bulk-product]:checked"), | |
| 700 | - function (cb) { | |
| 701 | - return { oid: cb.getAttribute("data-oid"), vid: cb.getAttribute("data-vid") || 0 }; | |
| 702 | - }, | |
| 703 | - ); | |
| 704 | - if (!products.length) { | |
| 705 | - save.notify(i18n.bulkNoProducts || i18n.genericError, "error"); | |
| 706 | - return; | |
| 707 | - } | |
| 708 | - | |
| 709 | - var planIds = Array.prototype.map.call(modal.querySelectorAll("[data-subscrpt-bulk-term]:checked"), function (cb) { | |
| 710 | - return cb.value; | |
| 711 | - }); | |
| 712 | - if (!planIds.length) { | |
| 713 | - save.notify(i18n.bulkNoTerms || i18n.genericError, "error"); | |
| 714 | - return; | |
| 715 | - } | |
| 716 | - | |
| 717 | - setLoading(btn, true); | |
| 718 | - api("POST", "/bulk-price", { | |
| 719 | - plan_ids: planIds, | |
| 720 | - products: products, | |
| 721 | - regular_price: regular, | |
| 722 | - sale_price: saleInput ? saleInput.value.trim() : "", | |
| 723 | - }) | |
| 724 | - .then(function () { | |
| 725 | - // Close before the panel refresh replaces (and orphans) the modal — | |
| 726 | - // otherwise its body-scroll lock is never released. | |
| 727 | - if (window.WPSubsModal && window.WPSubsModal.close) { | |
| 728 | - window.WPSubsModal.close("subscrpt-bulk-price"); | |
| 729 | - } | |
| 730 | - return refreshProducts(currentGroupId()); | |
| 731 | - }) | |
| 732 | - .then(function () { | |
| 733 | - setLoading(btn, false); | |
| 734 | - save.notify(i18n.bulkApplied || i18n.pricesSaved); | |
| 735 | - }) | |
| 736 | - .catch(function (err) { | |
| 737 | - setLoading(btn, false); | |
| 738 | - save.notify(err.message || i18n.genericError, "error"); | |
| 739 | - }); | |
| 740 | - }); | |
| 741 | - | |
| 742 | - /* ------------------------------------------------------------------ * | |
| 743 | 583 | * Products tab (Pro): bulk-add products to the plan group. |
| 744 | 584 | * ------------------------------------------------------------------ */ |
| 745 | 585 | |
| 746 | 586 | /** |
| @@ -944,9 +784,8 @@ | ||
| 944 | 784 | if (list.lastElementChild) { |
| 945 | 785 | list.lastElementChild.style.borderBottom = "none"; |
| 946 | 786 | } |
| 947 | 787 | syncPickerCount(modal); |
| 948 | - syncSelectAll(modal); | |
| 949 | 788 | }) |
| 950 | 789 | .catch(function () { |
| 951 | 790 | list.innerHTML = |
| 952 | 791 | '<li style="padding:10px 4px;color:var(--wpsubs-text-subtle);font-size:13px;">' + |
| @@ -976,75 +815,17 @@ | ||
| 976 | 815 | }); |
| 977 | 816 | out.textContent = n ? (i18n.picked || "%d on this plan").replace("%d", n) : i18n.pickedNone || ""; |
| 978 | 817 | } |
| 979 | 818 | |
| 980 | - /** | |
| 981 | - * The picker's attachable, non-locked checkboxes — what select-all acts on. | |
| 982 | - * Rows locked to another plan are disabled and left out. | |
| 983 | - * | |
| 984 | - * @param {HTMLElement} modal The add-product modal. | |
| 985 | - * @return {HTMLElement[]} Enabled row checkboxes. | |
| 986 | - */ | |
| 987 | - function selectableBoxes(modal) { | |
| 988 | - var boxes = modal.querySelectorAll("[data-subscrpt-product-list] input[data-oid]"); | |
| 989 | - return Array.prototype.filter.call(boxes, function (box) { | |
| 990 | - return !box.disabled; | |
| 991 | - }); | |
| 992 | - } | |
| 993 | - | |
| 994 | - /** | |
| 995 | - * Reflect the rows in the select-all checkbox: on when all are ticked, | |
| 996 | - * indeterminate when some are, off when none (disabled when nothing to pick). | |
| 997 | - * | |
| 998 | - * @param {HTMLElement} modal The add-product modal. | |
| 999 | - */ | |
| 1000 | - function syncSelectAll(modal) { | |
| 1001 | - var toggle = modal && modal.querySelector("[data-subscrpt-picker-all]"); | |
| 1002 | - if (!toggle) { | |
| 1003 | - return; | |
| 1004 | - } | |
| 1005 | - var boxes = selectableBoxes(modal); | |
| 1006 | - var checked = boxes.filter(function (box) { | |
| 1007 | - return box.checked; | |
| 1008 | - }).length; | |
| 1009 | - toggle.disabled = 0 === boxes.length; | |
| 1010 | - toggle.checked = boxes.length > 0 && checked === boxes.length; | |
| 1011 | - toggle.indeterminate = checked > 0 && checked < boxes.length; | |
| 1012 | - } | |
| 1013 | - | |
| 1014 | - // Any tick in the picker updates the tally and the select-all state. | |
| 819 | + // Any tick in the picker updates the tally. | |
| 1015 | 820 | document.addEventListener("change", function (e) { |
| 1016 | 821 | var box = e.target.closest("[data-subscrpt-product-list] input[type=checkbox]"); |
| 1017 | 822 | var modal = box && box.closest("[data-subscrpt-add-product]"); |
| 1018 | 823 | if (modal) { |
| 1019 | 824 | syncPickerCount(modal); |
| 1020 | - syncSelectAll(modal); | |
| 1021 | 825 | } |
| 1022 | 826 | }); |
| 1023 | 827 | |
| 1024 | - // Select-all ticks/unticks every enabled row. Select-all makes the picker | |
| 1025 | - // uniform, so parent controls and children all take the same state directly — | |
| 1026 | - // no synthetic change events (which would re-enter syncSelectAll mid-loop). | |
| 1027 | - document.addEventListener("change", function (e) { | |
| 1028 | - var toggle = e.target.closest("[data-subscrpt-picker-all]"); | |
| 1029 | - var modal = toggle && toggle.closest("[data-subscrpt-add-product]"); | |
| 1030 | - if (!modal) { | |
| 1031 | - return; | |
| 1032 | - } | |
| 1033 | - var checked = toggle.checked; | |
| 1034 | - var boxes = modal.querySelectorAll("[data-subscrpt-product-list] input[type=checkbox]"); | |
| 1035 | - Array.prototype.forEach.call(boxes, function (box) { | |
| 1036 | - if (box.disabled) { | |
| 1037 | - return; | |
| 1038 | - } | |
| 1039 | - box.checked = checked; | |
| 1040 | - box.indeterminate = false; | |
| 1041 | - }); | |
| 1042 | - toggle.checked = checked; | |
| 1043 | - toggle.indeterminate = false; | |
| 1044 | - syncPickerCount(modal); | |
| 1045 | - }); | |
| 1046 | - | |
| 1047 | 828 | // Load the picker when the modal opens (pre-checking attached products). |
| 1048 | 829 | document.addEventListener("wpsubs:modal:open", function (e) { |
| 1049 | 830 | var modal = e.target; |
| 1050 | 831 | if (modal && modal.id === "subscrpt-add-product") { |
| @@ -1470,331 +1251,9 @@ | ||
| 1470 | 1251 | save.notify(err.message || i18n.genericError, "error"); |
| 1471 | 1252 | }); |
| 1472 | 1253 | }); |
| 1473 | 1254 | |
| 1474 | - // "Allow User Cancellation" saves itself too; on failure the switch goes back | |
| 1475 | - // to what is stored. | |
| 1476 | - document.addEventListener("change", function (e) { | |
| 1477 | - var toggle = e.target.closest("[data-subscrpt-user-cancel-card] [data-subscrpt-user-cancel-enable]"); | |
| 1478 | - if (!toggle) { | |
| 1479 | - return; | |
| 1480 | - } | |
| 1481 | - var card = toggle.closest("[data-subscrpt-user-cancel-card]"); | |
| 1482 | - | |
| 1483 | - toggle.disabled = true; | |
| 1484 | - api("PUT", "/product-user-cancel/" + card.getAttribute("data-product-id"), { enabled: toggle.checked }) | |
| 1485 | - .then(function () { | |
| 1486 | - toggle.disabled = false; | |
| 1487 | - save.notify(toggle.checked ? i18n.userCancelOn : i18n.userCancelOff); | |
| 1488 | - }) | |
| 1489 | - .catch(function (err) { | |
| 1490 | - toggle.disabled = false; | |
| 1491 | - toggle.checked = !toggle.checked; | |
| 1492 | - save.notify(err.message || i18n.genericError, "error"); | |
| 1493 | - }); | |
| 1494 | - }); | |
| 1495 | - | |
| 1496 | 1255 | /* ------------------------------------------------------------------ * |
| 1497 | - * Drag-and-drop reorder: durations + products (vertical only). | |
| 1498 | - * | |
| 1499 | - * A drag starts on [data-subscrpt-sort-handle] only, moves its | |
| 1500 | - * [data-subscrpt-sort-item] within the [data-subscrpt-sortable] list, and | |
| 1501 | - * saves the list's full id order (hidden/paged-out items included) to | |
| 1502 | - * /groups/{id}/order. Siblings slide aside with a FLIP transition. Arrow | |
| 1503 | - * keys on a focused handle move it one step. Everything is delegated, | |
| 1504 | - * because the Products panel is re-rendered in place. | |
| 1505 | - * ------------------------------------------------------------------ */ | |
| 1506 | - | |
| 1507 | - var SORT_ANIM_MS = 200; | |
| 1508 | - var SORT_EDGE_PX = 60; | |
| 1509 | - var sortDrag = null; | |
| 1510 | - var sortPending = {}; | |
| 1511 | - | |
| 1512 | - /** | |
| 1513 | - * Items of a list the merchant can currently see (search/pager aware). | |
| 1514 | - * | |
| 1515 | - * @param {HTMLElement} list Sortable list. | |
| 1516 | - * @return {HTMLElement[]} | |
| 1517 | - */ | |
| 1518 | - function sortVisible(list) { | |
| 1519 | - return Array.prototype.filter.call(list.children, function (el) { | |
| 1520 | - return el.hasAttribute("data-subscrpt-sort-item") && "none" !== el.style.display; | |
| 1521 | - }); | |
| 1522 | - } | |
| 1523 | - | |
| 1524 | - /** | |
| 1525 | - * Every item id of a list, in DOM order. | |
| 1526 | - * | |
| 1527 | - * @param {HTMLElement} list Sortable list. | |
| 1528 | - * @return {number[]} | |
| 1529 | - */ | |
| 1530 | - function sortIds(list) { | |
| 1531 | - return Array.prototype.map.call(list.querySelectorAll(":scope > [data-subscrpt-sort-item]"), function (el) { | |
| 1532 | - return parseInt(el.getAttribute("data-subscrpt-sort-item"), 10); | |
| 1533 | - }); | |
| 1534 | - } | |
| 1535 | - | |
| 1536 | - /** | |
| 1537 | - * Move `el` in the DOM (before `ref`, or to the end) and slide every other | |
| 1538 | - * visible item from where it was to where it now is. | |
| 1539 | - * | |
| 1540 | - * @param {HTMLElement} list Sortable list. | |
| 1541 | - * @param {HTMLElement} el Item to move. | |
| 1542 | - * @param {HTMLElement|null} ref Insert before this item. | |
| 1543 | - * @param {boolean} self Animate `el` too (keyboard moves). | |
| 1544 | - */ | |
| 1545 | - function sortMove(list, el, ref, self) { | |
| 1546 | - var others = sortVisible(list).filter(function (it) { | |
| 1547 | - return self || it !== el; | |
| 1548 | - }); | |
| 1549 | - var before = others.map(function (it) { | |
| 1550 | - return it.offsetTop; | |
| 1551 | - }); | |
| 1552 | - | |
| 1553 | - list.insertBefore(el, ref); | |
| 1554 | - | |
| 1555 | - others.forEach(function (it, i) { | |
| 1556 | - var delta = before[i] - it.offsetTop; | |
| 1557 | - if (!delta) { | |
| 1558 | - return; | |
| 1559 | - } | |
| 1560 | - it.style.transition = "none"; | |
| 1561 | - it.style.transform = "translateY(" + delta + "px)"; | |
| 1562 | - void it.offsetHeight; // Commit the start frame. | |
| 1563 | - it.style.transition = "transform " + SORT_ANIM_MS + "ms cubic-bezier(0.2, 0, 0, 1)"; | |
| 1564 | - it.style.transform = ""; | |
| 1565 | - window.clearTimeout(it._subscrptSortTimer); | |
| 1566 | - it._subscrptSortTimer = window.setTimeout(function () { | |
| 1567 | - it.style.transition = ""; | |
| 1568 | - }, SORT_ANIM_MS); | |
| 1569 | - }); | |
| 1570 | - } | |
| 1571 | - | |
| 1572 | - /** | |
| 1573 | - * Put a list back into a given id order (a failed save). | |
| 1574 | - * | |
| 1575 | - * @param {HTMLElement} list Sortable list. | |
| 1576 | - * @param {number[]} ids Order to restore. | |
| 1577 | - */ | |
| 1578 | - function sortRestore(list, ids) { | |
| 1579 | - ids.forEach(function (id) { | |
| 1580 | - var el = list.querySelector(':scope > [data-subscrpt-sort-item="' + id + '"]'); | |
| 1581 | - if (el) { | |
| 1582 | - list.appendChild(el); | |
| 1583 | - } | |
| 1584 | - }); | |
| 1585 | - sortSynced(list); | |
| 1586 | - } | |
| 1587 | - | |
| 1588 | - /** | |
| 1589 | - * Tell the list's search/pager the DOM order changed. | |
| 1590 | - * | |
| 1591 | - * @param {HTMLElement} list Sortable list. | |
| 1592 | - */ | |
| 1593 | - function sortSynced(list) { | |
| 1594 | - var root = list.closest("[data-subscrpt-browse]"); | |
| 1595 | - if (root) { | |
| 1596 | - root.dispatchEvent(new CustomEvent("subscrpt:sorted")); | |
| 1597 | - } | |
| 1598 | - } | |
| 1599 | - | |
| 1600 | - /** | |
| 1601 | - * Save a list's order. Debounced so a run of arrow-key moves is one write; | |
| 1602 | - * on failure the list goes back to the order it had before the run. | |
| 1603 | - * | |
| 1604 | - * @param {HTMLElement} list Sortable list. | |
| 1605 | - * @param {number[]} before Order before this change. | |
| 1606 | - */ | |
| 1607 | - function sortSave(list, before) { | |
| 1608 | - var key = list.getAttribute("data-subscrpt-sortable"); | |
| 1609 | - var groupId = currentGroupId(); | |
| 1610 | - if (!key || !groupId) { | |
| 1611 | - return; | |
| 1612 | - } | |
| 1613 | - | |
| 1614 | - var pending = sortPending[key] || { before: before }; | |
| 1615 | - sortPending[key] = pending; | |
| 1616 | - window.clearTimeout(pending.timer); | |
| 1617 | - pending.timer = window.setTimeout(function () { | |
| 1618 | - delete sortPending[key]; | |
| 1619 | - var ids = sortIds(list); | |
| 1620 | - if (ids.join() === pending.before.join()) { | |
| 1621 | - return; | |
| 1622 | - } | |
| 1623 | - var body = {}; | |
| 1624 | - body[key] = ids; | |
| 1625 | - api("PUT", "/groups/" + groupId + "/order", body) | |
| 1626 | - .then(function () { | |
| 1627 | - save.notify(i18n.orderSaved); | |
| 1628 | - }) | |
| 1629 | - .catch(function (err) { | |
| 1630 | - sortRestore(list, pending.before); | |
| 1631 | - save.notify(err.message || i18n.genericError, "error"); | |
| 1632 | - }); | |
| 1633 | - }, 300); | |
| 1634 | - } | |
| 1635 | - | |
| 1636 | - /** | |
| 1637 | - * Follow the pointer: position the lifted item, swap it past any sibling | |
| 1638 | - * whose midpoint it crossed, and keep it inside the list. | |
| 1639 | - * | |
| 1640 | - * @param {number} clientY Pointer Y in the viewport. | |
| 1641 | - */ | |
| 1642 | - function sortUpdate(clientY) { | |
| 1643 | - var d = sortDrag; | |
| 1644 | - var el = d.item; | |
| 1645 | - var items = sortVisible(d.list); | |
| 1646 | - var first = items[0]; | |
| 1647 | - var last = items[items.length - 1]; | |
| 1648 | - | |
| 1649 | - // Page coordinates, so auto-scroll does not throw the item off the pointer. | |
| 1650 | - var dy = clientY + window.scrollY - d.startY; | |
| 1651 | - var top = d.startTop + dy; | |
| 1652 | - top = Math.max(first.offsetTop, Math.min(top, last.offsetTop + last.offsetHeight - el.offsetHeight)); | |
| 1653 | - // Leading edge, not centre: an expanded (tall) product must still be able | |
| 1654 | - // to pass a short one inside the clamped range. | |
| 1655 | - var bottom = top + el.offsetHeight; | |
| 1656 | - | |
| 1657 | - var i = items.indexOf(el); | |
| 1658 | - var next = items[i + 1]; | |
| 1659 | - var prev = items[i - 1]; | |
| 1660 | - var movedDown = false; | |
| 1661 | - while (next && bottom > next.offsetTop + next.offsetHeight / 2) { | |
| 1662 | - sortMove(d.list, el, next.nextSibling, false); | |
| 1663 | - items = sortVisible(d.list); | |
| 1664 | - next = items[items.indexOf(el) + 1]; | |
| 1665 | - movedDown = true; | |
| 1666 | - } | |
| 1667 | - while (!movedDown && prev && top < prev.offsetTop + prev.offsetHeight / 2) { | |
| 1668 | - sortMove(d.list, el, prev, false); | |
| 1669 | - items = sortVisible(d.list); | |
| 1670 | - prev = items[items.indexOf(el) - 1]; | |
| 1671 | - } | |
| 1672 | - | |
| 1673 | - el.style.transform = "translateY(" + (top - el.offsetTop) + "px) scale(1.01)"; | |
| 1674 | - } | |
| 1675 | - | |
| 1676 | - /** | |
| 1677 | - * Scroll the page while the pointer rests near the top/bottom edge. | |
| 1678 | - */ | |
| 1679 | - function sortAutoScroll() { | |
| 1680 | - if (!sortDrag) { | |
| 1681 | - return; | |
| 1682 | - } | |
| 1683 | - var y = sortDrag.clientY; | |
| 1684 | - var h = window.innerHeight; | |
| 1685 | - var step = 0; | |
| 1686 | - if (y < SORT_EDGE_PX) { | |
| 1687 | - step = -Math.ceil((SORT_EDGE_PX - y) / 4); | |
| 1688 | - } else if (y > h - SORT_EDGE_PX) { | |
| 1689 | - step = Math.ceil((y - (h - SORT_EDGE_PX)) / 4); | |
| 1690 | - } | |
| 1691 | - if (step) { | |
| 1692 | - window.scrollBy(0, step); | |
| 1693 | - sortUpdate(y); | |
| 1694 | - } | |
| 1695 | - sortDrag.raf = window.requestAnimationFrame(sortAutoScroll); | |
| 1696 | - } | |
| 1697 | - | |
| 1698 | - function sortOnMove(e) { | |
| 1699 | - if (!sortDrag || e.pointerId !== sortDrag.pointerId) { | |
| 1700 | - return; | |
| 1701 | - } | |
| 1702 | - e.preventDefault(); | |
| 1703 | - sortDrag.clientY = e.clientY; | |
| 1704 | - sortUpdate(e.clientY); | |
| 1705 | - } | |
| 1706 | - | |
| 1707 | - function sortOnEnd(e) { | |
| 1708 | - if (!sortDrag || e.pointerId !== sortDrag.pointerId) { | |
| 1709 | - return; | |
| 1710 | - } | |
| 1711 | - var d = sortDrag; | |
| 1712 | - var el = d.item; | |
| 1713 | - sortDrag = null; | |
| 1714 | - | |
| 1715 | - window.cancelAnimationFrame(d.raf); | |
| 1716 | - window.removeEventListener("pointermove", sortOnMove); | |
| 1717 | - window.removeEventListener("pointerup", sortOnEnd); | |
| 1718 | - window.removeEventListener("pointercancel", sortOnEnd); | |
| 1719 | - document.body.classList.remove("wpsubs-sorting"); | |
| 1720 | - | |
| 1721 | - // Glide into the slot, then drop the lift. | |
| 1722 | - el.classList.remove("wpsubs-sort-item--dragging"); | |
| 1723 | - el.classList.add("wpsubs-sort-item--settling"); | |
| 1724 | - el.style.transform = ""; | |
| 1725 | - window.setTimeout(function () { | |
| 1726 | - el.classList.remove("wpsubs-sort-item--settling"); | |
| 1727 | - }, 240); | |
| 1728 | - | |
| 1729 | - if (sortIds(d.list).join() !== d.before.join()) { | |
| 1730 | - sortSynced(d.list); | |
| 1731 | - sortSave(d.list, d.before); | |
| 1732 | - } | |
| 1733 | - } | |
| 1734 | - | |
| 1735 | - document.addEventListener("pointerdown", function (e) { | |
| 1736 | - var handle = e.target.closest("[data-subscrpt-sort-handle]"); | |
| 1737 | - if (!handle || sortDrag || (e.pointerType === "mouse" && 0 !== e.button)) { | |
| 1738 | - return; | |
| 1739 | - } | |
| 1740 | - var item = handle.closest("[data-subscrpt-sort-item]"); | |
| 1741 | - var list = item && item.parentElement; | |
| 1742 | - if (!list || !list.hasAttribute("data-subscrpt-sortable")) { | |
| 1743 | - return; | |
| 1744 | - } | |
| 1745 | - e.preventDefault(); | |
| 1746 | - handle.focus({ preventScroll: true }); | |
| 1747 | - | |
| 1748 | - sortDrag = { | |
| 1749 | - item: item, | |
| 1750 | - list: list, | |
| 1751 | - pointerId: e.pointerId, | |
| 1752 | - startY: e.clientY + window.scrollY, | |
| 1753 | - startTop: item.offsetTop, | |
| 1754 | - clientY: e.clientY, | |
| 1755 | - before: sortIds(list), | |
| 1756 | - }; | |
| 1757 | - | |
| 1758 | - item.style.transition = ""; | |
| 1759 | - item.classList.remove("wpsubs-sort-item--settling"); | |
| 1760 | - item.classList.add("wpsubs-sort-item--dragging"); | |
| 1761 | - item.style.transform = "scale(1.01)"; | |
| 1762 | - document.body.classList.add("wpsubs-sorting"); | |
| 1763 | - | |
| 1764 | - window.addEventListener("pointermove", sortOnMove, { passive: false }); | |
| 1765 | - window.addEventListener("pointerup", sortOnEnd); | |
| 1766 | - window.addEventListener("pointercancel", sortOnEnd); | |
| 1767 | - sortDrag.raf = window.requestAnimationFrame(sortAutoScroll); | |
| 1768 | - }); | |
| 1769 | - | |
| 1770 | - // Keyboard: ArrowUp / ArrowDown on a focused handle moves one step. | |
| 1771 | - document.addEventListener("keydown", function (e) { | |
| 1772 | - var handle = e.target.closest ? e.target.closest("[data-subscrpt-sort-handle]") : null; | |
| 1773 | - if (!handle || sortDrag || ("ArrowUp" !== e.key && "ArrowDown" !== e.key)) { | |
| 1774 | - return; | |
| 1775 | - } | |
| 1776 | - var item = handle.closest("[data-subscrpt-sort-item]"); | |
| 1777 | - var list = item && item.parentElement; | |
| 1778 | - if (!list || !list.hasAttribute("data-subscrpt-sortable")) { | |
| 1779 | - return; | |
| 1780 | - } | |
| 1781 | - e.preventDefault(); | |
| 1782 | - | |
| 1783 | - var items = sortVisible(list); | |
| 1784 | - var i = items.indexOf(item); | |
| 1785 | - var target = "ArrowUp" === e.key ? items[i - 1] : items[i + 1]; | |
| 1786 | - if (!target) { | |
| 1787 | - return; | |
| 1788 | - } | |
| 1789 | - var before = sortIds(list); | |
| 1790 | - sortMove(list, item, "ArrowUp" === e.key ? target : target.nextSibling, true); | |
| 1791 | - handle.focus({ preventScroll: true }); | |
| 1792 | - sortSynced(list); | |
| 1793 | - sortSave(list, before); | |
| 1794 | - }); | |
| 1795 | - | |
| 1796 | - /* ------------------------------------------------------------------ * | |
| 1797 | 1256 | * Products tab: client-side search + pagination over the product list. |
| 1798 | 1257 | * ------------------------------------------------------------------ */ |
| 1799 | 1258 | |
| 1800 | 1259 | /** |
| @@ -1925,13 +1384,8 @@ | ||
| 1925 | 1384 | } |
| 1926 | 1385 | perPage = parseInt(e.detail && e.detail.value, 10) || perPage; |
| 1927 | 1386 | page = 1; |
| 1928 | 1387 | render(); |
| 1929 | - }); | |
| 1930 | - | |
| 1931 | - // A drag-and-drop reorder moved items in the DOM: page in the new order. | |
| 1932 | - root.addEventListener("subscrpt:sorted", function () { | |
| 1933 | - items = Array.prototype.slice.call(root.querySelectorAll("[data-subscrpt-browse-item]")); | |
| 1934 | 1388 | }); |
| 1935 | 1389 | |
| 1936 | 1390 | pager.addEventListener("click", function (e) { |
| 1937 | 1391 | var btn = e.target.closest("[data-subscrpt-page]"); |