@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,144 +0,0 @@
|
|
|
1
|
-
import {Spinner, FormTokenField, Modal} from '@wordpress/components';
|
|
2
|
-
import {useDebounce} from "@wordpress/compose";
|
|
3
|
-
import {type MapSelect, useSelect} from "@wordpress/data";
|
|
4
|
-
import {memo, useCallback, useEffect, useMemo, useRef, useState} from "@wordpress/element";
|
|
5
|
-
import {__} from "@wordpress/i18n";
|
|
6
|
-
|
|
7
|
-
import type {SNPControlSlots} from "../blocks";
|
|
8
|
-
import {packDisplayTokenText, unpackDisplayedTokenText, useImprovedTokenManager, ValidationState} from "./hooks/useImprovedTokenManager";
|
|
9
|
-
import {useMultiSingleConversionLayer} from "./hooks/useMultiSingleConversionLayer";
|
|
10
|
-
import {potentialWpErrorToString} from "../lib/helpers";
|
|
11
|
-
import {TokenItem} from "../lib/useful-types";
|
|
12
|
-
|
|
13
|
-
export {packDisplayTokenText, unpackDisplayedTokenText, ValidationState};
|
|
14
|
-
|
|
15
|
-
import type {ReactNode} from "react";
|
|
16
|
-
|
|
17
|
-
type ExtendedFormTokenFieldPropsBase = {
|
|
18
|
-
label?: string;
|
|
19
|
-
help?: ReactNode;
|
|
20
|
-
|
|
21
|
-
validationQuery(tokens: string[], ...args: Parameters<MapSelect>): TokenItem[]|undefined;
|
|
22
|
-
suggestionQuery(input: string, ...args: Parameters<MapSelect>): TokenItem[]|undefined;
|
|
23
|
-
stringToTokenConverter(suggestion: string): TokenItem;
|
|
24
|
-
validator?: (value: string) => boolean;
|
|
25
|
-
expandOnFocus?: boolean;
|
|
26
|
-
initialSuggestions?: TokenItem[];
|
|
27
|
-
hasLoadingError?: (input: string, ...args: Parameters<MapSelect>) => boolean;
|
|
28
|
-
hasValidationError?: (...args: Parameters<MapSelect>) => boolean;
|
|
29
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
30
|
-
type ExtendedFormTokenFieldPropsSingle = ExtendedFormTokenFieldPropsBase&{
|
|
31
|
-
value?: string;
|
|
32
|
-
onChange: (value: string) => void;
|
|
33
|
-
multiple: false;
|
|
34
|
-
};
|
|
35
|
-
type ExtendedFormTokenFieldPropsMultiple = ExtendedFormTokenFieldPropsBase&{
|
|
36
|
-
value?: string[];
|
|
37
|
-
onChange: (value: string[]) => void;
|
|
38
|
-
multiple?: true|undefined;
|
|
39
|
-
maxLength?: number;
|
|
40
|
-
};
|
|
41
|
-
export type ExtendedFormTokenFieldProps = ExtendedFormTokenFieldPropsSingle|ExtendedFormTokenFieldPropsMultiple;
|
|
42
|
-
|
|
43
|
-
const EMPTY_ARRAY: TokenItem[] = [];
|
|
44
|
-
export function ExtendedFormTokenField(props: ExtendedFormTokenFieldProps) {
|
|
45
|
-
const [debouncedInput, setDebouncedInput, setImmediateInput, input] = useDebouncedValue('');
|
|
46
|
-
const [hasBeenFocused, setHasBeenFocused] = useState(false);
|
|
47
|
-
|
|
48
|
-
const tokenTitleCacheRef = useRef<Map<TokenItem['value'], TokenItem['title']>|undefined>(undefined);
|
|
49
|
-
if (tokenTitleCacheRef.current === undefined) {
|
|
50
|
-
tokenTitleCacheRef.current = new Map();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const {rawSuggestions, isLoading, hasLoadingError, thrownLoadingError} = useSelect((select, registry) => {
|
|
54
|
-
if (!debouncedInput && (!hasBeenFocused || props.initialSuggestions !== undefined)) {
|
|
55
|
-
return {rawSuggestions: props.initialSuggestions ?? EMPTY_ARRAY, isLoading: false};
|
|
56
|
-
}
|
|
57
|
-
let res;
|
|
58
|
-
try {
|
|
59
|
-
res = props.suggestionQuery(debouncedInput, select, registry);
|
|
60
|
-
} catch (e) {
|
|
61
|
-
return {rawSuggestions: [], isLoading: false, hasLoadingError: true, thrownLoadingError: potentialWpErrorToString(e)};
|
|
62
|
-
}
|
|
63
|
-
if (res === undefined) {
|
|
64
|
-
return {rawSuggestions: props.initialSuggestions ?? EMPTY_ARRAY, isLoading: true};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {rawSuggestions: res, isLoading: false, hasLoadingError: props.hasLoadingError?.(debouncedInput, select, registry)};
|
|
68
|
-
}, [debouncedInput, props.initialSuggestions, props.suggestionQuery, hasBeenFocused]);
|
|
69
|
-
const suggestions = useMemo(() => rawSuggestions?.map(packDisplayTokenText), [rawSuggestions]);
|
|
70
|
-
|
|
71
|
-
const {value, setValue} = useMultiSingleConversionLayer(props.value, props.onChange, props.stringToTokenConverter, props.multiple);
|
|
72
|
-
const {currentTokens, isValidating, updateComponentValue, tokenStatusCache, thrownValidationError}
|
|
73
|
-
= useImprovedTokenManager(value, setValue, props.validationQuery, props.stringToTokenConverter, tokenTitleCacheRef.current);
|
|
74
|
-
|
|
75
|
-
// This is just a trick to avoid needing to send an additional validation request when adding a token from the suggestions list
|
|
76
|
-
useMemo(() => {
|
|
77
|
-
if (rawSuggestions) {
|
|
78
|
-
for (const suggestion of rawSuggestions) {
|
|
79
|
-
if (suggestion.status !== undefined && !tokenStatusCache.has(suggestion.value)) {
|
|
80
|
-
tokenStatusCache.set(suggestion.value, suggestion.status);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}, [rawSuggestions, tokenStatusCache]);
|
|
85
|
-
|
|
86
|
-
const {hasValidationError} = useSelect((select, registry) => ({
|
|
87
|
-
hasValidationError: !!thrownValidationError || props.hasValidationError?.(select, registry)
|
|
88
|
-
}), [props.hasLoadingError, props.hasValidationError, thrownValidationError]);
|
|
89
|
-
|
|
90
|
-
const {help, expandOnFocus = false} = props;
|
|
91
|
-
|
|
92
|
-
//TODO: If focus is in field but user hasn't started typing, show a message telling them to start typing
|
|
93
|
-
return <>
|
|
94
|
-
<FormTokenField
|
|
95
|
-
value={currentTokens}
|
|
96
|
-
label={props.label}
|
|
97
|
-
placeholder="Start typing to see suggestions"
|
|
98
|
-
suggestions={suggestions}
|
|
99
|
-
onChange={updateComponentValue}
|
|
100
|
-
displayTransform={useCallback((token: string) => tokenTitleCacheRef.current!.get(unpackDisplayedTokenText(token).value) ?? token, [tokenTitleCacheRef])}
|
|
101
|
-
maxLength={props.multiple !== false ? props.maxLength : 1}
|
|
102
|
-
__experimentalValidateInput={props.validator}
|
|
103
|
-
__experimentalAutoSelectFirstMatch={true}
|
|
104
|
-
__experimentalExpandOnFocus={expandOnFocus}
|
|
105
|
-
__experimentalShowHowTo={props.multiple !== false}
|
|
106
|
-
onInputChange={setDebouncedInput}
|
|
107
|
-
onFocus={useCallback(() => {
|
|
108
|
-
setHasBeenFocused(true);
|
|
109
|
-
setImmediateInput(input);
|
|
110
|
-
}, [input])}
|
|
111
|
-
__next40pxDefaultSize
|
|
112
|
-
__nextHasNoMarginBottom
|
|
113
|
-
/>
|
|
114
|
-
{help && <div><span className="components-form-token-field__help">{help}</span></div>}
|
|
115
|
-
{hasLoadingError && <ErrorSummaryWithModal summary={<span className="components-form-token-field__help">{__("An Error Occurred While Loading Suggestions")}</span>} detail={thrownLoadingError} />}
|
|
116
|
-
{isLoading && <div><Spinner /><span className="components-form-token-field__help">{__("Updating Suggestions")}</span></div>}
|
|
117
|
-
{hasValidationError && <ErrorSummaryWithModal summary={<span className="components-form-token-field__help">{__("An Error Occurred While Validating")}</span>} detail={thrownValidationError} />}
|
|
118
|
-
{isValidating && <div><Spinner /><span className="components-form-token-field__help">{__("Validating")}</span></div>}
|
|
119
|
-
</>;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const ErrorSummaryWithModal = memo(({summary, detail}: {summary: ReactNode, detail: ReactNode}) => {
|
|
123
|
-
const [isOpen, setOpen] = useState(false);
|
|
124
|
-
const openModal = useCallback(() => setOpen(true), [setOpen]);
|
|
125
|
-
const closeModal = () => setOpen(false);
|
|
126
|
-
|
|
127
|
-
return <>
|
|
128
|
-
<div onClick={openModal} children={summary} />
|
|
129
|
-
{isOpen && <Modal title="Error Details" onRequestClose={closeModal} children={<pre>{detail}</pre>} />}
|
|
130
|
-
</>;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
function useDebouncedValue<V>(defaultValue: V): [V, (value: V) => any, (value: V) => any, V] {
|
|
134
|
-
const [value, setValue] = useState(defaultValue);
|
|
135
|
-
const [debouncedValue, setDebouncedState] = useState(defaultValue);
|
|
136
|
-
const setDebounced = useDebounce(setDebouncedState, 100);
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
setDebounced(value);
|
|
139
|
-
}, [value, setDebounced]);
|
|
140
|
-
return [debouncedValue, setValue, useCallback((value: V) => {
|
|
141
|
-
setValue(value);
|
|
142
|
-
setDebouncedState(value);
|
|
143
|
-
}, [setValue, setDebounced]), value];
|
|
144
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import apiFetch from "@wordpress/api-fetch";
|
|
2
|
-
import {memo, useMemo} from "@wordpress/element";
|
|
3
|
-
|
|
4
|
-
import {basicNumericallyIdedItemPicker} from "./basicNumericallyIdedItemPicker";
|
|
5
|
-
import type {SNPControlSlots} from "../blocks";
|
|
6
|
-
import {ValidationState} from "./ExtendedFormTokenField";
|
|
7
|
-
import {registerSimpleGutenbergApiEndpoint} from "../editor/simple-gutenberg-endpoints-api";
|
|
8
|
-
import type {TokenItem, WPTaxonomyQuery} from "../lib/useful-types";
|
|
9
|
-
|
|
10
|
-
import type {ReactNode} from "react";
|
|
11
|
-
|
|
12
|
-
type ExtendedPostPickerPropsBase = {
|
|
13
|
-
label?: string;
|
|
14
|
-
help?: ReactNode;
|
|
15
|
-
postTypes?: string[];
|
|
16
|
-
placeholder?: string;
|
|
17
|
-
taxonomyQuery?: WPTaxonomyQuery;
|
|
18
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
19
|
-
type ExtendedPostPickerPropsSingle = ExtendedPostPickerPropsBase&{
|
|
20
|
-
onChange(value: string): void;
|
|
21
|
-
value?: string;
|
|
22
|
-
multiple: false;
|
|
23
|
-
};
|
|
24
|
-
type ExtendedPostPickerPropsMultiple = ExtendedPostPickerPropsBase&{
|
|
25
|
-
onChange(value: string[]): void;
|
|
26
|
-
value?: string[];
|
|
27
|
-
multiple?: true|undefined;
|
|
28
|
-
};
|
|
29
|
-
export type ExtendedPostPickerProps = ExtendedPostPickerPropsSingle|ExtendedPostPickerPropsMultiple;
|
|
30
|
-
export type ExtendedPostPickerConstructorProps = ExtendedPostPickerProps;
|
|
31
|
-
|
|
32
|
-
registerSimpleGutenbergApiEndpoint(
|
|
33
|
-
"plaudit-common.post-table-search-options",
|
|
34
|
-
data => apiFetch<Array<{id: number, title: string, type: string}>>({data, method: 'POST', path: "/plaudit/common/v1/post-table-search"}),
|
|
35
|
-
{
|
|
36
|
-
transformer: posts => posts.map(post => ({value: post.id.toString(), title: post.title, status: ValidationState.Valid})),
|
|
37
|
-
maxCachedResults: 10
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
registerSimpleGutenbergApiEndpoint(
|
|
41
|
-
"plaudit-common.post-table-search-validation",
|
|
42
|
-
data => apiFetch<Array<{id: number, title: string, type: string}>>({data, method: 'POST', path: "/plaudit/common/v1/post-table-search"}),
|
|
43
|
-
{
|
|
44
|
-
transformer: posts => posts
|
|
45
|
-
.map((post): TokenItem => ({value: post.id.toString(), title: post.title, status: ValidationState.Valid})),
|
|
46
|
-
maxCachedResults: 10
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
export const ExtendedPostPicker = memo((props: ExtendedPostPickerProps) => {
|
|
51
|
-
const {postTypes, taxonomyQuery, ...passthrough} = props;
|
|
52
|
-
|
|
53
|
-
const queryArgs = useMemo(() => {
|
|
54
|
-
return {postTypes: postTypes?.join(','), taxonomyQuery};
|
|
55
|
-
}, [postTypes, taxonomyQuery]);
|
|
56
|
-
return basicNumericallyIdedItemPicker(passthrough, "plaudit-common.post-table-search", queryArgs);
|
|
57
|
-
});
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import {BaseControl, RadioControl, __experimentalVStack as VStack} from "@wordpress/components";
|
|
2
|
-
import {useInstanceId} from "@wordpress/compose";
|
|
3
|
-
import {useCallback, useState} from "@wordpress/element";
|
|
4
|
-
|
|
5
|
-
import type {SNPControlSlots} from "../blocks";
|
|
6
|
-
|
|
7
|
-
import type {ChangeEvent, ComponentPropsWithoutRef, ElementType} from "react";
|
|
8
|
-
|
|
9
|
-
type RawElementSafeProps<P, T extends ElementType> = P&Omit<ComponentPropsWithoutRef<T>, 'as' | keyof P | 'children'>;
|
|
10
|
-
export type ExtendedRadioControlProps = RawElementSafeProps<ComponentPropsWithoutRef<typeof RadioControl> & { allowCustom?: boolean }, 'input'>&Partial<SNPControlSlots>;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* This is a variant of WordPress'
|
|
14
|
-
*/
|
|
15
|
-
export function ExtendedRadioControl(props: ExtendedRadioControlProps) {
|
|
16
|
-
const {
|
|
17
|
-
label,
|
|
18
|
-
className,
|
|
19
|
-
selected,
|
|
20
|
-
help,
|
|
21
|
-
onChange,
|
|
22
|
-
hideLabelFromVision,
|
|
23
|
-
options = [],
|
|
24
|
-
allowCustom,
|
|
25
|
-
Label, Messages,
|
|
26
|
-
...additionalProps
|
|
27
|
-
} = props;
|
|
28
|
-
const instanceId = useInstanceId( ExtendedRadioControl );
|
|
29
|
-
const id = `inspector-radio-control-${instanceId}`;
|
|
30
|
-
const [valueIsCustom, setValueIsCustom] = useState(
|
|
31
|
-
() => options.find(option => option.value === selected) === undefined);
|
|
32
|
-
const [customValue, setCustomValue] = useState(valueIsCustom ? selected : "");
|
|
33
|
-
|
|
34
|
-
const onChangeValue = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
|
35
|
-
setValueIsCustom(options.find(option => option.value === event.target.value) === undefined);
|
|
36
|
-
onChange(event.target.value);
|
|
37
|
-
}, [onChange, options, setValueIsCustom]);
|
|
38
|
-
const onChangeCustom = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
39
|
-
onChangeValue(e);
|
|
40
|
-
setValueIsCustom(true);
|
|
41
|
-
setCustomValue(e.target.value);
|
|
42
|
-
}, [onChangeValue, setValueIsCustom, setCustomValue]);
|
|
43
|
-
|
|
44
|
-
if (!options?.length) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const distinctnessSet = new Set<string>();
|
|
49
|
-
const classNames = [...className?.split(/\s+/g) ?? [], 'components-radio-control'].filter(cn => distinctnessSet.add(cn)).join(' ');
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<BaseControl
|
|
53
|
-
__nextHasNoMarginBottom
|
|
54
|
-
label={Label ? <Label /> : label}
|
|
55
|
-
id={id}
|
|
56
|
-
hideLabelFromVision={hideLabelFromVision}
|
|
57
|
-
help={help}
|
|
58
|
-
className={classNames}
|
|
59
|
-
>
|
|
60
|
-
<VStack spacing={1}>
|
|
61
|
-
{options.map((option, index) => (
|
|
62
|
-
<div
|
|
63
|
-
key={`${id}-${index}`}
|
|
64
|
-
className="components-radio-control__option"
|
|
65
|
-
>
|
|
66
|
-
<input
|
|
67
|
-
id={`${id}-${index}`}
|
|
68
|
-
className="components-radio-control__input"
|
|
69
|
-
type="radio"
|
|
70
|
-
name={id}
|
|
71
|
-
value={option.value}
|
|
72
|
-
onChange={onChangeValue}
|
|
73
|
-
checked={option.value === selected}
|
|
74
|
-
aria-describedby={help ? `${id}__help` : undefined}
|
|
75
|
-
{...additionalProps}
|
|
76
|
-
/>
|
|
77
|
-
<label
|
|
78
|
-
className="components-radio-control__label"
|
|
79
|
-
htmlFor={`${id}-${index}`}
|
|
80
|
-
>
|
|
81
|
-
{option.label}
|
|
82
|
-
</label>
|
|
83
|
-
</div>
|
|
84
|
-
))}
|
|
85
|
-
{allowCustom && <div
|
|
86
|
-
key={`${id}-${options.length}`}
|
|
87
|
-
className="components-radio-control__option"
|
|
88
|
-
>
|
|
89
|
-
<input
|
|
90
|
-
id={`${id}-${options.length}`}
|
|
91
|
-
className="components-radio-control__input"
|
|
92
|
-
type="radio"
|
|
93
|
-
name={id}
|
|
94
|
-
value={customValue ?? ''}
|
|
95
|
-
onChange={onChangeValue}
|
|
96
|
-
checked={valueIsCustom}
|
|
97
|
-
aria-describedby={help ? `${id}__help` : undefined}
|
|
98
|
-
{...additionalProps}
|
|
99
|
-
/>
|
|
100
|
-
<label id={`${id}-custom-label`} className="components-radio-control__label" htmlFor={`${id}-${options.length}`}>Custom:</label>
|
|
101
|
-
<input aria-labelledby={`${id}-custom-label`} type="text" value={customValue ?? ''} onChange={onChangeCustom} />
|
|
102
|
-
</div>}
|
|
103
|
-
</VStack>
|
|
104
|
-
{Messages && <Messages/>}
|
|
105
|
-
</BaseControl>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import {type BaseEntityRecords, store as coreStore, type Taxonomy} from "@wordpress/core-data";
|
|
2
|
-
import {useSelect} from "@wordpress/data";
|
|
3
|
-
import {useCallback, useMemo} from "@wordpress/element";
|
|
4
|
-
|
|
5
|
-
import type {SNPControlSlots} from "../blocks";
|
|
6
|
-
import type {TokenItem} from "../lib/useful-types";
|
|
7
|
-
import {useOutputMemoizingFilter} from "./hooks/useOutputMemoizingFilter";
|
|
8
|
-
import {ExtendedFormTokenField, unpackDisplayedTokenText, ValidationState} from "./ExtendedFormTokenField";
|
|
9
|
-
|
|
10
|
-
import type {ReactNode} from "react";
|
|
11
|
-
|
|
12
|
-
type ExtendedTaxonomyPickerPropsBase = {
|
|
13
|
-
label?: string;
|
|
14
|
-
help?: ReactNode;
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
visibility?: Partial<BaseEntityRecords.TaxonomyVisibility>
|
|
17
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
18
|
-
export type ExtendedTaxonomyPickerPropsSingle = {
|
|
19
|
-
onChange(value: string): void;
|
|
20
|
-
value?: string;
|
|
21
|
-
multiple: false;
|
|
22
|
-
};
|
|
23
|
-
export type ExtendedTaxonomyPickerPropsMultiple = {
|
|
24
|
-
onChange(value: string[]): void;
|
|
25
|
-
value?: string[];
|
|
26
|
-
multiple?: true|undefined;
|
|
27
|
-
};
|
|
28
|
-
export type ExtendedTaxonomyPickerProps = ExtendedTaxonomyPickerPropsBase&(ExtendedTaxonomyPickerPropsSingle|ExtendedTaxonomyPickerPropsMultiple);
|
|
29
|
-
|
|
30
|
-
export function ExtendedTaxonomyPicker(props: ExtendedTaxonomyPickerProps) {
|
|
31
|
-
const {visibility, ...extendedFormTokenFieldProps} = props;
|
|
32
|
-
const availableTaxonomies = useTaxonomiesFilteredByVisibility(visibility);
|
|
33
|
-
const taxonomyVisibilityFilter = makeTaxonomyVisibilityFilter(visibility);
|
|
34
|
-
|
|
35
|
-
const taxonomySuggestionQuery = useOutputMemoizingFilter((input: string, taxonomies: Taxonomy[]|null) => {
|
|
36
|
-
return taxonomies?.filter(taxonomy => !props.value?.includes(taxonomy.slug))
|
|
37
|
-
.filter(taxonomy => taxonomy.name.toLowerCase().includes(input.toLowerCase()) || taxonomy.slug.toLowerCase().includes(input.toLowerCase()))
|
|
38
|
-
.map(taxonomy => ({value: taxonomy.slug, status: ValidationState.Valid, title: taxonomy.name})) ?? [];
|
|
39
|
-
}, [props.value]);
|
|
40
|
-
const taxonomyValidationQuery = useOutputMemoizingFilter((tokens: string[], rawTaxonomies: Taxonomy[]|null) => {
|
|
41
|
-
if (rawTaxonomies === null) {
|
|
42
|
-
return tokens.map(token => ({value: token, status: ValidationState.Validating}));
|
|
43
|
-
}
|
|
44
|
-
const tokenItemCreator = (taxonomy: Taxonomy): [string, TokenItem] => {
|
|
45
|
-
return [taxonomy.slug, {value: taxonomy.slug, status: ValidationState.Valid, title: taxonomy.name}];
|
|
46
|
-
};
|
|
47
|
-
const taxonomies = rawTaxonomies?.length ? Object.fromEntries(rawTaxonomies.map(tokenItemCreator)) : undefined;
|
|
48
|
-
if (!taxonomies) {
|
|
49
|
-
return tokens.map(token => ({value: token, status: ValidationState.Invalid}));
|
|
50
|
-
}
|
|
51
|
-
return tokens.map(token => taxonomies[token] ?? {value: token, status: ValidationState.Invalid});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return <ExtendedFormTokenField
|
|
55
|
-
expandOnFocus={true}
|
|
56
|
-
{...extendedFormTokenFieldProps}
|
|
57
|
-
stringToTokenConverter={useCallback(token => {
|
|
58
|
-
if (availableTaxonomies === null) {
|
|
59
|
-
return {value: token, status: ValidationState.Validating};
|
|
60
|
-
}
|
|
61
|
-
const {value, title} = unpackDisplayedTokenText(token);
|
|
62
|
-
const foundTaxonomy = availableTaxonomies.find(taxonomy => taxonomy.slug === value);
|
|
63
|
-
if (foundTaxonomy) {
|
|
64
|
-
return {value: foundTaxonomy.slug, status: ValidationState.Valid, title: foundTaxonomy.name};
|
|
65
|
-
}
|
|
66
|
-
return title === undefined ? {value, status: ValidationState.Validating} : {value, status: ValidationState.Invalid, title};
|
|
67
|
-
}, [availableTaxonomies])}
|
|
68
|
-
suggestionQuery={useCallback((input, select) => {
|
|
69
|
-
return taxonomySuggestionQuery(input, taxonomyVisibilityFilter(select(coreStore).getTaxonomies()));
|
|
70
|
-
}, [taxonomyVisibilityFilter, taxonomySuggestionQuery])}
|
|
71
|
-
validationQuery={useCallback((tokens, select) => {
|
|
72
|
-
return taxonomyValidationQuery(tokens, taxonomyVisibilityFilter(select(coreStore).getTaxonomies()));
|
|
73
|
-
}, [taxonomyVisibilityFilter, taxonomyValidationQuery])}
|
|
74
|
-
validator={useCallback((token: string) => {
|
|
75
|
-
if (!availableTaxonomies) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
const {value} = unpackDisplayedTokenText(token);
|
|
79
|
-
return availableTaxonomies.find(taxonomy => taxonomy.slug === value) !== undefined;
|
|
80
|
-
}, [availableTaxonomies])}
|
|
81
|
-
/>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function useTaxonomiesFilteredByVisibility(visibility: ExtendedTaxonomyPickerProps['visibility']) {
|
|
85
|
-
const taxonomies = useSelect(select => select(coreStore).getTaxonomies(), []);
|
|
86
|
-
return useMemo(() => filterTaxonomiesByVisibility(taxonomies, visibility), [taxonomies, visibility]);
|
|
87
|
-
}
|
|
88
|
-
function makeTaxonomyVisibilityFilter(visibility: Parameters<typeof filterTaxonomiesByVisibility>[1]) {
|
|
89
|
-
return useOutputMemoizingFilter((taxonomies: Parameters<typeof filterTaxonomiesByVisibility>[0]) => filterTaxonomiesByVisibility(taxonomies, visibility), [visibility]);
|
|
90
|
-
}
|
|
91
|
-
function filterTaxonomiesByVisibility(taxonomies: Taxonomy[]|null, visibility: ExtendedTaxonomyPickerProps['visibility']) {
|
|
92
|
-
if (!taxonomies || !visibility) {
|
|
93
|
-
return taxonomies;
|
|
94
|
-
}
|
|
95
|
-
const tests = Object.entries(visibility)
|
|
96
|
-
.filter((entry): entry is [keyof BaseEntityRecords.TaxonomyVisibility, boolean] => entry[1] !== undefined && entry[1] !== null);
|
|
97
|
-
return tests.length
|
|
98
|
-
? taxonomies.filter(taxonomy => tests.every(([key, value]) => taxonomy.visibility[key] === value))
|
|
99
|
-
: taxonomies;
|
|
100
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import apiFetch from "@wordpress/api-fetch";
|
|
2
|
-
import {select as dataSelect} from "@wordpress/data";
|
|
3
|
-
import {useCallback} from "@wordpress/element";
|
|
4
|
-
|
|
5
|
-
import type {SNPControlSlots} from "../blocks";
|
|
6
|
-
import {ExtendedFormTokenField, unpackDisplayedTokenText, ValidationState} from "./ExtendedFormTokenField";
|
|
7
|
-
import {registerSimpleGutenbergApiEndpoint} from "../editor/simple-gutenberg-endpoints-api";
|
|
8
|
-
import {store as endpointsStore} from "../editor/simple-gutenberg-endpoints-impl";
|
|
9
|
-
import type {TokenItem} from "../lib/useful-types";
|
|
10
|
-
|
|
11
|
-
import type {ReactNode} from "react";
|
|
12
|
-
|
|
13
|
-
type ExtendedTermPickerPropsBase = {
|
|
14
|
-
label?: string;
|
|
15
|
-
help?: ReactNode;
|
|
16
|
-
taxonomy: string;
|
|
17
|
-
placeholder?: string;
|
|
18
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
19
|
-
export type ExtendedTermPickerPropsSingle = ExtendedTermPickerPropsBase&{
|
|
20
|
-
onChange(value: string): void;
|
|
21
|
-
value?: string;
|
|
22
|
-
multiple: false;
|
|
23
|
-
};
|
|
24
|
-
export type ExtendedTermPickerPropsMultiple = ExtendedTermPickerPropsBase&{
|
|
25
|
-
onChange(value: string[]): void;
|
|
26
|
-
value?: string[];
|
|
27
|
-
multiple?: true|undefined;
|
|
28
|
-
};
|
|
29
|
-
export type ExtendedTermPickerProps = ExtendedTermPickerPropsSingle|ExtendedTermPickerPropsMultiple;
|
|
30
|
-
|
|
31
|
-
export type TermDataStruct = {term_id: number, name: string, slug: number, term_taxonomy_id: number, taxonomy: string};
|
|
32
|
-
registerSimpleGutenbergApiEndpoint(
|
|
33
|
-
"plaudit-common.term-table-search-options",
|
|
34
|
-
data => apiFetch<TermDataStruct[]>({data, method: 'POST', path: "/plaudit/common/v1/term-table-search"}),
|
|
35
|
-
{
|
|
36
|
-
transformer: terms => terms.map(term => ({value: term.slug, title: term.name, status: ValidationState.Valid})),
|
|
37
|
-
maxCachedResults: 10
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
registerSimpleGutenbergApiEndpoint(
|
|
41
|
-
"plaudit-common.term-table-search-validation",
|
|
42
|
-
data => apiFetch<TermDataStruct[]>({data, method: 'POST', path: "/plaudit/common/v1/term-table-search"}),
|
|
43
|
-
{
|
|
44
|
-
transformer: terms => terms
|
|
45
|
-
.map((term) => ({value: term.slug, title: term.name, status: ValidationState.Valid})),
|
|
46
|
-
maxCachedResults: 10
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
export function ExtendedTermPicker(props: ExtendedTermPickerProps) {
|
|
51
|
-
const {taxonomy} = props;
|
|
52
|
-
|
|
53
|
-
const suggestionQuery = useCallback((input: string, select: typeof dataSelect) => {
|
|
54
|
-
return select(endpointsStore).get("plaudit-common.term-table-search-options", {search: input, taxonomy}) as TokenItem[]|undefined;
|
|
55
|
-
}, [taxonomy]);
|
|
56
|
-
const validationQuery = useCallback((slugsBeingValidated: string[], select: typeof dataSelect) => {
|
|
57
|
-
return select(endpointsStore).get("plaudit-common.term-table-search-validation", {slugs: slugsBeingValidated.join(','), taxonomy}) as TokenItem[]|undefined;
|
|
58
|
-
}, [taxonomy]);
|
|
59
|
-
|
|
60
|
-
return <ExtendedFormTokenField {...props} validationQuery={validationQuery} suggestionQuery={suggestionQuery} stringToTokenConverter={unpackDisplayedTokenText} />;
|
|
61
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import {TextareaControl} from "@wordpress/components";
|
|
2
|
-
import {useDebounce} from "@wordpress/compose";
|
|
3
|
-
|
|
4
|
-
import {forwardRef, useCallback, useEffect, useImperativeHandle, useRef} from "@wordpress/element";
|
|
5
|
-
|
|
6
|
-
import type {SNPControlSlots} from "../blocks";
|
|
7
|
-
|
|
8
|
-
import type {ComponentProps} from "react";
|
|
9
|
-
|
|
10
|
-
export type TextareaControlPropsExtension = {newline?: undefined|"\n"|"br"|"p", placeholder?: string|undefined, rich?: boolean};
|
|
11
|
-
export type ExtendedTextAreaControlProps = Omit<ComponentProps<typeof TextareaControl>, 'value'>&{value: ComponentProps<typeof TextareaControl>['value']|null|undefined}
|
|
12
|
-
&Partial<Pick<SNPControlSlots, 'Messages'>>&TextareaControlPropsExtension;
|
|
13
|
-
export const ExtendedTextareaControl = (() => {
|
|
14
|
-
const debouncerSettings = {leading: false, maxWait: 250, trailing: true};
|
|
15
|
-
return forwardRef<HTMLTextAreaElement, ExtendedTextAreaControlProps>((props, ref) => {
|
|
16
|
-
const {newline, onChange, placeholder, rich, value, Messages, ...forwardedProps} = props;
|
|
17
|
-
let v = value ?? '';
|
|
18
|
-
if (!rich) {
|
|
19
|
-
if (newline === "br") {
|
|
20
|
-
v = v.replaceAll(/<br>\s*<\/br>|<br\/?>/gi, "\n");
|
|
21
|
-
} else if (newline === "p") {
|
|
22
|
-
v = v.replaceAll(/<p>(.*?)<\/p>/gi, "$1\n");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
const safeOnChange = useCallback((v: string) => {
|
|
26
|
-
if (rich) {
|
|
27
|
-
onChange(v);
|
|
28
|
-
} else if (newline === "br") {
|
|
29
|
-
onChange(v.replaceAll(/\r?\n/g, "<br/>"));
|
|
30
|
-
} else if (newline === "p") {
|
|
31
|
-
onChange(v.split(/\r?\n/g).map(v => "<p>" + v + "</p>").join(""));
|
|
32
|
-
} else {
|
|
33
|
-
onChange(v);
|
|
34
|
-
}
|
|
35
|
-
}, [onChange]);
|
|
36
|
-
|
|
37
|
-
const textareaRef = useRef<HTMLTextAreaElement|null>(null);
|
|
38
|
-
useImperativeHandle(ref, () => textareaRef.current!, []);
|
|
39
|
-
|
|
40
|
-
const debouncedUpdate = useDebounce(safeOnChange, 100, debouncerSettings);
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
const id = textareaRef.current?.id;
|
|
44
|
-
if (id && rich && document.getElementById(id)) {
|
|
45
|
-
(window as any).tinymce.EditorManager.execCommand('mceAddEditor', true, id);
|
|
46
|
-
const editor = (window as any).tinymce.editors.findLast((e: {id: string}) => e.id === id);
|
|
47
|
-
editor.on('input', () => debouncedUpdate(editor.getContent()));
|
|
48
|
-
editor.on('NodeChange', () => debouncedUpdate(editor.getContent()));
|
|
49
|
-
editor.on('change', () => {
|
|
50
|
-
debouncedUpdate.cancel();
|
|
51
|
-
safeOnChange(editor.getContent());
|
|
52
|
-
});
|
|
53
|
-
return () => {
|
|
54
|
-
(window as any).tinymce.EditorManager.execCommand('mceRemoveEditor', true, id);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return () => {};
|
|
58
|
-
}, [rich, debouncedUpdate, textareaRef.current]);
|
|
59
|
-
|
|
60
|
-
return <>
|
|
61
|
-
<TextareaControl {...forwardedProps} ref={textareaRef} value={v} onChange={safeOnChange} __nextHasNoMarginBottom />
|
|
62
|
-
{Messages && <Messages/>}
|
|
63
|
-
</>;
|
|
64
|
-
})
|
|
65
|
-
})();
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import apiFetch from "@wordpress/api-fetch";
|
|
2
|
-
import {memo, useMemo} from "@wordpress/element";
|
|
3
|
-
|
|
4
|
-
import {basicNumericallyIdedItemPicker} from "./basicNumericallyIdedItemPicker";
|
|
5
|
-
import type {SNPControlSlots} from "../blocks";
|
|
6
|
-
import {ValidationState} from "./ExtendedFormTokenField";
|
|
7
|
-
import {registerSimpleGutenbergApiEndpoint} from "../editor/simple-gutenberg-endpoints-api";
|
|
8
|
-
import type {TokenItem} from "../lib/useful-types";
|
|
9
|
-
|
|
10
|
-
import type {ReactNode} from "react";
|
|
11
|
-
|
|
12
|
-
type ExtendedUserPickerPropsBase = {
|
|
13
|
-
label?: string;
|
|
14
|
-
help?: ReactNode;
|
|
15
|
-
userRoles?: string[];
|
|
16
|
-
placeholder?: string;
|
|
17
|
-
}&Partial<Pick<SNPControlSlots, 'Messages'>>;
|
|
18
|
-
type ExtendedUserPickerPropsSingle = ExtendedUserPickerPropsBase&{
|
|
19
|
-
onChange(value: string): void;
|
|
20
|
-
value?: string;
|
|
21
|
-
multiple: false;
|
|
22
|
-
};
|
|
23
|
-
type ExtendedUserPickerPropsMultiple = ExtendedUserPickerPropsBase&{
|
|
24
|
-
onChange(value: string[]): void;
|
|
25
|
-
value?: string[];
|
|
26
|
-
multiple?: true|undefined;
|
|
27
|
-
};
|
|
28
|
-
export type ExtendedUserPickerProps = ExtendedUserPickerPropsSingle|ExtendedUserPickerPropsMultiple;
|
|
29
|
-
|
|
30
|
-
registerSimpleGutenbergApiEndpoint(
|
|
31
|
-
"plaudit-common.user-table-search-options",
|
|
32
|
-
data => apiFetch<Array<{id: number, name: string, roles?: string[]}>>({data, method: 'POST', path: "/plaudit/common/v1/user-table-search"}),
|
|
33
|
-
{
|
|
34
|
-
transformer: users => users.map(user => ({value: user.id.toString(), title: user.name, status: ValidationState.Valid})),
|
|
35
|
-
maxCachedResults: 10
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
registerSimpleGutenbergApiEndpoint(
|
|
39
|
-
"plaudit-common.user-table-search-validation",
|
|
40
|
-
data => apiFetch<Array<{id: number, name: string, roles?: string[]}>>({data, method: 'POST', path: "/plaudit/common/v1/user-table-search"}),
|
|
41
|
-
{
|
|
42
|
-
transformer: user => user
|
|
43
|
-
.map((user): TokenItem => ({value: user.id.toString(), title: user.name, status: ValidationState.Valid})),
|
|
44
|
-
maxCachedResults: 10
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
export const ExtendedUserPicker = memo((props: ExtendedUserPickerProps) => {
|
|
49
|
-
const {userRoles, ...passthrough} = props;
|
|
50
|
-
|
|
51
|
-
const queryArgs = useMemo(() => {
|
|
52
|
-
return {roles: userRoles?.join(",")};
|
|
53
|
-
}, [userRoles]);
|
|
54
|
-
|
|
55
|
-
return basicNumericallyIdedItemPicker(passthrough, "plaudit-common.user-table-search", queryArgs);
|
|
56
|
-
})
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {MediaUpload, MediaUploadCheck} from "@wordpress/block-editor";
|
|
2
|
-
import {BaseControl, Button, useBaseControlProps} from "@wordpress/components";
|
|
3
|
-
import {store as coreStore} from "@wordpress/core-data";
|
|
4
|
-
import {useSuspenseSelect} from "@wordpress/data";
|
|
5
|
-
import {useCallback} from "@wordpress/element";
|
|
6
|
-
import {__} from "@wordpress/i18n";
|
|
7
|
-
|
|
8
|
-
import type {ReactNode} from "react";
|
|
9
|
-
|
|
10
|
-
export type FileControlProps = {
|
|
11
|
-
label: ReactNode,
|
|
12
|
-
help?: ReactNode,
|
|
13
|
-
onChange: (value?: number) => void,
|
|
14
|
-
value?: number,
|
|
15
|
-
allowedTypes?: string[]
|
|
16
|
-
};
|
|
17
|
-
export function FileControl(props: FileControlProps) {
|
|
18
|
-
const {allowedTypes, help, label, onChange, value} = props;
|
|
19
|
-
const onSelect = useCallback((media: {id: number}) => {
|
|
20
|
-
if (media.id !== value) {
|
|
21
|
-
onChange(media.id);
|
|
22
|
-
}
|
|
23
|
-
}, [onChange, value]);
|
|
24
|
-
|
|
25
|
-
const attachment = useSuspenseSelect(select => value !== undefined ? select(coreStore).getMedia(value) : undefined, [value]);
|
|
26
|
-
const {baseControlProps, controlProps} = useBaseControlProps({label, help});
|
|
27
|
-
return <BaseControl {...baseControlProps} __nextHasNoMarginBottom>
|
|
28
|
-
<MediaUploadCheck>
|
|
29
|
-
<MediaUpload
|
|
30
|
-
onSelect={onSelect}
|
|
31
|
-
value={value}
|
|
32
|
-
allowedTypes={allowedTypes}
|
|
33
|
-
render={useCallback(({open}) => {
|
|
34
|
-
if (!attachment?.id) {
|
|
35
|
-
return <div {...controlProps}><Button onClick={open} children={__('Select a File', 'plaudit')} __next40pxDefaultSize /></div>
|
|
36
|
-
}
|
|
37
|
-
return <div {...controlProps}>
|
|
38
|
-
<p>{__('Selected File', 'plaudit')}: <a href={attachment.source_url} target="_blank" rel="noopener noreferrer">{attachment.title.rendered}</a></p>
|
|
39
|
-
<div className="selected-file-control-buttons">
|
|
40
|
-
<Button onClick={open} variant="secondary" children="Replace File" __next40pxDefaultSize />
|
|
41
|
-
<Button onClick={() => onChange(undefined)} isDestructive children={__('Remove File', 'plaudit')} __next40pxDefaultSize />
|
|
42
|
-
</div>
|
|
43
|
-
</div>;
|
|
44
|
-
}, [attachment, onChange, controlProps])}
|
|
45
|
-
/>
|
|
46
|
-
</MediaUploadCheck>
|
|
47
|
-
</BaseControl>;
|
|
48
|
-
}
|