@plaudit/gutenberg-api-extensions 1.1.0 → 1.3.0

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.
@@ -1,15 +1,31 @@
1
- export interface LayeredBlockStyleLayer {
1
+ import type { BlockIcon } from "@wordpress/blocks";
2
+ import type { PickableOptions } from "../controls/types";
3
+ type BaseLayeredBlockStyleLayer = {
2
4
  name: string;
3
5
  title: string;
4
6
  default?: string | undefined;
5
- options: Array<{
6
- name: string;
7
- label: string;
7
+ };
8
+ type LayeredBlockStyleColorLayer = BaseLayeredBlockStyleLayer & {
9
+ picker: 'color';
10
+ options: PickableOptions<{
11
+ color?: string;
8
12
  }>;
9
- }
13
+ };
14
+ type LayeredBlockStyleToggleLayer = BaseLayeredBlockStyleLayer & {
15
+ picker?: 'toggle-control';
16
+ options: PickableOptions<{
17
+ icon: BlockIcon;
18
+ }>;
19
+ };
20
+ type LayeredBlockStyleSelectOrRadioLayer = BaseLayeredBlockStyleLayer & {
21
+ picker: 'select' | 'radio';
22
+ options: PickableOptions;
23
+ };
24
+ export type LayeredBlockStyleLayer = LayeredBlockStyleColorLayer | LayeredBlockStyleToggleLayer | LayeredBlockStyleSelectOrRadioLayer;
10
25
  export interface LayeredBlockStylesConfig {
11
26
  block: `${string}/${string}`;
12
27
  layers: LayeredBlockStyleLayer[];
13
28
  }
14
29
  export declare function registerLayeredBlockStyles(config: LayeredBlockStylesConfig): void;
15
30
  export declare function installLayeredBlockStylesSupport(): void;
31
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { createHigherOrderComponent } from "@wordpress/compose";
2
2
  import { addFilter } from "@wordpress/hooks";
3
3
  import React from "react";
4
- import { InspectorPanel, PickOne } from "../controls";
4
+ import { InspectorPanel, PickOneFromColors, PickOneFromRadios, PickOneFromSelect, PickOneFromToggleGroup } from "../controls";
5
5
  export function registerLayeredBlockStyles(config) {
6
6
  var _a;
7
7
  const layeredBlockStyles = (_a = window.plauditLayeredBlockStyles) !== null && _a !== void 0 ? _a : (window.plauditLayeredBlockStyles = new Map());
@@ -14,50 +14,52 @@ export function installLayeredBlockStylesSupport() {
14
14
  }
15
15
  window.plauditLayeredBlockStylesInstalled = true;
16
16
  const layeredBlockStyles = (_a = window.plauditLayeredBlockStyles) !== null && _a !== void 0 ? _a : (window.plauditLayeredBlockStyles = new Map());
17
- function convertLayersToRadios(layer, props) {
18
- const options = {};
19
- layer.options.forEach(({ name, label }) => options[name] = label);
20
- return React.createElement(PickOne, Object.assign({}, props, { attribute: `layeredStyles__${layer.name}`, options: options, layout: 'toggle-group', label: layer.title }));
17
+ function layeredStyleValueGetter(layer, block) {
18
+ var _a, _b, _c, _d;
19
+ const currentLayerClassPrefix = `style-${layer.name}-`;
20
+ return (_d = (_c = (_b = (_a = block.attributes['className']) === null || _a === void 0 ? void 0 : _a.split(/\s+/).filter(cn => cn.startsWith(currentLayerClassPrefix))[0]) === null || _b === void 0 ? void 0 : _b.substring(currentLayerClassPrefix.length)) !== null && _c !== void 0 ? _c : layer.default) !== null && _d !== void 0 ? _d : '';
21
21
  }
22
- addFilter('blocks.registerBlockType', 'plaudit/gutenberg-api-extensions/layered-styles', (atts) => {
23
- var _a;
24
- if (layeredBlockStyles.has(atts.name)) {
25
- (_a = layeredBlockStyles.get(atts.name)) === null || _a === void 0 ? void 0 : _a.layers.forEach(layer => {
26
- const attName = `layeredStyles__${layer.name}`;
27
- if (!atts.attributes[attName]) {
28
- atts.attributes[attName] = { type: 'string', source: 'attribute', attribute: `data-layered-style-${layer.name}` };
29
- }
30
- });
22
+ function layeredStyleValueSetter(attrs, layers, block) {
23
+ const [key, value] = Object.entries(attrs)[0];
24
+ if (!key.startsWith("layeredStyles__")) {
25
+ return;
31
26
  }
32
- return atts;
33
- });
34
- addFilter('blocks.getBlockAttributes', 'plaudit/gutenberg-api-extensions/layered-styles', (blockAttributes, blockType, innerHTML) => {
35
- var _a;
36
- if (layeredBlockStyles.has(blockType.name)) {
37
- const blockRoot = new DOMParser().parseFromString(innerHTML, "application/xml").documentElement;
38
- (_a = layeredBlockStyles.get(blockType.name)) === null || _a === void 0 ? void 0 : _a.layers.forEach(layer => {
39
- var _a;
40
- blockAttributes[`layeredStyles__${layer.name}`] = (_a = blockRoot.getAttribute(`data-layered-style-${layer.name}`)) !== null && _a !== void 0 ? _a : layer.default;
41
- });
42
- }
43
- return blockAttributes;
44
- });
45
- addFilter('blocks.getSaveContent.extraProps', 'plaudit/gutenberg-api-extensions/layered-styles', (props, blockType, attributes) => {
46
- if (!layeredBlockStyles.has(blockType.name)) {
47
- return props;
27
+ const layerName = key.substring(15);
28
+ const currentLayerClassPrefix = `style-${layerName}-`;
29
+ const classNames = block.attributes['className'] ? block.attributes['className'].split(/\s+/).filter(cn => !cn.startsWith(currentLayerClassPrefix)) : [];
30
+ const layerClassPrefixes = layers.map(layer => `style-${layer.name}-`);
31
+ const layerClasses = classNames.filter(cn => layerClassPrefixes.some(lcp => cn.startsWith(lcp)));
32
+ const nonLayerClasses = classNames.filter(cn => !layerClasses.includes(cn));
33
+ layerClasses.push(`${currentLayerClassPrefix}${value}`);
34
+ layerClasses.sort();
35
+ block.setAttributes({ className: [...nonLayerClasses, ...layerClasses].join(' ') });
36
+ }
37
+ function convertLayersToRadios(layer, layers, props) {
38
+ const value = layeredStyleValueGetter(layer, props);
39
+ const sharedProps = {
40
+ label: props.label,
41
+ attribute: `layeredStyles__${layer.name}`,
42
+ attributes: { [`layeredStyles__${layer.name}`]: value },
43
+ setAttributes: (attrs) => layeredStyleValueSetter(attrs, layers, props)
44
+ };
45
+ switch (layer.picker) {
46
+ case undefined:
47
+ case 'toggle-control':
48
+ return React.createElement(PickOneFromToggleGroup, Object.assign({}, layer, sharedProps));
49
+ case 'color':
50
+ return React.createElement(PickOneFromColors, Object.assign({}, layer, sharedProps));
51
+ case 'radio':
52
+ return React.createElement(PickOneFromRadios, Object.assign({}, layer, sharedProps));
53
+ case 'select':
54
+ return React.createElement(PickOneFromSelect, Object.assign({}, layer, sharedProps));
48
55
  }
49
- const res = Object.assign({}, props);
50
- layeredBlockStyles.get(blockType.name).layers.forEach(layer => {
51
- res[`data-layered-style-${layer.name}`] = attributes[`layeredStyles__${layer.name}`];
52
- });
53
- return res;
54
- });
55
- addFilter('editor.BlockEdit', 'plaudit/gutenberg-api-extensions/layered-styles', createHigherOrderComponent((BlockEdit) => (props) => {
56
+ }
57
+ addFilter('editor.BlockEdit', 'plaudit/gutenberg-api-extensions/layered-styles', createHigherOrderComponent(BlockEdit => (props) => {
56
58
  if (!layeredBlockStyles.has(props['name'])) {
57
59
  return React.createElement(BlockEdit, Object.assign({}, props));
58
60
  }
59
61
  return React.createElement(React.Fragment, null,
60
62
  React.createElement(BlockEdit, Object.assign({}, props)),
61
- React.createElement(InspectorPanel, { title: "Layered Styles", initialOpen: true }, ...layeredBlockStyles.get(props['name']).layers.map(layer => convertLayersToRadios(layer, props))));
63
+ React.createElement(InspectorPanel, { title: "Layered Styles", initialOpen: true }, ...layeredBlockStyles.get(props['name']).layers.map(layer => convertLayersToRadios(layer, layeredBlockStyles.get(props['name']).layers, props))));
62
64
  }, 'plauditGutenbergApiExtensionsLayeredStyles'));
63
65
  }
@@ -1,6 +1,19 @@
1
1
  import type { PickableOptions, SimpleBlockControlProps } from "./types";
2
2
  import React from "react";
3
- export declare function PickOne<T extends string>(props: SimpleBlockControlProps<T, string> & {
3
+ import { BlockIcon } from "@wordpress/blocks";
4
+ export declare function PickOneFromToggleGroup<T extends string>(props: SimpleBlockControlProps<T, string> & {
5
+ options: PickableOptions<{
6
+ icon: BlockIcon;
7
+ }>;
8
+ }): React.JSX.Element;
9
+ export declare function PickOneFromSelect<T extends string>(props: SimpleBlockControlProps<T, string> & {
10
+ options: PickableOptions;
11
+ }): React.JSX.Element;
12
+ export declare function PickOneFromRadios<T extends string>(props: SimpleBlockControlProps<T, string> & {
4
13
  options: PickableOptions;
5
- layout: 'toggle-group' | 'dropdown' | 'radio';
14
+ }): React.JSX.Element;
15
+ export declare function PickOneFromColors<T extends string>(props: SimpleBlockControlProps<T, string> & {
16
+ options: PickableOptions<{
17
+ color?: string | undefined;
18
+ }>;
6
19
  }): React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import { __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption, __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, RadioControl, SelectControl } from "@wordpress/components";
1
+ import { __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption, __experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, ColorPalette, RadioControl, SelectControl } from "@wordpress/components";
2
2
  import React from "react";
3
3
  function makeSharedRadioAndSelectProps(props) {
4
4
  return {
@@ -9,17 +9,38 @@ function makeSharedRadioAndSelectProps(props) {
9
9
  }))
10
10
  };
11
11
  }
12
- export function PickOne(props) {
13
- switch (props.layout) {
14
- case 'toggle-group':
15
- return React.createElement(ToggleGroupControl, { label: props.label, value: props.attributes[props.attribute], onChange: value => props.setAttributes({ [props.attribute]: value }), children: Object.entries(props.options).map(([value, label]) => typeof label === 'string'
16
- ? React.createElement(ToggleGroupControlOption, { key: value, value: value, label: label })
17
- : React.createElement(ToggleGroupControlOptionIcon, { key: value, value: value, label: label.text, icon: label.icon })) });
18
- case 'dropdown':
19
- return React.createElement(SelectControl, Object.assign({}, makeSharedRadioAndSelectProps(props), { value: props.attributes[props.attribute] }));
20
- case 'radio':
21
- return React.createElement(RadioControl, Object.assign({}, makeSharedRadioAndSelectProps(props), { selected: props.attributes[props.attribute] }));
22
- default:
23
- throw new Error(`Unsupported layout: ${props.layout}`);
12
+ export function PickOneFromToggleGroup(props) {
13
+ return React.createElement(ToggleGroupControl, { label: props.label, value: props.attributes[props.attribute], onChange: value => props.setAttributes({ [props.attribute]: value }), children: Object.entries(props.options).map(([value, label]) => typeof label === 'string'
14
+ ? React.createElement(ToggleGroupControlOption, { key: value, value: value, label: label })
15
+ : React.createElement(ToggleGroupControlOptionIcon, { key: value, value: value, label: label.text, icon: label.icon })) });
16
+ }
17
+ export function PickOneFromSelect(props) {
18
+ return React.createElement(SelectControl, Object.assign({}, makeSharedRadioAndSelectProps(props), { value: props.attributes[props.attribute] }));
19
+ }
20
+ export function PickOneFromRadios(props) {
21
+ return React.createElement(RadioControl, Object.assign({}, makeSharedRadioAndSelectProps(props), { selected: props.attributes[props.attribute] }));
22
+ }
23
+ export function PickOneFromColors(props) {
24
+ var _a;
25
+ const valueToColorMap = new Map();
26
+ const colorToValueMap = new Map();
27
+ const colors = [];
28
+ for (const [value, display] of Object.entries(props.options)) {
29
+ if (typeof display === 'string') {
30
+ colors.push({ color: value, name: display });
31
+ }
32
+ else if (display.color) {
33
+ valueToColorMap.set(value, display.color);
34
+ colorToValueMap.set(display.color, value);
35
+ colors.push({ color: display.color, name: display.text });
36
+ }
37
+ else {
38
+ colors.push({ color: value, name: display.text });
39
+ }
24
40
  }
41
+ const currentColor = (_a = valueToColorMap.get(props.attributes[props.attribute])) !== null && _a !== void 0 ? _a : props.attributes[props.attribute];
42
+ const id = `layeredStyles__colorPicker--${Math.random().toString().replace(/[.\-]/, '')}`;
43
+ return React.createElement(React.Fragment, null,
44
+ React.createElement("label", { htmlFor: id }, props.label),
45
+ React.createElement(ColorPalette, { id: id, disableCustomColors: true, onChange: color => { var _a, _b; return props.setAttributes({ [props.attribute]: (_b = (_a = colorToValueMap.get(color !== null && color !== void 0 ? color : currentColor)) !== null && _a !== void 0 ? _a : color) !== null && _b !== void 0 ? _b : currentColor }); }, colors: colors, value: currentColor, clearable: false }));
25
46
  }
@@ -1,11 +1,12 @@
1
- import type { BlockEditProps, BlockIcon } from "@wordpress/blocks";
2
- export type SimpleBlockControlProps<T extends string, V> = {
3
- label: string;
1
+ import type { BlockEditProps } from "@wordpress/blocks";
2
+ export type SpecificValueBlockProps<T extends string, V> = {
4
3
  attribute: T;
5
4
  attributes: BlockEditProps<Record<T, V> & Record<string, unknown>>['attributes'];
6
5
  setAttributes: BlockEditProps<any>['setAttributes'];
7
6
  };
8
- export type PickableOptions = Record<string, string | {
7
+ export type SimpleBlockControlProps<T extends string, V> = SpecificValueBlockProps<T, V> & {
8
+ label: string;
9
+ };
10
+ export type PickableOptions<T extends object = {}> = Record<string, string | ({
9
11
  text: string;
10
- icon: BlockIcon;
11
- }>;
12
+ } & T)>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaudit/gutenberg-api-extensions",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "scripts": {
5
5
  "prepublishOnly": "rm -rf build && mkdir build && tsc",
6
6
  "build": "tsc",