| @@ -8,8 +8,10 @@ | ||
| 8 | 8 | use FluentCart\App\Helpers\AddressHelper; |
| 9 | 9 | use FluentCart\App\Helpers\CurrenciesHelper; |
| 10 | 10 | use FluentCart\App\Services\OrderService; |
| 11 | 11 | use FluentCart\App\Services\Theme\ColorPalette; |
| 12 | +use FluentCart\App\Services\Theme\FrontendTheme; | |
| 13 | +use FluentCart\App\Services\Theme\RadiusPalette; | |
| 12 | 14 | use FluentCart\App\Services\Theme\ThemePalette; |
| 13 | 15 | use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager; |
| 14 | 16 | use FluentCart\App\Modules\StoreManagedRenewal\Services\RenewalService; |
| 15 | 17 | use FluentCart\App\Modules\Subscriptions\Services\SubscriptionManagementMode; |
| @@ -120,8 +122,9 @@ | ||
| 120 | 122 | 'weight_unit' => 'kg', |
| 121 | 123 | 'dimension_unit' => 'cm', |
| 122 | 124 | 'appearance_source' => ColorPalette::SOURCE_DEFAULT, |
| 123 | 125 | 'appearance_colors' => [], |
| 126 | + 'appearance_radius' => [], | |
| 124 | 127 | // 'wordpress', not 'fluent_cart': the FluentCart patterns are |
| 125 | 128 | // literals ('M j, Y'), and a literal renders a half-translated date |
| 126 | 129 | // on a localized store -- a German month in English field order, |
| 127 | 130 | // which a German reader misreads as day-first. Following |
| @@ -1711,14 +1714,22 @@ | ||
| 1711 | 1714 | ] |
| 1712 | 1715 | ]; |
| 1713 | 1716 | } |
| 1714 | 1717 | |
| 1715 | - // Product Rating toggles only apply while the reviews module is | |
| 1716 | - // active — hide the section (and its divider) otherwise. | |
| 1718 | + // Review settings only apply while the reviews module is active — | |
| 1719 | + // hide them (and their dividers) otherwise: the Product Rating | |
| 1720 | + // section, the "Show Reviews In Single Page" toggle with its note, | |
| 1721 | + // and the Order Review page selector. The stored values survive: | |
| 1722 | + // save() merges the submitted form over what is stored, so a field | |
| 1723 | + // left off the form keeps its value for when the module returns. | |
| 1717 | 1724 | if (!ModuleSettings::isActive('reviews')) { |
| 1718 | 1725 | unset( |
| 1719 | 1726 | $fields['setting_tabs']['schema']['single_product_setup']['schema']['hr_product_slug'], |
| 1720 | - $fields['setting_tabs']['schema']['single_product_setup']['schema']['product_rating_grid'] | |
| 1727 | + $fields['setting_tabs']['schema']['single_product_setup']['schema']['product_rating_grid'], | |
| 1728 | + $fields['setting_tabs']['schema']['single_product_setup']['schema']['product_settings_grid']['schema']['fields']['schema']['show_reviews_in_single_page'], | |
| 1729 | + $fields['setting_tabs']['schema']['single_product_setup']['schema']['product_settings_grid']['schema']['fields']['schema']['show_reviews_note'], | |
| 1730 | + $fields['setting_tabs']['schema']['pages_setup']['schema']['hr_order_review'], | |
| 1731 | + $fields['setting_tabs']['schema']['pages_setup']['schema']['order_review_page_grid'] | |
| 1721 | 1732 | ); |
| 1722 | 1733 | } |
| 1723 | 1734 | |
| 1724 | 1735 | return apply_filters("fluent_cart/store_settings/fields", $fields, []); |
| @@ -1751,8 +1762,28 @@ | ||
| 1751 | 1762 | |
| 1752 | 1763 | return empty($data) ? $default : $data; |
| 1753 | 1764 | } |
| 1754 | 1765 | |
| 1766 | + /** | |
| 1767 | + * All store settings, as the settings form should edit them. | |
| 1768 | + * | |
| 1769 | + * A stored radius the grammar refuses (hand-edited, or written by an older | |
| 1770 | + * build) never printed on the storefront; dropped here too, so the form | |
| 1771 | + * never holds a value it would refuse to save. | |
| 1772 | + * | |
| 1773 | + * @return array | |
| 1774 | + */ | |
| 1775 | + public function getForEditing(): array | |
| 1776 | + { | |
| 1777 | + $settings = (array)$this->get(); | |
| 1778 | + | |
| 1779 | + if (array_key_exists('appearance_radius', $settings)) { | |
| 1780 | + $settings['appearance_radius'] = RadiusPalette::sanitizeOwnerMap($settings['appearance_radius']); | |
| 1781 | + } | |
| 1782 | + | |
| 1783 | + return $settings; | |
| 1784 | + } | |
| 1785 | + | |
| 1755 | 1786 | public function moduleSettings(): array |
| 1756 | 1787 | { |
| 1757 | 1788 | $settings = $this->get('modules_settings', []); |
| 1758 | 1789 | return Arr::wrap($settings); |
| @@ -2095,30 +2126,30 @@ | ||
| 2095 | 2126 | // Plain text — the component renders these as text nodes, so no markup |
| 2096 | 2127 | // and no escaping here or the entities would show up verbatim. |
| 2097 | 2128 | $sourceOptions = [ |
| 2098 | 2129 | [ |
| 2099 | - 'label' => __("FluentCart's own colors", 'fluent-cart'), | |
| 2130 | + 'label' => __("FluentCart's own style", 'fluent-cart'), | |
| 2100 | 2131 | 'value' => ColorPalette::SOURCE_DEFAULT, |
| 2101 | 2132 | 'icon' => 'PaletteLine', |
| 2102 | - 'note' => __('The storefront keeps the colors it ships with.', 'fluent-cart'), | |
| 2133 | + 'note' => __('The storefront keeps the colors and corner rounding it ships with.', 'fluent-cart'), | |
| 2103 | 2134 | ], |
| 2104 | 2135 | [ |
| 2105 | 2136 | 'label' => __('Inherit from the active theme', 'fluent-cart'), |
| 2106 | 2137 | 'value' => ColorPalette::SOURCE_THEME, |
| 2107 | 2138 | 'icon' => 'PaintLine', |
| 2108 | - 'note' => __('FluentCart uses your active theme\'s colors for the storefront. If they don\'t come through as expected, choose Customize to set them yourself.', 'fluent-cart'), | |
| 2139 | + 'note' => __('FluentCart uses your active theme\'s colors, and its button and form field corner rounding, for the storefront. Product cards keep FluentCart\'s corners. If something doesn\'t come through as expected, choose Customize to set it yourself.', 'fluent-cart'), | |
| 2109 | 2140 | ], |
| 2110 | 2141 | [ |
| 2111 | 2142 | 'label' => __('Customize', 'fluent-cart'), |
| 2112 | 2143 | 'value' => ColorPalette::SOURCE_CUSTOM, |
| 2113 | 2144 | 'icon' => 'PaletteLine', |
| 2114 | - 'note' => __('Pick the colors yourself. Only the ones you set are written to the storefront.', 'fluent-cart'), | |
| 2145 | + 'note' => __('Pick the colors and corner rounding yourself. Only what you set is written to the storefront.', 'fluent-cart'), | |
| 2115 | 2146 | ], |
| 2116 | 2147 | ]; |
| 2117 | 2148 | |
| 2118 | 2149 | $sourceHeading = [ |
| 2119 | - 'label' => __('Where colors come from', 'fluent-cart'), | |
| 2120 | - 'note' => __('Storefront colors cascade from a small set of globals, so changing one here updates every page that uses it.', 'fluent-cart'), | |
| 2150 | + 'label' => __('Where colors and corner rounding come from', 'fluent-cart'), | |
| 2151 | + 'note' => __('Storefront colors and corner rounding cascade from a small set of globals, so changing one here updates every page that uses it.', 'fluent-cart'), | |
| 2121 | 2152 | ]; |
| 2122 | 2153 | |
| 2123 | 2154 | return [ |
| 2124 | 2155 | 'title' => __('Appearance', 'fluent-cart'), |
| @@ -2142,8 +2173,9 @@ | ||
| 2142 | 2173 | 'heading' => $sourceHeading, |
| 2143 | 2174 | 'source_options' => $sourceOptions, |
| 2144 | 2175 | 'custom_source' => ColorPalette::SOURCE_CUSTOM, |
| 2145 | 2176 | 'color_groups' => $this->getAppearanceColorGroups(), |
| 2177 | + 'radius_section' => $this->getAppearanceRadiusSection(), | |
| 2146 | 2178 | 'preview' => $this->getAppearancePreviewData(), |
| 2147 | 2179 | ], |
| 2148 | 2180 | 'appearance_source' => [ |
| 2149 | 2181 | 'type' => 'hidden', |
| @@ -2152,8 +2184,12 @@ | ||
| 2152 | 2184 | 'appearance_colors' => [ |
| 2153 | 2185 | 'type' => 'hidden', |
| 2154 | 2186 | 'value' => (object)[], |
| 2155 | 2187 | ], |
| 2188 | + 'appearance_radius' => [ | |
| 2189 | + 'type' => 'hidden', | |
| 2190 | + 'value' => (object)[], | |
| 2191 | + ], | |
| 2156 | 2192 | ], |
| 2157 | 2193 | ]; |
| 2158 | 2194 | } |
| 2159 | 2195 | |
| @@ -2178,8 +2214,9 @@ | ||
| 2178 | 2214 | return [ |
| 2179 | 2215 | 'slots' => $this->getAppearancePreviewSlots(), |
| 2180 | 2216 | 'theme_source' => ColorPalette::SOURCE_THEME, |
| 2181 | 2217 | 'theme_roles' => $this->getAppearanceThemeRoles(), |
| 2218 | + 'theme_radii' => $this->getAppearanceThemeRadii(), | |
| 2182 | 2219 | 'notes' => [ |
| 2183 | 2220 | ColorPalette::SOURCE_THEME => $this->getAppearanceThemeNote(), |
| 2184 | 2221 | ColorPalette::SOURCE_CUSTOM => __('Set only the colors you want to change — anything you leave unset keeps FluentCart\'s own default.', 'fluent-cart'), |
| 2185 | 2222 | ], |
| @@ -2242,9 +2279,9 @@ | ||
| 2242 | 2279 | */ |
| 2243 | 2280 | protected function getAppearanceThemeNote(): string |
| 2244 | 2281 | { |
| 2245 | 2282 | if (!ThemePalette::hasUsableSource()) { |
| 2246 | - return __('The active theme publishes nothing to inherit, so the storefront keeps FluentCart\'s own colors.', 'fluent-cart'); | |
| 2283 | + return __('The active theme publishes no colors to inherit, so the storefront keeps FluentCart\'s own colors.', 'fluent-cart'); | |
| 2247 | 2284 | } |
| 2248 | 2285 | |
| 2249 | 2286 | // Only a colour with no hex anywhere is unpreviewable. A live |
| 2250 | 2287 | // reference with a hex fallback — what the theme-settings readers and |
| @@ -2294,8 +2331,21 @@ | ||
| 2294 | 2331 | return $roles; |
| 2295 | 2332 | } |
| 2296 | 2333 | |
| 2297 | 2334 | /** |
| 2335 | + * The theme's border radii for the preview, role => length. | |
| 2336 | + * | |
| 2337 | + * Exactly what inheriting would print, storefront filter included, so the | |
| 2338 | + * preview never promises a radius the storefront won't write. | |
| 2339 | + * | |
| 2340 | + * @return array Role key => length (`8px`, `0.5rem`). | |
| 2341 | + */ | |
| 2342 | + protected function getAppearanceThemeRadii(): array | |
| 2343 | + { | |
| 2344 | + return FrontendTheme::radiiFor(ColorPalette::SOURCE_THEME); | |
| 2345 | + } | |
| 2346 | + | |
| 2347 | + /** | |
| 2298 | 2348 | * The colour knobs, grouped the way the registry groups them, as data the |
| 2299 | 2349 | * appearance component renders its pickers from. |
| 2300 | 2350 | * |
| 2301 | 2351 | * @return array |
| @@ -2329,8 +2379,38 @@ | ||
| 2329 | 2379 | ]; |
| 2330 | 2380 | } |
| 2331 | 2381 | |
| 2332 | 2382 | return $groups; |
| 2383 | + } | |
| 2384 | + | |
| 2385 | + /** | |
| 2386 | + * The border-radius controls, as data the appearance component renders | |
| 2387 | + * its number inputs from. | |
| 2388 | + * | |
| 2389 | + * Driven off RadiusPalette::roles(), the same registry the sanitiser and | |
| 2390 | + * the storefront CSS read, so a control can never write a role the page | |
| 2391 | + * would not print. | |
| 2392 | + * | |
| 2393 | + * @return array | |
| 2394 | + */ | |
| 2395 | + protected function getAppearanceRadiusSection(): array | |
| 2396 | + { | |
| 2397 | + $fields = []; | |
| 2398 | + | |
| 2399 | + foreach (RadiusPalette::roles() as $key => $definition) { | |
| 2400 | + $fields[] = [ | |
| 2401 | + 'key' => $key, | |
| 2402 | + 'label' => (string)Arr::get($definition, 'label', $key), | |
| 2403 | + 'note' => (string)Arr::get($definition, 'note', ''), | |
| 2404 | + ]; | |
| 2405 | + } | |
| 2406 | + | |
| 2407 | + return [ | |
| 2408 | + 'label' => __('Border radius', 'fluent-cart'), | |
| 2409 | + 'note' => __('A number is read as pixels, or add a unit: 8, 8px, 0.5rem, 1em. Leave a field empty to keep FluentCart\'s default.', 'fluent-cart'), | |
| 2410 | + 'invalid' => __('Use a number, optionally with px, rem or em.', 'fluent-cart'), | |
| 2411 | + 'fields' => $fields, | |
| 2412 | + ]; | |
| 2333 | 2413 | } |
| 2334 | 2414 | |
| 2335 | 2415 | /** |
| 2336 | 2416 | * The hint under one colour picker: what it drives, and the value it |