deprecated
2 weeks ago
_swiper._scss
2 months ago
block.json
2 weeks ago
edit-multiItem.js
2 weeks ago
edit-slider.js
2 weeks ago
edit.js
1 week ago
icon.svg
2 months ago
index.js
2 months ago
index.php
2 months ago
loop-min-slides.js
2 months ago
pause-button.js
1 week ago
save.js
1 week ago
style.scss
2 months ago
view.js
1 week ago
edit-multiItem.js
355 lines
| 1 | import { __, sprintf } from '@wordpress/i18n'; |
| 2 | import { |
| 3 | PanelBody, |
| 4 | BaseControl, |
| 5 | TextControl, |
| 6 | RadioControl, |
| 7 | ToggleControl, |
| 8 | } from '@wordpress/components'; |
| 9 | import { useSelect } from '@wordpress/data'; |
| 10 | import { getMinSlidesForLoop } from './loop-min-slides'; |
| 11 | |
| 12 | export const MultiItemSetting = (props) => { |
| 13 | const { attributes, setAttributes, clientId } = props; |
| 14 | const { |
| 15 | slidesPerGroup, |
| 16 | slidesPerViewMobile, |
| 17 | slidesPerViewTablet, |
| 18 | slidesPerViewPC, |
| 19 | loop, |
| 20 | effect, |
| 21 | centeredSlides, |
| 22 | zoomAnimation, |
| 23 | } = attributes; |
| 24 | |
| 25 | // インナーブロックを取得 |
| 26 | const innerBlocks = useSelect((select) => |
| 27 | select('core/block-editor').getBlocks(clientId) |
| 28 | ); |
| 29 | |
| 30 | // 1スライドあたりの表示枚数がスライダーの総枚数の約数出なかったときに表示するアラート |
| 31 | const slidesPerViewAlert = ( |
| 32 | <div className="text-danger font-size-11px"> |
| 33 | {__( |
| 34 | 'Enter integer divisors for the number of placed slide items for each display size.', |
| 35 | 'vk-blocks' |
| 36 | )} |
| 37 | </div> |
| 38 | ); |
| 39 | |
| 40 | const isNotDivisor = (value) => { |
| 41 | if (value === null || value === undefined || value === '') { |
| 42 | return false; |
| 43 | } |
| 44 | const n = parseInt(value, 10); |
| 45 | return !Number.isNaN(n) && innerBlocks.length % n !== 0; |
| 46 | }; |
| 47 | |
| 48 | const slidesPerViewMobileAlert = |
| 49 | slidesPerGroup === 'slides-per-view' && |
| 50 | isNotDivisor(slidesPerViewMobile) |
| 51 | ? slidesPerViewAlert |
| 52 | : ''; |
| 53 | const slidesPerViewTabletAlert = |
| 54 | slidesPerGroup === 'slides-per-view' && |
| 55 | isNotDivisor(slidesPerViewTablet) |
| 56 | ? slidesPerViewAlert |
| 57 | : ''; |
| 58 | const slidesPerViewPCAlert = |
| 59 | slidesPerGroup === 'slides-per-view' && isNotDivisor(slidesPerViewPC) |
| 60 | ? slidesPerViewAlert |
| 61 | : ''; |
| 62 | |
| 63 | const makeLoopAlert = (spv, spg) => { |
| 64 | const minRequired = getMinSlidesForLoop(spv, spg, !!centeredSlides); |
| 65 | if (innerBlocks.length >= minRequired) { |
| 66 | return ''; |
| 67 | } |
| 68 | return ( |
| 69 | <div className="alert alert-danger font-size-11px"> |
| 70 | {sprintf( |
| 71 | /* translators: %d: minimum number of slides required for loop mode */ |
| 72 | __( |
| 73 | 'Loop mode requires at least %d slides with the current slider settings. Please add more slides or disable loop.', |
| 74 | 'vk-blocks' |
| 75 | ), |
| 76 | minRequired |
| 77 | )} |
| 78 | </div> |
| 79 | ); |
| 80 | }; |
| 81 | |
| 82 | // toSpv: spv を最低 1 にクランプする(小数はそのまま保持し getMinSlidesForLoop 側で正規化)。 |
| 83 | // toSpg: spv を正整数に正規化する。null/undefined/'' または有限数でない場合は undefined を返す。 |
| 84 | const toSpv = (spv) => Math.max(1, spv); |
| 85 | const toSpg = (spv) => { |
| 86 | if (spv === null || spv === undefined || spv === '') { |
| 87 | return undefined; |
| 88 | } |
| 89 | const n = Number(spv); |
| 90 | return Number.isFinite(n) ? Math.max(1, Math.floor(n)) : undefined; |
| 91 | }; |
| 92 | |
| 93 | /* ループ時のアラート */ |
| 94 | const slidesPerViewMobileLoopAlert = loop |
| 95 | ? makeLoopAlert( |
| 96 | toSpv(slidesPerViewMobile || 1), |
| 97 | slidesPerGroup === 'slides-per-view' |
| 98 | ? (toSpg(slidesPerViewMobile || 1) ?? 1) |
| 99 | : 1 |
| 100 | ) |
| 101 | : ''; |
| 102 | const slidesPerViewTabletLoopAlert = |
| 103 | loop && |
| 104 | slidesPerViewTablet !== null && |
| 105 | slidesPerViewTablet !== undefined && |
| 106 | slidesPerViewTablet !== '' |
| 107 | ? makeLoopAlert( |
| 108 | toSpv(slidesPerViewTablet), |
| 109 | slidesPerGroup === 'slides-per-view' |
| 110 | ? (toSpg(slidesPerViewTablet) ?? 1) |
| 111 | : 1 |
| 112 | ) |
| 113 | : ''; |
| 114 | const slidesPerViewPCLoopAlert = |
| 115 | loop && |
| 116 | slidesPerViewPC !== null && |
| 117 | slidesPerViewPC !== undefined && |
| 118 | slidesPerViewPC !== '' |
| 119 | ? makeLoopAlert( |
| 120 | toSpv(slidesPerViewPC), |
| 121 | slidesPerGroup === 'slides-per-view' |
| 122 | ? (toSpg(slidesPerViewPC) ?? 1) |
| 123 | : 1 |
| 124 | ) |
| 125 | : ''; |
| 126 | |
| 127 | // 複数枚表示設定 |
| 128 | let multiItemSetting = ''; |
| 129 | |
| 130 | // フェードエフェクトでない場合の設定UI |
| 131 | const multiItemUI = |
| 132 | effect !== 'fade' ? ( |
| 133 | <> |
| 134 | <BaseControl |
| 135 | label={__( |
| 136 | 'Number of Items to display per view', |
| 137 | 'vk-blocks' |
| 138 | )} |
| 139 | id={`vk_slider-MultiItem`} |
| 140 | > |
| 141 | <p className="font-size-11px"> |
| 142 | {__( |
| 143 | 'Enter the number of slide items to display on each device.', |
| 144 | 'vk-blocks' |
| 145 | )} |
| 146 | </p> |
| 147 | <p className="font-size-11px"> |
| 148 | {__( |
| 149 | 'In one-by-one mode, you can set decimals (e.g. 1.5). If you specify a decimal, please enable "Centering the active slide".', |
| 150 | 'vk-blocks' |
| 151 | )} |
| 152 | </p> |
| 153 | <p className="font-size-11px"> |
| 154 | {__( |
| 155 | 'In slides-per-view mode, enter a divisor of the total number of placed slides. If the number is not a divisor, the sliding behavior will be unnatural.', |
| 156 | 'vk-blocks' |
| 157 | )} |
| 158 | </p> |
| 159 | <p className="font-size-11px"> |
| 160 | {__( |
| 161 | 'When "Centering the active slide" is enabled and the number of items to display is even, it is automatically treated as the next odd number internally. This may increase the minimum number of slides required for loop mode.', |
| 162 | 'vk-blocks' |
| 163 | )} |
| 164 | </p> |
| 165 | <TextControl |
| 166 | type={'number'} |
| 167 | label={__('PC', 'vk-blocks')} |
| 168 | value={slidesPerViewPC} |
| 169 | onChange={(value) => { |
| 170 | if ( |
| 171 | Number.isNaN(Number(value)) || |
| 172 | Number(value) < 1 |
| 173 | ) { |
| 174 | setAttributes({ |
| 175 | slidesPerViewPC: 1, |
| 176 | }); |
| 177 | } else if (slidesPerGroup === 'slides-per-view') { |
| 178 | setAttributes({ |
| 179 | slidesPerViewPC: parseInt( |
| 180 | Number(value), |
| 181 | 10 |
| 182 | ), |
| 183 | }); |
| 184 | } else { |
| 185 | setAttributes({ |
| 186 | slidesPerViewPC: parseFloat(Number(value)), |
| 187 | }); |
| 188 | } |
| 189 | }} |
| 190 | min={1} |
| 191 | step={slidesPerGroup === 'slides-per-view' ? 1 : 0.1} |
| 192 | /> |
| 193 | {slidesPerViewPCAlert} |
| 194 | {slidesPerViewPCLoopAlert} |
| 195 | <TextControl |
| 196 | type={'number'} |
| 197 | label={__('Tablet', 'vk-blocks')} |
| 198 | value={slidesPerViewTablet} |
| 199 | onChange={(value) => { |
| 200 | if ( |
| 201 | Number.isNaN(Number(value)) || |
| 202 | Number(value) < 1 |
| 203 | ) { |
| 204 | setAttributes({ |
| 205 | slidesPerViewTablet: 1, |
| 206 | }); |
| 207 | } else if (slidesPerGroup === 'slides-per-view') { |
| 208 | setAttributes({ |
| 209 | slidesPerViewTablet: parseInt( |
| 210 | Number(value), |
| 211 | 10 |
| 212 | ), |
| 213 | }); |
| 214 | } else { |
| 215 | setAttributes({ |
| 216 | slidesPerViewTablet: parseFloat( |
| 217 | Number(value) |
| 218 | ), |
| 219 | }); |
| 220 | } |
| 221 | }} |
| 222 | min={1} |
| 223 | step={slidesPerGroup === 'slides-per-view' ? 1 : 0.1} |
| 224 | /> |
| 225 | {slidesPerViewTabletAlert} |
| 226 | {slidesPerViewTabletLoopAlert} |
| 227 | <TextControl |
| 228 | type={'number'} |
| 229 | label={__('Mobile', 'vk-blocks')} |
| 230 | value={slidesPerViewMobile} |
| 231 | onChange={(value) => { |
| 232 | if ( |
| 233 | Number.isNaN(Number(value)) || |
| 234 | Number(value) < 1 |
| 235 | ) { |
| 236 | setAttributes({ |
| 237 | slidesPerViewMobile: 1, |
| 238 | }); |
| 239 | } else if (slidesPerGroup === 'slides-per-view') { |
| 240 | setAttributes({ |
| 241 | slidesPerViewMobile: parseInt( |
| 242 | Number(value), |
| 243 | 10 |
| 244 | ), |
| 245 | }); |
| 246 | } else { |
| 247 | setAttributes({ |
| 248 | slidesPerViewMobile: parseFloat( |
| 249 | Number(value) |
| 250 | ), |
| 251 | }); |
| 252 | } |
| 253 | }} |
| 254 | min={1} |
| 255 | step={slidesPerGroup === 'slides-per-view' ? 1 : 0.1} |
| 256 | /> |
| 257 | {slidesPerViewMobileAlert} |
| 258 | {slidesPerViewMobileLoopAlert} |
| 259 | </BaseControl> |
| 260 | <BaseControl |
| 261 | label={__( |
| 262 | 'Number of items to change in a transition', |
| 263 | 'vk-blocks' |
| 264 | )} |
| 265 | id={`vk_slider-slidesPerGroup`} |
| 266 | > |
| 267 | <RadioControl |
| 268 | selected={slidesPerGroup} |
| 269 | className={'vk-radioControl'} |
| 270 | options={[ |
| 271 | { |
| 272 | label: __('One by One', 'vk-blocks'), |
| 273 | value: 'one-by-one', |
| 274 | }, |
| 275 | { |
| 276 | label: __( |
| 277 | 'Same as the number of items to display', |
| 278 | 'vk-blocks' |
| 279 | ), |
| 280 | value: 'slides-per-view', |
| 281 | }, |
| 282 | ]} |
| 283 | onChange={(value) => { |
| 284 | const attrs = { slidesPerGroup: value }; |
| 285 | if (value === 'slides-per-view') { |
| 286 | const pcVal = toSpg(slidesPerViewPC); |
| 287 | if (pcVal !== undefined) { |
| 288 | attrs.slidesPerViewPC = pcVal; |
| 289 | } |
| 290 | const tabletVal = toSpg(slidesPerViewTablet); |
| 291 | if (tabletVal !== undefined) { |
| 292 | attrs.slidesPerViewTablet = tabletVal; |
| 293 | } |
| 294 | const mobileVal = toSpg(slidesPerViewMobile); |
| 295 | if (mobileVal !== undefined) { |
| 296 | attrs.slidesPerViewMobile = mobileVal; |
| 297 | } |
| 298 | } |
| 299 | setAttributes(attrs); |
| 300 | }} |
| 301 | /> |
| 302 | </BaseControl> |
| 303 | <BaseControl id={`vk_slider-slidesPerGroup`}> |
| 304 | <ToggleControl |
| 305 | label={__('Centering the active slide', 'vk-blocks')} |
| 306 | className={'mb-1'} |
| 307 | checked={centeredSlides} |
| 308 | onChange={(checked) => |
| 309 | setAttributes({ centeredSlides: checked }) |
| 310 | } |
| 311 | help={__( |
| 312 | 'If you specify the center, you can display items that are cut off on the left and right.', |
| 313 | 'vk-blocks' |
| 314 | )} |
| 315 | /> |
| 316 | </BaseControl> |
| 317 | </> |
| 318 | ) : null; |
| 319 | |
| 320 | // 複数枚表示設定が無効な場合のアラート |
| 321 | const getDisabledReason = () => { |
| 322 | if (zoomAnimation) { |
| 323 | return __('Background Image Zoom Animation', 'vk-blocks'); |
| 324 | } else if (effect === 'fade') { |
| 325 | return __('Fade', 'vk-blocks') + ' ' + __('Effect', 'vk-blocks'); |
| 326 | } |
| 327 | return null; |
| 328 | }; |
| 329 | |
| 330 | const disabledReason = getDisabledReason(); |
| 331 | const disabledAlert = disabledReason ? ( |
| 332 | <div className="alert alert-warning font-size-11px"> |
| 333 | {sprintf( |
| 334 | /* translators: %s: name of the feature that is enabled (e.g. "Fade Effect" or "Background Image Zoom Animation") */ |
| 335 | __( |
| 336 | '%s is enabled, so multi-item settings cannot be configured.', |
| 337 | 'vk-blocks' |
| 338 | ), |
| 339 | disabledReason |
| 340 | )} |
| 341 | </div> |
| 342 | ) : null; |
| 343 | |
| 344 | multiItemSetting = ( |
| 345 | <PanelBody |
| 346 | title={__('Multi-item Display Setting', 'vk-blocks')} |
| 347 | initialOpen={false} |
| 348 | > |
| 349 | {disabledAlert} |
| 350 | {!zoomAnimation && multiItemUI} |
| 351 | </PanelBody> |
| 352 | ); |
| 353 | return multiItemSetting; |
| 354 | }; |
| 355 |