@webikon/webentor-core 0.9.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/.husky/pre-commit +40 -0
  2. package/.prettierrc.js +5 -0
  3. package/CHANGELOG.md +88 -0
  4. package/LICENCE.md +7 -0
  5. package/README.md +26 -0
  6. package/core-js/_alpine.ts +20 -0
  7. package/core-js/_slider.ts +232 -0
  8. package/core-js/_utils.ts +126 -0
  9. package/core-js/blocks-components/block-appender.tsx +36 -0
  10. package/core-js/blocks-components/button.tsx +424 -0
  11. package/core-js/blocks-components/custom-image-sizes-panel.tsx +197 -0
  12. package/core-js/blocks-components/index.ts +4 -0
  13. package/core-js/blocks-components/typography-picker-select.tsx +31 -0
  14. package/core-js/blocks-filters/_filter-core-typography.tsx +108 -0
  15. package/core-js/blocks-filters/_slider-settings.tsx +283 -0
  16. package/core-js/blocks-filters/index.ts +3 -0
  17. package/core-js/blocks-filters/responsive-settings/components/DisabledSliderInfo.tsx +10 -0
  18. package/core-js/blocks-filters/responsive-settings/constants.ts +11 -0
  19. package/core-js/blocks-filters/responsive-settings/index.tsx +196 -0
  20. package/core-js/blocks-filters/responsive-settings/settings/block-link/index.ts +1 -0
  21. package/core-js/blocks-filters/responsive-settings/settings/block-link/panel.tsx +47 -0
  22. package/core-js/blocks-filters/responsive-settings/settings/border/border/index.tsx +1 -0
  23. package/core-js/blocks-filters/responsive-settings/settings/border/border/properties.ts +27 -0
  24. package/core-js/blocks-filters/responsive-settings/settings/border/border/settings.tsx +310 -0
  25. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/index.tsx +1 -0
  26. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/properties.ts +31 -0
  27. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/settings.tsx +211 -0
  28. package/core-js/blocks-filters/responsive-settings/settings/border/index.ts +1 -0
  29. package/core-js/blocks-filters/responsive-settings/settings/border/panel.tsx +54 -0
  30. package/core-js/blocks-filters/responsive-settings/settings/container/display/index.ts +2 -0
  31. package/core-js/blocks-filters/responsive-settings/settings/container/display/properties.ts +167 -0
  32. package/core-js/blocks-filters/responsive-settings/settings/container/display/settings.tsx +73 -0
  33. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/index.ts +2 -0
  34. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/properties.ts +187 -0
  35. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/settings.tsx +131 -0
  36. package/core-js/blocks-filters/responsive-settings/settings/container/grid/index.ts +2 -0
  37. package/core-js/blocks-filters/responsive-settings/settings/container/grid/properties.ts +187 -0
  38. package/core-js/blocks-filters/responsive-settings/settings/container/grid/settings.tsx +132 -0
  39. package/core-js/blocks-filters/responsive-settings/settings/container/index.ts +4 -0
  40. package/core-js/blocks-filters/responsive-settings/settings/container/panel.tsx +92 -0
  41. package/core-js/blocks-filters/responsive-settings/settings/spacing/index.ts +3 -0
  42. package/core-js/blocks-filters/responsive-settings/settings/spacing/panel.tsx +45 -0
  43. package/core-js/blocks-filters/responsive-settings/settings/spacing/properties.ts +74 -0
  44. package/core-js/blocks-filters/responsive-settings/settings/spacing/settings.tsx +85 -0
  45. package/core-js/blocks-filters/responsive-settings/types/index.ts +68 -0
  46. package/core-js/blocks-filters/responsive-settings/utils.ts +321 -0
  47. package/core-js/blocks-utils/_use-block-parent.ts +27 -0
  48. package/core-js/blocks-utils/_use-post-types.ts +43 -0
  49. package/core-js/blocks-utils/_use-taxonomies.ts +29 -0
  50. package/core-js/blocks-utils/index.ts +3 -0
  51. package/core-js/config/webentor-config.ts +718 -0
  52. package/core-js/index.ts +14 -0
  53. package/core-js/types/_block-components.ts +7 -0
  54. package/core-js/types/_webentor-config.ts +182 -0
  55. package/package.json +98 -0
  56. package/resources/blocks/e-accordion/block.json +34 -0
  57. package/resources/blocks/e-accordion/e-accordion.block.tsx +125 -0
  58. package/resources/blocks/e-accordion/script.ts +1 -0
  59. package/resources/blocks/e-accordion/style.css +8 -0
  60. package/resources/blocks/e-accordion-group/block.json +56 -0
  61. package/resources/blocks/e-accordion-group/e-accordion-group.block.tsx +99 -0
  62. package/resources/blocks/e-breadcrumbs/block.json +41 -0
  63. package/resources/blocks/e-breadcrumbs/e-breadcrumbs.block.tsx +53 -0
  64. package/resources/blocks/e-button/block.json +32 -0
  65. package/resources/blocks/e-button/e-button.block.tsx +55 -0
  66. package/resources/blocks/e-gallery/block.json +90 -0
  67. package/resources/blocks/e-gallery/e-gallery.block.tsx +316 -0
  68. package/resources/blocks/e-icon-picker/block.json +37 -0
  69. package/resources/blocks/e-icon-picker/e-icon-picker.block.tsx +230 -0
  70. package/resources/blocks/e-icon-picker/style.css +17 -0
  71. package/resources/blocks/e-image/block.json +78 -0
  72. package/resources/blocks/e-image/e-image.block.tsx +331 -0
  73. package/resources/blocks/e-picker-query-loop/block.json +25 -0
  74. package/resources/blocks/e-picker-query-loop/e-picker-query-loop.block.tsx +189 -0
  75. package/resources/blocks/e-post-template/block.json +25 -0
  76. package/resources/blocks/e-post-template/e-post-template.block.tsx +100 -0
  77. package/resources/blocks/e-query-loop/block.json +36 -0
  78. package/resources/blocks/e-query-loop/constants.tsx +8 -0
  79. package/resources/blocks/e-query-loop/e-query-loop.block.tsx +270 -0
  80. package/resources/blocks/e-query-loop/taxonomy-controls.tsx +184 -0
  81. package/resources/blocks/e-slider/block.json +42 -0
  82. package/resources/blocks/e-slider/e-slider.block.tsx +100 -0
  83. package/resources/blocks/e-svg/block.json +37 -0
  84. package/resources/blocks/e-svg/e-svg.block.tsx +156 -0
  85. package/resources/blocks/e-tab-container/block.json +49 -0
  86. package/resources/blocks/e-tab-container/e-tab-container.block.tsx +123 -0
  87. package/resources/blocks/e-table/block.json +30 -0
  88. package/resources/blocks/e-table/e-table.block.tsx +120 -0
  89. package/resources/blocks/e-table/script.ts +48 -0
  90. package/resources/blocks/e-table-cell/block.json +40 -0
  91. package/resources/blocks/e-table-cell/e-table-cell.block.tsx +180 -0
  92. package/resources/blocks/e-table-row/block.json +28 -0
  93. package/resources/blocks/e-table-row/e-table-row.block.tsx +118 -0
  94. package/resources/blocks/e-tabs/block.json +27 -0
  95. package/resources/blocks/e-tabs/e-tabs.block.tsx +90 -0
  96. package/resources/blocks/l-404/block.json +51 -0
  97. package/resources/blocks/l-404/l-404.block.tsx +75 -0
  98. package/resources/blocks/l-flexible-container/block.json +34 -0
  99. package/resources/blocks/l-flexible-container/l-flexible-container.block.tsx +97 -0
  100. package/resources/blocks/l-footer/block.json +23 -0
  101. package/resources/blocks/l-footer/l-footer.block.tsx +51 -0
  102. package/resources/blocks/l-formatted-content/block.json +28 -0
  103. package/resources/blocks/l-formatted-content/l-formatted-content.block.tsx +97 -0
  104. package/resources/blocks/l-header/block.json +26 -0
  105. package/resources/blocks/l-header/l-header.block.tsx +100 -0
  106. package/resources/blocks/l-mobile-nav/block.json +15 -0
  107. package/resources/blocks/l-mobile-nav/l-mobile-nav.block.tsx +56 -0
  108. package/resources/blocks/l-mobile-nav/style.css +54 -0
  109. package/resources/blocks/l-nav-menu/block.json +27 -0
  110. package/resources/blocks/l-nav-menu/l-nav-menu.block.tsx +109 -0
  111. package/resources/blocks/l-nav-menu/style.css +134 -0
  112. package/resources/blocks/l-post-card/block.json +13 -0
  113. package/resources/blocks/l-post-card/l-post-card.block.tsx +52 -0
  114. package/resources/blocks/l-section/block.json +89 -0
  115. package/resources/blocks/l-section/l-section.block.tsx +316 -0
  116. package/resources/blocks/l-site-logo/block.json +15 -0
  117. package/resources/blocks/l-site-logo/l-site-logo.block.tsx +54 -0
  118. package/resources/core-components/slider/slider.script.ts +11 -0
  119. package/resources/core-components/slider/slider.style.css +134 -0
  120. package/resources/scripts/editor.ts +29 -0
  121. package/resources/styles/app.css +21 -0
  122. package/resources/styles/common/_editor.css +86 -0
  123. package/resources/styles/common/_form.css +83 -0
  124. package/resources/styles/common/_global.css +73 -0
  125. package/resources/styles/common/_theme.css +75 -0
  126. package/resources/styles/common/_utilities.css +33 -0
  127. package/resources/styles/common/_wordpress.css +110 -0
  128. package/resources/styles/components/_table.css +102 -0
  129. package/resources/styles/editor.css +16 -0
  130. package/resources/styles/partials/.gitkeep +0 -0
  131. package/resources/styles/partials/_header.css +21 -0
  132. package/resources/styles/partials/_pagination.css +35 -0
