Plugin Directory

Changeset 3390679


Ignore:
Timestamp:
11/05/2025 07:28:48 PM (4 months ago)
Author:
boxodev
Message:

Update to version 0.0.63 from GitHub

Location:
boxo-return
Files:
18 edited
1 copied

Legend:

Unmodified
Added
Removed
  • boxo-return/tags/0.0.63/admin/order.css

    r3208611 r3390679  
    11.boxo_packaging-info {
    2   display: flex;
    3   align-items: center;
    4   gap: 5px;
    5   color: #01cc9b;
    6   font-weight: 500;
     2    display: flex;
     3    align-items: center;
     4    gap: 5px;
     5    color: #01cc9b;
     6    font-weight: 500;
    77}
    88
    99.boxo_packaging-icon {
    10   display: inline-block;
    11   width: 22px !important;
    12   height: 22px !important;
     10    display: inline-block;
     11    width: 22px !important;
     12    height: 22px !important;
    1313}
    1414
    1515.boxo_packaging-icon svg {
    16   display: block !important;
    17   width: 100% !important;
    18   height: 100% !important;
     16    display: block !important;
     17    width: 100% !important;
     18    height: 100% !important;
    1919}
  • boxo-return/tags/0.0.63/admin/product-picker.css

    r3274490 r3390679  
    11.bxpp,
    22.bxpp * {
    3   /* Reset */
    4   margin: 0;
    5   box-sizing: border-box;
     3    /* Reset */
     4    margin: 0;
     5    box-sizing: border-box;
    66}
    77
    88.bxpp {
    9   display: flex;
    10   flex-direction: column;
    11   gap: 10px;
     9    display: flex;
     10    flex-direction: column;
     11    gap: 10px;
    1212}
    1313
    1414.bxpp_search {
    15   position: relative;
     15    position: relative;
    1616}
    1717
    1818.bxpp_search-input-container {
    19   display: flex;
    20   width: fit-content;
    21   align-items: center;
    22   gap: 10px;
     19    display: flex;
     20    width: fit-content;
     21    align-items: center;
     22    gap: 10px;
    2323}
    2424
    2525.bxpp_search-results {
    26   z-index: 999;
    27   display: none;
    28   position: absolute;
    29   top: calc(100% + 5px);
    30   width: 100%;
    31   max-width: 600px;
    32   max-height: 330px;
    33   overflow: scroll;
    34   border: 1px solid #8c8f94;
    35   background-color: white;
     26    z-index: 999;
     27    display: none;
     28    position: absolute;
     29    top: calc(100% + 5px);
     30    width: 100%;
     31    max-width: 600px;
     32    max-height: 330px;
     33    overflow: scroll;
     34    border: 1px solid #8c8f94;
     35    background-color: white;
    3636}
    3737
    3838.bxpp_search:focus-within .bxpp_search-results {
    39   display: block;
     39    display: block;
    4040}
    4141
    4242.bxpp_search-result {
    43   display: flex;
    44   gap: 10px;
    45   align-items: center;
    46   padding: 10px;
     43    display: flex;
     44    gap: 10px;
     45    align-items: center;
     46    padding: 10px;
    4747}
    4848
    4949.bxpp_search-result-image {
    50   width: 30px;
    51   height: 30px;
    52   object-fit: contain;
     50    width: 30px;
     51    height: 30px;
     52    object-fit: contain;
    5353}
    5454
    5555.bxpp_search-result.clickable {
    56   cursor: pointer;
     56    cursor: pointer;
    5757}
    5858
    5959.bxpp_search-result.clickable:hover {
    60   background-color: #dcdcde;
     60    background-color: #dcdcde;
    6161}
    6262
    6363.bxpp_selected-products-viewport {
    64   max-height: 300px;
    65   overflow: scroll;
     64    max-height: 300px;
     65    overflow: scroll;
    6666}
    6767
    6868.bxpp_selected-product-image-column {
    69   width: 40px;
     69    width: 40px;
    7070}
    7171
    7272.bxpp_selected-product-image {
    73   width: 30px;
    74   height: 30px;
    75   object-fit: contain;
     73    width: 30px;
     74    height: 30px;
     75    object-fit: contain;
    7676}
  • boxo-return/tags/0.0.63/admin/product-picker.js

    r3273560 r3390679  
    11document.addEventListener('alpine:init', () => {
    2   /**
    3    * @param {string} ajaxUrl
    4    * @param {string} nonce
    5    */
    6   const productPicker = (ajaxUrl, nonce) => ({
    7     init() {
    8       this.selectedProducts = JSON.parse(
    9         // @ts-ignore: no Alpine type for 'this'.
    10         this.$root.querySelector('.bxpp_initial-products-json').innerText
    11       )
    12       this.searchProducts()
    13       // @ts-ignore: no Alpine type for 'this'.
    14       this.$watch('keyword', (val) => this.searchProducts(val))
    15     },
     2    /**
     3     * @param {string} ajaxUrl
     4     * @param {string} nonce
     5     */
     6    const productPicker = (ajaxUrl, nonce) => ({
     7        init() {
     8            this.selectedProducts = JSON.parse(
     9                // @ts-ignore: no Alpine type for 'this'.
     10                this.$root.querySelector('.bxpp_initial-products-json').innerText
     11            )
     12            this.searchProducts()
     13            // @ts-ignore: no Alpine type for 'this'.
     14            this.$watch('keyword', (val) => this.searchProducts(val))
     15        },
    1616
    17     // State:
    18     /** @type {{id: string; title: string, image: string | null}[]} */
    19     selectedProducts: [],
    20     keyword: '',
    21     loading: false,
    22     searchResults: [],
     17        // State:
     18        /** @type {{id: string; title: string, image: string | null}[]} */
     19        selectedProducts: [],
     20        keyword: '',
     21        loading: false,
     22        searchResults: [],
    2323
    24     // Derived state:
    25     value() {
    26       return this.selectedProducts.map((p) => p.id).join(',')
    27     },
    28     hasSelectedProducts() {
    29       return this.selectedProducts.length > 0
    30     },
    31     hasSearchResults() {
    32       return this.searchResults.length > 0
    33     },
     24        // Derived state:
     25        value() {
     26            return this.selectedProducts.map((p) => p.id).join(',')
     27        },
     28        hasSelectedProducts() {
     29            return this.selectedProducts.length > 0
     30        },
     31        hasSearchResults() {
     32            return this.searchResults.length > 0
     33        },
    3434
    35     // Methods:
    36     async searchProducts() {
    37       this.loading = true
     35        // Methods:
     36        async searchProducts() {
     37            this.loading = true
    3838
    39       try {
    40         const params = new URLSearchParams({
    41           action: 'boxo_product_search',
    42           _ajax_nonce: nonce,
    43           keyword: this.keyword,
    44           skip: this.selectedProducts.map((p) => p.id).join(',')
    45         })
     39            try {
     40                const params = new URLSearchParams({
     41                    action: 'boxo_product_search',
     42                    _ajax_nonce: nonce,
     43                    keyword: this.keyword,
     44                    skip: this.selectedProducts.map((p) => p.id).join(','),
     45                })
    4646
    47         const res = await fetch(ajaxUrl + '?' + params.toString())
    48         if (!res.ok) {
    49           console.error('[BOXO Return] Search products: ' + res.status + ' ' + res.statusText)
    50           this.searchResults = []
    51           return
    52         }
    53         this.searchResults = await res.json()
    54       } catch (err) {
    55         console.error(err)
    56       } finally {
    57         this.loading = false
    58       }
    59     },
     47                const res = await fetch(ajaxUrl + '?' + params.toString())
     48                if (!res.ok) {
     49                    console.error(
     50                        '[BOXO Return] Search products: ' + res.status + ' ' + res.statusText
     51                    )
     52                    this.searchResults = []
     53                    return
     54                }
     55                this.searchResults = await res.json()
     56            } catch (err) {
     57                console.error(err)
     58            } finally {
     59                this.loading = false
     60            }
     61        },
    6062
    61     /**
    62      *
    63      * @param {KeyboardEvent} e
    64      */
    65     handleEnterPress(e) {
    66       // Prevent parent form submit.
    67       e.preventDefault()
    68     },
     63        /**
     64         *
     65         * @param {KeyboardEvent} e
     66         */
     67        handleEnterPress(e) {
     68            // Prevent parent form submit.
     69            e.preventDefault()
     70        },
    6971
    70     /**
    71      * @param {string} id
    72      * @param {string} title
    73      * @param {string} image
    74      */
    75     handleSearchResultClick(id, title, image) {
    76       this.selectedProducts.unshift({id, title, image})
    77       if (this.keyword) {
    78         // Clear keyword, which triggers a new blank search automatically.
    79         this.keyword = ''
    80         return
    81       }
    82       // If keyword was already blank, trigger a new blank search manually.
    83       this.searchProducts()
    84     },
     72        /**
     73         * @param {string} id
     74         * @param {string} title
     75         * @param {string} image
     76         */
     77        handleSearchResultClick(id, title, image) {
     78            this.selectedProducts.unshift({id, title, image})
     79            if (this.keyword) {
     80                // Clear keyword, which triggers a new blank search automatically.
     81                this.keyword = ''
     82                return
     83            }
     84            // If keyword was already blank, trigger a new blank search manually.
     85            this.searchProducts()
     86        },
    8587
    86     /**
    87      * @param {string} id
    88      */
    89     handleRemoveProductClick(id) {
    90       this.selectedProducts.splice(
    91         this.selectedProducts.findIndex((p) => p.id === id),
    92         1
    93       )
    94       this.searchProducts()
    95     }
    96   })
     88        /**
     89         * @param {string} id
     90         */
     91        handleRemoveProductClick(id) {
     92            this.selectedProducts.splice(
     93                this.selectedProducts.findIndex((p) => p.id === id),
     94                1
     95            )
     96            this.searchProducts()
     97        },
     98    })
    9799
    98   // @ts-ignore: no Alpine types.
    99   window.Alpine.data('productPicker', productPicker)
     100    // @ts-ignore: no Alpine types.
     101    window.Alpine.data('productPicker', productPicker)
    100102})
  • boxo-return/tags/0.0.63/admin/settings.css

    r3208611 r3390679  
    11[x-cloak] {
    2   display: none !important;
     2    display: none !important;
    33}
    44
    55.boxo_settings-field {
    6   display: flex;
    7   flex-direction: column;
    8   gap: 20px;
     6    display: flex;
     7    flex-direction: column;
     8    gap: 20px;
    99}
  • boxo-return/tags/0.0.63/boxo-return.php

    r3375141 r3390679  
    22/*
    33* Plugin Name: BOXO Return
    4 * Version: 0.0.62
     4* Version: 0.0.63
    55* Requires at least: 6.5
    66* Requires PHP: 7.1
     
    2222}
    2323
    24 const BOXO_RETURN_PLUGIN_VERSION = "0.0.62";
     24const BOXO_RETURN_PLUGIN_VERSION = "0.0.63";
    2525
    2626include plugin_dir_path(__FILE__) . 'includes/constants.php';
  • boxo-return/tags/0.0.63/checkout/boxo-checkout.css

    r3312507 r3390679  
    11#boxo_container br {
    2   /* Hide automatically inserted br tags. */
    3   display: none !important;
     2    /* Hide automatically inserted br tags. */
     3    display: none !important;
    44}
    55
    66.boxo_option-icon {
    7   display: inline-block;
    8   width: 22px !important;
    9   height: 22px !important;
    10   vertical-align: text-bottom;
     7    display: inline-block;
     8    width: 22px !important;
     9    height: 22px !important;
     10    vertical-align: text-bottom;
    1111}
    1212
    1313.boxo_option-icon svg {
    14   display: block !important;
    15   width: 100% !important;
    16   height: 100% !important;
     14    display: block !important;
     15    width: 100% !important;
     16    height: 100% !important;
    1717}
    1818
    1919.boxo_list-item-reusable {
    20   /* Anchors tooltip. */
    21   position: relative;
     20    /* Anchors tooltip. */
     21    position: relative;
    2222}
    2323
    2424.boxo_info-container {
    25   /* Reset */
    26   display: inline !important;
     25    /* Reset */
     26    display: inline !important;
    2727}
    2828
    2929.boxo_info-container,
    3030.boxo_info-container * {
    31   /* Reset */
    32   color: inherit !important;
    33   border: none !important;
    34   box-shadow: none !important;
     31    /* Reset */
     32    color: inherit !important;
     33    border: none !important;
     34    box-shadow: none !important;
    3535}
    3636
    3737.boxo_info-container:hover .boxo_info-tooltip {
    38   visibility: visible;
    39   opacity: 1;
     38    visibility: visible;
     39    opacity: 1;
    4040}
    4141
    4242.boxo_info-icon {
    43   display: inline-block;
    44   width: 12px !important;
    45   height: 12px !important;
    46   vertical-align: middle;
     43    display: inline-block;
     44    width: 12px !important;
     45    height: 12px !important;
     46    vertical-align: middle;
    4747}
    4848
    4949.boxo_info-icon svg {
    50   display: block !important;
    51   width: 100% !important;
    52   height: 100% !important;
     50    display: block !important;
     51    width: 100% !important;
     52    height: 100% !important;
    5353}
    5454
    5555.boxo_info-tooltip {
    56   /* Reset */
    57   white-space: normal;
    58   text-align: left;
    59   text-indent: initial;
    60   font-weight: normal;
    61   font-style: normal;
    62   text-transform: none;
     56    /* Reset */
     57    white-space: normal;
     58    text-align: left;
     59    text-indent: initial;
     60    font-weight: normal;
     61    font-style: normal;
     62    text-transform: none;
    6363
    64   z-index: 999;
    65   position: absolute;
    66   top: calc(100% + 8px);
    67   right: 0;
    68   width: max-content;
    69   max-width: 360px;
    70   border: 1px solid black !important;
    71   background-color: white;
    72   padding: 16px;
    73   word-break: keep-all;
    74   visibility: hidden;
    75   opacity: 0;
    76   transition: visibility 0.2s, opacity 0.2s;
     64    z-index: 999;
     65    position: absolute;
     66    top: calc(100% + 8px);
     67    right: 0;
     68    width: max-content;
     69    max-width: 360px;
     70    border: 1px solid black !important;
     71    background-color: white;
     72    padding: 16px;
     73    word-break: keep-all;
     74    visibility: hidden;
     75    opacity: 0;
     76    transition:
     77        visibility 0.2s,
     78        opacity 0.2s;
    7779}
    7880
    7981.boxo_header {
    80   /* Prevent table header from being hidden by another plugin. */
    81   display: table-cell;
     82    /* Prevent table header from being hidden by another plugin. */
     83    display: table-cell;
    8284}
  • boxo-return/tags/0.0.63/checkout/boxo-checkout.js

    r3371141 r3390679  
    11if (document.readyState !== 'loading') {
    2   init()
     2    init()
    33} else {
    4   document.addEventListener('DOMContentLoaded', init)
     4    document.addEventListener('DOMContentLoaded', init)
    55}
    66
    77async function init() {
    8   // Must use jQuery for event handlers because propagation of certain events appears to be blocked.
    9   jQuery(function ($) {
    10     // AJAX update checkout (incl. total price) on change in relevant data or packaging selection.
    11     $(document.body).on(
    12       'change',
    13       '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]',
    14       async () => {
    15         document.body.dispatchEvent(new Event('update_checkout'))
    16       }
    17     )
    18   })
     8    // Must use jQuery for event handlers because propagation of certain events appears to be blocked.
     9    jQuery(function ($) {
     10        // AJAX update checkout (incl. total price) on change in relevant data or packaging selection.
     11        $(document.body).on(
     12            'change',
     13            '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]',
     14            async () => {
     15                document.body.dispatchEvent(new Event('update_checkout'))
     16            }
     17        )
     18    })
    1919
    20   console.info('[BOXO Return] Ready')
     20    // Add selected packaging to AJAX request for custom checkouts where the input
     21    // is not inside the checkout form. When the input is inside the checkout form,
     22    // the data is already added in post_data, but it's redundantly added here as well.
     23    jQuery(function ($) {
     24        $.ajaxPrefilter(function (options) {
     25            // If anything goes wrong, do not block the AJAX request.
     26            try {
     27                if (!options.url?.includes('wc-ajax=update_order_review')) {
     28                    return
     29                }
     30                /** @type HTMLInputElement | null */
     31                const input = document.querySelector('input[name="boxo_packaging"]:checked')
     32                const packaging = input?.value
     33                if (!packaging) {
     34                    throw new Error('[BOXO Return] No packaging input value.')
     35                }
     36                options.data += '&boxo_packaging=' + packaging
     37            } catch (err) {
     38                console.error('[BOXO Return]', err)
     39            }
     40        })
     41    })
     42
     43    console.info('[BOXO Return] Ready')
    2144}
  • boxo-return/tags/0.0.63/checkout/checkout.php

    r3371141 r3390679  
    88add_action('woocommerce_init', 'Boxo_Checkout::init');
    99
     10// On trigger, eg. change in packaging selection, an AJAX request is made to
     11// ?wc-ajax=update-order-review. This request contains the selected packaging.
     12// This value is normally added to the AJAX because the form with class="checkout"
     13// is serialized as post_data. However, when the plugin inputs are outside of the
     14// checkout form, this does not work. To solve this, the selected packaging is
     15// also added to the AJAX request by script.
    1016if (!class_exists('Boxo_Checkout')) {
    1117    class Boxo_Checkout {
     
    288294         */
    289295        private static function get_selected_packaging() {
     296            // In AJAX requests in standard woocommerce checkouts, the selected packaging
     297            // will be inside the post_data field.
    290298            if (isset($_POST['post_data']) && is_string($_POST['post_data'])) {
    291                 // AJAX requests.
     299                $post_data = [];
    292300                parse_str($_POST['post_data'], $post_data);
    293             } else {
    294                 // Non-AJAX requests (eg. checkout submit).
    295                 $post_data = $_POST;
    296             }
    297             return isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging'])
    298                 ? $post_data['boxo_packaging']
    299                 : null;
     301                if (isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging'])) {
     302                    return $post_data['boxo_packaging'];
     303                }
     304            }
     305
     306            // In custom woocommerce checkouts the value is added to $_POST by our script.
     307            // Also, in non-AJAX requests (eg. checkout submit) the value will be in $_POST.
     308            if (isset($_POST['boxo_packaging']) && is_string($_POST['boxo_packaging'])) {
     309                return $_POST['boxo_packaging'];
     310            }
     311            return null;
    300312        }
    301313    }
  • boxo-return/tags/0.0.63/readme.txt

    r3375141 r3390679  
    44Requires at least: 6.5
    55Tested up to: 6.8.3
    6 Stable tag: 0.0.62
     6Stable tag: 0.0.63
    77Requires PHP: 7.1
    88License: GPLv2 or later
     
    2626
    2727== Changelog ==
     28
     29= 0.0.63 =
     30Support custom checkouts where the checkout form has been modified.
    2831
    2932= 0.0.62 =
  • boxo-return/trunk/admin/order.css

    r3208611 r3390679  
    11.boxo_packaging-info {
    2   display: flex;
    3   align-items: center;
    4   gap: 5px;
    5   color: #01cc9b;
    6   font-weight: 500;
     2    display: flex;
     3    align-items: center;
     4    gap: 5px;
     5    color: #01cc9b;
     6    font-weight: 500;
    77}
    88
    99.boxo_packaging-icon {
    10   display: inline-block;
    11   width: 22px !important;
    12   height: 22px !important;
     10    display: inline-block;
     11    width: 22px !important;
     12    height: 22px !important;
    1313}
    1414
    1515.boxo_packaging-icon svg {
    16   display: block !important;
    17   width: 100% !important;
    18   height: 100% !important;
     16    display: block !important;
     17    width: 100% !important;
     18    height: 100% !important;
    1919}
  • boxo-return/trunk/admin/product-picker.css

    r3274490 r3390679  
    11.bxpp,
    22.bxpp * {
    3   /* Reset */
    4   margin: 0;
    5   box-sizing: border-box;
     3    /* Reset */
     4    margin: 0;
     5    box-sizing: border-box;
    66}
    77
    88.bxpp {
    9   display: flex;
    10   flex-direction: column;
    11   gap: 10px;
     9    display: flex;
     10    flex-direction: column;
     11    gap: 10px;
    1212}
    1313
    1414.bxpp_search {
    15   position: relative;
     15    position: relative;
    1616}
    1717
    1818.bxpp_search-input-container {
    19   display: flex;
    20   width: fit-content;
    21   align-items: center;
    22   gap: 10px;
     19    display: flex;
     20    width: fit-content;
     21    align-items: center;
     22    gap: 10px;
    2323}
    2424
    2525.bxpp_search-results {
    26   z-index: 999;
    27   display: none;
    28   position: absolute;
    29   top: calc(100% + 5px);
    30   width: 100%;
    31   max-width: 600px;
    32   max-height: 330px;
    33   overflow: scroll;
    34   border: 1px solid #8c8f94;
    35   background-color: white;
     26    z-index: 999;
     27    display: none;
     28    position: absolute;
     29    top: calc(100% + 5px);
     30    width: 100%;
     31    max-width: 600px;
     32    max-height: 330px;
     33    overflow: scroll;
     34    border: 1px solid #8c8f94;
     35    background-color: white;
    3636}
    3737
    3838.bxpp_search:focus-within .bxpp_search-results {
    39   display: block;
     39    display: block;
    4040}
    4141
    4242.bxpp_search-result {
    43   display: flex;
    44   gap: 10px;
    45   align-items: center;
    46   padding: 10px;
     43    display: flex;
     44    gap: 10px;
     45    align-items: center;
     46    padding: 10px;
    4747}
    4848
    4949.bxpp_search-result-image {
    50   width: 30px;
    51   height: 30px;
    52   object-fit: contain;
     50    width: 30px;
     51    height: 30px;
     52    object-fit: contain;
    5353}
    5454
    5555.bxpp_search-result.clickable {
    56   cursor: pointer;
     56    cursor: pointer;
    5757}
    5858
    5959.bxpp_search-result.clickable:hover {
    60   background-color: #dcdcde;
     60    background-color: #dcdcde;
    6161}
    6262
    6363.bxpp_selected-products-viewport {
    64   max-height: 300px;
    65   overflow: scroll;
     64    max-height: 300px;
     65    overflow: scroll;
    6666}
    6767
    6868.bxpp_selected-product-image-column {
    69   width: 40px;
     69    width: 40px;
    7070}
    7171
    7272.bxpp_selected-product-image {
    73   width: 30px;
    74   height: 30px;
    75   object-fit: contain;
     73    width: 30px;
     74    height: 30px;
     75    object-fit: contain;
    7676}
  • boxo-return/trunk/admin/product-picker.js

    r3273560 r3390679  
    11document.addEventListener('alpine:init', () => {
    2   /**
    3    * @param {string} ajaxUrl
    4    * @param {string} nonce
    5    */
    6   const productPicker = (ajaxUrl, nonce) => ({
    7     init() {
    8       this.selectedProducts = JSON.parse(
    9         // @ts-ignore: no Alpine type for 'this'.
    10         this.$root.querySelector('.bxpp_initial-products-json').innerText
    11       )
    12       this.searchProducts()
    13       // @ts-ignore: no Alpine type for 'this'.
    14       this.$watch('keyword', (val) => this.searchProducts(val))
    15     },
     2    /**
     3     * @param {string} ajaxUrl
     4     * @param {string} nonce
     5     */
     6    const productPicker = (ajaxUrl, nonce) => ({
     7        init() {
     8            this.selectedProducts = JSON.parse(
     9                // @ts-ignore: no Alpine type for 'this'.
     10                this.$root.querySelector('.bxpp_initial-products-json').innerText
     11            )
     12            this.searchProducts()
     13            // @ts-ignore: no Alpine type for 'this'.
     14            this.$watch('keyword', (val) => this.searchProducts(val))
     15        },
    1616
    17     // State:
    18     /** @type {{id: string; title: string, image: string | null}[]} */
    19     selectedProducts: [],
    20     keyword: '',
    21     loading: false,
    22     searchResults: [],
     17        // State:
     18        /** @type {{id: string; title: string, image: string | null}[]} */
     19        selectedProducts: [],
     20        keyword: '',
     21        loading: false,
     22        searchResults: [],
    2323
    24     // Derived state:
    25     value() {
    26       return this.selectedProducts.map((p) => p.id).join(',')
    27     },
    28     hasSelectedProducts() {
    29       return this.selectedProducts.length > 0
    30     },
    31     hasSearchResults() {
    32       return this.searchResults.length > 0
    33     },
     24        // Derived state:
     25        value() {
     26            return this.selectedProducts.map((p) => p.id).join(',')
     27        },
     28        hasSelectedProducts() {
     29            return this.selectedProducts.length > 0
     30        },
     31        hasSearchResults() {
     32            return this.searchResults.length > 0
     33        },
    3434
    35     // Methods:
    36     async searchProducts() {
    37       this.loading = true
     35        // Methods:
     36        async searchProducts() {
     37            this.loading = true
    3838
    39       try {
    40         const params = new URLSearchParams({
    41           action: 'boxo_product_search',
    42           _ajax_nonce: nonce,
    43           keyword: this.keyword,
    44           skip: this.selectedProducts.map((p) => p.id).join(',')
    45         })
     39            try {
     40                const params = new URLSearchParams({
     41                    action: 'boxo_product_search',
     42                    _ajax_nonce: nonce,
     43                    keyword: this.keyword,
     44                    skip: this.selectedProducts.map((p) => p.id).join(','),
     45                })
    4646
    47         const res = await fetch(ajaxUrl + '?' + params.toString())
    48         if (!res.ok) {
    49           console.error('[BOXO Return] Search products: ' + res.status + ' ' + res.statusText)
    50           this.searchResults = []
    51           return
    52         }
    53         this.searchResults = await res.json()
    54       } catch (err) {
    55         console.error(err)
    56       } finally {
    57         this.loading = false
    58       }
    59     },
     47                const res = await fetch(ajaxUrl + '?' + params.toString())
     48                if (!res.ok) {
     49                    console.error(
     50                        '[BOXO Return] Search products: ' + res.status + ' ' + res.statusText
     51                    )
     52                    this.searchResults = []
     53                    return
     54                }
     55                this.searchResults = await res.json()
     56            } catch (err) {
     57                console.error(err)
     58            } finally {
     59                this.loading = false
     60            }
     61        },
    6062
    61     /**
    62      *
    63      * @param {KeyboardEvent} e
    64      */
    65     handleEnterPress(e) {
    66       // Prevent parent form submit.
    67       e.preventDefault()
    68     },
     63        /**
     64         *
     65         * @param {KeyboardEvent} e
     66         */
     67        handleEnterPress(e) {
     68            // Prevent parent form submit.
     69            e.preventDefault()
     70        },
    6971
    70     /**
    71      * @param {string} id
    72      * @param {string} title
    73      * @param {string} image
    74      */
    75     handleSearchResultClick(id, title, image) {
    76       this.selectedProducts.unshift({id, title, image})
    77       if (this.keyword) {
    78         // Clear keyword, which triggers a new blank search automatically.
    79         this.keyword = ''
    80         return
    81       }
    82       // If keyword was already blank, trigger a new blank search manually.
    83       this.searchProducts()
    84     },
     72        /**
     73         * @param {string} id
     74         * @param {string} title
     75         * @param {string} image
     76         */
     77        handleSearchResultClick(id, title, image) {
     78            this.selectedProducts.unshift({id, title, image})
     79            if (this.keyword) {
     80                // Clear keyword, which triggers a new blank search automatically.
     81                this.keyword = ''
     82                return
     83            }
     84            // If keyword was already blank, trigger a new blank search manually.
     85            this.searchProducts()
     86        },
    8587
    86     /**
    87      * @param {string} id
    88      */
    89     handleRemoveProductClick(id) {
    90       this.selectedProducts.splice(
    91         this.selectedProducts.findIndex((p) => p.id === id),
    92         1
    93       )
    94       this.searchProducts()
    95     }
    96   })
     88        /**
     89         * @param {string} id
     90         */
     91        handleRemoveProductClick(id) {
     92            this.selectedProducts.splice(
     93                this.selectedProducts.findIndex((p) => p.id === id),
     94                1
     95            )
     96            this.searchProducts()
     97        },
     98    })
    9799
    98   // @ts-ignore: no Alpine types.
    99   window.Alpine.data('productPicker', productPicker)
     100    // @ts-ignore: no Alpine types.
     101    window.Alpine.data('productPicker', productPicker)
    100102})
  • boxo-return/trunk/admin/settings.css

    r3208611 r3390679  
    11[x-cloak] {
    2   display: none !important;
     2    display: none !important;
    33}
    44
    55.boxo_settings-field {
    6   display: flex;
    7   flex-direction: column;
    8   gap: 20px;
     6    display: flex;
     7    flex-direction: column;
     8    gap: 20px;
    99}
  • boxo-return/trunk/boxo-return.php

    r3375141 r3390679  
    22/*
    33* Plugin Name: BOXO Return
    4 * Version: 0.0.62
     4* Version: 0.0.63
    55* Requires at least: 6.5
    66* Requires PHP: 7.1
     
    2222}
    2323
    24 const BOXO_RETURN_PLUGIN_VERSION = "0.0.62";
     24const BOXO_RETURN_PLUGIN_VERSION = "0.0.63";
    2525
    2626include plugin_dir_path(__FILE__) . 'includes/constants.php';
  • boxo-return/trunk/checkout/boxo-checkout.css

    r3312507 r3390679  
    11#boxo_container br {
    2   /* Hide automatically inserted br tags. */
    3   display: none !important;
     2    /* Hide automatically inserted br tags. */
     3    display: none !important;
    44}
    55
    66.boxo_option-icon {
    7   display: inline-block;
    8   width: 22px !important;
    9   height: 22px !important;
    10   vertical-align: text-bottom;
     7    display: inline-block;
     8    width: 22px !important;
     9    height: 22px !important;
     10    vertical-align: text-bottom;
    1111}
    1212
    1313.boxo_option-icon svg {
    14   display: block !important;
    15   width: 100% !important;
    16   height: 100% !important;
     14    display: block !important;
     15    width: 100% !important;
     16    height: 100% !important;
    1717}
    1818
    1919.boxo_list-item-reusable {
    20   /* Anchors tooltip. */
    21   position: relative;
     20    /* Anchors tooltip. */
     21    position: relative;
    2222}
    2323
    2424.boxo_info-container {
    25   /* Reset */
    26   display: inline !important;
     25    /* Reset */
     26    display: inline !important;
    2727}
    2828
    2929.boxo_info-container,
    3030.boxo_info-container * {
    31   /* Reset */
    32   color: inherit !important;
    33   border: none !important;
    34   box-shadow: none !important;
     31    /* Reset */
     32    color: inherit !important;
     33    border: none !important;
     34    box-shadow: none !important;
    3535}
    3636
    3737.boxo_info-container:hover .boxo_info-tooltip {
    38   visibility: visible;
    39   opacity: 1;
     38    visibility: visible;
     39    opacity: 1;
    4040}
    4141
    4242.boxo_info-icon {
    43   display: inline-block;
    44   width: 12px !important;
    45   height: 12px !important;
    46   vertical-align: middle;
     43    display: inline-block;
     44    width: 12px !important;
     45    height: 12px !important;
     46    vertical-align: middle;
    4747}
    4848
    4949.boxo_info-icon svg {
    50   display: block !important;
    51   width: 100% !important;
    52   height: 100% !important;
     50    display: block !important;
     51    width: 100% !important;
     52    height: 100% !important;
    5353}
    5454
    5555.boxo_info-tooltip {
    56   /* Reset */
    57   white-space: normal;
    58   text-align: left;
    59   text-indent: initial;
    60   font-weight: normal;
    61   font-style: normal;
    62   text-transform: none;
     56    /* Reset */
     57    white-space: normal;
     58    text-align: left;
     59    text-indent: initial;
     60    font-weight: normal;
     61    font-style: normal;
     62    text-transform: none;
    6363
    64   z-index: 999;
    65   position: absolute;
    66   top: calc(100% + 8px);
    67   right: 0;
    68   width: max-content;
    69   max-width: 360px;
    70   border: 1px solid black !important;
    71   background-color: white;
    72   padding: 16px;
    73   word-break: keep-all;
    74   visibility: hidden;
    75   opacity: 0;
    76   transition: visibility 0.2s, opacity 0.2s;
     64    z-index: 999;
     65    position: absolute;
     66    top: calc(100% + 8px);
     67    right: 0;
     68    width: max-content;
     69    max-width: 360px;
     70    border: 1px solid black !important;
     71    background-color: white;
     72    padding: 16px;
     73    word-break: keep-all;
     74    visibility: hidden;
     75    opacity: 0;
     76    transition:
     77        visibility 0.2s,
     78        opacity 0.2s;
    7779}
    7880
    7981.boxo_header {
    80   /* Prevent table header from being hidden by another plugin. */
    81   display: table-cell;
     82    /* Prevent table header from being hidden by another plugin. */
     83    display: table-cell;
    8284}
  • boxo-return/trunk/checkout/boxo-checkout.js

    r3371141 r3390679  
    11if (document.readyState !== 'loading') {
    2   init()
     2    init()
    33} else {
    4   document.addEventListener('DOMContentLoaded', init)
     4    document.addEventListener('DOMContentLoaded', init)
    55}
    66
    77async function init() {
    8   // Must use jQuery for event handlers because propagation of certain events appears to be blocked.
    9   jQuery(function ($) {
    10     // AJAX update checkout (incl. total price) on change in relevant data or packaging selection.
    11     $(document.body).on(
    12       'change',
    13       '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]',
    14       async () => {
    15         document.body.dispatchEvent(new Event('update_checkout'))
    16       }
    17     )
    18   })
     8    // Must use jQuery for event handlers because propagation of certain events appears to be blocked.
     9    jQuery(function ($) {
     10        // AJAX update checkout (incl. total price) on change in relevant data or packaging selection.
     11        $(document.body).on(
     12            'change',
     13            '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]',
     14            async () => {
     15                document.body.dispatchEvent(new Event('update_checkout'))
     16            }
     17        )
     18    })
    1919
    20   console.info('[BOXO Return] Ready')
     20    // Add selected packaging to AJAX request for custom checkouts where the input
     21    // is not inside the checkout form. When the input is inside the checkout form,
     22    // the data is already added in post_data, but it's redundantly added here as well.
     23    jQuery(function ($) {
     24        $.ajaxPrefilter(function (options) {
     25            // If anything goes wrong, do not block the AJAX request.
     26            try {
     27                if (!options.url?.includes('wc-ajax=update_order_review')) {
     28                    return
     29                }
     30                /** @type HTMLInputElement | null */
     31                const input = document.querySelector('input[name="boxo_packaging"]:checked')
     32                const packaging = input?.value
     33                if (!packaging) {
     34                    throw new Error('[BOXO Return] No packaging input value.')
     35                }
     36                options.data += '&boxo_packaging=' + packaging
     37            } catch (err) {
     38                console.error('[BOXO Return]', err)
     39            }
     40        })
     41    })
     42
     43    console.info('[BOXO Return] Ready')
    2144}
  • boxo-return/trunk/checkout/checkout.php

    r3371141 r3390679  
    88add_action('woocommerce_init', 'Boxo_Checkout::init');
    99
     10// On trigger, eg. change in packaging selection, an AJAX request is made to
     11// ?wc-ajax=update-order-review. This request contains the selected packaging.
     12// This value is normally added to the AJAX because the form with class="checkout"
     13// is serialized as post_data. However, when the plugin inputs are outside of the
     14// checkout form, this does not work. To solve this, the selected packaging is
     15// also added to the AJAX request by script.
    1016if (!class_exists('Boxo_Checkout')) {
    1117    class Boxo_Checkout {
     
    288294         */
    289295        private static function get_selected_packaging() {
     296            // In AJAX requests in standard woocommerce checkouts, the selected packaging
     297            // will be inside the post_data field.
    290298            if (isset($_POST['post_data']) && is_string($_POST['post_data'])) {
    291                 // AJAX requests.
     299                $post_data = [];
    292300                parse_str($_POST['post_data'], $post_data);
    293             } else {
    294                 // Non-AJAX requests (eg. checkout submit).
    295                 $post_data = $_POST;
    296             }
    297             return isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging'])
    298                 ? $post_data['boxo_packaging']
    299                 : null;
     301                if (isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging'])) {
     302                    return $post_data['boxo_packaging'];
     303                }
     304            }
     305
     306            // In custom woocommerce checkouts the value is added to $_POST by our script.
     307            // Also, in non-AJAX requests (eg. checkout submit) the value will be in $_POST.
     308            if (isset($_POST['boxo_packaging']) && is_string($_POST['boxo_packaging'])) {
     309                return $_POST['boxo_packaging'];
     310            }
     311            return null;
    300312        }
    301313    }
  • boxo-return/trunk/readme.txt

    r3375141 r3390679  
    44Requires at least: 6.5
    55Tested up to: 6.8.3
    6 Stable tag: 0.0.62
     6Stable tag: 0.0.63
    77Requires PHP: 7.1
    88License: GPLv2 or later
     
    2626
    2727== Changelog ==
     28
     29= 0.0.63 =
     30Support custom checkouts where the checkout form has been modified.
    2831
    2932= 0.0.62 =
Note: See TracChangeset for help on using the changeset viewer.