diff --git a/assets/css/frontend/mnm-frontend-rtl.css b/assets/css/frontend/mnm-frontend-rtl.css new file mode 100644 index 0000000..564a175 --- /dev/null +++ b/assets/css/frontend/mnm-frontend-rtl.css @@ -0,0 +1,376 @@ +.mnm-variable-product .mnm_table th, .mnm-variable-product .woocommerce-loop-category__title { + text-align: right; } + +.mnm-variable-product.wc-block-grid .wc-block-grid__product .wc-mnm-block-child-item__product-details { + text-align: right; + margin-top: 10px; } + +.mnm-variable-product.wc-block-grid .wc-block-grid__product .child_item__quantity{ + margin-top: 15px; } + +.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity { + background: #006fad; + border: unset; + border-radius: 50%; + color: #fff; + height: 40px; + margin: 0 10px; + max-width: 40px; } + +.mnm-minicart-view-footer-wrapper .woocommerce-variation-add-to-cart { + display: flex; + justify-content: center; + align-items: center; + margin-top: .5em; } + +.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity { + width: 100%; + justify-content: center; + margin: 0 auto; } + +.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button { + background: #e7e9eb; + border-radius: 50%; + width: 40px; + height: 40px; + margin:0; } + +form.cart .mnm-variable-product .mnm_child_products.tabular .quantity.mnm-checkbox-qty { + align-items: center; + flex-flow: column; } + +.mnm-minicart-view-main { + background: #f7f7f7; + bottom: 0; + display: block; + max-height: 75%; + position: fixed; + left: 0; + width: 420px; + z-index: 1000; } + +.mnm-minicart-view-title-wrapper { + background: #c4c4c480; + color: #363636; + cursor:pointer; } + +.mnm-minicart-view-title-wrapper h4 { + font-style: normal; + font-weight: 700; + font-size: 20px; + line-height: 1.2; + margin: 5px 0; } + +.mnm-minicart-view-content-container .minicart-product-grid h4 { + margin-top:5px } + +.mnm-minicart-view-content-wrapper,.mnm-minicart-view-footer-wrapper,.mnm-minicart-view-title-wrapper { + padding: .625em; + position: relative; + text-align: center; } + +.mnm-minicart-view-footer-wrapper p { + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 1.5; + margin: .5em 0 0; } + +.variable-cart-footer-actions a { + color: #cd2653; + font-size: 14px; + line-height: 24px; + margin: 0 16px; + text-decoration: underline; } + +.mnm-minicart-view-footer-wrapper .mnm-minicart-quantity { + font-size: 16px; + line-height: 1.5; } + +.mnm-minicart-price-label { + font-weight: 700; } + +.mnm-minicart-view-footer-wrapper .mnm-minicart-price .price { + font-size: 18px !important; } + +.mnm-minicart-price .mnm-minicart-total-price .woocommerce-Price-amount { + font-size: 16px !important; + font-style: normal; + line-height: 1.5; } + +.mnm-minicart-total-price { + margin-left: 1em; } + +.mnm-minicart-view-content-container { + display: flex; + flex-wrap: wrap; + max-height: 310px; + overflow: scroll; + margin: 0 auto; + text-align: center; } + +.minicart-product-grid { + border: 1px solid #e7e9eb; + font-size: 1.875em; + flex: none; + margin-bottom:5px; + padding: .669em .125em; + position: relative; + width: calc( 33.33% - 13px ); } + +.minicart-product-grid:nth-child(2), +.minicart-product-grid:nth-child(5) { + margin: 0 5px 5px; } + +.minicart-product-grid * { + text-align: center; } + +.mnm-minicart-view-content-container img { + width: 100px; } + +.mnm-minicart-view-content-container h4 { + font-weight: 700; + font-size: 20px; + line-height: 1.1; + margin: 0; + word-break: break-word; } + +.remove-child-item { + background: #cd2653; + border-radius: 50%; + color: #fff; + font-size: 0.733em; + line-height: 1; + position: absolute; + left: .313em; + text-align: center; + top: .5em; + width: 1.1em; + height: 1.1em; } + +.mnm-edit-cart,.mnm-reset-cart,.remove-child-item { + cursor: pointer; } + +.mix-and-match-root.single_mnm_variation { + position: relative; } + +.variable_mnm_form .hide-button input[type=number]::-webkit-inner-spin-button, +.variable_mnm_form .hide-button input[type=number]::-webkit-outer-spin-button, +.variable_mnm_form .hide-button input[type=number] { + -moz-appearance:textfield; + -webkit-appearance: none; + margin: 0; } + +.variable_mnm_form .hide-button .child_item__quantity_input.input-text { + width: 1em; } + +.mnm-hidden, .hidden { + display: none; } + +.mnm-minicart-popup-icon { + position: absolute; + top: calc(50% - 10px); + left: 10px; + max-height: 40px; + overflow: hidden; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty { + --primary: #006fad; + --secondary: #fafbff; + --duration: .5s; + -webkit-appearance: none; + -moz-appearance: none; + -webkit-tap-highlight-color: transparent; + -webkit-mask-image: -webkit-radial-gradient(white, black); + outline: none; + cursor: pointer; + position: relative; + overflow: hidden; + transform-style: preserve-3d; + perspective: 240px; + border-radius: 50%; + width: 36px; + height: 36px; + border: 2px solid var(--primary); + background-size: 300% 300%; + transition: transform .3s; + transform: scale(var(--scale, 1)) translateZ(0); + animation: var(--name, unchecked) var(--duration) ease forwards; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after, +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{ + content: ''; + position: absolute; + width: 16px; + height: var(--height, 16px); + right: 8px; + top: var(--top, 8px); + background: var(--background, var(--primary)); + border:none; + animation: var(--name-icon-b, var(--name-icon, unchecked-icon)) var(--duration) ease forwards; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before { + -webkit-clip-path: polygon(0 6px, 6px 6px, 6px 0, 10px 0, 10px 6px, 16px 6px, 16px 10px, 10px 10px, 10px 16px, 6px 16px, 6px 10px, 0 10px); + clip-path: polygon(0 6px, 6px 6px, 6px 0, 10px 0, 10px 6px, 16px 6px, 16px 10px, 10px 10px, 10px 16px, 6px 16px, 6px 10px, 0 10px); } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after{ + --height: 4px; + --top: 15px; + --background: var(--secondary); + --name-icon-b: var(--name-icon-a, checked-icon); } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:active{ + --scale: .95; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:checked{ + --name: checked; + --name-icon-b: checked-icon; + --name-icon-a: unchecked-icon; } + +.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty .qty{ + justify-content:center; } +.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty { + display: block !important; + width: 100%; } + +.mnm-checkbox-qty-label { + width: 100%;} + +@keyframes checked-icon { + from { + transform: translateZ(12px); } + to { + transform: translateX(-16px) rotateY(-90deg) translateZ(12px); } +} + +@keyframes unchecked-icon { + from { + transform: translateX(16px) rotateY(90deg) translateZ(12px); } + to { + transform: translateZ(12px); } +} + +@keyframes checked { + from { + background-image: radial-gradient(ellipse at center, var(--primary) 0%, var(--primary) 25%, var(--secondary) 25.1%, var(--secondary) 100%); + background-position: 0% 50%; } + to { + background-image: radial-gradient(ellipse at center, var(--primary) 0%, var(--primary) 25%, var(--secondary) 25.1%, var(--secondary) 100%); + background-position: 50% 50%; } +} + +@keyframes unchecked { + from { + background-image: radial-gradient(ellipse at center, var(--secondary) 0%, var(--secondary) 25%, var(--primary) 25.1%, var(--primary) 100%); + background-position: 0% 50%; } + to { + background-image: radial-gradient(ellipse at center, var(--secondary) 0%, var(--secondary) 25%, var(--primary) 25.1%, var(--primary) 100%); + background-position: 50% 50%; } +} + +@media screen and (min-width: 767px) and (max-width: 1200px) { + .mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{ + height: 30px; + margin: 0 5px; + width: 30px; + max-width: 30px; } + + .mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button { + height: 30px; + width: 30px; } +} + +@media screen and (min-width: 601px) { + .mnm-variable-product li.wc-block-grid__product { + background: #f7f7f7; + margin: 20px 0 0; + border-right: unset; + border-left: unset; } + + .mnm-variable-product li.wc-block-grid__product .wc-mnm-block-child-item__product-details, + .mnm-variable-product li.wc-block-grid__product .child_item__quantity, + .mnm-variable-product li.wc-block-grid__product > .product-quantity { + padding:0 10px; } + + .mnm-variable-product .wc-block-grid__products{ + gap: 0 10px; } + + .mnm-variable-product.wc-block-grid.has-2-columns .wc-block-grid__product{ + max-width: calc(50% - 10px); } + + .mnm-variable-product.wc-block-grid.has-3-columns .wc-block-grid__product{ + max-width: calc(33.3333333333% - 10px); } + + .mnm-variable-product.wc-block-grid.has-4-columns .wc-block-grid__product{ + max-width: calc(25% - 10px); } + + .mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{ + max-width: calc(20% - 10px); } + + .mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{ + max-width: calc(16.6666666667% - 10px); } +} + +@media screen and (max-width: 600px) { + .mnm-minicart-view-main { + width: 100%; } + + .mnm-minicart-view-content-container { + background: #6d6d6d; + display: flex; + flex-wrap: nowrap; + height: 110px; + overflow: scroll; } + + .minicart-product-grid { + border:none; + flex: none; + width: 110px; } + + .minicart-product-grid:nth-child(2), + .minicart-product-grid:nth-child(5){ + margin: 0; } + + .mnm-minicart-view-content-container img{ + width: 70px; } + + .mnm-minicart-view-content-container .minicart-product-grid h4{ + display:none; } + + .remove-child-item { + left:10px; + top:6px; } + + .mnm_form.layout_tabular table .woocommerce-loop-product__title, .mnm_form.layout_grid .wc-block-grid__products .wc-block-grid__product-title { + font-weight: 500; + font-size: 20px; + line-height: 24px; } + + .wc-mnm-block-child-item__product-description{ + font-size: 18px; + line-height: 1.4; } + + .wc-mnm-child-item { + display: flex; + width: 100%; + flex-wrap: wrap; + margin-top: 20px; } + + .mnm_form:not('.layout_tabular') .mnm_child_product_images { + display: block; + width: 45%; } + + .mnm_form:not('.layout_tabular') .wc-mnm-block-child-item__product-details { + width: calc( 55% - 20px); + display: block; + margin-right: 20px; + text-align: right; } + + .child_item__quantity.product-quantity.show-button { + display: block; + width: 100%; + margin-top: 20px; } + + .wc-mnm-block-child-item__product-details a { + text-decoration: none; } +} \ No newline at end of file diff --git a/assets/css/frontend/mnm-frontend-rtl.min.css b/assets/css/frontend/mnm-frontend-rtl.min.css new file mode 100644 index 0000000..941686f --- /dev/null +++ b/assets/css/frontend/mnm-frontend-rtl.min.css @@ -0,0 +1 @@ +.mnm-variable-product .mnm_table th,.mnm-variable-product .woocommerce-loop-category__title{text-align:right}.mnm-variable-product.wc-block-grid .wc-block-grid__product .wc-mnm-block-child-item__product-details{text-align:right;margin-top:10px}.mnm-variable-product.wc-block-grid .wc-block-grid__product .child_item__quantity{margin-top:15px}.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{background:#006fad;border:unset;border-radius:50%;color:#fff;height:40px;margin:0 10px;max-width:40px}.mnm-minicart-view-footer-wrapper .woocommerce-variation-add-to-cart{display:flex;justify-content:center;align-items:center;margin-top:.5em}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity{width:100%;justify-content:center;margin:0 auto}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button{background:#e7e9eb;border-radius:50%;width:40px;height:40px;margin:0}form.cart .mnm-variable-product .mnm_child_products.tabular .quantity.mnm-checkbox-qty{align-items:center;flex-flow:column}.mnm-minicart-view-main{background:#f7f7f7;bottom:0;display:block;max-height:75%;position:fixed;left:0;width:420px;z-index:1000}.mnm-minicart-view-title-wrapper{background:#c4c4c480;color:#363636;cursor:pointer}.mnm-minicart-view-title-wrapper h4{font-style:normal;font-weight:700;font-size:20px;line-height:1.2;margin:5px 0}.mnm-minicart-view-content-container .minicart-product-grid h4{margin-top:5px}.mnm-minicart-view-content-wrapper,.mnm-minicart-view-footer-wrapper,.mnm-minicart-view-title-wrapper{padding:.625em;position:relative;text-align:center}.mnm-minicart-view-footer-wrapper p{font-size:18px;font-style:normal;font-weight:400;line-height:1.5;margin:.5em 0 0}.variable-cart-footer-actions a{color:#cd2653;font-size:14px;line-height:24px;margin:0 16px;text-decoration:underline}.mnm-minicart-view-footer-wrapper .mnm-minicart-quantity{font-size:16px;line-height:1.5}.mnm-minicart-price-label{font-weight:700}.mnm-minicart-view-footer-wrapper .mnm-minicart-price .price{font-size:18px!important}.mnm-minicart-price .mnm-minicart-total-price .woocommerce-Price-amount{font-size:16px!important;font-style:normal;line-height:1.5}.mnm-minicart-total-price{margin-left:1em}.mnm-minicart-view-content-container{display:flex;flex-wrap:wrap;max-height:310px;overflow:scroll;margin:0 auto;text-align:center}.minicart-product-grid{border:1px solid #e7e9eb;font-size:1.875em;flex:none;margin-bottom:5px;padding:.669em .125em;position:relative;width:calc(33.33% - 13px)}.minicart-product-grid:nth-child(2),.minicart-product-grid:nth-child(5){margin:0 5px 5px}.minicart-product-grid *{text-align:center}.mnm-minicart-view-content-container img{width:100px}.mnm-minicart-view-content-container h4{font-weight:700;font-size:20px;line-height:1.1;margin:0;word-break:break-word}.remove-child-item{background:#cd2653;border-radius:50%;color:#fff;font-size:.733em;line-height:1;position:absolute;left:.313em;text-align:center;top:.5em;width:1.1em;height:1.1em}.mnm-edit-cart,.mnm-reset-cart,.remove-child-item{cursor:pointer}.mix-and-match-root.single_mnm_variation{position:relative}.variable_mnm_form .hide-button input[type=number],.variable_mnm_form .hide-button input[type=number]::-webkit-inner-spin-button,.variable_mnm_form .hide-button input[type=number]::-webkit-outer-spin-button{-moz-appearance:textfield;-webkit-appearance:none;margin:0}.variable_mnm_form .hide-button .child_item__quantity_input.input-text{width:1em}.hidden,.mnm-hidden{display:none}.mnm-minicart-popup-icon{position:absolute;top:calc(50% - 10px);left:10px;max-height:40px;overflow:hidden}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty{--primary:#006fad;--secondary:#fafbff;--duration:.5s;-webkit-appearance:none;-moz-appearance:none;-webkit-tap-highlight-color:transparent;-webkit-mask-image:-webkit-radial-gradient(white,black);outline:0;cursor:pointer;position:relative;overflow:hidden;transform-style:preserve-3d;perspective:240px;border-radius:50%;width:36px;height:36px;border:2px solid var(--primary);background-size:300% 300%;transition:transform .3s;transform:scale(var(--scale,1)) translateZ(0);animation:var(--name,unchecked) var(--duration) ease forwards}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after,.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{content:'';position:absolute;width:16px;height:var(--height,16px);right:8px;top:var(--top,8px);background:var(--background,var(--primary));border:none;animation:var(--name-icon-b,var(--name-icon,unchecked-icon)) var(--duration) ease forwards}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{-webkit-clip-path:polygon(0 6px,6px 6px,6px 0,10px 0,10px 6px,16px 6px,16px 10px,10px 10px,10px 16px,6px 16px,6px 10px,0 10px);clip-path:polygon(0 6px,6px 6px,6px 0,10px 0,10px 6px,16px 6px,16px 10px,10px 10px,10px 16px,6px 16px,6px 10px,0 10px)}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after{--height:4px;--top:15px;--background:var(--secondary);--name-icon-b:var(--name-icon-a, checked-icon)}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:active{--scale:.95}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:checked{--name:checked;--name-icon-b:checked-icon;--name-icon-a:unchecked-icon}.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty .qty{justify-content:center}.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty{display:block!important;width:100%}.mnm-checkbox-qty-label{width:100%}@keyframes checked-icon{from{transform:translateZ(12px)}to{transform:translateX(-16px) rotateY(-90deg) translateZ(12px)}}@keyframes unchecked-icon{from{transform:translateX(16px) rotateY(90deg) translateZ(12px)}to{transform:translateZ(12px)}}@keyframes checked{from{background-image:radial-gradient(ellipse at center,var(--primary) 0,var(--primary) 25%,var(--secondary) 25.1%,var(--secondary) 100%);background-position:0 50%}to{background-image:radial-gradient(ellipse at center,var(--primary) 0,var(--primary) 25%,var(--secondary) 25.1%,var(--secondary) 100%);background-position:50% 50%}}@keyframes unchecked{from{background-image:radial-gradient(ellipse at center,var(--secondary) 0,var(--secondary) 25%,var(--primary) 25.1%,var(--primary) 100%);background-position:0 50%}to{background-image:radial-gradient(ellipse at center,var(--secondary) 0,var(--secondary) 25%,var(--primary) 25.1%,var(--primary) 100%);background-position:50% 50%}}@media screen and (min-width:767px) and (max-width:1200px){.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{height:30px;margin:0 5px;width:30px;max-width:30px}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button{height:30px;width:30px}}@media screen and (min-width:601px){.mnm-variable-product li.wc-block-grid__product{background:#f7f7f7;margin:20px 0 0;border-right:unset;border-left:unset}.mnm-variable-product li.wc-block-grid__product .child_item__quantity,.mnm-variable-product li.wc-block-grid__product .wc-mnm-block-child-item__product-details,.mnm-variable-product li.wc-block-grid__product>.product-quantity{padding:0 10px}.mnm-variable-product .wc-block-grid__products{gap:0 10px}.mnm-variable-product.wc-block-grid.has-2-columns .wc-block-grid__product{max-width:calc(50% - 10px)}.mnm-variable-product.wc-block-grid.has-3-columns .wc-block-grid__product{max-width:calc(33.3333333333% - 10px)}.mnm-variable-product.wc-block-grid.has-4-columns .wc-block-grid__product{max-width:calc(25% - 10px)}.mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{max-width:calc(20% - 10px)}.mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{max-width:calc(16.6666666667% - 10px)}}@media screen and (max-width:600px){.mnm-minicart-view-main{width:100%}.mnm-minicart-view-content-container{background:#6d6d6d;display:flex;flex-wrap:nowrap;height:110px;overflow:scroll}.minicart-product-grid{border:none;flex:none;width:110px}.minicart-product-grid:nth-child(2),.minicart-product-grid:nth-child(5){margin:0}.mnm-minicart-view-content-container img{width:70px}.mnm-minicart-view-content-container .minicart-product-grid h4{display:none}.remove-child-item{left:10px;top:6px}.mnm_form.layout_grid .wc-block-grid__products .wc-block-grid__product-title,.mnm_form.layout_tabular table .woocommerce-loop-product__title{font-weight:500;font-size:20px;line-height:24px}.wc-mnm-block-child-item__product-description{font-size:18px;line-height:1.4}.wc-mnm-child-item{display:flex;width:100%;flex-wrap:wrap;margin-top:20px}.mnm_form:not('.layout_tabular') .mnm_child_product_images{display:block;width:45%}.mnm_form:not('.layout_tabular') .wc-mnm-block-child-item__product-details{width:calc(55% - 20px);display:block;margin-right:20px;text-align:right}.child_item__quantity.product-quantity.show-button{display:block;width:100%;margin-top:20px}.wc-mnm-block-child-item__product-details a{text-decoration:none}} \ No newline at end of file diff --git a/assets/css/frontend/mnm-frontend.css b/assets/css/frontend/mnm-frontend.css new file mode 100644 index 0000000..1bd749c --- /dev/null +++ b/assets/css/frontend/mnm-frontend.css @@ -0,0 +1,376 @@ +.mnm-variable-product .mnm_table th, .mnm-variable-product .woocommerce-loop-category__title { + text-align: left; } + +.mnm-variable-product.wc-block-grid .wc-block-grid__product .wc-mnm-block-child-item__product-details { + text-align: left; + margin-top: 10px; } + +.mnm-variable-product.wc-block-grid .wc-block-grid__product .child_item__quantity{ + margin-top: 15px; } + +.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity { + background: #006fad; + border: unset; + border-radius: 50%; + color: #fff; + height: 40px; + margin: 0 10px; + max-width: 40px; } + +.mnm-minicart-view-footer-wrapper .woocommerce-variation-add-to-cart { + display: flex; + justify-content: center; + align-items: center; + margin-top: .5em; } + +.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity { + width: 100%; + justify-content: center; + margin: 0 auto; } + +.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button { + background: #e7e9eb; + border-radius: 50%; + width: 40px; + height: 40px; + margin:0; } + +form.cart .mnm-variable-product .mnm_child_products.tabular .quantity.mnm-checkbox-qty { + align-items: center; + flex-flow: column; } + +.mnm-minicart-view-main { + background: #f7f7f7; + bottom: 0; + display: block; + max-height: 75%; + position: fixed; + right: 0; + width: 420px; + z-index: 1000; } + +.mnm-minicart-view-title-wrapper { + background: #c4c4c480; + color: #363636; + cursor:pointer; } + +.mnm-minicart-view-title-wrapper h4 { + font-style: normal; + font-weight: 700; + font-size: 20px; + line-height: 1.2; + margin: 5px 0; } + +.mnm-minicart-view-content-container .minicart-product-grid h4 { + margin-top:5px } + +.mnm-minicart-view-content-wrapper,.mnm-minicart-view-footer-wrapper,.mnm-minicart-view-title-wrapper { + padding: .625em; + position: relative; + text-align: center; } + +.mnm-minicart-view-footer-wrapper p { + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 1.5; + margin: .5em 0 0; } + +.variable-cart-footer-actions a { + color: #cd2653; + font-size: 14px; + line-height: 24px; + margin: 0 16px; + text-decoration: underline; } + +.mnm-minicart-view-footer-wrapper .mnm-minicart-quantity { + font-size: 16px; + line-height: 1.5; } + +.mnm-minicart-price-label { + font-weight: 700; } + +.mnm-minicart-view-footer-wrapper .mnm-minicart-price .price { + font-size: 18px !important; } + +.mnm-minicart-price .mnm-minicart-total-price .woocommerce-Price-amount { + font-size: 16px !important; + font-style: normal; + line-height: 1.5; } + +.mnm-minicart-total-price { + margin-right: 1em; } + +.mnm-minicart-view-content-container { + display: flex; + flex-wrap: wrap; + max-height: 310px; + overflow: scroll; + margin: 0 auto; + text-align: center; } + +.minicart-product-grid { + border: 1px solid #e7e9eb; + font-size: 1.875em; + flex: none; + margin-bottom:5px; + padding: .669em .125em; + position: relative; + width: calc( 33.33% - 13px ); } + +.minicart-product-grid:nth-child(2), +.minicart-product-grid:nth-child(5) { + margin: 0 5px 5px; } + +.minicart-product-grid * { + text-align: center; } + +.mnm-minicart-view-content-container img { + width: 100px; } + +.mnm-minicart-view-content-container h4 { + font-weight: 700; + font-size: 20px; + line-height: 1.1; + margin: 0; + word-break: break-word; } + +.remove-child-item { + background: #cd2653; + border-radius: 50%; + color: #fff; + font-size: 0.733em; + line-height: 1; + position: absolute; + right: .313em; + text-align: center; + top: .5em; + width: 1.1em; + height: 1.1em; } + +.mnm-edit-cart,.mnm-reset-cart,.remove-child-item { + cursor: pointer; } + +.mix-and-match-root.single_mnm_variation { + position: relative; } + +.variable_mnm_form .hide-button input[type=number]::-webkit-inner-spin-button, +.variable_mnm_form .hide-button input[type=number]::-webkit-outer-spin-button, +.variable_mnm_form .hide-button input[type=number] { + -moz-appearance:textfield; + -webkit-appearance: none; + margin: 0; } + +.variable_mnm_form .hide-button .child_item__quantity_input.input-text { + width: 1em; } + +.mnm-hidden, .hidden { + display: none; } + +.mnm-minicart-popup-icon { + position: absolute; + top: calc(50% - 10px); + right: 10px; + max-height: 40px; + overflow: hidden; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty { + --primary: #006fad; + --secondary: #fafbff; + --duration: .5s; + -webkit-appearance: none; + -moz-appearance: none; + -webkit-tap-highlight-color: transparent; + -webkit-mask-image: -webkit-radial-gradient(white, black); + outline: none; + cursor: pointer; + position: relative; + overflow: hidden; + transform-style: preserve-3d; + perspective: 240px; + border-radius: 50%; + width: 36px; + height: 36px; + border: 2px solid var(--primary); + background-size: 300% 300%; + transition: transform .3s; + transform: scale(var(--scale, 1)) translateZ(0); + animation: var(--name, unchecked) var(--duration) ease forwards; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after, +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{ + content: ''; + position: absolute; + width: 16px; + height: var(--height, 16px); + left: 8px; + top: var(--top, 8px); + background: var(--background, var(--primary)); + border:none; + animation: var(--name-icon-b, var(--name-icon, unchecked-icon)) var(--duration) ease forwards; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before { + -webkit-clip-path: polygon(0 6px, 6px 6px, 6px 0, 10px 0, 10px 6px, 16px 6px, 16px 10px, 10px 10px, 10px 16px, 6px 16px, 6px 10px, 0 10px); + clip-path: polygon(0 6px, 6px 6px, 6px 0, 10px 0, 10px 6px, 16px 6px, 16px 10px, 10px 10px, 10px 16px, 6px 16px, 6px 10px, 0 10px); } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after{ + --height: 4px; + --top: 15px; + --background: var(--secondary); + --name-icon-b: var(--name-icon-a, checked-icon); } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:active{ + --scale: .95; } + +.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:checked{ + --name: checked; + --name-icon-b: checked-icon; + --name-icon-a: unchecked-icon; } + +.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty .qty{ + justify-content:center; } +.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty { + display: block !important; + width: 100%; } + +.mnm-checkbox-qty-label { + width: 100%;} + +@keyframes checked-icon { + from { + transform: translateZ(12px); } + to { + transform: translateX(16px) rotateY(90deg) translateZ(12px); } +} + +@keyframes unchecked-icon { + from { + transform: translateX(-16px) rotateY(-90deg) translateZ(12px); } + to { + transform: translateZ(12px); } +} + +@keyframes checked { + from { + background-image: radial-gradient(ellipse at center, var(--primary) 0%, var(--primary) 25%, var(--secondary) 25.1%, var(--secondary) 100%); + background-position: 100% 50%; } + to { + background-image: radial-gradient(ellipse at center, var(--primary) 0%, var(--primary) 25%, var(--secondary) 25.1%, var(--secondary) 100%); + background-position: 50% 50%; } +} + +@keyframes unchecked { + from { + background-image: radial-gradient(ellipse at center, var(--secondary) 0%, var(--secondary) 25%, var(--primary) 25.1%, var(--primary) 100%); + background-position: 100% 50%; } + to { + background-image: radial-gradient(ellipse at center, var(--secondary) 0%, var(--secondary) 25%, var(--primary) 25.1%, var(--primary) 100%); + background-position: 50% 50%; } +} + +@media screen and (min-width: 767px) and (max-width: 1200px) { + .mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{ + height: 30px; + margin: 0 5px; + width: 30px; + max-width: 30px; } + + .mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button { + height: 30px; + width: 30px; } +} + +@media screen and (min-width: 601px) { + .mnm-variable-product li.wc-block-grid__product { + background: #f7f7f7; + margin: 20px 0 0; + border-left: unset; + border-right: unset; } + + .mnm-variable-product li.wc-block-grid__product .wc-mnm-block-child-item__product-details, + .mnm-variable-product li.wc-block-grid__product .child_item__quantity, + .mnm-variable-product li.wc-block-grid__product > .product-quantity { + padding:0 10px; } + + .mnm-variable-product .wc-block-grid__products{ + gap: 0 10px; } + + .mnm-variable-product.wc-block-grid.has-2-columns .wc-block-grid__product{ + max-width: calc(50% - 10px); } + + .mnm-variable-product.wc-block-grid.has-3-columns .wc-block-grid__product{ + max-width: calc(33.3333333333% - 10px); } + + .mnm-variable-product.wc-block-grid.has-4-columns .wc-block-grid__product{ + max-width: calc(25% - 10px); } + + .mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{ + max-width: calc(20% - 10px); } + + .mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{ + max-width: calc(16.6666666667% - 10px); } +} + +@media screen and (max-width: 600px) { + .mnm-minicart-view-main { + width: 100%; } + + .mnm-minicart-view-content-container { + background: #6d6d6d; + display: flex; + flex-wrap: nowrap; + height: 110px; + overflow: scroll; } + + .minicart-product-grid { + border:none; + flex: none; + width: 110px; } + + .minicart-product-grid:nth-child(2), + .minicart-product-grid:nth-child(5){ + margin: 0; } + + .mnm-minicart-view-content-container img{ + width: 70px; } + + .mnm-minicart-view-content-container .minicart-product-grid h4{ + display:none; } + + .remove-child-item { + right:10px; + top:6px; } + + .mnm_form.layout_tabular table .woocommerce-loop-product__title, .mnm_form.layout_grid .wc-block-grid__products .wc-block-grid__product-title { + font-weight: 500; + font-size: 20px; + line-height: 24px; } + + .wc-mnm-block-child-item__product-description{ + font-size: 18px; + line-height: 1.4; } + + .wc-mnm-child-item { + display: flex; + width: 100%; + flex-wrap: wrap; + margin-top: 20px; } + + .mnm_form:not('.layout_tabular') .mnm_child_product_images { + display: block; + width: 45%; } + + .mnm_form:not('.layout_tabular') .wc-mnm-block-child-item__product-details { + width: calc( 55% - 20px); + display: block; + margin-left: 20px; + text-align: left; } + + .child_item__quantity.product-quantity.show-button { + display: block; + width: 100%; + margin-top: 20px; } + + .wc-mnm-block-child-item__product-details a { + text-decoration: none; } +} \ No newline at end of file diff --git a/assets/css/frontend/mnm-frontend.min.css b/assets/css/frontend/mnm-frontend.min.css new file mode 100644 index 0000000..7ec6ceb --- /dev/null +++ b/assets/css/frontend/mnm-frontend.min.css @@ -0,0 +1 @@ +.mnm-variable-product .mnm_table th,.mnm-variable-product .woocommerce-loop-category__title{text-align:left}.mnm-variable-product.wc-block-grid .wc-block-grid__product .wc-mnm-block-child-item__product-details{text-align:left;margin-top:10px}.mnm-variable-product.wc-block-grid .wc-block-grid__product .child_item__quantity{margin-top:15px}.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{background:#006fad;border:unset;border-radius:50%;color:#fff;height:40px;margin:0 10px;max-width:40px}.mnm-minicart-view-footer-wrapper .woocommerce-variation-add-to-cart{display:flex;justify-content:center;align-items:center;margin-top:.5em}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity{width:100%;justify-content:center;margin:0 auto}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button{background:#e7e9eb;border-radius:50%;width:40px;height:40px;margin:0}form.cart .mnm-variable-product .mnm_child_products.tabular .quantity.mnm-checkbox-qty{align-items:center;flex-flow:column}.mnm-minicart-view-main{background:#f7f7f7;bottom:0;display:block;max-height:75%;position:fixed;right:0;width:420px;z-index:1000}.mnm-minicart-view-title-wrapper{background:#c4c4c480;color:#363636;cursor:pointer}.mnm-minicart-view-title-wrapper h4{font-style:normal;font-weight:700;font-size:20px;line-height:1.2;margin:5px 0}.mnm-minicart-view-content-container .minicart-product-grid h4{margin-top:5px}.mnm-minicart-view-content-wrapper,.mnm-minicart-view-footer-wrapper,.mnm-minicart-view-title-wrapper{padding:.625em;position:relative;text-align:center}.mnm-minicart-view-footer-wrapper p{font-size:18px;font-style:normal;font-weight:400;line-height:1.5;margin:.5em 0 0}.variable-cart-footer-actions a{color:#cd2653;font-size:14px;line-height:24px;margin:0 16px;text-decoration:underline}.mnm-minicart-view-footer-wrapper .mnm-minicart-quantity{font-size:16px;line-height:1.5}.mnm-minicart-price-label{font-weight:700}.mnm-minicart-view-footer-wrapper .mnm-minicart-price .price{font-size:18px!important}.mnm-minicart-price .mnm-minicart-total-price .woocommerce-Price-amount{font-size:16px!important;font-style:normal;line-height:1.5}.mnm-minicart-total-price{margin-right:1em}.mnm-minicart-view-content-container{display:flex;flex-wrap:wrap;max-height:310px;overflow:scroll;margin:0 auto;text-align:center}.minicart-product-grid{border:1px solid #e7e9eb;font-size:1.875em;flex:none;margin-bottom:5px;padding:.669em .125em;position:relative;width:calc(33.33% - 13px)}.minicart-product-grid:nth-child(2),.minicart-product-grid:nth-child(5){margin:0 5px 5px}.minicart-product-grid *{text-align:center}.mnm-minicart-view-content-container img{width:100px}.mnm-minicart-view-content-container h4{font-weight:700;font-size:20px;line-height:1.1;margin:0;word-break:break-word}.remove-child-item{background:#cd2653;border-radius:50%;color:#fff;font-size:.733em;line-height:1;position:absolute;right:.313em;text-align:center;top:.5em;width:1.1em;height:1.1em}.mnm-edit-cart,.mnm-reset-cart,.remove-child-item{cursor:pointer}.mix-and-match-root.single_mnm_variation{position:relative}.variable_mnm_form .hide-button input[type=number],.variable_mnm_form .hide-button input[type=number]::-webkit-inner-spin-button,.variable_mnm_form .hide-button input[type=number]::-webkit-outer-spin-button{-moz-appearance:textfield;-webkit-appearance:none;margin:0}.variable_mnm_form .hide-button .child_item__quantity_input.input-text{width:1em}.hidden,.mnm-hidden{display:none}.mnm-minicart-popup-icon{position:absolute;top:calc(50% - 10px);right:10px;max-height:40px;overflow:hidden}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty{--primary:#006fad;--secondary:#fafbff;--duration:.5s;-webkit-appearance:none;-moz-appearance:none;-webkit-tap-highlight-color:transparent;-webkit-mask-image:-webkit-radial-gradient(white,black);outline:0;cursor:pointer;position:relative;overflow:hidden;transform-style:preserve-3d;perspective:240px;border-radius:50%;width:36px;height:36px;border:2px solid var(--primary);background-size:300% 300%;transition:transform .3s;transform:scale(var(--scale,1)) translateZ(0);animation:var(--name,unchecked) var(--duration) ease forwards}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after,.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{content:'';position:absolute;width:16px;height:var(--height,16px);left:8px;top:var(--top,8px);background:var(--background,var(--primary));border:none;animation:var(--name-icon-b,var(--name-icon,unchecked-icon)) var(--duration) ease forwards}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:before{-webkit-clip-path:polygon(0 6px,6px 6px,6px 0,10px 0,10px 6px,16px 6px,16px 10px,10px 10px,10px 16px,6px 16px,6px 10px,0 10px);clip-path:polygon(0 6px,6px 6px,6px 0,10px 0,10px 6px,16px 6px,16px 10px,10px 10px,10px 16px,6px 16px,6px 10px,0 10px)}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:after{--height:4px;--top:15px;--background:var(--secondary);--name-icon-b:var(--name-icon-a, checked-icon)}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:active{--scale:.95}.mnm_form .mnm-variable-product.mnm_child_products .product-quantity .mnm-checkbox-qty .qty:checked{--name:checked;--name-icon-b:checked-icon;--name-icon-a:unchecked-icon}.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty .qty{justify-content:center}.mnm_form.layout_grid.has-center-aligned-quantity .product-quantity .mnm-checkbox-qty{display:block!important;width:100%}.mnm-checkbox-qty-label{width:100%}@keyframes checked-icon{from{transform:translateZ(12px)}to{transform:translateX(16px) rotateY(90deg) translateZ(12px)}}@keyframes unchecked-icon{from{transform:translateX(-16px) rotateY(-90deg) translateZ(12px)}to{transform:translateZ(12px)}}@keyframes checked{from{background-image:radial-gradient(ellipse at center,var(--primary) 0,var(--primary) 25%,var(--secondary) 25.1%,var(--secondary) 100%);background-position:100% 50%}to{background-image:radial-gradient(ellipse at center,var(--primary) 0,var(--primary) 25%,var(--secondary) 25.1%,var(--secondary) 100%);background-position:50% 50%}}@keyframes unchecked{from{background-image:radial-gradient(ellipse at center,var(--secondary) 0,var(--secondary) 25%,var(--primary) 25.1%,var(--primary) 100%);background-position:100% 50%}to{background-image:radial-gradient(ellipse at center,var(--secondary) 0,var(--secondary) 25%,var(--primary) 25.1%,var(--primary) 100%);background-position:50% 50%}}@media screen and (min-width:767px) and (max-width:1200px){.mnm_form .product-quantity.child_item__quantity .quantity .child_item__quantity_input.mnm-quantity{height:30px;margin:0 5px;width:30px;max-width:30px}.mnm_form.has-plus-minus-buttons .product-quantity.child_item__quantity .quantity .button{height:30px;width:30px}}@media screen and (min-width:601px){.mnm-variable-product li.wc-block-grid__product{background:#f7f7f7;margin:20px 0 0;border-left:unset;border-right:unset}.mnm-variable-product li.wc-block-grid__product .child_item__quantity,.mnm-variable-product li.wc-block-grid__product .wc-mnm-block-child-item__product-details,.mnm-variable-product li.wc-block-grid__product>.product-quantity{padding:0 10px}.mnm-variable-product .wc-block-grid__products{gap:0 10px}.mnm-variable-product.wc-block-grid.has-2-columns .wc-block-grid__product{max-width:calc(50% - 10px)}.mnm-variable-product.wc-block-grid.has-3-columns .wc-block-grid__product{max-width:calc(33.3333333333% - 10px)}.mnm-variable-product.wc-block-grid.has-4-columns .wc-block-grid__product{max-width:calc(25% - 10px)}.mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{max-width:calc(20% - 10px)}.mnm-variable-product.wc-block-grid.has-5-columns .wc-block-grid__product{max-width:calc(16.6666666667% - 10px)}}@media screen and (max-width:600px){.mnm-minicart-view-main{width:100%}.mnm-minicart-view-content-container{background:#6d6d6d;display:flex;flex-wrap:nowrap;height:110px;overflow:scroll}.minicart-product-grid{border:none;flex:none;width:110px}.minicart-product-grid:nth-child(2),.minicart-product-grid:nth-child(5){margin:0}.mnm-minicart-view-content-container img{width:70px}.mnm-minicart-view-content-container .minicart-product-grid h4{display:none}.remove-child-item{right:10px;top:6px}.mnm_form.layout_grid .wc-block-grid__products .wc-block-grid__product-title,.mnm_form.layout_tabular table .woocommerce-loop-product__title{font-weight:500;font-size:20px;line-height:24px}.wc-mnm-block-child-item__product-description{font-size:18px;line-height:1.4}.wc-mnm-child-item{display:flex;width:100%;flex-wrap:wrap;margin-top:20px}.mnm_form:not('.layout_tabular') .mnm_child_product_images{display:block;width:45%}.mnm_form:not('.layout_tabular') .wc-mnm-block-child-item__product-details{width:calc(55% - 20px);display:block;margin-left:20px;text-align:left}.child_item__quantity.product-quantity.show-button{display:block;width:100%;margin-top:20px}.wc-mnm-block-child-item__product-details a{text-decoration:none}} \ No newline at end of file diff --git a/assets/icons/close-window.png b/assets/icons/close-window.png new file mode 100644 index 0000000..61025e2 Binary files /dev/null and b/assets/icons/close-window.png differ diff --git a/assets/icons/open-window.png b/assets/icons/open-window.png new file mode 100644 index 0000000..b134f5d Binary files /dev/null and b/assets/icons/open-window.png differ diff --git a/assets/js/frontend/wc-mnm-add-to-cart-variation.js b/assets/js/frontend/wc-mnm-add-to-cart-variation.js index 12cf6c1..344eec2 100644 --- a/assets/js/frontend/wc-mnm-add-to-cart-variation.js +++ b/assets/js/frontend/wc-mnm-add-to-cart-variation.js @@ -1,320 +1,320 @@ /*global WC_MNM_ADD_TO_CART_VARIATION_PARAMS */ ;(function ( $, window, document, undefined ) { - /** - * WC_MNM_Variation_Form class which handles variation forms and attributes. - */ - var WC_MNM_Variation_Form = function( $form ) { - var self = this; - - self.$form = $form; - self.$selectors = $form.find( '.wc-mnm-variations :radio' ); - self.$mnmVariation = $form.find( '.single_mnm_variation' ); - - self.variationData = $form.data( 'product_variations' ); - self.useAjax = false === self.variationData; - self.xhr = false; - self.scroll = false; - self.html_forms = []; // Keyed by variation ID. - self.validation_context = $form.data( 'validation_context' ) || 'add-to-cart'; - - self.storedConfig = []; - - // Add MNM container class. - self.$form.addClass( 'mnm_form variations_form' ); - - // Bind methods. - self.shutdown = self.shutdown.bind( self ); - - // Events. - $form.on( 'found_variation.wc-mnm-variable-form', { mnmVariationForm: self }, self.onFoundVariation ); - $form.on( 'check_radio_variations.wc-mnm-variable-form', { mnmVariationForm: self }, self.checkRadioVariation ); - $form.on( 'update_variation_values', self.shutdown ); - - // Catch initial reset and re-run findVariations with data from radio inputs. - $form.on( 'reset_data', { mnmVariationForm: self }, self.onReset ); - $form.on( 'reload_product_variations', { mnmVariationForm: self }, self.onReload ); - - // Listen for radio change. - $form.on( 'change.wc-mnm-variable-form', '.wc-mnm-variations :radio', { mnmVariationForm: self }, self.onChange ); - - // Stash the configuration for later. - $form.on( - 'wc-mnm-container-quantities-updated', - function(event, container) { - self.storedConfig = container.api.get_container_config(); - } - ); - // Persist config when switching between variations (as much as possible given quantities). - $form.on( - 'wc-mnm-initializing', - function(event, container) { + /** + * WC_MNM_Variation_Form class which handles variation forms and attributes. + */ + var WC_MNM_Variation_Form = function( $form ) { + var self = this; + + self.$form = $form; + self.$selectors = $form.find( '.wc-mnm-variations :radio' ); + self.$mnmVariation = $form.find( '.single_mnm_variation' ); + + self.variationData = $form.data( 'product_variations' ); + self.useAjax = false === self.variationData; + self.xhr = false; + self.scroll = false; + self.html_forms = []; // Keyed by variation ID. + self.validation_context = $form.data( 'validation_context' ) || 'add-to-cart'; + + self.storedConfig = []; + + // Add MNM container class. + self.$form.addClass( 'mnm_form variations_form' ); + + // Bind methods. + self.shutdown = self.shutdown.bind( self ); + + // Events. + $form.on( 'found_variation.wc-mnm-variable-form', { mnmVariationForm: self }, self.onFoundVariation ); + $form.on( 'check_radio_variations.wc-mnm-variable-form', { mnmVariationForm: self }, self.checkRadioVariation ); + $form.on( 'update_variation_values', self.shutdown ); + + // Catch initial reset and re-run findVariations with data from radio inputs. + $form.on( 'reset_data', { mnmVariationForm: self }, self.onReset ); + $form.on( 'reload_product_variations', { mnmVariationForm: self }, self.onReload ); + + // Listen for radio change. + $form.on( 'change.wc-mnm-variable-form', '.wc-mnm-variations :radio', { mnmVariationForm: self }, self.onChange ); + + // Stash the configuration for later. + $form.on( + 'wc-mnm-container-quantities-updated', + function(event, container) { + self.storedConfig = container.api.get_container_config(); + } + ); + // Persist config when switching between variations (as much as possible given quantities). + $form.on( + 'wc-mnm-initializing', + function(event, container) { + + let storedConfig = self.storedConfig; // Set here as child_item.update_quantity() is going to wipe out the container.storedConfig on first pass through for loop. + let maxContainerSize = container.api.get_max_container_size(); - let storedConfig = self.storedConfig; // Set here as child_item.update_quantity() is going to wipe out the container.storedConfig on first pass through for loop. - let maxContainerSize = container.api.get_max_container_size(); + if ( ! isNaN( maxContainerSize ) && container.child_items.length && Object.keys( storedConfig ).length ) { - if ( ! isNaN( maxContainerSize ) && container.child_items.length && Object.keys( storedConfig ).length ) { + // Add up quantities. + for ( let child_item of container.child_items ) { - // Add up quantities. - for ( let child_item of container.child_items ) { + let slotsRemaining = maxContainerSize - container.api.get_container_size(); + let newQty = storedConfig[ child_item.get_item_id() ] || 0; - let slotsRemaining = maxContainerSize - container.api.get_container_size(); - let newQty = storedConfig[ child_item.get_item_id() ] || 0; + if ( slotsRemaining - newQty >= 0 ) { + child_item.update_quantity( newQty ); + } else { + child_item.update_quantity( slotsRemaining ); + break; + } + + } + + } - if ( slotsRemaining - newQty >= 0 ) { - child_item.update_quantity( newQty ); - } else { - child_item.update_quantity( slotsRemaining ); - break; } + ); - } + // Add data to ajax submit when editing a container. + $( document ).on( 'wc_mnm_update_container_order_item_data', { mnmVariationForm: self }, self.addVariationData ); + }; - } + /** + * Shutdown the mix and match listeners + */ + WC_MNM_Variation_Form.prototype.shutdown = function() { + // Shutdown all MNM listeners. Future self: this cannot be removed or chaging the attribute fires the change event on all child items for an unknown reason. + this.$form.find( '*' ).off( '.wc-mnm-form' ); + }; - } - ); + /** + * Triggered when an attribute field changes. + */ + WC_MNM_Variation_Form.prototype.onChange = function( event ) { + + var form = event.data.mnmVariationForm; + + // Set the scroll flag. + form.scroll = true; - // Add data to ajax submit when editing a container. - $( document ).on( 'wc_mnm_update_container_order_item_data', { mnmVariationForm: self }, self.addVariationData ); - }; + form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' ); + form.$form.find( '.wc-no-matching-variations' ).remove(); - /** - * Shutdown the mix and match listeners - */ - WC_MNM_Variation_Form.prototype.shutdown = function() { - // Shutdown all MNM listeners. Future self: this cannot be removed or chaging the attribute fires the change event on all child items for an unknown reason. - this.$form.find( '*' ).off( '.wc-mnm-form' ); - }; + if ( form.useAjax ) { + form.$form.trigger( 'check_radio_variations' ); + } else { + form.$form.trigger( 'woocommerce_variation_select_change' ); + form.$form.trigger( 'check_radio_variations' ); + } - /** - * Triggered when an attribute field changes. - */ - WC_MNM_Variation_Form.prototype.onChange = function( event ) { + // Custom event for when variation selection has been changed + form.$form.trigger( 'woocommerce_variation_has_changed' ); + }; - var form = event.data.mnmVariationForm; + /** + * Custom callback to tell Woo to check variations with our radio attributes. + */ + WC_MNM_Variation_Form.prototype.checkRadioVariation = function( event ) { + var form = event.data.mnmVariationForm; + var chosenAttributes = form.radioGetChosenAttributes( form.$form ); + form.$form.trigger( 'check_variations', chosenAttributes ); + }; - // Set the scroll flag. - form.scroll = true; + /** + * When variation is found, load the MNM form. + */ + WC_MNM_Variation_Form.prototype.onFoundVariation = function( event, variation ) { - form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' ); - form.$form.find( '.wc-no-matching-variations' ).remove(); - if ( form.useAjax ) { - form.$form.trigger( 'check_radio_variations' ); - } else { - form.$form.trigger( 'woocommerce_variation_select_change' ); - form.$form.trigger( 'check_radio_variations' ); - } - // Custom event for when variation selection has been changed - form.$form.trigger( 'woocommerce_variation_has_changed' ); - }; + let form = event.data.mnmVariationForm; - /** - * Custom callback to tell Woo to check variations with our radio attributes. - */ - WC_MNM_Variation_Form.prototype.checkRadioVariation = function( event ) { - var form = event.data.mnmVariationForm; - var chosenAttributes = form.radioGetChosenAttributes( form.$form ); - form.$form.trigger( 'check_variations', chosenAttributes ); - }; - /** - * When variation is found, load the MNM form. - */ - WC_MNM_Variation_Form.prototype.onFoundVariation = function( event, variation ) { + if ( variation.variation_is_visible ) { - + let $target = form.$mnmVariation; - let form = event.data.mnmVariationForm; - + // form.$mnmVariation.data( 'product_id', variation.variation_id ); - if ( variation.variation_is_visible ) { + event.currentTarget.querySelector('.mix-and-match-root').setAttribute( 'data-product_id', variation.variation_id ); - let $target = form.$mnmVariation; - // form.$mnmVariation.data( 'product_id', variation.variation_id ); - event.currentTarget.querySelector('.mix-and-match-root').setAttribute( 'data-product_id', variation.variation_id ); + // Fire MNM scripts. + //$( event.target ).trigger( 'wc-mnm-initialize.mix-and-match' ); - + // Dynamically store variation ID in place that is automatically include in submit data when editing container. + // $( event.target ).data( 'variation_id', variation.variation_id ); - // Fire MNM scripts. - //$( event.target ).trigger( 'wc-mnm-initialize.mix-and-match' ); - - // Dynamically store variation ID in place that is automatically include in submit data when editing container. - // $( event.target ).data( 'variation_id', variation.variation_id ); - - if ( ! $target.wcMNMisInViewport() && this.scroll && false !== $( document.body ).triggerHandler( 'wc_mnm_scroll_to_variation' ) ) { - $( 'html,body' ).animate( - { - scrollTop: $target.offset().top + if ( ! $target.wcMNMisInViewport() && this.scroll && false !== $( document.body ).triggerHandler( 'wc_mnm_scroll_to_variation' ) ) { + $( 'html,body' ).animate( + { + scrollTop: $target.offset().top + } + ); } - ); - } - - $( event.target ).trigger( 'wc_mnm_variation_form_loaded', [ variation ] ); - } + $( event.target ).trigger( 'wc_mnm_variation_form_loaded', [ variation ] ); - }; + } - // Uncheeck all radio buttons when reset. - WC_MNM_Variation_Form.prototype.onReset = function( event ) { + }; - let form = event.data.mnmVariationForm; + // Uncheeck all radio buttons when reset. + WC_MNM_Variation_Form.prototype.onReset = function( event ) { - // Woo core's first pass at checking variations will not find the match because it is looking specifically for its elements. + if ( ! form.initialized ) { + form.initialized = true; + form.$form.trigger( 'check_radio_variations' ); + return false; + } - event.currentTarget.querySelector('.mix-and-match-root').setAttribute( 'data-product_id', '' ); + // Reset stored config. + form.storedConfig = []; - form.$selectors.prop( 'checked', false ); + event.currentTarget.querySelector('.mix-and-match-root').setAttribute( 'data-product_id', '' ); - form.$form.find( '.reset_variations' ).css( 'visibility', 'hidden' ); + form.$selectors.prop( 'checked', false ); - $( event.target ).trigger( 'wc_mnm_variation_reset' ); - }; + form.$form.find( '.reset_variations' ).css( 'visibility', 'hidden' ); - - // Uncheeck all radio buttons when reset. - WC_MNM_Variation_Form.prototype.onReload = function( event ) { - var form = event.data.mnmVariationForm; - form.$form.trigger( 'check_radio_variations' ); - }; + $( event.target ).trigger( 'wc_mnm_variation_reset' ); + }; - /** - * Get chosen attributes from form. - * @return array - */ - WC_MNM_Variation_Form.prototype.radioGetChosenAttributes = function( $form ) { - var data = {}; - var count = 0; - var chosen = 0; - $form.find( '.wc-mnm-variations' ).each( - function() { - var attribute_name = $( this ).find( 'input:radio' ).first().attr( 'name' ); - var value = $( this ).find( 'input:checked' ).val() || ''; + // Uncheeck all radio buttons when reset. + WC_MNM_Variation_Form.prototype.onReload = function( event ) { + var form = event.data.mnmVariationForm; + form.$form.trigger( 'check_radio_variations' ); + }; - if ( value.length > 0 ) { - chosen ++; - } + /** + * Get chosen attributes from form. + * @return array + */ + WC_MNM_Variation_Form.prototype.radioGetChosenAttributes = function( $form ) { + var data = {}; + var count = 0; + var chosen = 0; + + $form.find( '.wc-mnm-variations' ).each( + function() { + var attribute_name = $( this ).find( 'input:radio' ).first().attr( 'name' ); + var value = $( this ).find( 'input:checked' ).val() || ''; + + if ( value.length > 0 ) { + chosen ++; + } + + count ++; + data[ attribute_name ] = value; + } + ); - count ++; - data[ attribute_name ] = value; - } - ); + return { + 'count' : count, + 'chosenCount': chosen, + 'data' : data + }; + + }; - return { - 'count' : count, - 'chosenCount': chosen, - 'data' : data + /** + * Add variation_id to $_POST + * + */ + WC_MNM_Variation_Form.prototype.addVariationData = function( event ) { + var form = event.data.mnmVariationForm; + return { variation_id: form.$form.data( 'variation_id' ) || 0 }; }; - }; - - /** - * Add variation_id to $_POST - * - */ - WC_MNM_Variation_Form.prototype.addVariationData = function( event ) { - var form = event.data.mnmVariationForm; - return { variation_id: form.$form.data( 'variation_id' ) || 0 }; - }; - - /** - * Check if a node is blocked for processing. - * - * @param {JQuery Object} $node - * @return {bool} True if the DOM Element is UI Blocked, false if not. - */ - WC_MNM_Variation_Form.prototype.is_blocked = function( $node ) { - return $node.is( '.processing' ) || $node.parents( '.processing' ).length; - }; - - /** - * Block a node visually for processing. - * - * @param {JQuery Object} $node - */ - WC_MNM_Variation_Form.prototype.block = function( $node ) { - if ( ! WC_MNM_Variation_Form.prototype.is_blocked( $node ) ) { - $node.addClass( 'processing' ).block( + /** + * Check if a node is blocked for processing. + * + * @param {JQuery Object} $node + * @return {bool} True if the DOM Element is UI Blocked, false if not. + */ + WC_MNM_Variation_Form.prototype.is_blocked = function( $node ) { + return $node.is( '.processing' ) || $node.parents( '.processing' ).length; + }; + + /** + * Block a node visually for processing. + * + * @param {JQuery Object} $node + */ + WC_MNM_Variation_Form.prototype.block = function( $node ) { + if ( ! WC_MNM_Variation_Form.prototype.is_blocked( $node ) ) { + $node.addClass( 'processing' ).block( { - message: null, - theme: true - } + message: null, + theme: true + } ); - } - }; - - /** - * Unblock a node after processing is complete. - * - * @param {JQuery Object} $node - */ - WC_MNM_Variation_Form.prototype.unblock = function( $node ) { - $node.removeClass( 'processing' ).unblock(); - }; - - - /*-----------------------------------------------------------------*/ - /* Helpers. */ - /*-----------------------------------------------------------------*/ - - $.fn.wcMNMisInViewport = function() { - - if ( ! this.length ) { - return true; - } - var elementTop = $( this ).offset().top; - var elementBottom = elementTop + $( this ).outerHeight(); - var viewportTop = $( window ).scrollTop(); - var viewportBottom = viewportTop + $( window ).height(); - return elementBottom > viewportTop && elementTop < viewportBottom; - }; - - /*-----------------------------------------------------------------*/ - /* Initialization. */ - /*-----------------------------------------------------------------*/ - - /** - * Function to call wc_mnm_variation_form on jquery selector. - */ - $.fn.wc_mnm_variation_form = function() { - if ( typeof WC_MNM_ADD_TO_CART_VARIATION_PARAMS !== 'undefined' && typeof wc_add_to_cart_variation_params !== 'undefined' && typeof wc_mnm_params !== 'undefined' ) { - $( this ).wc_variation_form(); - new WC_MNM_Variation_Form( this ); - } - return this; - }; - -$( - function() { - $( document ).on( - 'wc-mnm-initialize.variable-mix-and-match', - '.variable_mnm_form', - function() { - $( this ).wc_mnm_variation_form(); - } - ); - - $( '.variable_mnm_form' ).each( + } + }; + + /** + * Unblock a node after processing is complete. + * + * @param {JQuery Object} $node + */ + WC_MNM_Variation_Form.prototype.unblock = function( $node ) { + $node.removeClass( 'processing' ).unblock(); + }; + + + /*-----------------------------------------------------------------*/ + /* Helpers. */ + /*-----------------------------------------------------------------*/ + + $.fn.wcMNMisInViewport = function() { + + if ( ! this.length ) { + return true; + } + var elementTop = $( this ).offset().top; + var elementBottom = elementTop + $( this ).outerHeight(); + var viewportTop = $( window ).scrollTop(); + var viewportBottom = viewportTop + $( window ).height(); + return elementBottom > viewportTop && elementTop < viewportBottom; + }; + + /*-----------------------------------------------------------------*/ + /* Initialization. */ + /*-----------------------------------------------------------------*/ + + /** + * Function to call wc_mnm_variation_form on jquery selector. + */ + $.fn.wc_mnm_variation_form = function() { + if ( typeof WC_MNM_ADD_TO_CART_VARIATION_PARAMS !== 'undefined' && typeof wc_add_to_cart_variation_params !== 'undefined' && typeof wc_mnm_params !== 'undefined' ) { + $( this ).wc_variation_form(); + new WC_MNM_Variation_Form( this ); + } + return this; + }; + + $( function() { - $( this ).trigger( 'wc-mnm-initialize.variable-mix-and-match' ); + $( document ).on( + 'wc-mnm-initialize.variable-mix-and-match', + '.variable_mnm_form', + function() { + $( this ).wc_mnm_variation_form(); + } + ); + + $( '.variable_mnm_form' ).each( + function() { + $( this ).trigger( 'wc-mnm-initialize.variable-mix-and-match' ); + } + ); } ); - } -); -} )( jQuery, window, document ); +} )( jQuery, window, document ); \ No newline at end of file diff --git a/includes/class-wc-mnm-variable-transients.php b/includes/class-wc-mnm-variable-transients.php new file mode 100644 index 0000000..25eb6f2 --- /dev/null +++ b/includes/class-wc-mnm-variable-transients.php @@ -0,0 +1,86 @@ +query_containers_by_product( $post_id ); + + if ( ! empty( $container_ids ) ) { + foreach( $container_ids as $id ) { + self::delete_transients( $id ); + } + } + + + } + + + /** + * Clear ALL form transients. + * + * @param string $key + * @param string $group_key + * @return mixed + */ + public static function delete_transients( $product_id, $variation_id = '', $category_ids = array() ) { + + global $wpdb; + $prefix = 'wc_mnm_variation_add_to_cart_' . $product_id; + + if ( $variation_id ) { + $prefix .= '_variation:' . $variation_id; + } + $transients = $wpdb->get_col( "SELECT `option_name` FROM $wpdb->options WHERE `option_name` LIKE '%{$prefix}%'" ); + foreach( $transients as $transient ){ + delete_transient( str_replace($prefix, '', $transient) ); + } + } + +} //end class +WC_MNM_Variable_Transients::init(); \ No newline at end of file diff --git a/includes/class-wc-product-mix-and-match-variation.php b/includes/class-wc-product-mix-and-match-variation.php index e952084..df36549 100755 --- a/includes/class-wc-product-mix-and-match-variation.php +++ b/includes/class-wc-product-mix-and-match-variation.php @@ -60,6 +60,22 @@ public function get_type() { return 'mix-and-match-variation'; } + /** + * Cache key. + * + * @return string + */ + public function get_cache_key() { + + $key = $this->get_parent_id() . '_variation:' . $this->get_id(); + + if ( 'categories' === $this->get_content_source() ) { + $key .= '-cats:' . implode( '|', $this->get_child_category_ids() ); + } + + return $key; + } + /** * Share content getter. @@ -110,6 +126,33 @@ public function get_child_category_ids( $context = 'view' ) { } + /** + * Category contents with title getter. + * + * @param string $context + * @return array + */ + public function get_child_categories( $context = 'view' ) { + + // Inherit value from parent if sharing content. + if ( $this->is_sharing_content( $context ) ) { + $value = 'view' === $context ? apply_filters( $this->get_hook_prefix() . 'child_category_ids', $this->parent_data['child_category_ids'], $this ) : $this->parent_data['child_category_ids']; + $categories = []; + if( !empty( $value ) && is_array( $value ) ){ + foreach ($value as $category_id){ + $category = get_term_by('term_taxonomy_id',$category_id); + $categories[$category_id] = !empty($category->name) ? $category->name : ''; + } + $value = !empty( $categories ) ? $categories : $value; + } + } else { + $value = $this->get_prop( 'child_category_ids', $context ); + } + + return $value; + } + + /** * "Form Location" getter. * diff --git a/includes/compatibility/modules/apfs/class-wc-mnm-variable-apfs-switching-compatibility.php b/includes/compatibility/modules/apfs/class-wc-mnm-variable-apfs-switching-compatibility.php index b68c41f..82397f0 100644 --- a/includes/compatibility/modules/apfs/class-wc-mnm-variable-apfs-switching-compatibility.php +++ b/includes/compatibility/modules/apfs/class-wc-mnm-variable-apfs-switching-compatibility.php @@ -50,7 +50,7 @@ public static function add_hooks() { */ public static function get_posted_container_form_data( $form_data, $configuration, $container ) { - if ( $container->is_type( 'mix-and-match-variation' ) ) { + if ( $container && $container->is_type( 'mix-and-match-variation' ) ) { $attributes = array_filter( $container->get_variation_attributes(), 'wc_array_filter_default_attributes' ); diff --git a/includes/rest-api/class-wc-mnm-variable-store-api.php b/includes/rest-api/class-wc-mnm-variable-store-api.php index 79b1f3a..504e903 100644 --- a/includes/rest-api/class-wc-mnm-variable-store-api.php +++ b/includes/rest-api/class-wc-mnm-variable-store-api.php @@ -153,9 +153,13 @@ public static function extend_mnm_product_data( $product ) { $item_data['packing_mode'] = $product->get_packing_mode(); $item_data['shipped_per_product'] = ! $product->is_packed_together(); $item_data['weight_cumulative'] = $product->is_weight_cumulative(); - $item_data['discount'] = $product->get_discount(); - $item_data['child_items'] = self::prepare_child_items_response( $product ); - } + $item_data['discount'] = $product->get_discount(); + $item_data['child_items'] = self::prepare_child_items_response( $product ); + if( !empty( $item_data['content_source'] ) && $item_data['content_source'] !== 'products' ) { + $item_data['child_categories'] = $product->get_child_categories($product); + } + + } return $item_data; } @@ -444,7 +448,8 @@ private static function prepare_child_items_response( $product ) { 'images' => self::get_images( $child_item, $product ), 'name' => $child_item->get_product()->get_name(), 'permalink' => $child_item->get_product()->get_permalink(), - 'short_description' => $child_item->get_product()->get_short_description(), + 'short_description' => $child_item->get_product()->get_short_description(), + 'category_ids' => $child_item->get_product()->get_category_ids() ), $child_item, $product ); } @@ -540,7 +545,11 @@ public static function preload_response() { $data = $rest_preload_api_requests[$rest_route]['body']['extensions']->variable_mix_and_match['variations'] ?? []; // Currently this will only support 1 vmnm product per page. - Automattic\WooCommerce\Blocks\Package::container()->get( Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry::class )->add( 'wcMNMVariableSettings', $data ); + $data_form_location = !empty( $data[0]['extensions']->mix_and_match['form_location'] ) ? $data[0]['extensions']->mix_and_match['form_location'] : ''; + + if( !empty( $data_form_location ) && $data_form_location !== 'after_summary' ){ + Automattic\WooCommerce\Blocks\Package::container()->get( Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry::class )->add( 'wcMNMVariableSettings', $data ); + } } } diff --git a/includes/wc-mnm-variable-template-functions.php b/includes/wc-mnm-variable-template-functions.php index 3f0ed31..4a5a7fe 100644 --- a/includes/wc-mnm-variable-template-functions.php +++ b/includes/wc-mnm-variable-template-functions.php @@ -48,6 +48,11 @@ function wc_mnm_variable_template_add_to_cart( $custom_product = false ) { // Get Available variations? $get_variations = count( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product ); + // Input name. Technically, this should be the variation's ID, but as long as nobody is filtering it, it will be fine. + $name = wc_mnm_get_child_input_name( $product->get_id() ); + + $config = isset( $_GET[$name] ) ? array_map( 'intval', $_GET[$name] ) : []; + // Load the template. wc_get_template( 'single-product/add-to-cart/variable-mnm.php', @@ -56,6 +61,7 @@ function wc_mnm_variable_template_add_to_cart( $custom_product = false ) { 'attributes' => $product->get_variation_attributes(), 'selected_attributes' => $product->get_default_attributes(), 'classes' => wc_mnm_get_form_classes( array( 'variable_mnm_form' ), $product ), + 'config' => $config, ), '', WC_MNM_Variable::get_instance()->get_plugin_path() . 'templates/' @@ -67,12 +73,30 @@ function wc_mnm_variable_template_add_to_cart( $custom_product = false ) { } } +if( ! function_exists( 'wc_mnm_template_override' ) ) { + + function wc_mnm_template_override( $template, $template_name, $template_path ) { + global $product; + + $product_type = is_object($product) ? $product->get_type() : ''; + $plugin_path = WC_MNM_Variable::get_instance()->get_plugin_path() . 'templates/'; + + if( $template_name === 'single-product/add-to-cart/variation-add-to-cart-button.php' && $product_type === 'variable-mix-and-match' ) { + if( file_exists( $plugin_path . $template_name ) ){ + $template = $plugin_path . $template_name; + } + } + + return $template; + } +} + if ( ! function_exists( 'wc_mnm_variable_template_add_to_cart_after_summary' ) ) { /** * Add-to-cart template for Mix and Match. Handles the 'Form location > After summary' case. */ - function wc_mnm_variable_template_add_to_cart_after_summary() { + function wc_mnm_variable_template_add_to_cart_after_summary() { global $product; @@ -109,14 +133,35 @@ function wc_mnm_variation_add_to_cart( $variation ) { */ // Load the template. - wc_get_template( - 'single-product/add-to-cart/mnm-variation-add-to-cart.php', - array( - 'variation' => $variation, - ), - '', - WC_MNM_Variable::get_instance()->get_plugin_path() . 'templates/' - ); + $cached_key = 'wc_mnm_variation_add_to_cart_' . $variation->get_cache_key(); + + $html = get_transient( $cached_key ); + + if ( false === $html ) { + ob_start(); + + echo '
'; + + /** + * 'wc_mnm_content_loop' action. + * + * @param WC_Mix_and_Match_Variation $variation + * + * @hooked wc_mnm_variation_header - 10 + * @hooked wc_mnm_content_loop - 20 + * @hooked wc_mnm_template_reset_link - 30 + * @hooked wc_mnm_template_status - 40 + */ + do_action( 'wc_mnm_variation_content_loop', $variation ); + + echo '
'; + + $html = ob_get_clean(); + + set_transient($cached_key, $html, WEEK_IN_SECONDS); + } + + echo $html; } } @@ -270,7 +315,18 @@ function wc_mnm_template_edit_variable_container_order_item( $product, $order_it // Get Available variations? $get_variations = count( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product ); - + + $config = []; + + // Input name. + $name = wc_mnm_get_child_input_name( $variation->get_id() ); + + // Initialize form state based on the actual configuration of the container. + $configuration = WC_Mix_and_Match_Order::get_current_container_configuration( $order_item, $order ); + + // Rebuild config. + $config = WC_Mix_and_Match()->cart->rebuild_posted_container_form_data( $configuration ); + wc_get_template( 'edit-order-item/edit-variable-container.php', array( @@ -280,6 +336,7 @@ function wc_mnm_template_edit_variable_container_order_item( $product, $order_it 'available_variations' => $get_variations ? $product->get_available_variations(): false, 'attributes' => $product->get_variation_attributes(), 'source' => $source, + 'config' => $config, ), '', WC_MNM_Variable::get_instance()->get_plugin_path() . 'templates/' diff --git a/includes/wc-mnm-variable-template-hooks.php b/includes/wc-mnm-variable-template-hooks.php index cf09124..74bb0af 100644 --- a/includes/wc-mnm-variable-template-hooks.php +++ b/includes/wc-mnm-variable-template-hooks.php @@ -9,6 +9,7 @@ defined( 'ABSPATH' ) || exit; // Add to cart template. +add_filter( 'woocommerce_locate_template', 'wc_mnm_template_override', 1, 3 ); add_action( 'woocommerce_variable-mix-and-match_add_to_cart', 'wc_setup_loop', 0 ); add_action( 'woocommerce_variable-mix-and-match_add_to_cart', 'wc_mnm_variable_template_add_to_cart' ); diff --git a/package.json b/package.json index f3ce02b..2c2303b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "WooCommerce Mix and Match Products - Variable Mix and Match", "name": "wc-mnm-variable", - "version": "1.0.0-reactified.alpha-2", + "version": "1.0.0-rc.13", "description": "Make different sized Mix and Match packs all in once place!", "main": "Gruntfile.js", "license": "GPL-3.0", diff --git a/readme.txt b/readme.txt index fe2f201..34860f4 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Contributors: helgatheviking Tags: woocommerce, product, container, boxes, kits, configurable, filter Requires at least: 6.0 Tested up to: 6.0 -Stable tag: 1.0.0-reactified.alpha-2 +Stable tag: 1.0.0-rc.13 WC requires at least: 6.0 WC tested up to: 7.0 License: GNU General Public License v3.0 diff --git a/src/MixAndMatchApp.js b/src/MixAndMatchApp.js index c945c23..75332e2 100644 --- a/src/MixAndMatchApp.js +++ b/src/MixAndMatchApp.js @@ -101,7 +101,7 @@ export default function MixAndMatchApp( {target} ) { } return ( - + ) } diff --git a/src/add-to-cart/ChildItems/ChildItem/ChildItem.js b/src/add-to-cart/ChildItems/ChildItem/ChildItem.js index 28c54f0..ca9b9ff 100644 --- a/src/add-to-cart/ChildItems/ChildItem/ChildItem.js +++ b/src/add-to-cart/ChildItems/ChildItem/ChildItem.js @@ -15,42 +15,86 @@ import ProductQty from './ProductQty'; function ChildItem() { - const childItem = useContext(ChildContext); + const {childItem,isReset} = useContext(ChildContext); + const params = new URLSearchParams(window.location.search); const [quantity, setQuantity] = useState(0); + const [isQuantity, setIsQuantity] = useState(false); const { name, images, catalog_visibility, purchasable } = childItem; const firstImage = images.length ? images[ 0 ] : {}; const permalink = catalog_visibility === 'hidden' || catalog_visibility === 'search' ? false : childItem.permalink; - - const handleQuantityChange = (value) => { + const isGridLayout = WC_MNM_ADD_TO_CART_REACT_PARAMS.display_layout === 'grid'; + + /** + * Handle the child product quantity change event. + * + * @param value Get the item quantity. + * @param isQtyReset product reset or not. + * + * @since 1.0.0 + */ + const handleQuantityChange = (value, isQtyReset = false) => { setQuantity(value); + if(isQtyReset){ + setTimeout( function (){ + setIsQuantity(false); + },500); + } }; - // Fetch the inital product on page load. + /** + * Fetch the initial product on page load. + * + * @since 1.0.0 + */ useEffect(() => { - const initialQty = childItem.qty || 0; + let initialQty = childItem.qty || 0; setQuantity(initialQty); + if( params.get(`mnm_quantity[${childItem.child_id}]`) && isQuantity !== 0){ + setIsQuantity(true); + } }, [] ); - return ( - + /** + * Handle Clear all button event in child product. + * + * @since 1.0.0 + */ + if(isReset && quantity !== 0 && ( ! isQuantity || ( params.get(`mnm_quantity[${childItem.child_id}]`) && quantity !== params.get(`mnm_quantity[${childItem.child_id}]`) ) ) ){ + if( isQuantity ) { + handleQuantityChange(params.get(`mnm_quantity[${childItem.child_id}]`), true); + }else{ + setQuantity(0); + } + } + + return ( + isGridLayout ? (
  • - { WC_MNM_ADD_TO_CART_REACT_PARAMS.display_thumbnails && ( - - ) } - - - -
  • - + + ) : ( + + { WC_MNM_ADD_TO_CART_REACT_PARAMS.display_thumbnails && ( + + + + ) } + + + + + + + + ) ) } diff --git a/src/add-to-cart/ChildItems/ChildItem/ProductDetails.js b/src/add-to-cart/ChildItems/ChildItem/ProductDetails.js index efd9ed8..2b77c8d 100644 --- a/src/add-to-cart/ChildItems/ChildItem/ProductDetails.js +++ b/src/add-to-cart/ChildItems/ChildItem/ProductDetails.js @@ -16,7 +16,7 @@ import ProductPrice from './ProductPrice'; function ProductDetails() { const container = useContext(ContainerContext); - const childItem = useContext(ChildContext); + const {childItem} = useContext(ChildContext); const { name, catalog_visibility, permalink, short_description, price_html } = childItem; diff --git a/src/add-to-cart/ChildItems/ChildItem/ProductImage.js b/src/add-to-cart/ChildItems/ChildItem/ProductImage.js index b201fbb..7332e66 100644 --- a/src/add-to-cart/ChildItems/ChildItem/ProductImage.js +++ b/src/add-to-cart/ChildItems/ChildItem/ProductImage.js @@ -13,6 +13,8 @@ const ProductImage = ( { permalink } ) => { + const imageSrc = image.src ? image.src : PLACEHOLDER_IMG_SRC; + const imageProps = image.src ? { src: image.src, @@ -20,6 +22,9 @@ const ProductImage = ( { decodeEntities( image.alt ) || fallbackAlt || __( 'Product Image', 'woocommmerce-mix-and-match-products' ), + className: 'attachment-woocommerce_thumbnail size-woocommerce_thumbnail', + 'data-large_image': imageSrc, + 'loading': "lazy" } : { src: PLACEHOLDER_IMG_SRC, @@ -28,24 +33,34 @@ const ProductImage = ( { if ( permalink ) { return ( - - { - +
    +
    + + { + +
    +
    ) } return ( - { +
    +
    + + { + +
    +
    ); }; diff --git a/src/add-to-cart/ChildItems/ChildItem/ProductQty.js b/src/add-to-cart/ChildItems/ChildItem/ProductQty.js index 02b0de4..d785b1e 100644 --- a/src/add-to-cart/ChildItems/ChildItem/ProductQty.js +++ b/src/add-to-cart/ChildItems/ChildItem/ProductQty.js @@ -1,10 +1,10 @@ /** * External dependencies */ -import { useState } from "react"; -import { useContext } from '@wordpress/element'; -import { sprintf, _x } from '@wordpress/i18n'; +import {useContext, RawHTML, useEffect,useState} from '@wordpress/element'; +import { sprintf, _x, __ } from '@wordpress/i18n'; import { useDebouncedCallback } from 'use-debounce'; +import { PLACEHOLDER_IMG_SRC } from '@woocommerce/settings'; /** * Internal dependencies @@ -19,36 +19,420 @@ function ProductQty( { max, step = 1, value, - onChange, + onChange } ) { - const childItem = useContext(ChildContext); + const { childItem } = useContext(ChildContext); + const [containerMaxSize, setContainerMaxSize] = useState(1); + const [isCheckboxChecked, setCheckboxChecked] = useState(0); + + useEffect(() => { + + setCheckboxChecked(value); + + window.onbeforeunload = function() { + localStorage.removeItem('productLoaded'); + }; + }, [value]); const hasMaximum = typeof max !== 'undefined'; const isSelectable = childItem.purchasable && childItem.in_stock; - const handleMinusClick = () => { - const newValue = value - step; - if (newValue >= min) { - onChange(newValue); + const woocommerceVariationAddToCart = '.woocommerce-variation-add-to-cart'; + const singleAddToCartButton = '.single_add_to_cart_button'; + const childItemQuantityInput = '.child_item__quantity_input'; + const mixAndMatchRoot = '.wc-block-components-product-add-to-cart-loading'; + const hasButton = WC_MNM_ADD_TO_CART_REACT_PARAMS.display_plus_minus_buttons ? 'show-button' : 'hide-button'; + const childItemQuantityCheckbox = '.mnm_child_products .mnm-checkbox-qty input[type="checkbox"].mnm-quantity'; + let selectedChildItems = []; + let imageSrc = childItem.images.length ? childItem.images[ 0 ] : PLACEHOLDER_IMG_SRC; + imageSrc = imageSrc.src ? imageSrc.src : PLACEHOLDER_IMG_SRC; + + /** + * Display the loader. + * + * @since 1.0.0 + */ + const displayLoader = () => { + document.querySelectorAll(mixAndMatchRoot).forEach( (loader) => { + loader.style.display = 'block'; + }); + }; + + /** + * Remove the loader. + * + * @since 1.0.0 + */ + const removeLoader = () => { + document.querySelectorAll(mixAndMatchRoot).forEach( (loader) => { + loader.style.display = 'none'; + }); + }; + + /** + * Manage Remove child item from minicart box. + * + * @param event Event object. + * + * @since 1.0.0 + */ + const handleRemoveChildItem = (event) => { + displayLoader(); + const productName = event.target.getAttribute('data-product'); + const childProduct = document.querySelector(`[name="${productName}"]`); + + if (childProduct.type === 'number') { + childProduct.value = childProduct.value - 1; + const minusButton = childProduct.parentNode.querySelector('.button--minus'); + minusButton.dispatchEvent(clickEvent); + } else { + childProduct.dispatchEvent(clickEvent); + childProduct.checked = false; } }; - const handlePlusClick = () => { - const newValue = value + step; - if (newValue <= max) { - onChange(newValue); + /** + * Enable cart button. + * + * @since 1.0.0 + */ + const enabledCart = () => { + document.querySelectorAll(woocommerceVariationAddToCart).forEach((button) => { + if( !button.classList.contains('variations_button') ){ + button.classList.remove('woocommerce-variation-add-to-cart-disabled'); + button.querySelector(singleAddToCartButton).classList.remove('disabled','wc-variation-selection-needed'); + button.classList.add('woocommerce-variation-add-to-cart-enabled'); + } + }); + }; + + /** + * Disable cart button. + * + * @since 1.0.0 + */ + const disableCart = () => { + document.querySelectorAll(woocommerceVariationAddToCart).forEach((button) => { + if( !button.classList.contains('variations_button') ) { + button.classList.add('woocommerce-variation-add-to-cart-disabled'); + button.querySelector(singleAddToCartButton).classList.add('disabled', 'wc-variation-selection-needed'); + button.classList.remove('woocommerce-variation-add-to-cart-enabled'); + } + }); + }; + + /** + * Display validation messages. + * + * @param obj current input object. + * @param message display message. + * + * @since 1.0.0 + */ + const displayMessage = (obj, message) => { + let currentObj = ''; + if( undefined === obj.target || null === obj.target ) { + currentObj = obj.parentElement.lastElementChild; + } else { + currentObj = obj.target.parentElement.lastElementChild; + } + currentObj.innerHTML = message; + currentObj.classList.add('show'); + setTimeout(function (){ + currentObj.innerHTML = ""; + currentObj.classList.remove('show'); + },3000); + }; + + /** + * Reset cart quantity + * + * @since 1.0.0 + */ + const resetCart = () => { + const child_product_checkboxes = document.querySelectorAll(childItemQuantityCheckbox); + if (child_product_checkboxes !== null && child_product_checkboxes.length > 0) { + child_product_checkboxes.forEach((element) => { + element.disabled = false; + element.checked = false; + }); + } + }; + + /** + * Update the cart message. + * + * @param cartTotal Get the cart total. + * @param mnm_max_container Get the container max value. + * + * @since 1.0.0 + */ + const updateCartMessage = (cartTotal,mnm_max_container) => { + + let miniCartMessage = __('Completed. Your bundle is full.','wc-mnm-variable'); + + if ( Number(mnm_max_container) > 0 && Number(cartTotal) < Number(mnm_max_container) - 1 ) { + miniCartMessage = __('Please add %d items to complete.','wc-mnm-variable'); + miniCartMessage = miniCartMessage.replace('%d', Number(mnm_max_container) - Number(cartTotal)); + } else if ( Number(mnm_max_container) > 0 && Number(cartTotal) === Number(mnm_max_container) - 1) { + miniCartMessage = __('Please add %d item more to complete.','wc-mnm-variable'); + miniCartMessage = miniCartMessage.replace('%d', Number(mnm_max_container) - Number(cartTotal)); } + + document.querySelector('.mnm-minicart-quantity.note').innerHTML = miniCartMessage; + document.querySelector('.mnm-cart-product-items').innerHTML = cartTotal; + document.querySelector('.mnm-minicart-total-price').innerHTML = document.querySelector('.woocommerce-variation .woocommerce-variation-price').innerHTML; }; - const handleInputChange = (event) => { - const newValue = parseInt(event.target.value); + /** + * Manage localstorage for update the cart quantity and add to cart button. + * + * @since 1.0.0 + */ + let variationId = document.querySelector('.woocommerce-variation-add-to-cart .variation_id').value; + variationId = ( undefined !== variationId && null !== variationId ) ? variationId : 0; + if ( !localStorage.getItem('productLoaded') || !localStorage.getItem('variationId') || variationId !== localStorage.getItem('variationId') ) { + displayLoader(); + localStorage.setItem('productLoaded', 'true'); + localStorage.setItem('variationId',variationId); + setTimeout(function (){ + const resetCartButton = document.querySelector('.mnm-reset-cart'); + const mnm_max_container = document.querySelector('#mnm_max_container').value; + setContainerMaxSize(mnm_max_container); + disableCart(); + updateCartMessage(0,containerMaxSize); + resetCart(); + selectedChildItems = []; + updateTotal(false); + resetCartButton.dispatchEvent(clickEvent); + resetCartButton.addEventListener('click',handleResetCart); + },100); + } + + /** + * Manage the reset cart event. + * + * @since 1.0.0 + */ + const handleResetCart = () => { + disableCart(); + updateCartMessage(0,containerMaxSize); + resetCart(); + selectedChildItems = []; + updateTotal(false); + }; + + /** + * Handle checkbox click event. + * + * @param event Get the event object. + * + * @since 1.0.0 + */ + const handleCheckboxClick = (event) => { + + selectedChildItems = []; + + if (event.target.checked) { + enabledCart(); + updateTotal(event.target); + setCheckboxChecked(1); + } else { + disableCart(); + updateTotal(event.target); + setCheckboxChecked(0); + } + }; + + /** + * Handle the minus button event. + * + * @param e Get the event object. + * + * @since 1.0.0 + */ + const handleMinusClick = (e) => { + displayLoader(); + const newValue = Number(value) - Number(step); if (newValue >= min && newValue <= max) { onChange(newValue); + updateTotal(e); + } else { + updateTotal(e); + } + }; + + /** + * Trigger click event. + * + * @type {MouseEvent | MouseEvent} + * + * @since 1.0.0 + */ + const clickEvent = new MouseEvent('click', { + bubbles: true, + cancelable: true, + view: window + }); + + /** + * Handle the plus button event. + * + * @param e + * + * @since 1.0.0 + */ + const handlePlusClick = (e) => { + displayLoader(); + const newValue = Number(value) + Number(step); + if (newValue <= max && newValue >= min) { + onChange(newValue); + updateTotal(e); + } else { + updateTotal(e); } }; + /** + * Update the total quantity. + * + * @since 1.0.0 + * + * @type {DebouncedState<(function(*): void)|*>} + */ + const updateTotal = useDebouncedCallback( (obj) => { + + displayLoader(); + selectedChildItems = []; + const child_items_quantity = document.querySelectorAll('.child_item__quantity ' + childItemQuantityInput); + const mnm_min_container = document.querySelector('#mnm_min_container').value; + const mnm_max_container = document.querySelector('#mnm_max_container').value; + + if ( null !== child_items_quantity && child_items_quantity.length > 0 ) { + let objectTypeCheckbox = false; + if( obj ){ + objectTypeCheckbox = undefined === obj.target ? obj.type === 'checkbox' : obj.target.type === 'checkbox'; + } + let cartTotal = 0; + child_items_quantity.forEach((element, index) => { + let isCheckbox = ( element.type === 'checkbox'); + let checkboxSelected = isCheckbox ? element.checked : true; + + if( element.value > 0 && checkboxSelected ){ + for (let i = 0 ; i < element.value; i++){ + selectedChildItems.push({ image: element.getAttribute('data-src'), title: element.getAttribute('data-title'), name: element.getAttribute('name'), dataId: element.getAttribute('data-id'), required: element.getAttribute('data-required') }); + } + } + let currentIndex = index + 1; + + if( checkboxSelected ){ + cartTotal = Number(cartTotal) + Number(element.value); + } + if ( cartTotal >= mnm_max_container ) { + enabledCart(); + if ( obj && ( cartTotal > mnm_max_container || element.value > max ) && checkboxSelected ) { + displayMessage(obj,wc_mnm_params.i18n_child_item_max_qty_message.replace('%d', element.value > max ? max : mnm_max_container)); + } + } else if (cartTotal <= mnm_min_container) { + disableCart(); + if ( obj && element.value < min && checkboxSelected ) { + displayMessage(obj,wc_mnm_params.i18n_child_item_min_qty_message.replace('%d', min)); + } + } else { + disableCart(); + } + + if ( currentIndex === child_items_quantity.length ) { + if ( obj && cartTotal > mnm_max_container ) { + const extraQuantity = cartTotal - mnm_max_container; + let currentQuantityInput = ''; + if( ! objectTypeCheckbox && undefined === obj.target || null === obj.target ) { + currentQuantityInput = obj.parentElement.querySelector(childItemQuantityInput); + } else if( ! objectTypeCheckbox ) { + currentQuantityInput = obj.target.parentElement.querySelector(childItemQuantityInput); + } + + if( objectTypeCheckbox ){ + obj.checked = false; + onChange(0); + } else { + currentQuantityInput.value = currentQuantityInput.value - extraQuantity; + onChange(currentQuantityInput.value); + } + updateTotal(obj); + } else { + displaySelectedProducts(mnm_max_container - cartTotal); + updateCartMessage(cartTotal,mnm_max_container); + } + } + }); + } + },300); + + /** + * Display selected products. + * + * @param placeholderQuantity Get the placeholder count. + * + * @since 1.0.0 + */ + const displaySelectedProducts = (placeholderQuantity) => { + displayLoader(); + let mnmMiniCartContentContainer = document.querySelector('.mnm-minicart-view-content-container '); + let displaySelectedItem = []; + if( selectedChildItems.length > 0 ){ + Object.entries(selectedChildItems).map(([index,selectedItem]) => { + displaySelectedItem.push(selectedItem); + }); + } + if( Number( placeholderQuantity) > 0 ){ + for(let i = 0; i < Number(placeholderQuantity) ; i++ ) { + displaySelectedItem.push({image: PLACEHOLDER_IMG_SRC, title: __('Empty','wc-mnm-variable'), name: ''}); + } + } + + mnmMiniCartContentContainer.innerHTML = ''; + if (displaySelectedItem.length > 0 ){ + displaySelectedItem.map( (item, index) => { + mnmMiniCartContentContainer.innerHTML += getProductHTML(item); + if( index + 1 === displaySelectedItem.length ){ + removeLoader(); + document.querySelectorAll('.remove-child-item').forEach((childItem) => { + childItem.addEventListener('click', handleRemoveChildItem); + return () => { + childItem.removeEventListener('click', handleRemoveChildItem); + }; + }); + } + }); + } + }; + + /** + * Get selected child product structure. + * + * @param obj Get the product object. + * + * @since 1.0.0 + * + * @returns {`
    + * ${string|string} + * + *

    ${string}

    + *
    `} + */ + const getProductHTML = ( obj ) => { + let closeButton = (obj.name !== '' && obj.required !== 'true') ? `×`: ''; + + return `
    + ${closeButton} + +

    ${obj.title}

    +
    `; + }; + /** * The goal of this function is to normalize what was inserted, * but after the customer has stopped typing. @@ -60,32 +444,28 @@ function ProductQty( { * * Copied from */ - const normalizeQuantity = useDebouncedCallback( ( initialValue ) => { - // We copy the starting value. - let newValue = initialValue; - - // We check if we have a maximum value, and select the lowest between what was inserted and the maximum. - if ( hasMaximum ) { - newValue = Math.min( - newValue, - // the maximum possible value in step increments. - Math.floor( max / step ) * step - ); - } - - // Select the biggest between what's inserted, the the minimum value in steps. - newValue = Math.max( newValue, Math.ceil( min / step ) * step ); + const normalizeQuantity = useDebouncedCallback( ( initialValue, e ) => { + let newValue = initialValue; + // We check if we have a maximum value, and select the lowest between what was inserted and the maximum. + if ( hasMaximum ) { + newValue = Math.min( + newValue, + // the maximum possible value in step increments. + Math.floor( max / step ) * step + ); + } - // We round off the value to our steps. - newValue = Math.floor( newValue / step ) * step; + // Select the biggest between what's inserted, the the minimum value in steps. + newValue = Math.max( newValue, Math.ceil( min / step ) * step ); - // Only commit if the value has changed - if ( newValue !== initialValue ) { - onChange?.( newValue ); - } - }, - 300 - ); + // We round off the value to our steps. + newValue = Math.floor( newValue / step ) * step; + updateTotal(e); + // Only commit if the value has changed + if ( newValue !== initialValue ) { + onChange?.( newValue ); + } + },300); // If out of stock or not purchasable we do not show a quantity input. if ( ! isSelectable ) { @@ -98,47 +478,50 @@ function ProductQty( { if ( max && min === max ) { /* translators: %1$d: Quantity, %2$s: Product name. */ - let required_text = sprintf( _x( '×%1d %2$s', '[Frontend]', 'text-domain' ), max, childItem.name ); + let required_text = sprintf( _x( '×%1d %2$s', '[Frontend]', 'wc-mnm-variable' ), max, childItem.name ); return ( -

    - { required_text } - +

    + { required_text } + +

    ) } - - // Show a checkbox. @todo - handle check/uncheck. + /** + * Show a checkbox. + * + * @since 1.0.0 + */ if ( max && step === max ) { - /* translators: %1$d: Quantity, %2$s: Product name. */ - let checkbox_label = sprintf( _x( 'Add %1d %2$s', '[Frontend]', 'text-domain' ), max, childItem.name ); + let checkbox_label = sprintf( _x( 'Add %1d %2$s', '[Frontend]', 'text-domain' ), max, childItem.name ); return ( -
    - - +
    +
    + 0} className="qty mnm-quantity child_item__quantity_input" data-required={false} data-title={childItem.name} data-src={imageSrc} type="checkbox" name={`mnm_quantity[${childItem.child_id}]`} value={max} onClick={handleCheckboxClick} /> + +
    +
    ) - } // Otherwise show the quantity input. return ( -
    +
    - { WC_MNM_ADD_TO_CART_REACT_PARAMS.display_plus_minus_buttons && ( - - ) } + normalizeQuantity(e.target.value)} + onChange={(e) => normalizeQuantity(e.target.value,e)} + data-title={childItem.name} + data-src={imageSrc} name={`mnm_quantity[${childItem.child_id}]`} + data-required={false} + data-id={childItem.child_id} /> { WC_MNM_ADD_TO_CART_REACT_PARAMS.display_plus_minus_buttons && ( - + ) } - +
    diff --git a/src/add-to-cart/ChildItems/ChildItem/ProductTitle.js b/src/add-to-cart/ChildItems/ChildItem/ProductTitle.js index 07f5d95..f265ded 100644 --- a/src/add-to-cart/ChildItems/ChildItem/ProductTitle.js +++ b/src/add-to-cart/ChildItems/ChildItem/ProductTitle.js @@ -1,9 +1,9 @@ import { Interweave } from 'interweave'; function ProductTitle( {title} ) { - + const titleClass = WC_MNM_ADD_TO_CART_REACT_PARAMS.display_layout === 'grid' ? 'wc-block-grid__product-title' : 'woocommerce-loop-product__title'; return ( -

    +

    ) diff --git a/src/add-to-cart/ChildItems/ChildItems.js b/src/add-to-cart/ChildItems/ChildItems.js index 77eefb9..d9b4560 100644 --- a/src/add-to-cart/ChildItems/ChildItems.js +++ b/src/add-to-cart/ChildItems/ChildItems.js @@ -1,7 +1,8 @@ /** * External dependencies */ -import { createContext, useContext } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + /** * WooCommerce dependencies @@ -13,33 +14,84 @@ import { createContext, useContext } from '@wordpress/element'; * Internal dependencies */ import ChildItem from './ChildItem/ChildItem'; -import { ConfigContext, ChildContext } from '../../context/Context'; +import { ChildContext } from '../../context/Context'; +import Loading from "../Loading"; -function ChildItems( {childItems} ) { - - const config = useContext(ConfigContext); + +function ChildItems( {childItems, childCategories, isReset} ) { const num_columns = WC_MNM_ADD_TO_CART_REACT_PARAMS.num_columns; - const has_rows = childItems.length > num_columns ? 'has-multilpe-rows' : '' + const display_layout = WC_MNM_ADD_TO_CART_REACT_PARAMS.display_layout; + const has_rows = childItems.length > num_columns ? 'has-multilpe-rows' : ''; + const mobile_optimized = WC_MNM_ADD_TO_CART_REACT_PARAMS.mobile_optimized_layout ? 'mnm-mobile-optimized' : ''; - return ( + const getItems = (childProducts) => { + return ( + display_layout === 'grid' ? ( +
      + { childProducts.map((childItem, index) => ( + + + + ) ) } +
    + ) : ( + + + + + + + + + + { + childProducts.map((childItem, index) => ( + + + + ) ) + } + +
    {__('Product','wc-mnm-variable')}{__('Quantity','wc-mnm-variable')}
    + ) + ); + } -
    + const getCategoryItems = (categories, childItems) => { + let displayItems = []; + let displayedItems = []; -
      + return Object.entries(categories).map(([categoryId, categoryName]) => { + displayItems = []; - { - childItems.map((childItem, index) => ( - - - - ) ) - } + return ( + <> +

      {categoryName}

      + {childItems.map((childItem, index) => { + if ( + childItem.category_ids.some(item => Number(item) === Number(categoryId)) && + (0 === displayedItems.length || !displayedItems.some(item => item.child_id === childItem.child_id)) + ) { + displayItems.push(childItem); + } + if (index + 1 === childItems.length) { + displayedItems = displayedItems.length !== 0 + ? [...displayItems, ...displayedItems.filter(item => !displayItems.some(displayItem => displayItem.child_id === item.child_id))] + : displayItems; + return displayItems.length !== 0 ? getItems(displayItems) : ''; + } + })} + + ); + }); + }; -
    + return ( +
    + { childCategories.length !== 0 ? getCategoryItems(childCategories,childItems) : getItems(childItems) }
    - ) } diff --git a/src/add-to-cart/Loading.js b/src/add-to-cart/Loading.js index d0c1f8f..b08ab12 100644 --- a/src/add-to-cart/Loading.js +++ b/src/add-to-cart/Loading.js @@ -1,16 +1,10 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; const Loading = () => { return ( -
    - { __( - 'Loading...', - 'wc-mnm-reactified' - ) } -
    +
    ); }; diff --git a/src/add-to-cart/MixAndMatch.js b/src/add-to-cart/MixAndMatch.js index e59f850..82cb1e4 100644 --- a/src/add-to-cart/MixAndMatch.js +++ b/src/add-to-cart/MixAndMatch.js @@ -1,39 +1,133 @@ +import { useRef } from 'react'; import { __ } from '@wordpress/i18n'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect,useState } from '@wordpress/element'; /** * Internal dependencies */ import './style.scss'; -//import { ChildItemsState } from './data/child-item-state'; - import ChildItems from './ChildItems/ChildItems'; -import ProductUnavailable from './ProductUnavailable.js'; -import ContainerStatus from './StatusUI/ContainerStatus'; import { ContainerContext } from '../context/Context'; +import Loading from "./Loading"; export default function MixAndMatch( {product} ) { - + const windowSize = useRef([window.innerWidth, window.innerHeight]); + let [isVisible, setIsVisible] = useState(windowSize.current[0] > 600); + const [isReset, setIsReset] = useState(false); + const [containerKey, setContainerKey] = useState(''); const items = 'undefined' !== typeof product.extensions.mix_and_match && 'undefined' !== typeof product.extensions.mix_and_match.child_items ? product.extensions.mix_and_match.child_items : []; - - // Unavailable product (technically this would also be a place where the product ID is missing or not a mix and match, or some error). - /* - if (!product) { - return ; - } - */ + const Categories = 'undefined' !== typeof product.extensions.mix_and_match && 'undefined' !== typeof product.extensions.mix_and_match.child_categories ? product.extensions.mix_and_match.child_categories : []; + const ContainerMinSize = 'undefined' !== typeof product.extensions.mix_and_match && 'undefined' !== typeof product.extensions.mix_and_match.min_container_size ? product.extensions.mix_and_match.min_container_size : 1; + const ContainerMaxSize = 'undefined' !== typeof product.extensions.mix_and_match && 'undefined' !== typeof product.extensions.mix_and_match.max_container_size ? product.extensions.mix_and_match.max_container_size : 1; + const maxQuantity = 'undefined' !== typeof product.add_to_cart && 'undefined' !== typeof product.add_to_cart.maximum ? product.add_to_cart.maximum : 1; + const minQuantity = 'undefined' !== typeof product.add_to_cart && 'undefined' !== typeof product.add_to_cart.minimum ? product.add_to_cart.minimum : 1; + const productTitle = 'undefined' !== typeof product.name ? product.name : ""; // No child items. Should add other results, like not purchasable? if ( ! items || items.length === 0 ) { return

    { __( 'No child items', 'woocommmerce-mix-and-match-products' ) }

    } - - return ( + + const [quantity, setQuantity] = useState(minQuantity); + + /** + * Check the update cart parameters exists or not. + * + * @since 1.0.0 + */ + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const getQuantity = params.get('quantity'); + if(params.get('update-container')){ + setContainerKey(params.get('update-container')); + } + if(getQuantity){ + setQuantity(getQuantity); + } + + }, []); + + /** + * Handle the Main quantity change event. + * + * @param event Get the event object. + * + * @since 1.0.0 + */ + const handleQuantityChange = (event) => { + const value = Number(event.target.value); + if (value >= minQuantity && value <= maxQuantity) { + setQuantity(value); + } + }; + + /** + * Handle the minicart popup. + * + * @since 1.0.0 + */ + const handleMinicartPopup = () => { + setIsVisible(!isVisible); + } + + /** + * Handle the Clear cart button event. + * + * @since 1.0.0 + */ + const handleResetCart = () => { + setIsReset(true); + setTimeout(function () { + setIsReset(false); + },500); + }; + + return ( - - + +
    +
    +
    +

    {__('Your Selection','wc-mnm-variable')}

    +
    +
    +
    + +
    +
    + +

    {__('Please add 0 items to complete.','wc-mnm-variable')}

    +

    {__('Total: ','wc-mnm-variable')}(0{__(' items','wc-mnm-variable')})

    +
    +
    + + +
    + {containerKey ? : ''} + + + +
    +
    + {} +
    +
    +
    ) diff --git a/src/add-to-cart/StatusUI/ContainerStatus.js b/src/add-to-cart/StatusUI/ContainerStatus.js index 7368c0c..6410dae 100644 --- a/src/add-to-cart/StatusUI/ContainerStatus.js +++ b/src/add-to-cart/StatusUI/ContainerStatus.js @@ -1,8 +1,12 @@ - +import {RawHTML} from '@wordpress/element'; function ContainerStatus() { return (
    -

    Status goes here

    +
    +
      +
    • {WC_MNM_ADD_TO_CART_REACT_PARAMS.cart_status_message}
    • +
    +
    ) } diff --git a/templates/edit-order-item/edit-variable-container.php b/templates/edit-order-item/edit-variable-container.php index 532f72b..00cb760 100644 --- a/templates/edit-order-item/edit-variable-container.php +++ b/templates/edit-order-item/edit-variable-container.php @@ -2,7 +2,7 @@ /** * Mix and Match Product Edit Variable Container * - * This template can be overridden by copying it to yourtheme/woocommerce/single-product/edit-order-item/edit-variable-mix-and-match.php. + * This template can be overridden by copying it to yourtheme/woocommerce/single-product/edit-order-item/edit-variable-container.php. * * HOWEVER, on occasion WooCommerce Mix and Match will need to update template files and you * (the theme developer) will need to copy the new files to your theme to @@ -26,13 +26,14 @@ $attribute_keys = array_keys( $attributes ); $variations_json = wp_json_encode( $available_variations ); $variations_attr = function_exists( 'wc_esc_json' ) ? wc_esc_json( $variations_json ) : _wp_specialchars( $variations_json, ENT_QUOTES, 'UTF-8', true ); +$config_attr = wc_esc_json( wp_json_encode( $config ) ); /** * wc_mnm_before_edit_container_form hook. */ do_action( 'wc_mnm_before_edit_container_order_item_form', $product, $order_item, $order, $source ); ?> -
    + diff --git a/templates/single-product/add-to-cart/variable-mnm.php b/templates/single-product/add-to-cart/variable-mnm.php index 503a1b2..9f81e7f 100644 --- a/templates/single-product/add-to-cart/variable-mnm.php +++ b/templates/single-product/add-to-cart/variable-mnm.php @@ -21,11 +21,12 @@ $attribute_keys = array_keys( $attributes ); $variations_json = wp_json_encode( $available_variations ); -$variations_attr = function_exists( 'wc_esc_json' ) ? wc_esc_json( $variations_json ) : _wp_specialchars( $variations_json, ENT_QUOTES, 'UTF-8', true ); +$variations_attr = wc_esc_json( $variations_json ); +$config_attr = wc_esc_json( wp_json_encode( $config ) ); do_action( 'woocommerce_before_add_to_cart_form' ); ?> - + diff --git a/templates/single-product/add-to-cart/variation-add-to-cart-button.php b/templates/single-product/add-to-cart/variation-add-to-cart-button.php new file mode 100644 index 0000000..bc130eb --- /dev/null +++ b/templates/single-product/add-to-cart/variation-add-to-cart-button.php @@ -0,0 +1,28 @@ + +
    + + + + + + + + + +
    diff --git a/wc-mnm-variable.php b/wc-mnm-variable.php index 8da4567..66e84fa 100755 --- a/wc-mnm-variable.php +++ b/wc-mnm-variable.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce Mix and Match - Variable Mix and Match * Plugin URI: * Description: Variable mix and match product type - * Version: 1.0.0-reactified.alpha-2 + * Version: 1.0.0-rc.13 * Author: Kathy Darling * Author URI: http://kathyisawesome.com/ * Text Domain: wc-mnm-variable @@ -30,7 +30,7 @@ class WC_MNM_Variable { - const VERSION = '1.0.0-reactified.alpha-2'; + const VERSION = '1.0.0-rc.13'; const REQ_MNM_VERSION = '2.4.0-beta.5'; const REQ_WC_VERSION = '6.9.0'; // @todo -check this. @@ -149,12 +149,12 @@ public function attach_hooks_and_filters() { // Tells core that the Variable product is also editable. add_filter( 'wc_mnm_is_container_order_item_editable', [ $this, 'variable_is_editable' ], 10, 2 ); - // Force tabular layout of attributes when editing in admin. + // Force table/dropdowns layout of attributes when editing in admin. add_action( 'wc_mnm_edit_container_order_item_in_shop_order', array( __CLASS__, 'force_edit_container_styles' ), 0, 4 ); add_action( 'wc_mnm_edit_container_order_item_in_shop_subscription', array( __CLASS__, 'force_edit_container_styles' ), 0, 4 ); // Force variations into tabular layout when editing in admin. - add_action( 'wc_mnm_variation_add_to_cart', [ $this, 'force_edit_variation_styles' ], 0 ); + //add_action( 'wc_mnm_variation_add_to_cart', [ $this, 'force_edit_variation_styles' ], 0 ); // Admin order style tweaks for variable mix and match. add_action( 'admin_enqueue_scripts', [ $this, 'admin_inline_styles' ], 20 ); @@ -162,6 +162,9 @@ public function attach_hooks_and_filters() { // Preload the current variation. add_filter( 'woocommerce_available_variation', [ $this, 'preload_order_item_variation' ], 20, 3 ); + // Core MNM preloads the selected children from an order item by merging config into REQUEST. Need to disable that for variations. + add_filter( 'wc_mnm_get_posted_container_form_data', [ $this, 'remove_posted_data' ], 10, 3 ); + // Handle change variation. add_filter( 'wc_mnm_get_product_from_edit_order_item', [ $this, 'switch_variation' ], 10, 4 ); @@ -185,6 +188,9 @@ public function includes() { include_once 'includes/data-stores/class-wc-product-variable-mix-and-match-data-store-cpt.php'; include_once 'includes/data-stores/class-wc-product-mix-and-match-variation-data-store-cpt.php'; + // Transients - this is a temporary solution. + include_once 'includes/class-wc-mnm-variable-transients.php'; + // Compatibility include_once 'includes/compatibility/class-wc-mnm-variable-compatibility.php'; @@ -484,6 +490,8 @@ public function frontend_scripts( $auto_enqueue = false ) { $style_url = $this->get_plugin_url() . $style_path; $style_version = WC_Mix_and_Match()->get_file_version( $this->get_plugin_path() . $style_path, self::VERSION ); + wp_enqueue_style( 'dashicons' ); + wp_enqueue_style( 'wc-mnm-variable-frontend', $this->get_plugin_url() . '/assets/css/frontend/mnm-frontend.min.css', array(), self::VERSION ); wp_enqueue_style( 'wc-mnm-add-to-cart-variation', $style_url, [ 'wc-mnm-frontend' ], $style_version ); wp_style_add_data( 'wc-mnm-add-to-cart-variation', 'rtl', 'replace' ); @@ -519,6 +527,8 @@ public function frontend_scripts( $auto_enqueue = false ) { 'wc_ajax_url' => \WC_AJAX::get_endpoint( '%%endpoint%%' ), 'i18n_form_error' => __( 'Failed to initialize form. If this issue persists, please reload the page and try again.', 'wc-mnm-variable' ), 'form_nonce' => wp_create_nonce( 'wc_mnm_container_form' ), + 'closeWindowIcon' => $this->get_plugin_url().'/assets/icons/close-window.png', + 'openWindowIcon' => $this->get_plugin_url().'/assets/icons/open-window.png', ); wp_localize_script( 'wc-mnm-add-to-cart-variation', 'WC_MNM_ADD_TO_CART_VARIATION_PARAMS', $params ); @@ -550,10 +560,21 @@ public function frontend_scripts( $auto_enqueue = false ) { 'display_thumbnails' => wc_string_to_bool( get_option( 'wc_mnm_display_thumbnail', 'yes' ) ), 'display_short_description' => wc_string_to_bool( get_option( 'wc_mnm_display_short_description', 'no' ) ), 'display_plus_minus_buttons' => wc_string_to_bool( get_option( 'wc_mnm_display_plus_minus_buttons', 'no' ) ), + 'display_layout' => get_option('wc_mnm_layout','tabular'), + 'mobile_optimized_layout' => wc_string_to_bool( get_option('wc_mnm_mobile_optimized_layout','no')), 'display_visual_status_ui' => wc_string_to_bool( get_option( 'wc_mnm_visual_status_ui', 'no' ) ), 'num_columns' => (int) apply_filters( 'wc_mnm_grid_layout_columns', get_option( 'wc_mnm_number_columns', 3 ) ), + 'cart_status_message' => __('You have selected 0 items. You may select between 0 and 0 items or add to cart to continue.', 'wc-mnm-variable' ), ); + if( is_product() && is_single() ){ + $product_id = get_the_ID(); + $is_override_template = wc_string_to_bool(get_post_meta($product_id,'_mnm_layout_override',true)); + if( $is_override_template ){ + $params['display_layout'] = get_post_meta($product_id,'_mnm_layout_style',true); + } + } + wp_localize_script( 'wc-mnm-add-to-cart-reatified', 'WC_MNM_ADD_TO_CART_REACT_PARAMS', $params ); @@ -711,21 +732,6 @@ public function get_container_form() { wp_send_json_error( $error ); } - // Initialize form state based on the URL params. - if ( ! empty( $_POST['request'] ) ) { - parse_str( parse_url( $_POST['request'], PHP_URL_QUERY ), $params ); - $_REQUEST = array_merge( $_REQUEST, $params ); - } - - unset( $_REQUEST['request'] ); - - // Initialize form state based on the actual configuration of the container. - $configuration = ! empty( $_POST['configuration'] ) ? wc_clean( $_POST['configuration'] ) : array(); - - if ( ! empty( $configuration ) ) { - $_REQUEST = array_merge( $_REQUEST, WC_Mix_and_Match()->cart->rebuild_posted_container_form_data( $configuration, $product ) ); - } - /* * `wc_mnm_container_form_fragments` filter * @@ -820,6 +826,53 @@ public function admin_inline_styles() { margin-right: 1em; } .wc-mnm-backbone-modal form.edit_container .blockUI.blockOverlay::before { border: none; } + + /* Temp add some styles for grid - since template is cached we can't serve tabular layout in the admin */ + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid { + display: flex; + flex-wrap: wrap; + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid .child-item { + padding: 0 .5em; + margin-bottom: 1em; + overflow: hidden; + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid .child-item img { + width: 100%; + height: auto; + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-1> .child-item { + max-width:100%; + flex-basis:100% + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-2> .child-item { + max-width:50%; + flex-basis:50% + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-3> .child-item { + max-width:33.33333%; + flex-basis:33.33333% + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-4> .child-item { + max-width:25%; + flex-basis:25% + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-5> .child-item { + max-width:20%; + flex-basis:20% + } + + .wc-mnm-backbone-modal form.edit_container .single_variation_wrap .wc_mnm_variation .grid.columns-6> .child-item { + max-width:16.66667%; + flex-basis:16.66667% + } "; wp_add_inline_style( 'wc-mnm-admin-order-style', $custom_css ); @@ -847,7 +900,25 @@ public function preload_order_item_variation( $data, $product, $variation ) { return $data; - } + } + + /** + * Filter the rebuilt configuration to an empty array as variations will prefill using JS. + * + * @param array $form_data + * @param array $configuration + * @param WC_Mix_and_Match_Product $container + * @return array + */ + public static function remove_posted_data( $form_data, $configuration, $container ) { + + if ( $container && $container->is_type( 'mix-and-match-variation' ) ) { + //$form_data = []; + } + + return $form_data; + + } /** * Switch the product object if variation.