@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.
- package/.husky/pre-commit +40 -0
- package/.prettierrc.js +5 -0
- package/CHANGELOG.md +88 -0
- package/LICENCE.md +7 -0
- package/README.md +26 -0
- package/core-js/_alpine.ts +20 -0
- package/core-js/_slider.ts +232 -0
- package/core-js/_utils.ts +126 -0
- package/core-js/blocks-components/block-appender.tsx +36 -0
- package/core-js/blocks-components/button.tsx +424 -0
- package/core-js/blocks-components/custom-image-sizes-panel.tsx +197 -0
- package/core-js/blocks-components/index.ts +4 -0
- package/core-js/blocks-components/typography-picker-select.tsx +31 -0
- package/core-js/blocks-filters/_filter-core-typography.tsx +108 -0
- package/core-js/blocks-filters/_slider-settings.tsx +283 -0
- package/core-js/blocks-filters/index.ts +3 -0
- package/core-js/blocks-filters/responsive-settings/components/DisabledSliderInfo.tsx +10 -0
- package/core-js/blocks-filters/responsive-settings/constants.ts +11 -0
- package/core-js/blocks-filters/responsive-settings/index.tsx +196 -0
- package/core-js/blocks-filters/responsive-settings/settings/block-link/index.ts +1 -0
- package/core-js/blocks-filters/responsive-settings/settings/block-link/panel.tsx +47 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border/index.tsx +1 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border/properties.ts +27 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border/settings.tsx +310 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/index.tsx +1 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/properties.ts +31 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/settings.tsx +211 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/index.ts +1 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/panel.tsx +54 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/display/index.ts +2 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/display/properties.ts +167 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/display/settings.tsx +73 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/index.ts +2 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/properties.ts +187 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/settings.tsx +131 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/index.ts +2 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/properties.ts +187 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/settings.tsx +132 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/container/panel.tsx +92 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/index.ts +3 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/panel.tsx +45 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/properties.ts +74 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/settings.tsx +85 -0
- package/core-js/blocks-filters/responsive-settings/types/index.ts +68 -0
- package/core-js/blocks-filters/responsive-settings/utils.ts +321 -0
- package/core-js/blocks-utils/_use-block-parent.ts +27 -0
- package/core-js/blocks-utils/_use-post-types.ts +43 -0
- package/core-js/blocks-utils/_use-taxonomies.ts +29 -0
- package/core-js/blocks-utils/index.ts +3 -0
- package/core-js/config/webentor-config.ts +718 -0
- package/core-js/index.ts +14 -0
- package/core-js/types/_block-components.ts +7 -0
- package/core-js/types/_webentor-config.ts +182 -0
- package/package.json +98 -0
- package/resources/blocks/e-accordion/block.json +34 -0
- package/resources/blocks/e-accordion/e-accordion.block.tsx +125 -0
- package/resources/blocks/e-accordion/script.ts +1 -0
- package/resources/blocks/e-accordion/style.css +8 -0
- package/resources/blocks/e-accordion-group/block.json +56 -0
- package/resources/blocks/e-accordion-group/e-accordion-group.block.tsx +99 -0
- package/resources/blocks/e-breadcrumbs/block.json +41 -0
- package/resources/blocks/e-breadcrumbs/e-breadcrumbs.block.tsx +53 -0
- package/resources/blocks/e-button/block.json +32 -0
- package/resources/blocks/e-button/e-button.block.tsx +55 -0
- package/resources/blocks/e-gallery/block.json +90 -0
- package/resources/blocks/e-gallery/e-gallery.block.tsx +316 -0
- package/resources/blocks/e-icon-picker/block.json +37 -0
- package/resources/blocks/e-icon-picker/e-icon-picker.block.tsx +230 -0
- package/resources/blocks/e-icon-picker/style.css +17 -0
- package/resources/blocks/e-image/block.json +78 -0
- package/resources/blocks/e-image/e-image.block.tsx +331 -0
- package/resources/blocks/e-picker-query-loop/block.json +25 -0
- package/resources/blocks/e-picker-query-loop/e-picker-query-loop.block.tsx +189 -0
- package/resources/blocks/e-post-template/block.json +25 -0
- package/resources/blocks/e-post-template/e-post-template.block.tsx +100 -0
- package/resources/blocks/e-query-loop/block.json +36 -0
- package/resources/blocks/e-query-loop/constants.tsx +8 -0
- package/resources/blocks/e-query-loop/e-query-loop.block.tsx +270 -0
- package/resources/blocks/e-query-loop/taxonomy-controls.tsx +184 -0
- package/resources/blocks/e-slider/block.json +42 -0
- package/resources/blocks/e-slider/e-slider.block.tsx +100 -0
- package/resources/blocks/e-svg/block.json +37 -0
- package/resources/blocks/e-svg/e-svg.block.tsx +156 -0
- package/resources/blocks/e-tab-container/block.json +49 -0
- package/resources/blocks/e-tab-container/e-tab-container.block.tsx +123 -0
- package/resources/blocks/e-table/block.json +30 -0
- package/resources/blocks/e-table/e-table.block.tsx +120 -0
- package/resources/blocks/e-table/script.ts +48 -0
- package/resources/blocks/e-table-cell/block.json +40 -0
- package/resources/blocks/e-table-cell/e-table-cell.block.tsx +180 -0
- package/resources/blocks/e-table-row/block.json +28 -0
- package/resources/blocks/e-table-row/e-table-row.block.tsx +118 -0
- package/resources/blocks/e-tabs/block.json +27 -0
- package/resources/blocks/e-tabs/e-tabs.block.tsx +90 -0
- package/resources/blocks/l-404/block.json +51 -0
- package/resources/blocks/l-404/l-404.block.tsx +75 -0
- package/resources/blocks/l-flexible-container/block.json +34 -0
- package/resources/blocks/l-flexible-container/l-flexible-container.block.tsx +97 -0
- package/resources/blocks/l-footer/block.json +23 -0
- package/resources/blocks/l-footer/l-footer.block.tsx +51 -0
- package/resources/blocks/l-formatted-content/block.json +28 -0
- package/resources/blocks/l-formatted-content/l-formatted-content.block.tsx +97 -0
- package/resources/blocks/l-header/block.json +26 -0
- package/resources/blocks/l-header/l-header.block.tsx +100 -0
- package/resources/blocks/l-mobile-nav/block.json +15 -0
- package/resources/blocks/l-mobile-nav/l-mobile-nav.block.tsx +56 -0
- package/resources/blocks/l-mobile-nav/style.css +54 -0
- package/resources/blocks/l-nav-menu/block.json +27 -0
- package/resources/blocks/l-nav-menu/l-nav-menu.block.tsx +109 -0
- package/resources/blocks/l-nav-menu/style.css +134 -0
- package/resources/blocks/l-post-card/block.json +13 -0
- package/resources/blocks/l-post-card/l-post-card.block.tsx +52 -0
- package/resources/blocks/l-section/block.json +89 -0
- package/resources/blocks/l-section/l-section.block.tsx +316 -0
- package/resources/blocks/l-site-logo/block.json +15 -0
- package/resources/blocks/l-site-logo/l-site-logo.block.tsx +54 -0
- package/resources/core-components/slider/slider.script.ts +11 -0
- package/resources/core-components/slider/slider.style.css +134 -0
- package/resources/scripts/editor.ts +29 -0
- package/resources/styles/app.css +21 -0
- package/resources/styles/common/_editor.css +86 -0
- package/resources/styles/common/_form.css +83 -0
- package/resources/styles/common/_global.css +73 -0
- package/resources/styles/common/_theme.css +75 -0
- package/resources/styles/common/_utilities.css +33 -0
- package/resources/styles/common/_wordpress.css +110 -0
- package/resources/styles/components/_table.css +102 -0
- package/resources/styles/editor.css +16 -0
- package/resources/styles/partials/.gitkeep +0 -0
- package/resources/styles/partials/_header.css +21 -0
- package/resources/styles/partials/_pagination.css +35 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { FormTokenField } from '@wordpress/components';
|
|
3
|
+
import { useDebounce } from '@wordpress/compose';
|
|
4
|
+
import { useSelect } from '@wordpress/data';
|
|
5
|
+
import { decodeEntities } from '@wordpress/html-entities';
|
|
6
|
+
|
|
7
|
+
import { useTaxonomies } from '@webentorCore/blocks-utils/_use-taxonomies';
|
|
8
|
+
|
|
9
|
+
const EMPTY_ARRAY = [];
|
|
10
|
+
const BASE_QUERY = {
|
|
11
|
+
order: 'asc',
|
|
12
|
+
_fields: 'id,name',
|
|
13
|
+
context: 'view',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Helper function to get the term id based on user input in terms `FormTokenField`.
|
|
17
|
+
const getTermIdByTermValue = (terms, termValue) => {
|
|
18
|
+
// First we check for exact match by `term.id` or case sensitive `term.name` match.
|
|
19
|
+
const termId =
|
|
20
|
+
termValue?.id || terms?.find((term) => term.name === termValue)?.id;
|
|
21
|
+
if (termId) {
|
|
22
|
+
return termId;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Here we make an extra check for entered terms in a non case sensitive way,
|
|
27
|
+
* to match user expectations, due to `FormTokenField` behaviour that shows
|
|
28
|
+
* suggestions which are case insensitive.
|
|
29
|
+
*
|
|
30
|
+
* Although WP tries to discourage users to add terms with the same name (case insensitive),
|
|
31
|
+
* it's still possible if you manually change the name, as long as the terms have different slugs.
|
|
32
|
+
* In this edge case we always apply the first match from the terms list.
|
|
33
|
+
*/
|
|
34
|
+
const termValueLower = termValue.toLocaleLowerCase();
|
|
35
|
+
return terms?.find((term) => term.name.toLocaleLowerCase() === termValueLower)
|
|
36
|
+
?.id;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function TaxonomyControls({ onChange, query }) {
|
|
40
|
+
const { postType, taxQuery } = query;
|
|
41
|
+
|
|
42
|
+
const taxonomies = useTaxonomies(postType);
|
|
43
|
+
if (!taxonomies || taxonomies.length === 0) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
{taxonomies.map((taxonomy) => {
|
|
50
|
+
const termIds = taxQuery?.[taxonomy.slug] || [];
|
|
51
|
+
const handleChange = (newTermIds) =>
|
|
52
|
+
onChange({
|
|
53
|
+
taxQuery: {
|
|
54
|
+
...taxQuery,
|
|
55
|
+
[taxonomy.slug]: newTermIds,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<TaxonomyItem
|
|
61
|
+
key={taxonomy.slug}
|
|
62
|
+
taxonomy={taxonomy}
|
|
63
|
+
termIds={termIds}
|
|
64
|
+
onChange={handleChange}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Renders a `FormTokenField` for a given taxonomy.
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} props The props for the component.
|
|
76
|
+
* @param {Object} props.taxonomy The taxonomy object.
|
|
77
|
+
* @param {number[]} props.termIds An array with the block's term ids for the given taxonomy.
|
|
78
|
+
* @param {Function} props.onChange Callback `onChange` function.
|
|
79
|
+
* @return {JSX.Element} The rendered component.
|
|
80
|
+
*/
|
|
81
|
+
function TaxonomyItem({ taxonomy, termIds, onChange }) {
|
|
82
|
+
const [search, setSearch] = useState('');
|
|
83
|
+
const [value, setValue] = useState(EMPTY_ARRAY);
|
|
84
|
+
const [suggestions, setSuggestions] = useState(EMPTY_ARRAY);
|
|
85
|
+
const debouncedSearch = useDebounce(setSearch, 250);
|
|
86
|
+
const { searchResults, searchHasResolved } = useSelect(
|
|
87
|
+
(select) => {
|
|
88
|
+
if (!search) {
|
|
89
|
+
return { searchResults: EMPTY_ARRAY, searchHasResolved: true };
|
|
90
|
+
}
|
|
91
|
+
const { getEntityRecords, hasFinishedResolution } = select('core');
|
|
92
|
+
const selectorArgs = [
|
|
93
|
+
'taxonomy',
|
|
94
|
+
taxonomy.slug,
|
|
95
|
+
{
|
|
96
|
+
...BASE_QUERY,
|
|
97
|
+
search,
|
|
98
|
+
orderby: 'name',
|
|
99
|
+
exclude: termIds,
|
|
100
|
+
per_page: 20,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
return {
|
|
104
|
+
searchResults: getEntityRecords(...selectorArgs),
|
|
105
|
+
searchHasResolved: hasFinishedResolution(
|
|
106
|
+
'getEntityRecords',
|
|
107
|
+
selectorArgs,
|
|
108
|
+
),
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
[search, termIds],
|
|
112
|
+
);
|
|
113
|
+
// `existingTerms` are the ones fetched from the API and their type is `{ id: number; name: string }`.
|
|
114
|
+
// They are used to extract the terms' names to populate the `FormTokenField` properly
|
|
115
|
+
// and to sanitize the provided `termIds`, by setting only the ones that exist.
|
|
116
|
+
const existingTerms = useSelect(
|
|
117
|
+
(select) => {
|
|
118
|
+
if (!termIds?.length) {
|
|
119
|
+
return EMPTY_ARRAY;
|
|
120
|
+
}
|
|
121
|
+
const { getEntityRecords } = select('core');
|
|
122
|
+
return getEntityRecords('taxonomy', taxonomy.slug, {
|
|
123
|
+
...BASE_QUERY,
|
|
124
|
+
include: termIds,
|
|
125
|
+
per_page: termIds.length,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
[termIds],
|
|
129
|
+
);
|
|
130
|
+
// Update the `value` state only after the selectors are resolved
|
|
131
|
+
// to avoid emptying the input when we're changing terms.
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!termIds?.length) {
|
|
134
|
+
setValue(EMPTY_ARRAY);
|
|
135
|
+
}
|
|
136
|
+
if (!existingTerms?.length) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Returns only the existing entity ids. This prevents the component
|
|
140
|
+
// from crashing in the editor, when non existing ids are provided.
|
|
141
|
+
const sanitizedValue = termIds.reduce((accumulator, id) => {
|
|
142
|
+
const entity = existingTerms.find((term) => term.id === id);
|
|
143
|
+
if (entity) {
|
|
144
|
+
accumulator.push({
|
|
145
|
+
id,
|
|
146
|
+
value: entity.name,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return accumulator;
|
|
150
|
+
}, []);
|
|
151
|
+
setValue(sanitizedValue);
|
|
152
|
+
}, [termIds, existingTerms]);
|
|
153
|
+
// Update suggestions only when the query has resolved.
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (!searchHasResolved) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
setSuggestions(searchResults.map((result) => result.name));
|
|
159
|
+
}, [searchResults, searchHasResolved]);
|
|
160
|
+
const onTermsChange = (newTermValues) => {
|
|
161
|
+
const newTermIds = new Set();
|
|
162
|
+
for (const termValue of newTermValues) {
|
|
163
|
+
const termId = getTermIdByTermValue(searchResults, termValue);
|
|
164
|
+
if (termId) {
|
|
165
|
+
newTermIds.add(termId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
setSuggestions(EMPTY_ARRAY);
|
|
169
|
+
onChange(Array.from(newTermIds));
|
|
170
|
+
};
|
|
171
|
+
return (
|
|
172
|
+
<div className="block-library-query-inspector__taxonomy-control">
|
|
173
|
+
<FormTokenField
|
|
174
|
+
label={taxonomy.name}
|
|
175
|
+
value={value}
|
|
176
|
+
onInputChange={debouncedSearch}
|
|
177
|
+
suggestions={suggestions}
|
|
178
|
+
displayTransform={decodeEntities}
|
|
179
|
+
onChange={onTermsChange}
|
|
180
|
+
__experimentalShowHowTo={false}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/webentor-block.json",
|
|
3
|
+
"apiVersion": 3,
|
|
4
|
+
"name": "webentor/e-slider",
|
|
5
|
+
"title": "Slider",
|
|
6
|
+
"description": "Slider block.",
|
|
7
|
+
"category": "webentor-elements",
|
|
8
|
+
"icon": "slides",
|
|
9
|
+
"keywords": ["slider", "carousel"],
|
|
10
|
+
"example": {
|
|
11
|
+
"attributes": {
|
|
12
|
+
"mode": "preview",
|
|
13
|
+
"data": {
|
|
14
|
+
"preview_image_help": true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"attributes": {
|
|
19
|
+
"slider": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"default": {}
|
|
22
|
+
},
|
|
23
|
+
"template": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"default": null
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"supports": {
|
|
29
|
+
"color": {
|
|
30
|
+
"background": false,
|
|
31
|
+
"text": true
|
|
32
|
+
},
|
|
33
|
+
"webentor": {
|
|
34
|
+
"spacing": true,
|
|
35
|
+
"display": true,
|
|
36
|
+
"grid": true,
|
|
37
|
+
"gridItem": true,
|
|
38
|
+
"flexbox": true,
|
|
39
|
+
"flexboxItem": true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InnerBlocks,
|
|
3
|
+
useBlockProps,
|
|
4
|
+
useInnerBlocksProps,
|
|
5
|
+
} from '@wordpress/block-editor';
|
|
6
|
+
import {
|
|
7
|
+
BlockEditProps,
|
|
8
|
+
registerBlockType,
|
|
9
|
+
TemplateArray,
|
|
10
|
+
} from '@wordpress/blocks';
|
|
11
|
+
import { applyFilters } from '@wordpress/hooks';
|
|
12
|
+
import { __ } from '@wordpress/i18n';
|
|
13
|
+
|
|
14
|
+
import { useBlockParent } from '@webentorCore/blocks-utils/_use-block-parent';
|
|
15
|
+
|
|
16
|
+
import block from './block.json';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Edit component.
|
|
20
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit
|
|
21
|
+
*
|
|
22
|
+
* @param {object} props The block props.
|
|
23
|
+
* @returns {Function} Render the edit screen
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// TODO extend with slider/responsive settings
|
|
27
|
+
type AttributesType = {
|
|
28
|
+
coverImage: string;
|
|
29
|
+
template?: TemplateArray;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const BlockEdit: React.FC<BlockEditProps<AttributesType>> = (props) => {
|
|
33
|
+
const { attributes } = props;
|
|
34
|
+
|
|
35
|
+
const blockProps = useBlockProps();
|
|
36
|
+
const parentBlockProps = useBlockParent();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Filter allowed blocks used in webentor/e-slider inner block
|
|
40
|
+
*/
|
|
41
|
+
const allowedBlocks: string[] = applyFilters(
|
|
42
|
+
'webentor.core.e-slider.allowedBlocks',
|
|
43
|
+
[
|
|
44
|
+
'webentor/l-flexible-container',
|
|
45
|
+
'webentor/e-query-loop',
|
|
46
|
+
'webentor/e-picker-query-loop',
|
|
47
|
+
],
|
|
48
|
+
blockProps,
|
|
49
|
+
parentBlockProps,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Filter template used in webentor/e-slider inner block
|
|
54
|
+
*/
|
|
55
|
+
const defaultTemplate: TemplateArray = attributes?.template;
|
|
56
|
+
const template: TemplateArray = applyFilters(
|
|
57
|
+
'webentor.core.e-slider.template',
|
|
58
|
+
defaultTemplate,
|
|
59
|
+
blockProps,
|
|
60
|
+
parentBlockProps,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const { children, ...innerBlocksProps } = useInnerBlocksProps(blockProps, {
|
|
64
|
+
allowedBlocks,
|
|
65
|
+
template,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Preview image for block inserter
|
|
69
|
+
if (attributes.coverImage) {
|
|
70
|
+
return <img src={attributes.coverImage} width="468" />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
{...innerBlocksProps}
|
|
76
|
+
className={`${innerBlocksProps.className} slider-content wbtr:relative wbtr:border wbtr:border-editor-border wbtr:p-2 wbtr:pt-4`}
|
|
77
|
+
>
|
|
78
|
+
<div className="wbtr:absolute wbtr:top-[2px] wbtr:left-2 wbtr:mb-1 wbtr:text-10 wbtr:opacity-50">
|
|
79
|
+
{__('Slider', 'webentor')}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{children}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save
|
|
89
|
+
*
|
|
90
|
+
* @return {null} Dynamic blocks do not save the HTML.
|
|
91
|
+
*/
|
|
92
|
+
const BlockSave = () => <InnerBlocks.Content />;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Register block.
|
|
96
|
+
*/
|
|
97
|
+
registerBlockType(block, {
|
|
98
|
+
edit: BlockEdit,
|
|
99
|
+
save: BlockSave,
|
|
100
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/webentor-block.json",
|
|
3
|
+
"apiVersion": 3,
|
|
4
|
+
"name": "webentor/e-svg",
|
|
5
|
+
"title": "Svg",
|
|
6
|
+
"description": "Svg block.",
|
|
7
|
+
"category": "webentor-elements",
|
|
8
|
+
"icon": "art",
|
|
9
|
+
"keywords": ["svg"],
|
|
10
|
+
"example": {
|
|
11
|
+
"attributes": {
|
|
12
|
+
"mode": "preview",
|
|
13
|
+
"data": {
|
|
14
|
+
"preview_image_help": true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"attributes": {
|
|
19
|
+
"imgId": {
|
|
20
|
+
"type": "number",
|
|
21
|
+
"default": 0
|
|
22
|
+
},
|
|
23
|
+
"width": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"default": "0"
|
|
26
|
+
},
|
|
27
|
+
"height": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"default": "0"
|
|
30
|
+
},
|
|
31
|
+
"link": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"default": {}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"supports": {}
|
|
37
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Image, Link, MediaToolbar } from '@10up/block-components';
|
|
2
|
+
import {
|
|
3
|
+
BlockControls,
|
|
4
|
+
InspectorControls,
|
|
5
|
+
useBlockProps,
|
|
6
|
+
} from '@wordpress/block-editor';
|
|
7
|
+
import { BlockEditProps, registerBlockType } from '@wordpress/blocks';
|
|
8
|
+
import {
|
|
9
|
+
__experimentalNumberControl as NumberControl,
|
|
10
|
+
PanelBody,
|
|
11
|
+
PanelRow,
|
|
12
|
+
} from '@wordpress/components';
|
|
13
|
+
import { __ } from '@wordpress/i18n';
|
|
14
|
+
|
|
15
|
+
import block from './block.json';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Edit component.
|
|
19
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit
|
|
20
|
+
*
|
|
21
|
+
* @param {object} props The block props.
|
|
22
|
+
* @returns {Function} Render the edit screen
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
type AttributesType = {
|
|
26
|
+
coverImage: string;
|
|
27
|
+
link: {
|
|
28
|
+
title: string;
|
|
29
|
+
url: string;
|
|
30
|
+
opensInNewTab: boolean;
|
|
31
|
+
};
|
|
32
|
+
imgId: number;
|
|
33
|
+
width: string;
|
|
34
|
+
height: string;
|
|
35
|
+
imageSize: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const BlockEdit: React.FC<BlockEditProps<AttributesType>> = (props) => {
|
|
39
|
+
const { attributes, setAttributes } = props;
|
|
40
|
+
|
|
41
|
+
const blockProps = useBlockProps();
|
|
42
|
+
|
|
43
|
+
const imageWidth = 40;
|
|
44
|
+
const imageHeight = 40;
|
|
45
|
+
|
|
46
|
+
// Preview image for block inserter
|
|
47
|
+
if (attributes.coverImage) {
|
|
48
|
+
return <img src={attributes.coverImage} width="468" />;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const handleLinkTextChange = (value) =>
|
|
52
|
+
setAttributes({
|
|
53
|
+
link: {
|
|
54
|
+
...attributes.link,
|
|
55
|
+
title: value,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const handleLinkChange = (value) =>
|
|
60
|
+
setAttributes({
|
|
61
|
+
link: {
|
|
62
|
+
url: value?.url,
|
|
63
|
+
opensInNewTab: value?.opensInNewTab,
|
|
64
|
+
title: value?.title ?? attributes?.link?.title,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const handleLinkRemove = () => {
|
|
69
|
+
setAttributes({ link: null });
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleImageSelect = (image) => {
|
|
73
|
+
setAttributes({ imgId: image.id });
|
|
74
|
+
};
|
|
75
|
+
const handleImageRemove = () => {
|
|
76
|
+
setAttributes({ imgId: null });
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<InspectorControls>
|
|
82
|
+
<PanelBody title="Block Settings" initialOpen={true}>
|
|
83
|
+
<PanelRow>
|
|
84
|
+
{/* External link */}
|
|
85
|
+
<Link
|
|
86
|
+
value={attributes?.link?.title}
|
|
87
|
+
url={attributes?.link?.url}
|
|
88
|
+
opensInNewTab={attributes?.link?.opensInNewTab}
|
|
89
|
+
onTextChange={handleLinkTextChange}
|
|
90
|
+
onLinkChange={handleLinkChange}
|
|
91
|
+
onLinkRemove={handleLinkRemove}
|
|
92
|
+
placeholder="Enter Link Text here..."
|
|
93
|
+
/>
|
|
94
|
+
</PanelRow>
|
|
95
|
+
<PanelRow>
|
|
96
|
+
{/* Custom image size with with, heigh and crop */}
|
|
97
|
+
<div className="wbtr:flex wbtr:gap-2">
|
|
98
|
+
<NumberControl
|
|
99
|
+
label={__('Width', 'webentor')}
|
|
100
|
+
value={attributes?.width || ''}
|
|
101
|
+
onChange={(width) => setAttributes({ width })}
|
|
102
|
+
required
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<NumberControl
|
|
106
|
+
label={__('Height', 'webentor')}
|
|
107
|
+
value={attributes?.height || ''}
|
|
108
|
+
onChange={(height) => setAttributes({ height })}
|
|
109
|
+
required
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
</PanelRow>
|
|
113
|
+
</PanelBody>
|
|
114
|
+
</InspectorControls>
|
|
115
|
+
|
|
116
|
+
<BlockControls>
|
|
117
|
+
<MediaToolbar
|
|
118
|
+
isOptional
|
|
119
|
+
id={attributes.imgId}
|
|
120
|
+
onSelect={handleImageSelect}
|
|
121
|
+
onRemove={handleImageRemove}
|
|
122
|
+
/>
|
|
123
|
+
</BlockControls>
|
|
124
|
+
|
|
125
|
+
<div {...blockProps} className={blockProps.className}>
|
|
126
|
+
<Image
|
|
127
|
+
id={attributes.imgId}
|
|
128
|
+
size={attributes.imageSize}
|
|
129
|
+
onSelect={handleImageSelect}
|
|
130
|
+
allowedTypes={['image/svg+xml']}
|
|
131
|
+
labels={{
|
|
132
|
+
title: 'Select Image',
|
|
133
|
+
instructions: 'Upload or pick one from your media library.',
|
|
134
|
+
}}
|
|
135
|
+
width={attributes?.width || imageWidth}
|
|
136
|
+
height={attributes?.height || imageHeight}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save
|
|
145
|
+
*
|
|
146
|
+
* @return {null} Dynamic blocks do not save the HTML.
|
|
147
|
+
*/
|
|
148
|
+
const BlockSave = () => null;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Register block.
|
|
152
|
+
*/
|
|
153
|
+
registerBlockType(block, {
|
|
154
|
+
edit: BlockEdit,
|
|
155
|
+
save: BlockSave,
|
|
156
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/webentor-block.json",
|
|
3
|
+
"apiVersion": 3,
|
|
4
|
+
"name": "webentor/e-tab-container",
|
|
5
|
+
"title": "Single Tab",
|
|
6
|
+
"description": "Tab used in Tabs block.",
|
|
7
|
+
"category": "webentor-elements",
|
|
8
|
+
"icon": "table-row-after",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"tabs",
|
|
11
|
+
"container",
|
|
12
|
+
"elements"
|
|
13
|
+
],
|
|
14
|
+
"attributes": {
|
|
15
|
+
"title": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"default": ""
|
|
18
|
+
},
|
|
19
|
+
"hideTitle": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": false
|
|
22
|
+
},
|
|
23
|
+
"template": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"default": null
|
|
26
|
+
},
|
|
27
|
+
"spacing": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"default": {
|
|
30
|
+
"padding-top": {
|
|
31
|
+
"value": {
|
|
32
|
+
"basic": "pt-5",
|
|
33
|
+
"md": "pt-8"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"padding-bottom": {
|
|
37
|
+
"value": {
|
|
38
|
+
"basic": "pb-4"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"supports": {
|
|
45
|
+
"webentor": {
|
|
46
|
+
"display": true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InnerBlocks,
|
|
3
|
+
InspectorControls,
|
|
4
|
+
RichText,
|
|
5
|
+
useBlockProps,
|
|
6
|
+
useInnerBlocksProps,
|
|
7
|
+
} from '@wordpress/block-editor';
|
|
8
|
+
import {
|
|
9
|
+
BlockEditProps,
|
|
10
|
+
registerBlockType,
|
|
11
|
+
TemplateArray,
|
|
12
|
+
} from '@wordpress/blocks';
|
|
13
|
+
import { PanelBody, PanelRow, ToggleControl } from '@wordpress/components';
|
|
14
|
+
import { applyFilters } from '@wordpress/hooks';
|
|
15
|
+
import { __ } from '@wordpress/i18n';
|
|
16
|
+
|
|
17
|
+
import { useBlockParent } from '@webentorCore/blocks-utils/_use-block-parent';
|
|
18
|
+
|
|
19
|
+
import block from './block.json';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Edit component.
|
|
23
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#edit
|
|
24
|
+
*
|
|
25
|
+
* @param {object} props The block props.
|
|
26
|
+
* @returns {Function} Render the edit screen
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
type AttributesType = {
|
|
30
|
+
coverImage: string;
|
|
31
|
+
hideTitle: boolean;
|
|
32
|
+
title: string;
|
|
33
|
+
template?: TemplateArray;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const BlockEdit: React.FC<BlockEditProps<AttributesType>> = (props) => {
|
|
37
|
+
const { attributes, setAttributes } = props;
|
|
38
|
+
|
|
39
|
+
const blockProps = useBlockProps();
|
|
40
|
+
const parentBlockProps = useBlockParent();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Filter allowed blocks used in webentor/e-tab-container inner block
|
|
44
|
+
*/
|
|
45
|
+
const allowedBlocks: string[] = applyFilters(
|
|
46
|
+
'webentor.core.e-tab-container.allowedBlocks',
|
|
47
|
+
null,
|
|
48
|
+
blockProps,
|
|
49
|
+
parentBlockProps,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Filter template used in webentor/e-tab-container inner block
|
|
54
|
+
*/
|
|
55
|
+
const defaultTemplate: TemplateArray = attributes?.template;
|
|
56
|
+
const template: TemplateArray = applyFilters(
|
|
57
|
+
'webentor.core.e-tab-container.template',
|
|
58
|
+
defaultTemplate,
|
|
59
|
+
blockProps,
|
|
60
|
+
parentBlockProps,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const innerBlocksProps = useInnerBlocksProps(blockProps, {
|
|
64
|
+
allowedBlocks,
|
|
65
|
+
template,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Preview image for block inserter
|
|
69
|
+
if (attributes.coverImage) {
|
|
70
|
+
return <img src={attributes.coverImage} width="468" />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<InspectorControls>
|
|
76
|
+
<PanelBody title="Block Settings" initialOpen={true}>
|
|
77
|
+
<PanelRow>
|
|
78
|
+
<ToggleControl
|
|
79
|
+
label={__('Hide title', 'webentor')}
|
|
80
|
+
help={__(
|
|
81
|
+
'Title would be used only in Tabs navigation, but hidden in the content area.',
|
|
82
|
+
'webentor',
|
|
83
|
+
)}
|
|
84
|
+
checked={attributes.hideTitle}
|
|
85
|
+
onChange={(hideTitle) => setAttributes({ hideTitle })}
|
|
86
|
+
/>
|
|
87
|
+
</PanelRow>
|
|
88
|
+
</PanelBody>
|
|
89
|
+
</InspectorControls>
|
|
90
|
+
|
|
91
|
+
<div {...blockProps} className={`${blockProps.className}`}>
|
|
92
|
+
<div className="md:wbtr:pt-8 wbtr:flex wbtr:w-full wbtr:flex-col wbtr:border wbtr:border-editor-border wbtr:px-4 wbtr:pt-5 wbtr:pb-4">
|
|
93
|
+
<RichText
|
|
94
|
+
tagName="h2"
|
|
95
|
+
placeholder={__('Tab Title (required)', 'webentor')}
|
|
96
|
+
value={attributes.title}
|
|
97
|
+
onChange={(title) => setAttributes({ title })}
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<div
|
|
101
|
+
{...innerBlocksProps}
|
|
102
|
+
className={`${innerBlocksProps.className} wbtr:border wbtr:border-editor-border wbtr:p-4`}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* See https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-edit-save/#save
|
|
112
|
+
*
|
|
113
|
+
* @return {null} Dynamic blocks do not save the HTML.
|
|
114
|
+
*/
|
|
115
|
+
const BlockSave = () => <InnerBlocks.Content />;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Register block.
|
|
119
|
+
*/
|
|
120
|
+
registerBlockType(block, {
|
|
121
|
+
edit: BlockEdit,
|
|
122
|
+
save: BlockSave,
|
|
123
|
+
});
|