@plaudit/gutenberg-api-extensions 2.95.0 → 2.96.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.
- package/CHANGELOG.md +8 -0
- package/dist/lib/useful-types.d.ts +1 -4
- package/package.json +1 -2
- package/src/blocks/MoveError.ts +0 -7
- package/src/blocks/PathError.ts +0 -18
- package/src/blocks/SNPFlexibleItemsListComponent.tsx +0 -30
- package/src/blocks/SNPGroupComponent.tsx +0 -38
- package/src/blocks/SNPListComponent.tsx +0 -25
- package/src/blocks/SNPTreeContext.tsx +0 -13
- package/src/blocks/basic-custom-block-bindings-support.tsx +0 -248
- package/src/blocks/common-native-property-constructors.tsx +0 -927
- package/src/blocks/conditions.ts +0 -261
- package/src/blocks/csnp-api.ts +0 -221
- package/src/blocks/data-controller/actions.ts +0 -20
- package/src/blocks/data-controller/reducer.ts +0 -146
- package/src/blocks/data-controller/trigger-handlers.ts +0 -150
- package/src/blocks/data-controller/utils.ts +0 -415
- package/src/blocks/data-controller-manager.ts +0 -50
- package/src/blocks/data-controller.ts +0 -165
- package/src/blocks/hooks/built-in-suspendable-option-protocols/select.ts +0 -51
- package/src/blocks/hooks/built-in-suspendable-option-protocols/settings.ts +0 -70
- package/src/blocks/hooks/useSuspendableOptions.ts +0 -122
- package/src/blocks/index.ts +0 -23
- package/src/blocks/layered-styles-api.ts +0 -142
- package/src/blocks/layered-styles-impl.ts +0 -95
- package/src/blocks/layout/LaidOutProperty.tsx +0 -72
- package/src/blocks/layout/LaidOutPropertyRow.tsx +0 -28
- package/src/blocks/layout/NodeContext.tsx +0 -54
- package/src/blocks/layout/PanelRoot.tsx +0 -30
- package/src/blocks/layout/TabsRoot.tsx +0 -56
- package/src/blocks/layout/ToolsPanelContext.tsx +0 -22
- package/src/blocks/problematic-blocks-blocker.ts +0 -24
- package/src/blocks/problematic-variations-blocker.ts +0 -32
- package/src/blocks/shared-exportable-types.ts +0 -6
- package/src/blocks/shared-internal-types.ts +0 -18
- package/src/blocks/simple-block.tsx +0 -74
- package/src/blocks/simple-native-property-api.ts +0 -173
- package/src/blocks/simple-native-property-impl.tsx +0 -335
- package/src/blocks/simple-native-property-internal-shared.ts +0 -19
- package/src/blocks/snp-api.ts +0 -5
- package/src/blocks/snp-data-store.ts +0 -72
- package/src/blocks/utilities.ts +0 -66
- package/src/controls/AsynchronousFormTokenField.tsx +0 -86
- package/src/controls/BaseSortableItemsControl.tsx +0 -84
- package/src/controls/ExtendedFormTokenField.tsx +0 -144
- package/src/controls/ExtendedPostPicker.ts +0 -57
- package/src/controls/ExtendedRadioControl.tsx +0 -107
- package/src/controls/ExtendedTaxonomyPicker.tsx +0 -100
- package/src/controls/ExtendedTermPicker.tsx +0 -61
- package/src/controls/ExtendedTextareaControl.tsx +0 -65
- package/src/controls/ExtendedUserPicker.ts +0 -56
- package/src/controls/FileControl.tsx +0 -48
- package/src/controls/FullSizeToggleControl.tsx +0 -95
- package/src/controls/ImageControl.tsx +0 -143
- package/src/controls/InspectorPanel.tsx +0 -37
- package/src/controls/LazySuggestionsComboboxControl.tsx +0 -64
- package/src/controls/MultiSelectControl.tsx +0 -59
- package/src/controls/PickOne.tsx +0 -88
- package/src/controls/PromisableComponent.tsx +0 -56
- package/src/controls/ProperLinkControl.tsx +0 -98
- package/src/controls/SimpleToggle.tsx +0 -9
- package/src/controls/SortableFlexibleItemsControl.tsx +0 -37
- package/src/controls/SortableItemsControl.tsx +0 -22
- package/src/controls/basicNumericallyIdedItemPicker.tsx +0 -75
- package/src/controls/hooks/useImprovedTokenManager.ts +0 -163
- package/src/controls/hooks/useMultiSingleConversionLayer.ts +0 -17
- package/src/controls/hooks/useNonRenderingCounter.ts +0 -6
- package/src/controls/hooks/useOutputMemoizingFilter.ts +0 -16
- package/src/controls/hooks/useSortableItemsModel.ts +0 -196
- package/src/controls/hooks/useSuggestions.ts +0 -91
- package/src/controls/hooks/useTokenManager.ts +0 -177
- package/src/controls/index.ts +0 -24
- package/src/controls/shared.ts +0 -50
- package/src/controls/types.ts +0 -18
- package/src/editor/insert-sibling-or-child-block-shortcut.tsx +0 -60
- package/src/editor/install-insert-sole-allowed-block-shortcut-support.tsx +0 -51
- package/src/editor/simple-gutenberg-endpoints-api.ts +0 -31
- package/src/editor/simple-gutenberg-endpoints-impl.ts +0 -126
- package/src/index.ts +0 -30
- package/src/lib/compat-types.ts +0 -21
- package/src/lib/gutenberg-api-extensions-state/custom-block-bindings-support-logic.ts +0 -35
- package/src/lib/gutenberg-api-extensions-state/general-logic.ts +0 -41
- package/src/lib/gutenberg-api-extensions-state/layered-block-styles-logic.ts +0 -43
- package/src/lib/gutenberg-api-extensions-state/snp-logic.ts +0 -240
- package/src/lib/gutenberg-api-extensions-state.ts +0 -69
- package/src/lib/helpers.ts +0 -115
- package/src/lib/modified-fast-deep-equals.ts +0 -91
- package/src/lib/plaudit-icons/column-1.tsx +0 -6
- package/src/lib/plaudit-icons/column-2.tsx +0 -6
- package/src/lib/plaudit-icons/column-3.tsx +0 -6
- package/src/lib/plaudit-icons/placement-center.tsx +0 -3
- package/src/lib/plaudit-icons/placement-end.tsx +0 -3
- package/src/lib/plaudit-icons/placement-start.tsx +0 -3
- package/src/lib/plaudit-icons/placement-stretch.tsx +0 -3
- package/src/lib/plaudit-icons/plaudit-icon.tsx +0 -4
- package/src/lib/plaudit-icons/reusable-block-marker.tsx +0 -3
- package/src/lib/plaudit-icons.ts +0 -13
- package/src/lib/sectioned-cache-store.ts +0 -120
- package/src/lib/suspense/promise-handlers.ts +0 -72
- package/src/lib/suspense.tsx +0 -18
- package/src/lib/useful-types.ts +0 -82
- package/src/schemas/README.md +0 -1
- package/src/schemas/plaudit-block-schema.json +0 -818
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption,
|
|
3
|
-
__experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon
|
|
4
|
-
} from "@wordpress/components";
|
|
5
|
-
import {memo, useCallback, useMemo} from "@wordpress/element";
|
|
6
|
-
import {__} from "@wordpress/i18n";
|
|
7
|
-
import * as icons from "@wordpress/icons";
|
|
8
|
-
|
|
9
|
-
import type {IconName, OptionalLabel} from "../blocks";
|
|
10
|
-
import type {TogglePropertyCSNPConfig} from "../blocks/csnp-api";
|
|
11
|
-
import type {CSNPControlComponentProps, NormalSwitch, PlauditIconName, WordPressIconName} from "../blocks/shared-internal-types";
|
|
12
|
-
import * as plauditIcons from "../lib/plaudit-icons";
|
|
13
|
-
|
|
14
|
-
import parse from "html-react-parser";
|
|
15
|
-
|
|
16
|
-
import type {ComponentPropsWithoutRef, ReactElement} from "react";
|
|
17
|
-
|
|
18
|
-
type ToggleGroupControlProps = ComponentPropsWithoutRef<typeof ToggleGroupControl>;
|
|
19
|
-
export type FullSizeToggleControlProps = Omit<CSNPControlComponentProps<TogglePropertyCSNPConfig, boolean, {switchCfg: NormalSwitch}>, 'config'>&{
|
|
20
|
-
componentConfig?: Partial<ToggleGroupControlProps>,
|
|
21
|
-
label: ToggleGroupControlProps['label'],
|
|
22
|
-
help?: ToggleGroupControlProps['help']
|
|
23
|
-
};
|
|
24
|
-
export function FullSizeToggleControl({componentConfig, label, help, onChange, switchCfg, value}: FullSizeToggleControlProps) {
|
|
25
|
-
const [switchLabels, style] = useMemo(() => {
|
|
26
|
-
let switchLabels: {onLabel: NonNullable<OptionalLabel>, offLabel: NonNullable<OptionalLabel>};
|
|
27
|
-
if (typeof switchCfg === 'string') {
|
|
28
|
-
switchCfg = {type: switchCfg};
|
|
29
|
-
}
|
|
30
|
-
switch (switchCfg.type) {
|
|
31
|
-
case "showHide":
|
|
32
|
-
switchLabels = {onLabel: "Show", offLabel: "Hide"};
|
|
33
|
-
break;
|
|
34
|
-
case "yesNo":
|
|
35
|
-
switchLabels = {onLabel: "Yes", offLabel: "No"};
|
|
36
|
-
break;
|
|
37
|
-
default:
|
|
38
|
-
if (switchCfg.type !== undefined) {
|
|
39
|
-
console.error("Invalid switch type:", switchCfg.type);
|
|
40
|
-
}
|
|
41
|
-
switchLabels = {onLabel: "On", offLabel: "Off"};
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
if (switchCfg.onLabel) {
|
|
45
|
-
switchLabels.onLabel = switchCfg.onLabel;
|
|
46
|
-
}
|
|
47
|
-
if (switchCfg.offLabel) {
|
|
48
|
-
switchLabels.offLabel = switchCfg.offLabel;
|
|
49
|
-
}
|
|
50
|
-
return [{onLabel: i18nSwitchLabel(switchLabels.onLabel), offLabel: i18nSwitchLabel(switchLabels.offLabel)}, {width: switchCfg.narrow ? "min-content" : undefined}];
|
|
51
|
-
}, [switchCfg]);
|
|
52
|
-
|
|
53
|
-
const safeOnChange = useCallback((v: string|number|undefined) => onChange(v === "true"), [onChange]);
|
|
54
|
-
|
|
55
|
-
return <ToggleGroupControl
|
|
56
|
-
{...componentConfig}
|
|
57
|
-
value={value ? "true" : "false"} label={label} help={help} isBlock
|
|
58
|
-
onChange={safeOnChange} style={style} __next40pxDefaultSize __nextHasNoMarginBottom
|
|
59
|
-
>
|
|
60
|
-
<ToggleGroupControlOptionWithOptionalIcon key="true" value="true" label={switchLabels.onLabel} />
|
|
61
|
-
<ToggleGroupControlOptionWithOptionalIcon key="false" value="false" label={switchLabels.offLabel} />
|
|
62
|
-
</ToggleGroupControl>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const ToggleGroupControlOptionWithOptionalIcon
|
|
66
|
-
= memo(function<V extends string|number>({value, label}: {value: V, label: NonNullable<OptionalLabel>}) {
|
|
67
|
-
if (typeof label === 'string') {
|
|
68
|
-
return <ToggleGroupControlOption value={value} label={label} />;
|
|
69
|
-
} else {
|
|
70
|
-
const icon = resolveIcon(label.icon);
|
|
71
|
-
if (icon) {
|
|
72
|
-
return <ToggleGroupControlOptionIcon value={value} label={label.text} icon={icon} aria-label={label.tooltip} />;
|
|
73
|
-
} else {
|
|
74
|
-
return <ToggleGroupControlOption value={value} label={label.text} aria-label={label.tooltip} showTooltip={!!label.tooltip} />;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
function i18nSwitchLabel(label: NonNullable<OptionalLabel>): typeof label {
|
|
80
|
-
// eslint-disable-next-line @wordpress/i18n-no-variables -- There's nothing we can do about this - it's a requirement for being able to pass arbitrary labels
|
|
81
|
-
return typeof label === 'string' ? __(label) : {...label, text: __(label.text)};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function resolveIcon(icon: ReactElement|IconName|undefined): ReactElement|undefined {
|
|
85
|
-
if (typeof icon !== 'string') {
|
|
86
|
-
return icon;
|
|
87
|
-
}
|
|
88
|
-
if (icon.startsWith("plaudit:")) {
|
|
89
|
-
return plauditIcons[icon.substring(8) as PlauditIconName];
|
|
90
|
-
} else if (icon.startsWith("<svg")) {
|
|
91
|
-
return parse(icon) as ReactElement;
|
|
92
|
-
} else {
|
|
93
|
-
return icons[icon as WordPressIconName];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import {MediaUpload, MediaUploadCheck} from "@wordpress/block-editor";
|
|
2
|
-
import {BaseControl, Button, FocalPointPicker, ResponsiveWrapper, useBaseControlProps} from "@wordpress/components";
|
|
3
|
-
import {type Attachment, type Context, store as coreStore} from "@wordpress/core-data";
|
|
4
|
-
import {useSelect} from "@wordpress/data";
|
|
5
|
-
import {useCallback, useMemo} from "@wordpress/element";
|
|
6
|
-
import {__} from "@wordpress/i18n";
|
|
7
|
-
|
|
8
|
-
import type {ProvidedSlot} from "../blocks";
|
|
9
|
-
|
|
10
|
-
import type {ComponentPropsWithoutRef, ReactNode} from "react";
|
|
11
|
-
|
|
12
|
-
export type ImageData = {media?: {id?: number, url?: string}, pos?: {x?: number, y?: number}};
|
|
13
|
-
|
|
14
|
-
type FocalPoint = ComponentPropsWithoutRef<typeof FocalPointPicker>['value'];
|
|
15
|
-
|
|
16
|
-
export type ImageControlProps = {
|
|
17
|
-
includeFocalPointPicker?: boolean,
|
|
18
|
-
label: string,
|
|
19
|
-
help?: ReactNode,
|
|
20
|
-
onChange: (value?: Record<string|number, unknown>) => void,
|
|
21
|
-
value?: ImageData,
|
|
22
|
-
Label?: ProvidedSlot, Messages?: ProvidedSlot,
|
|
23
|
-
storage?: 'default'|'slim'
|
|
24
|
-
};
|
|
25
|
-
export function ImageControl(props: ImageControlProps) {
|
|
26
|
-
const {label, help, value, Label, Messages} = props;
|
|
27
|
-
|
|
28
|
-
const media = useSelect(select => value?.media?.id ? select(coreStore).getMedia(value.media.id) : undefined, [value]);
|
|
29
|
-
const {baseControlProps, controlProps} = useBaseControlProps({label: Label ? <Label /> : label, help});
|
|
30
|
-
return <BaseControl {...baseControlProps} __nextHasNoMarginBottom>
|
|
31
|
-
<div {...controlProps} className="editor-post-featured-image">
|
|
32
|
-
{media
|
|
33
|
-
? <ImageControlWithUploadedImage {...props} media={media} />
|
|
34
|
-
: <ImageControlWithoutUploadedImage {...props} media={media} />}
|
|
35
|
-
</div>
|
|
36
|
-
{Messages && <Messages />}
|
|
37
|
-
</BaseControl>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const RETAINED_FIELDS = ["id", "filename", "url", "date", "mime", "height", "width", "orientation"] as const;
|
|
41
|
-
export const MINIMAL_STORAGE_FIELDS = ['id', 'url'];
|
|
42
|
-
type HypotheticalImageDataType<C extends Context> = Partial<Attachment<C>>&ImageData['media']&{[k in typeof RETAINED_FIELDS[number]]?: k extends keyof Attachment<C> ? Attachment<C>[k] : unknown};
|
|
43
|
-
export function filterImageValueForSaving<C extends Context = 'edit'>(attachment: HypotheticalImageDataType<C>, storage: 'default'|'slim'): Pick<HypotheticalImageDataType<C>, typeof RETAINED_FIELDS[number]> {
|
|
44
|
-
if (storage === 'slim') {
|
|
45
|
-
return Object.fromEntries(Object.entries(attachment).filter((entry): entry is [typeof MINIMAL_STORAGE_FIELDS[number], any] => MINIMAL_STORAGE_FIELDS.includes(entry[0] as any)));
|
|
46
|
-
}
|
|
47
|
-
return Object.fromEntries(Object.entries(attachment).filter((entry): entry is [typeof RETAINED_FIELDS[number], any] => RETAINED_FIELDS.includes(entry[0] as any)));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const ALLOWED_TYPES = ['image'];
|
|
51
|
-
function ImageControlWithUploadedImage(props: ImageControlProps&{media: Attachment}) {
|
|
52
|
-
const {includeFocalPointPicker, media, storage, onChange, value} = props;
|
|
53
|
-
const onFPPChange = useCallback((pos: FocalPoint) => onChange(mergeInUpdatedValuePart(value, 'pos', {x: pos.x * 100, y: pos.y * 100}, storage)), [onChange, value]);
|
|
54
|
-
const onSelect = useCallback((media: {id: number}&{[k: string]: any}) => onChange(mergeInUpdatedValuePart(value, 'media', media, storage)), [onChange, value]);
|
|
55
|
-
const renderOpenButton = useCallback(({open}: {open(): void}) => <Button onClick={open} variant="secondary" __next40pxDefaultSize>{__('Replace image', 'plaudit')}</Button>, []);
|
|
56
|
-
const clear = useCallback(() => onChange(undefined), [onChange]);
|
|
57
|
-
|
|
58
|
-
const fppValue = useMemo(() => ({
|
|
59
|
-
x: (value?.pos?.x ?? 50) / 100,
|
|
60
|
-
y: (value?.pos?.y ?? 50) / 100
|
|
61
|
-
}), [value?.pos?.x, value?.pos?.y]);
|
|
62
|
-
|
|
63
|
-
let fppOrMedia;
|
|
64
|
-
if (includeFocalPointPicker !== false) {
|
|
65
|
-
fppOrMedia = <FocalPointPicker
|
|
66
|
-
onChange={onFPPChange}
|
|
67
|
-
url={value?.media?.url ?? ''}
|
|
68
|
-
value={fppValue}
|
|
69
|
-
__nextHasNoMarginBottom
|
|
70
|
-
/>;
|
|
71
|
-
} else {
|
|
72
|
-
fppOrMedia = <ResponsiveWrapper
|
|
73
|
-
naturalWidth={parsePossiblyUndefinedFloat(media.media_details["width"])}
|
|
74
|
-
naturalHeight={parsePossiblyUndefinedFloat(media.media_details["height"])}
|
|
75
|
-
>
|
|
76
|
-
<img src={value?.media?.url ?? ''} alt="The currently-selected image." />
|
|
77
|
-
</ResponsiveWrapper>
|
|
78
|
-
}
|
|
79
|
-
return <>
|
|
80
|
-
{value?.media?.url && fppOrMedia}
|
|
81
|
-
<MediaUploadCheck>
|
|
82
|
-
<MediaUpload
|
|
83
|
-
title={__('Replace image', 'plaudit')}
|
|
84
|
-
value={value?.media?.id ?? 0}
|
|
85
|
-
onSelect={onSelect}
|
|
86
|
-
allowedTypes={ALLOWED_TYPES}
|
|
87
|
-
render={renderOpenButton}
|
|
88
|
-
/>
|
|
89
|
-
<Button onClick={clear} isDestructive __next40pxDefaultSize>{__('Remove image', 'plaudit')}</Button>
|
|
90
|
-
</MediaUploadCheck>
|
|
91
|
-
</>;
|
|
92
|
-
}
|
|
93
|
-
function ImageControlWithoutUploadedImage(props: ImageControlProps&{media: Attachment|undefined}) {
|
|
94
|
-
const {media, onChange, storage, value} = props;
|
|
95
|
-
const onSelect = useCallback((media: {id: number}&{[k: string]: any}) => onChange(mergeInUpdatedValuePart(value, 'media', media, storage)), [onChange, value]);
|
|
96
|
-
const renderOpenButton = useCallback(({open}: {open(): void}) => (
|
|
97
|
-
<Button
|
|
98
|
-
className={value?.media?.id ? 'editor-post-featured-image__preview' : 'editor-post-featured-image__toggle'} onClick={open} __next40pxDefaultSize
|
|
99
|
-
>
|
|
100
|
-
{!value?.media?.id && __('Choose an image', 'plaudit')}
|
|
101
|
-
{media &&
|
|
102
|
-
<ResponsiveWrapper
|
|
103
|
-
naturalWidth={parsePossiblyUndefinedFloat(media.media_details["width"])}
|
|
104
|
-
naturalHeight={parsePossiblyUndefinedFloat(media.media_details["height"])}
|
|
105
|
-
>
|
|
106
|
-
<img src={media.source_url} alt="The currently-selected image." />
|
|
107
|
-
</ResponsiveWrapper>
|
|
108
|
-
}
|
|
109
|
-
</Button>
|
|
110
|
-
), [media, value?.media?.id]);
|
|
111
|
-
return <MediaUploadCheck>
|
|
112
|
-
<MediaUpload
|
|
113
|
-
onSelect={onSelect}
|
|
114
|
-
value={value?.media?.id ?? 0}
|
|
115
|
-
allowedTypes={ALLOWED_TYPES}
|
|
116
|
-
render={renderOpenButton}
|
|
117
|
-
/>
|
|
118
|
-
</MediaUploadCheck>;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function parsePossiblyUndefinedFloat(string: string|number|undefined): number|undefined {
|
|
122
|
-
return typeof string === 'number' ? string : (string !== undefined ? parseFloat(string) : undefined);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function mergeInUpdatedValuePart(value: ImageData|undefined, partName: 'media'|'pos', part: Required<ImageData>[typeof partName]|undefined, storage: 'default'|'slim'|undefined): ImageData|undefined {
|
|
126
|
-
if (part === undefined) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
if (partName === 'media') {
|
|
130
|
-
const prt = (part as Required<ImageData>[typeof partName]);
|
|
131
|
-
if (prt.id === 0) {
|
|
132
|
-
return undefined;
|
|
133
|
-
} else if (value === undefined) {
|
|
134
|
-
return {media: filterImageValueForSaving(prt, storage ?? 'default'), pos: {x: 50, y: 50}};
|
|
135
|
-
}
|
|
136
|
-
return {media: filterImageValueForSaving(prt, storage ?? 'default'), pos: {x: value.pos?.x ?? 50, y: value.pos?.y ?? 50}};
|
|
137
|
-
} else if (value === undefined || !value.media?.id) {
|
|
138
|
-
return undefined;
|
|
139
|
-
} else {
|
|
140
|
-
const prt = (part as Required<ImageData>[typeof partName]);
|
|
141
|
-
return {media: value.media, pos: {x: prt.x ?? value.pos?.x ?? 50, y: prt.y ?? value.pos?.y ?? 50}};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import {InspectorControls} from "@wordpress/block-editor";
|
|
2
|
-
import {Panel, PanelBody, TabPanel} from "@wordpress/components";
|
|
3
|
-
import {useState} from "@wordpress/element";
|
|
4
|
-
|
|
5
|
-
import {type ComponentPropsWithoutRef, type ReactNode, useCallback} from "react";
|
|
6
|
-
|
|
7
|
-
type TabPanelProps = ComponentPropsWithoutRef<typeof TabPanel>;
|
|
8
|
-
type PanelBodyProps = ComponentPropsWithoutRef<typeof PanelBody>;
|
|
9
|
-
type Tab = TabPanelProps['tabs'][number]&{items: ReactNode};
|
|
10
|
-
type GroupAndCondition = {group?: string, condition?(): boolean};
|
|
11
|
-
type NormalPanelInspectorPanelProps = {tabbed?: false|undefined, raw?: boolean|undefined}&Omit<PanelBodyProps, 'children'>&Pick<InspectorControls.Props, 'children'>&GroupAndCondition;
|
|
12
|
-
type TabbedPanelInspectorPanelProps = {tabbed: true, tabs: Tab[]}&Omit<TabPanelProps, 'children'|'tabs'>&GroupAndCondition;
|
|
13
|
-
export type InspectorPanelProps = (NormalPanelInspectorPanelProps|TabbedPanelInspectorPanelProps);
|
|
14
|
-
export function InspectorPanel(props: InspectorPanelProps) {
|
|
15
|
-
if (props["tabbed"]) {
|
|
16
|
-
const {tabbed, tabs, group, condition, ...tabPanelProps} = props;
|
|
17
|
-
return <InspectorControls group={group}>
|
|
18
|
-
<TabPanel {...tabPanelProps} tabs={condition === undefined || condition() ? tabs : []}>
|
|
19
|
-
{tab => <Panel>{(tab as Tab).items}</Panel>}
|
|
20
|
-
</TabPanel>
|
|
21
|
-
</InspectorControls>;
|
|
22
|
-
} else {
|
|
23
|
-
return <InspectorPanelPanel {...props} />;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function InspectorPanelPanel(props: NormalPanelInspectorPanelProps) {
|
|
27
|
-
const {tabbed, raw, group, condition, children, onToggle, initialOpen, ...panelBodyProps} = props;
|
|
28
|
-
const [wasOpened, setWasOpened] = useState(initialOpen);
|
|
29
|
-
const memoOnToggle = useCallback((value: boolean) => {
|
|
30
|
-
onToggle?.(value);
|
|
31
|
-
setWasOpened(value);
|
|
32
|
-
}, [onToggle, setWasOpened]);
|
|
33
|
-
return <InspectorControls group={group} children={raw
|
|
34
|
-
? (condition === undefined || condition() ? children : [])
|
|
35
|
-
: <PanelBody {...panelBodyProps} children={condition === undefined || condition() ? children : []} onToggle={memoOnToggle} initialOpen={wasOpened} />
|
|
36
|
-
} />;
|
|
37
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import {BaseControl, ComboboxControl, Spinner} from "@wordpress/components";
|
|
2
|
-
import {useCallback, useMemo, useState} from "@wordpress/element";
|
|
3
|
-
import {__, sprintf} from "@wordpress/i18n";
|
|
4
|
-
|
|
5
|
-
import {useSuggestions} from "./hooks/useSuggestions";
|
|
6
|
-
|
|
7
|
-
import type {ComponentProps, FocusEvent} from "react";
|
|
8
|
-
|
|
9
|
-
type ComboboxControlProps = ComponentProps<typeof ComboboxControl>;
|
|
10
|
-
type ComboboxControlOption = ComboboxControlProps['options'][number];
|
|
11
|
-
export type LazySuggestionsComboboxControlProps = Omit<ComboboxControlProps, 'options'> & {
|
|
12
|
-
getOption(value?: string): Promise<ComboboxControlOption|undefined>;
|
|
13
|
-
getSuggestions(filterValue: string): Promise<ComboboxControlOption[]>;
|
|
14
|
-
};
|
|
15
|
-
export function LazySuggestionsComboboxControl(props: LazySuggestionsComboboxControlProps) {
|
|
16
|
-
const {help, allowReset, className, ...passthroughProps} = props;
|
|
17
|
-
|
|
18
|
-
const {
|
|
19
|
-
hasLoadingError, isInitializing, isLoading, suggestions, input, setInput
|
|
20
|
-
} = useSuggestions(props.value, props);
|
|
21
|
-
|
|
22
|
-
const [hasFocus, setHasFocus] = useState(false);
|
|
23
|
-
const options = useMemo(
|
|
24
|
-
() => hasFocus && input.length < 2 || (isLoading && !suggestions.length) ? [{label: input, value: "", disabled: true}] : suggestions,
|
|
25
|
-
[hasFocus, input, isLoading, suggestions]);
|
|
26
|
-
|
|
27
|
-
const onFocus = useCallback((e: FocusEvent<HTMLElement>) => setHasFocus(e.currentTarget.contains(e.target)), [setHasFocus]);
|
|
28
|
-
const onBlur = useCallback((e: FocusEvent<HTMLElement>) => setHasFocus(e.currentTarget.contains(e.relatedTarget)), [setHasFocus]);
|
|
29
|
-
|
|
30
|
-
const onFilterValueChange = useCallback((value: string) => {
|
|
31
|
-
if (props.onFilterValueChange) {
|
|
32
|
-
props.onFilterValueChange(value);
|
|
33
|
-
}
|
|
34
|
-
setInput(value);
|
|
35
|
-
}, [props.onFilterValueChange, setInput]);
|
|
36
|
-
|
|
37
|
-
const __experimentalRenderItem = useMemo(() => {
|
|
38
|
-
return hasFocus && input.length < 2 ? () => __("Start typing to see suggestions", 'plaudit')
|
|
39
|
-
: (isLoading && !suggestions.length ? () => __("Loading suggestions", 'plaudit') : undefined);
|
|
40
|
-
}, [hasFocus, input, isLoading, suggestions])
|
|
41
|
-
|
|
42
|
-
if (isInitializing) {
|
|
43
|
-
return <BaseControl {...props} __nextHasNoMarginBottom>
|
|
44
|
-
<Spinner /><span>{/* translators: %s is replaced with the label of the property */ sprintf(__("Initializing %s", 'plaudit'), props.label)}</span>
|
|
45
|
-
</BaseControl>
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const needsGreyOut = input.length < 2 || (isLoading && !suggestions.length);
|
|
49
|
-
return <div onFocus={onFocus} onBlur={onBlur}>
|
|
50
|
-
{isLoading && <Spinner style={{marginTop: "30px", position: "absolute", right: "40px"}} aria-label="Updating Suggestions" />}
|
|
51
|
-
<ComboboxControl
|
|
52
|
-
{...passthroughProps}
|
|
53
|
-
className={className ? `${className}${needsGreyOut ? " insufficient-input-length" : ""}` : (needsGreyOut ? "insufficient-input-length" : undefined)}
|
|
54
|
-
help={help}
|
|
55
|
-
options={options}
|
|
56
|
-
onFilterValueChange={onFilterValueChange}
|
|
57
|
-
__experimentalRenderItem={__experimentalRenderItem}
|
|
58
|
-
allowReset={allowReset !== false}
|
|
59
|
-
__next40pxDefaultSize
|
|
60
|
-
__nextHasNoMarginBottom
|
|
61
|
-
/>
|
|
62
|
-
{hasLoadingError && <div><span className="components-base-control__help">{__("An error occurred while updating suggestions", 'plaudit')}</span></div>}
|
|
63
|
-
</div>;
|
|
64
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {useCallback, useMemo} from "@wordpress/element";
|
|
2
|
-
|
|
3
|
-
import {ExtendedFormTokenField, ValidationState} from "./ExtendedFormTokenField";
|
|
4
|
-
import type {SNPControlSlots} from "../blocks";
|
|
5
|
-
import {getLabel, normalizePickableOptionsToPairs} from "./shared";
|
|
6
|
-
import type {PickableOptions} from "./types";
|
|
7
|
-
|
|
8
|
-
import type {ReactNode} from "react";
|
|
9
|
-
|
|
10
|
-
type MultiSelectProps = {
|
|
11
|
-
onChange: (value: string[]) => void;
|
|
12
|
-
options: PickableOptions<string|number>;
|
|
13
|
-
label: string;
|
|
14
|
-
help?: ReactNode;
|
|
15
|
-
placeholder: string;
|
|
16
|
-
value?: string[];
|
|
17
|
-
expandOnFocus?: boolean;
|
|
18
|
-
maxLength?: number;
|
|
19
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
20
|
-
|
|
21
|
-
type MandatoryKeys = 'onChange'|'value'|'label'|'options';
|
|
22
|
-
export type MultiSelectConstructorProps = Partial<Omit<MultiSelectProps, MandatoryKeys>> & Pick<MultiSelectProps, MandatoryKeys>;
|
|
23
|
-
|
|
24
|
-
export function MultiSelectControl(props: MultiSelectConstructorProps) {
|
|
25
|
-
const {value, options, ...fieldProps} = props;
|
|
26
|
-
const normalizedOptions = useMemo(() => normalizePickableOptionsToPairs(options), [options]);
|
|
27
|
-
const validOptions = useMemo(
|
|
28
|
-
() => Object.fromEntries(normalizedOptions.map(opt => [opt[0], getLabel(opt)])),
|
|
29
|
-
[normalizedOptions]);
|
|
30
|
-
|
|
31
|
-
return <ExtendedFormTokenField
|
|
32
|
-
{...fieldProps}
|
|
33
|
-
value={value}
|
|
34
|
-
multiple={true}
|
|
35
|
-
stringToTokenConverter={useCallback(token => {
|
|
36
|
-
if (validOptions[token]) {
|
|
37
|
-
return {value: token, title: validOptions[token], status: ValidationState.Valid};
|
|
38
|
-
} else {
|
|
39
|
-
const result = /(.+) \(#([^)]+)\)$/.exec(token);
|
|
40
|
-
return result && result[1] && result[2] ? {value: result[2], title: result[1], status: ValidationState.Validating} : {value: token, status: ValidationState.Invalid};
|
|
41
|
-
}
|
|
42
|
-
}, [validOptions])}
|
|
43
|
-
suggestionQuery={useCallback(input => {
|
|
44
|
-
return normalizedOptions
|
|
45
|
-
.filter(option => option[0]?.toString().includes(input) || getLabel(option).includes(input))
|
|
46
|
-
.map(option => ({value: option[0]?.toString(), status: ValidationState.Valid, title: getLabel(option)}));
|
|
47
|
-
}, [normalizedOptions])}
|
|
48
|
-
validationQuery={useCallback(values => {
|
|
49
|
-
return values
|
|
50
|
-
.filter(value => validOptions[value] !== undefined)
|
|
51
|
-
.map(value => ({value, title: validOptions[value], status: ValidationState.Valid}));
|
|
52
|
-
}, [validOptions])}
|
|
53
|
-
validator={validator}
|
|
54
|
-
/>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function validator(token: string) {
|
|
58
|
-
return /\(#([^)]+)\)$/.exec(token)?.[1] !== undefined;
|
|
59
|
-
}
|
package/src/controls/PickOne.tsx
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__experimentalToggleGroupControl as ToggleGroupControl,
|
|
3
|
-
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
|
|
4
|
-
__experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon, BaseControl,
|
|
5
|
-
ColorPalette, RadioControl, SelectControl, useBaseControlProps
|
|
6
|
-
} from "@wordpress/components";
|
|
7
|
-
|
|
8
|
-
import {normalizePickableOptionsToPairs} from "./shared";
|
|
9
|
-
import type {PickableOptions, SimpleBlockControlProps} from "./types";
|
|
10
|
-
|
|
11
|
-
import type {ComponentPropsWithoutRef, ReactElement} from "react";
|
|
12
|
-
|
|
13
|
-
function makeSharedRadioAndSelectProps<T extends string>(props: SimpleBlockControlProps<T, string>&{options: PickableOptions<string>}) {
|
|
14
|
-
return {
|
|
15
|
-
label: props.label,
|
|
16
|
-
help: props.help,
|
|
17
|
-
onChange(value: string) {
|
|
18
|
-
props.setAttributes({[props.attribute]: value});
|
|
19
|
-
},
|
|
20
|
-
options: normalizePickableOptionsToPairs(props.options).map(([value, label]) => ({
|
|
21
|
-
value, label: typeof label === 'string' ? label : label.text
|
|
22
|
-
}))
|
|
23
|
-
} as Pick<Parameters<typeof RadioControl>[0], 'label'|'options'>&{onChange(value: string): void};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type PickOneFromToggleGroupProps<T extends string> = SimpleBlockControlProps<T, string|number>&{options: PickableOptions<string, {icon: ReactElement}>};
|
|
27
|
-
export function PickOneFromToggleGroup<T extends string>(props: PickOneFromToggleGroupProps<T>) {
|
|
28
|
-
return <ToggleGroupControl
|
|
29
|
-
label={props.label}
|
|
30
|
-
help={props.help}
|
|
31
|
-
value={props.attributes[props.attribute]}
|
|
32
|
-
onChange={value => props.setAttributes({[props.attribute]: value})}
|
|
33
|
-
children={
|
|
34
|
-
normalizePickableOptionsToPairs(props.options).map(([value, label]) => typeof label === 'string'
|
|
35
|
-
? <ToggleGroupControlOption key={value} value={value} label={label} />
|
|
36
|
-
: <ToggleGroupControlOptionIcon key={value} value={value} label={label.text} icon={label.icon} />)
|
|
37
|
-
}
|
|
38
|
-
__next40pxDefaultSize
|
|
39
|
-
__nextHasNoMarginBottom
|
|
40
|
-
/>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function PickOneFromSelect<T extends string>(props: SimpleBlockControlProps<T, string>& { options: PickableOptions<string> }) {
|
|
44
|
-
return <SelectControl
|
|
45
|
-
{...makeSharedRadioAndSelectProps(props)}
|
|
46
|
-
value={props.attributes[props.attribute]}
|
|
47
|
-
__next40pxDefaultSize
|
|
48
|
-
__nextHasNoMarginBottom
|
|
49
|
-
/>;
|
|
50
|
-
}
|
|
51
|
-
export function PickOneFromRadios<T extends string>(props: SimpleBlockControlProps<T, string>& { options: PickableOptions<string> }) {
|
|
52
|
-
return <RadioControl
|
|
53
|
-
{...makeSharedRadioAndSelectProps(props)}
|
|
54
|
-
selected={props.attributes[props.attribute]}
|
|
55
|
-
/>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
type ColorObject = Exclude<NonNullable<ComponentPropsWithoutRef<typeof ColorPalette>['colors']>, {colors: any}[]>[number];
|
|
59
|
-
|
|
60
|
-
export function PickOneFromColors<T extends string>(props: SimpleBlockControlProps<T, string>&{options: PickableOptions<string, {color?: string|undefined}>}) {
|
|
61
|
-
const valueToColorMap = new Map<string, string>();
|
|
62
|
-
const colorToValueMap = new Map<string, string>();
|
|
63
|
-
const colors: ColorObject[] = [];
|
|
64
|
-
for (const [value, display] of normalizePickableOptionsToPairs(props.options)) {
|
|
65
|
-
if (typeof display === 'string') {
|
|
66
|
-
colors.push({color: value, name: display});
|
|
67
|
-
} else if (display.color) {
|
|
68
|
-
valueToColorMap.set(value, display.color);
|
|
69
|
-
colorToValueMap.set(display.color, value);
|
|
70
|
-
colors.push({color: display.color, name: display.text});
|
|
71
|
-
} else {
|
|
72
|
-
colors.push({color: value, name: display.text});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const currentColor = valueToColorMap.get(props.attributes[props.attribute]) ?? props.attributes[props.attribute];
|
|
77
|
-
const {baseControlProps, controlProps} = useBaseControlProps({label: props.label, help: props.help});
|
|
78
|
-
return <BaseControl {...baseControlProps} __nextHasNoMarginBottom>
|
|
79
|
-
<ColorPalette
|
|
80
|
-
{...controlProps}
|
|
81
|
-
disableCustomColors={true}
|
|
82
|
-
onChange={color => props.setAttributes({[props.attribute]: colorToValueMap.get(color ?? currentColor) ?? color ?? currentColor})}
|
|
83
|
-
colors={colors}
|
|
84
|
-
value={currentColor}
|
|
85
|
-
clearable={false}
|
|
86
|
-
/>
|
|
87
|
-
</BaseControl>;
|
|
88
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import {Spinner} from '@wordpress/components';
|
|
2
|
-
import {useRef} from "@wordpress/element";
|
|
3
|
-
|
|
4
|
-
import {use, type WrappedPromise, wrapPromise} from "../lib/suspense";
|
|
5
|
-
|
|
6
|
-
import {Suspense, type ReactNode} from "react";
|
|
7
|
-
|
|
8
|
-
export type AwaitedProps<T extends object> = {[K in keyof T]: Awaited<T[K]>};
|
|
9
|
-
export type PromisableComponentProps<T extends Awaited<object>> = {
|
|
10
|
-
promisedProps: T|Promise<T>, initializing?: (() => ReactNode)|ReactNode, renderer(props: AwaitedProps<T>): ReactNode,
|
|
11
|
-
forceSuspend?: boolean
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function PromisableComponent<T extends Awaited<object>>(props: PromisableComponentProps<T>) {
|
|
15
|
-
const wrappedPromise = useWrappedArgsParameter(props.promisedProps, props.forceSuspend);
|
|
16
|
-
if (!(wrappedPromise instanceof Promise)) {
|
|
17
|
-
return props.renderer(wrappedPromise);
|
|
18
|
-
}
|
|
19
|
-
return <Suspense fallback={typeof props.initializing === 'function' ? props.initializing() : <><Spinner /> {props.initializing}</>}>
|
|
20
|
-
<PromisableComponentDelegator promise={wrappedPromise} children={props.renderer} />
|
|
21
|
-
</Suspense>;
|
|
22
|
-
}
|
|
23
|
-
function PromisableComponentDelegator<T extends AwaitedProps<Awaited<object>>>(
|
|
24
|
-
{promise, children}: {promise: WrappedPromise<T>, children(props: T): ReactNode}
|
|
25
|
-
) {
|
|
26
|
-
return children(use(promise));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function useWrappedArgsParameter<T extends {}>(args: T, forcePromise?: boolean) {
|
|
30
|
-
const ref = useRef<WrappedArgsHolder<T>|null>(null);
|
|
31
|
-
if (ref.current === null || args !== ref.current.args || forcePromise !== ref.current.forcePromise) {
|
|
32
|
-
return (ref.current = {args, forcePromise, value: actuallyWrapTheArgsParameter(args, forcePromise)}).value;
|
|
33
|
-
} else {
|
|
34
|
-
const oldArgs = Object.values(ref.current.args);
|
|
35
|
-
const newArgs = Object.values(args);
|
|
36
|
-
if (oldArgs.length !== newArgs.length) {
|
|
37
|
-
return (ref.current = {args, forcePromise, value: actuallyWrapTheArgsParameter(args, forcePromise)}).value;
|
|
38
|
-
}
|
|
39
|
-
for (let i = 0; i < newArgs.length; i++) {
|
|
40
|
-
if (newArgs[i] !== oldArgs[i]) {
|
|
41
|
-
return (ref.current = {args, forcePromise, value: actuallyWrapTheArgsParameter(args, forcePromise)}).value;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return ref.current.value;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type WrappedArgsHolder<T extends {}> = {args: T, forcePromise?: boolean, value: ReturnType<typeof actuallyWrapTheArgsParameter<T>>};
|
|
49
|
-
function actuallyWrapTheArgsParameter<T extends {}>(args: T, forcePromise?: boolean) {
|
|
50
|
-
if (!Object.values(args).some(v => v instanceof Promise)) {
|
|
51
|
-
return forcePromise ? (wrapPromise(Promise.resolve(args)) as WrappedPromise<AwaitedProps<Awaited<T>>>) : (args as AwaitedProps<Awaited<T>>);
|
|
52
|
-
}
|
|
53
|
-
return wrapPromise(Promise
|
|
54
|
-
.all(Object.entries(args).map(async ([key, value]): Promise<[typeof key, Awaited<typeof value>]> => [key, await value]))
|
|
55
|
-
.then<AwaitedProps<T>>(entries => Object.fromEntries(entries) as any));
|
|
56
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {LinkControl} from "@wordpress/block-editor";
|
|
2
|
-
import {BaseControl, Button, TextControl, ToggleControl} from "@wordpress/components";
|
|
3
|
-
import {useCallback, useEffect, useRef, useState} from "@wordpress/element";
|
|
4
|
-
import {chevronDown, chevronUp} from "@wordpress/icons";
|
|
5
|
-
|
|
6
|
-
import type {ReactNode} from "react";
|
|
7
|
-
|
|
8
|
-
export type ProperLinkControlSetting = LinkControl.Setting&({type?: 'toggle'|undefined, default?: boolean|undefined}|{type: 'text', default?: string|undefined});
|
|
9
|
-
|
|
10
|
-
export type ProperLinkControlProps = Omit<LinkControl.Props, 'settings'>&{label: ReactNode, help?: ReactNode, settings?: ProperLinkControlSetting[]|false};
|
|
11
|
-
export function ProperLinkControl(props: ProperLinkControlProps) {
|
|
12
|
-
const {value, onChange, onRemove, label, help,
|
|
13
|
-
settings = ProperLinkControl.DEFAULT_LINK_SETTINGS, ...linkControlProps} = props;
|
|
14
|
-
const [areAdvancedSettingsVisible, setAreAdvancedSettingsVisible] = useState(false);
|
|
15
|
-
const advancedSettingsRef = useRef<HTMLDivElement|null>(null);
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
const advancedSettingsElement = advancedSettingsRef.current;
|
|
19
|
-
if (!advancedSettingsElement) {
|
|
20
|
-
return () => {};
|
|
21
|
-
}
|
|
22
|
-
const transitionListener = (evt: TransitionEvent) => {
|
|
23
|
-
if (evt.propertyName === 'max-height' && evt.target === advancedSettingsElement) {
|
|
24
|
-
if (advancedSettingsElement.style.maxHeight === '0px') {
|
|
25
|
-
advancedSettingsElement.style.display = 'none';
|
|
26
|
-
} else {
|
|
27
|
-
advancedSettingsElement.style.maxHeight = '';
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
advancedSettingsElement.addEventListener('transitionend', transitionListener);
|
|
32
|
-
return () => advancedSettingsElement.addEventListener('transitionend', transitionListener);
|
|
33
|
-
}, [advancedSettingsRef]);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const advancedSettingsElement = advancedSettingsRef.current;
|
|
37
|
-
if (!advancedSettingsElement) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (areAdvancedSettingsVisible) {
|
|
41
|
-
advancedSettingsElement.style.display = '';
|
|
42
|
-
advancedSettingsElement.style.maxHeight = advancedSettingsElement.scrollHeight + "px";
|
|
43
|
-
} else {
|
|
44
|
-
advancedSettingsElement.style.maxHeight = advancedSettingsElement.scrollHeight + "px";
|
|
45
|
-
// We have to use window.requestAnimationFrame in order to force the maxHeight to be treated as "recalculated".
|
|
46
|
-
// Unless something else on the page goes horribly wrong, there shouldn't be any actual painting done as part of updating the max height.
|
|
47
|
-
window.requestAnimationFrame(() => advancedSettingsElement.style.maxHeight = "0px");
|
|
48
|
-
}
|
|
49
|
-
}, [areAdvancedSettingsVisible, advancedSettingsRef]);
|
|
50
|
-
|
|
51
|
-
const safeOnRemove = useCallback(() => onRemove !== undefined ? onRemove() : onChange?.(undefined), [onRemove, onChange]);
|
|
52
|
-
|
|
53
|
-
const renderableSettings = settings === false ? [] : settings;
|
|
54
|
-
const renderControlBottom = useCallback(() => (value?.url && renderableSettings?.length && <>
|
|
55
|
-
<Button
|
|
56
|
-
variant="tertiary" onClick={() => setAreAdvancedSettingsVisible(!areAdvancedSettingsVisible)}
|
|
57
|
-
icon={areAdvancedSettingsVisible ? chevronUp : chevronDown} iconPosition="right" text="Advanced"
|
|
58
|
-
__next40pxDefaultSize accessibleWhenDisabled
|
|
59
|
-
/>
|
|
60
|
-
<div ref={advancedSettingsRef} style={{display: "none", transition: "max-height 200ms ease-in-out", overflow: "hidden"}}>
|
|
61
|
-
{...renderableSettings.map(setting => <ProperLinkControlSetting key={setting.id} setting={setting} onChange={onChange} value={value} />)}
|
|
62
|
-
</div>
|
|
63
|
-
</>) || "", [value, settings, areAdvancedSettingsVisible, onChange]);
|
|
64
|
-
|
|
65
|
-
return <BaseControl __nextHasNoMarginBottom id="" label={props.label} help={props.help}>
|
|
66
|
-
<LinkControl
|
|
67
|
-
{...linkControlProps}
|
|
68
|
-
value={value}
|
|
69
|
-
onChange={onChange}
|
|
70
|
-
onRemove={safeOnRemove}
|
|
71
|
-
settings={ProperLinkControl.DUMMY_LINK_SETTINGS}
|
|
72
|
-
renderControlBottom={renderControlBottom}
|
|
73
|
-
/>
|
|
74
|
-
</BaseControl>
|
|
75
|
-
}
|
|
76
|
-
ProperLinkControl.DEFAULT_LINK_SETTINGS = LinkControl.DEFAULT_LINK_SETTINGS;
|
|
77
|
-
ProperLinkControl.DUMMY_LINK_SETTINGS = [] as LinkControl.Setting[];
|
|
78
|
-
|
|
79
|
-
export interface ProperLinkControl {
|
|
80
|
-
(props: ProperLinkControlProps): ReactNode;
|
|
81
|
-
readonly DEFAULT_LINK_SETTINGS: ReadonlyArray<ProperLinkControlSetting>;
|
|
82
|
-
readonly DUMMY_LINK_SETTINGS: ReadonlyArray<LinkControl.Setting>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type ProperLinkControlSettingProps = {value?: LinkControl.Value, onChange?: (nextValue?: LinkControl.Value) => void, setting: ProperLinkControlSetting};
|
|
86
|
-
function ProperLinkControlSetting({value, onChange, setting}: ProperLinkControlSettingProps) {
|
|
87
|
-
return <div style={{marginTop: "8px"}}>
|
|
88
|
-
{setting.type === 'text'
|
|
89
|
-
? <TextControl
|
|
90
|
-
style={{marginTop: "8px"}} value={value?.[setting.id] ?? setting.default ?? ''} label={setting.title}
|
|
91
|
-
onChange={v => onChange?.({...value, [setting.id]: v})}
|
|
92
|
-
__next40pxDefaultSize __nextHasNoMarginBottom
|
|
93
|
-
/>
|
|
94
|
-
: <ToggleControl style={{marginTop: "8px"}} checked={value?.[setting.id] ?? setting.default ?? false} label={setting.title}
|
|
95
|
-
onChange={v => onChange?.({...value, [setting.id]: v})} />
|
|
96
|
-
}
|
|
97
|
-
</div>;
|
|
98
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import {ToggleControl} from "@wordpress/components";
|
|
2
|
-
import {useCallback} from "@wordpress/element";
|
|
3
|
-
|
|
4
|
-
import type {SimpleBlockControlProps} from "./types";
|
|
5
|
-
|
|
6
|
-
export function SimpleToggle<T extends string>(props: SimpleBlockControlProps<T, boolean>) {
|
|
7
|
-
const onChange = useCallback((checked: boolean) => props.setAttributes({[props.attribute]: checked}), [props.setAttributes, props.attribute]);
|
|
8
|
-
return <ToggleControl __nextHasNoMarginBottom checked={props.attributes[props.attribute]} label={props.label} onChange={onChange} />;
|
|
9
|
-
}
|