@@ -0,0 +1,40 @@
1
+ #!/bin/sh
2
+
3
+ # Skip install on CI
4
+ [ -n "$CI" ] && exit 0
5
+
6
+ # Check if any TypeScript, JavaScript, CSS files, or package files are staged for commit
7
+ if git diff --cached --name-only | grep -E '\.(ts|tsx|js|jsx|css|scss|sass|less)$|package\.json$|pnpm-lock\.yaml$' > /dev/null; then
8
+ echo "🔨 TypeScript/JavaScript/CSS/Package files detected, running pnpm build..."
9
+
10
+ # Run the build process
11
+ pnpm build
12
+
13
+ # Check if build was successful
14
+ if [ $? -ne 0 ]; then
15
+ echo "❌ Build failed! Commit aborted."
16
+ exit 1
17
+ fi
18
+
19
+ echo "✅ Build completed successfully!"
20
+
21
+ # Check if there are any changes in the public/ folder
22
+ if [ -n "$(git status --porcelain public/)" ]; then
23
+ echo "📦 Found changes in public/ folder, adding to commit..."
24
+
25
+ # Add all changes in public/ folder to the current commit
26
+ git add public/
27
+
28
+ # Show what was added
29
+ echo "📋 Added files to commit:"
30
+ git status --porcelain public/ | sed 's/^/ /'
31
+
32
+ echo "✅ Public folder changes have been automatically added to this commit!"
33
+ else
34
+ echo "ℹ️ No changes detected in public/ folder."
35
+ fi
36
+ else
37
+ echo "ℹ️ No TypeScript/JavaScript/CSS/Package files changed, skipping build."
38
+ fi
39
+
40
+ echo "🚀 Proceeding with commit..."
package/.prettierrc.js ADDED
@@ -0,0 +1,5 @@
1
+ import { createPrettierConfig } from '@webikon/webentor-configs/prettier';
2
+
3
+ export default createPrettierConfig({
4
+ tailwindStylesheet: './resources/styles/app.css',
5
+ });
package/CHANGELOG.md ADDED
@@ -0,0 +1,88 @@
1
+ # Webentor Core Changelog
2
+
3
+ ## DEV
4
+
5
+
6
+ ## 0.9.12
7
+
8
+ - Add `webentor.core.button.output` filter
9
+ - Allow picking up multiple post types in Query Loop block
10
+ - Add e-button HTML Element setting
11
+ - Fix webentorConfig safelist
12
+ - Fix image object-fit defaults
13
+ - Fix useParentBlock hook to get proper parent block
14
+ - Adjust responsive settings to be conditioned by parent block
15
+ - Fix Table block cell colspan
16
+ - Fix button url escaping & show conditional
17
+ - Fix Vite blocks build on Windows Herd
18
+ - **BREAKING**: Fix accordion collapse animation - if you customized accordion blade view, adjust its content wrapper
19
+ - Fix l-404 block button
20
+ - Fix $anchor passed variable in inner blocks
21
+
22
+ ## 0.9.11
23
+
24
+ - Fix husky install
25
+ - Add missing `wbtr:` prefixes in blocks edit
26
+ - Fix rendering in REST API request
27
+ - Refactor custom size settings into separate React component
28
+ - Improve block classes rendering conditions
29
+ - Improve block assets registering, so they can be overriden in the theme
30
+ - Allow picking up multiple post types in Picker Query Loop block
31
+ - NEW: Add basic `webentor-config`, which can be included in the theme, export `spacing`
32
+ - NEW: Add flexbox, grid and background image settings to **Section block**
33
+ - NEW: Add `webentor/e-gallery` block
34
+ - NEW: Add hooks `webentor/skip_render_block_blade` and `webentor/slider/view/swiper_params`
35
+ - NEW: Add `loop` and `slider_id` settings to **Slider block**
36
+ - NEW: Add flex/grid item order settings
37
+ - NEW: Add JS events `e_tabs_nav_initialized`, `e_tabs_nav_item_clicked`, `e_accordion_btn_clicked`
38
+ - NEW: Add components BEM classes
39
+ - NEW: Add `aspect-ratio` settings to `e-image` block.
40
+ - **BREAKING**: Add `aspect-ratio` config to `webentor-config.ts`
41
+ - **BREAKING:**: Change view paths for Blade `blocks` and `core-components`. Now these folder names doesn't need to be included in the path when including views from them.
42
+ - Replace `core-components.button.button` and `core-components.slider.slider` with `button.button` and `slider.slider`
43
+ - If you included blocks as `blocks.blockName.view`, remove `blocks.` and leave just `blockName.view`
44
+ - Fix slider pagination styles
45
+ - Update deps
46
+ - Fix publicDir Vite error
47
+ - Remove Button asPill parameter
48
+
49
+ ## 0.9.10
50
+
51
+ - Add ability to select icon in Button Block
52
+ - Fix condition to render flex/grid item classes depending on parent display setting
53
+
54
+ ## 0.9.9
55
+
56
+ - **BREAKING:** Move slider styles to `@layer components`
57
+ - Add more slider view classes
58
+ - Remove empty styles folders
59
+ - Add opacity to hidden FC blocks instead of hiding them entirely
60
+
61
+ ## 0.9.8
62
+
63
+ - Add `pnpm`
64
+ - Update node deps
65
+ - **BREAKING:** Rename npm package to `@webikon/webentor-core`
66
+ - **BREAKING:** Include `build/` assets
67
+
68
+ ## 0.9.7
69
+
70
+ - Fix empty content in WP REST API response
71
+
72
+ ## 0.9.6
73
+
74
+ - Add Icon Picker block
75
+ - Update deps
76
+
77
+ ## 0.9.5
78
+
79
+ - Add new border & border radius responsive settings, applied to Flexible Content, Section and Image
80
+ - Add Picker Query Loop block
81
+ - (BREAKING) Remove old Image block border and rounded setting which are replaced by border responsive settings
82
+
83
+ ## 0.9.4
84
+
85
+ - Bundle `core-js` package
86
+ - Add Button component extension hooks
87
+ - Add Vite build plugin
88
+ - Update deps
package/LICENCE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) Webikon s.r.o.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # webentor-core
2
+
3
+ Reusable Webentor core functionality for PHP and JS runtimes.
4
+
5
+ ## Packages
6
+
7
+ - Composer: `webikon/webentor-core`
8
+ - npm: `@webikon/webentor-core`
9
+
10
+ ## Requirements
11
+
12
+ - PHP >= 8.2
13
+ - Node >= 20
14
+ - pnpm >= 10
15
+
16
+ ## Development
17
+
18
+ ```bash
19
+ composer install
20
+ pnpm install
21
+ pnpm build
22
+ ```
23
+
24
+ ## Linting
25
+
26
+ Core consumes shared presets from `@webikon/webentor-configs` to keep lint behavior aligned across repositories.
@@ -0,0 +1,20 @@
1
+ import collapse from '@alpinejs/collapse';
2
+ import Alpine from 'alpinejs';
3
+
4
+ // Initialize Alpine if it's not already initialized
5
+ const AlpineInstance = window.Alpine || Alpine;
6
+ if (!window.Alpine) {
7
+ // Make Alpine globally available
8
+ window.Alpine = Alpine;
9
+
10
+ document.addEventListener('DOMContentLoaded', () => {
11
+ AlpineInstance.start();
12
+ });
13
+ }
14
+
15
+ document.addEventListener('alpine:init', () => {
16
+ // Register default plugins
17
+ AlpineInstance.plugin(collapse);
18
+ });
19
+
20
+ export { AlpineInstance as Alpine };
@@ -0,0 +1,232 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import Swiper from 'swiper/bundle';
3
+
4
+ import { debounce, throttle } from './_utils';
5
+
6
+ type BreakpointConfig = Swiper & {
7
+ enabled: boolean;
8
+ };
9
+
10
+ class SliderComponent {
11
+ private element: HTMLElement;
12
+ private params: Swiper & {
13
+ breakpoints: Record<string, BreakpointConfig>;
14
+ autoplayControl: boolean;
15
+ };
16
+ private swiper: Swiper | null;
17
+ private container: HTMLElement;
18
+ private breakpoints: Record<string, BreakpointConfig> | null;
19
+ private autoplayButton: HTMLElement | null;
20
+ private timerSeconds: HTMLElement | null;
21
+ private timerCircle: HTMLElement | null;
22
+ private autoplayControlEnabled: boolean;
23
+ private resizeHandler?: () => void;
24
+
25
+ constructor(element, params) {
26
+ this.element = element;
27
+ this.params = params;
28
+ this.swiper = null;
29
+ this.container = element.querySelector('.js-swiper-container');
30
+ this.breakpoints = null;
31
+
32
+ const { breakpoints, autoplayControl } = this.params;
33
+ this.breakpoints = breakpoints;
34
+
35
+ this.autoplayButton = this.element.querySelector('.js-autoplay-btn');
36
+ this.timerSeconds = this.element.querySelector('.js-slider-timer-seconds');
37
+ this.timerCircle = this.element.querySelector('.js-slider-timer-circle');
38
+ this.autoplayControlEnabled = autoplayControl;
39
+
40
+ this.initSwiper();
41
+ this.addResizeListener();
42
+ this.addBreakpointChecker();
43
+ this.breakpointChecker = this.breakpointChecker.bind(this);
44
+
45
+ if (this.autoplayControlEnabled && this.autoplayButton) {
46
+ this.autoplayButton.addEventListener('click', () =>
47
+ this.toggleAutoplay(),
48
+ );
49
+ }
50
+ }
51
+
52
+ initSwiper() {
53
+ this.breakpointChecker();
54
+ }
55
+
56
+ getSwiper() {
57
+ return this.swiper;
58
+ }
59
+
60
+ addResizeListener() {
61
+ this.resizeHandler = () => {
62
+ this.breakpointChecker();
63
+ };
64
+
65
+ window.addEventListener('resize', debounce(this.resizeHandler, 300));
66
+ }
67
+
68
+ breakpointChecker() {
69
+ const currentBreakpoint = this.getCurrentBreakpoint(window.innerWidth);
70
+
71
+ if (currentBreakpoint && currentBreakpoint.enabled) {
72
+ this.enableSwiper();
73
+ } else {
74
+ this.destroySwiper();
75
+ }
76
+ }
77
+
78
+ addBreakpointChecker() {
79
+ this.breakpointChecker();
80
+ }
81
+
82
+ enableSwiper() {
83
+ if (this.swiper) {
84
+ return;
85
+ }
86
+
87
+ this.element.classList.add('slider-enabled');
88
+
89
+ // Find all direct child elements of ".js-slider-wrapper"
90
+ const sliderWrapperChildren =
91
+ this.element.querySelector('.js-slider-wrapper').children;
92
+
93
+ // Add "swiper-slide" class to each child element
94
+ for (const child of sliderWrapperChildren) {
95
+ child.classList.add('swiper-slide');
96
+ }
97
+
98
+ // this.recalculateSlidesWidth();
99
+
100
+ this.swiper = new Swiper(this.container, {
101
+ ...this.params,
102
+ on: {
103
+ init: (swiper) => {
104
+ this.fixInitialSlideCalculation(swiper);
105
+
106
+ if (this.autoplayControlEnabled) {
107
+ this.updateAutoplayButtonState(swiper);
108
+ }
109
+ },
110
+ autoplayTimeLeft: (swiper, time, progress) => {
111
+ if (this.autoplayControlEnabled) {
112
+ this.timerCircle.style.setProperty(
113
+ '--slider-timer-progress',
114
+ 1 - progress,
115
+ );
116
+ this.timerSeconds.textContent = `${Math.ceil(time / 1000)}s`;
117
+ }
118
+ },
119
+ autoplayResume: (swiper) => {
120
+ if (this.autoplayControlEnabled) {
121
+ this.updateAutoplayButtonState(swiper);
122
+ }
123
+ },
124
+ resize: throttle(this.fixInitialSlideCalculation, 100),
125
+ ...this.params.on,
126
+ },
127
+ navigation: {
128
+ nextEl: this.element.querySelector('.js-slider-button-next'),
129
+ prevEl: this.element.querySelector('.js-slider-button-prev'),
130
+ },
131
+ });
132
+
133
+ // Ensure that slidesPerGroupSkip is defined
134
+ if (this.swiper.params.slidesPerGroupSkip === undefined) {
135
+ this.swiper.params.slidesPerGroupSkip = 0;
136
+ }
137
+ }
138
+
139
+ destroySwiper() {
140
+ if (!this.swiper) {
141
+ return;
142
+ }
143
+
144
+ // Remove the "swiper-slide" class from all direct child elements of ".js-slider-wrapper"
145
+ const sliderWrapperChildren =
146
+ this.element.querySelector('.js-slider-wrapper').children;
147
+ for (const child of sliderWrapperChildren) {
148
+ child.classList.remove('swiper-slide');
149
+ }
150
+
151
+ this.element.classList.remove('slider-enabled');
152
+
153
+ this.swiper.destroy(true, true);
154
+ this.swiper = undefined;
155
+ }
156
+
157
+ // Get current breakpoint by iterating over the breakpoint and finding the first one that is bigger than the current width but smaller than the next breakpoint
158
+ getCurrentBreakpoint(width) {
159
+ let currentBreakpoint = 0;
160
+
161
+ for (const breakpoint in this.breakpoints) {
162
+ if (width > parseInt(breakpoint)) {
163
+ currentBreakpoint = parseInt(breakpoint);
164
+ } else {
165
+ break;
166
+ }
167
+ }
168
+
169
+ return this.breakpoints[currentBreakpoint];
170
+ }
171
+
172
+ recalculateSlidesWidth() {
173
+ const wrapper = this.element.querySelector('.js-slider-wrapper');
174
+
175
+ if (!wrapper) return;
176
+
177
+ const slides = Array.from(wrapper.children);
178
+
179
+ if (slides.length === 0) return;
180
+
181
+ // Get the dynamically calculated gap
182
+ const gap = parseInt(window.getComputedStyle(wrapper).gap);
183
+
184
+ // Return early if the gap is zero
185
+ if (gap === 0) return;
186
+
187
+ const maxWidth = `calc(100% - ${gap}px)`;
188
+ const translateX = `${gap / 2}px`;
189
+
190
+ slides.forEach((slide) => {
191
+ slide.style.maxWidth = maxWidth;
192
+ slide.style.transform = `translateX(${translateX})`;
193
+ });
194
+ }
195
+
196
+ // Swiper incorrectly calculates initial slide when using loop and centered slides
197
+ // See: https://github.com/nolimits4web/swiper/issues/7216
198
+ fixInitialSlideCalculation(swiper) {
199
+ if (swiper.params.centeredSlides) {
200
+ swiper.slideToLoop(0, 0);
201
+ }
202
+ }
203
+
204
+ toggleAutoplay() {
205
+ if (this.swiper) {
206
+ if (this.swiper.autoplay.running && !this.swiper.autoplay.paused) {
207
+ this.swiper.autoplay.pause();
208
+ } else {
209
+ this.swiper.autoplay.resume();
210
+ }
211
+ this.updateAutoplayButtonState(this.swiper);
212
+ }
213
+ }
214
+
215
+ updateAutoplayButtonState(swiper) {
216
+ if (this.autoplayButton) {
217
+ this.autoplayButton.setAttribute(
218
+ 'aria-label',
219
+ swiper?.autoplay.running && !swiper?.autoplay.paused
220
+ ? __('Pause autoplay', 'webentor')
221
+ : __('Start autoplay', 'webentor'),
222
+ );
223
+ }
224
+ this.element.classList.toggle(
225
+ 'is-playing',
226
+ swiper?.autoplay.running && !swiper?.autoplay.paused,
227
+ );
228
+ }
229
+ }
230
+
231
+ // Also export Swiper for external use
232
+ export { SliderComponent, Swiper };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Immutably sets a value inside an object. Like `lodash#set`, but returning a
3
+ * new object. Treats nullish initial values as empty objects. Clones any
4
+ * nested objects. Supports arrays, too.
5
+ *
6
+ * @param {Object} object Object to set a value in.
7
+ * @param {number|string|Array} path Path in the object to modify.
8
+ * @param {*} value New value to set.
9
+ * @return {Object} Cloned object with the new value set.
10
+ */
11
+ export function setImmutably(
12
+ object: object,
13
+ path: number | string | Array<any>,
14
+ value: any,
15
+ ): object {
16
+ // Normalize path
17
+ path = Array.isArray(path) ? [...path] : [path];
18
+
19
+ // Shallowly clone the base of the object
20
+ object = Array.isArray(object) ? [...object] : { ...object };
21
+
22
+ const leaf = path.pop();
23
+
24
+ // Traverse object from root to leaf, shallowly cloning at each level
25
+ let prev = object;
26
+ for (const key of path) {
27
+ const lvl = prev[key];
28
+ prev = prev[key] = Array.isArray(lvl) ? [...lvl] : { ...lvl };
29
+ }
30
+
31
+ prev[leaf] = value;
32
+
33
+ return object;
34
+ }
35
+
36
+ /**
37
+ * Debounce functions for better performance
38
+ * Source: https://www.joshwcomeau.com/snippets/javascript/debounce/
39
+ *
40
+ * @param {Function} fn The function to debounce
41
+ * @param {number} wait The time, in milliseconds, to wait before calling the function
42
+ * @return {Function} The debounced function
43
+ */
44
+ export const debounce = (callback, wait = 50) => {
45
+ let timeoutId = null;
46
+ return (...args) => {
47
+ window.clearTimeout(timeoutId);
48
+ timeoutId = window.setTimeout(() => {
49
+ callback(...args);
50
+ }, wait);
51
+ };
52
+ };
53
+
54
+ /**
55
+ * Throttle a function so that it is only called once within a given time interval.
56
+ *
57
+ * @param {Function} fn - The function to throttle
58
+ * @param {Number} wait - The time interval, in milliseconds, to wait between calls to the function
59
+ * @return {Function} - Returns a throttled version of the original function
60
+ */
61
+ export function throttle(fn, wait = 20) {
62
+ let time = Date.now();
63
+ return function (...args) {
64
+ if (time + wait - Date.now() < 0) {
65
+ fn(...args);
66
+ time = Date.now();
67
+ }
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Convert a string to camelCase
73
+ *
74
+ * @param {string} text - The string to convert
75
+ * @return {string} The camelCase string
76
+ */
77
+ export function camelize(text: string) {
78
+ const a = text
79
+ .toLowerCase()
80
+ .replace(/[-_\s.]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''));
81
+ return a.substring(0, 1).toLowerCase() + a.substring(1);
82
+ }
83
+
84
+ /**
85
+ * Check if an object is empty
86
+ *
87
+ * @param {Object} obj - The object to check
88
+ * @return {boolean} True if the object is empty, false otherwise
89
+ */
90
+ export function isEmpty(obj: object) {
91
+ for (const prop in obj) {
92
+ if (Object.hasOwn(obj, prop)) {
93
+ return false;
94
+ }
95
+ }
96
+
97
+ return true;
98
+ }
99
+
100
+ /**
101
+ * Get color slug by color hex value.
102
+ * Usually we don't want to save hex value from WP color palette, we want to save slug.
103
+ *
104
+ * @param colors - Colors array
105
+ * @param color - Color string
106
+ * @returns Color slug
107
+ */
108
+ export const getColorSlugByColor = (
109
+ colors: { color: string; slug: string }[],
110
+ color: string,
111
+ ) => {
112
+ return colors.find((c: any) => c.color === color)?.slug;
113
+ };
114
+
115
+ /**
116
+ * Get color by slug.
117
+ * This is the opposite of getColorSlugByColor.
118
+ * We want to use hex value for ColorPallete control, but we saved it as slug.
119
+ *
120
+ * @param colors - Colors array
121
+ * @param slug - Color slug
122
+ * @returns Color string
123
+ */
124
+ export const getColorBySlug = (colors: any, slug: string) => {
125
+ return colors.find((color: any) => color.slug === slug)?.color;
126
+ };
@@ -0,0 +1,36 @@
1
+ import { Inserter } from '@wordpress/block-editor';
2
+ import { Button, IconType } from '@wordpress/components';
3
+
4
+ type WebentorBlockAppenderProps = {
5
+ rootClientId: string | null;
6
+ text: string;
7
+ icon?: IconType;
8
+ variant?: 'primary' | 'secondary' | 'tertiary';
9
+ };
10
+
11
+ /**
12
+ * Block appender component with customizable button.
13
+ */
14
+ export const WebentorBlockAppender: React.FC<WebentorBlockAppenderProps> = ({
15
+ rootClientId,
16
+ text,
17
+ icon,
18
+ variant,
19
+ }) => {
20
+ return (
21
+ <Inserter
22
+ rootClientId={rootClientId}
23
+ renderToggle={({ onToggle }) => (
24
+ <Button
25
+ className="webentor-block-appender"
26
+ onClick={onToggle}
27
+ // disabled={disabled}
28
+ icon={icon ?? 'plus'}
29
+ text={text}
30
+ variant={variant ?? 'secondary'}
31
+ />
32
+ )}
33
+ isAppender
34
+ />
35
+ );
36
+ };